Skip to content

Commit

Permalink
Merge pull request #1358 from exadel-inc/feature/esl-share
Browse files Browse the repository at this point in the history
epic (esl-share): initial implementation
  • Loading branch information
dshovchko authored Mar 7, 2023
2 parents 9afaba9 + f9011a6 commit a11db42
Show file tree
Hide file tree
Showing 27 changed files with 767 additions and 0 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@
"modules/lib.js",
"src/modules/lib.ts",
"modules/esl-media/providers/**/*.js",
"modules/esl-share/actions/**/*.js",
"src/modules/esl-media/providers/**/*.ts",
"src/modules/esl-share/actions/**/*.ts",
"modules/draft/esl-carousel/core/view/*.js",
"src/modules/draft/esl-carousel/core/view/*.ts",
"polyfills/**/*.js",
Expand Down
12 changes: 12 additions & 0 deletions pages/src/esl-share/esl-share.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
esl-share-list esl-share-button[unavailable] {
display: inline-block;
opacity: .3;
cursor: not-allowed;
filter: grayscale(1);
}

esl-share-button {
path, circle {
fill: #fff;
}
}
1 change: 1 addition & 0 deletions pages/src/localdev.less
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

@import "./collection-grid/collection-grid.less";
@import "./esl-media-demo/test-media.less";
@import "./esl-share/esl-share.less";

@import "../../src/modules/all.less";
@import "../../src/modules/draft/all.less";
Expand Down
10 changes: 10 additions & 0 deletions pages/src/localdev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
ESLFootnotes,
ESLTooltip,
ESLAnimate,
ESLShareList,
ESLRelatedTarget
} from '../../src/modules/all';

Expand All @@ -41,6 +42,12 @@ import '../../src/modules/esl-media/providers/html5/video-provider';
import '../../src/modules/esl-media/providers/youtube-provider';
import '../../src/modules/esl-media/providers/brightcove-provider';

import '../../src/modules/esl-share/actions/copy-action';
import '../../src/modules/esl-share/actions/external-action';
import '../../src/modules/esl-share/actions/media-action';
import '../../src/modules/esl-share/actions/native-action';
import '../../src/modules/esl-share/actions/print-action';

