Skip to content

Commit 7641286

Browse files
warrenbuckleyiOvergaardnielslyngsoe
authored
feat: Adds uui-button-copy-text (#985)
* Adds scaffolding files for uui-copy npm run new-package * Adds first attempt at uui-copy * Make eslint happy * WIP: To add useful tests * Update packages/uui-copy/lib/uui-copy.element.ts Co-authored-by: Jacob Overgaard <[email protected]> * Update packages/uui-copy/lib/uui-copy.element.ts Co-authored-by: Jacob Overgaard <[email protected]> * Remove the reflect on the props as PR suggestion * Adds in the util func demandCustomElement in ctor as suggested by Jacob * Fix typo * Remove the tests for the clipboard API - agreed with Jacob to keep the tests minimal due to the permisions problem with the clipboard API running headless * Waits 2 seconds before setting uuiButton state to success * Fix the problem with the story, as you needed to browse to uui-button before this would work properly as it had not registered the webcoponents that it depends upon * Adds the LabelMixin to add the prop. Flows the label prop down into the UUI-button for the aria-label for the uui-button used inside uui-copy * Make the sonar code quality thing a bit happier * Add a prop & story for the timeout delay for the animation/state of the button * An explicit story to help with docs to show how to use a different icon * Fix test failing on CI only - not locally * Rename from uui-copy to more explicit uui-text-copy-button * Renaming files & folders was bein a PITA and things not working 🤬 So had to stub it out again with npm run new-package * FIx problem from test * Remove console.log * Wrap with try/catch & drop the uneeded .then() * Removes the event from bubbling, not sure there is a valid reason to * feat: rename element to `uui-button-copy-text` * feat: rename to uui-button-copy-text and add dependencies * feat: rename to uui-button-copy-text and add dependencies * feat: uui-icon package is optional * feat: uui-icon package is optional * feat: extend from uui-button and rename `value` to `text` * feat: copy only from other element if there is no 'value' field * feat: use real class property to hold copied value * feat: clear animation timer if element disconnects * feat: rename base folder * chore: format lint * docs: update README * chore: cleanup old files --------- Co-authored-by: Jacob Overgaard <[email protected]> Co-authored-by: Niels Lyngsø <[email protected]>
1 parent 0291215 commit 7641286

File tree

11 files changed

+553
-0
lines changed

11 files changed

+553
-0
lines changed

package-lock.json

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# uui-button-copy-text
2+
3+
![npm](https://img.shields.io/npm/v/@umbraco-ui/uui-button-copy-text?logoColor=%231B264F)
4+
5+
Umbraco style text-copy component.
6+
7+
## Installation
8+
9+
### ES imports
10+
11+
```zsh
12+
npm i @umbraco-ui/uui-button-copy-text
13+
```
14+
15+
Import the registration of `<uui-button-copy-text>` via:
16+
17+
```javascript
18+
import '@umbraco-ui/uui-button-copy-text';
19+
```
20+
21+
When looking to leverage the `UUIButtonCopyTextElement` base class as a type and/or for extension purposes, do so via:
22+
23+
```javascript
24+
import { UUIButtonCopyTextElement } from '@umbraco-ui/uui-button-copy-text';
25+
```
26+
27+
## Usage
28+
29+
```html
30+
<uui-button-copy-text></uui-button-copy-text>
31+
```
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { UUIEvent } from '@umbraco-ui/uui-base/lib/events';
2+
import { UUIButtonCopyTextElement } from './uui-button-copy-text.element';
3+
4+
export class UUICopyTextEvent extends UUIEvent<
5+
{ text: string },
6+
UUIButtonCopyTextElement
7+
> {
8+
public static readonly COPIED: string = 'copied';
9+
public static readonly COPYING: string = 'copying';
10+
11+
/**
12+
* The text content that is about to be copied to the clipboard
13+
*/
14+
public text: string | null = null;
15+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './uui-button-copy-text.element';
2+
export * from './UUICopyTextEvent';
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { html } from 'lit';
2+
import { property } from 'lit/decorators.js';
3+
import { defineElement } from '@umbraco-ui/uui-base/lib/registration';
4+
import { demandCustomElement } from '@umbraco-ui/uui-base/lib/utils';
5+
import { UUIButtonElement } from '@umbraco-ui/uui-button/lib';
6+
import { UUICopyTextEvent } from './UUICopyTextEvent.js';
7+
8+
/**
9+
* @summary A button to trigger text content to be copied to the clipboard
10+
* @element uui-button-copy-text
11+
* @dependency uui-button
12+
* @dependency uui-icon
13+
* @fires {UUICopyTextEvent} copying - Fires before the content is about to copied to the clipboard and can be used to transform or modify the data before its added to the clipboard
14+
* @fires {UUICopyTextEvent} copied - Fires when the content is copied to the clipboard
15+
* @slot - Use to replace the default content of the copy icon
16+
*/
17+
@defineElement('uui-button-copy-text')
18+
export class UUIButtonCopyTextElement extends UUIButtonElement {
19+
/**
20+
* Set a string you wish to copy to the clipboard
21+
* @type {string}
22+
* @default ''
23+
*/
24+
@property({ type: String })
25+
text: string = '';
26+
27+
/**
28+
* Copies the text content from another element by specifying the ID of the element
29+
* The ID of the element does not need to start with # like a CSS selector
30+
* If this property is set, the value property is ignored
31+
* @type {string}
32+
* @attr
33+
* @default ''
34+
* @example copy-from="element-id"
35+
*/
36+
@property({ type: String, attribute: 'copy-from' })
37+
copyFrom: string = '';
38+
39+
/**
40+
* The delay in milliseconds before the button returns to its default state after a successful copy
41+
* @type {number}
42+
* @attr
43+
* @default 250
44+
*/
45+
@property({ type: Number, attribute: 'animation-state-delay' })
46+
animationStateDelay: number = 250;
47+
48+
#animationTimer?: any;
49+
50+
constructor() {
51+
super();
52+
demandCustomElement(this, 'uui-icon');
53+
54+
this.addEventListener('click', this.#onClick);
55+
}
56+
57+
disconnectedCallback(): void {
58+
super.disconnectedCallback();
59+
if (this.#animationTimer) {
60+
clearTimeout(this.#animationTimer);
61+
}
62+
}
63+
64+
readonly #onClick = async () => {
65+
this.state = 'waiting';
66+
67+
// By default use the value property
68+
let valueToCopy = this.text;
69+
70+
// If copy-from is set use that instead
71+
if (this.copyFrom) {
72+
// Try & find an element with the ID
73+
const el = document.getElementById(this.copyFrom);
74+
if (el) {
75+
// Override the value to copy, if the element has a value property
76+
// Such as uui-input or uui-textarea or native inout elements
77+
if ('value' in el) {
78+
valueToCopy = (el as any).value;
79+
} else {
80+
valueToCopy = el.textContent ?? el.innerText ?? '';
81+
}
82+
} else {
83+
console.error(`Element ID ${this.copyFrom} not found to copy from`);
84+
this.state = 'failed';
85+
return;
86+
}
87+
}
88+
89+
const beforeCopyEv = new UUICopyTextEvent(UUICopyTextEvent.COPYING);
90+
beforeCopyEv.text = valueToCopy;
91+
this.dispatchEvent(beforeCopyEv);
92+
93+
if (beforeCopyEv.text != null) {
94+
valueToCopy = beforeCopyEv.text;
95+
}
96+
97+
try {
98+
await navigator.clipboard.writeText(valueToCopy);
99+
const copiedEv = new UUICopyTextEvent(UUICopyTextEvent.COPIED);
100+
copiedEv.text = valueToCopy;
101+
this.dispatchEvent(copiedEv);
102+
this.#animationTimer = setTimeout(() => {
103+
this.state = 'success';
104+
}, this.animationStateDelay);
105+
} catch (err) {
106+
this.state = 'failed';
107+
console.error('Error copying to clipboard', err);
108+
}
109+
};
110+
111+
protected override renderLabel() {
112+
return html`
113+
<slot class="label">
114+
<uui-icon name="copy"></uui-icon>
115+
</slot>
116+
`;
117+
}
118+
119+
static override readonly styles = UUIButtonElement.styles;
120+
}
121+
122+
declare global {
123+
interface HTMLElementTagNameMap {
124+
'uui-button-copy-text': UUIButtonCopyTextElement;
125+
}
126+
}

0 commit comments

Comments
 (0)