diff --git a/docs/pages/components/clipboard.md b/docs/pages/components/clipboard.md
deleted file mode 100644
index 08e007db..00000000
--- a/docs/pages/components/clipboard.md
+++ /dev/null
@@ -1,241 +0,0 @@
----
-meta:
- title: Clipboard
- description: Enables you to save content into the clipboard providing visual feedback.
-layout: component
----
-
-```html:preview
-
Clicking the clipboard button will put "shoelace rocks" into your clipboard
-
-```
-
-```jsx:react
-import { SlClipboard } from '@shoelace-style/shoelace/dist/react';
-
-const App = () => (
- <>
- Clicking the clipboard button will put "shoelace rocks" into your clipboard
-
- >
-);
-```
-
-## Examples
-
-### Use your own button
-
-```html:preview
-
- Copy to clipboard
- Copied
- Error
-
-
-
- Copy
- Copied
- Error
-
-```
-
-```jsx:react
-import { SlClipboard } from '@shoelace-style/shoelace/dist/react';
-
-const App = () => (
- <>
-
- Copy to clipboard
- copied
- Error
-
-
- Copy
- Copied
- Error
-
- >
-);
-```
-
-### Get the textValue from a different element
-
-```html:preview
-
-
- Phone Number
- +1 234 456789
-
-
-
-
-
-```
-
-```jsx:react
-import { SlClipboard } from '@shoelace-style/shoelace/dist/react';
-
-const css = `
- dl, .row {
- display: flex;
- margin: 0;
- }
-`;
-
-const App = () => (
- <>
-
-
- Phone Number
- +1 234 456789
-
-
-
-
-
- >
-);
-```
-
-### Copy an input/textarea or link
-
-```html:preview
-
-
-
-
-
-
-
-
-Shoelace
-
-
-
-
-
-
-
-
-
-```
-
-```jsx:react
-import { SlClipboard } from '@shoelace-style/shoelace/dist/react';
-
-const App = () => (
- <>
-
-
-
-
-
-
- Shoelace
-
- >
-);
-```
-
-### 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
-
-
-
- Copy
- Copied
- Error
-
-```
-
-```jsx:react
-import { SlClipboard } from '@shoelace-style/shoelace/dist/react';
-
-const App = () => (
- <>
-
-
- Copy
- Copied
- Error
-
- >
-);
-```
-
-### Change duration of reset to copy button
-
-```html:preview
-
-```
-
-```jsx:react
-import { SlClipboard } from '@shoelace-style/shoelace/dist/react';
-
-const App = () => (
- <>
-
- >
-);
-```
-
-### Supports Shadow Dom
-
-```html:preview
-
-
-
-```
-
-```jsx:react
-import { SlClipboard } from '@shoelace-style/shoelace/dist/react';
-
-const App = () => (
- <>
-
- >
-);
-
-customElements.define('sl-copy-demo-el', class extends HTMLElement {
- constructor() {
- super();
- this.attachShadow({ mode: 'open' });
- }
-
- connectedCallback() {
- this.shadowRoot.innerHTML = `
- copy me (inside shadow root)
-
- `;
- }
-});
-```
-
-## Disclaimer
-
-The public API is partially inspired by https://github.com/github/clipboard-copy-element
diff --git a/docs/pages/components/copy.md b/docs/pages/components/copy.md
new file mode 100644
index 00000000..fc4cc67e
--- /dev/null
+++ b/docs/pages/components/copy.md
@@ -0,0 +1,156 @@
+---
+meta:
+ title: Copy
+ description: Copies data to the clipboard when the user clicks or taps the trigger.
+layout: component
+---
+
+```html:preview
+
+```
+
+```jsx:react
+import { SlCopy } from '@shoelace-style/shoelace/dist/react';
+
+const App = () => (
+
+);
+```
+
+## 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
+
+ Copy
+ Copied!
+ Error
+
+```
+
+```jsx:react
+import { SlButton, SlCopy } from '@shoelace-style/shoelace/dist/react';
+
+const App = () => (
+ <>
+
+ Copy
+ Copied!
+ Error
+
+ >
+);
+```
+
+### Copying the Value From Other Elements
+
+By default, the data to copy will come from the `value` attribute. You
+
+```html:preview
++1 (234) 456-7890
+
+
+
+
+
+
+
+
+
+Shoelace Website
+
+```
+
+```jsx:react
+import { SlCopy, SlInput } from '@shoelace-style/shoelace/dist/react';
+
+const App = () => (
+ <>
+ +1 (234) 456-7890
+
+
+
+
+
+
+
+
+
+ Shoelace Website
+
+ >
+);
+```
+
+### Displaying Copy Errors
+
+Copy errors can occur if the value is an empty string, if the `for` 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
+
+
+
+
+
+ Copy
+ Copied
+ Error
+
+```
+
+```jsx:react
+import { SlCopy } from '@shoelace-style/shoelace/dist/react';
+
+const App = () => (
+ <>
+
+
+
+
+
+ Copy
+ Copied
+ Error
+
+ >
+);
+```
+
+### Showing Tooltips
+
+You can wrap a tooltip around `` to provide a hint to users.
+
+```html:preview
+
+
+
+```
+
+```jsx:react
+import { SlCopy, SlTooltip } from '@shoelace-style/shoelace/dist/react';
+
+const App = () => (
+
+
+
+);
+```
+
+### 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
+
+```
+
+```jsx:react
+import { SlCopy } from '@shoelace-style/shoelace/dist/react';
+
+const App = () => (
+
+);
+```
diff --git a/docs/pages/resources/changelog.md b/docs/pages/resources/changelog.md
index 3698347b..d2b610e5 100644
--- a/docs/pages/resources/changelog.md
+++ b/docs/pages/resources/changelog.md
@@ -12,6 +12,10 @@ Components with the Experimental bad
New versions of Shoelace are released as-needed and generally occur when a critical mass of changes have accumulated. At any time, you can see what's coming in the next release by visiting [next.shoelace.style](https://next.shoelace.style).
+## Next
+
+- Added the `` component [[#1473]]
+
## 2.6.0
- Added JSDoc comments to React Wrappers for better documentation when hovering a component. [#1450]
diff --git a/src/components/clipboard/clipboard.component.ts b/src/components/clipboard/clipboard.component.ts
deleted file mode 100644
index 05710202..00000000
--- a/src/components/clipboard/clipboard.component.ts
+++ /dev/null
@@ -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`
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `;
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- 'sl-clipboard': SlClipboard;
- }
-}
diff --git a/src/components/clipboard/clipboard.styles.ts b/src/components/clipboard/clipboard.styles.ts
deleted file mode 100644
index e99391b5..00000000
--- a/src/components/clipboard/clipboard.styles.ts
+++ /dev/null
@@ -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);
- }
-`;
diff --git a/src/components/clipboard/clipboard.ts b/src/components/clipboard/clipboard.ts
deleted file mode 100644
index 390f5940..00000000
--- a/src/components/clipboard/clipboard.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import SlClipboard from './clipboard.component.js';
-export * from './clipboard.component.js';
-export default SlClipboard;
-SlClipboard.define('sl-clipboard');
diff --git a/src/components/copy/copy.component.ts b/src/components/copy/copy.component.ts
new file mode 100644
index 00000000..61b99da1
--- /dev/null
+++ b/src/components/copy/copy.component.ts
@@ -0,0 +1,165 @@
+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`
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ }
+}
+
+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;
+ }
+}
diff --git a/src/components/copy/copy.styles.ts b/src/components/copy/copy.styles.ts
new file mode 100644
index 00000000..77cbf72c
--- /dev/null
+++ b/src/components/copy/copy.styles.ts
@@ -0,0 +1,24 @@
+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;
+ }
+`;
diff --git a/src/components/clipboard/clipboard.test.ts b/src/components/copy/copy.test.ts
similarity index 79%
rename from src/components/clipboard/clipboard.test.ts
rename to src/components/copy/copy.test.ts
index 86093ff4..47a915e5 100644
--- a/src/components/clipboard/clipboard.test.ts
+++ b/src/components/copy/copy.test.ts
@@ -1,13 +1,13 @@
import '../../../dist/shoelace.js';
import { aTimeout, expect, fixture, html } from '@open-wc/testing';
-import type SlClipboard from './clipboard.js';
+import type SlCopy from './copy.js';
-describe('', () => {
- let el: SlClipboard;
+describe('', () => {
+ let el: SlCopy;
describe('when provided no parameters', () => {
before(async () => {
- el = await fixture(html` `);
+ el = await fixture(html` `);
});
it('should pass accessibility tests', async () => {
diff --git a/src/components/copy/copy.ts b/src/components/copy/copy.ts
new file mode 100644
index 00000000..ce56b309
--- /dev/null
+++ b/src/components/copy/copy.ts
@@ -0,0 +1,4 @@
+import SlCopy from './copy.component.js';
+export * from './copy.component.js';
+export default SlCopy;
+SlCopy.define('sl-copy');
diff --git a/src/components/icon/library.system.ts b/src/components/icon/library.system.ts
index a0b6ab3e..0dedf7e6 100644
--- a/src/components/icon/library.system.ts
+++ b/src/components/icon/library.system.ts
@@ -16,7 +16,7 @@ const icons = {
check: `
-
+
@@ -40,6 +40,11 @@ const icons = {
`,
+ copy: `
+
+
+
+ `,
eye: `
diff --git a/src/events/events.ts b/src/events/events.ts
index 322b9c8d..8786b25d 100644
--- a/src/events/events.ts
+++ b/src/events/events.ts
@@ -8,8 +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 { SlCopyingEvent } from './sl-copying';
-export type { SlCopiedEvent } from './sl-copied';
+export type { default as 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';
diff --git a/src/events/sl-copied.ts b/src/events/sl-copied.ts
index 6293ba8e..b170d5cb 100644
--- a/src/events/sl-copied.ts
+++ b/src/events/sl-copied.ts
@@ -1,7 +1,9 @@
-export type SlCopiedEvent = CustomEvent>;
+type SlCopiedEvent = CustomEvent>;
declare global {
interface GlobalEventHandlersEventMap {
'sl-copied': SlCopiedEvent;
}
}
+
+export default SlCopiedEvent;
diff --git a/src/events/sl-copying.ts b/src/events/sl-copying.ts
deleted file mode 100644
index 33ad22ad..00000000
--- a/src/events/sl-copying.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export type SlCopyingEvent = CustomEvent>;
-
-declare global {
- interface GlobalEventHandlersEventMap {
- 'sl-copying': SlCopyingEvent;
- }
-}
diff --git a/src/shoelace.ts b/src/shoelace.ts
index 69c2e647..5c02bdfd 100644
--- a/src/shoelace.ts
+++ b/src/shoelace.ts
@@ -12,8 +12,8 @@ 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 SlCopy } from './components/copy/copy.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';
diff --git a/src/translations/da.ts b/src/translations/da.ts
index e0d459c8..3925e68d 100644
--- a/src/translations/da.ts
+++ b/src/translations/da.ts
@@ -9,8 +9,10 @@ const translation: Translation = {
carousel: 'Karrusel',
clearEntry: 'Ryd indtastning',
close: 'Luk',
+ copied: 'Kopieret',
copy: 'Kopier',
currentValue: 'Nuværende værdi',
+ error: 'Fejl',
goToSlide: (slide, count) => `Gå til dias ${slide} af ${count}`,
hidePassword: 'Skjul adgangskode',
loading: 'Indlæser',
diff --git a/src/translations/de.ts b/src/translations/de.ts
index 4c1df4fb..1894e600 100644
--- a/src/translations/de.ts
+++ b/src/translations/de.ts
@@ -9,8 +9,10 @@ const translation: Translation = {
carousel: 'Karussell',
clearEntry: 'Eingabe löschen',
close: 'Schließen',
+ copied: 'Kopiert',
copy: 'Kopieren',
currentValue: 'Aktueller Wert',
+ error: 'Fehler',
goToSlide: (slide, count) => `Gehen Sie zu Folie ${slide} von ${count}`,
hidePassword: 'Passwort verbergen',
loading: 'Wird geladen',
diff --git a/src/translations/en.ts b/src/translations/en.ts
index 86a5e015..155cfcfd 100644
--- a/src/translations/en.ts
+++ b/src/translations/en.ts
@@ -9,8 +9,10 @@ const translation: Translation = {
carousel: 'Carousel',
clearEntry: 'Clear entry',
close: 'Close',
+ copied: 'Copied',
copy: 'Copy',
currentValue: 'Current value',
+ error: 'Error',
goToSlide: (slide, count) => `Go to slide ${slide} of ${count}`,
hidePassword: 'Hide password',
loading: 'Loading',
diff --git a/src/translations/es.ts b/src/translations/es.ts
index d15fdff7..4e948967 100644
--- a/src/translations/es.ts
+++ b/src/translations/es.ts
@@ -9,8 +9,10 @@ const translation: Translation = {
carousel: 'Carrusel',
clearEntry: 'Borrar entrada',
close: 'Cerrar',
+ copied: 'Copiado',
copy: 'Copiar',
currentValue: 'Valor actual',
+ error: 'Error',
goToSlide: (slide, count) => `Ir a la diapositiva ${slide} de ${count}`,
hidePassword: 'Ocultar contraseña',
loading: 'Cargando',
diff --git a/src/translations/fa.ts b/src/translations/fa.ts
index 2f39bde1..6b70bddb 100644
--- a/src/translations/fa.ts
+++ b/src/translations/fa.ts
@@ -9,8 +9,10 @@ const translation: Translation = {
carousel: 'چرخ فلک',
clearEntry: 'پاک کردن ورودی',
close: 'بستن',
+ copied: 'کپی شد',
copy: 'رونوشت',
currentValue: 'مقدار فعلی',
+ error: 'خطا',
goToSlide: (slide, count) => `رفتن به اسلاید ${slide} از ${count}`,
hidePassword: 'پنهان کردن رمز',
loading: 'بارگذاری',
diff --git a/src/translations/fr.ts b/src/translations/fr.ts
index a760fe84..e1386c97 100644
--- a/src/translations/fr.ts
+++ b/src/translations/fr.ts
@@ -9,8 +9,10 @@ const translation: Translation = {
carousel: 'Carrousel',
clearEntry: `Effacer l'entrée`,
close: 'Fermer',
+ copied: 'Copié',
copy: 'Copier',
currentValue: 'Valeur actuelle',
+ error: 'Erreur',
goToSlide: (slide, count) => `Aller à la diapositive ${slide} de ${count}`,
hidePassword: 'Masquer le mot de passe',
loading: 'Chargement',
diff --git a/src/translations/he.ts b/src/translations/he.ts
index 59187804..91d0f2c9 100644
--- a/src/translations/he.ts
+++ b/src/translations/he.ts
@@ -9,8 +9,10 @@ const translation: Translation = {
carousel: 'קרוסלה',
clearEntry: 'נקה קלט',
close: 'סגור',
+ copied: 'מוּעֲתָק',
copy: 'העתק',
currentValue: 'ערך נוכחי',
+ error: 'שְׁגִיאָה',
goToSlide: (slide, count) => `עבור לשקופית ${slide} של ${count}`,
hidePassword: 'הסתר סיסמא',
loading: 'טוען',
diff --git a/src/translations/hu.ts b/src/translations/hu.ts
index 7ff13be2..8272af85 100644
--- a/src/translations/hu.ts
+++ b/src/translations/hu.ts
@@ -9,8 +9,10 @@ const translation: Translation = {
carousel: 'Körhinta',
clearEntry: 'Bejegyzés törlése',
close: 'Bezárás',
+ copied: 'Másolva',
copy: 'Másolás',
currentValue: 'Aktuális érték',
+ error: 'Hiba',
goToSlide: (slide, count) => `Ugrás a ${count}/${slide}. diára`,
hidePassword: 'Jelszó elrejtése',
loading: 'Betöltés',
diff --git a/src/translations/ja.ts b/src/translations/ja.ts
index 47217e80..62ba4ed6 100644
--- a/src/translations/ja.ts
+++ b/src/translations/ja.ts
@@ -9,8 +9,10 @@ const translation: Translation = {
carousel: 'カルーセル',
clearEntry: 'クリアエントリ',
close: '閉じる',
+ copied: 'コピーされました',
copy: 'コピー',
currentValue: '現在の価値',
+ error: 'エラー',
goToSlide: (slide, count) => `${count} 枚中 ${slide} 枚のスライドに移動`,
hidePassword: 'パスワードを隠す',
loading: '読み込み中',
diff --git a/src/translations/nl.ts b/src/translations/nl.ts
index a4c859b8..6357fa7b 100644
--- a/src/translations/nl.ts
+++ b/src/translations/nl.ts
@@ -9,8 +9,10 @@ const translation: Translation = {
carousel: 'Carrousel',
clearEntry: 'Invoer wissen',
close: 'Sluiten',
+ copied: 'Gekopieerd',
copy: 'Kopiëren',
currentValue: 'Huidige waarde',
+ error: 'Fout',
goToSlide: (slide, count) => `Ga naar slide ${slide} van ${count}`,
hidePassword: 'Verberg wachtwoord',
loading: 'Bezig met laden',
diff --git a/src/translations/pl.ts b/src/translations/pl.ts
index 5630a4e0..b22913c6 100644
--- a/src/translations/pl.ts
+++ b/src/translations/pl.ts
@@ -9,8 +9,10 @@ const translation: Translation = {
carousel: 'Karuzela',
clearEntry: 'Wyczyść wpis',
close: 'Zamknij',
+ copied: 'Skopiowane',
copy: 'Kopiuj',
currentValue: 'Aktualna wartość',
+ error: 'Błąd',
goToSlide: (slide, count) => `Przejdź do slajdu ${slide} z ${count}`,
hidePassword: 'Ukryj hasło',
loading: 'Ładowanie',
diff --git a/src/translations/pt.ts b/src/translations/pt.ts
index b433c6a3..c093efac 100644
--- a/src/translations/pt.ts
+++ b/src/translations/pt.ts
@@ -9,8 +9,10 @@ const translation: Translation = {
carousel: 'Carrossel',
clearEntry: 'Limpar entrada',
close: 'Fechar',
+ copied: 'Copiado',
copy: 'Copiar',
currentValue: 'Valor atual',
+ error: 'Erro',
goToSlide: (slide, count) => `Vá para o slide ${slide} de ${count}`,
hidePassword: 'Esconder a senha',
loading: 'Carregando',
diff --git a/src/translations/ru.ts b/src/translations/ru.ts
index ee9f1472..87eae99f 100644
--- a/src/translations/ru.ts
+++ b/src/translations/ru.ts
@@ -9,8 +9,10 @@ const translation: Translation = {
carousel: 'Карусель',
clearEntry: 'Очистить запись',
close: 'Закрыть',
+ copied: 'Скопировано',
copy: 'Скопировать',
currentValue: 'Текущее значение',
+ error: 'Ошибка',
goToSlide: (slide, count) => `Перейти к слайду ${slide} из ${count}`,
hidePassword: 'Скрыть пароль',
loading: 'Загрузка',
diff --git a/src/translations/sv.ts b/src/translations/sv.ts
index 9c1015c8..e9c9f546 100644
--- a/src/translations/sv.ts
+++ b/src/translations/sv.ts
@@ -9,8 +9,10 @@ const translation: Translation = {
carousel: 'Karusell',
clearEntry: 'Återställ val',
close: 'Stäng',
+ copied: 'Kopierade',
copy: 'Kopiera',
currentValue: 'Nuvarande värde',
+ error: 'Fel',
goToSlide: (slide, count) => `Gå till bild ${slide} av ${count}`,
hidePassword: 'Dölj lösenord',
loading: 'Läser in',
diff --git a/src/translations/tr.ts b/src/translations/tr.ts
index 1d983a92..5635963a 100644
--- a/src/translations/tr.ts
+++ b/src/translations/tr.ts
@@ -9,8 +9,10 @@ const translation: Translation = {
carousel: 'Atlıkarınca',
clearEntry: 'Girişi sil',
close: 'Kapat',
+ copied: 'Kopyalandı',
copy: 'Kopya',
currentValue: 'Mevcut değer',
+ error: 'Hata',
goToSlide: (slide, count) => `${count} slayttan ${slide} slayta gidin`,
hidePassword: 'Şifreyi sakla',
loading: 'Yükleme',
diff --git a/src/translations/zh-tw.ts b/src/translations/zh-tw.ts
index 425c182a..6cb7a173 100644
--- a/src/translations/zh-tw.ts
+++ b/src/translations/zh-tw.ts
@@ -9,8 +9,10 @@ const translation: Translation = {
carousel: '旋轉木馬',
clearEntry: '清空',
close: '關閉',
+ copied: '已復制',
copy: '複製',
currentValue: '當前值',
+ error: '錯誤',
goToSlide: (slide, count) => `轉到第 ${slide} 張幻燈片,共 ${count} 張`,
hidePassword: '隱藏密碼',
loading: '載入中',
diff --git a/src/utilities/localize.ts b/src/utilities/localize.ts
index 0b52443a..9f22282e 100644
--- a/src/utilities/localize.ts
+++ b/src/utilities/localize.ts
@@ -16,8 +16,10 @@ export interface Translation extends DefaultTranslation {
carousel: string;
clearEntry: string;
close: string;
+ copied: string;
copy: string;
currentValue: string;
+ error: string;
goToSlide: (slide: number, count: number) => string;
hidePassword: string;
loading: string;