@leavepulse/ui 0.55.0 → 0.56.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.
@@ -2,7 +2,7 @@ import { defineComponent, useSlots, computed, ref, watch, onBeforeUnmount, openB
2
2
  import { useLabels } from "../composables/useLpConfig.js";
3
3
  import { useBarStack } from "../components/actionBar.js";
4
4
  import { CLOSE_ICON } from "../components/dropdown.js";
5
- import { _ as _sfc_main$2 } from "./LpButton-CKSwC6Mu.js";
5
+ import { _ as _sfc_main$2 } from "./LpButton-BdfZFNqi.js";
6
6
  import { _ as _sfc_main$1 } from "./LpIcon-DYtaid0K.js";
7
7
  const _hoisted_1 = ["aria-label"];
8
8
  const _hoisted_2 = {
@@ -3,7 +3,7 @@ import { useAsyncTask } from "../composables/useAsyncTask.js";
3
3
  import { OSM_TILES, createNominatimProvider, emptyAddress, formatAddress } from "../components/address.js";
4
4
  import { flagEmoji, loadCountries } from "../components/countries.js";
5
5
  import { _ as _sfc_main$2 } from "./LpAutocomplete-C8IgSMsi.js";
6
- import { _ as _sfc_main$3 } from "./LpButton-CKSwC6Mu.js";
6
+ import { _ as _sfc_main$3 } from "./LpButton-BdfZFNqi.js";
7
7
  import { _ as _sfc_main$1 } from "./LpFormField-CtVYj6tO.js";
8
8
  import { _ as _sfc_main$4 } from "./LpIcon-DYtaid0K.js";
9
9
  import { _ as _sfc_main$5 } from "./LpInput-D-YGQbEv.js";
@@ -1,6 +1,6 @@
1
1
  import { defineComponent, computed, ref, useId, watch, openBlock, createElementBlock, mergeProps, unref, createElementVNode, withModifiers, toDisplayString, createVNode, createSlots, withCtx, renderSlot, normalizeProps, guardReactiveProps, normalizeClass, createBlock } from "vue";
2
2
  import { useLabels } from "../composables/useLpConfig.js";
3
- import { _ as _sfc_main$2 } from "./LpButton-CKSwC6Mu.js";
3
+ import { _ as _sfc_main$2 } from "./LpButton-BdfZFNqi.js";
4
4
  import { _ as _sfc_main$3 } from "./LpIcon-DYtaid0K.js";
5
5
  import { _ as _sfc_main$5 } from "./LpScrollArea-Hz3-JWUG.js";
6
6
  import { _ as _sfc_main$4 } from "./LpShift-DXNsauQZ.js";
@@ -1,7 +1,8 @@
1
- import { defineComponent, computed, openBlock, createBlock, resolveDynamicComponent, mergeProps, unref, withCtx, renderSlot } from "vue";
1
+ import { defineComponent, computed, openBlock, createBlock, resolveDynamicComponent, mergeProps, unref, withCtx, renderSlot, createElementBlock, normalizeClass, createVNode, createCommentVNode } from "vue";
2
2
  import { tv } from "tailwind-variants";
3
3
  import { useLinkComponent } from "../composables/useLpConfig.js";
4
4
  import { useMergedAttrs } from "../composables/useMergedClass.js";
5
+ import { _ as _sfc_main$1 } from "./LpIcon-DYtaid0K.js";
5
6
  import { resolveLink } from "../components/link.js";
6
7
  const __default__ = { inheritAttrs: false };
7
8
  const _sfc_main = /* @__PURE__ */ defineComponent({
@@ -17,7 +18,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
17
18
  to: {},
18
19
  href: {},
19
20
  type: { default: "button" },
20
- disabled: { type: Boolean }
21
+ disabled: { type: Boolean },
22
+ loading: { type: Boolean }
21
23
  },
22
24
  setup(__props) {
23
25
  const button = tv({
@@ -54,7 +56,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
54
56
  // icon-only square button — equal sides, no horizontal padding
55
57
  square: { true: "aspect-square p-0" },
56
58
  // soft elevation glow keyed to the variant's colour
57
- glow: { true: "" }
59
+ glow: { true: "" },
60
+ // busy: the label fades out in place (no width jump) and a spinner sits on top
61
+ loading: { true: "relative cursor-progress !text-transparent hover:!text-transparent" }
58
62
  },
59
63
  compoundVariants: [
60
64
  { square: true, size: "xs", class: "size-8" },
@@ -91,10 +95,18 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
91
95
  });
92
96
  const isNativeButton = computed(() => resolved.value.is === "button");
93
97
  function blockDisabled(event) {
94
- if (!props.disabled || isNativeButton.value) return;
98
+ if (!props.loading && (!props.disabled || isNativeButton.value)) return;
95
99
  event.preventDefault();
96
100
  event.stopImmediatePropagation();
97
101
  }
102
+ const SPINNER_TONE = {
103
+ solid: "text-ink-inverse",
104
+ action: "text-ink-inverse",
105
+ danger: "text-ink-inverse",
106
+ light: "text-[#0b1210]",
107
+ muted: "text-muted"
108
+ };
109
+ const spinnerTone = computed(() => SPINNER_TONE[props.variant ?? "solid"] ?? "text-ink");
98
110
  const linkProps = computed(() => {
99
111
  if (isNativeButton.value) return {};
100
112
  if (!props.disabled) return resolved.value.props;
@@ -106,7 +118,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
106
118
  size: props.size,
107
119
  block: props.block,
108
120
  square: props.square,
109
- glow: props.glow
121
+ glow: props.glow,
122
+ loading: props.loading
110
123
  })
111
124
  );
112
125
  return (_ctx, _cache) => {
@@ -115,14 +128,25 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
115
128
  disabled: isNativeButton.value ? __props.disabled : void 0,
116
129
  "aria-disabled": !isNativeButton.value && __props.disabled ? "true" : void 0,
117
130
  tabindex: !isNativeButton.value && __props.disabled ? -1 : void 0,
131
+ "aria-busy": __props.loading ? "true" : void 0,
118
132
  class: unref(classes),
119
133
  onClickCapture: blockDisabled
120
134
  }), {
121
135
  default: withCtx(() => [
122
- renderSlot(_ctx.$slots, "default")
136
+ renderSlot(_ctx.$slots, "default"),
137
+ __props.loading ? (openBlock(), createElementBlock("span", {
138
+ key: 0,
139
+ class: normalizeClass(["pointer-events-none absolute inset-0 flex items-center justify-center animate-[fade-in_var(--duration-fast)_var(--ease-emphasized)] motion-reduce:animate-none", spinnerTone.value]),
140
+ "aria-hidden": "true"
141
+ }, [
142
+ createVNode(_sfc_main$1, {
143
+ name: "lucide:loader-circle",
144
+ class: "size-[1.15em] animate-spin motion-reduce:animate-none"
145
+ })
146
+ ], 2)) : createCommentVNode("", true)
123
147
  ]),
124
148
  _: 3
125
- }, 16, ["type", "disabled", "aria-disabled", "tabindex", "class"]);
149
+ }, 16, ["type", "disabled", "aria-disabled", "tabindex", "aria-busy", "class"]);
126
150
  };
