copy button updates

pull/1483/head
Cory LaViska 2023-08-10 18:28:17 -04:00
rodzic d6f9618814
commit 2e6133d4d8
17 zmienionych plików z 575 dodań i 392 usunięć

Wyświetl plik

@ -0,0 +1,252 @@
---
meta:
title: Copy Button
description: Copies data to the clipboard when the user clicks the button.
layout: component
---
```html:preview
<sl-copy-button value="Shoelace rocks!"></sl-copy-button>
```
```jsx:react
import { SlCopyButton } from '@shoelace-style/shoelace/dist/react/sl-copy-button';
const App = () => (
<SlCopyButton value="Shoelace rocks!" />
);
```
## Examples
### Custom Labels
Copy Buttons display feedback labels in a tooltip. You can change the labels by setting the `copy-label`, `success-label`, and `error-label` attributes.
```html:preview
<sl-copy-button
value="Custom labels are easy"
copy-label="Click to copy"
success-label="You did it!"
error-label="Whoops, your browser doesn't support this!"
></sl-copy-button>
```
```jsx:react
import { SlCopyButton } from '@shoelace-style/shoelace/dist/react/sl-copy-button';
const App = () => (
<SlCopyButton
value="Custom labels are easy"
copy-label="Click to copy"
success-label="You did it!"
error-label="Whoops, your browser doesn't support this!"
/>
);
```
### Custom Icons
Use the `copy-icon`, `success-icon`, and `error-icon` slots to customize the icons that get displayed for each state. You can use [`<sl-icon>`](/components/icon) or your own images.
```html:preview
<sl-copy-button value="Copied from a custom button">
<sl-icon slot="copy-icon" name="clipboard"></sl-icon>
<sl-icon slot="success-icon" name="clipboard-check"></sl-icon>
<sl-icon slot="error-icon" name="clipboard-x"></sl-icon>
</sl-copy-button>
```
```jsx:react
import { SlCopyButton } from '@shoelace-style/shoelace/dist/react/sl-copy-button';
import { SlIcon } from '@shoelace-style/shoelace/dist/react/sl-icon';
const App = () => (
<>
<SlCopyButton value="Copied from a custom button">
<SlIcon slot="copy-icon" name="clipboard" />
<SlIcon slot="success-icon" name="clipboard-check" />
<SlIcon slot="error-icon" name="clipboard-x" />
</SlCopyButton>
</>
);
```
### Copying Values From Other Elements
Normally, the data that gets copied will come from the component's `value` attribute, but you can copy data from any element within the same document by providing its `id` to the `from` attribute.
When using the `from` attribute, the element's [`textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) will be copied by default. Passing an attribute or property modifier will let you copy data from one of the element's attributes or properties instead.
To copy data from an attribute, use `from="id[attr]"` where `id` is the id of the target element and `attr` is the name of the attribute you'd like to copy. To copy data from a property, use `from="id.prop"` where `id` is the id of the target element and `prop` is the name of the property you'd like to copy.
```html:preview
<!-- Copies the span's textContent -->
<span id="my-phone">+1 (234) 456-7890</span>
<sl-copy-button from="my-phone"></sl-copy-button>
<br><br>
<!-- Copies the input's "value" property -->
<sl-input id="my-input" type="text" value="User input" style="display: inline-block; max-width: 300px;"></sl-input>
<sl-copy-button from="my-input.value"></sl-copy-button>
<br><br>
<!-- Copies the link's "href" attribute -->
<a id="my-link" href="https://shoelace.style/">Shoelace Website</a>
<sl-copy-button from="my-link[href]"></sl-copy-button>
```
```jsx:react
import { SlCopyButton } from '@shoelace-style/shoelace/dist/react/sl-copy-button';
import { SlInput } from '@shoelace-style/shoelace/dist/react/sl-input';
const App = () => (
<>
{/* Copies the span's textContent */}
<span id="my-phone">+1 (234) 456-7890</span>
<SlCopyButton from="my-phone" />
<br /><br />
{/* Copies the input's "value" property */}
<SlInput id="my-input" type="text" />
<SlCopyButton from="my-input.value" />
<br /><br />
{/* Copies the link's "href" attribute */}
<a id="my-link" href="https://shoelace.style/">Shoelace Website</a>
<SlCopyButton from="my-link[href]" />
</>
);
```
### Handling Errors
Copy errors occur if the value is an empty string, if the `from` attribute points to an id that doesn't exist, or if the browser rejects the operation for any reason. This example shows what happens when a copy error occurs.
```html:preview
<sl-copy-button from="i-do-not-exist"></sl-copy-button>
```
```jsx:react
import { SlCopyButton } from '@shoelace-style/shoelace/dist/react/sl-copy-button';
const App = () => (
<SlCopyButton from="i-do-not-exist" />
);
```
### Disabled
Copy buttons can be disabled by adding the `disabled` attribute.
```html:preview
<sl-copy-button value="You can't copy me" disabled></sl-copy-button>
```
```jsx:react
import { SlCopyButton } from '@shoelace-style/shoelace/dist/react/sl-copy-button';
const App = () => (
<SlCopyButton value="You can't copy me" disabled />
);
```
### Changing Feedback Duration
A success indicator is briefly shown after copying. You can customize the length of time the indicator is shown using the `feedback-duration` attribute.
```html:preview
<sl-copy-button value="Shoelace rocks!" feedback-duration="250"></sl-copy-button>
```
```jsx:react
import { SlCopyButton } from '@shoelace-style/shoelace/dist/react/sl-copy-button';
const App = () => (
<SlCopyButton value="Shoelace rocks!" feedback-duration={250} />
);
```
### Custom Styles
You can customize the button to your liking with CSS.
```html:preview
<sl-copy-button value="I'm so stylish" class="custom-styles"></sl-copy-button>
<style>
.custom-styles {
--success-color: white;
--error-color: white;
color: white;
}
.custom-styles::part(button) {
background-color: #ff1493;
border: solid 4px #ff7ac1;
border-right-color: #ad005c;
border-bottom-color: #ad005c;
border-radius: 0;
transition: 100ms scale ease-in-out, 100ms translate ease-in-out;
}
.custom-styles::part(button):hover {
scale: 1.1;
}
.custom-styles::part(button):active {
translate: 0 2px;
}
.custom-styles::part(button):focus-visible {
outline: dashed 2px deeppink;
outline-offset: 4px;
}
</style>
```
```jsx:react
import { SlCopyButton } from '@shoelace-style/shoelace/dist/react/sl-copy-button';
const css = `
.custom-styles {
--success-color: white;
--error-color: white;
color: white;
}
.custom-styles::part(button) {
background-color: #ff1493;
border: solid 4px #ff7ac1;
border-right-color: #ad005c;
border-bottom-color: #ad005c;
border-radius: 0;
transition: 100ms scale ease-in-out, 100ms translate ease-in-out;
}
.custom-styles::part(button):hover {
scale: 1.1;
}
.custom-styles::part(button):active {
translate: 0 2px;
}
.custom-styles::part(button):focus-visible {
outline: dashed 2px deeppink;
outline-offset: 4px;
}
`;
const App = () => (
<>
<SlCopyButton value="I'm so stylish" className="custom-styles" />
<style>{css}</style>
</>
);
```

