@leavepulse/ui 0.14.8 → 0.14.9

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.
@@ -1,4 +1,4 @@
1
- import { defineComponent, computed, openBlock, createBlock, withCtx, createElementVNode, mergeProps, renderSlot, createElementBlock } from "vue";
1
+ import { defineComponent, computed, openBlock, createBlock, withCtx, resolveDynamicComponent, mergeProps, renderSlot } from "vue";
2
2
  import { tv } from "tailwind-variants";
3
3
  import { _ as _sfc_main$1 } from "./LpContextMenu-D0mzXqA7.js";
4
4
  const __default__ = { inheritAttrs: false };
@@ -9,7 +9,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
9
9
  variant: { default: "raised" },
10
10
  padded: { type: Boolean, default: true },
11
11
  interactive: { type: Boolean, default: false },
12
- menuItems: {}
12
+ menuItems: {},
13
+ as: { default: "div" }
13
14
  },
14
15
  setup(__props) {
15
16
  const card = tv({
@@ -32,6 +33,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
32
33
  // NOTE: in Tailwind v4 `translate-*` writes the native `translate` property
33
34
  // (not `transform`), so the transition list MUST name `translate` explicitly
34
35
  // or the lift snaps while the rest eases.
36
+ //
37
+ // The glow reaches past the card, so a clipping ancestor (scroll area,
38
+ // dialog body) will slice it unless the list reserves room — see the
39
+ // `.lp-glow-room` helper in tokens.css, whose spread tokens describe exactly
40
+ // the distance this shadow travels.
35
41
  interactive: {
36
42
  true: "cursor-pointer transition-[translate,box-shadow,border-color] duration-medium ease-[var(--ease-emphasized)] hover:-translate-y-0.5 hover:border-brand/40 hover:shadow-[var(--lp-card-glow)] hover:[--surface-panel-shadow:var(--lp-card-glow)] [--lp-card-glow:0_8px_24px_-6px_color-mix(in_srgb,var(--color-brand)_38%,transparent)] motion-reduce:transition-none motion-reduce:hover:translate-y-0"
37
43
  }
@@ -47,17 +53,23 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
47
53
  items: __props.menuItems
48
54
  }, {
49
55
  default: withCtx(() => [
50
- createElementVNode("div", mergeProps({ class: classes.value }, _ctx.$attrs), [
51
- renderSlot(_ctx.$slots, "default")
52
- ], 16)
56
+ (openBlock(), createBlock(resolveDynamicComponent(__props.as), mergeProps({ class: classes.value }, _ctx.$attrs), {
57
+ default: withCtx(() => [
58
+ renderSlot(_ctx.$slots, "default")
59
+ ]),
60
+ _: 3
61
+ }, 16, ["class"]))
53
62
  ]),
54
63
  _: 3
55
- }, 8, ["items"])) : (openBlock(), createElementBlock("div", mergeProps({
64
+ }, 8, ["items"])) : (openBlock(), createBlock(resolveDynamicComponent(__props.as), mergeProps({
56
65
  key: 1,
57
66
  class: classes.value
58
- }, _ctx.$attrs), [
59
- renderSlot(_ctx.$slots, "default")
60
- ], 16));
67
+ }, _ctx.$attrs), {
68
+ default: withCtx(() => [
69
+ renderSlot(_ctx.$slots, "default")
70
+ ]),
71
+ _: 3
72
+ }, 16, ["class"]));
61
73
  };
62
74
  }
63
75
  });
