@leavepulse/ui 0.22.22 → 0.24.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.
Files changed (56) hide show
  1. package/dist/chunks/{LpAddressInput-peTpNSJK.js → LpAddressInput-BTqnUXZG.js} +1 -1
  2. package/dist/chunks/{LpAvatar-B1Ez0ZAx.js → LpAvatar-CzCE5cXg.js} +30 -8
  3. package/dist/chunks/LpBreadcrumbs-5uvViCXi.js +60 -0
  4. package/dist/chunks/{LpConfirmDialog-DOU_zaTI.js → LpConfirmDialog-CObRah9c.js} +10 -5
  5. package/dist/chunks/LpEmptyState-BV3q7Qyi.js +49 -0
  6. package/dist/chunks/{LpFileTree-Cw50eG_y.js → LpFileTree-nU1wRRxZ.js} +2 -2
  7. package/dist/chunks/{LpFileTreeNode-DFg4PhOK.js → LpFileTreeNode-D7WgUPcJ.js} +23 -21
  8. package/dist/chunks/{LpInput-BfLIWV-P.js → LpInput-jwqXlAy0.js} +5 -2
  9. package/dist/chunks/LpLink-Bu01SZzB.js +51 -0
  10. package/dist/chunks/{LpNotificationBell-a08cl0yY.js → LpNotificationBell-X1M30e0X.js} +1 -1
  11. package/dist/chunks/{LpPasswordInput-BfjDrvZq.js → LpPasswordInput-Bmo6q9Kd.js} +11 -3
  12. package/dist/chunks/{LpStepper-5dFTrW21.js → LpStepper-Wtl-6nUh.js} +27 -10
  13. package/dist/chunks/{LpTextarea-BDl7PR9U.js → LpTextarea-1ENb3ZAO.js} +6 -3
  14. package/dist/chunks/{LpThemeSwitcher-B_kj0T2b.js → LpThemeSwitcher-BgpJgrnP.js} +27 -0
  15. package/dist/components/LpAddressInput.vue.js +1 -1
  16. package/dist/components/LpAvatar.vue.d.ts +17 -3
  17. package/dist/components/LpAvatar.vue.js +1 -1
  18. package/dist/components/LpBreadcrumbs.vue.d.ts +13 -1
  19. package/dist/components/LpBreadcrumbs.vue.js +1 -1
  20. package/dist/components/LpConfirmDialog.vue.d.ts +8 -0
  21. package/dist/components/LpConfirmDialog.vue.js +1 -1
  22. package/dist/components/LpEmptyState.vue.d.ts +5 -0
  23. package/dist/components/LpEmptyState.vue.js +1 -1
  24. package/dist/components/LpFileTree.vue.js +1 -1
  25. package/dist/components/LpFileTreeNode.vue.js +1 -1
  26. package/dist/components/LpInput.vue.d.ts +12 -0
  27. package/dist/components/LpInput.vue.js +1 -1
  28. package/dist/components/LpLink.vue.d.ts +24 -4
  29. package/dist/components/LpLink.vue.js +1 -1
  30. package/dist/components/LpNotificationBell.vue.js +1 -1
  31. package/dist/components/LpPasswordInput.vue.d.ts +8 -0
  32. package/dist/components/LpPasswordInput.vue.js +1 -1
  33. package/dist/components/LpStepper.vue.d.ts +15 -1
  34. package/dist/components/LpStepper.vue.js +1 -1
  35. package/dist/components/LpTextarea.vue.d.ts +5 -0
  36. package/dist/components/LpTextarea.vue.js +1 -1
  37. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  38. package/dist/index.js +14 -14
  39. package/dist/theme/tokens.d.ts +3 -0
  40. package/package.json +1 -1
  41. package/src/components/LpAvatar.vue +33 -5
  42. package/src/components/LpBreadcrumbs.vue +30 -7
  43. package/src/components/LpConfirmDialog.vue +21 -6
  44. package/src/components/LpEmptyState.vue +18 -5
  45. package/src/components/LpFileTreeNode.vue +11 -2
  46. package/src/components/LpInput.vue +8 -0
  47. package/src/components/LpLink.vue +48 -10
  48. package/src/components/LpPasswordInput.vue +16 -1
  49. package/src/components/LpStepper.vue +49 -9
  50. package/src/components/LpTextarea.vue +7 -0
  51. package/src/theme/presets.ts +24 -0
  52. package/src/theme/tokens.ts +6 -0
  53. package/src/tokens/tokens.css +3 -0
  54. package/dist/chunks/LpBreadcrumbs-B7Aqatzw.js +0 -42
  55. package/dist/chunks/LpEmptyState-CrfyRJUG.js +0 -47
  56. package/dist/chunks/LpLink-DHwOEp4e.js +0 -36