Wyświetl plik

@ -1,156 +0,0 @@
---
meta:
title: Copy
description: Copies data to the clipboard when the user clicks or taps the trigger.
layout: component
---
```html:preview
<sl-copy value="Shoelace rocks!"></sl-copy>
```
```jsx:react
import { SlCopy } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlCopy value="Shoelace rocks!"></SlCopy>
);
```
## Examples
### Custom Buttons
Use the default slot to customize the copy trigger. You can also customize the success and error messages using the respective slots.
```html:preview
<sl-copy value="Copied from a custom button" class="custom-buttons">
<sl-button>Copy</sl-button>
<sl-button slot="success">Copied!</sl-button>
<sl-button slot="error">Error</sl-button>
</sl-copy>
```
```jsx:react
import { SlButton, SlCopy } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlCopy value="Copied from a custom button">
<SlButton>Copy</SlButton>
<SlButton slot="success">Copied!</SlButton>
<SlButton slot="error">Error</SlButton>
</SlCopy>
</>
);
```
### Copying the Value From Other Elements
By default, the data to copy will come from the `value` attribute. You
```html:preview
<span id="phone-number">+1 (234) 456-7890</span>
<sl-copy from="phone-number"></sl-copy>
<br><br>
<sl-input type="text" value="Just an input" id="my-input" style="display: inline-block; max-width: 300px;"></sl-input>
<sl-copy from="my-input"></sl-copy>
<br><br>
<a href="https://shoelace.style/" id="my-link">Shoelace Website</a>
<sl-copy from="my-link"></sl-copy>
```
```jsx:react
import { SlCopy, SlInput } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<span id="phone-number">+1 (234) 456-7890</span>
<SlCopy from="phone-number" />
<br /><br />
<SlInput type="text" value="Just an input" id="my-input" style="display: inline-block; max-width: 300px;" />
<SlCopy from="my-input" />
<br /><br />
<a href="https://shoelace.style/" id="my-link">Shoelace Website</a>
<SlCopy from="my-link" />
</>
);
```
### Displaying Copy Errors
Copy errors can occur if the value is an empty string, if the `from` attribute points to an id that doesn't exist, or if the browser rejects the operation. You can customize the error that's shown by populating the `error` slot with your own content.
```html:preview
<sl-copy from="not-found"></sl-copy>
<br><br>
<sl-copy from="not-found">
<sl-button>Copy</sl-button>
<sl-button slot="success">Copied</sl-button>
<sl-button slot="error">Error</sl-button>
</sl-copy>
```
```jsx:react
import { SlCopy } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlCopy from="not-found"></SlCopy>
<br /><br />
<SlCopy from="not-found">
<sl-button>Copy</sl-button>
<sl-button slot="success">Copied</sl-button>
<sl-button slot="error">Error</sl-button>
</SlCopy>
</>
);
```
### Showing Tooltips
You can wrap a tooltip around `<sl-copy>` to provide a hint to users.
```html:preview
<sl-tooltip content="Copy to clipboard">
<sl-copy value="Shoelace rocks!"></sl-copy>
</sl-tooltip>
```
```jsx:react
import { SlCopy, SlTooltip } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTooltip content="Copy to clipboard">
<SlCopy value="Shoelace rocks!" />
</SlTooltip>
);
```
### Changing Feedback Duration
A success indicator is briefly shown after copying. You can customize the length of time the indicator is shown using the `feedback-duration` attribute.
```html:preview
<sl-copy value="Shoelace rocks!" feedback-duration="250"></sl-copy>
```
```jsx:react
import { SlCopy } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlCopy value="Shoelace rocks!" feedback-duration={250} />
);
```

