@juspay/svelte-ui-components 4.7.0 → 4.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { onDestroy } from 'svelte';
|
|
2
3
|
import type { SnippetProperties } from './properties';
|
|
3
4
|
import Button from '../Button/Button.svelte';
|
|
4
5
|
import copySvg from '../assets/copy.svg?raw';
|
|
6
|
+
import { createCopyResetTimer } from './copyResetTimer';
|
|
5
7
|
|
|
6
8
|
let {
|
|
7
9
|
text,
|
|
@@ -9,20 +11,31 @@
|
|
|
9
11
|
showCopyButton = true,
|
|
10
12
|
testId,
|
|
11
13
|
copyIcon,
|
|
14
|
+
copiedLabel = 'Copied!',
|
|
15
|
+
copyResetMs = 2000,
|
|
12
16
|
oncopy,
|
|
13
17
|
classes
|
|
14
18
|
}: SnippetProperties = $props();
|
|
15
19
|
|
|
16
20
|
let copied = $state(false);
|
|
17
21
|
|
|
22
|
+
// The reset timer used to be a bare setTimeout with nothing clearing it: a
|
|
23
|
+
// second copy before the first one's timer fired left two timers racing to
|
|
24
|
+
// flip `copied`, and navigating away mid-flash left the timer armed against
|
|
25
|
+
// a destroyed component. `createCopyResetTimer` (unit-tested on its own in
|
|
26
|
+
// copyResetTimer.test.ts) guarantees only one timer is ever pending, and
|
|
27
|
+
// onDestroy guarantees it never outlives the component it targets.
|
|
28
|
+
const resetTimer = createCopyResetTimer();
|
|
29
|
+
onDestroy(resetTimer.cancel);
|
|
30
|
+
|
|
18
31
|
async function handleCopy() {
|
|
19
32
|
try {
|
|
20
33
|
await navigator.clipboard.writeText(text);
|
|
21
34
|
copied = true;
|
|
22
35
|
oncopy?.();
|
|
23
|
-
|
|
36
|
+
resetTimer.arm(() => {
|
|
24
37
|
copied = false;
|
|
25
|
-
},
|
|
38
|
+
}, copyResetMs);
|
|
26
39
|
} catch {
|
|
27
40
|
// Clipboard API unavailable (non-secure context, iframe restrictions)
|
|
28
41
|
}
|
|
@@ -42,7 +55,7 @@
|
|
|
42
55
|
<div class="snippet-copy">
|
|
43
56
|
<Button onclick={handleCopy} ariaLabel="Copy to clipboard">
|
|
44
57
|
{#if copied}
|
|
45
|
-
<span class="snippet-copied">
|
|
58
|
+
<span class="snippet-copied" aria-live="polite">{copiedLabel}</span>
|
|
46
59
|
{:else if typeof copyIcon === 'function'}
|
|
47
60
|
{@render copyIcon()}
|
|
48
61
|
{:else}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages the single "revert the copied-feedback flag" timer behind Snippet's
|
|
3
|
+
* copy button (#530). Extracted as a plain, Svelte-free module so the timer
|
|
4
|
+
* safety this fixes -- never leaving two timers racing after a fast double
|
|
5
|
+
* copy, never firing against a component that has already unmounted -- is
|
|
6
|
+
* unit-testable on its own, the same way `Tooltip/tooltip-action.ts` keeps
|
|
7
|
+
* its DOM-timing logic outside the component.
|
|
8
|
+
*/
|
|
9
|
+
export type CopyResetTimer = {
|
|
10
|
+
/**
|
|
11
|
+
* (Re)arms the timer: clears any timer already pending, then schedules
|
|
12
|
+
* `onReset` to run after `delayMs`.
|
|
13
|
+
*/
|
|
14
|
+
arm: (onReset: () => void, delayMs: number) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Cancels a pending timer, if any. Safe to call when nothing is pending --
|
|
17
|
+
* this is what Snippet calls from `onDestroy`.
|
|
18
|
+
*/
|
|
19
|
+
cancel: () => void;
|
|
20
|
+
};
|
|
21
|
+
export declare function createCopyResetTimer(): CopyResetTimer;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function createCopyResetTimer() {
|
|
2
|
+
let handle = null;
|
|
3
|
+
return {
|
|
4
|
+
arm(onReset, delayMs) {
|
|
5
|
+
if (handle !== null) {
|
|
6
|
+
clearTimeout(handle);
|
|
7
|
+
}
|
|
8
|
+
handle = setTimeout(() => {
|
|
9
|
+
handle = null;
|
|
10
|
+
onReset();
|
|
11
|
+
}, delayMs);
|
|
12
|
+
},
|
|
13
|
+
cancel() {
|
|
14
|
+
if (handle !== null) {
|
|
15
|
+
clearTimeout(handle);
|
|
16
|
+
handle = null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -8,6 +8,10 @@ export type OptionalSnippetProperties = {
|
|
|
8
8
|
showCopyButton?: boolean;
|
|
9
9
|
testId?: string;
|
|
10
10
|
copyIcon?: Snippet;
|
|
11
|
+
/** Text shown in place of the copy icon after a successful copy. Defaults to `'Copied!'`. */
|
|
12
|
+
copiedLabel?: string;
|
|
13
|
+
/** Milliseconds before the copied feedback reverts to the copy icon. Defaults to `2000`. */
|
|
14
|
+
copyResetMs?: number;
|
|
11
15
|
classes?: string;
|
|
12
16
|
};
|
|
13
17
|
export type SnippetEventProperties = {
|
package/dist-wc/index.js
CHANGED
|
@@ -17671,21 +17671,40 @@ customElements.define("sui-sheet", create_custom_element(Sheet_wc, {
|
|
|
17671
17671
|
}, ["default", "footer"], [], { mode: "open" }));
|
|
17672
17672
|
//#endregion
|
|
17673
17673
|
//#region src/lib/assets/copy.svg?raw
|
|
17674
|
-
var copy_default = "<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"></rect>\n <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"></path>\n</svg>\n"
|
|
17674
|
+
var copy_default = "<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"></rect>\n <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"></path>\n</svg>\n";
|
|
17675
|
+
//#endregion
|
|
17676
|
+
//#region src/lib/Snippet/copyResetTimer.ts
|
|
17677
|
+
function createCopyResetTimer() {
|
|
17678
|
+
let e = null;
|
|
17679
|
+
return {
|
|
17680
|
+
arm(t, n) {
|
|
17681
|
+
e !== null && clearTimeout(e), e = setTimeout(() => {
|
|
17682
|
+
e = null, t();
|
|
17683
|
+
}, n);
|
|
17684
|
+
},
|
|
17685
|
+
cancel() {
|
|
17686
|
+
e !== null && (clearTimeout(e), e = null);
|
|
17687
|
+
}
|
|
17688
|
+
};
|
|
17689
|
+
}
|
|
17690
|
+
//#endregion
|
|
17691
|
+
//#region src/lib/Snippet/Snippet.svelte
|
|
17692
|
+
var root$30 = /* @__PURE__ */ from_html("<span class=\"snippet-copied svelte-ql5iye\" aria-live=\"polite\"> </span>"), root_1$27 = /* @__PURE__ */ from_html("<span class=\"snippet-copy-icon svelte-ql5iye\"></span>"), root_2$21 = /* @__PURE__ */ from_html("<div class=\"snippet-copy svelte-ql5iye\"><!></div>"), root_3$19 = /* @__PURE__ */ from_html("<div><code class=\"snippet-code svelte-ql5iye\"><span class=\"snippet-prompt svelte-ql5iye\"> </span> <span class=\"snippet-text svelte-ql5iye\"> </span></code> <!></div>"), $$css$32 = {
|
|
17675
17693
|
hash: "svelte-ql5iye",
|
|
17676
17694
|
code: ".snippet.svelte-ql5iye {display:flex;align-items:center;justify-content:space-between;gap:var(--snippet-gap, 8px);background:var(--snippet-background, #1e1e1e);border:var(--snippet-border, 1px solid #333);border-radius:var(--snippet-border-radius, var(--radius, 4px));padding:var(--snippet-padding, 12px 16px);font-family:var(--snippet-font-family, monospace);font-size:var(--snippet-font-size, 14px);color:var(--snippet-color, #e0e0e0);margin:var(--snippet-margin, 0);}.snippet-code.svelte-ql5iye {display:flex;align-items:center;overflow:hidden;flex:1;min-width:0;}.snippet-prompt.svelte-ql5iye {color:var(--snippet-prompt-color, #888);margin-right:var(--snippet-prompt-margin-right, 8px);flex-shrink:0;}.snippet-text.svelte-ql5iye {color:var(--snippet-text-color, #e0e0e0);font-family:var(--snippet-text-font-family, inherit);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}.snippet-copy.svelte-ql5iye {--button-color: var(--snippet-copy-background, transparent);--button-text-color: var(--snippet-copy-color, #888);--button-border: var(--snippet-copy-border, none);--button-padding: var(--snippet-copy-padding, 4px);--button-border-radius: var(--snippet-copy-border-radius, var(--radius, 4px));--cursor: var(--snippet-copy-cursor, pointer);--button-hover-color: var(--snippet-copy-hover-background, #333);display:flex;align-items:center;justify-content:center;flex-shrink:0;}.snippet-copy-icon.svelte-ql5iye {display:inline-flex;align-items:center;justify-content:center;width:var(--snippet-copy-size, 16px);height:var(--snippet-copy-size, 16px);}.snippet-copy-icon.svelte-ql5iye svg {width:100%;height:100%;}.snippet-copied.svelte-ql5iye {color:var(--snippet-copied-color, #4caf50);font-size:var(--snippet-copied-font-size, 12px);font-family:var(--snippet-font-family, monospace);}"
|
|
17677
17695
|
};
|
|
17678
17696
|
function Snippet(e, t) {
|
|
17679
17697
|
push(t, !0), append_styles$1(e, $$css$32);
|
|
17680
|
-
let n = prop(t, "text", 7), i = prop(t, "prompt", 7, "$"), a = prop(t, "showCopyButton", 7, !0), o = prop(t, "testId", 7), s = prop(t, "copyIcon", 7), c = prop(t, "
|
|
17681
|
-
|
|
17698
|
+
let n = prop(t, "text", 7), i = prop(t, "prompt", 7, "$"), a = prop(t, "showCopyButton", 7, !0), o = prop(t, "testId", 7), s = prop(t, "copyIcon", 7), c = prop(t, "copiedLabel", 7, "Copied!"), l = prop(t, "copyResetMs", 7, 2e3), u = prop(t, "oncopy", 7), f = prop(t, "classes", 7), p = /* @__PURE__ */ state(!1), h = createCopyResetTimer();
|
|
17699
|
+
onDestroy(h.cancel);
|
|
17700
|
+
async function S() {
|
|
17682
17701
|
try {
|
|
17683
|
-
await navigator.clipboard.writeText(n()), set(
|
|
17684
|
-
set(
|
|
17685
|
-
},
|
|
17702
|
+
await navigator.clipboard.writeText(n()), set(p, !0), u()?.(), h.arm(() => {
|
|
17703
|
+
set(p, !1);
|
|
17704
|
+
}, l());
|
|
17686
17705
|
} catch {}
|
|
17687
17706
|
}
|
|
17688
|
-
var
|
|
17707
|
+
var C = {
|
|
17689
17708
|
get text() {
|
|
17690
17709
|
return n();
|
|
17691
17710
|
},
|
|
@@ -17716,49 +17735,62 @@ function Snippet(e, t) {
|
|
|
17716
17735
|
set copyIcon(e) {
|
|
17717
17736
|
s(e), flushSync();
|
|
17718
17737
|
},
|
|
17719
|
-
get
|
|
17738
|
+
get copiedLabel() {
|
|
17720
17739
|
return c();
|
|
17721
17740
|
},
|
|
17722
|
-
set
|
|
17741
|
+
set copiedLabel(e = "Copied!") {
|
|
17723
17742
|
c(e), flushSync();
|
|
17724
17743
|
},
|
|
17725
|
-
get
|
|
17744
|
+
get copyResetMs() {
|
|
17726
17745
|
return l();
|
|
17727
17746
|
},
|
|
17728
|
-
set
|
|
17747
|
+
set copyResetMs(e = 2e3) {
|
|
17729
17748
|
l(e), flushSync();
|
|
17749
|
+
},
|
|
17750
|
+
get oncopy() {
|
|
17751
|
+
return u();
|
|
17752
|
+
},
|
|
17753
|
+
set oncopy(e) {
|
|
17754
|
+
u(e), flushSync();
|
|
17755
|
+
},
|
|
17756
|
+
get classes() {
|
|
17757
|
+
return f();
|
|
17758
|
+
},
|
|
17759
|
+
set classes(e) {
|
|
17760
|
+
f(e), flushSync();
|
|
17730
17761
|
}
|
|
17731
|
-
},
|
|
17732
|
-
reset(
|
|
17733
|
-
var
|
|
17734
|
-
reset(
|
|
17735
|
-
var
|
|
17762
|
+
}, k = root_3$19(), R = child(k), te = child(R), ne = child(te, !0);
|
|
17763
|
+
reset(te);
|
|
17764
|
+
var re = sibling(te, 2), G = child(re, !0);
|
|
17765
|
+
reset(re), reset(R);
|
|
17766
|
+
var be = sibling(R, 2), Me = (e) => {
|
|
17736
17767
|
var t = root_2$21();
|
|
17737
17768
|
Button(child(t), {
|
|
17738
|
-
onclick:
|
|
17769
|
+
onclick: S,
|
|
17739
17770
|
ariaLabel: "Copy to clipboard",
|
|
17740
17771
|
children: (e, t) => {
|
|
17741
17772
|
var n = comment(), i = first_child(n), a = (e) => {
|
|
17742
|
-
|
|
17773
|
+
var t = root$30(), n = child(t, !0);
|
|
17774
|
+
reset(t), template_effect(() => set_text(n, c())), append(e, t);
|
|
17743
17775
|
}, o = (e) => {
|
|
17744
17776
|
var t = comment();
|
|
17745
17777
|
snippet(first_child(t), s), append(e, t);
|
|
17746
|
-
},
|
|
17778
|
+
}, l = (e) => {
|
|
17747
17779
|
var t = root_1$27();
|
|
17748
17780
|
html(t, () => copy_default, !0), reset(t), append(e, t);
|
|
17749
17781
|
};
|
|
17750
17782
|
if_block(i, (e) => {
|
|
17751
|
-
get(
|
|
17783
|
+
get(p) ? e(a) : typeof s() == "function" ? e(o, 1) : e(l, -1);
|
|
17752
17784
|
}), append(e, n);
|
|
17753
17785
|
},
|
|
17754
17786
|
$$slots: { default: !0 }
|
|
17755
17787
|
}), reset(t), append(e, t);
|
|
17756
17788
|
};
|
|
17757
|
-
return if_block(
|
|
17758
|
-
a() && e(
|
|
17759
|
-
}), reset(
|
|
17760
|
-
set_class(
|
|
17761
|
-
}), append(e,
|
|
17789
|
+
return if_block(be, (e) => {
|
|
17790
|
+
a() && e(Me);
|
|
17791
|
+
}), reset(k), template_effect(() => {
|
|
17792
|
+
set_class(k, 1, `snippet ${f() ?? "" ?? ""}`, "svelte-ql5iye"), set_attribute(k, "data-pw", typeof o() == "string" ? o() : null), set_attribute(k, "testid", typeof o() == "string" ? o() : null), set_text(ne, i()), set_text(G, n());
|
|
17793
|
+
}), append(e, k), pop(C);
|
|
17762
17794
|
}
|
|
17763
17795
|
create_custom_element(Snippet, {
|
|
17764
17796
|
text: {},
|
|
@@ -17766,6 +17798,8 @@ create_custom_element(Snippet, {
|
|
|
17766
17798
|
showCopyButton: {},
|
|
17767
17799
|
testId: {},
|
|
17768
17800
|
copyIcon: {},
|
|
17801
|
+
copiedLabel: {},
|
|
17802
|
+
copyResetMs: {},
|
|
17769
17803
|
oncopy: {},
|
|
17770
17804
|
classes: {}
|
|
17771
17805
|
}, [], [], { mode: "open" });
|
|
@@ -17805,6 +17839,16 @@ customElements.define("sui-snippet", create_custom_element(Snippet_wc, {
|
|
|
17805
17839
|
attribute: "test-id",
|
|
17806
17840
|
type: "String"
|
|
17807
17841
|
},
|
|
17842
|
+
copiedLabel: {
|
|
17843
|
+
attribute: "copied-label",
|
|
17844
|
+
reflect: !0,
|
|
17845
|
+
type: "String"
|
|
17846
|
+
},
|
|
17847
|
+
copyResetMs: {
|
|
17848
|
+
attribute: "copy-reset-ms",
|
|
17849
|
+
reflect: !0,
|
|
17850
|
+
type: "Number"
|
|
17851
|
+
},
|
|
17808
17852
|
classes: { type: "String" },
|
|
17809
17853
|
oncopy: { type: "Object" },
|
|
17810
17854
|
copyIcon: { type: "Object" }
|