@@ -1,12 +1,27 @@
1
1
  <script setup lang="ts">
2
+ import type { Component } from "vue"
2
3
  import LpIcon from "./LpIcon.vue"
3
4
 
4
5
  export interface Crumb {
5
6
  label: string
6
7
  href?: string
8
+ /** Destination for router links; passed straight to the `as` component. */
9
+ to?: string | Record<string, unknown>
10
+ /** Leading icon, for crumbs that carry a glyph (a home root, a section). */
11
+ icon?: string
7
12
  }
8
13
 
9
- defineProps<{ items: Crumb[] }>()
14
+ withDefaults(
15
+ defineProps<{
16
+ items: Crumb[]
17
+ /**
18
+ * Root element/component for linked crumbs. Defaults to a native <a>;
19
+ * pass a router link component to keep client-side navigation.
20
+ */
21
+ as?: string | Component
22
+ }>(),
23
+ { as: "a" },
24
+ )
10
25
  defineEmits<{ (e: "navigate", item: Crumb, index: number): void }>()
11
26
  </script>
12
27
 
@@ -19,15 +34,23 @@ defineEmits<{ (e: "navigate", item: Crumb, index: number): void }>()
19
34
  :size="14"
20
35
  class="text-muted"
21
36
  />
22
- <a
23
- v-if="item.href && i < items.length - 1"
24
- :href="item.href"
25
- class="rounded-xs text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink focus-visible:ring-2 focus-visible:ring-ring"
37
+ <component
38
+ :is="as"
39
+ v-if="(item.href || item.to) && i < items.length - 1"
40
+ :href="as === 'a' ? item.href : undefined"
41
+ :to="as === 'a' ? undefined : item.to"
42
+ class="inline-flex items-center gap-1.5 rounded-xs text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink focus-visible:ring-2 focus-visible:ring-ring"
26
43
  @click="$emit('navigate', item, i)"
27
44
  >
45
+ <LpIcon v-if="item.icon" :name="item.icon" :size="14" />
28
46
  {{ item.label }}
29
- </a>
30
- <span v-else :class="i === items.length - 1 ? 'text-ink' : 'text-muted'">
47
+ </component>
48
+ <span
49
+ v-else
50
+ class="inline-flex items-center gap-1.5"
51
+ :class="i === items.length - 1 ? 'text-ink' : 'text-muted'"
52
+ >
53
+ <LpIcon v-if="item.icon" :name="item.icon" :size="14" />
31
54
  {{ item.label }}
32
55
  </span>
33
56
  </template>
@@ -3,7 +3,7 @@
3
3
  import LpButton from "./LpButton.vue"
4
4
  import LpModal from "./LpModal.vue"
5
5
 
