Revert "feat(clipboard): add new component sl-clipboard (#1473)"

This reverts commit 16f3e256b0.
pull/1496/head
Cory LaViska 2023-08-02 15:35:11 -04:00
rodzic dcbbc55f28
commit b7acb27c98
10 zmienionych plików z 2 dodań i 463 usunięć

Wyświetl plik

@ -1,241 +0,0 @@
---
meta:
title: Clipboard
description: Enables you to save content into the clipboard providing visual feedback.
layout: component
---
```html:preview
<p>Clicking the clipboard button will put "shoelace rocks" into your clipboard</p>
<sl-clipboard value="shoelace rocks"></sl-clipboard>
```
```jsx:react
import { SlClipboard } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<p>Clicking the clipboard button will put "shoelace rocks" into your clipboard</p>
<SlClipboard value="shoelace rocks"></SlClipboard>
</>
);
```
## Examples
### Use your own button
```html:preview
<sl-clipboard value="shoelace rocks">
<button type="button">Copy to clipboard</button>
<button slot="copied">Copied</button>
<button slot="error">Error</button>
</sl-clipboard>
<br>
<sl-clipboard value="shoelace rocks">
<sl-button>Copy</sl-button>
<sl-button slot="copied">Copied</sl-button>
<sl-button slot="error">Error</sl-button>
</sl-clipboard>
```
```jsx:react
import { SlClipboard } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlClipboard value="shoelace rocks">
<button type="button">Copy to clipboard</button>
<div slot="copied">copied</div>
<button slot="error">Error</button>
</SlClipboard>
<SlClipboard value="shoelace rocks">
<sl-button>Copy</sl-button>
<sl-button slot="copied">Copied</sl-button>
<sl-button slot="error">Error</sl-button>
</SlClipboard>
</>
);
```
### Get the textValue from a different element
```html:preview
<div class="row">
<dl>
<dt>Phone Number</dt>
<dd id="phone-value">+1 234 456789</dd>
</dl>
<sl-clipboard for="phone-value"></sl-clipboard>
</div>
<style>
dl, .row {
display: flex;
margin: 0;
}
</style>
```
```jsx:react
import { SlClipboard } from '@shoelace-style/shoelace/dist/react';
const css = `
dl, .row {
display: flex;
margin: 0;
}
`;
const App = () => (
<>
<div class="row">
<dl>
<dt>Phone Number</dt>
<dd id="phone-value">+1 234 456789</dd>
</dl>
<SlClipboard for="phone-value"></SlClipboard>
</div>
<style>{css}</style>
</>
);
```
### Copy an input/textarea or link
```html:preview
<input type="text" value="input rocks" id="input-rocks">
<sl-clipboard for="input-rocks"></sl-clipboard>
<br>
<textarea id="textarea-rocks">textarea
rocks</textarea>
<sl-clipboard for="textarea-rocks"></sl-clipboard>
<br>
<a href="https://shoelace.style/" id="link-rocks">Shoelace</a>
<sl-clipboard for="link-rocks"></sl-clipboard>
<br>
<sl-input value="sl-input rocks" id="sl-input-rocks"></sl-input>
<sl-clipboard for="sl-input-rocks"></sl-clipboard>
<br>
<sl-textarea value="sl-textarea rocks" id="sl-textarea-rocks"></sl-textarea>
<sl-clipboard for="sl-textarea-rocks"></sl-clipboard>
```
```jsx:react
import { SlClipboard } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<input type="text" value="input rocks" id="input-rocks">
<SlClipboard for="input-rocks"></SlClipboard>
<br>
<textarea id="textarea-rocks">textarea
rocks</textarea>
<SlClipboard for="textarea-rocks"></SlClipboard>
<br>
<a href="https://shoelace.style/" id="link-rocks">Shoelace</a>
<SlClipboard for="input-rocks"></SlClipboard>
</>
);
```
### Error if copy fails
For example if a `for` target element is not found or if not using `https`.
An empty string value like `value=""` will also result in an error.
```html:preview
<sl-clipboard for="not-found"></sl-clipboard>
<br>
<sl-clipboard for="not-found">
<sl-button>Copy</sl-button>
<sl-button slot="copied">Copied</sl-button>
<sl-button slot="error">Error</sl-button>
</sl-clipboard>
```
```jsx:react
import { SlClipboard } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlClipboard for="not-found"></SlClipboard>
<SlClipboard for="not-found">
<sl-button>Copy</sl-button>
<sl-button slot="copied">Copied</sl-button>
<sl-button slot="error">Error</sl-button>
</SlClipboard>
</>
);
```
### Change duration of reset to copy button
```html:preview
<sl-clipboard value="shoelace rocks" reset-timeout="500"></sl-clipboard>
```
```jsx:react
import { SlClipboard } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlClipboard value="shoelace rocks" reset-timeout="500"></SlClipboard>
</>
);
```
### Supports Shadow Dom
```html:preview
<sl-copy-demo-el></sl-copy-demo-el>
<script>
customElements.define('sl-copy-demo-el', class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.shadowRoot.innerHTML = `
<p id="copy-me">copy me (inside shadow root)</p>
<sl-clipboard for="copy-me"></sl-clipboard>
`;
}
});
</script>
```
```jsx:react
import { SlClipboard } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<sl-copy-demo-el></sl-copy-demo-el>
</>
);
customElements.define('sl-copy-demo-el', class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.shadowRoot.innerHTML = `
<p id="copy-me">copy me (inside shadow root)</p>
<sl-clipboard for="copy-me"></sl-clipboard>
`;
}
});
```
## Disclaimer
The public API is partially inspired by https://github.com/github/clipboard-copy-element

