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
JavaScript
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