UNPKG

4.08 kBJavaScriptView Raw
1import Emitter from 'tiny-emitter';
2import listen from 'good-listener';
3import ClipboardActionDefault from './actions/default';
4import ClipboardActionCut from './actions/cut';
5import ClipboardActionCopy from './actions/copy';
6
7/**
8 * Helper function to retrieve attribute value.
9 * @param {String} suffix
10 * @param {Element} element
11 */
12function getAttributeValue(suffix, element) {
13 const attribute = `data-clipboard-${suffix}`;
14
15 if (!element.hasAttribute(attribute)) {
16 return;
17 }
18
19 return element.getAttribute(attribute);
20}
21
22/**
23 * Base class which takes one or more elements, adds event listeners to them,
24 * and instantiates a new `ClipboardAction` on each click.
25 */
26class Clipboard extends Emitter {
27 /**
28 * @param {String|HTMLElement|HTMLCollection|NodeList} trigger
29 * @param {Object} options
30 */
31 constructor(trigger, options) {
32 super();
33
34 this.resolveOptions(options);
35 this.listenClick(trigger);
36 }
37
38 /**
39 * Defines if attributes would be resolved using internal setter functions
40 * or custom functions that were passed in the constructor.
41 * @param {Object} options
42 */
43 resolveOptions(options = {}) {
44 this.action =
45 typeof options.action === 'function'
46 ? options.action
47 : this.defaultAction;
48 this.target =
49 typeof options.target === 'function'
50 ? options.target
51 : this.defaultTarget;
52 this.text =
53 typeof options.text === 'function' ? options.text : this.defaultText;
54 this.container =
55 typeof options.container === 'object' ? options.container : document.body;
56 }
57
58 /**
59 * Adds a click event listener to the passed trigger.
60 * @param {String|HTMLElement|HTMLCollection|NodeList} trigger
61 */
62 listenClick(trigger) {
63 this.listener = listen(trigger, 'click', (e) => this.onClick(e));
64 }
65
66 /**
67 * Defines a new `ClipboardAction` on each click event.
68 * @param {Event} e
69 */
70 onClick(e) {
71 const trigger = e.delegateTarget || e.currentTarget;
72 const action = this.action(trigger) || 'copy';
73 const text = ClipboardActionDefault({
74 action,
75 container: this.container,
76 target: this.target(trigger),
77 text: this.text(trigger),
78 });
79
80 // Fires an event based on the copy operation result.
81 this.emit(text ? 'success' : 'error', {
82 action,
83 text,
84 trigger,
85 clearSelection() {
86 if (trigger) {
87 trigger.focus();
88 }
89 window.getSelection().removeAllRanges();
90 },
91 });
92 }
93
94 /**
95 * Default `action` lookup function.
96 * @param {Element} trigger
97 */
98 defaultAction(trigger) {
99 return getAttributeValue('action', trigger);
100 }
101
102 /**
103 * Default `target` lookup function.
104 * @param {Element} trigger
105 */
106 defaultTarget(trigger) {
107 const selector = getAttributeValue('target', trigger);
108
109 if (selector) {
110 return document.querySelector(selector);
111 }
112 }
113
114 /**
115 * Allow fire programmatically a copy action
116 * @param {String|HTMLElement} target
117 * @param {Object} options
118 * @returns Text copied.
119 */
120 static copy(target, options = { container: document.body }) {
121 return ClipboardActionCopy(target, options);
122 }
123
124 /**
125 * Allow fire programmatically a cut action
126 * @param {String|HTMLElement} target
127 * @returns Text cutted.
128 */
129 static cut(target) {
130 return ClipboardActionCut(target);
131 }
132
133 /**
134 * Returns the support of the given action, or all actions if no action is
135 * given.
136 * @param {String} [action]
137 */
138 static isSupported(action = ['copy', 'cut']) {
139 const actions = typeof action === 'string' ? [action] : action;
140 let support = !!document.queryCommandSupported;
141
142 actions.forEach((action) => {
143 support = support && !!document.queryCommandSupported(action);
144 });
145
146 return support;
147 }
148
149 /**
150 * Default `text` lookup function.
151 * @param {Element} trigger
152 */
153 defaultText(trigger) {
154 return getAttributeValue('text', trigger);
155 }
156
157 /**
158 * Destroy lifecycle.
159 */
160 destroy() {
161 this.listener.destroy();
162 }
163}
164
165export default Clipboard;