Wyświetl plik

@ -14,9 +14,10 @@ New versions of Shoelace are released as-needed and generally occur when a criti
## Next
- Added the `<sl-copy>` component [#1473]
- Added the `<sl-copy-button>` component [#1473]
- Fixed a bug in `<sl-dropdown>` where pressing [[Up]] or [[Down]] when focused on the trigger wouldn't focus the first/last menu items [#1472]
- Improved the behavior of the clear button in `<sl-input>` to prevent the component's width from shifting when toggled [#1496]
- Improved `<sl-tooltip>` to prevent user selection so the tooltip doesn't get highlighted when dragging selections
- Removed `sideEffects` key from `package.json`. Update React docs to use cherry-picking. [#1485]
- Updated Bootstrap Icons to 1.10.5

Wyświetl plik

@ -0,0 +1,234 @@
import { classMap } from 'lit/directives/class-map.js';
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry.js';
import { html } from 'lit';
import { LocalizeController } from '../../utilities/localize.js';
import { property, query, state } from 'lit/decorators.js';
import ShoelaceElement from '../../internal/shoelace-element.js';
import SlIcon from '../icon/icon.component.js';
import SlTooltip from '../tooltip/tooltip.component.js';
import styles from './copy-button.styles.js';
import type { CSSResultGroup } from 'lit';
/**
* @summary Copies text data to the clipboard when the user clicks the trigger.
* @documentation https://shoelace.style/components/copy
* @status experimental
* @since 2.7
*
* @dependency sl-icon
* @dependency sl-tooltip
*
* @event sl-copy - Emitted when the data has been copied.
* @event sl-error - Emitted when the data could not be copied.
*
* @slot copy-icon - The icon to show in the default copy state. Works best with `<sl-icon>`.
* @slot success-icon - The icon to show when the content is copied. Works best with `<sl-icon>`.
* @slot error-icon - The icon to show when a copy error occurs. Works best with `<sl-icon>`.
*
* @csspart button - The internal `<button>` element.
*
* @cssproperty --success-color - The color to use for success feedback.
* @cssproperty --error-color - The color to use for error feedback.
*
* @animation copy.in - The animation to use when feedback icons animate in.
* @animation copy.out - The animation to use when feedback icons animate out.
*/
export default class SlCopyButton extends ShoelaceElement {
static styles: CSSResultGroup = styles;
static dependencies = {
'sl-icon': SlIcon,
'sl-tooltip': SlTooltip
};
private readonly localize = new LocalizeController(this);
@query('slot[name="copy-icon"]') copyIcon: HTMLSlotElement;
@query('slot[name="success-icon"]') successIcon: HTMLSlotElement;
@query('slot[name="error-icon"]') errorIcon: HTMLSlotElement;
@query('sl-tooltip') tooltip: SlTooltip;
@state() isCopying = false;
@state() status: 'rest' | 'success' | 'error' = 'rest';
/** The text value to copy. */
@property() value = '';
/**
* An id that references an element in the same document from which data will be copied. If both this and `value` are
* present, this value will take precedence. By default, the target element's `textContent` will be copied. To copy an
* attribute, append the attribute name wrapped in square brackets, e.g. `from="el[value]"`. To copy a property,
* append a dot and the property name, e.g. `from="el.value"`.
*/
@property() from = '';
/** Disables the copy button. */
@property({ type: Boolean, reflect: true }) disabled = false;
/** A custom label to show in the tooltip. */
@property({ attribute: 'copy-label' }) copyLabel = '';
/** A custom label to show in the tooltip after copying. */
@property({ attribute: 'success-label' }) successLabel = '';
/** A custom label to show in the tooltip when a copy error occurs. */
@property({ attribute: 'error-label' }) errorLabel = '';
/** The length of time to show feedback before restoring the default trigger. */
@property({ attribute: 'feedback-duration', type: Number }) feedbackDuration = 1000;
/** The preferred placement of the tooltip. */
@property({ attribute: 'tooltip-placement' }) tooltipPlacement: 'top' | 'right' | 'bottom' | 'left' = 'top';
private async handleCopy() {
if (this.disabled || this.isCopying) {
return;
}
this.isCopying = true;
// Copy the value by default
let valueToCopy = this.value;
// If an element is specified, copy from that instead
if (this.from) {
const root = this.getRootNode() as ShadowRoot | Document;
// Simple way to parse ids, properties, and attributes
const isProperty = this.from.includes('.');
const isAttribute = this.from.includes('[');
let id = this.from;
let field = '';
if (isProperty) {
[id, field] = this.from.trim().split('.');
} else if (isAttribute) {
[id, field] = this.from.trim().replace(/\]$/, '').split('[');
}
// Locate the target element by id
const target = 'getElementById' in root ? root.getElementById(id) : null;
if (target) {
if (isAttribute) {
valueToCopy = target.getAttribute(field) || '';
} else if (isProperty) {
// @ts-expect-error - deal with it
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
valueToCopy = target[field] || '';
} else {
valueToCopy = target.textContent || '';
}
} else {
// No target
this.showStatus('error');
this.emit('sl-error');
}
}
// No value
if (!valueToCopy) {
this.showStatus('error');
this.emit('sl-error');
} else {
try {
await navigator.clipboard.writeText(valueToCopy);
this.showStatus('success');
this.emit('sl-copy', {
detail: {
value: valueToCopy
}
});
} catch (error) {
// Rejected by browser
this.showStatus('error');
this.emit('sl-error');
}
}
}
private async showStatus(status: 'success' | 'error') {
const copyLabel = this.copyLabel || this.localize.term('copy');
const successLabel = this.successLabel || this.localize.term('copied');
const errorLabel = this.errorLabel || this.localize.term('error');
const iconToShow = status === 'success' ? this.successIcon : this.errorIcon;
const showAnimation = getAnimation(this, 'copy.in', { dir: 'ltr' });
const hideAnimation = getAnimation(this, 'copy.out', { dir: 'ltr' });
this.tooltip.content = status === 'success' ? successLabel : errorLabel;
// Show the feedback icon
await this.copyIcon.animate(hideAnimation.keyframes, hideAnimation.options).finished;
this.copyIcon.hidden = true;
this.status = status;
iconToShow.hidden = false;
await iconToShow.animate(showAnimation.keyframes, showAnimation.options).finished;
// After a brief delay, restore the original state
setTimeout(async () => {
await iconToShow.animate(hideAnimation.keyframes, hideAnimation.options).finished;
iconToShow.hidden = true;
this.status = 'rest';
this.copyIcon.hidden = false;
await this.copyIcon.animate(showAnimation.keyframes, showAnimation.options).finished;
this.tooltip.content = copyLabel;
this.isCopying = false;
}, this.feedbackDuration);
}
render() {
const copyLabel = this.copyLabel || this.localize.term('copy');
return html`
<sl-tooltip
class=${classMap({
'copy-button': true,
'copy-button--success': this.status === 'success',
'copy-button--error': this.status === 'error'
})}
content=${copyLabel}
placement=${this.tooltipPlacement}
?disabled=${this.disabled}
>
<button
class="copy-button__button"
part="button"
type="button"
?disabled=${this.disabled}
@click=${this.handleCopy}
>
<slot name="copy-icon">
<sl-icon library="system" name="copy"></sl-icon>
</slot>
<slot name="success-icon" hidden>
<sl-icon library="system" name="check"></sl-icon>
</slot>
<slot name="error-icon" hidden>
<sl-icon library="system" name="x-lg"></sl-icon>
</slot>
</button>
</sl-tooltip>
`;
}
}
setDefaultAnimation('copy.in', {
keyframes: [
{ scale: '.25', opacity: '.25' },
{ scale: '1', opacity: '1' }
],
options: { duration: 100 }
});
setDefaultAnimation('copy.out', {
keyframes: [
{ scale: '1', opacity: '1' },
{ scale: '.25', opacity: '0' }
],
options: { duration: 100 }
});
declare global {
interface HTMLElementTagNameMap {
'sl-copy-button': SlCopyButton;
}
}

Wyświetl plik

@ -0,0 +1,49 @@
import { css } from 'lit';
import componentStyles from '../../styles/component.styles.js';
export default css`
${componentStyles}
:host {
--error-color: var(--sl-color-danger-600);
--success-color: var(--sl-color-success-600);
display: inline-block;
}
.copy-button__button {
flex: 0 0 auto;
display: flex;
align-items: center;
background: none;
border: none;
border-radius: var(--sl-border-radius-medium);
font-size: inherit;
color: inherit;
padding: var(--sl-spacing-x-small);
cursor: pointer;
transition: var(--sl-transition-x-fast) color;
}
.copy-button--success .copy-button__button {
color: var(--success-color);
}
.copy-button--error .copy-button__button {
color: var(--error-color);
}
.copy-button__button:focus-visible {
outline: var(--sl-focus-ring);
outline-offset: var(--sl-focus-ring-offset);
}
.copy-button__button[disabled] {
opacity: 0.5;
cursor: not-allowed !important;
}
slot {
display: inline-flex;
}
`;

Wyświetl plik

@ -0,0 +1,20 @@
import '../../../dist/shoelace.js';
import { expect, fixture, html } from '@open-wc/testing';
import type SlCopyButton from './copy-button.js';
// We use aria-live to announce labels via tooltips
const ignoredRules = ['button-name'];
describe('<sl-copy-button>', () => {
let el: SlCopyButton;
describe('when provided no parameters', () => {
before(async () => {
el = await fixture(html`<sl-copy-button value="something"></sl-copy-button> `);
});
it('should pass accessibility tests', async () => {
await expect(el).to.be.accessible({ ignoredRules });
});
});
});

Wyświetl plik

@ -0,0 +1,4 @@
import SlCopyButton from './copy-button.component.js';
export * from './copy-button.component.js';
export default SlCopyButton;
SlCopyButton.define('sl-copy-button');

Wyświetl plik

@ -1,165 +0,0 @@
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry.js';
import { html } from 'lit';
import { LocalizeController } from '../../utilities/localize.js';
import { property, query } from 'lit/decorators.js';
import ShoelaceElement from '../../internal/shoelace-element.js';
import SlIconButton from '../icon-button/icon-button.component.js';
import styles from './copy.styles.js';
import type { CSSResultGroup } from 'lit';
/**
* @summary Copies data to the clipboard when the user clicks or taps the trigger.
* @documentation https://shoelace.style/components/copy
* @status experimental
* @since 2.7
*
* @dependency sl-icon-button
*
* @event sl-copied - Emitted when the data has been copied.
* @event sl-error - Emitted when the data could not be copied.
*
* @slot - A button that triggers copying.
* @slot success - A button to briefly show when copying is successful.
* @slot error - A button to briefly show when a copy error occurs.
*
* @animation copy.in - The animation to use when copy buttons animate in.
* @animation copy.out - The animation to use when copy buttons animate out.
*/
export default class SlCopy extends ShoelaceElement {
static styles: CSSResultGroup = styles;
static dependencies = {
'sl-icon-button': SlIconButton
};
private readonly localize = new LocalizeController(this);
@query('slot:not([name])') defaultSlot: HTMLSlotElement;
@query('slot[name="success"]') successSlot: HTMLSlotElement;
@query('slot[name="error"]') errorSlot: HTMLSlotElement;
/** The text value to copy. */
@property({ type: String }) value = '';
/** The length of time to show feedback before restoring the default trigger. */
@property({ attribute: 'feedback-duration', type: Number }) feedbackDuration = 1000;
/**
* An id that references an element in the same document from which data will be copied. If the element is a link, the
* `href` will be copied. If the element is a form control or has a `value` property, its `value` will be copied.
* Otherwise, the element's text content will be copied.
*/
@property({ type: String }) from = '';
connectedCallback() {
super.connectedCallback();
this.setAttribute('aria-live', 'polite');
}
private async handleCopy() {
// Copy the value by default
let valueToCopy = this.value;
// If an element is specified, copy from that instead
if (this.from) {
const root = this.getRootNode() as ShadowRoot | Document;
const target = 'getElementById' in root ? root.getElementById(this.from) : false;
if (target) {
if (target instanceof HTMLAnchorElement && target.hasAttribute('href')) {
valueToCopy = target.href;
} else if ('value' in target) {
valueToCopy = String(target.value);
} else {
valueToCopy = target.textContent || '';
}
} else {
this.showStatus('error');
this.emit('sl-error');
}
}
// Copy from the value property otherwise
if (valueToCopy) {
try {
await navigator.clipboard.writeText(valueToCopy);
this.showStatus('success');
this.emit('sl-copied');
} catch (error) {
this.showStatus('error');
this.emit('sl-error');
}
}
}
private async showStatus(status: 'success' | 'error') {
const target = status === 'success' ? this.successSlot : this.errorSlot;
const showAnimation = getAnimation(this, 'copy.in', { dir: 'ltr' });
const hideAnimation = getAnimation(this, 'copy.out', { dir: 'ltr' });
await this.defaultSlot.animate(hideAnimation.keyframes, hideAnimation.options).finished;
this.defaultSlot.hidden = true;
target.hidden = false;
await target.animate(showAnimation.keyframes, showAnimation.options).finished;
setTimeout(async () => {
await target.animate(hideAnimation.keyframes, hideAnimation.options).finished;
target.hidden = true;
this.defaultSlot.hidden = false;
this.defaultSlot.animate(showAnimation.keyframes, showAnimation.options);
}, this.feedbackDuration);
}
render() {
return html`
<slot @click=${this.handleCopy}>
<sl-icon-button
library="system"
name="copy"
label=${this.localize.term('copy')}
exportparts="base:icon-button__base"
></sl-icon-button>
</slot>
<slot name="success" hidden>
<sl-icon-button
library="system"
name="check"
label=${this.localize.term('copied')}
exportparts="base:icon-button__base"
></sl-icon-button>
</slot>
<slot name="error" hidden>
<sl-icon-button
library="system"
name="x-lg"
label=${this.localize.term('error')}
exportparts="base:icon-button__base"
></sl-icon-button>
</slot>
`;
}
}
setDefaultAnimation('copy.in', {
keyframes: [
{ scale: '.25', opacity: '.25' },
{ scale: '1', opacity: '1' }
],
options: { duration: 125 }
});
setDefaultAnimation('copy.out', {
keyframes: [
{ scale: '1', opacity: '1' },
{ scale: '.25', opacity: '0' }
],
options: { duration: 125 }
});
declare global {
interface HTMLElementTagNameMap {
'sl-copy': SlCopy;
}
}

Wyświetl plik

@ -1,24 +0,0 @@
import { css } from 'lit';
import componentStyles from '../../styles/component.styles.js';
export default css`
${componentStyles}
:host {
display: inline-block;
cursor: pointer;
}
slot {
display: inline-flex;
}
.copy {
background: none;
border: none;
font: inherit;
color: inherit;
padding: 0;
margin: 0;
}
`;

Wyświetl plik

@ -1,29 +0,0 @@
import '../../../dist/shoelace.js';
import { expect, fixture, html } from '@open-wc/testing';
import type SlCopy from './copy.js';
describe('<sl-copy>', () => {
let el: SlCopy;
describe('when provided no parameters', () => {
before(async () => {
el = await fixture<SlCopy>(html`<sl-copy value="something"></sl-copy> `);
});
it('should pass accessibility tests', async () => {
await expect(el).to.be.accessible();
});
// it('should initially be in the trigger status', () => {
// expect(el.copyStatus).to.equal('trigger');
// });
// it('should reset copyStatus after 2 seconds', async () => {
// expect(el.copyStatus).to.equal('trigger');
// await el.copy(); // this will result in an error as copy needs to always be called from a user action
// expect(el.copyStatus).to.equal('error');
// await aTimeout(2100);
// expect(el.copyStatus).to.equal('trigger');
// });
});
});

Wyświetl plik

@ -1,4 +0,0 @@
import SlCopy from './copy.component.js';
export * from './copy.component.js';
export default SlCopy;
SlCopy.define('sl-copy');

Wyświetl plik

@ -41,8 +41,8 @@ const icons = {
</svg>
`,
copy: `
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="M3.5 11.889c-.828 0-1.5-.697-1.5-1.556V2.556C2 1.696 2.672 1 3.5 1h5.25c.555 0 1.04.313 1.3.778M7.25 15h5.25c.828 0 1.5-.696 1.5-1.556V5.667c0-.86-.672-1.556-1.5-1.556H7.25c-.828 0-1.5.697-1.5 1.556v7.777c0 .86.672 1.556 1.5 1.556Z" stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"/>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M3.5 11.889c-.828 0-1.5-.697-1.5-1.556V2.556C2 1.696 2.672 1 3.5 1h5.25c.555 0 1.04.313 1.3.778M7.25 15h5.25c.828 0 1.5-.696 1.5-1.556V5.667c0-.86-.672-1.556-1.5-1.556H7.25c-.828 0-1.5.697-1.5 1.556v7.777c0 .86.672 1.556 1.5 1.556Z" stroke="currentColor" fill="none" fill-rule="evenodd" />
</svg>
`,
eye: `

Wyświetl plik

@ -51,5 +51,6 @@ export default css`
color: var(--sl-tooltip-color);
padding: var(--sl-tooltip-padding);
pointer-events: none;
user-select: none;
}
`;

Wyświetl plik

@ -8,7 +8,7 @@ export type { default as SlChangeEvent } from './sl-change';
export type { default as SlClearEvent } from './sl-clear';
export type { default as SlCloseEvent } from './sl-close';
export type { default as SlCollapseEvent } from './sl-collapse';
export type { default as SlCopiedEvent } from './sl-copied';
export type { default as SlCopyEvent } from './sl-copy';
export type { default as SlErrorEvent } from './sl-error';
export type { default as SlExpandEvent } from './sl-expand';
export type { default as SlFinishEvent } from './sl-finish';

Wyświetl plik

@ -1,9 +0,0 @@
type SlCopiedEvent = CustomEvent<Record<PropertyKey, never>>;
declare global {
interface GlobalEventHandlersEventMap {
'sl-copied': SlCopiedEvent;
}
}
export default SlCopiedEvent;

Wyświetl plik

@ -0,0 +1,9 @@
type SlCopyEvent = CustomEvent<{ value: string }>;
declare global {
interface GlobalEventHandlersEventMap {
'sl-copy': SlCopyEvent;
}
}
export default SlCopyEvent;

Wyświetl plik

@ -13,7 +13,7 @@ export { default as SlCarousel } from './components/carousel/carousel.js';
export { default as SlCarouselItem } from './components/carousel-item/carousel-item.js';
export { default as SlCheckbox } from './components/checkbox/checkbox.js';
export { default as SlColorPicker } from './components/color-picker/color-picker.js';
export { default as SlCopy } from './components/copy/copy.js';
export { default as SlCopyButton } from './components/copy-button/copy-button.js';
export { default as SlDetails } from './components/details/details.js';
export { default as SlDialog } from './components/dialog/dialog.js';
export { default as SlDivider } from './components/divider/divider.js';