@leavepulse/ui 0.29.4 → 0.30.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/dist/assets/LpChart-CzgCGaAA.css +8 -0
- package/dist/chunks/LpActionBar-CxmBZpGu.js +173 -0
- package/dist/chunks/{LpAppShell-BJk3b-u4.js → LpAppShell-DnocEJAu.js} +1 -1
- package/dist/chunks/{LpCodeBlock-rS_6mO3D.js → LpCodeBlock-BWX1KZwk.js} +7 -13
- package/dist/chunks/LpColorPicker-CFC6iaNK.js +491 -0
- package/dist/chunks/{LpDisclosure-BnwP4Q-b.js → LpDisclosure-C1_YkMrd.js} +8 -2
- package/dist/chunks/{LpLogViewer-CVaGfTF_.js → LpLogViewer-TSRyt3t9.js} +3 -6
- package/dist/chunks/{LpSidebar-C_Mr-JPI.js → LpSidebar-CWSbczWi.js} +11 -2
- package/dist/component-names.d.ts +1 -1
- package/dist/components/LpActionBar.vue.d.ts +84 -0
- package/dist/components/LpActionBar.vue.js +4 -0
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpChart.vue.js +814 -2
- package/dist/components/LpCodeBlock.vue.js +1 -1
- package/dist/components/LpColorPicker.vue.d.ts +124 -0
- package/dist/components/LpColorPicker.vue.js +4 -0
- package/dist/components/LpDisclosure.vue.js +1 -1
- package/dist/components/LpLogViewer.vue.js +1 -1
- package/dist/components/LpNotificationBell.vue.d.ts +2 -2
- package/dist/components/LpSidebar.vue.d.ts +21 -0
- package/dist/components/LpSidebar.vue.js +1 -1
- package/dist/components/actionBar.d.ts +19 -0
- package/dist/components/actionBar.js +31 -0
- package/dist/components/color.d.ts +65 -0
- package/dist/components/color.js +158 -0
- package/dist/composables/useClipboard.d.ts +46 -0
- package/dist/composables/useClipboard.js +28 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +154 -135
- package/package.json +1 -1
- package/src/component-names.ts +2 -0
- package/src/components/LpActionBar.vue +268 -0
- package/src/components/LpChart.vue +337 -63
- package/src/components/LpCodeBlock.vue +5 -10
- package/src/components/LpColorPicker.vue +784 -0
- package/src/components/LpDisclosure.vue +17 -2
- package/src/components/LpLogViewer.vue +5 -8
- package/src/components/LpSidebar.vue +42 -6
- package/src/components/actionBar.ts +73 -0
- package/src/components/color.ts +257 -0
- package/src/composables/useClipboard.ts +107 -0
- package/src/index.ts +21 -0
- package/dist/chunks/LpChart-Bmw7y34Z.js +0 -672
package/dist/index.js
CHANGED
|
@@ -2,84 +2,88 @@ import { _ } from "./chunks/LayoutCanvas-Dd20boUG.js";
|
|
|
2
2
|
import { _ as _2 } from "./chunks/LpAddressInput-Bk6D8Bcy.js";
|
|
3
3
|
import { _ as _3 } from "./chunks/LpMapPicker-CXvoTrAe.js";
|
|
4
4
|
import { OSM_TILES, createNominatimProvider, emptyAddress, formatAddress, project, tileUrl, unproject } from "./components/address.js";
|
|
5
|
-
import { _ as _4 } from "./chunks/
|
|
6
|
-
import { _ as _5 } from "./chunks/
|
|
7
|
-
import { _ as _6 } from "./chunks/
|
|
8
|
-
import { _ as _7 } from "./chunks/
|
|
9
|
-
import { _ as _8 } from "./chunks/
|
|
10
|
-
import { _ as _9 } from "./chunks/
|
|
11
|
-
import { _ as _10 } from "./chunks/
|
|
12
|
-
import { _ as _11 } from "./chunks/
|
|
13
|
-
import { _ as _12 } from "./chunks/
|
|
14
|
-
import { _ as _13 } from "./chunks/
|
|
5
|
+
import { _ as _4 } from "./chunks/LpActionBar-CxmBZpGu.js";
|
|
6
|
+
import { _ as _5 } from "./chunks/LpAlert-BAYHDSS1.js";
|
|
7
|
+
import { _ as _6 } from "./chunks/LpAppShell-DnocEJAu.js";
|
|
8
|
+
import { _ as _7 } from "./chunks/LpAutocomplete-DExlIZe_.js";
|
|
9
|
+
import { _ as _8 } from "./chunks/LpAvatar-D3l4nabg.js";
|
|
10
|
+
import { _ as _9 } from "./chunks/LpBadge-BPodVcJa.js";
|
|
11
|
+
import { _ as _10 } from "./chunks/LpBreadcrumbs-5uvViCXi.js";
|
|
12
|
+
import { _ as _11 } from "./chunks/LpButton-5VXXCjwF.js";
|
|
13
|
+
import { _ as _12 } from "./chunks/LpCalendar-B7ZL0ty3.js";
|
|
14
|
+
import { _ as _13 } from "./chunks/LpCard-D0M2ZCnB.js";
|
|
15
|
+
import { default as default2 } from "./components/LpChart.vue.js";
|
|
15
16
|
import { CHART_PALETTE, axisTicks, formatTick, niceScale, resolveSlices } from "./components/chart.js";
|
|
16
17
|
import { _ as _14 } from "./chunks/LpCheckbox-BO0zuuPh.js";
|
|
17
|
-
import { _ as _15 } from "./chunks/LpCodeBlock-
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import { _ as
|
|
22
|
-
import { _ as
|
|
23
|
-
import { _ as
|
|
24
|
-
import { _ as
|
|
25
|
-
import { _ as
|
|
26
|
-
import {
|
|
27
|
-
import { _ as _23 } from "./chunks/
|
|
28
|
-
import {
|
|
18
|
+
import { _ as _15 } from "./chunks/LpCodeBlock-BWX1KZwk.js";
|
|
19
|
+
import { _ as _16 } from "./chunks/LpColorPicker-CFC6iaNK.js";
|
|
20
|
+
import { DEFAULT_SWATCHES, clampHsv, formatColor, hsvToHsl, hsvToRgb, isLight, normalizeHue, opaqueCss, parseColor, parseHex, rgbToHex, rgbToHsv } from "./components/color.js";
|
|
21
|
+
import { default as default3 } from "./components/LpCommandPalette.vue.js";
|
|
22
|
+
import { _ as _17 } from "./chunks/LpConfirmDialog-CjOGcj1e.js";
|
|
23
|
+
import { _ as _18 } from "./chunks/LpContextMenu-B6WBLleS.js";
|
|
24
|
+
import { _ as _19 } from "./chunks/LpDatePicker-dY2q4dr4.js";
|
|
25
|
+
import { _ as _20 } from "./chunks/LpDisclosure-C1_YkMrd.js";
|
|
26
|
+
import { _ as _21 } from "./chunks/LpDivider-CUX46KR0.js";
|
|
27
|
+
import { _ as _22 } from "./chunks/LpDrawer-DAJN5rGK.js";
|
|
28
|
+
import { _ as _23 } from "./chunks/LpDropdownMenu-DJpoqRCb.js";
|
|
29
|
+
import { default as default4 } from "./components/LpDropzone.vue.js";
|
|
30
|
+
import { _ as _24 } from "./chunks/LpEmptyState-BV3q7Qyi.js";
|
|
31
|
+
import { _ as _25 } from "./chunks/LpFileTree-DfFqY__1.js";
|
|
29
32
|
import { ancestorIds, checkStateOf, checkedCount, computeStats, fileIcon, formatModified, formatSize, sortNodes, subtreeIds, subtreeSize } from "./components/fileTree.js";
|
|
30
|
-
import { _ as
|
|
31
|
-
import { _ as
|
|
32
|
-
import { default as
|
|
33
|
-
import { _ as
|
|
34
|
-
import { _ as
|
|
35
|
-
import { _ as
|
|
33
|
+
import { _ as _26 } from "./chunks/LpFormField-fV_2ZdCk.js";
|
|
34
|
+
import { _ as _27 } from "./chunks/LpIcon-CCnX5_2j.js";
|
|
35
|
+
import { default as default5 } from "./components/LpImageEditor.vue.js";
|
|
36
|
+
import { _ as _28 } from "./chunks/LpInfraNode-Ds_8SZgN.js";
|
|
37
|
+
import { _ as _29 } from "./chunks/LpInput-CYcTL9gj.js";
|
|
38
|
+
import { _ as _30 } from "./chunks/LpLightbox-Ci35Y_xP.js";
|
|
36
39
|
import { fileNameOf } from "./components/lightbox.js";
|
|
37
|
-
import { _ as
|
|
38
|
-
import { _ as
|
|
39
|
-
import { _ as
|
|
40
|
-
import { _ as
|
|
41
|
-
import { _ as
|
|
42
|
-
import { _ as
|
|
43
|
-
import { _ as
|
|
44
|
-
import { _ as
|
|
45
|
-
import { _ as
|
|
46
|
-
import { _ as
|
|
47
|
-
import { _ as
|
|
40
|
+
import { _ as _31 } from "./chunks/LpLink-BHZ1CLlF.js";
|
|
41
|
+
import { _ as _32 } from "./chunks/LpLogViewer-TSRyt3t9.js";
|
|
42
|
+
import { _ as _33 } from "./chunks/LpModal-D_o-56To.js";
|
|
43
|
+
import { _ as _34 } from "./chunks/LpNotificationBell-CTsIcPqX.js";
|
|
44
|
+
import { _ as _35 } from "./chunks/LpNumberField-NPE8Bumx.js";
|
|
45
|
+
import { _ as _36 } from "./chunks/LpNumberFlow-amWYaeuA.js";
|
|
46
|
+
import { _ as _37 } from "./chunks/LpOtpInput-DqaT0Z75.js";
|
|
47
|
+
import { _ as _38 } from "./chunks/LpPagination-CeGuNe_q.js";
|
|
48
|
+
import { _ as _39 } from "./chunks/LpPasswordInput-DrYE4J5d.js";
|
|
49
|
+
import { _ as _40 } from "./chunks/LpPie-BL1vTDSM.js";
|
|
50
|
+
import { _ as _41 } from "./chunks/LpPhoneInput-NXXjciJJ.js";
|
|
48
51
|
import { flagEmoji, loadCountries, matchCountryByValue } from "./components/countries.js";
|
|
49
|
-
import { _ as
|
|
50
|
-
import { _ as
|
|
51
|
-
import { _ as
|
|
52
|
-
import { _ as
|
|
53
|
-
import { _ as
|
|
54
|
-
import { _ as
|
|
55
|
-
import { _ as
|
|
56
|
-
import { _ as
|
|
57
|
-
import { _ as
|
|
58
|
-
import { _ as
|
|
59
|
-
import { _ as
|
|
60
|
-
import { default as
|
|
61
|
-
import { _ as
|
|
62
|
-
import { _ as
|
|
63
|
-
import { _ as
|
|
64
|
-
import { _ as
|
|
65
|
-
import { _ as
|
|
66
|
-
import { _ as
|
|
67
|
-
import { _ as
|
|
68
|
-
import { default as
|
|
69
|
-
import { _ as
|
|
70
|
-
import { _ as
|
|
71
|
-
import { _ as
|
|
52
|
+
import { _ as _42 } from "./chunks/LpPopover-8268UmlC.js";
|
|
53
|
+
import { _ as _43 } from "./chunks/LpProgress-Btq5W4Ut.js";
|
|
54
|
+
import { _ as _44 } from "./chunks/LpRadio-CL-pI0_O.js";
|
|
55
|
+
import { _ as _45 } from "./chunks/LpRadioGroup-B4yDTmi6.js";
|
|
56
|
+
import { _ as _46 } from "./chunks/LpRollingText-CM5gkSeF.js";
|
|
57
|
+
import { _ as _47 } from "./chunks/LpScrollArea-D8xyKXcF.js";
|
|
58
|
+
import { _ as _48 } from "./chunks/LpSegmented-BmpanyAo.js";
|
|
59
|
+
import { _ as _49 } from "./chunks/LpSection-aM6J_u51.js";
|
|
60
|
+
import { _ as _50 } from "./chunks/LpSelect-ANIVtrpM.js";
|
|
61
|
+
import { _ as _51 } from "./chunks/LpServiceNode-D8uJ8sGk.js";
|
|
62
|
+
import { _ as _52 } from "./chunks/LpShift-DXNsauQZ.js";
|
|
63
|
+
import { default as default6 } from "./components/LpShortcutsDialog.vue.js";
|
|
64
|
+
import { _ as _53 } from "./chunks/LpSidebar-CWSbczWi.js";
|
|
65
|
+
import { _ as _54 } from "./chunks/LpSkeleton-CTI-vBgW.js";
|
|
66
|
+
import { _ as _55 } from "./chunks/LpSlider-B1SF3pRV.js";
|
|
67
|
+
import { _ as _56 } from "./chunks/LpSparkline-C6xQ2dwv.js";
|
|
68
|
+
import { _ as _57 } from "./chunks/LpStat-CDanpzU0.js";
|
|
69
|
+
import { _ as _58 } from "./chunks/LpStepper-Wtl-6nUh.js";
|
|
70
|
+
import { _ as _59 } from "./chunks/LpSwitch-v6fnccSM.js";
|
|
71
|
+
import { default as default7 } from "./components/LpTable.vue.js";
|
|
72
|
+
import { _ as _60 } from "./chunks/LpTableOfContents-CjvxPMpU.js";
|
|
73
|
+
import { _ as _61 } from "./chunks/LpTabs-Cefe_lys.js";
|
|
74
|
+
import { _ as _62 } from "./chunks/LpTextarea-BMIOfw5l.js";
|
|
72
75
|
import { p as presets, a as parseTheme, s as serializeTheme } from "./chunks/LpThemeSwitcher-r1te9nB2.js";
|
|
73
|
-
import { D, _ as
|
|
74
|
-
import { _ as
|
|
75
|
-
import { _ as
|
|
76
|
-
import { _ as
|
|
77
|
-
import { _ as
|
|
78
|
-
import { _ as
|
|
76
|
+
import { D, _ as _63, N, T, b, c, d, e, f, g, i, l, h, j, n, r, t, k, m, u, v } from "./chunks/LpThemeSwitcher-r1te9nB2.js";
|
|
77
|
+
import { _ as _64 } from "./chunks/LpTilt-DuyKbpkA.js";
|
|
78
|
+
import { _ as _65 } from "./chunks/LpToaster-gDB1EaU3.js";
|
|
79
|
+
import { _ as _66 } from "./chunks/LpTooltip-hCLPAqMG.js";
|
|
80
|
+
import { _ as _67 } from "./chunks/LpTopologyCanvas-Cm1L39uL.js";
|
|
81
|
+
import { _ as _68 } from "./chunks/LpUptimeBar-DcQ8HikG.js";
|
|
79
82
|
import { deserializeLayout, serializeLayout } from "./layout/tree.js";
|
|
80
83
|
import { addLeaf, addTab, countBlocks, countLeaves, makeLayout, moveLeaf, removeBlock, removeLeaf, reorderTab, resizeAt, setActiveTab } from "./layout/tree.js";
|
|
81
84
|
import { blockTitle, defineBlocks } from "./layout/registry.js";
|
|
82
85
|
import { useLayout } from "./layout/useLayout.js";
|
|
86
|
+
import { useClipboard } from "./composables/useClipboard.js";
|
|
83
87
|
import { appleModifierLabel, isAppleKeyboard, useHotkeys } from "./composables/useHotkeys.js";
|
|
84
88
|
import { useInputFilter } from "./composables/useInputFilter.js";
|
|
85
89
|
import { easeOut, easeTravel, easingToken, prefersReducedMotion } from "./composables/easing.js";
|
|
@@ -124,78 +128,81 @@ function parseConfig(json) {
|
|
|
124
128
|
export {
|
|
125
129
|
CHART_PALETTE,
|
|
126
130
|
D as DEFAULT_SURFACE,
|
|
131
|
+
DEFAULT_SWATCHES,
|
|
127
132
|
_ as LayoutCanvas,
|
|
133
|
+
_4 as LpActionBar,
|
|
128
134
|
_2 as LpAddressInput,
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
_5 as LpAlert,
|
|
136
|
+
_6 as LpAppShell,
|
|
137
|
+
_7 as LpAutocomplete,
|
|
138
|
+
_8 as LpAvatar,
|
|
139
|
+
_9 as LpBadge,
|
|
140
|
+
_10 as LpBreadcrumbs,
|
|
141
|
+
_11 as LpButton,
|
|
142
|
+
_12 as LpCalendar,
|
|
143
|
+
_13 as LpCard,
|
|
144
|
+
default2 as LpChart,
|
|
139
145
|
_14 as LpCheckbox,
|
|
140
146
|
_15 as LpCodeBlock,
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
_17 as
|
|
144
|
-
_18 as
|
|
145
|
-
_19 as
|
|
146
|
-
_20 as
|
|
147
|
-
_21 as
|
|
148
|
-
_22 as
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
_24 as
|
|
152
|
-
_25 as
|
|
153
|
-
_26 as
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
_28 as
|
|
157
|
-
_29 as
|
|
158
|
-
_30 as
|
|
159
|
-
_31 as
|
|
147
|
+
_16 as LpColorPicker,
|
|
148
|
+
default3 as LpCommandPalette,
|
|
149
|
+
_17 as LpConfirmDialog,
|
|
150
|
+
_18 as LpContextMenu,
|
|
151
|
+
_19 as LpDatePicker,
|
|
152
|
+
_20 as LpDisclosure,
|
|
153
|
+
_21 as LpDivider,
|
|
154
|
+
_22 as LpDrawer,
|
|
155
|
+
_23 as LpDropdownMenu,
|
|
156
|
+
default4 as LpDropzone,
|
|
157
|
+
_24 as LpEmptyState,
|
|
158
|
+
_25 as LpFileTree,
|
|
159
|
+
_26 as LpFormField,
|
|
160
|
+
_27 as LpIcon,
|
|
161
|
+
default5 as LpImageEditor,
|
|
162
|
+
_28 as LpInfraNode,
|
|
163
|
+
_29 as LpInput,
|
|
164
|
+
_30 as LpLightbox,
|
|
165
|
+
_31 as LpLink,
|
|
166
|
+
_32 as LpLogViewer,
|
|
160
167
|
_3 as LpMapPicker,
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
168
|
+
_33 as LpModal,
|
|
169
|
+
_34 as LpNotificationBell,
|
|
170
|
+
_35 as LpNumberField,
|
|
171
|
+
_36 as LpNumberFlow,
|
|
172
|
+
_37 as LpOtpInput,
|
|
173
|
+
_38 as LpPagination,
|
|
174
|
+
_39 as LpPasswordInput,
|
|
175
|
+
_41 as LpPhoneInput,
|
|
176
|
+
_40 as LpPie,
|
|
177
|
+
_42 as LpPopover,
|
|
178
|
+
_43 as LpProgress,
|
|
179
|
+
_44 as LpRadio,
|
|
180
|
+
_45 as LpRadioGroup,
|
|
181
|
+
_46 as LpRollingText,
|
|
182
|
+
_47 as LpScrollArea,
|
|
183
|
+
_49 as LpSection,
|
|
184
|
+
_48 as LpSegmented,
|
|
185
|
+
_50 as LpSelect,
|
|
186
|
+
_51 as LpServiceNode,
|
|
187
|
+
_52 as LpShift,
|
|
188
|
+
default6 as LpShortcutsDialog,
|
|
189
|
+
_53 as LpSidebar,
|
|
190
|
+
_54 as LpSkeleton,
|
|
191
|
+
_55 as LpSlider,
|
|
192
|
+
_56 as LpSparkline,
|
|
193
|
+
_57 as LpStat,
|
|
194
|
+
_58 as LpStepper,
|
|
195
|
+
_59 as LpSwitch,
|
|
196
|
+
default7 as LpTable,
|
|
197
|
+
_60 as LpTableOfContents,
|
|
198
|
+
_61 as LpTabs,
|
|
199
|
+
_62 as LpTextarea,
|
|
200
|
+
_63 as LpThemeSwitcher,
|
|
201
|
+
_64 as LpTilt,
|
|
202
|
+
_65 as LpToaster,
|
|
203
|
+
_66 as LpTooltip,
|
|
204
|
+
_67 as LpTopologyCanvas,
|
|
205
|
+
_68 as LpUptimeBar,
|
|
199
206
|
N as NO_BACKDROP_ATTR,
|
|
200
207
|
OSM_TILES,
|
|
201
208
|
T as THEME_COOKIE,
|
|
@@ -212,6 +219,7 @@ export {
|
|
|
212
219
|
e as bootstrapTheme,
|
|
213
220
|
checkStateOf,
|
|
214
221
|
checkedCount,
|
|
222
|
+
clampHsv,
|
|
215
223
|
computeStats,
|
|
216
224
|
countBlocks,
|
|
217
225
|
countLeaves,
|
|
@@ -229,11 +237,15 @@ export {
|
|
|
229
237
|
fileNameOf,
|
|
230
238
|
flagEmoji,
|
|
231
239
|
formatAddress,
|
|
240
|
+
formatColor,
|
|
232
241
|
formatModified,
|
|
233
242
|
formatSize,
|
|
234
243
|
formatTick,
|
|
244
|
+
hsvToHsl,
|
|
245
|
+
hsvToRgb,
|
|
235
246
|
isAppleKeyboard,
|
|
236
247
|
i as isBrokenBackdropEngine,
|
|
248
|
+
isLight,
|
|
237
249
|
l as leavepulse,
|
|
238
250
|
h as light,
|
|
239
251
|
j as lime,
|
|
@@ -243,7 +255,11 @@ export {
|
|
|
243
255
|
moveLeaf,
|
|
244
256
|
niceScale,
|
|
245
257
|
n as nord,
|
|
258
|
+
normalizeHue,
|
|
259
|
+
opaqueCss,
|
|
260
|
+
parseColor,
|
|
246
261
|
parseConfig,
|
|
262
|
+
parseHex,
|
|
247
263
|
parseTheme,
|
|
248
264
|
prefersReducedMotion,
|
|
249
265
|
presets,
|
|
@@ -253,6 +269,8 @@ export {
|
|
|
253
269
|
reorderTab,
|
|
254
270
|
resizeAt,
|
|
255
271
|
resolveSlices,
|
|
272
|
+
rgbToHex,
|
|
273
|
+
rgbToHsv,
|
|
256
274
|
r as rose,
|
|
257
275
|
serializeConfig,
|
|
258
276
|
serializeLayout,
|
|
@@ -266,6 +284,7 @@ export {
|
|
|
266
284
|
m as themeToCssVars,
|
|
267
285
|
tileUrl,
|
|
268
286
|
unproject,
|
|
287
|
+
useClipboard,
|
|
269
288
|
useFormDraft,
|
|
270
289
|
useHotkeys,
|
|
271
290
|
useInputFilter,
|
package/package.json
CHANGED
package/src/component-names.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
export const COMPONENT_NAMES = [
|
|
5
5
|
"LayoutCanvas",
|
|
6
6
|
"LayoutNode",
|
|
7
|
+
"LpActionBar",
|
|
7
8
|
"LpAddressInput",
|
|
8
9
|
"LpAlert",
|
|
9
10
|
"LpAppShell",
|
|
@@ -17,6 +18,7 @@ export const COMPONENT_NAMES = [
|
|
|
17
18
|
"LpChart",
|
|
18
19
|
"LpCheckbox",
|
|
19
20
|
"LpCodeBlock",
|
|
21
|
+
"LpColorPicker",
|
|
20
22
|
"LpCommandPalette",
|
|
21
23
|
"LpConfirmDialog",
|
|
22
24
|
"LpContextMenu",
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/*
|
|
3
|
+
* A bar that rises from the bottom of the window to ask for one decision, then
|
|
4
|
+
* leaves. Unsaved changes with Save/Discard, a cookie notice, "3 rows selected"
|
|
5
|
+
* with a bulk action, an update waiting to be applied.
|
|
6
|
+
*
|
|
7
|
+
* It is deliberately NOT a dialog and NOT a toast, because it is neither:
|
|
8
|
+
*
|
|
9
|
+
* A dialog takes the page hostage. The whole point of an unsaved-changes bar
|
|
10
|
+
* is that you can keep editing while it sits there — trapping focus and
|
|
11
|
+
* dimming the form behind it would be answering a question the user has not
|
|
12
|
+
* been asked yet.
|
|
13
|
+
*
|
|
14
|
+
* A toast leaves on a timer. A decision that vanishes after four seconds is a
|
|
15
|
+
* decision the user can miss, and "your changes were lost because you looked
|
|
16
|
+
* away" is the outcome. This stays until something resolves it.
|
|
17
|
+
*
|
|
18
|
+
* So: fixed to the bottom, above the page but below modals, never focus-
|
|
19
|
+
* trapped, and it does not close itself. `role="region"` with an accessible
|
|
20
|
+
* name rather than `alertdialog` — it is a landmark you can Tab into, not a
|
|
21
|
+
* thing demanding an answer before anything else can happen.
|
|
22
|
+
*/
|
|
23
|
+
import { computed, nextTick, ref, useSlots, watch } from "vue"
|
|
24
|
+
import { useBarStack } from "./actionBar"
|
|
25
|
+
import { CLOSE_ICON } from "./dropdown"
|
|
26
|
+
import LpButton from "./LpButton.vue"
|
|
27
|
+
import LpIcon from "./LpIcon.vue"
|
|
28
|
+
|
|
29
|
+
export interface ActionBarAction {
|
|
30
|
+
label: string
|
|
31
|
+
/** Leading icon (any name LpIcon takes). */
|
|
32
|
+
icon?: string
|
|
33
|
+
/** Mirrors LpButton's variants; the default reads as the primary action. */
|
|
34
|
+
variant?: "solid" | "action" | "outline" | "soft" | "ghost" | "muted" | "light" | "danger"
|
|
35
|
+
disabled?: boolean
|
|
36
|
+
/** Shows a spinner in place of the icon and blocks repeat clicks. */
|
|
37
|
+
loading?: boolean
|
|
38
|
+
onClick?: () => void | Promise<void>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const props = withDefaults(
|
|
42
|
+
defineProps<{
|
|
43
|
+
/** Shown or not (v-model:open, or a plain :open). */
|
|
44
|
+
open?: boolean
|
|
45
|
+
/** Lead line — "You have unsaved changes". */
|
|
46
|
+
title?: string
|
|
47
|
+
/** Second line, quieter. Long explanations belong here, not in the title. */
|
|
48
|
+
description?: string
|
|
49
|
+
/** Leading icon. Omit for a bar that is purely text + buttons. */
|
|
50
|
+
icon?: string
|
|
51
|
+
/**
|
|
52
|
+
* Buttons on the right, in reading order. The LAST one is the primary and
|
|
53
|
+
* gets the solid treatment unless it says otherwise — the confirming action
|
|
54
|
+
* sits closest to the edge the thumb and the eye finish on.
|
|
55
|
+
*/
|
|
56
|
+
actions?: ActionBarAction[]
|
|
57
|
+
/** Adds a "✕" that emits `dismiss`. For notices, not for decisions. */
|
|
58
|
+
dismissible?: boolean
|
|
59
|
+
/**
|
|
60
|
+
* Tone. `danger` for a destructive pending action; the default stays neutral
|
|
61
|
+
* because most of these bars are routine and a page edged in red for every
|
|
62
|
+
* unsaved field trains people to ignore red.
|
|
63
|
+
*/
|
|
64
|
+
variant?: "default" | "danger"
|
|
65
|
+
/**
|
|
66
|
+
* How wide the bar runs.
|
|
67
|
+
*
|
|
68
|
+
* "contained" (the default) keeps it to the width of its own content, up to
|
|
69
|
+
* a cap, and centres it: a bar carrying six words and two buttons has no
|
|
70
|
+
* business spanning a 1920px display, and stretching it puts the buttons a
|
|
71
|
+
* screen away from the field you were editing.
|
|
72
|
+
*
|
|
73
|
+
* "full" spans the viewport edge to edge — for a cookie notice, which reads
|
|
74
|
+
* as part of the page frame rather than as a message about one thing on it.
|
|
75
|
+
* Its CONTENTS are still capped and centred, so the buttons stay beside the
|
|
76
|
+
* text instead of being flung to the far corner.
|
|
77
|
+
*/
|
|
78
|
+
width?: "contained" | "full"
|
|
79
|
+
/**
|
|
80
|
+
* Cap for the bar (or, when `width="full"`, for the row inside it). Any CSS
|
|
81
|
+
* length. Defaults to a comfortable measure for a line of text and two or
|
|
82
|
+
* three buttons — wider for `full`, whose job is to span the page and whose
|
|
83
|
+
* contents therefore sit on the page's own measure, not a card's.
|
|
84
|
+
*/
|
|
85
|
+
maxWidth?: string
|
|
86
|
+
/** Accessible name for the landmark. Falls back to `title`. */
|
|
87
|
+
ariaLabel?: string
|
|
88
|
+
}>(),
|
|
89
|
+
{
|
|
90
|
+
open: true,
|
|
91
|
+
actions: () => [],
|
|
92
|
+
variant: "default",
|
|
93
|
+
width: "contained",
|
|
94
|
+
},
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
const emit = defineEmits<{
|
|
98
|
+
(e: "update:open", value: boolean): void
|
|
99
|
+
(e: "dismiss"): void
|
|
100
|
+
}>()
|
|
101
|
+
|
|
102
|
+
const slots = useSlots()
|
|
103
|
+
|
|
104
|
+
// The last action is the primary one unless the caller has said otherwise; the
|
|
105
|
+
// rest default to `ghost` so a bar with three buttons still has one obvious
|
|
106
|
+
// answer instead of three competing ones.
|
|
107
|
+
const resolvedActions = computed(() =>
|
|
108
|
+
props.actions.map((a, i) => ({
|
|
109
|
+
...a,
|
|
110
|
+
variant:
|
|
111
|
+
a.variant ??
|
|
112
|
+
(i === props.actions.length - 1
|
|
113
|
+
? props.variant === "danger"
|
|
114
|
+
? ("danger" as const)
|
|
115
|
+
: ("solid" as const)
|
|
116
|
+
: ("ghost" as const)),
|
|
117
|
+
})),
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
const hasBody = computed(() => !!props.title || !!props.description || !!slots.default)
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 36rem for a contained bar: a line of text and two or three buttons, at about
|
|
124
|
+
* the measure prose stays comfortable at — wider starts reading as a page
|
|
125
|
+
* element, which a contained bar is not. A full-width bar IS page furniture, so
|
|
126
|
+
* its row sits on the shell's own measure (LpAppShell centres content at
|
|
127
|
+
* max-w-6xl) rather than being squeezed into a card's width, which cropped the
|
|
128
|
+
* description of a cookie notice halfway through a word.
|
|
129
|
+
*/
|
|
130
|
+
const maxWidth = computed(() => props.maxWidth ?? (props.width === "full" ? "72rem" : "36rem"))
|
|
131
|
+
|
|
132
|
+
function run(action: ActionBarAction) {
|
|
133
|
+
if (action.disabled || action.loading) return
|
|
134
|
+
void action.onClick?.()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function dismiss() {
|
|
138
|
+
emit("dismiss")
|
|
139
|
+
emit("update:open", false)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* ── stacking ─────────────────────────────────────
|
|
143
|
+
Two bars at once is ordinary — a cookie notice over a form that has just been
|
|
144
|
+
edited — and both are translucent, so overlapping them makes each unreadable
|
|
145
|
+
through the other. Each open bar takes a slot and rides above the ones that
|
|
146
|
+
opened before it. */
|
|
147
|
+
const root = ref<HTMLElement | null>(null)
|
|
148
|
+
const { register, unregister, setHeight, offset, bars } = useBarStack()
|
|
149
|
+
|
|
150
|
+
const stackOffset = computed(() => {
|
|
151
|
+
// `bars` is read so the offset recomputes when a bar below is added, resized
|
|
152
|
+
// or removed; the value itself comes from the registry.
|
|
153
|
+
void bars.value
|
|
154
|
+
return offset()
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
watch(
|
|
158
|
+
() => props.open,
|
|
159
|
+
async (isOpen) => {
|
|
160
|
+
if (!isOpen) {
|
|
161
|
+
unregister()
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
register()
|
|
165
|
+
await nextTick()
|
|
166
|
+
if (root.value) setHeight(root.value.getBoundingClientRect().height)
|
|
167
|
+
},
|
|
168
|
+
{ immediate: true },
|
|
169
|
+
)
|
|
170
|
+
</script>
|
|
171
|
+
|
|
172
|
+
<template>
|
|
173
|
+
<!-- Teleported so the bar is pinned to the VIEWPORT rather than to whatever
|
|
174
|
+
container it was declared in: a `position: fixed` element inside an
|
|
175
|
+
ancestor that has a transform, filter or backdrop-filter anchors to that
|
|
176
|
+
ancestor instead, and the kit is full of such ancestors (any panel with a
|
|
177
|
+
blur). Declaring it next to the form it belongs to should not decide
|
|
178
|
+
where on screen it lands. -->
|
|
179
|
+
<Teleport to="body">
|
|
180
|
+
<Transition
|
|
181
|
+
enter-active-class="animate-[rise-in_var(--duration-medium)_var(--ease-emphasized)]"
|
|
182
|
+
leave-active-class="animate-[rise-out_var(--duration-fast)_ease]"
|
|
183
|
+
>
|
|
184
|
+
<div
|
|
185
|
+
v-if="open"
|
|
186
|
+
ref="root"
|
|
187
|
+
role="region"
|
|
188
|
+
:aria-label="ariaLabel ?? title"
|
|
189
|
+
class="pointer-events-none fixed inset-x-0 bottom-0 z-(--z-modal) flex justify-center transition-[translate] duration-[var(--duration-medium)] ease-[var(--ease-emphasized)] motion-reduce:transition-none"
|
|
190
|
+
:class="width === 'full' ? '' : 'p-3 sm:p-4'"
|
|
191
|
+
:style="{ translate: `0 -${stackOffset}px` }"
|
|
192
|
+
>
|
|
193
|
+
<div
|
|
194
|
+
class="lp-skin-panel pointer-events-auto flex items-center gap-3 border-solid bg-surface-overlay"
|
|
195
|
+
:class="[
|
|
196
|
+
// A full-width bar is the page's own furniture: square, edge to
|
|
197
|
+
// edge, and only its top border drawn, so it reads as a frame
|
|
198
|
+
// rather than as a card that happens to be very wide.
|
|
199
|
+
width === 'full'
|
|
200
|
+
? 'w-full justify-center border-x-0 border-b-0 px-4 py-3 sm:px-6'
|
|
201
|
+
: 'w-full rounded-card px-4 py-3',
|
|
202
|
+
variant === 'danger' ? 'border-danger/40' : 'border-line',
|
|
203
|
+
]"
|
|
204
|
+
:style="width === 'full' ? undefined : { maxWidth }"
|
|
205
|
+
>
|
|
206
|
+
<!-- `full` keeps its contents on the same measure as a contained bar,
|
|
207
|
+
so the buttons stay next to the words instead of being pushed to
|
|
208
|
+
the far edge of a wide display. -->
|
|
209
|
+
<div
|
|
210
|
+
class="flex w-full items-center gap-3"
|
|
211
|
+
:style="{ maxWidth }"
|
|
212
|
+
>
|
|
213
|
+
<LpIcon
|
|
214
|
+
v-if="icon"
|
|
215
|
+
:name="icon"
|
|
216
|
+
:size="18"
|
|
217
|
+
:class="['shrink-0', variant === 'danger' ? 'text-danger' : 'text-brand']"
|
|
218
|
+
/>
|
|
219
|
+
|
|
220
|
+
<!-- min-w-0 so a long description truncates instead of pushing the
|
|
221
|
+
buttons off the end of the bar. -->
|
|
222
|
+
<div v-if="hasBody" class="min-w-0 flex-1">
|
|
223
|
+
<slot>
|
|
224
|
+
<p v-if="title" class="truncate text-sm font-medium text-ink">{{ title }}</p>
|
|
225
|
+
<p v-if="description" class="truncate text-xs text-muted">{{ description }}</p>
|
|
226
|
+
</slot>
|
|
227
|
+
</div>
|
|
228
|
+
<div v-else class="flex-1" />
|
|
229
|
+
|
|
230
|
+
<!-- Buttons never shrink: the bar gives up text before it gives up
|
|
231
|
+
the thing you came here to click. -->
|
|
232
|
+
<div class="flex shrink-0 items-center gap-2">
|
|
233
|
+
<slot name="actions">
|
|
234
|
+
<LpButton
|
|
235
|
+
v-for="(action, i) in resolvedActions"
|
|
236
|
+
:key="i"
|
|
237
|
+
:variant="action.variant"
|
|
238
|
+
size="sm"
|
|
239
|
+
:disabled="action.disabled || action.loading"
|
|
240
|
+
@click="run(action)"
|
|
241
|
+
>
|
|
242
|
+
<LpIcon
|
|
243
|
+
v-if="action.loading"
|
|
244
|
+
name="lucide:loader-circle"
|
|
245
|
+
:size="15"
|
|
246
|
+
class="animate-spin"
|
|
247
|
+
/>
|
|
248
|
+
<LpIcon v-else-if="action.icon" :name="action.icon" :size="15" />
|
|
249
|
+
{{ action.label }}
|
|
250
|
+
</LpButton>
|
|
251
|
+
</slot>
|
|
252
|
+
|
|
253
|
+
<button
|
|
254
|
+
v-if="dismissible"
|
|
255
|
+
type="button"
|
|
256
|
+
aria-label="Dismiss"
|
|
257
|
+
class="group -mr-1 flex size-8 shrink-0 items-center justify-center rounded-control text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink focus-visible:ring-2 focus-visible:ring-ring"
|
|
258
|
+
@click="dismiss"
|
|
259
|
+
>
|
|
260
|
+
<LpIcon name="lucide:x" :size="16" :class="CLOSE_ICON" />
|
|
261
|
+
</button>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
</Transition>
|
|
267
|
+
</Teleport>
|
|
268
|
+
</template>
|