@leavepulse/ui 0.85.2 → 0.86.1
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.
- package/README.md +367 -72
- package/dist/assets/{LpTable-CCk0ZHo7.css → LpTable-CEFsRz5J.css} +19 -19
- package/dist/chunks/{LpCodeBlock-Dx0J9xlm.js → LpCodeBlock-kGT15Zou.js} +1 -1
- package/dist/chunks/{LpCodeDisplay-BlN7U8Z-.js → LpCodeDisplay-DlwzZo9o.js} +1 -1
- package/dist/chunks/{LpCopyButton-BrbvIVMG.js → LpCopyButton-TGoE43mE.js} +2 -2
- package/dist/chunks/{LpDataList-DhETPjzv.js → LpDataList-Due909Cy.js} +1 -1
- package/dist/chunks/{LpInspector-CqPgVXrm.js → LpInspector-BNxjuWMP.js} +1 -1
- package/dist/chunks/{LpLightbox-Bg1LW_ED.js → LpLightbox-DVyiB4dw.js} +1 -1
- package/dist/chunks/{LpMediaGallery-pX-znMeP.js → LpMediaGallery-OJemmbV7.js} +1 -1
- package/dist/chunks/{LpPaletteTheme-Bi6pD1GL.js → LpPaletteTheme-CWFz4H2D.js} +1 -1
- package/dist/chunks/{LpSecretField-_B2qPfk8.js → LpSecretField-Dp-c7wWc.js} +1 -1
- package/dist/chunks/{LpThemeStudio-C1xKiv8C.js → LpThemeStudio-RyI-SWiw.js} +1 -1
- package/dist/chunks/{LpThemeSwitcher-Dk4Bk6u1.js → LpThemeSwitcher-BZYJ0AXy.js} +1 -1
- package/dist/chunks/{LpToaster-C_CFWSys.js → LpToaster-YcLJFSsH.js} +1 -1
- package/dist/chunks/{LpTruncate-CBUKnr36.js → LpTruncate-DCZjfcE-.js} +1 -1
- package/dist/components/LpCodeBlock.vue.js +1 -1
- package/dist/components/LpCodeDisplay.vue.js +1 -1
- package/dist/components/LpCopyButton.vue.js +1 -1
- package/dist/components/LpDataList.vue.js +1 -1
- package/dist/components/LpInspector.vue.js +1 -1
- package/dist/components/LpLightbox.vue.js +1 -1
- package/dist/components/LpMediaGallery.vue.js +1 -1
- package/dist/components/LpPaletteTheme.vue.js +1 -1
- package/dist/components/LpSecretField.vue.js +1 -1
- package/dist/components/LpTable.vue.d.ts +2 -0
- package/dist/components/LpTable.vue.js +381 -370
- package/dist/components/LpThemeStudio.vue.js +1 -1
- package/dist/components/LpThemeSwitcher.vue.js +1 -1
- package/dist/components/LpToaster.vue.js +1 -1
- package/dist/components/LpTruncate.vue.js +1 -1
- package/dist/composables/useClipboard.d.ts +17 -3
- package/dist/composables/useClipboard.js +6 -2
- package/dist/index.js +13 -13
- package/package.json +3 -2
- package/src/components/LpCopyButton.vue +2 -2
- package/src/components/LpTable.vue +32 -9
- package/src/composables/useClipboard.ts +18 -5
|
@@ -1,16 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The kit's words for copying, scope "copyButton": LpCopyButton's own text and
|
|
3
|
+
* what `toast: true` says. One set, so an app that translates the scope once
|
|
4
|
+
* has every copy in it speak the same language.
|
|
5
|
+
*/
|
|
6
|
+
export declare const COPY_LABELS: {
|
|
7
|
+
copy: string;
|
|
8
|
+
copied: string;
|
|
9
|
+
failed: string;
|
|
10
|
+
};
|
|
1
11
|
export interface CopyOptions {
|
|
2
12
|
/**
|
|
3
13
|
* Toast to raise on success. `false` (the default) stays silent, for callers
|
|
4
14
|
* whose button already shows the tick and would otherwise say it twice.
|
|
15
|
+
* `true` says the kit's "Copied", in the app's language when its config
|
|
16
|
+
* translates the "copyButton" scope, so a menu item that copies does not
|
|
17
|
+
* have to bring its own word for it.
|
|
5
18
|
*/
|
|
6
|
-
toast?: string |
|
|
19
|
+
toast?: string | boolean;
|
|
7
20
|
/**
|
|
8
21
|
* Toast to raise when the copy fails. Defaults to silence for the same
|
|
9
22
|
* reason the kit's other best-effort paths are silent: a copy that didn't
|
|
10
23
|
* happen is usually obvious, and a scary red box over a blocked clipboard
|
|
11
|
-
* helps nobody. Pass a string where losing the copy actually costs something
|
|
24
|
+
* helps nobody. Pass a string where losing the copy actually costs something,
|
|
25
|
+
* or `true` for the kit's own "Couldn't copy".
|
|
12
26
|
*/
|
|
13
|
-
errorToast?: string |
|
|
27
|
+
errorToast?: string | boolean;
|
|
14
28
|
}
|
|
15
29
|
export interface UseClipboard {
|
|
16
30
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ref, onScopeDispose } from "vue";
|
|
2
|
+
import { useLabels } from "./useLpConfig.js";
|
|
2
3
|
import { useToast } from "./useToast.js";
|
|
3
4
|
async function writeAsync(text) {
|
|
4
5
|
if (typeof navigator === "undefined" || !navigator.clipboard) return false;
|
|
@@ -44,9 +45,11 @@ async function writeBlob(blob) {
|
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
const COPIED_MS = 1400;
|
|
48
|
+
const COPY_LABELS = { copy: "Copy", copied: "Copied", failed: "Couldn't copy" };
|
|
47
49
|
function useClipboard() {
|
|
48
50
|
const copied = ref(false);
|
|
49
51
|
const { success, error } = useToast();
|
|
52
|
+
const labels = useLabels("copyButton", COPY_LABELS);
|
|
50
53
|
let timer;
|
|
51
54
|
async function copy(data, options = {}) {
|
|
52
55
|
const { toast = false, errorToast = false } = options;
|
|
@@ -56,10 +59,10 @@ function useClipboard() {
|
|
|
56
59
|
copied.value = true;
|
|
57
60
|
clearTimeout(timer);
|
|
58
61
|
timer = setTimeout(() => copied.value = false, COPIED_MS);
|
|
59
|
-
if (toast) success(toast);
|
|
62
|
+
if (toast) success(toast === true ? labels.value.copied : toast);
|
|
60
63
|
return true;
|
|
61
64
|
} catch {
|
|
62
|
-
if (errorToast) error(errorToast);
|
|
65
|
+
if (errorToast) error(errorToast === true ? labels.value.failed : errorToast);
|
|
63
66
|
return false;
|
|
64
67
|
}
|
|
65
68
|
}
|
|
@@ -67,5 +70,6 @@ function useClipboard() {
|
|
|
67
70
|
return { copied, copy };
|
|
68
71
|
}
|
|
69
72
|
export {
|
|
73
|
+
COPY_LABELS,
|
|
70
74
|
useClipboard
|
|
71
75
|
};
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ import { chartView, lttbIndices, minMaxBuckets, panRange, slotOf, sourceOf, zoom
|
|
|
19
19
|
import { useChartSync } from "./composables/useChartSync.js";
|
|
20
20
|
import { createLinkedView, linkChart, linkLog, linkPipeline, linkTable, linkTopology, provideLinkedView, useLinkedView } from "./composables/useLinkedView.js";
|
|
21
21
|
import { _ as _14 } from "./chunks/LpCheckbox-DcXC2qd5.js";
|
|
22
|
-
import { _ as _15 } from "./chunks/LpCodeBlock-
|
|
22
|
+
import { _ as _15 } from "./chunks/LpCodeBlock-kGT15Zou.js";
|
|
23
23
|
import { tokenizeLines } from "./components/codeHighlight.js";
|
|
24
24
|
import { _ as _16 } from "./chunks/LpColorPicker-Cjna_Bio.js";
|
|
25
25
|
import { DEFAULT_SWATCHES, clampHsv, formatColor, hsvToHsl, hsvToRgb, isLight, normalizeHue, opaqueCss, parseColor, parseHex, rgbToHex, rgbToHsv } from "./components/color.js";
|
|
@@ -56,14 +56,14 @@ import { i, b, a, c } from "./chunks/icons-V0k93Puc.js";
|
|
|
56
56
|
import { default as default10 } from "./components/LpImageEditor.vue.js";
|
|
57
57
|
import { _ as _33 } from "./chunks/LpInfraNode-BsExhdPE.js";
|
|
58
58
|
import { _ as _34 } from "./chunks/LpInput-CKMblRtp.js";
|
|
59
|
-
import { _ as _35 } from "./chunks/LpLightbox-
|
|
59
|
+
import { _ as _35 } from "./chunks/LpLightbox-DVyiB4dw.js";
|
|
60
60
|
import { fileNameOf, isVideoItem } from "./components/lightbox.js";
|
|
61
61
|
import { _ as _36 } from "./chunks/LpLink-Krkjhe_y.js";
|
|
62
62
|
import { _ as _37 } from "./chunks/LpLogViewer-Bki-IdNC.js";
|
|
63
63
|
import { _ as _38 } from "./chunks/LpLogInspector-Yj310k4n.js";
|
|
64
64
|
import { compilePattern, findIds, messageShape, structuredPayload, timeMs } from "./components/logTools.js";
|
|
65
65
|
import { countLevels, matchPositions, matchesTerm, ringWindow, stepMatch } from "./components/logSearch.js";
|
|
66
|
-
import { _ as _39 } from "./chunks/LpMediaGallery-
|
|
66
|
+
import { _ as _39 } from "./chunks/LpMediaGallery-OJemmbV7.js";
|
|
67
67
|
import { _ as _40 } from "./chunks/LpModal-B5y9_LgA.js";
|
|
68
68
|
import { _ as _41 } from "./chunks/LpModalNav-BLvDVl2-.js";
|
|
69
69
|
import { _ as _42 } from "./chunks/LpNotificationBell-Bf0u3t5q.js";
|
|
@@ -121,9 +121,9 @@ import { _ as _79 } from "./chunks/LpBottomNav-BcJVkAhh.js";
|
|
|
121
121
|
import { _ as _80 } from "./chunks/LpTagsInput-By7LMb47.js";
|
|
122
122
|
import { addTags, splitTags } from "./components/tags.js";
|
|
123
123
|
import { _ as _81 } from "./chunks/LpTextarea-Ck6RAfzu.js";
|
|
124
|
-
import { _ as _82 } from "./chunks/LpThemeSwitcher-
|
|
124
|
+
import { _ as _82 } from "./chunks/LpThemeSwitcher-BZYJ0AXy.js";
|
|
125
125
|
import { _ as _83 } from "./chunks/LpTilt-B7KHX1zH.js";
|
|
126
|
-
import { _ as _84 } from "./chunks/LpToaster-
|
|
126
|
+
import { _ as _84 } from "./chunks/LpToaster-YcLJFSsH.js";
|
|
127
127
|
import { _ as _85 } from "./chunks/LpTooltip-Bz_rrq5S.js";
|
|
128
128
|
import { _ as _86 } from "./chunks/LpTour-D_AN_SsZ.js";
|
|
129
129
|
import { holeOf, openContextMenu, panelsAround } from "./components/tour.js";
|
|
@@ -139,7 +139,7 @@ import { blockTitle, defineBlocks } from "./layout/registry.js";
|
|
|
139
139
|
import { layoutController, useLayout } from "./layout/useLayout.js";
|
|
140
140
|
import { useWorkspaces } from "./layout/useWorkspaces.js";
|
|
141
141
|
import { _ as _92 } from "./chunks/LpWorkspaceSwitcher-D50sHs9N.js";
|
|
142
|
-
import { _ as _93 } from "./chunks/LpInspector-
|
|
142
|
+
import { _ as _93 } from "./chunks/LpInspector-BNxjuWMP.js";
|
|
143
143
|
import { _ as _94 } from "./chunks/LpWindow-Bo5qQgVY.js";
|
|
144
144
|
import { WINDOW_DOCKS, clampRect, dockAt, dockedRect, isWindowRect, resizeRect, snapRect, useWindowLayer } from "./composables/windowing.js";
|
|
145
145
|
import { THEME_COOKIE, applyTheme, applyThemeWithTransition, bootstrapTheme, cacheSystemThemes, normalizeTheme, parseTheme, prefersDarkScheme, revealTransition, serializeTheme, themeEntries, themeFromCookie, themeToCssRule, themeToCssVars, useTheme } from "./theme/useTheme.js";
|
|
@@ -188,7 +188,7 @@ import { useZoomPan } from "./composables/useZoomPan.js";
|
|
|
188
188
|
import { useToast } from "./composables/useToast.js";
|
|
189
189
|
import { _ as _95 } from "./chunks/LpThemeProvider-XK6ZeWAE.js";
|
|
190
190
|
import { _ as _96 } from "./chunks/LpFrame-Bv5Fphte.js";
|
|
191
|
-
import { _ as _97 } from "./chunks/LpThemeStudio-
|
|
191
|
+
import { _ as _97 } from "./chunks/LpThemeStudio-RyI-SWiw.js";
|
|
192
192
|
import { _ as _98 } from "./chunks/LpThemeStudioSection-B7bt8Iyg.js";
|
|
193
193
|
import { isInternalHref, resolveLink } from "./components/link.js";
|
|
194
194
|
import { _ as _99 } from "./chunks/LpRepeater-BKq5XPh4.js";
|
|
@@ -211,12 +211,12 @@ import { useLoadingGate } from "./composables/useLoadingGate.js";
|
|
|
211
211
|
import { useNow } from "./composables/useNow.js";
|
|
212
212
|
import { _ as _108 } from "./chunks/LpCountdown-DVl7vXI1.js";
|
|
213
213
|
import { DEFAULT_MILESTONES, DEFAULT_UNIT_LABELS, ceilToSecond, crossedMilestone, formatElapsed, formatTime, msToNextSecond, splitTime, timeParts } from "./components/timeSpan.js";
|
|
214
|
-
import { _ as _109 } from "./chunks/LpCopyButton-
|
|
215
|
-
import { _ as _110 } from "./chunks/LpSecretField-
|
|
216
|
-
import { _ as _111 } from "./chunks/LpDataList-
|
|
214
|
+
import { _ as _109 } from "./chunks/LpCopyButton-TGoE43mE.js";
|
|
215
|
+
import { _ as _110 } from "./chunks/LpSecretField-Dp-c7wWc.js";
|
|
216
|
+
import { _ as _111 } from "./chunks/LpDataList-Due909Cy.js";
|
|
217
217
|
import { _ as _112 } from "./chunks/LpEditable-DCPdDDBb.js";
|
|
218
|
-
import { _ as _113 } from "./chunks/LpTruncate-
|
|
219
|
-
import { _ as _114 } from "./chunks/LpCodeDisplay-
|
|
218
|
+
import { _ as _113 } from "./chunks/LpTruncate-DCZjfcE-.js";
|
|
219
|
+
import { _ as _114 } from "./chunks/LpCodeDisplay-DlwzZo9o.js";
|
|
220
220
|
import { formatRemaining, groupCode, middleTruncate, normalizeCode } from "./components/fieldText.js";
|
|
221
221
|
import { _ as _115 } from "./chunks/LpHoverCard-CHE9jAJG.js";
|
|
222
222
|
import { _ as _116 } from "./chunks/LpIdenticon-Dbbr4fvt.js";
|
|
@@ -246,7 +246,7 @@ import { _ as _128 } from "./chunks/LpCommandScope-DRUvHiX7.js";
|
|
|
246
246
|
import { createPaletteRegistry, mergePaletteCommands, usePaletteCommands, vPalette } from "./composables/usePaletteCommands.js";
|
|
247
247
|
import { commandShortcuts, commandToHotkey, commandToMenuItem, commandToPalette, useCommands } from "./composables/useCommands.js";
|
|
248
248
|
import { usePaletteTheme } from "./theme/palette.js";
|
|
249
|
-
import { _ as _129 } from "./chunks/LpPaletteTheme-
|
|
249
|
+
import { _ as _129 } from "./chunks/LpPaletteTheme-CWFz4H2D.js";
|
|
250
250
|
const uk = {
|
|
251
251
|
actionBar: { dismiss: "Закрити" },
|
|
252
252
|
activityLine: { busy: "Завантаження" },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leavepulse/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.86.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "LeavePulse token-driven Vue component kit.",
|
|
6
6
|
"repository": {
|
|
@@ -72,7 +72,8 @@
|
|
|
72
72
|
"check:size": "node scripts/check-size.mjs",
|
|
73
73
|
"test:coverage": "bun test --coverage",
|
|
74
74
|
"gen:icons": "node scripts/gen-kit-icons.mjs",
|
|
75
|
-
"gen:meta": "node scripts/gen-preview-meta.mjs"
|
|
75
|
+
"gen:meta": "node scripts/gen-preview-meta.mjs",
|
|
76
|
+
"gen:readme": "node scripts/readme-catalogue.mjs"
|
|
76
77
|
},
|
|
77
78
|
"peerDependencies": {
|
|
78
79
|
"@iconify-json/lucide": "^1.2.0",
|
|
@@ -39,7 +39,7 @@ export type CopyValue =
|
|
|
39
39
|
* `failed` state and the `error` event rather than as an exception.
|
|
40
40
|
*/
|
|
41
41
|
import { computed, ref, useAttrs } from "vue"
|
|
42
|
-
import { useClipboard } from "../composables/useClipboard"
|
|
42
|
+
import { COPY_LABELS, useClipboard } from "../composables/useClipboard"
|
|
43
43
|
import { useLabels } from "../composables/useLpConfig"
|
|
44
44
|
import LpButton from "./LpButton.vue"
|
|
45
45
|
import LpIcon from "./LpIcon.vue"
|
|
@@ -92,7 +92,7 @@ const emit = defineEmits<{
|
|
|
92
92
|
|
|
93
93
|
const text = useLabels<CopyButtonLabels>(
|
|
94
94
|
"copyButton",
|
|
95
|
-
|
|
95
|
+
COPY_LABELS,
|
|
96
96
|
() => props.labels,
|
|
97
97
|
)
|
|
98
98
|
|
|
@@ -5,7 +5,7 @@ export default { inheritAttrs: false }
|
|
|
5
5
|
</script>
|
|
6
6
|
|
|
7
7
|
<script setup lang="ts" generic="T extends Record<string, unknown>">
|
|
8
|
-
import { computed, h, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, useAttrs, useId, watch } from "vue"
|
|
8
|
+
import { computed, h, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, useAttrs, useId, useSlots, watch } from "vue"
|
|
9
9
|
import { suppressPaletteDiscovery, usePaletteCommands, usePaletteScope } from "../composables/usePaletteCommands"
|
|
10
10
|
import { menuPaletteCommands } from "./commandPalette"
|
|
11
11
|
import LpDataList from "./LpDataList.vue"
|
|
@@ -1383,8 +1383,14 @@ const countText = computed(() => {
|
|
|
1383
1383
|
// As documented on `summary`: the bar shows on its own while filters are
|
|
1384
1384
|
// active or rows are selected, not merely because rows can be selected, which
|
|
1385
1385
|
// left a lone "75 rows" over every selectable table.
|
|
1386
|
+
const slots = useSlots()
|
|
1386
1387
|
const showBar = computed(
|
|
1387
|
-
() =>
|
|
1388
|
+
() =>
|
|
1389
|
+
props.summary ||
|
|
1390
|
+
props.exportable ||
|
|
1391
|
+
!!slots.toolbar ||
|
|
1392
|
+
(!!props.selectable && props.selected.length > 0) ||
|
|
1393
|
+
activeFilters.value.length > 0,
|
|
1388
1394
|
)
|
|
1389
1395
|
const filteredOut = computed(() => {
|
|
1390
1396
|
if (busy.value || failed.value || displayRows.value.length) return false
|
|
@@ -1606,12 +1612,6 @@ usePaletteCommands(() => {
|
|
|
1606
1612
|
<!-- No top fade: a sticky header sits in the masked band and would be dimmed
|
|
1607
1613
|
by it. The bottom edge is left crisp with it for symmetry. -->
|
|
1608
1614
|
<div ref="frameEl" class="flex flex-col gap-3" :class="rootClass">
|
|
1609
|
-
<div v-if="exportable" class="flex justify-end">
|
|
1610
|
-
<LpButton variant="ghost" size="sm" @click="exportCsv()">
|
|
1611
|
-
<LpIcon name="lucide:download" :size="14" />
|
|
1612
|
-
{{ text.exportCsv }}
|
|
1613
|
-
</LpButton>
|
|
1614
|
-
</div>
|
|
1615
1615
|
<!-- What the table is showing right now: the filters in force, each one
|
|
1616
1616
|
a chip that clears it, the row count, and what is selected with room
|
|
1617
1617
|
for the consumer's bulk actions. Eased in and out (LpShift), so the
|
|
@@ -1619,7 +1619,14 @@ usePaletteCommands(() => {
|
|
|
1619
1619
|
<!-- The gap below the bar lives inside the eased box (pb-3), cancelled
|
|
1620
1620
|
outside it (-mb-3): an empty box must not leave the root's gap behind. -->
|
|
1621
1621
|
<LpShift class="-mb-3">
|
|
1622
|
-
|
|
1622
|
+
<!-- One line above the table: the page's own controls (#toolbar), the
|
|
1623
|
+
filters in force, then the status and Export at the end. Export used
|
|
1624
|
+
to sit on a line of its own and the page's search and filters on a
|
|
1625
|
+
third, so every table opened with two bands of mostly empty space. -->
|
|
1626
|
+
<div v-if="showBar" class="flex min-h-8 flex-wrap items-center gap-x-3 gap-y-2 pb-3 text-xs">
|
|
1627
|
+
<div v-if="$slots.toolbar" class="flex min-w-0 flex-wrap items-center gap-2 text-sm">
|
|
1628
|
+
<slot name="toolbar" />
|
|
1629
|
+
</div>
|
|
1623
1630
|
<div v-if="activeFilters.length" class="flex min-w-0 flex-1 flex-wrap items-center gap-1.5">
|
|
1624
1631
|
<LpBadge
|
|
1625
1632
|
v-for="f in activeFilters"
|
|
@@ -1689,10 +1696,25 @@ usePaletteCommands(() => {
|
|
|
1689
1696
|
<slot name="bulk" :selected="selected" :all-matching="allMatching" :clear="clearSelection" />
|
|
1690
1697
|
</span>
|
|
1691
1698
|
</Transition>
|
|
1699
|
+
<LpButton v-if="exportable" variant="ghost" size="sm" @click="exportCsv()">
|
|
1700
|
+
<LpIcon name="lucide:download" :size="14" />
|
|
1701
|
+
{{ text.exportCsv }}
|
|
1702
|
+
</LpButton>
|
|
1692
1703
|
</div>
|
|
1693
1704
|
</LpShift>
|
|
1694
1705
|
</div>
|
|
1695
1706
|
</LpShift>
|
|
1707
|
+
<!-- The frame eases to its new height as rows come and go. A leaving row
|
|
1708
|
+
stays in the flow while it fades (a positioned <tr> loses the column
|
|
1709
|
+
grid), so when it is finally removed the table lost its height in one
|
|
1710
|
+
frame: the rows slid aside smoothly and the box under them snapped.
|
|
1711
|
+
Always on rather than only while `animating`: LpShift starts over when
|
|
1712
|
+
it is switched on, so the change that switched it on was never eased.
|
|
1713
|
+
Rows the parent replaces wholesale still swap at once; only the frame
|
|
1714
|
+
eases. A virtual table owns its own height and stays out. -->
|
|
1715
|
+
<!-- flex-auto, not flex-1: a zero basis overrides the height LpShift sets
|
|
1716
|
+
while it eases, and the box jumped to its new size regardless. -->
|
|
1717
|
+
<LpShift :disabled="!!virtualRowHeight" class="flex min-h-0 flex-auto flex-col">
|
|
1696
1718
|
<LpScrollArea
|
|
1697
1719
|
v-bind="scrollAttrs"
|
|
1698
1720
|
:fade="false"
|
|
@@ -1994,6 +2016,7 @@ usePaletteCommands(() => {
|
|
|
1994
2016
|
</table>
|
|
1995
2017
|
</LpContextMenu>
|
|
1996
2018
|
</LpScrollArea>
|
|
2019
|
+
</LpShift>
|
|
1997
2020
|
<LpPagination
|
|
1998
2021
|
v-if="load && total > pageSize"
|
|
1999
2022
|
v-model:page="page"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { onScopeDispose, ref } from "vue"
|
|
2
|
+
import { useLabels } from "./useLpConfig"
|
|
2
3
|
import { useToast } from "./useToast"
|
|
3
4
|
|
|
4
5
|
/*
|
|
@@ -90,19 +91,30 @@ async function writeBlob(blob: Blob): Promise<boolean> {
|
|
|
90
91
|
/** How long `copied` stays true — long enough to read, short enough to not linger. */
|
|
91
92
|
const COPIED_MS = 1400
|
|
92
93
|
|
|
94
|
+
/**
|
|
95
|
+
* The kit's words for copying, scope "copyButton": LpCopyButton's own text and
|
|
96
|
+
* what `toast: true` says. One set, so an app that translates the scope once
|
|
97
|
+
* has every copy in it speak the same language.
|
|
98
|
+
*/
|
|
99
|
+
export const COPY_LABELS = { copy: "Copy", copied: "Copied", failed: "Couldn't copy" }
|
|
100
|
+
|
|
93
101
|
export interface CopyOptions {
|
|
94
102
|
/**
|
|
95
103
|
* Toast to raise on success. `false` (the default) stays silent, for callers
|
|
96
104
|
* whose button already shows the tick and would otherwise say it twice.
|
|
105
|
+
* `true` says the kit's "Copied", in the app's language when its config
|
|
106
|
+
* translates the "copyButton" scope, so a menu item that copies does not
|
|
107
|
+
* have to bring its own word for it.
|
|
97
108
|
*/
|
|
98
|
-
toast?: string |
|
|
109
|
+
toast?: string | boolean
|
|
99
110
|
/**
|
|
100
111
|
* Toast to raise when the copy fails. Defaults to silence for the same
|
|
101
112
|
* reason the kit's other best-effort paths are silent: a copy that didn't
|
|
102
113
|
* happen is usually obvious, and a scary red box over a blocked clipboard
|
|
103
|
-
* helps nobody. Pass a string where losing the copy actually costs something
|
|
114
|
+
* helps nobody. Pass a string where losing the copy actually costs something,
|
|
115
|
+
* or `true` for the kit's own "Couldn't copy".
|
|
104
116
|
*/
|
|
105
|
-
errorToast?: string |
|
|
117
|
+
errorToast?: string | boolean
|
|
106
118
|
}
|
|
107
119
|
|
|
108
120
|
export interface UseClipboard {
|
|
@@ -143,6 +155,7 @@ export interface UseClipboard {
|
|
|
143
155
|
export function useClipboard(): UseClipboard {
|
|
144
156
|
const copied = ref(false)
|
|
145
157
|
const { success, error } = useToast()
|
|
158
|
+
const labels = useLabels("copyButton", COPY_LABELS)
|
|
146
159
|
let timer: ReturnType<typeof setTimeout> | undefined
|
|
147
160
|
|
|
148
161
|
async function copy(data: string | Blob, options: CopyOptions = {}): Promise<boolean> {
|
|
@@ -155,10 +168,10 @@ export function useClipboard(): UseClipboard {
|
|
|
155
168
|
copied.value = true
|
|
156
169
|
clearTimeout(timer)
|
|
157
170
|
timer = setTimeout(() => (copied.value = false), COPIED_MS)
|
|
158
|
-
if (toast) success(toast)
|
|
171
|
+
if (toast) success(toast === true ? labels.value.copied : toast)
|
|
159
172
|
return true
|
|
160
173
|
} catch {
|
|
161
|
-
if (errorToast) error(errorToast)
|
|
174
|
+
if (errorToast) error(errorToast === true ? labels.value.failed : errorToast)
|
|
162
175
|
return false
|
|
163
176
|
}
|
|
164
177
|
}
|