Wyświetl plik

@ -1,116 +0,0 @@
import { classMap } from 'lit/directives/class-map.js';
import { html } from 'lit';
import { property } from 'lit/decorators.js';
import ShoelaceElement from '../../internal/shoelace-element.js';
import SlIconButton from '../icon-button/icon-button.component.js';
import SlTooltip from '../tooltip/tooltip.component.js';
import styles from './clipboard.styles.js';
import type { CSSResultGroup } from 'lit';
/**
* @summary Enables you to save content into the clipboard providing visual feedback.
* @documentation https://shoelace.style/components/clipboard
* @status experimental
* @since 2.0
*
* @dependency sl-icon-button
* @dependency sl-tooltip
*
* @event sl-copying - Event when copying starts.
* @event sl-copied - Event when copying finished.
*
* @slot - The content that gets clicked to copy.
* @slot copied - The content shown after a successful copy.
* @slot error - The content shown if an error occurs.
*/
export default class SlClipboard extends ShoelaceElement {
static styles: CSSResultGroup = styles;
static dependencies = { 'sl-tooltip': SlTooltip, 'sl-icon-button': SlIconButton };
/**
* Indicates the current status the copy action is in.
*/
@property({ type: String }) copyStatus: 'trigger' | 'copied' | 'error' = 'trigger';
/** Value to copy. */
@property({ type: String }) value = '';
/** Id of the element to copy the text value from. */
@property({ type: String }) for = '';
/** Duration in milliseconds to reset to the trigger state. */
@property({ type: Number, attribute: 'reset-timeout' }) resetTimeout = 2000;
private handleClick() {
if (this.copyStatus === 'copied') return;
this.copy();
}
/** Copies the clipboard */
async copy() {
if (this.for) {
const root = this.getRootNode() as ShadowRoot | Document;
const target = 'getElementById' in root ? root.getElementById(this.for) : false;
if (target) {
if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) {
this.value = target.value;
} else if (target instanceof HTMLAnchorElement && target.hasAttribute('href')) {
this.value = target.href;
} else if ('value' in target) {
this.value = String(target.value);
} else {
this.value = target.textContent || '';
}
}
}
if (this.value) {
try {
this.emit('sl-copying');
await navigator.clipboard.writeText(this.value);
this.emit('sl-copied');
this.copyStatus = 'copied';
} catch (error) {
this.copyStatus = 'error';
}
} else {
this.copyStatus = 'error';
}
setTimeout(() => (this.copyStatus = 'trigger'), this.resetTimeout);
}
render() {
return html`
<div
part="base"
aria-live="polite"
class=${classMap({
clipboard: true,
[`clipboard--${this.copyStatus}`]: true
})}
>
<slot id="default" @click=${this.handleClick}>
<sl-tooltip content="Copy">
<sl-icon-button name="files" label="Copy"></sl-icon-button>
</sl-tooltip>
</slot>
<slot name="copied" @click=${this.handleClick}>
<sl-tooltip content="Copied">
<sl-icon-button class="green" name="file-earmark-check" label="Copied"></sl-icon-button>
</sl-tooltip>
</slot>
<slot name="error" @click=${this.handleClick}>
<sl-tooltip content="Failed to copy">
<sl-icon-button class="red" name="file-earmark-x" label="Failed to copy"></sl-icon-button>
</sl-tooltip>
</slot>
</div>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'sl-clipboard': SlClipboard;
}
}

Wyświetl plik

@ -1,54 +0,0 @@
import { css } from 'lit';
import componentStyles from '../../styles/component.styles.js';
export default css`
${componentStyles}
:host {
display: inline-block;
}
/* successful copy */
slot[name='copied'] {
display: none;
}
.clipboard--copied #default {
display: none;
}
.clipboard--copied slot[name='copied'] {
display: block;
}
.green::part(base) {
color: var(--sl-color-success-600);
}
.green::part(base):hover,
.green::part(base):focus {
color: var(--sl-color-success-600);
}
.green::part(base):active {
color: var(--sl-color-success-600);
}
/* failed to copy */
slot[name='error'] {
display: none;
}
.clipboard--error #default {
display: none;
}
.clipboard--error slot[name='error'] {
display: block;
}
.red::part(base) {
color: var(--sl-color-danger-600);
}
.red::part(base):hover,
.red::part(base):focus {
color: var(--sl-color-danger-600);
}
.red::part(base):active {
color: var(--sl-color-danger-600);
}
`;

Wyświetl plik

@ -1,29 +0,0 @@
import '../../../dist/shoelace.js';
import { aTimeout, expect, fixture, html } from '@open-wc/testing';
import type SlClipboard from './clipboard.js';
describe('<sl-clipboard>', () => {
let el: SlClipboard;
describe('when provided no parameters', () => {
before(async () => {
el = await fixture<SlClipboard>(html`<sl-clipboard value="something"></sl-clipboard> `);
});
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 SlClipboard from './clipboard.component.js';
export * from './clipboard.component.js';
export default SlClipboard;
SlClipboard.define('sl-clipboard');

Wyświetl plik

@ -12,7 +12,7 @@ import SlCheckbox from '../checkbox/checkbox.component.js';
import SlIcon from '../icon/icon.component.js';
import SlSpinner from '../spinner/spinner.component.js';
import styles from './tree-item.styles.js';
import type { CSSResultGroup, PropertyValues } from 'lit';
import type { CSSResultGroup, PropertyValueMap } from 'lit';
/**
* @summary A tree item serves as a hierarchical node that lives inside a [tree](/components/tree).
@ -139,7 +139,7 @@ export default class SlTreeItem extends ShoelaceElement {
this.isLeaf = !this.lazy && this.getChildrenItems().length === 0;
}
protected willUpdate(changedProperties: PropertyValues<SlTreeItem> | Map<PropertyKey, unknown>) {
protected willUpdate(changedProperties: PropertyValueMap<SlTreeItem> | Map<PropertyKey, unknown>) {
if (changedProperties.has('selected') && !changedProperties.has('indeterminate')) {
this.indeterminate = false;
}

Wyświetl plik

@ -8,8 +8,6 @@ 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 { SlCopyingEvent } from './sl-copying';
export type { SlCopiedEvent } from './sl-copied';
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,7 +0,0 @@
export type SlCopiedEvent = CustomEvent<Record<PropertyKey, never>>;
declare global {
interface GlobalEventHandlersEventMap {
'sl-copied': SlCopiedEvent;
}
}

Wyświetl plik

@ -1,7 +0,0 @@
export type SlCopyingEvent = CustomEvent<Record<PropertyKey, never>>;
declare global {
interface GlobalEventHandlersEventMap {
'sl-copying': SlCopyingEvent;
}
}

Wyświetl plik

@ -12,7 +12,6 @@ export { default as SlCard } from './components/card/card.js';
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 SlClipboard } from './components/clipboard/clipboard.js';
export { default as SlColorPicker } from './components/color-picker/color-picker.js';
export { default as SlDetails } from './components/details/details.js';
export { default as SlDialog } from './components/dialog/dialog.js';