import {
ESLCarousel,
ESLCarouselPlugins
Expand Down Expand Up @@ -106,5 +113,8 @@ ESLCarouselPlugins.Link.register();
ESLCarouselPlugins.Touch.register();
ESLCarouselPlugins.Autoplay.register();

ESLShareList.config(() => fetch('/assets/share/config.json').then((response) => response.json()));
ESLShareList.register();

// Register ESL Mixins
ESLRelatedTarget.register();
10 changes: 10 additions & 0 deletions pages/static/assets/examples/share.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
177 changes: 177 additions & 0 deletions pages/static/assets/share/config.json

Large diffs are not rendered by default.

71 changes: 71 additions & 0 deletions pages/views/examples/share.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
layout: content
title: Share
name: Share
tags: [examples, beta]
icon: examples/share.svg
aside:
components:
- esl-share
---
{% import 'lorem.njk' as lorem %}

<section>
<h2>Share example</h2>

<h3>Just a standalone share button</h3>
<esl-share-button action="media" link="//www.linkedin.com/sharing/share-offsite/?url={u}" name="linkedin" data-share-title="Standalone share button" data-share-url="/standalone.html" tabindex="0" role="button" title="LinkedIn">
<span title="Linkedin" class="esl-share-icon" style="background-color:#0b77b3;">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" role="presentation" viewBox="0 0 27.99 27.99"><path d="M6.37 11.33h3.25v10.45H6.37zM8 6.13A1.88 1.88 0 116.12 8 1.88 1.88 0 018 6.13M11.65 11.33h3.12v1.43h.05a3.4 3.4 0 013.07-1.69c3.29 0 3.9 2.17 3.9 5v5.73h-3.25v-5.11c0-1.21 0-2.77-1.69-2.77s-1.94 1.32-1.94 2.69v5.17h-3.25V11.33z"></path></svg>
</span>
</esl-share-button>
<hr>

<h3>Display list of share buttons of group 'demo'</h3>
<p>Shows buttons from a specified group.</p>
<esl-share-list group="demo"></esl-share-list>
<hr>

<h3>Display list of share buttons of group 'alternative'</h3>
<p>Shows buttons from a specified group.</p>
<esl-share-list group="alternative"></esl-share-list>
<hr>

<h3>Display list of share buttons on nonexistent group</h3>
<p>Shows buttons from a specified group. Displays all buttons from config when a nonexistent group is specified.</p>
<esl-share-list group="foo"></esl-share-list>
<hr>

<h3>Display list of share buttons via list of buttons id</h3>
<p>Shows specified buttons. Displays nothing in the case when specified wrong button id.</p>
<esl-share-list list="kakao,pusha,alladin,mix,myspace"></esl-share-list>
<hr>

<h3>List of share buttons which presents buttons with all action types</h3>
<p>This example shows a button with the native share mechanism on the device which will be inactive on the desktop browser.</p>
<esl-share-list list="copy,linkedin,mail,native-share,print"></esl-share-list>
<hr>

<h3>Display list of share buttons via incorrect list</h3>
<p>Shows buttons only with the correct id.</p>
<esl-share-list list="twittor , copi"></esl-share-list>
<hr>

<h3>Display list of share buttons (without group or list)</h3>
<p>Shows all buttons from config.</p>
<esl-share-list data-share-url="https://esl-ui.com/" data-share-title="Exadel Smart Library"></esl-share-list>
<hr>

<h3>Trigger icon with share popup</h3>
<p>Displays trigger icon. Hovering or clicking on the icon shows a popup with the list of share icons.</p>
<esl-trigger target="#demo-share-popup" mode="toggle" track-click track-hover style="display: inline-block; width: 40px; height: 40px;">
<svg viewBox="0 0 483 483" fill="none" xmlns="http://www.w3.org/2000/svg">
<g fill="#add1fa">
<path d="M395.72,0c-48.204,0-87.281,39.078-87.281,87.281c0,2.036,0.164,4.03,0.309,6.029l-161.233,75.674 c-15.668-14.971-36.852-24.215-60.231-24.215c-48.204,0.001-87.282,39.079-87.282,87.282c0,48.204,39.078,87.281,87.281,87.281 c15.206,0,29.501-3.907,41.948-10.741l69.789,58.806c-3.056,8.896-4.789,18.396-4.789,28.322c0,48.204,39.078,87.281,87.281,87.281 c48.205,0,87.281-39.078,87.281-87.281s-39.077-87.281-87.281-87.281c-15.205,0-29.5,3.908-41.949,10.74l-69.788-58.805 c3.057-8.891,4.789-18.396,4.789-28.322c0-2.035-0.164-4.024-0.308-6.029l161.232-75.674c15.668,14.971,36.852,24.215,60.23,24.215 c48.203,0,87.281-39.078,87.281-87.281C482.999,39.079,443.923,0,395.72,0z"/>
</g>
</svg>
</esl-trigger>
<esl-popup id="demo-share-popup" position="top" default-params="{hideDelay: 500}" close-on-esc close-on-outside-action>
<esl-share-list group="alternative" style="margin: 10px;"></esl-share-list><span class="esl-popup-arrow"></span>
</esl-popup>
</section>
2 changes: 2 additions & 0 deletions src/modules/all.less
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@
@import "./esl-tooltip/core.less";

@import "./esl-animate/core.less";

@import "./esl-share/core.less";
3 changes: 3 additions & 0 deletions src/modules/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@ export * from './esl-animate/core';

// Related Target Mixin
export * from './esl-related-target/core';

// Share
export * from './esl-share/core';
15 changes: 15 additions & 0 deletions src/modules/esl-share/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# [ESL](../../../) Share

Version: *1.0.0-beta*.

Authors: *Dmytro Shovchko*.

***Important Notice: the component is under beta version, it is tested and ready to use but be aware of its potential critical API changes.***

<a name="intro"></a>

The ESL Share component allows the user to share the page on social media platforms.

**ESLShareButton** is a custom element that is used to show the "Share on social media" button.

**ESLShareList** is a custom element that is used to show the list of social media buttons.
31 changes: 31 additions & 0 deletions src/modules/esl-share/actions/copy-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {ESLShareBaseAction} from '../core/esl-share-action';
import {ESLEventUtils} from '../../esl-utils/dom/events';

import type {ESLShareButton} from '../core/esl-share-button';

@ESLShareBaseAction.register
export class ESLShareCopyAction extends ESLShareBaseAction {
public static override readonly is: string = 'copy';

public override get isAvailable(): boolean {
return navigator.clipboard !== undefined;
}

public share($button: ESLShareButton): void {
const shareData = this.getShareData($button);
const {url} = shareData;
if (!this.isAvailable || !url) return;

navigator.clipboard.writeText(url);
this.showCopyAlert($button.additional?.alertText);
}

protected showCopyAlert(alertText: string): void {
if (!alertText) return;
const detail = {
cls: 'esl-share-alert',
html: `<span>${alertText}</span>`
};
ESLEventUtils.dispatch(document.body, 'esl:alert:show', {detail});
}
}
18 changes: 18 additions & 0 deletions src/modules/esl-share/actions/external-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {ESLShareUrlGenericAction} from './url-generic-action';

import type {ESLShareButton} from '../core/esl-share-button';

@ESLShareUrlGenericAction.register
export class ESLShareExternalAction extends ESLShareUrlGenericAction {
public static override readonly is: string = 'external';

public share($button: ESLShareButton): void {
const {link} = $button;
if (!link) return;

const shareData = this.getShareData($button);
const a = document.createElement('a');
a.href = this.buildURL(link, shareData);
a.click();
}
}
33 changes: 33 additions & 0 deletions src/modules/esl-share/actions/media-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {ESLShareUrlGenericAction} from './url-generic-action';

import type {ESLShareButton} from '../core/esl-share-button';

@ESLShareUrlGenericAction.register
export class ESLShareMediaAction extends ESLShareUrlGenericAction {
public static override readonly is: string = 'media';

public static FEATURES: Record<string, number> = {
scrollbars: 0,
resizable: 1,
menubar: 0,
left: 100,
top: 100,
width: 750,
height: 500,
toolbar: 0,
status: 0
};

protected get windowFeatures(): string {
const features = (this.constructor as typeof ESLShareMediaAction).FEATURES;
return Object.entries(features).map(([key, value]) => `${key}=${value}`).join(',');
}

public share($button: ESLShareButton): void {
const {link} = $button;
if (!link) return;

const shareData = this.getShareData($button);
window.open(this.buildURL(link, shareData), '_blank', this.windowFeatures);
}
}
19 changes: 19 additions & 0 deletions src/modules/esl-share/actions/native-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {ESLShareBaseAction} from '../core/esl-share-action';

import type {ESLShareButton} from '../core/esl-share-button';

@ESLShareBaseAction.register
export class ESLShareNativeAction extends ESLShareBaseAction {
public static override readonly is: string = 'native';

public override get isAvailable(): boolean {
return navigator.share !== undefined;
}

public share($button: ESLShareButton): void {
if (!this.isAvailable) return;

const shareData = this.getShareData($button);
navigator.share(shareData);
}
}
12 changes: 12 additions & 0 deletions src/modules/esl-share/actions/print-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {ESLShareBaseAction} from '../core/esl-share-action';

import type {ESLShareButton} from '../core/esl-share-button';

@ESLShareBaseAction.register
export class ESLSharePrintAction extends ESLShareBaseAction {
public static override readonly is: string = 'print';

public share($button: ESLShareButton): void {
window.print();
}
}
20 changes: 20 additions & 0 deletions src/modules/esl-share/actions/url-generic-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {ESLShareBaseAction} from '../core/esl-share-action';
import {format} from '../../esl-utils/misc/format';

export abstract class ESLShareUrlGenericAction extends ESLShareBaseAction {

protected getFormatSource(shareData: ShareData): Record<string, string> {
const title = encodeURIComponent(shareData.title || '');
const url = encodeURIComponent(shareData.url || '');
return {
u: url,
t: title,
url,
title
};
}

protected buildURL(link: string, shareData: ShareData): string {
return format(link, this.getFormatSource(shareData));
}
}
2 changes: 2 additions & 0 deletions src/modules/esl-share/core.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@import "./core/esl-share-button.less";
@import "./core/esl-share-list.less";
4 changes: 4 additions & 0 deletions src/modules/esl-share/core.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './core/esl-share-list';
export * from './core/esl-share-action';
export * from './core/esl-share-action-registry';
export * from './core/esl-share-button';
36 changes: 36 additions & 0 deletions src/modules/esl-share/core/esl-share-action-registry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {memoize} from '../../esl-utils/decorators';

import type {ActionType, ESLShareBaseAction} from './esl-share-action';
import type {ESLShareButton} from './esl-share-button';

export class ESLShareActionRegistry {
private actionsMap: Map<string, ESLShareBaseAction> = new Map();

@memoize()
public static get instance(): ESLShareActionRegistry {
return new ESLShareActionRegistry();
}

/** Register action */
public register(action: ActionType): void {
if (!action.is) throw new Error('Action should have a name');
this.actionsMap.set(action.is, new action());
}

/** Checks if action is registered for passed name */
public has(name: string): boolean {
return this.actionsMap.has(name);
}

/** Get action by name */
public get(name: string): ESLShareBaseAction | null {
if (!name) return null;
return this.actionsMap.get(name.toLowerCase()) || null;
}

/** Do the share action at passed Share button */
public share(button: ESLShareButton): void {
const action = this.get(button.action);
action && action.share(button);
}
}
32 changes: 32 additions & 0 deletions src/modules/esl-share/core/esl-share-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {ESLShareActionRegistry} from './esl-share-action-registry';

import type {ESLShareButton} from './esl-share-button';

export type ActionType = (new() => ESLShareBaseAction) & typeof ESLShareBaseAction;

export abstract class ESLShareBaseAction {
public static readonly is: string;

/** Register this action. Can be used as a decorator */
public static register(this: ActionType): void;
public static register(this: unknown, action?: ActionType): void;
public static register(this: any, action?: ActionType): void {
action = action || this;
if (action === ESLShareBaseAction) throw new Error('`ESLShareBaseAction` can\'t be registered.');
if (!(action?.prototype instanceof ESLShareBaseAction)) throw new Error('Action should be instanceof `ESLShareBaseAction`');
ESLShareActionRegistry.instance.register(action);
}

public get isAvailable(): boolean {
return true;
}

protected getShareData($button: ESLShareButton): ShareData {
return {
url: $button.urlToShare,
title: $button.titleToShare
};
}

public abstract share($button: ESLShareButton): void;
}
Loading

0 comments on commit a11db42

Please sign in to comment.