Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds UUI-Text-Copy #985

Open
wants to merge 24 commits into
base: v1/contrib
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
380f454
Adds scaffolding files for uui-copy
warrenbuckley Dec 16, 2024
9620a66
Adds first attempt at uui-copy
warrenbuckley Dec 23, 2024
8bffab2
Make eslint happy
warrenbuckley Dec 24, 2024
0901ebe
WIP: To add useful tests
warrenbuckley Jan 2, 2025
75aeaa1
Update packages/uui-copy/lib/uui-copy.element.ts
warrenbuckley Jan 7, 2025
54943f7
Update packages/uui-copy/lib/uui-copy.element.ts
warrenbuckley Jan 7, 2025
474a352
Remove the reflect on the props as PR suggestion
warrenbuckley Jan 7, 2025
3b0bd2c
Merge branch 'feature/uui-copy' of https://github.com/warrenbuckley/U…
warrenbuckley Jan 7, 2025
a291199
Adds in the util func demandCustomElement in ctor as suggested by Jacob
warrenbuckley Jan 7, 2025
e46e459
Fix typo
warrenbuckley Jan 7, 2025
de55e9b
Remove the tests for the clipboard API - agreed with Jacob to keep th…
warrenbuckley Jan 7, 2025
9ca79f3
Waits 2 seconds before setting uuiButton state to success
warrenbuckley Jan 7, 2025
3c1fc10
Fix the problem with the story, as you needed to browse to uui-button…
warrenbuckley Jan 7, 2025
65a4e60
Adds the LabelMixin to add the prop.
warrenbuckley Jan 7, 2025
c72d0b7
Make the sonar code quality thing a bit happier
warrenbuckley Jan 7, 2025
c371aa8
Add a prop & story for the timeout delay for the animation/state of t…
warrenbuckley Jan 7, 2025
dffdef4
An explicit story to help with docs to show how to use a different icon
warrenbuckley Jan 7, 2025
bf86ac4
Fix test failing on CI only - not locally
warrenbuckley Jan 7, 2025
8e2bb32
Rename from uui-copy to more explicit uui-text-copy-button
warrenbuckley Jan 10, 2025
fbe484f
Merge branch 'umbraco:v1/contrib' into feature/uui-copy
warrenbuckley Jan 10, 2025
5e6134f
Renaming files & folders was bein a PITA and things not working 🤬
warrenbuckley Jan 12, 2025
9a4ed42
FIx problem from test
warrenbuckley Jan 13, 2025
2a8db77
Remove console.log
warrenbuckley Jan 15, 2025
ac50a35
Wrap with try/catch & drop the uneeded .then()
warrenbuckley Jan 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions packages/uui-text-copy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# uui-text-copy