6
- withDefaults(
6
+ const props = withDefaults(
7
7
  defineProps<{
8
8
  open?: boolean
9
9
  title?: string
@@ -11,8 +11,15 @@ withDefaults(
11
11
  confirmLabel?: string
12
12
  cancelLabel?: string
13
13
  danger?: boolean
14
+ /**
15
+ * The confirm action is in flight: the button disables and swaps to
16
+ * `loadingLabel`, and confirming no longer closes the dialog — the owner
17
+ * closes it once the work settles, so a failure can stay on screen.
18
+ */
19
+ loading?: boolean
20
+ loadingLabel?: string
14
21
  }>(),
15
- { confirmLabel: "Confirm", cancelLabel: "Cancel" },
22
+ { confirmLabel: "Confirm", cancelLabel: "Cancel", loadingLabel: "Working…" },
16
23
  )
17
24
 
18
25
  const emit = defineEmits<{
@@ -23,7 +30,9 @@ const emit = defineEmits<{
23
30
 
24
31
  function confirm() {
25
32
  emit("confirm")
26
- emit("update:open", false)
33
+ // An async confirm owns its own dismissal; closing here would tear the
34
+ // dialog down before the caller knows whether the action succeeded.
35
+ if (!props.loading) emit("update:open", false)
27
36
  }
28
37
  function cancel() {
29
38
  emit("cancel")
@@ -40,9 +49,15 @@ function cancel() {
40
49
  >
41
50
  <slot />
42
51
  <template #footer>
43
- <LpButton variant="ghost" @click="cancel">{{ cancelLabel }}</LpButton>
44
- <LpButton :variant="danger ? 'danger' : 'solid'" @click="confirm">
45
- {{ confirmLabel }}
52
+ <LpButton variant="ghost" :disabled="loading" @click="cancel">
53
+ {{ cancelLabel }}
54
+ </LpButton>
55
+ <LpButton
56
+ :variant="danger ? 'danger' : 'solid'"
57
+ :disabled="loading"
58
+ @click="confirm"
59
+ >
60
+ {{ loading ? loadingLabel : confirmLabel }}
46
61
  </LpButton>
47
62
  </template>
48
63
  </LpModal>
@@ -1,19 +1,32 @@
1
1
  <script setup lang="ts">
2
2
  import LpIcon from "./LpIcon.vue"
3
3
 
4
- defineProps<{ icon?: string; title: string; description?: string }>()
4
+ defineProps<{
5
+ icon?: string
6
+ title: string
7
+ description?: string
8
+ /**
9
+ * Tightens the block for empty states nested inside a card or a panel,
10
+ * where the full-page padding would dwarf its container.
11
+ */
12
+ compact?: boolean
13
+ }>()
5
14
  </script>
6
15
 
7
16
  <template>
8
- <div class="flex flex-col items-center gap-3 px-6 py-12 text-center">
17
+ <div
18
+ class="flex flex-col items-center px-6 text-center"
19
+ :class="compact ? 'gap-2 py-6' : 'gap-3 py-12'"
20
+ >
9
21
  <div
10
22
  v-if="icon"
11
- class="flex size-12 items-center justify-center rounded-pill bg-surface-soft text-muted"
23
+ class="flex items-center justify-center rounded-pill bg-surface-soft text-muted"
24
+ :class="compact ? 'size-9' : 'size-12'"
12
25
  >
13
- <LpIcon :name="icon" :size="22" />
26
+ <LpIcon :name="icon" :size="compact ? 16 : 22" />
14
27
  </div>
15
28
  <div class="flex flex-col gap-1">
16
- <p class="font-semibold text-ink">{{ title }}</p>
29
+ <p class="font-semibold text-ink" :class="compact ? 'text-sm' : ''">{{ title }}</p>
17
30
  <p v-if="description" class="max-w-sm text-sm text-muted">{{ description }}</p>
18
31
  </div>
19
32
  <div v-if="$slots.default" class="mt-1"><slot /></div>
@@ -178,8 +178,17 @@ function onActivate() {
178
178
  @click.stop="onActivate"
179
179
  >
180
180
  <!-- Chevron sits in a fixed-width slot so names line up whether or not
181
- the row can expand. Leaves get the same gutter, left empty. -->
182
- <span class="flex size-4 shrink-0 items-center justify-center">
181
+ the row can expand. Leaves get the same gutter, left empty.
182
+
183
+ It is its own hit target, like the checkbox: opening a folder to
184
+ see what is in it is a different intent from selecting it, and on
185
+ a tree that drives a details pane the two must not be the same
186
+ click. The row as a whole still does both. -->
187
+ <span
188
+ class="flex size-4 shrink-0 items-center justify-center"
189
+ :class="canExpand ? 'cursor-pointer rounded-sm hover:bg-line' : ''"
190
+ @click.stop="canExpand && !node.disabled ? emit('toggle', node) : undefined"
191
+ >
183
192
  <LpIcon
184
193
  v-if="isLoading"
185
194
  name="lucide:loader-circle"
@@ -54,6 +54,12 @@ const props = withDefaults(
54
54
  hint?: string
55
55
  error?: string
56
56
  icon?: string
57
+ /**
58
+ * Marks the label with the required indicator. The attribute still reaches
59
+ * the underlying <input> for native validation; this only makes the field
60
+ * read as required.
61
+ */
62
+ required?: boolean
57
63
  /**
58
64
  * RegExp/string the whole value must match to be valid. Validation only —
59
65
  * never blocks typing; a non-matching non-empty value shows the invalid
@@ -118,6 +124,7 @@ defineExpose({
118
124
  :label="hasField ? label : undefined"
119
125
  :hint="hasField ? hint : undefined"
120
126
  :error="hasField ? error : undefined"
127
+ :required="hasField ? required : undefined"
121
128
  :class="hasField ? rootClass : undefined"
122
129
  :style="hasField ? rootStyle : undefined"
123
130
  >
@@ -136,6 +143,7 @@ defineExpose({
136
143
  :value="modelValue"
137
144
  :placeholder="placeholder"
138
145
  :disabled="disabled"
146
+ :required="required || undefined"
139
147
  :aria-invalid="isInvalidState || undefined"
140
148
  :class="[
141
149
  'lp-autofill min-w-0 flex-1 self-stretch bg-transparent outline-none placeholder:text-muted',
@@ -1,28 +1,61 @@
1
1
  <script setup lang="ts">
2
- import { computed } from "vue"
2
+ import { computed, type Component } from "vue"
3
3
 
4
4
  const props = withDefaults(
5
5
  defineProps<{
6
6
  href?: string
7
7
  external?: boolean
8
+ /** Shorthand for `tone="muted"`. */
8
9
  muted?: boolean
10
+ /**
11
+ * "brand" — the accent link colour (default).
12
+ * "ink" — inherits body text colour; for links that sit inside prose or
13
+ * chrome where an accent would shout (breadcrumbs, footers, nav).
14
+ * "muted" / "soft" — progressively quieter, both resolving to ink on hover.
15
+ */
16
+ tone?: "brand" | "ink" | "muted" | "soft"
17
+ /**
18
+ * Root element/component. Defaults to a native <a>. Pass a router link
19
+ * component (e.g. NuxtLink or RouterLink) to keep client-side navigation;
20
+ * the kit stays DOM-agnostic, the consumer supplies the link component.
21
+ * Router links take their destination through their own prop (`to`), which
22
+ * falls through as a plain attribute.
23
+ */
24
+ as?: string | Component
9
25
  /**
10
26
  * "default" — plain hover underline.
27
+ * "underline" — underlined at rest; for links inside running prose,
28
+ * where hover-only underlines leave the link invisible until pointed at.
11
29
  * "reveal" — an underline bar that grows in from the left on hover
12
30
  * (origin-left scale-x 0→1). The LeavePulse landing "monitoring" link feel;
13
31
  * good for inline prose links and nav items that want a bit of motion.
14
32
  */
15
- variant?: "default" | "reveal"
33
+ variant?: "default" | "underline" | "reveal"
16
34
  }>(),
17
- { variant: "default" },
35
+ { variant: "default", as: "a" },
18
36
  )
19
37
 
38
+ // A router link component owns its destination through its own prop (`to`),
39
+ // so `href` is only bound when this really renders an anchor. `target`/`rel`
40
+ // stay in play either way — router links honour them for external targets.
41
+ const isAnchor = computed(() => props.as === "a")
42
+
20
43
  const rel = computed(() => (props.external ? "noopener noreferrer" : undefined))
21
44
  const target = computed(() => (props.external ? "_blank" : undefined))
22
45
 
23
- const colorClass = computed(() =>
24
- props.muted ? "text-muted hover:text-ink" : "text-brand hover:text-brand-hover",
25
- )
46
+ const colorClass = computed(() => {
47
+ const tone = props.tone ?? (props.muted ? "muted" : "brand")
48
+ switch (tone) {
49
+ case "ink":
50
+ return "text-ink"
51
+ case "muted":
52
+ return "text-muted-strong hover:text-ink"
53
+ case "soft":
54
+ return "text-muted hover:text-ink"
55
+ default:
56
+ return "text-brand hover:text-brand-hover"
57
+ }
58
+ })
26
59
 
27
60
  // The reveal bar: an ::after pseudo-element pinned to the baseline, full-width
28
61
  // but scaled to 0 on the x-axis from the left, growing to 1 on hover/focus. The
@@ -33,16 +66,21 @@ const revealClass =
33
66
  </script>
34
67
 
35
68
  <template>
36
- <a
37
- :href="href"
69
+ <component
70
+ :is="as"
71
+ :href="isAnchor ? href : undefined"
38
72
  :target="target"
39
73
  :rel="rel"
40
74
  class="inline-flex items-center gap-1 underline-offset-2 outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring"
41
75
  :class="[
42
76
  colorClass,
43
- variant === 'reveal' ? revealClass : 'hover:underline',
77
+ variant === 'reveal'
78
+ ? revealClass
79
+ : variant === 'underline'
80
+ ? 'underline decoration-current/40 hover:decoration-current'
81
+ : 'hover:underline',
44
82
  ]"
45
83
  >
46
84
  <slot />
47
- </a>
85
+ </component>
48
86
  </template>
@@ -9,7 +9,18 @@ import { ref } from "vue"
9
9
  import LpIcon from "./LpIcon.vue"
10
10
  import LpInput from "./LpInput.vue"
11
11
 
12
- defineProps<{ modelValue?: string; placeholder?: string; invalid?: boolean; disabled?: boolean }>()
12
+ defineProps<{
13
+ modelValue?: string
14
+ placeholder?: string
15
+ invalid?: boolean
16
+ disabled?: boolean
17
+ // Field wrapping mirrors LpInput: passing any of these wraps the control in
18
+ // LpFormField, so a password field labels itself like every other field.
19
+ label?: string
20
+ hint?: string
21
+ error?: string
22
+ required?: boolean
23
+ }>()
13
24
  defineEmits<{ (e: "update:modelValue", value: string): void }>()
14
25
 
15
26
  const visible = ref(false)
@@ -29,6 +40,10 @@ defineExpose({
29
40
  :placeholder="placeholder"
30
41
  :invalid="invalid"
31
42
  :disabled="disabled"
43
+ :label="label"
44
+ :hint="hint"
45
+ :error="error"
46
+ :required="required"
32
47
  @update:model-value="(v) => $emit('update:modelValue', v)"
33
48
  >
34
49
  <template v-if="$slots.leading" #leading><slot name="leading" /></template>
@@ -1,45 +1,85 @@
1
1
  <script setup lang="ts">
2
+ import { computed } from "vue"
2
3
  import LpIcon from "./LpIcon.vue"
3
4
 
4
5
  export interface Step {
5
6
  label: string
7
+ /** Stable id, for flows whose steps are addressed by name rather than order. */
8
+ key?: string
9
+ /** Replaces the step number while the step is pending or current. */
10
+ icon?: string
6
11
  }
7
12
 
8
- defineProps<{ steps: Step[]; current: number }>()
13
+ const props = defineProps<{
14
+ steps: Step[]
15
+ /**
16
+ * The active step: an index, or a step `key` for keyed flows.
17
+ */
18
+ current: number | string
19
+ /**
20
+ * Which steps count as done. Omit it and everything before `current` is
21
+ * complete, which is the linear case. Pass keys (or indices) when a flow can
22
+ * finish steps out of order — a verification that succeeds before its
23
+ * prerequisite is filled in, say.
24
+ */
25
+ completed?: Array<string | number>
26
+ }>()
27
+
28
+ const currentIndex = computed(() => {
29
+ if (typeof props.current === "number") return props.current
30
+ return props.steps.findIndex((s) => s.key === props.current)
31
+ })
32
+
33
+ function isCompleted(step: Step, i: number): boolean {
34
+ if (!props.completed) return i < currentIndex.value
35
+ return props.completed.includes(i) || (!!step.key && props.completed.includes(step.key))
36
+ }
37
+
38
+ function isCurrent(i: number): boolean {
39
+ return i === currentIndex.value
40
+ }
9
41
  </script>
10
42
 
11
43
  <template>
12
44
  <ol class="flex items-center gap-2">
13
45
  <li
14
46
  v-for="(step, i) in steps"
15
- :key="i"
47
+ :key="step.key ?? i"
16
48
  class="flex flex-1 items-center gap-2 last:flex-none"
17
- :aria-current="i === current ? 'step' : undefined"
49
+ :aria-current="isCurrent(i) ? 'step' : undefined"
18
50
  >
19
51
  <div class="flex items-center gap-2">
20
52
  <span
21
53
  class="flex size-7 shrink-0 items-center justify-center rounded-pill border text-xs font-semibold transition-colors"
22
- :class="i < current
54
+ :class="isCompleted(step, i)
23
55
  ? 'border-transparent bg-brand text-ink-inverse'
24
- : i === current
56
+ : isCurrent(i)
25
57
  ? 'border-brand text-brand'
26
58
  : 'border-line text-muted'"
27
59
  >
28
- <LpIcon v-if="i < current" name="lucide:check" :size="14" />
60
+ <LpIcon v-if="isCompleted(step, i)" name="lucide:check" :size="14" />
61
+ <LpIcon v-else-if="step.icon" :name="step.icon" :size="14" />
29
62
  <template v-else>{{ i + 1 }}</template>
30
63
  </span>
31
- <span class="text-sm" :class="i <= current ? 'text-ink' : 'text-muted'">
64
+ <span
65
+ class="text-sm"
66
+ :class="isCompleted(step, i) || isCurrent(i) ? 'text-ink' : 'text-muted'"
67
+ >
32
68
  {{ step.label }}
33
69
  <!-- Status for screen readers; the colour/check are visual only. -->
34
70
  <span class="sr-only">{{
35
- i < current ? "(completed)" : i === current ? "(current step)" : ""
71
+ isCompleted(step, i)
72
+ ? "(completed)"
73
+ : isCurrent(i)
74
+ ? "(current step)"
75
+ : ""
36
76
  }}</span>
37
77
  </span>
38
78
  </div>
39
79
  <span
40
80
  v-if="i < steps.length - 1"
41
81
  class="h-px flex-1"
42
- :class="i < current ? 'bg-brand' : 'bg-line'"
82
+ :class="isCompleted(step, i) ? 'bg-brand' : 'bg-line'"
43
83
  />
44
84
  </li>
45
85
  </ol>
@@ -11,6 +11,11 @@ const props = defineProps<{
11
11
  label?: string
12
12
  hint?: string
13
13
  error?: string
14
+ /**
15
+ * Marks the label with the required indicator; the attribute still reaches
16
+ * the <textarea> for native validation.
17
+ */
18
+ required?: boolean
14
19
  }>()
15
20
 
16
21
  defineEmits<{ (e: "update:modelValue", value: string): void }>()
@@ -24,6 +29,7 @@ const hasField = computed(() => !!props.label || !!props.hint || !!props.error)
24
29
  :label="hasField ? label : undefined"
25
30
  :hint="hasField ? hint : undefined"
26
31
  :error="hasField ? error : undefined"
32
+ :required="hasField ? required : undefined"
27
33
  :class="hasField ? undefined : 'contents'"
28
34
  >
29
35
  <textarea
@@ -31,6 +37,7 @@ const hasField = computed(() => !!props.label || !!props.hint || !!props.error)
31
37
  :placeholder="placeholder"
32
38
  :rows="rows ?? 4"
33
39
  :disabled="disabled"
40
+ :required="required || undefined"
34
41
  class="w-full resize-y rounded-control border bg-surface-soft px-3 py-2 text-sm text-ink outline-none transition-colors duration-[var(--duration-fast)] placeholder:text-muted focus:border-brand focus:ring-2 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-55"
35
42
  :class="(invalid || error) ? 'border-danger focus:border-danger focus:ring-danger-soft' : 'border-line'"
36
43
  @input="$emit('update:modelValue', ($event.target as HTMLTextAreaElement).value)"
@@ -35,7 +35,10 @@ export const dark: TokenSet = {
35
35
  brandSoft: "rgba(0, 188, 255, 0.16)",
36
36
  action: "#39e58c",
37
37
  actionHover: "#4dffa1",
38
+ actionSoft: "rgba(57, 229, 140, 0.14)",
38
39
  accent: "#f0b35a",
40
+ accentHover: "#f2bf74",
41
+ accentSoft: "rgba(240, 179, 90, 0.14)",
39
42
  danger: "#ff6f6f",
40
43
  dangerHover: "#ff8585",
41
44
  dangerSoft: "rgba(255, 111, 111, 0.14)",
@@ -64,7 +67,10 @@ export const light: TokenSet = {
64
67
  brandSoft: "rgba(0, 134, 199, 0.12)",
65
68
  action: "#1f9d5f",
66
69
  actionHover: "#23b06b",
70
+ actionSoft: "rgba(31, 157, 95, 0.12)",
67
71
  accent: "#aa6f1d",
72
+ accentHover: "#b88641",
73
+ accentSoft: "rgba(170, 111, 29, 0.12)",
68
74
  danger: "#c2333b",
69
75
  dangerHover: "#d23b44",
70
76
  dangerSoft: "rgba(194, 51, 59, 0.12)",
@@ -93,7 +99,10 @@ export const lime: TokenSet = {
93
99
  brandSoft: "rgba(196, 240, 66, 0.14)",
94
100
  action: "#5fd97a",
95
101
  actionHover: "#74e88d",
102
+ actionSoft: "rgba(95, 217, 122, 0.14)",
96
103
  accent: "#ffb84d",
104
+ accentHover: "#ffc369",
105
+ accentSoft: "rgba(255, 184, 77, 0.14)",
97
106
  danger: "#ff6f6f",
98
107
  dangerHover: "#ff8585",
99
108
  dangerSoft: "rgba(255, 111, 111, 0.14)",
@@ -148,7 +157,10 @@ export const leavepulse: TokenSet = {
148
157
  brandSoft: "rgba(0, 188, 255, 0.2)",
149
158
  action: "#44c2ad",
150
159
  actionHover: "#56d3be",
160
+ actionSoft: "rgba(68, 194, 173, 0.14)",
151
161
  accent: "#d36a2c",
162
+ accentHover: "#da824e",
163
+ accentSoft: "rgba(211, 106, 44, 0.14)",
152
164
  danger: "#f87171",
153
165
  dangerHover: "#fca5a5",
154
166
  dangerSoft: "rgba(248, 113, 113, 0.14)",
@@ -181,7 +193,10 @@ export const rose: TokenSet = {
181
193
  brandSoft: "rgba(251, 113, 133, 0.14)",
182
194
  action: "#34d399",
183
195
  actionHover: "#4adeaa",
196
+ actionSoft: "rgba(52, 211, 153, 0.14)",
184
197
  accent: "#fbbf24",
198
+ accentHover: "#fcc947",
199
+ accentSoft: "rgba(251, 191, 36, 0.14)",
185
200
  danger: "#ef4444",
186
201
  dangerHover: "#f56565",
187
202
  dangerSoft: "rgba(239, 68, 68, 0.14)",
@@ -210,7 +225,10 @@ export const amber: TokenSet = {
210
225
  brandSoft: "rgba(245, 158, 11, 0.14)",
211
226
  action: "#4ade80",
212
227
  actionHover: "#5cea90",
228
+ actionSoft: "rgba(74, 222, 128, 0.14)",
213
229
  accent: "#38bdf8",
230
+ accentHover: "#58c8f9",
231
+ accentSoft: "rgba(56, 189, 248, 0.14)",
214
232
  danger: "#f87171",
215
233
  dangerHover: "#fa8a8a",
216
234
  dangerSoft: "rgba(248, 113, 113, 0.14)",
@@ -239,7 +257,10 @@ export const violet: TokenSet = {
239
257
  brandSoft: "rgba(167, 139, 250, 0.14)",
240
258
  action: "#34d399",
241
259
  actionHover: "#4adeaa",
260
+ actionSoft: "rgba(52, 211, 153, 0.14)",
242
261
  accent: "#fbbf24",
262
+ accentHover: "#fcc947",
263
+ accentSoft: "rgba(251, 191, 36, 0.14)",
243
264
  danger: "#f87171",
244
265
  dangerHover: "#fa8a8a",
245
266
  dangerSoft: "rgba(248, 113, 113, 0.14)",
@@ -268,7 +289,10 @@ export const nord: TokenSet = {
268
289
  brandSoft: "rgba(136, 192, 208, 0.16)",
269
290
  action: "#a3be8c",
270
291
  actionHover: "#b3cb9f",
292
+ actionSoft: "rgba(163, 190, 140, 0.16)",
271
293
  accent: "#ebcb8b",
294
+ accentHover: "#eed39e",
295
+ accentSoft: "rgba(235, 203, 139, 0.16)",
272
296
  danger: "#bf616a",
273
297
  dangerHover: "#cb727b",
274
298
  dangerSoft: "rgba(191, 97, 106, 0.16)",
@@ -18,7 +18,10 @@ export interface ColorTokens {
18
18
  brandSoft: string
19
19
  action: string
20
20
  actionHover: string
21
+ actionSoft: string
21
22
  accent: string
23
+ accentHover: string
24
+ accentSoft: string
22
25
  danger: string
23
26
  dangerHover: string
24
27
  dangerSoft: string
@@ -99,7 +102,10 @@ export const COLOR_VARS: Record<keyof ColorTokens, string> = {
99
102
  brandSoft: "--color-brand-soft",
100
103
  action: "--color-action",
101
104
  actionHover: "--color-action-hover",
105
+ actionSoft: "--color-action-soft",
102
106
  accent: "--color-accent",
107
+ accentHover: "--color-accent-hover",
108
+ accentSoft: "--color-accent-soft",
103
109
  danger: "--color-danger",
104
110
  dangerHover: "--color-danger-hover",
105
111
  dangerSoft: "--color-danger-soft",
@@ -36,7 +36,10 @@
36
36
  --color-brand-soft: rgba(0, 188, 255, 0.16);
37
37
  --color-action: #39e58c;
38
38
  --color-action-hover: #4dffa1;
39
+ --color-action-soft: rgba(57, 229, 140, 0.14);
39
40
  --color-accent: #f0b35a;
41
+ --color-accent-hover: #f2bf74;
42
+ --color-accent-soft: rgba(240, 179, 90, 0.14);
40
43
  --color-danger: #ff6f6f;
41
44
  --color-danger-hover: #ff8585;
42
45
  --color-danger-soft: rgba(255, 111, 111, 0.14);
@@ -1,42 +0,0 @@
1
- import { defineComponent, openBlock, createElementBlock, Fragment, renderList, createBlock, createCommentVNode, toDisplayString, normalizeClass } from "vue";
2
- import { _ as _sfc_main$1 } from "./LpIcon-CCnX5_2j.js";
3
- const _hoisted_1 = {
4
- class: "flex items-center gap-1.5 text-sm",
5
- "aria-label": "Breadcrumb"
6
- };
7
- const _hoisted_2 = ["href", "onClick"];
8
- const _sfc_main = /* @__PURE__ */ defineComponent({
9
- __name: "LpBreadcrumbs",
10
- props: {
11
- items: {}
12
- },
13
- emits: ["navigate"],
14
- setup(__props) {
15
- return (_ctx, _cache) => {
16
- return openBlock(), createElementBlock("nav", _hoisted_1, [
17
- (openBlock(true), createElementBlock(Fragment, null, renderList(__props.items, (item, i) => {
18
- return openBlock(), createElementBlock(Fragment, { key: i }, [
19
- i > 0 ? (openBlock(), createBlock(_sfc_main$1, {
20
- key: 0,
21
- name: "lucide:chevron-right",
22
- size: 14,
23
- class: "text-muted"
24
- })) : createCommentVNode("", true),
25
- item.href && i < __props.items.length - 1 ? (openBlock(), createElementBlock("a", {
26
- key: 1,
27
- href: item.href,
28
- class: "rounded-xs text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink focus-visible:ring-2 focus-visible:ring-ring",
29
- onClick: ($event) => _ctx.$emit("navigate", item, i)
30
- }, toDisplayString(item.label), 9, _hoisted_2)) : (openBlock(), createElementBlock("span", {
31
- key: 2,
32
- class: normalizeClass(i === __props.items.length - 1 ? "text-ink" : "text-muted")
33
- }, toDisplayString(item.label), 3))
34
- ], 64);
35
- }), 128))
36
- ]);
37
- };
38
- }
39
- });
40
- export {
41
- _sfc_main as _
42
- };
@@ -1,47 +0,0 @@
1
- import { defineComponent, openBlock, createElementBlock, createVNode, createCommentVNode, createElementVNode, toDisplayString, renderSlot } from "vue";
2
- import { _ as _sfc_main$1 } from "./LpIcon-CCnX5_2j.js";
3
- const _hoisted_1 = { class: "flex flex-col items-center gap-3 px-6 py-12 text-center" };
4
- const _hoisted_2 = {
5
- key: 0,
6
- class: "flex size-12 items-center justify-center rounded-pill bg-surface-soft text-muted"
7
- };
8
- const _hoisted_3 = { class: "flex flex-col gap-1" };
9
- const _hoisted_4 = { class: "font-semibold text-ink" };
10
- const _hoisted_5 = {
11
- key: 0,
12
- class: "max-w-sm text-sm text-muted"
13
- };
14
- const _hoisted_6 = {
15
- key: 1,
16
- class: "mt-1"
17
- };
18
- const _sfc_main = /* @__PURE__ */ defineComponent({
19
- __name: "LpEmptyState",
20
- props: {
21
- icon: {},
22
- title: {},
23
- description: {}
24
- },
25
- setup(__props) {
26
- return (_ctx, _cache) => {
27
- return openBlock(), createElementBlock("div", _hoisted_1, [
28
- __props.icon ? (openBlock(), createElementBlock("div", _hoisted_2, [
29
- createVNode(_sfc_main$1, {
30
- name: __props.icon,
31
- size: 22
32
- }, null, 8, ["name"])
33
- ])) : createCommentVNode("", true),
34
- createElementVNode("div", _hoisted_3, [
35
- createElementVNode("p", _hoisted_4, toDisplayString(__props.title), 1),
36
- __props.description ? (openBlock(), createElementBlock("p", _hoisted_5, toDisplayString(__props.description), 1)) : createCommentVNode("", true)
37
- ]),
38
- _ctx.$slots.default ? (openBlock(), createElementBlock("div", _hoisted_6, [
39
- renderSlot(_ctx.$slots, "default")
40
- ])) : createCommentVNode("", true)
41
- ]);
42
- };
43
- }
44
- });
45
- export {
46
- _sfc_main as _
47
- };