@@ -7,13 +7,18 @@ declare const __VLS_export: __VLS_WithSlots<import('vue').DefineComponent<{
7
7
  interactive?: boolean;
8
8
  /** Right-click menu for the card. Omit (or pass []) to keep the native one. */
9
9
  menuItems?: ContextMenuItemDef[];
10
+ /** Element to render as. A card inside a <ul> has to be an `li` to be valid. */
11
+ as?: string;
10
12
  }, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{
11
13
  variant?: "flat" | "ghost" | "raised" | undefined;
12
14
  padded?: boolean;
13
15
  interactive?: boolean;
14
16
  /** Right-click menu for the card. Omit (or pass []) to keep the native one. */
15
17
  menuItems?: ContextMenuItemDef[];
18
+ /** Element to render as. A card inside a <ul> has to be an `li` to be valid. */
19
+ as?: string;
16
20
  }> & Readonly<{}>, {
21
+ as: string;
17
22
  variant: "flat" | "ghost" | "raised";
18
23
  padded: boolean;
19
24
  interactive: boolean;
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpCard-1yvWT9fx.js";
1
+ import { _ as _sfc_main } from "../chunks/LpCard-BmrTvWOe.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { _ as _6 } from "./chunks/LpBadge-CXzBPnwO.js";
7
7
  import { _ as _7 } from "./chunks/LpBreadcrumbs-B7Aqatzw.js";
8
8
  import { _ as _8 } from "./chunks/LpButton-5VXXCjwF.js";
9
9
  import { _ as _9 } from "./chunks/LpCalendar-B7ZL0ty3.js";
10
- import { _ as _10 } from "./chunks/LpCard-1yvWT9fx.js";
10
+ import { _ as _10 } from "./chunks/LpCard-BmrTvWOe.js";
11
11
  import { _ as _11 } from "./chunks/LpCheckbox-BO0zuuPh.js";
12
12
  import { _ as _12 } from "./chunks/LpCodeBlock-CSiOSPs8.js";
13
13
  import { default as default2 } from "./components/LpCommandPalette.vue.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leavepulse/ui",
3
- "version": "0.14.8",
3
+ "version": "0.14.9",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -31,7 +31,7 @@
31
31
  "./dist/*": "./dist/*"
32
32
  },
33
33
  "publishConfig": {
34
- "registry": "https://npm.pkg.github.com"
34
+ "registry": "https://registry.npmjs.org"
35
35
  },