127
151
  }
128
152
  });
@@ -1,6 +1,6 @@
1
1
  import { defineComponent, computed, ref, watch, openBlock, createBlock, createSlots, withCtx, renderSlot, createElementBlock, toDisplayString, createCommentVNode, createVNode, createTextVNode, unref, createElementVNode, normalizeClass, nextTick } from "vue";
2
2
  import { useLabels } from "../composables/useLpConfig.js";
3
- import { _ as _sfc_main$2 } from "./LpButton-CKSwC6Mu.js";
3
+ import { _ as _sfc_main$2 } from "./LpButton-BdfZFNqi.js";
4
4
  import { _ as _sfc_main$3 } from "./LpIcon-DYtaid0K.js";
5
5
  import { _ as _sfc_main$1 } from "./LpModal-JNa3B_Lr.js";
6
6
  const _hoisted_1 = { class: "inline-flex items-center gap-2.5" };
@@ -1,6 +1,6 @@
1
1
  import { defineComponent, computed, ref, watch, openBlock, createBlock, createCommentVNode } from "vue";
2
2
  import { confirmQueue, settleConfirm } from "../components/confirm.js";
3
- import { _ as _sfc_main$1 } from "./LpConfirmDialog-Ca-CTlxT.js";
3
+ import { _ as _sfc_main$1 } from "./LpConfirmDialog-CrbUZgJE.js";
4
4
  const _sfc_main = /* @__PURE__ */ defineComponent({
5
5
  __name: "LpConfirmHost",
6
6
  props: {
@@ -1,7 +1,7 @@
1
1
  import { defineComponent, computed, ref, openBlock, createBlock, createSlots, withCtx, renderSlot, createElementVNode, normalizeClass, createVNode, createCommentVNode, createTextVNode, toDisplayString } from "vue";
2
2
  import { useGlass } from "../composables/useGlass.js";
3
3
  import { useLabels } from "../composables/useLpConfig.js";
4
- import { _ as _sfc_main$2 } from "./LpButton-CKSwC6Mu.js";
4
+ import { _ as _sfc_main$2 } from "./LpButton-BdfZFNqi.js";
5
5
  import { _ as _sfc_main$3 } from "./LpIcon-DYtaid0K.js";
6
6
  import { _ as _sfc_main$1 } from "./LpModal-JNa3B_Lr.js";
7
7
  import { _ as _sfc_main$4 } from "./LpSidebar-C2oolgwy.js";
@@ -1,7 +1,7 @@
1
1
  import { defineComponent, computed, useModel, getCurrentInstance, ref, onMounted, onBeforeUnmount, openBlock, createBlock, withCtx, createElementVNode, toDisplayString, createElementBlock, createCommentVNode, createVNode, Fragment, renderList, resolveDynamicComponent, normalizeClass, Transition, mergeModels } from "vue";
2
2
  import { useLabels } from "../composables/useLpConfig.js";
3
3
  import { _ as _sfc_main$8 } from "./LpBadge-Fe0D1bYx.js";
4
- import { _ as _sfc_main$7 } from "./LpButton-CKSwC6Mu.js";
4
+ import { _ as _sfc_main$7 } from "./LpButton-BdfZFNqi.js";
5
5
  import { _ as _sfc_main$5 } from "./LpContextMenu-EF_90u3g.js";
6
6
  import { _ as _sfc_main$3 } from "./LpEmptyState-BJbZXC8D.js";
7
7
  import { _ as _sfc_main$6 } from "./LpIcon-DYtaid0K.js";
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpActionBar-CnfFVIwC.js";
1
+ import { _ as _sfc_main } from "../chunks/LpActionBar-C3hkVsFz.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpAddressInput-iOJhmRzy.js";
1
+ import { _ as _sfc_main } from "../chunks/LpAddressInput-NUHmplCd.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpAppShell-DcSXik4a.js";
1
+ import { _ as _sfc_main } from "../chunks/LpAppShell-BEGjSqFl.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -20,6 +20,11 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
20
20
  href?: string;
21
21
  type?: "button" | "submit" | "reset";
22
22
  disabled?: boolean;
23
+ /**
24
+ * Busy state: blocks clicks like `disabled` without dimming, fades the label out
25
+ * in place and shows a spinner over it, so the width never jumps. Sets `aria-busy`.
26
+ */
27
+ loading?: boolean;
23
28
  }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{
24
29
  variant?: "ghost" | "action" | "muted" | "outline" | "danger" | "solid" | "light" | "soft" | undefined;
25
30
  size?: "md" | "sm" | "lg" | "xs" | undefined;
@@ -38,6 +43,11 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
38
43
  href?: string;
39
44
  type?: "button" | "submit" | "reset";
40
45
  disabled?: boolean;
46
+ /**
47
+ * Busy state: blocks clicks like `disabled` without dimming, fades the label out
48
+ * in place and shows a spinner over it, so the width never jumps. Sets `aria-busy`.
49
+ */
50
+ loading?: boolean;
41
51
  }> & Readonly<{}>, {
42
52
  type: "button" | "submit" | "reset";
43
53
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpButton-CKSwC6Mu.js";
1
+ import { _ as _sfc_main } from "../chunks/LpButton-BdfZFNqi.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpConfirmDialog-Ca-CTlxT.js";
1
+ import { _ as _sfc_main } from "../chunks/LpConfirmDialog-CrbUZgJE.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpConfirmHost-BAaSaT2b.js";
1
+ import { _ as _sfc_main } from "../chunks/LpConfirmHost-BgMIV8r9.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -4,7 +4,7 @@ import { isSideways, coverScale, cropGeometry } from "./imageCrop.js";
4
4
  import { useHotkeys } from "../composables/useHotkeys.js";
5
5
  import { useReveal } from "../composables/useReveal.js";
6
6
  import { useZoomPan } from "../composables/useZoomPan.js";
7
- import { _ as _sfc_main$1 } from "../chunks/LpButton-CKSwC6Mu.js";
7
+ import { _ as _sfc_main$1 } from "../chunks/LpButton-BdfZFNqi.js";
8
8
  import "reka-ui";
9
9
  import { _ as _sfc_main$2 } from "../chunks/LpIcon-DYtaid0K.js";
10
10
  import { useMergedAttrs } from "../composables/useMergedClass.js";
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpModalNav-aWwc_5CD.js";
1
+ import { _ as _sfc_main } from "../chunks/LpModalNav-DXBilXr0.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpNotificationBell-wZ3meeim.js";
1
+ import { _ as _sfc_main } from "../chunks/LpNotificationBell-DEL-RoHS.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -14,7 +14,7 @@ import { usePaintSelect } from "../composables/usePaintSelect.js";
14
14
  import { useRovingFocus } from "../composables/useRovingFocus.js";
15
15
  import { useLabels } from "../composables/useLpConfig.js";
16
16
  import { compareValues } from "./tableSort.js";
17
- import { _ as _sfc_main$1 } from "../chunks/LpButton-CKSwC6Mu.js";
17
+ import { _ as _sfc_main$1 } from "../chunks/LpButton-BdfZFNqi.js";
18
18
  import { toCsv, downloadFile, fileStem } from "./exportData.js";
19
19
  import { summarizeShown, parseTableState, serializeTableState, countValues } from "./tableFilter.js";
20
20
  import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
package/dist/index.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import { _ } from "./chunks/LayoutCanvas-BkaGXTjx.js";
2
- import { _ as _2 } from "./chunks/LpAddressInput-iOJhmRzy.js";
2
+ import { _ as _2 } from "./chunks/LpAddressInput-NUHmplCd.js";
3
3
  import { _ as _3 } from "./chunks/LpMapPicker-Cc84LaM3.js";
4
4
  import { OSM_TILES, createNominatimProvider, emptyAddress, formatAddress, project, tileUrl, unproject } from "./components/address.js";
5
- import { _ as _4 } from "./chunks/LpActionBar-CnfFVIwC.js";
5
+ import { _ as _4 } from "./chunks/LpActionBar-C3hkVsFz.js";
6
6
  import { _ as _5 } from "./chunks/LpAlert-B6ilrM7T.js";
7
- import { _ as _6 } from "./chunks/LpAppShell-DcSXik4a.js";
7
+ import { _ as _6 } from "./chunks/LpAppShell-BEGjSqFl.js";
8
8
  import { _ as _7 } from "./chunks/LpAutocomplete-C8IgSMsi.js";
9
9
  import { _ as _8 } from "./chunks/LpAvatar-LTvy0OkE.js";
10
10
  import { _ as _9 } from "./chunks/LpBadge-Fe0D1bYx.js";
11
11
  import { _ as _10 } from "./chunks/LpBreadcrumbs-D4TsoFju.js";
12
- import { _ as _11 } from "./chunks/LpButton-CKSwC6Mu.js";
12
+ import { _ as _11 } from "./chunks/LpButton-BdfZFNqi.js";
13
13
  import { _ as _12 } from "./chunks/LpCalendar-DrA2McTU.js";
14
14
  import { _ as _13 } from "./chunks/LpCard-DbjO5yaF.js";
15
15
  import { default as default2 } from "./components/LpChart.vue.js";
@@ -30,8 +30,8 @@ import { default as default6 } from "./components/LpPaletteStats.vue.js";
30
30
  import { default as default7 } from "./components/LpPaletteSwatches.vue.js";
31
31
  import { default as default8 } from "./components/LpKeys.vue.js";
32
32
  import { comboKeys, comboSteps } from "./components/keys.js";
33
- import { _ as _18 } from "./chunks/LpConfirmDialog-Ca-CTlxT.js";
34
- import { _ as _19 } from "./chunks/LpConfirmHost-BAaSaT2b.js";
33
+ import { _ as _18 } from "./chunks/LpConfirmDialog-CrbUZgJE.js";
34
+ import { _ as _19 } from "./chunks/LpConfirmHost-BgMIV8r9.js";
35
35
  import { confirm, useConfirm } from "./components/confirm.js";
36
36
  import { _ as _20 } from "./chunks/LpContextMenu-EF_90u3g.js";
37
37
  import { _ as _21 } from "./chunks/LpDatePicker-BrdFtSTy.js";
@@ -60,8 +60,8 @@ import { _ as _37 } from "./chunks/LpLogViewer-BfgtXVKm.js";
60
60
  import { countLevels, matchPositions, matchesTerm, ringWindow, stepMatch } from "./components/logSearch.js";
61
61
  import { _ as _38 } from "./chunks/LpMediaGallery-BnlkN_1f.js";
62
62
  import { _ as _39 } from "./chunks/LpModal-JNa3B_Lr.js";
63
- import { _ as _40 } from "./chunks/LpModalNav-aWwc_5CD.js";
64
- import { _ as _41 } from "./chunks/LpNotificationBell-wZ3meeim.js";
63
+ import { _ as _40 } from "./chunks/LpModalNav-DXBilXr0.js";
64
+ import { _ as _41 } from "./chunks/LpNotificationBell-DEL-RoHS.js";
65
65
  import { _ as _42 } from "./chunks/LpNumberField-B77O-z28.js";
66
66
  import { _ as _43 } from "./chunks/LpTimeField-Cqe7fXnh.js";
67
67
  import { _ as _44 } from "./chunks/LpNumberFlow-z4WPZcZu.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leavepulse/ui",
3
- "version": "0.55.0",
3
+ "version": "0.56.0",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -9,6 +9,7 @@ import { tv, type VariantProps } from "tailwind-variants"
9
9
  import { computed, type Component } from "vue"
10
10
  import { useLinkComponent } from "../composables/useLpConfig"
11
11
  import { useMergedAttrs } from "../composables/useMergedClass"
12
+ import LpIcon from "./LpIcon.vue"
12
13
  import { resolveLink, type LinkTarget } from "./link"
13
14
 
14
15
  const button = tv({
@@ -47,6 +48,8 @@ const button = tv({
47
48
  square: { true: "aspect-square p-0" },
48
49
  // soft elevation glow keyed to the variant's colour
49
50
  glow: { true: "" },
51
+ // busy: the label fades out in place (no width jump) and a spinner sits on top
52
+ loading: { true: "relative cursor-progress !text-transparent hover:!text-transparent" },
50
53
  },
51
54
  compoundVariants: [
52
55
  { square: true, size: "xs", class: "size-8" },
@@ -96,6 +99,11 @@ const props = withDefaults(
96
99
  href?: string
97
100
  type?: "button" | "submit" | "reset"
98
101
  disabled?: boolean
102
+ /**
103
+ * Busy state: blocks clicks like `disabled` without dimming, fades the label out
104
+ * in place and shows a spinner over it, so the width never jumps. Sets `aria-busy`.
105
+ */
106
+ loading?: boolean
99
107
  }>(),
100
108
  { type: "button" },
101
109
  )
@@ -119,11 +127,20 @@ const isNativeButton = computed(() => resolved.value.is === "button")
119
127
  * own handler, which skips navigation once the event is default-prevented.
120
128
  */
121
129
  function blockDisabled(event: MouseEvent) {
122
- if (!props.disabled || isNativeButton.value) return
130
+ if (!props.loading && (!props.disabled || isNativeButton.value)) return
123
131
  event.preventDefault()
124
132
  event.stopImmediatePropagation()
125
133
  }
126
134
 
135
+ const SPINNER_TONE: Record<string, string> = {
136
+ solid: "text-ink-inverse",
137
+ action: "text-ink-inverse",
138
+ danger: "text-ink-inverse",
139
+ light: "text-[#0b1210]",
140
+ muted: "text-muted",
141
+ }
142
+ const spinnerTone = computed(() => SPINNER_TONE[props.variant ?? "solid"] ?? "text-ink")
143
+
127
144
  const linkProps = computed(() => {
128
145
  if (isNativeButton.value) return {}
129
146
  if (!props.disabled) return resolved.value.props
@@ -137,6 +154,7 @@ const { class: classes, attrs: rest } = useMergedAttrs(() =>
137
154
  block: props.block,
138
155
  square: props.square,
139
156
  glow: props.glow,
157
+ loading: props.loading,
140
158
  }),
141
159
  )
142
160
  </script>
@@ -149,9 +167,18 @@ const { class: classes, attrs: rest } = useMergedAttrs(() =>
149
167
  :disabled="isNativeButton ? disabled : undefined"
150
168
  :aria-disabled="!isNativeButton && disabled ? 'true' : undefined"
151
169
  :tabindex="!isNativeButton && disabled ? -1 : undefined"
170
+ :aria-busy="loading ? 'true' : undefined"
152
171
  :class="classes"
153
172
  @click.capture="blockDisabled"
154
173
  >
155
174
  <slot />
175
+ <span
176
+ v-if="loading"
177
+ class="pointer-events-none absolute inset-0 flex items-center justify-center animate-[fade-in_var(--duration-fast)_var(--ease-emphasized)] motion-reduce:animate-none"
178
+ :class="spinnerTone"
179
+ aria-hidden="true"
180
+ >
181
+ <LpIcon name="lucide:loader-circle" class="size-[1.15em] animate-spin motion-reduce:animate-none" />
182
+ </span>
156
183
  </component>
157
184
  </template>