UNPKG

ngx-sharebuttons

Version:

<p align="center"> <img height="200px" width="200px" style="text-align: center;" src="https://rawcdn.githack.com/MurhafSousli/ngx-sharebuttons/13279ed77c47fe9dd7b61e4dad3ded6d02488c2f/projects/ngx-sharebuttons-demo/src/assets/img/logo.svg"> <h1 alig

622 lines (613 loc) 21.2 kB
import * as i0 from '@angular/core'; import { InjectionToken, makeEnvironmentProviders, inject, Injectable, ElementRef, signal, computed, input, output, effect, untracked, Directive } from '@angular/core'; import { DOCUMENT } from '@angular/common'; import { HttpParams } from '@angular/common/http'; import { Meta } from '@angular/platform-browser'; import { Platform } from '@angular/cdk/platform'; import { Clipboard } from '@angular/cdk/clipboard'; /** Returns a valid URL or falls back to current URL */ function getValidUrl(url) { const isValidUrl = /^(http|https):\/\//.test(url); if (isValidUrl) { return url; } console.warn(`[ShareButtons]: Sharing link '${url}' is invalid!`); return null; } function printPage() { return document.defaultView.print(); } function copyToClipboard({ params, data, clipboard, uiState }) { clipboard.copy(params.url); // Disable copy button uiState.set({ icon: data.successIcon, text: data.successText, disabled: true }); setTimeout(() => { uiState.set({ icon: data.icon, text: data.text, disabled: false }); }, data.delay); } const SHARE_BUTTONS_CONFIG = new InjectionToken('SHARE_BUTTONS_CONFIG', { providedIn: 'root', factory: () => defaultOptions }); const SHARE_ICONS = new InjectionToken('SHARE_ICONS'); function provideShareButtonsOptions(...providers) { return makeEnvironmentProviders(providers); } function withConfig(options) { return makeEnvironmentProviders([ { provide: SHARE_BUTTONS_CONFIG, useValue: { ...defaultOptions, ...options } } ]); } class IShareButton { } var SharerMethods; (function (SharerMethods) { SharerMethods["Anchor"] = "anchor"; SharerMethods["Window"] = "window"; })(SharerMethods || (SharerMethods = {})); const defaultOptions = { sharerMethod: SharerMethods.Anchor, theme: 'default', windowWidth: 800, windowHeight: 500, moreButtonIcon: ['fas', 'ellipsis-h'], lessButtonIcon: ['fas', 'minus'], moreButtonAriaLabel: 'Show more share buttons', lessButtonAriaLabel: 'Show less share buttons' }; // Create message body that includes the sharing link used for Email, SMS and WhatsApp buttons const linkInDescription = { description: (p) => { return p.description ? `${p.description}\r\n${p.url}` : p.url; } }; const facebookParams = { type: 'facebook', text: 'Facebook', ariaLabel: 'Share on Facebook', icon: ['fab', 'facebook-f'], color: '#4267B2', share: { desktop: 'https://facebook.com/sharer/sharer.php' }, params: { url: 'u' } }; const xParams = { type: 'x', text: 'X', ariaLabel: 'Post on X', icon: ['fab', 'x-twitter'], color: '#000', share: { desktop: 'https://x.com/intent/post' }, params: { url: 'url', description: 'text', tags: 'hashtags', via: 'via' } }; const linkedInParams = { type: 'linkedin', text: 'LinkedIn', ariaLabel: 'Share on LinkedIn', icon: ['fab', 'linkedin-in'], color: '#0a66c2', share: { desktop: 'https://www.linkedin.com/sharing/share-offsite' }, params: { url: 'url' } }; const pinterestParams = { type: 'pinterest', text: 'Pinterest', ariaLabel: 'Share on Pinterest', icon: ['fab', 'pinterest-p'], color: '#e60023', share: { desktop: 'https://pinterest.com/pin/create/button/' }, params: { url: 'url', description: 'description', image: 'media' } }; const redditParams = { type: 'reddit', text: 'Reddit', ariaLabel: 'Share on Reddit', icon: ['fab', 'reddit-alien'], color: '#FF4006', share: { desktop: 'https://www.reddit.com/submit' }, params: { url: 'url', title: 'title' } }; const tumblrParams = { type: 'tumblr', text: 'Tumblr', ariaLabel: 'Share on Tumblr', icon: ['fab', 'tumblr'], color: '#36465D', share: { desktop: 'https://tumblr.com/widgets/share/tool' }, params: { url: 'canonicalUrl', description: 'caption', tags: 'tags' } }; const mixParams = { type: 'mix', text: 'Mix', ariaLabel: 'Share on Mix', icon: ['fab', 'mix'], color: '#eb4924', share: { desktop: 'https://mix.com/add' }, params: { url: 'url' } }; const viberParams = { type: 'viber', text: 'Viber', ariaLabel: 'Share on Viber', icon: ['fab', 'viber'], color: '#665ca7', share: { android: 'viber://forward', ios: 'viber://forward' }, params: { description: 'text' }, paramsFunc: linkInDescription, requiredParams: { description: true } }; const vkParams = { type: 'vk', text: 'VKontakte', ariaLabel: 'Share on VKontakte', icon: ['fab', 'vk'], color: '#4C75A3', share: { desktop: 'https://vk.com/share.php' }, params: { url: 'url' } }; const telegramParams = { type: 'telegram', text: 'Telegram', ariaLabel: 'Share on Telegram', icon: ['fab', 'telegram-plane'], color: '#0088cc', share: { desktop: 'https://t.me/share/url' }, params: { url: 'url', description: 'text' } }; const messengerParams = { type: 'messenger', text: 'Messenger', ariaLabel: 'Share on Messenger', icon: ['fab', 'facebook-messenger'], color: '#168AFF', share: { desktop: 'https://www.facebook.com/dialog/send', android: 'fb-messenger://share/', ios: 'fb-messenger://share/' }, params: { url: 'link', appId: 'app_id', redirectUrl: 'redirect_uri' } }; const whatsappParams = { type: 'whatsapp', text: 'WhatsApp', ariaLabel: 'Share on WhatsApp', icon: ['fab', 'whatsapp'], color: '#25D366', share: { desktop: 'https://api.whatsapp.com/send', android: 'whatsapp://send', ios: 'https://api.whatsapp.com/send' }, params: { url: 'link', description: 'text' }, requiredParams: { description: true }, paramsFunc: linkInDescription }; const xingParams = { type: 'xing', text: 'Xing', ariaLabel: 'Share on Xing', icon: ['fab', 'xing'], color: '#006567', share: { desktop: 'https://www.xing.com/spi/shares/new' }, params: { url: 'url' } }; const lineParams = { type: 'line', text: 'Line', ariaLabel: 'Share on Line', icon: ['fab', 'line'], color: '#00b900', share: { desktop: 'https://social-plugins.line.me/lineit/share' }, params: { url: 'url' } }; const smsParams = { type: 'sms', text: 'SMS', ariaLabel: 'Share link via SMS', icon: ['fas', 'sms'], color: '#20c16c', share: { desktop: 'sms:', ios: 'sms:&' }, params: { description: 'body' }, paramsFunc: linkInDescription, requiredParams: { description: true } }; const emailParams = { type: 'email', text: 'Email', ariaLabel: 'Share link via email', icon: ['fas', 'envelope'], color: '#FF961C', share: { desktop: 'mailto:' }, params: { title: 'subject', description: 'body' }, paramsFunc: linkInDescription, requiredParams: { description: true } }; const printerParams = { type: 'print', text: 'Print', ariaLabel: 'Print page', icon: ['fas', 'print'], color: '#765AA2', func: printPage }; const copyParams = { type: 'copy', text: 'Copy link', ariaLabel: 'Copy link', icon: ['fas', 'link'], color: '#607D8B', data: { text: 'Copy link', icon: ['fas', 'link'], successText: 'Copied', successIcon: ['fas', 'check'], delay: 2000 }, func: copyToClipboard }; const SHARE_BUTTONS = { facebook: facebookParams, x: xParams, linkedin: linkedInParams, pinterest: pinterestParams, reddit: redditParams, tumblr: tumblrParams, mix: mixParams, viber: viberParams, vk: vkParams, telegram: telegramParams, messenger: messengerParams, whatsapp: whatsappParams, xing: xingParams, line: lineParams, sms: smsParams, email: emailParams, print: printerParams, copy: copyParams }; class ShareService { constructor() { this.document = inject(DOCUMENT); // This declaration just to allow SHARE_ICONS to load the icons this.icons = inject(SHARE_ICONS, { optional: true }); this.meta = inject(Meta); this.platform = inject(Platform); this.clipboard = inject(Clipboard); } /** * Get meta tag content */ _getMetaTagContent(key) { const metaUsingProperty = this.meta.getTag(`property="${key}"`); const metaUsingName = this.meta.getTag(`name="${key}"`); return (metaUsingProperty || metaUsingName)?.getAttribute('content') ?? null; } _getComputedUrl(url) { return getValidUrl(url || this._getMetaTagContent('og:url') || this.document.location.href); } _getComputedParams(params) { // If user provided a URL, then we cannot use the meta tag of the current page for the sharing parameters if (params.url) { return { title: params.title, description: params.description, image: params.image, tags: params.tags, via: params.via, url: this._getComputedUrl(params.url), appId: params.appId || this._getMetaTagContent('fb:app_id'), redirectUrl: params.redirectUrl || this.document.location.href }; } return { title: params.title || this._getMetaTagContent('og:title'), description: params.description || this._getMetaTagContent('og:description'), image: params.image || this._getMetaTagContent('og:image'), tags: params.tags, via: params.via, url: this._getComputedUrl(params.url), appId: params.appId || this._getMetaTagContent('fb:app_id'), redirectUrl: params.redirectUrl || this.document.location.href }; } _getComputedUrlParams(shareButton, params) { const computedParams = this._getComputedParams(params); return Object.entries(shareButton.params).reduce((params, [key, realKey]) => { // Check if param has a value if ((shareButton.requiredParams && shareButton.requiredParams[key]) || computedParams[key]) { // Check if param has a resolver function const resolver = shareButton.paramsFunc?.[key]; params[realKey] = resolver ? resolver(computedParams) : computedParams[key]; } return params; }, {}); } _getShareButtonInstance(name, props) { /** Combine injected option with default options */ const button = props ? { ...SHARE_BUTTONS[name], ...props } : SHARE_BUTTONS[name]; if (button) { return button; } throw new Error(`[ShareButtons]: The share button '${button}' does not exist!`); } share(shareButton, options) { let sharerLink; if (this.platform.IOS && shareButton.share.ios) { sharerLink = shareButton.share.ios; } else if (this.platform.ANDROID && shareButton.share.android) { sharerLink = shareButton.share.android; } else { sharerLink = shareButton.share.desktop; } const params = this._getComputedUrlParams(shareButton, options.params); if (sharerLink) { switch (options.method) { case SharerMethods.Anchor: this.openViaAnchor({ params, url: sharerLink, target: options.target }, options.debug); break; case SharerMethods.Window: this.openViaWindow({ params, url: sharerLink, target: options.target }, options.windowOptions, options.debug); break; } } } open(options) { const button = this._getShareButtonInstance(options.name, options.props); this.openInstance(options, button); } openInstance(options, button) { if (button.share) { this.share(button, options); } else { button.func({ params: this._getComputedParams(options.params), data: button.data, clipboard: this.clipboard, uiState: options.uiState }); } } openViaWindow(args, windowOptions, debug) { const finalUrl = `${args.url}?${new HttpParams({ fromObject: args.params }).toString()}`; if (debug) { console.log('[SHARE BUTTONS]: open link via window', finalUrl); } const windowRef = windowOptions?.windowObj || this.document.defaultView; // Open link using Window object const openWindow = windowRef?.[windowOptions?.openFunc] || this.document.defaultView.open; openWindow(finalUrl, args.target ?? '_blank', `width=${windowOptions?.width || 800}, height=${windowOptions?.height || 500}`); // Prevent security vulnerability https://medium.com/@jitbit/target-blank-the-most-underestimated-vulnerability-ever-96e328301f4c windowRef.opener ??= null; } openViaAnchor(args, debug) { const finalUrl = `${args.url}?${new HttpParams({ fromObject: args.params }).toString()}`; if (debug) { console.log('[SHARE BUTTONS]: open link via anchor', finalUrl); } const linkElement = this.document.createElement('a'); // Make it open in a new tab/window (depends on user's browser configuration) linkElement.setAttribute('target', args.target ?? '_blank'); // Prevent security vulnerability https://medium.com/@jitbit/target-blank-the-most-underestimated-vulnerability-ever-96e328301f4c linkElement.setAttribute('rel', 'noopener noreferrer'); linkElement.href = finalUrl; linkElement.click(); linkElement.remove(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: ShareService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: ShareService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: ShareService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); const SHARE_BUTTONS_PROP = new InjectionToken('SHARE_BUTTONS_PROP', { providedIn: 'root', factory: () => SHARE_BUTTONS }); function customShareButton(key, button) { SHARE_BUTTONS[key] = { ...SHARE_BUTTONS[key], ...button }; return makeEnvironmentProviders([ { provide: SHARE_BUTTONS_PROP, useValue: SHARE_BUTTONS } ]); } class ShareButtonDirective { constructor() { this.shareButtonsProps = inject(SHARE_BUTTONS_PROP); /** Injected options */ this.options = inject(SHARE_BUTTONS_CONFIG); /** Share directive element ref */ this.shareService = inject(ShareService); this.nativeElement = inject((ElementRef)).nativeElement; /** Share button UI state */ this.uiState = signal({}); /** Share button color */ this.color = computed(() => this.shareButtonInstance().color); /** Share button text */ this.text = computed(() => this.uiState().text); /** Share button icon */ this.icon = computed(() => this.uiState().icon); /** Share button disabled */ this.disabled = computed(() => this.uiState().disabled); /** Share button type */ this.shareButton = input.required(); this.shareButtonInstance = computed(() => { return this.shareButtonsProps[this.shareButton()]; }); /** Sets the title parameter */ this.title = input(); /** Sets the description parameter */ this.description = input(); /** Sets the image parameter for sharing on Pinterest */ this.image = input(); /** Sets the tags parameter for sharing on X and Tumblr */ this.tags = input(); /** Sets the fb messenger redirect url to enable sharing on Messenger desktop */ this.redirectUrl = input(); /** Sharing link */ this.url = input(); /** Stream that emits when share dialog is opened */ this.opened = output(); effect(() => { const button = this.shareButtonInstance(); if (!button) { throw new Error(`[ShareButtons]: The share button '${this.shareButton()}' does not exist!`); } untracked(() => { // Set share button properties this.uiState.set({ icon: button.icon, text: button.text, disabled: false }); }); }); effect(() => { // Set disabled attribute only when disabled state is true, because disabled="false" will also disable the button this.nativeElement.toggleAttribute('disabled', this.uiState().disabled); }); } /** * Share the link */ share() { this.shareService.openInstance({ params: { url: this.url(), title: this.title(), description: this.description(), image: this.image(), tags: this.tags(), redirectUrl: this.redirectUrl() }, target: this.options.sharerTarget, debug: this.options.debug, method: this.options.sharerMethod, uiState: this.uiState, }, this.shareButtonInstance()); // Emit after share action is done this.opened.emit(this.shareButton()); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: ShareButtonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.1.5", type: ShareButtonDirective, isStandalone: true, selector: "[shareButton]", inputs: { shareButton: { classPropertyName: "shareButton", publicName: "shareButton", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, image: { classPropertyName: "image", publicName: "image", isSignal: true, isRequired: false, transformFunction: null }, tags: { classPropertyName: "tags", publicName: "tags", isSignal: true, isRequired: false, transformFunction: null }, redirectUrl: { classPropertyName: "redirectUrl", publicName: "redirectUrl", isSignal: true, isRequired: false, transformFunction: null }, url: { classPropertyName: "url", publicName: "url", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { opened: "opened" }, host: { listeners: { "click": "share()" }, properties: { "style.--button-color": "color()", "attr.aria-label": "shareButtonInstance().ariaLabel" } }, exportAs: ["shareButton"], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: ShareButtonDirective, decorators: [{ type: Directive, args: [{ selector: '[shareButton]', exportAs: 'shareButton', host: { '[style.--button-color]': 'color()', '[attr.aria-label]': 'shareButtonInstance().ariaLabel', '(click)': 'share()' } }] }], ctorParameters: () => [] }); /** * Generated bundle index. Do not edit. */ export { IShareButton, SHARE_BUTTONS, SHARE_BUTTONS_CONFIG, SHARE_BUTTONS_PROP, SHARE_ICONS, ShareButtonDirective, ShareService, SharerMethods, copyParams, customShareButton, defaultOptions, emailParams, facebookParams, lineParams, linkedInParams, messengerParams, mixParams, pinterestParams, printerParams, provideShareButtonsOptions, redditParams, smsParams, telegramParams, tumblrParams, viberParams, vkParams, whatsappParams, withConfig, xParams, xingParams }; //# sourceMappingURL=ngx-sharebuttons.mjs.map