![npm](https://img.shields.io/npm/v/@umbraco-ui/uui-text-copy?logoColor=%231B264F)

Umbraco style text-copy component.

## Installation

### ES imports

```zsh
npm i @umbraco-ui/uui-text-copy
```

Import the registration of `<uui-text-copy>` via:

```javascript
import '@umbraco-ui/uui-text-copy';
```

When looking to leverage the `UUITextCopyElement` base class as a type and/or for extension purposes, do so via:

```javascript
import { UUITextCopyElement } from '@umbraco-ui/uui-text-copy';
```

## Usage

```html
<uui-text-copy></uui-text-copy>
```
21 changes: 21 additions & 0 deletions packages/uui-text-copy/lib/UUITextCopyEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { UUIEvent } from '@umbraco-ui/uui-base/lib/events';
import { UUITextCopyElement } from './uui-text-copy.element';

interface UUITextCopyEventInit extends EventInit {
detail?: { text: string };
}

export class UUITextCopyEvent extends UUIEvent<
{ text: string },
UUITextCopyElement
> {
public static readonly COPIED: string = 'copied';
public static readonly COPYING: string = 'copying';

constructor(evName: string, eventInit: UUITextCopyEventInit = {}) {
super(evName, {
...{ bubbles: true },
...eventInit,
});
}
}
2 changes: 2 additions & 0 deletions packages/uui-text-copy/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './uui-text-copy.element';
export * from './UUITextCopyEvent';
175 changes: 175 additions & 0 deletions packages/uui-text-copy/lib/uui-text-copy.element.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import { css, html, LitElement } from 'lit';
import { property } from 'lit/decorators.js';
import { defineElement } from '@umbraco-ui/uui-base/lib/registration';
import { demandCustomElement } from '@umbraco-ui/uui-base/lib/utils';
import { UUIButtonElement } from '@umbraco-ui/uui-button/lib';
import { UUIInterfaceColor, UUIInterfaceLook } from '@umbraco-ui/uui-base';
import { LabelMixin } from '@umbraco-ui/uui-base/lib/mixins';
import { UUITextCopyEvent } from './UUITextCopyEvent';

/**
* @summary A button to trigger text content to be copied to the clipboard
* @element uui-text-copy
* @dependency uui-button
* @dependency uui-icon
* @fires {UUITextCopyEvent} 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
* @fires {UUITextCopyEvent} copied - Fires when the content is copied to the clipboard
* @slot - Use to replace the default content of 'Copy' and the copy icon
*/
@defineElement('uui-text-copy')
export class UUITextCopyElement extends LabelMixin('', LitElement) {
/**
* Set a string you wish to copy to the clipboard
* @type {string}
* @default ''
*/
@property({ type: String })
value = '';

/**
* Disables the button
* @type {boolean}
* @attr
* @default false
*/
@property({ type: Boolean })
disabled = false;

/**
* Copies the text content from another element by specifying the ID of the element
* The ID of the element does not need to start with # like a CSS selector
* If this property is set, the value property is ignored
* @type {string}
* @attr
* @default ''
* @example copy-from="element-id"
*/
@property({ type: String, attribute: 'copy-from' })
copyFrom = '';

/**
* Changes the look of the button to one of the predefined, symbolic looks.
* @type {"default" | "primary" | "secondary" | "outline" | "placeholder"}
* @attr
* @default "default"
*/
@property()
look: UUIInterfaceLook = 'default';

/**
* Changes the look of the button to one of the predefined, symbolic looks. For example - set this to positive if you want nice, green "confirm" button.
* @type {"default" | "positive" | "warning" | "danger"}
* @attr
* @default "default"
*/
@property({ reflect: true })
color: UUIInterfaceColor = 'default';

/**
* Makes the left and right padding of the button narrower.
* @type {boolean}
* @attr
* @default false
*/
@property({ type: Boolean })
compact = false;

/**
* The delay in milliseconds before the button returns to its default state after a successful copy
* @type {number}
* @attr
* @default 250
*/
@property({ type: Number, attribute: 'animation-state-delay' })
animationStateDelay = 250;

constructor() {
super();
demandCustomElement(this, 'uui-button');
demandCustomElement(this, 'uui-icon');
}

// Used to store the value that will be copied to the clipboard
#valueToCopy = '';

readonly #onClick = async (e: Event) => {
const button = e.target as UUIButtonElement;
button.state = 'waiting';

// By default use the value property
this.#valueToCopy = this.value;

// If copy-from is set use that instead
if (this.copyFrom) {
// Try & find an element with the ID
const el = document.getElementById(this.copyFrom);
if (el) {
console.log('Element found to copy from', el);
this.#valueToCopy = el.textContent ?? el.innerText ?? '';

// Override the value to copy ,if the element has a value property
// Such as uui-input or uui-textarea or native inout elements
if ('value' in el) {
console.log('This element has a value property', el);
this.#valueToCopy = (el as any).value;
}
} else {
console.error(`Element ID ${this.copyFrom} not found to copy from`);
warrenbuckley marked this conversation as resolved.
Show resolved Hide resolved
button.state = 'failed';
return;
}
}

const beforeCopyEv = new UUITextCopyEvent(UUITextCopyEvent.COPYING, {
detail: { text: this.#valueToCopy },
});
this.dispatchEvent(beforeCopyEv);

if (beforeCopyEv.detail.text != null) {
this.#valueToCopy = beforeCopyEv.detail.text;
}

await navigator.clipboard
.writeText(this.#valueToCopy)
.then(() => {
this.dispatchEvent(
new UUITextCopyEvent(UUITextCopyEvent.COPIED, {
detail: { text: this.#valueToCopy },
}),
);
setTimeout(() => {
button.state = 'success';
}, this.animationStateDelay);
})
.catch(err => {
button.state = 'failed';
console.error('Error copying to clipboard', err);
});
warrenbuckley marked this conversation as resolved.
Show resolved Hide resolved
};

render() {
return html` <uui-button
.color=${this.color}
.look=${this.look}
.disabled=${this.disabled}
.compact=${this.compact}
.label=${this.label}
@click=${this.#onClick}>
<slot> <uui-icon name="copy"></uui-icon> Copy </slot>
</uui-button>`;
}

static readonly styles = [
css`
slot {
pointer-events: none;
}
`,
];
}

declare global {
interface HTMLElementTagNameMap {
'uui-text-copy': UUITextCopyElement;
}
}
Loading
Loading