36
36
  "sideEffects": [
37
37
  "**/*.css",
@@ -30,6 +30,11 @@ const card = tv({
30
30
  // NOTE: in Tailwind v4 `translate-*` writes the native `translate` property
31
31
  // (not `transform`), so the transition list MUST name `translate` explicitly
32
32
  // or the lift snaps while the rest eases.
33
+ //
34
+ // The glow reaches past the card, so a clipping ancestor (scroll area,
35
+ // dialog body) will slice it unless the list reserves room — see the
36
+ // `.lp-glow-room` helper in tokens.css, whose spread tokens describe exactly
37
+ // the distance this shadow travels.
33
38
  interactive: {
34
39
  true: "cursor-pointer transition-[translate,box-shadow,border-color] duration-medium ease-[var(--ease-emphasized)] hover:-translate-y-0.5 hover:border-brand/40 hover:shadow-[var(--lp-card-glow)] hover:[--surface-panel-shadow:var(--lp-card-glow)] [--lp-card-glow:0_8px_24px_-6px_color-mix(in_srgb,var(--color-brand)_38%,transparent)] motion-reduce:transition-none motion-reduce:hover:translate-y-0",
35
40
  },
@@ -47,8 +52,10 @@ const props = withDefaults(
47
52
  interactive?: boolean
48
53
  /** Right-click menu for the card. Omit (or pass []) to keep the native one. */
49
54
  menuItems?: ContextMenuItemDef[]
55
+ /** Element to render as. A card inside a <ul> has to be an `li` to be valid. */
56
+ as?: string
50
57
  }>(),
51
- { variant: "raised", padded: true, interactive: false },
58
+ { variant: "raised", padded: true, interactive: false, as: "div" },
52
59
  )
53
60
 
54
61
  const classes = computed(() =>
@@ -61,11 +68,11 @@ const classes = computed(() =>
61
68
  (inheritAttrs:false above), so the card surface looks identical whether or
62
69
  not it's wrapped in a context menu. -->
63
70
  <LpContextMenu v-if="menuItems?.length" :items="menuItems">
64
- <div :class="classes" v-bind="$attrs">
71
+ <component :is="as" :class="classes" v-bind="$attrs">
65
72
  <slot />
66
- </div>
73
+ </component>
67
74
  </LpContextMenu>
68
- <div v-else :class="classes" v-bind="$attrs">
75
+ <component :is="as" v-else :class="classes" v-bind="$attrs">
69
76
  <slot />
70
- </div>
77
+ </component>
71
78
  </template>
@@ -60,6 +60,16 @@
60
60
  /* ── depth ───────────────────────────────────── */
61
61
  --shadow-panel: 0 16px 48px rgba(0, 0, 0, 0.32);
62
62
 
63
+ /* How far an interactive card's hover glow spreads past its own box, and how
64
+ far the card lifts. A scroll area or any `overflow: hidden` parent clips
65
+ whatever crosses its edge, so a list of cards has to reserve this much room
66
+ around itself or the glow gets sliced off — see the `.lp-glow-room` helper.
67
+ Derived from LpCard's glow (0 8px 24px -6px): blur 24 minus spread 6 = 18
68
+ sideways and up, plus the 8px downward offset below. */
69
+ --lp-glow-spread: 18px;
70
+ --lp-glow-spread-bottom: 26px;
71
+ --lp-card-lift: 2px;
72
+
63
73
  /* ── surface / skin ──────────────────────────────
64
74
  The skin axis (flat default). applyTheme() overrides these at runtime per
65
75
  TokenSet.surface; components paint panels via .lp-skin-panel which reads
@@ -131,16 +141,22 @@
131
141
  * of appearing. Exit drops back the same way but shorter and on an accelerating
132
142
  * curve, since a dismissal shouldn't be savoured.
133
143
  */
144
+ /*
145
+ * Centred-dialog enter/exit. Deliberately translate + fade with NO scale: the
146
+ * panel is a flex column with a max-height and its own scroll regions, and
147
+ * scaling it re-runs that layout every frame — the content visibly squashed and
148
+ * the animation stuttered. A short rise reads as arriving just as well.
149
+ */
134
150
  @keyframes pop-in {
135
151
  from {
136
152
  opacity: 0;
137
- transform: translate(-50%, calc(-50% + 8px)) scale(0.97);
153
+ transform: translate(-50%, calc(-50% + 10px));
138
154
  }
139
155
  }
140
156
  @keyframes pop-out {
141
157
  to {
142
158
  opacity: 0;
143
- transform: translate(-50%, calc(-50% + 4px)) scale(0.985);
159
+ transform: translate(-50%, calc(-50% + 5px));
144
160
  }
145
161
  }
146
162
  /*
@@ -294,6 +310,30 @@
294
310
  z-index: 1;
295
311
  }
296
312
 
313
+ /*
314
+ * Room for a hover glow. An interactive card's glow spreads past the card's own
315
+ * box, so inside anything that clips — a scroll area, a dialog body, a panel
316
+ * with `overflow: hidden` — the glow gets sliced off flat against the edge, and
317
+ * a card sitting at the very top or bottom of a list loses it entirely. The
318
+ * same clip takes the focus ring, which is likewise drawn outside the border;
319
+ * the glow is the wider of the two, so reserving for it covers both.
320
+ *
321
+ * Put this on the element that LAYS OUT the cards (the <ul>, the grid), not on
322
+ * the clipping parent: it pads the content inward by exactly the distance the
323
+ * glow travels, so the glow lands on padding instead of on the clip boundary.
324
+ * Negative margins pull that padding back out, so the cards stay where they
325
+ * were and nothing shifts — only the overflow gets somewhere to go.
326
+ *
327
+ * Bottom gets more room than the top because the glow is offset downward, and
328
+ * the top gets the lift added back, since a hovered card rises into it.
329
+ */
330
+ .lp-glow-room {
331
+ padding: calc(var(--lp-glow-spread) + var(--lp-card-lift)) var(--lp-glow-spread)
332
+ var(--lp-glow-spread-bottom);
333
+ margin: calc(-1 * (var(--lp-glow-spread) + var(--lp-card-lift))) calc(-1 * var(--lp-glow-spread))
334
+ calc(-1 * var(--lp-glow-spread-bottom));
335
+ }
336
+
297
337
  /* ── circular theme reveal (View Transitions) ────
298
338
  applyThemeWithTransition() snapshots the page, swaps the theme tokens, then
299
339
  shrinks a clip-path circle in the OLD snapshot from the toggle's position,