@leavepulse/ui 0.60.0 → 0.62.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.
- package/README.md +8 -0
- package/dist/assets/{LpTable-BZZf_rrt.css → LpTable-DpXyPzs8.css} +19 -19
- package/dist/chunks/{LpAddressInput-DD4zTN4P.js → LpAddressInput-CzdZguBo.js} +1 -1
- package/dist/chunks/{LpAlert--KLL4NLk.js → LpAlert-BAOujnXy.js} +8 -6
- package/dist/chunks/{LpAppShell-Cv-Y9qNA.js → LpAppShell-BXMNDEpF.js} +1 -1
- package/dist/chunks/{LpAutocomplete-PnL2ntKl.js → LpAutocomplete-D9mY5Tqk.js} +101 -36
- package/dist/chunks/{LpAvatar-CwGs1JCQ.js → LpAvatar-K6MOMfgn.js} +1 -1
- package/dist/chunks/{LpCard-B2D0GIUU.js → LpCard-D9HVgX8Y.js} +1 -1
- package/dist/chunks/{LpColorPicker-CvBF8TeL.js → LpColorPicker-BO1ZTY51.js} +1 -1
- package/dist/chunks/{LpContextMenu-BnN1k7ga.js → LpContextMenu-Cv_F2iCG.js} +6 -3
- package/dist/chunks/{LpFileTree-DEqh2ySW.js → LpFileTree-DTwUj0L-.js} +1 -1
- package/dist/chunks/{LpFileTreeNode-DCZA7sQ-.js → LpFileTreeNode-D-XLPZ6T.js} +1 -1
- package/dist/chunks/{LpHints-BTpvisgP.js → LpHints-B_85eSmm.js} +1 -1
- package/dist/chunks/{LpLogViewer-BuPe23sZ.js → LpLogViewer-D9UgNz2m.js} +1 -1
- package/dist/chunks/{LpModalNav-Cw7m-eks.js → LpModalNav-D4rmflQn.js} +1 -1
- package/dist/chunks/{LpNotificationBell-xH_JphpS.js → LpNotificationBell-CyylOlZR.js} +1 -1
- package/dist/chunks/{LpPie-BD5Lsh-2.js → LpPie-B2deu8CI.js} +1 -1
- package/dist/chunks/{LpSidebar-B3E_iViL.js → LpSidebar-D8tub3bt.js} +1 -1
- package/dist/chunks/{LpSidebarNav-32sAXNM-.js → LpSidebarNav-BBSRg34G.js} +1 -1
- package/dist/chunks/LpSwap-D82jiecV.js +38 -0
- package/dist/chunks/{LpThemeSwitcher-CeBG0e_D.js → LpThemeSwitcher-ZFFftBQn.js} +1 -1
- package/dist/chunks/{LpToaster-BcKs7vX7.js → LpToaster-D3OisETH.js} +1 -1
- package/dist/chunks/{LpTour-C29-1wha.js → LpTour-ByaL36fN.js} +75 -53
- package/dist/component-names.d.ts +1 -1
- package/dist/components/LpAddressInput.vue.js +1 -1
- package/dist/components/LpAlert.vue.d.ts +7 -0
- package/dist/components/LpAlert.vue.js +1 -1
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpAutocomplete.vue.d.ts +24 -0
- package/dist/components/LpAutocomplete.vue.js +1 -1
- package/dist/components/LpAvatar.vue.js +1 -1
- package/dist/components/LpCard.vue.js +1 -1
- package/dist/components/LpChart.vue.js +1 -1
- package/dist/components/LpColorPicker.vue.js +1 -1
- package/dist/components/LpCommandPalette.vue.js +1 -1
- package/dist/components/LpContextMenu.vue.js +1 -1
- package/dist/components/LpFileTree.vue.js +1 -1
- package/dist/components/LpFileTreeNode.vue.js +1 -1
- package/dist/components/LpHints.vue.js +1 -1
- package/dist/components/LpLogViewer.vue.js +1 -1
- package/dist/components/LpModalNav.vue.js +1 -1
- package/dist/components/LpNotificationBell.vue.js +1 -1
- package/dist/components/LpPaletteHeader.vue.js +1 -1
- package/dist/components/LpPie.vue.js +1 -1
- package/dist/components/LpSidebar.vue.js +1 -1
- package/dist/components/LpSidebarNav.vue.js +1 -1
- package/dist/components/LpSwap.vue.d.ts +19 -0
- package/dist/components/LpSwap.vue.js +4 -0
- package/dist/components/LpTable.vue.js +5 -4
- package/dist/components/LpThemeSwitcher.vue.js +1 -1
- package/dist/components/LpToaster.vue.js +1 -1
- package/dist/components/LpTour.vue.js +1 -1
- package/dist/components/tour.d.ts +9 -0
- package/dist/components/tour.js +22 -0
- package/dist/composables/useHintSystem.d.ts +2 -0
- package/dist/composables/useHintSystem.js +16 -4
- package/dist/composables/useToast.d.ts +13 -4
- package/dist/composables/useToast.js +13 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +63 -61
- package/package.json +2 -1
- package/src/component-names.ts +1 -0
- package/src/components/LpAlert.vue +16 -5
- package/src/components/LpAutocomplete.vue +110 -24
- package/src/components/LpContextMenu.vue +6 -3
- package/src/components/LpSwap.vue +45 -0
- package/src/components/LpTable.vue +1 -0
- package/src/components/LpToaster.vue +1 -1
- package/src/components/LpTour.vue +26 -8
- package/src/components/tour.ts +31 -0
- package/src/composables/useHintSystem.ts +26 -4
- package/src/composables/useToast.ts +32 -5
- package/src/index.ts +3 -1
- package/src/tokens/selection.css +20 -0
|
@@ -19,6 +19,8 @@ export interface ToastItem {
|
|
|
19
19
|
actions?: ToastAction[];
|
|
20
20
|
/** Whole-toast click handler (UAProject-style clickable toast). */
|
|
21
21
|
onClick?: () => void | Promise<void>;
|
|
22
|
+
/** Copied with the message, not shown: what a bug report needs (request, status, ids). */
|
|
23
|
+
details?: string;
|
|
22
24
|
}
|
|
23
25
|
export interface ToastOptions {
|
|
24
26
|
/**
|
|
@@ -31,12 +33,19 @@ export interface ToastOptions {
|
|
|
31
33
|
duration?: number;
|
|
32
34
|
actions?: ToastAction[];
|
|
33
35
|
onClick?: () => void | Promise<void>;
|
|
36
|
+
/** Copied with the message, not shown. An Error passed to `error()` brings its own `details`. */
|
|
37
|
+
details?: string;
|
|
38
|
+
/** The error behind a hand-written message ("Not saved: " + e.message): its `details` ride along. */
|
|
39
|
+
cause?: unknown;
|
|
34
40
|
}
|
|
35
|
-
|
|
41
|
+
export type ToastMessage = string | (Error & {
|
|
42
|
+
details?: string;
|
|
43
|
+
});
|
|
44
|
+
declare function push(variant: ToastVariant, input: ToastMessage, given?: ToastOptions): number;
|
|
36
45
|
export interface ToastPromiseMessages<T> {
|
|
37
46
|
loading: string;
|
|
38
47
|
success: string | ((value: T) => string);
|
|
39
|
-
error: string | ((error: unknown) =>
|
|
48
|
+
error: string | ((error: unknown) => ToastMessage);
|
|
40
49
|
}
|
|
41
50
|
/**
|
|
42
51
|
* One toast that follows a promise: a persistent "loading" toast that turns
|
|
@@ -50,8 +59,8 @@ export declare function useToast(): {
|
|
|
50
59
|
toast: (message: string, o?: ToastOptions) => number;
|
|
51
60
|
info: (message: string, o?: ToastOptions) => number;
|
|
52
61
|
success: (message: string, o?: ToastOptions) => number;
|
|
53
|
-
warning: (message:
|
|
54
|
-
error: (message:
|
|
62
|
+
warning: (message: ToastMessage, o?: ToastOptions) => number;
|
|
63
|
+
error: (message: ToastMessage, o?: ToastOptions) => number;
|
|
55
64
|
promise: typeof promise;
|
|
56
65
|
/** Low-level push for bridges (e.g. @leavepulse/ui-sdk apiError). */
|
|
57
66
|
push: typeof push;
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { reactive } from "vue";
|
|
2
|
+
function detailsOf(source) {
|
|
3
|
+
const details = source?.details;
|
|
4
|
+
return typeof details === "string" && details ? details : void 0;
|
|
5
|
+
}
|
|
6
|
+
function split(message, options) {
|
|
7
|
+
const text = typeof message === "string" ? message : message.message;
|
|
8
|
+
const details = options.details ?? (typeof message === "string" ? void 0 : detailsOf(message)) ?? detailsOf(options.cause);
|
|
9
|
+
return [text, { ...options, details }];
|
|
10
|
+
}
|
|
2
11
|
const DEFAULT_DURATION = 4500;
|
|
3
12
|
const MAX_ACTIVE = 4;
|
|
4
13
|
const STATE_KEY = /* @__PURE__ */ Symbol.for("@leavepulse/ui:toast-queue");
|
|
5
14
|
const g = globalThis;
|
|
6
15
|
const state = g[STATE_KEY] ??= { queue: reactive([]), nextId: 0 };
|
|
7
16
|
const queue = state.queue;
|
|
8
|
-
function push(variant,
|
|
17
|
+
function push(variant, input, given = {}) {
|
|
18
|
+
const [message, options] = split(input, given);
|
|
9
19
|
if (typeof window === "undefined") return -1;
|
|
10
20
|
const duration = options.duration ?? DEFAULT_DURATION;
|
|
11
21
|
const fields = {
|
|
@@ -16,7 +26,8 @@ function push(variant, message, options = {}) {
|
|
|
16
26
|
duration,
|
|
17
27
|
dismissAt: duration > 0 ? Date.now() + duration : null,
|
|
18
28
|
actions: options.actions,
|
|
19
|
-
onClick: options.onClick
|
|
29
|
+
onClick: options.onClick,
|
|
30
|
+
details: options.details
|
|
20
31
|
};
|
|
21
32
|
const same = options.key === void 0 ? -1 : queue.findIndex((t) => t.key === options.key);
|
|
22
33
|
const existing = queue[same];
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export type { ActionBarAction } from "./components/LpActionBar.vue";
|
|
|
8
8
|
export { default as LpAlert } from "./components/LpAlert.vue";
|
|
9
9
|
export { default as LpAppShell } from "./components/LpAppShell.vue";
|
|
10
10
|
export { default as LpAutocomplete } from "./components/LpAutocomplete.vue";
|
|
11
|
-
export type { AutocompleteOption } from "./components/LpAutocomplete.vue";
|
|
11
|
+
export type { AutocompleteOption, AutocompleteSelection } from "./components/LpAutocomplete.vue";
|
|
12
12
|
export { default as LpAvatar } from "./components/LpAvatar.vue";
|
|
13
13
|
export { default as LpBadge } from "./components/LpBadge.vue";
|
|
14
14
|
export { default as LpBreadcrumbs } from "./components/LpBreadcrumbs.vue";
|
|
@@ -125,6 +125,7 @@ export type { SelectOption, SelectValue } from "./components/LpSelect.vue";
|
|
|
125
125
|
export { default as LpServiceNode } from "./components/LpServiceNode.vue";
|
|
126
126
|
export type { ServiceNodeData } from "./components/LpServiceNode.vue";
|
|
127
127
|
export { default as LpShift } from "./components/LpShift.vue";
|
|
128
|
+
export { default as LpSwap } from "./components/LpSwap.vue";
|
|
128
129
|
export { default as LpShortcutsDialog } from "./components/LpShortcutsDialog.vue";
|
|
129
130
|
export { default as LpSidebar } from "./components/LpSidebar.vue";
|
|
130
131
|
export type { SidebarItem, SidebarSection } from "./components/LpSidebar.vue";
|
|
@@ -237,7 +238,7 @@ export type { UseTilt, UseTiltOptions } from "./composables/useTilt";
|
|
|
237
238
|
export { useZoomPan } from "./composables/useZoomPan";
|
|
238
239
|
export type { UseZoomPan, UseZoomPanOptions } from "./composables/useZoomPan";
|
|
239
240
|
export { useToast } from "./composables/useToast";
|
|
240
|
-
export type { ToastAction, ToastItem, ToastOptions, ToastVariant, } from "./composables/useToast";
|
|
241
|
+
export type { ToastAction, ToastItem, ToastMessage, ToastOptions, ToastVariant, } from "./composables/useToast";
|
|
241
242
|
export type { ToastPromiseMessages } from "./composables/useToast";
|
|
242
243
|
export { cacheSystemThemes, normalizeTheme, prefersDarkScheme, themeEntries, } from "./theme/useTheme";
|
|
243
244
|
export type { SystemThemes, UseTheme } from "./theme/useTheme";
|
package/dist/index.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import { _ } from "./chunks/LayoutCanvas-t7LWFCEn.js";
|
|
2
|
-
import { _ as _2 } from "./chunks/LpAddressInput-
|
|
2
|
+
import { _ as _2 } from "./chunks/LpAddressInput-CzdZguBo.js";
|
|
3
3
|
import { _ as _3 } from "./chunks/LpMapPicker-DybIlWnF.js";
|
|
4
4
|
import { OSM_TILES, createNominatimProvider, emptyAddress, formatAddress, project, tileUrl, unproject } from "./components/address.js";
|
|
5
5
|
import { _ as _4 } from "./chunks/LpActionBar-BsSp66S_.js";
|
|
6
|
-
import { _ as _5 } from "./chunks/LpAlert
|
|
7
|
-
import { _ as _6 } from "./chunks/LpAppShell-
|
|
8
|
-
import { _ as _7 } from "./chunks/LpAutocomplete-
|
|
9
|
-
import { _ as _8 } from "./chunks/LpAvatar-
|
|
6
|
+
import { _ as _5 } from "./chunks/LpAlert-BAOujnXy.js";
|
|
7
|
+
import { _ as _6 } from "./chunks/LpAppShell-BXMNDEpF.js";
|
|
8
|
+
import { _ as _7 } from "./chunks/LpAutocomplete-D9mY5Tqk.js";
|
|
9
|
+
import { _ as _8 } from "./chunks/LpAvatar-K6MOMfgn.js";
|
|
10
10
|
import { _ as _9 } from "./chunks/LpBadge-Fe0D1bYx.js";
|
|
11
11
|
import { _ as _10 } from "./chunks/LpBreadcrumbs-CndzPYiG.js";
|
|
12
12
|
import { _ as _11 } from "./chunks/LpButton-CJSD7N0Z.js";
|
|
13
13
|
import { _ as _12 } from "./chunks/LpCalendar-CXVDbb2M.js";
|
|
14
|
-
import { _ as _13 } from "./chunks/LpCard-
|
|
14
|
+
import { _ as _13 } from "./chunks/LpCard-D9HVgX8Y.js";
|
|
15
15
|
import { default as default2 } from "./components/LpChart.vue.js";
|
|
16
16
|
import { backgroundOf, csvField, downloadFile, fileStem, inlineSvg, resolveCssColor, svgToPng, toCsv } from "./components/exportData.js";
|
|
17
17
|
import { CHART_PALETTE, axisTicks, chartSummary, formatTick, markerIndex, niceScale, resolveSlices } from "./components/chart.js";
|
|
18
18
|
import { _ as _14 } from "./chunks/LpCheckbox-DV9W6T3H.js";
|
|
19
19
|
import { _ as _15 } from "./chunks/LpCodeBlock-t6MLA3fb.js";
|
|
20
20
|
import { tokenizeLines } from "./components/codeHighlight.js";
|
|
21
|
-
import { _ as _16 } from "./chunks/LpColorPicker-
|
|
21
|
+
import { _ as _16 } from "./chunks/LpColorPicker-BO1ZTY51.js";
|
|
22
22
|
import { DEFAULT_SWATCHES, clampHsv, formatColor, hsvToHsl, hsvToRgb, isLight, normalizeHue, opaqueCss, parseColor, parseHex, rgbToHex, rgbToHsv } from "./components/color.js";
|
|
23
23
|
import { default as default3 } from "./components/LpCommandPalette.vue.js";
|
|
24
24
|
import { applyQueryFilters, highlightParts, parseCommandQuery, parsePaletteQuery, rankCommandItems } from "./components/commandSearch.js";
|
|
@@ -33,7 +33,7 @@ import { comboKeys, comboSteps } from "./components/keys.js";
|
|
|
33
33
|
import { _ as _18 } from "./chunks/LpConfirmDialog-tkgyY3eu.js";
|
|
34
34
|
import { _ as _19 } from "./chunks/LpConfirmHost-CoYIcUdl.js";
|
|
35
35
|
import { confirm, useConfirm } from "./components/confirm.js";
|
|
36
|
-
import { _ as _20 } from "./chunks/LpContextMenu-
|
|
36
|
+
import { _ as _20 } from "./chunks/LpContextMenu-Cv_F2iCG.js";
|
|
37
37
|
import { _ as _21 } from "./chunks/LpDatePicker-DxdrVNCG.js";
|
|
38
38
|
import { _ as _22 } from "./chunks/LpDateRangePicker-DjeaqikC.js";
|
|
39
39
|
import { _ as _23 } from "./chunks/LpRangeCalendar-COeOYF52.js";
|
|
@@ -44,7 +44,7 @@ import { _ as _26 } from "./chunks/LpDrawer-C97yoWmh.js";
|
|
|
44
44
|
import { _ as _27 } from "./chunks/LpDropdownMenu-CbyYu4Rw.js";
|
|
45
45
|
import { default as default9 } from "./components/LpDropzone.vue.js";
|
|
46
46
|
import { _ as _28 } from "./chunks/LpEmptyState-CX8fdGSU.js";
|
|
47
|
-
import { _ as _29 } from "./chunks/LpFileTree-
|
|
47
|
+
import { _ as _29 } from "./chunks/LpFileTree-DTwUj0L-.js";
|
|
48
48
|
import { ancestorIds, checkStateOf, checkedCount, computeStats, fileIcon, formatModified, formatSize, sortNodes, subtreeIds, subtreeSize } from "./components/fileTree.js";
|
|
49
49
|
import { _ as _30 } from "./chunks/LpFieldNotice-B--RzKfj.js";
|
|
50
50
|
import { _ as _31 } from "./chunks/LpFormField-DMPPL1jF.js";
|
|
@@ -56,19 +56,19 @@ import { _ as _34 } from "./chunks/LpInput-C7FHxzj2.js";
|
|
|
56
56
|
import { _ as _35 } from "./chunks/LpLightbox-DiksFa4L.js";
|
|
57
57
|
import { fileNameOf } from "./components/lightbox.js";
|
|
58
58
|
import { _ as _36 } from "./chunks/LpLink-Krkjhe_y.js";
|
|
59
|
-
import { _ as _37 } from "./chunks/LpLogViewer-
|
|
59
|
+
import { _ as _37 } from "./chunks/LpLogViewer-D9UgNz2m.js";
|
|
60
60
|
import { countLevels, matchPositions, matchesTerm, ringWindow, stepMatch } from "./components/logSearch.js";
|
|
61
61
|
import { _ as _38 } from "./chunks/LpMediaGallery-Cic9UHsa.js";
|
|
62
62
|
import { _ as _39 } from "./chunks/LpModal-QFfctZsj.js";
|
|
63
|
-
import { _ as _40 } from "./chunks/LpModalNav-
|
|
64
|
-
import { _ as _41 } from "./chunks/LpNotificationBell-
|
|
63
|
+
import { _ as _40 } from "./chunks/LpModalNav-D4rmflQn.js";
|
|
64
|
+
import { _ as _41 } from "./chunks/LpNotificationBell-CyylOlZR.js";
|
|
65
65
|
import { _ as _42 } from "./chunks/LpNumberField-BFSRVGkJ.js";
|
|
66
66
|
import { _ as _43 } from "./chunks/LpTimeField-BTEt0kvL.js";
|
|
67
67
|
import { _ as _44 } from "./chunks/LpNumberFlow-z4WPZcZu.js";
|
|
68
68
|
import { _ as _45 } from "./chunks/LpOtpInput-PbeNJ5In.js";
|
|
69
69
|
import { _ as _46 } from "./chunks/LpPagination-BFl0I7tL.js";
|
|
70
70
|
import { _ as _47 } from "./chunks/LpPasswordInput-C1G4k_r0.js";
|
|
71
|
-
import { _ as _48 } from "./chunks/LpPie-
|
|
71
|
+
import { _ as _48 } from "./chunks/LpPie-B2deu8CI.js";
|
|
72
72
|
import { _ as _49 } from "./chunks/LpPhoneInput-CZrpVxla.js";
|
|
73
73
|
import { filterCountries, flagEmoji, loadCountries, matchCountryByValue } from "./components/countries.js";
|
|
74
74
|
import { _ as _50 } from "./chunks/LpPopover-DCKeqiVn.js";
|
|
@@ -82,36 +82,37 @@ import { _ as _57 } from "./chunks/LpSection-BAkbf0OK.js";
|
|
|
82
82
|
import { _ as _58 } from "./chunks/LpSelect-C4OXN_q3.js";
|
|
83
83
|
import { _ as _59 } from "./chunks/LpServiceNode-D8uJ8sGk.js";
|
|
84
84
|
import { _ as _60 } from "./chunks/LpShift-DXNsauQZ.js";
|
|
85
|
+
import { _ as _61 } from "./chunks/LpSwap-D82jiecV.js";
|
|
85
86
|
import { default as default11 } from "./components/LpShortcutsDialog.vue.js";
|
|
86
|
-
import { _ as
|
|
87
|
-
import { _ as
|
|
88
|
-
import { _ as
|
|
89
|
-
import { _ as
|
|
90
|
-
import { _ as
|
|
87
|
+
import { _ as _62 } from "./chunks/LpSidebar-D8tub3bt.js";
|
|
88
|
+
import { _ as _63 } from "./chunks/LpSkeleton-BB6XIV-w.js";
|
|
89
|
+
import { _ as _64 } from "./chunks/LpSlider-Cuzmrd7p.js";
|
|
90
|
+
import { _ as _65 } from "./chunks/LpRangeSlider-C1yF9-nP.js";
|
|
91
|
+
import { _ as _66 } from "./chunks/LpVideoBackdrop-qWRYBOTQ.js";
|
|
91
92
|
import { formatClipTime, isNativeLoop, planLoop, seamProgress, seamReached } from "./components/videoBackdrop.js";
|
|
92
|
-
import { _ as
|
|
93
|
-
import { _ as
|
|
94
|
-
import { _ as
|
|
95
|
-
import { _ as
|
|
93
|
+
import { _ as _67 } from "./chunks/LpSparkline-CB80N8SJ.js";
|
|
94
|
+
import { _ as _68 } from "./chunks/LpStat-oYk6V1LZ.js";
|
|
95
|
+
import { _ as _69 } from "./chunks/LpStepper-YgHGzgfD.js";
|
|
96
|
+
import { _ as _70 } from "./chunks/LpSwitch-DG2AhXIL.js";
|
|
96
97
|
import { default as default12 } from "./components/LpTable.vue.js";
|
|
97
98
|
import { compareValues } from "./components/tableSort.js";
|
|
98
|
-
import { _ as
|
|
99
|
-
import { _ as
|
|
100
|
-
import { _ as
|
|
99
|
+
import { _ as _71 } from "./chunks/LpTableOfContents-DYuqkByy.js";
|
|
100
|
+
import { _ as _72 } from "./chunks/LpTabs-00v3VQx4.js";
|
|
101
|
+
import { _ as _73 } from "./chunks/LpTagsInput-Sc56iy_j.js";
|
|
101
102
|
import { addTags, splitTags } from "./components/tags.js";
|
|
102
|
-
import { _ as
|
|
103
|
-
import { _ as
|
|
104
|
-
import { _ as
|
|
105
|
-
import { _ as
|
|
106
|
-
import { _ as
|
|
107
|
-
import { _ as
|
|
103
|
+
import { _ as _74 } from "./chunks/LpTextarea-CQZccQI3.js";
|
|
104
|
+
import { _ as _75 } from "./chunks/LpThemeSwitcher-ZFFftBQn.js";
|
|
105
|
+
import { _ as _76 } from "./chunks/LpTilt-B7KHX1zH.js";
|
|
106
|
+
import { _ as _77 } from "./chunks/LpToaster-D3OisETH.js";
|
|
107
|
+
import { _ as _78 } from "./chunks/LpTooltip-suFCM34F.js";
|
|
108
|
+
import { _ as _79 } from "./chunks/LpTour-ByaL36fN.js";
|
|
108
109
|
import { holeOf, openContextMenu, panelsAround } from "./components/tour.js";
|
|
109
|
-
import { _ as
|
|
110
|
-
import { _ as
|
|
110
|
+
import { _ as _80 } from "./chunks/LpHintLayer-DsclGYcD.js";
|
|
111
|
+
import { _ as _81 } from "./chunks/LpHints-B_85eSmm.js";
|
|
111
112
|
import { HINT_SIGNALS, contextMenuTip, createHintSystem, hintKey, hintSummary, localHintStorage, paletteTip, provideHintSystem, runningHintSystem, useHintSystem } from "./composables/useHintSystem.js";
|
|
112
|
-
import { _ as
|
|
113
|
-
import { _ as
|
|
114
|
-
import { _ as
|
|
113
|
+
import { _ as _82 } from "./chunks/LpTooltipProvider-CftjV7Xc.js";
|
|
114
|
+
import { _ as _83 } from "./chunks/LpTopologyCanvas-6ozNrQKh.js";
|
|
115
|
+
import { _ as _84 } from "./chunks/LpUptimeBar-B4ltCfno.js";
|
|
115
116
|
import { deserializeLayout, serializeLayout } from "./layout/tree.js";
|
|
116
117
|
import { addLeaf, addTab, countBlocks, countLeaves, makeLayout, moveLeaf, removeBlock, removeLeaf, reorderTab, resizeAt, setActiveTab } from "./layout/tree.js";
|
|
117
118
|
import { blockTitle, defineBlocks } from "./layout/registry.js";
|
|
@@ -145,7 +146,7 @@ import { useShift } from "./composables/useShift.js";
|
|
|
145
146
|
import { useTilt } from "./composables/useTilt.js";
|
|
146
147
|
import { useZoomPan } from "./composables/useZoomPan.js";
|
|
147
148
|
import { useToast } from "./composables/useToast.js";
|
|
148
|
-
import { _ as
|
|
149
|
+
import { _ as _85 } from "./chunks/LpThemeProvider-CQ2jC9TA.js";
|
|
149
150
|
import { isInternalHref, resolveLink } from "./components/link.js";
|
|
150
151
|
function defineTheme(base, overrides = {}) {
|
|
151
152
|
const src = typeof base === "string" ? presets[base] : base;
|
|
@@ -218,8 +219,8 @@ export {
|
|
|
218
219
|
_30 as LpFieldNotice,
|
|
219
220
|
_29 as LpFileTree,
|
|
220
221
|
_31 as LpFormField,
|
|
221
|
-
|
|
222
|
-
|
|
222
|
+
_80 as LpHintLayer,
|
|
223
|
+
_81 as LpHints,
|
|
223
224
|
_32 as LpIcon,
|
|
224
225
|
default10 as LpImageEditor,
|
|
225
226
|
_33 as LpInfraNode,
|
|
@@ -250,7 +251,7 @@ export {
|
|
|
250
251
|
_52 as LpRadio,
|
|
251
252
|
_53 as LpRadioGroup,
|
|
252
253
|
_23 as LpRangeCalendar,
|
|
253
|
-
|
|
254
|
+
_65 as LpRangeSlider,
|
|
254
255
|
_54 as LpRollingText,
|
|
255
256
|
_55 as LpScrollArea,
|
|
256
257
|
_57 as LpSection,
|
|
@@ -259,29 +260,30 @@ export {
|
|
|
259
260
|
_59 as LpServiceNode,
|
|
260
261
|
_60 as LpShift,
|
|
261
262
|
default11 as LpShortcutsDialog,
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
263
|
+
_62 as LpSidebar,
|
|
264
|
+
_63 as LpSkeleton,
|
|
265
|
+
_64 as LpSlider,
|
|
266
|
+
_67 as LpSparkline,
|
|
267
|
+
_68 as LpStat,
|
|
268
|
+
_69 as LpStepper,
|
|
269
|
+
_61 as LpSwap,
|
|
270
|
+
_70 as LpSwitch,
|
|
269
271
|
default12 as LpTable,
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
272
|
+
_71 as LpTableOfContents,
|
|
273
|
+
_72 as LpTabs,
|
|
274
|
+
_73 as LpTagsInput,
|
|
275
|
+
_74 as LpTextarea,
|
|
276
|
+
_85 as LpThemeProvider,
|
|
277
|
+
_75 as LpThemeSwitcher,
|
|
278
|
+
_76 as LpTilt,
|
|
277
279
|
_43 as LpTimeField,
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
280
|
+
_77 as LpToaster,
|
|
281
|
+
_78 as LpTooltip,
|
|
282
|
+
_82 as LpTooltipProvider,
|
|
283
|
+
_83 as LpTopologyCanvas,
|
|
284
|
+
_79 as LpTour,
|
|
285
|
+
_84 as LpUptimeBar,
|
|
286
|
+
_66 as LpVideoBackdrop,
|
|
285
287
|
N as NO_BACKDROP_ATTR,
|
|
286
288
|
OSM_TILES,
|
|
287
289
|
T as THEME_COOKIE,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leavepulse/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.62.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "LeavePulse token-driven Vue component kit.",
|
|
6
6
|
"repository": {
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"./css": "./src/index.css",
|
|
31
31
|
"./tokens.css": "./src/tokens/tokens.css",
|
|
32
32
|
"./scrollbar.css": "./src/tokens/scrollbar.css",
|
|
33
|
+
"./selection.css": "./src/tokens/selection.css",
|
|
33
34
|
"./canvas.css": "./src/canvas.css",
|
|
34
35
|
"./package.json": "./package.json",
|
|
35
36
|
"./dist/*.js": {
|
package/src/component-names.ts
CHANGED
|
@@ -45,23 +45,34 @@ const props = withDefaults(
|
|
|
45
45
|
* of the page from the start should not be shouted on load.
|
|
46
46
|
*/
|
|
47
47
|
live?: "assertive" | "polite" | false
|
|
48
|
+
/**
|
|
49
|
+
* Work is under way: the icon becomes a spinning loader (unless `icon` is
|
|
50
|
+
* given, which then spins instead) and the alert is announced politely.
|
|
51
|
+
* A loader drawn as a still icon read as a frozen page.
|
|
52
|
+
*/
|
|
53
|
+
busy?: boolean
|
|
48
54
|
}>(),
|
|
49
|
-
{ variant: "info", live: undefined },
|
|
55
|
+
{ variant: "info", live: undefined, busy: false },
|
|
50
56
|
)
|
|
51
57
|
|
|
52
58
|
const role = computed(() => {
|
|
53
|
-
const live = props.live ?? (props.variant === "danger" ? "assertive" : false)
|
|
59
|
+
const live = props.live ?? (props.variant === "danger" ? "assertive" : props.busy ? "polite" : false)
|
|
54
60
|
return live === "assertive" ? "alert" : live === "polite" ? "status" : undefined
|
|
55
61
|
})
|
|
56
62
|
|
|
57
63
|
const classes = computed(() => alert({ variant: props.variant }))
|
|
58
|
-
const resolvedIcon = computed(() => props.icon ?? ICONS[props.variant ?? "info"])
|
|
64
|
+
const resolvedIcon = computed(() => props.icon ?? (props.busy ? "lucide:loader-circle" : ICONS[props.variant ?? "info"]))
|
|
59
65
|
const iconTone = computed(() => ICON_TONE[props.variant ?? "info"])
|
|
60
66
|
</script>
|
|
61
67
|
|
|
62
68
|
<template>
|
|
63
|
-
<div :class="classes" :role="role">
|
|
64
|
-
<LpIcon
|
|
69
|
+
<div :class="classes" :role="role" :aria-busy="busy || undefined">
|
|
70
|
+
<LpIcon
|
|
71
|
+
:name="resolvedIcon"
|
|
72
|
+
:size="18"
|
|
73
|
+
:class="['mt-0.5 shrink-0', iconTone, busy ? 'animate-spin motion-reduce:animate-none' : '']"
|
|
74
|
+
aria-hidden="true"
|
|
75
|
+
/>
|
|
65
76
|
<div class="min-w-0">
|
|
66
77
|
<p v-if="title" class="font-semibold text-ink">{{ title }}</p>
|
|
67
78
|
<div :class="['text-muted-strong', title ? 'mt-0.5' : '']">
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
ComboboxRoot,
|
|
26
26
|
ComboboxViewport,
|
|
27
27
|
} from "reka-ui"
|
|
28
|
-
import { computed, ref, useAttrs, watch } from "vue"
|
|
28
|
+
import { computed, nextTick, ref, useAttrs, watch } from "vue"
|
|
29
29
|
import { useFieldControl } from "../composables/useField"
|
|
30
30
|
import { useInputFilter } from "../composables/useInputFilter"
|
|
31
31
|
import { useLabels } from "../composables/useLpConfig"
|
|
@@ -36,6 +36,15 @@ export interface AutocompleteOption {
|
|
|
36
36
|
value: string
|
|
37
37
|
label?: string
|
|
38
38
|
description?: string
|
|
39
|
+
/** A picture beside the option: an avatar, an icon of a server. */
|
|
40
|
+
image?: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** What the field's value stands for: a name for an id, with an optional muted hint and picture. */
|
|
44
|
+
export interface AutocompleteSelection {
|
|
45
|
+
label: string
|
|
46
|
+
hint?: string
|
|
47
|
+
image?: string
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
const props = withDefaults(
|
|
@@ -65,6 +74,14 @@ const props = withDefaults(
|
|
|
65
74
|
pattern?: RegExp | string
|
|
66
75
|
/** Allowed-character RegExp; hard-blocks other input (see LpInput). */
|
|
67
76
|
restrict?: RegExp
|
|
77
|
+
/**
|
|
78
|
+
* What the current value stands for, shown in the field itself while it is
|
|
79
|
+
* not being edited: a picked id reads as a person (avatar in place of the
|
|
80
|
+
* icon, name, muted hint) instead of digits nobody recognises, and nothing
|
|
81
|
+
* appears or jumps below the field. Focusing it brings the raw text back.
|
|
82
|
+
* When room runs short the hint gives way first; the name stays.
|
|
83
|
+
*/
|
|
84
|
+
selected?: AutocompleteSelection | null
|
|
68
85
|
}>(),
|
|
69
86
|
{
|
|
70
87
|
placeholder: "Type…",
|
|
@@ -136,6 +153,12 @@ const filtered = computed(() => {
|
|
|
136
153
|
|
|
137
154
|
const open = ref(false)
|
|
138
155
|
const focused = ref(false)
|
|
156
|
+
const showSelected = computed(() => !!props.selected?.label && !focused.value)
|
|
157
|
+
// Focusing a picked value starts a new search from its name, not from the raw
|
|
158
|
+
// value: the name is put in the box selected, so typing replaces it, and
|
|
159
|
+
// leaving without typing keeps the pick as it was.
|
|
160
|
+
const draft = ref<string | null>(null)
|
|
161
|
+
const showImage = computed(() => !!props.selected?.image && (!focused.value || draft.value !== null))
|
|
139
162
|
|
|
140
163
|
// Threshold met AND something to show → the list is allowed to open.
|
|
141
164
|
const canOpen = computed(
|
|
@@ -174,19 +197,31 @@ const anchorSize = {
|
|
|
174
197
|
function onInput(event: Event) {
|
|
175
198
|
filterInput(event)
|
|
176
199
|
typing.value = true
|
|
200
|
+
draft.value = null
|
|
177
201
|
text.value = (event.target as HTMLInputElement).value
|
|
178
202
|
}
|
|
179
203
|
|
|
180
|
-
function onFocus() {
|
|
204
|
+
function onFocus(event: FocusEvent) {
|
|
181
205
|
// Arriving at a field that already holds a value shows every option, not just
|
|
182
206
|
// the one it currently holds.
|
|
183
207
|
typing.value = false
|
|
184
208
|
focused.value = true
|
|
209
|
+
if (props.selected?.label) {
|
|
210
|
+
draft.value = props.selected.label
|
|
211
|
+
const input = event.target as HTMLInputElement
|
|
212
|
+
void nextTick(() => input.select())
|
|
213
|
+
}
|
|
185
214
|
if (canOpen.value) open.value = true
|
|
186
215
|
}
|
|
187
216
|
|
|
217
|
+
function onBlur() {
|
|
218
|
+
focused.value = false
|
|
219
|
+
draft.value = null
|
|
220
|
+
}
|
|
221
|
+
|
|
188
222
|
function choose(opt: AutocompleteOption) {
|
|
189
223
|
typing.value = false
|
|
224
|
+
draft.value = null
|
|
190
225
|
emit("update:modelValue", opt.value)
|
|
191
226
|
emit("select", opt.value)
|
|
192
227
|
open.value = false
|
|
@@ -194,6 +229,7 @@ function choose(opt: AutocompleteOption) {
|
|
|
194
229
|
|
|
195
230
|
function clear() {
|
|
196
231
|
typing.value = false
|
|
232
|
+
draft.value = null
|
|
197
233
|
emit("update:modelValue", "")
|
|
198
234
|
open.value = false
|
|
199
235
|
}
|
|
@@ -220,7 +256,28 @@ function clear() {
|
|
|
220
256
|
: 'border-line focus-within:border-brand',
|
|
221
257
|
]"
|
|
222
258
|
>
|
|
223
|
-
|
|
259
|
+
<!-- Picture and icon share one fixed box and cross-fade, so the text
|
|
260
|
+
beside them never moves when one gives way to the other. -->
|
|
261
|
+
<span
|
|
262
|
+
v-if="icon || selected?.image"
|
|
263
|
+
class="relative grid shrink-0 place-items-center"
|
|
264
|
+
:class="selected?.image ? 'size-5' : 'size-4'"
|
|
265
|
+
>
|
|
266
|
+
<LpIcon
|
|
267
|
+
v-if="icon"
|
|
268
|
+
:name="icon"
|
|
269
|
+
:size="16"
|
|
270
|
+
class="col-start-1 row-start-1 text-muted transition-[opacity,scale] duration-[var(--duration-fast)] motion-reduce:transition-none"
|
|
271
|
+
:class="showImage ? 'scale-75 opacity-0' : 'opacity-100'"
|
|
272
|
+
/>
|
|
273
|
+
<img
|
|
274
|
+
v-if="selected?.image"
|
|
275
|
+
:src="selected.image"
|
|
276
|
+
alt=""
|
|
277
|
+
class="col-start-1 row-start-1 size-5 rounded-full object-cover transition-[opacity,scale] duration-[var(--duration-fast)] motion-reduce:transition-none"
|
|
278
|
+
:class="showImage ? 'opacity-100' : 'scale-75 opacity-0'"
|
|
279
|
+
/>
|
|
280
|
+
</span>
|
|
224
281
|
<!--
|
|
225
282
|
Bind value one-way and update from the raw DOM event, NOT via reka's
|
|
226
283
|
ComboboxInput v-model: reka defers the value/search update to nextTick
|
|
@@ -228,23 +285,49 @@ function clear() {
|
|
|
228
285
|
keystroke lag by one char. Reading event.target.value here is synchronous
|
|
229
286
|
so `text` (and the reactive `open`) track the field exactly.
|
|
230
287
|
-->
|
|
231
|
-
<
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
288
|
+
<div class="relative flex min-w-0 flex-1 self-stretch">
|
|
289
|
+
<ComboboxInput
|
|
290
|
+
v-bind="inputAttrs"
|
|
291
|
+
:id="field.id"
|
|
292
|
+
:model-value="draft ?? text"
|
|
293
|
+
:placeholder="placeholder"
|
|
294
|
+
:disabled="field.disabled"
|
|
295
|
+
:required="field.required || undefined"
|
|
296
|
+
:aria-invalid="isInvalidState || undefined"
|
|
297
|
+
:aria-describedby="field.describedBy"
|
|
298
|
+
class="lp-autofill min-w-0 flex-1 self-stretch bg-transparent outline-none placeholder:text-muted"
|
|
299
|
+
:class="showSelected && 'text-transparent placeholder:text-transparent'"
|
|
300
|
+
@beforeinput="onBeforeInput"
|
|
301
|
+
@paste="onPaste"
|
|
302
|
+
@compositionend="onCompositionEnd"
|
|
303
|
+
@input="onInput"
|
|
304
|
+
@focus="onFocus"
|
|
305
|
+
@blur="onBlur"
|
|
306
|
+
/>
|
|
307
|
+
<!-- Focus and blur do not animate the name: it sits exactly where the
|
|
308
|
+
input's own text sits, so that swap cannot be seen. The hint, which
|
|
309
|
+
exists in one state alone, eases in and out beside it, and the
|
|
310
|
+
whole line fades when a pick appears or is cleared. -->
|
|
311
|
+
<Transition
|
|
312
|
+
enter-active-class="transition-opacity duration-[var(--duration-fast)] motion-reduce:transition-none"
|
|
313
|
+
leave-active-class="transition-opacity duration-[var(--duration-fast)] motion-reduce:transition-none"
|
|
314
|
+
enter-from-class="opacity-0"
|
|
315
|
+
leave-to-class="opacity-0"
|
|
316
|
+
>
|
|
317
|
+
<span
|
|
318
|
+
v-if="selected?.label"
|
|
319
|
+
class="pointer-events-none absolute inset-0 flex min-w-0 items-center gap-1.5"
|
|
320
|
+
aria-hidden="true"
|
|
321
|
+
>
|
|
322
|
+
<span class="max-w-full shrink-0 truncate" :class="showSelected ? '' : 'invisible'">{{ selected.label }}</span>
|
|
323
|
+
<span
|
|
324
|
+
v-if="selected.hint"
|
|
325
|
+
class="min-w-0 truncate text-sm text-muted transition-[opacity,translate] duration-[var(--duration-fast)] motion-reduce:transition-none"
|
|
326
|
+
:class="showSelected ? 'opacity-100' : '-translate-x-1 opacity-0'"
|
|
327
|
+
>{{ selected.hint }}</span>
|
|
328
|
+
</span>
|
|
329
|
+
</Transition>
|
|
330
|
+
</div>
|
|
248
331
|
<button
|
|
249
332
|
v-if="clearable && hasValue"
|
|
250
333
|
type="button"
|
|
@@ -285,10 +368,13 @@ function clear() {
|
|
|
285
368
|
:class="[OPTION_ITEM, 'justify-between']"
|
|
286
369
|
@select.prevent="choose(opt)"
|
|
287
370
|
>
|
|
288
|
-
<span class="flex min-w-0
|
|
289
|
-
<
|
|
290
|
-
<span
|
|
291
|
-
{{ opt.
|
|
371
|
+
<span class="flex min-w-0 items-center gap-2.5">
|
|
372
|
+
<img v-if="opt.image" :src="opt.image" alt="" class="size-6 shrink-0 rounded-full object-cover" />
|
|
373
|
+
<span class="flex min-w-0 flex-col">
|
|
374
|
+
<span class="truncate">{{ opt.label ?? opt.value }}</span>
|
|
375
|
+
<span v-if="opt.description" class="truncate text-xs text-muted">
|
|
376
|
+
{{ opt.description }}
|
|
377
|
+
</span>
|
|
292
378
|
</span>
|
|
293
379
|
</span>
|
|
294
380
|
</ComboboxItem>
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { ContextMenuContent, ContextMenuPortal, ContextMenuRoot, ContextMenuTrigger } from "reka-ui"
|
|
10
10
|
import { computed } from "vue"
|
|
11
11
|
import { hints } from "../composables/useHints"
|
|
12
|
-
import { HINT_SIGNALS } from "../composables/useHintSystem"
|
|
12
|
+
import { HINT_SIGNALS, noteContextTarget } from "../composables/useHintSystem"
|
|
13
13
|
import ContextMenuItems from "./ContextMenuItems.vue"
|
|
14
14
|
import MenuTitle from "./MenuTitle.vue"
|
|
15
15
|
import { POPOVER_PANEL } from "./dropdown"
|
|
@@ -103,8 +103,11 @@ function opened(open: boolean) {
|
|
|
103
103
|
if (open) hints.use(HINT_SIGNALS.contextMenu)
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
function leftClick() {
|
|
107
|
-
if (hinted.value)
|
|
106
|
+
function leftClick(event: MouseEvent) {
|
|
107
|
+
if (!hinted.value) return
|
|
108
|
+
hints.use(HINT_SIGNALS.contextTarget)
|
|
109
|
+
const el = event.currentTarget as Element
|
|
110
|
+
noteContextTarget((event.target as Element).closest?.("[data-lp-context-row]") ?? el)
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
function returnFocus(event: Event) {
|