@leavepulse/ui 0.25.0 → 0.26.1

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.
@@ -0,0 +1,29 @@
1
+
2
+ /*
3
+ * Unscoped: the items are written by the CONSUMER, inside the slot, so they
4
+ * carry no scope attribute of this component — a scoped rule could never reach
5
+ * them. Namespaced instead, and only ever applied under `.lp-skeleton`.
6
+ *
7
+ * One shared animation name means every item on the page pulses in phase: CSS
8
+ * animations are timed against the document, not against when each element
9
+ * mounted, so a skeleton that fills in progressively stays in step with itself.
10
+ */
11
+ .lp-skeleton-item {
12
+ animation: lp-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
13
+ }
14
+
15
+ /* Items nested anywhere inside a wrapper, at any depth. */
16
+ .lp-skeleton .lp-skeleton-item {
17
+ animation: lp-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
18
+ }
19
+ @keyframes lp-skeleton-pulse {
20
+ 50% {
21
+ opacity: 0.5;
22
+ }
23
+ }
24
+ @media (prefers-reduced-motion: reduce) {
25
+ .lp-skeleton-item {
26
+ animation: none;
27
+ opacity: 0.75;
28
+ }
29
+ }
@@ -0,0 +1,34 @@
1
+ import { defineComponent, useSlots, computed, openBlock, createElementBlock, normalizeClass, renderSlot } from "vue";
2
+ import '../assets/LpSkeleton-D7xU8IPV.css';const _sfc_main = /* @__PURE__ */ defineComponent({
3
+ __name: "LpSkeleton",
4
+ props: {
5
+ rounded: {}
6
+ },
7
+ setup(__props) {
8
+ const slots = useSlots();
9
+ const isBlock = computed(() => !slots.default);
10
+ return (_ctx, _cache) => {
11
+ return openBlock(), createElementBlock("div", {
12
+ "aria-hidden": "true",
13
+ class: normalizeClass(["lp-skeleton", [
14
+ // `rounded` describes the PLACEHOLDER's own shape, so it only applies to
15
+ // the bare form. A wrapper is invisible — rounding it would clip the
16
+ // layout inside without anything being drawn to justify the corner.
17
+ isBlock.value ? [
18
+ "lp-skeleton-item bg-surface-soft",
19
+ {
20
+ "rounded-control": __props.rounded === "control" || !__props.rounded,
21
+ "rounded-card": __props.rounded === "card",
22
+ "rounded-pill": __props.rounded === "pill"
23
+ }
24
+ ] : ""
25
+ ]])
26
+ }, [
27
+ renderSlot(_ctx.$slots, "default")
28
+ ], 2);
29
+ };
30
+ }
31
+ });
32
+ export {
33
+ _sfc_main as _
34
+ };
@@ -1,6 +1,16 @@
1
1
  type __VLS_Props = {
2
2
  rounded?: "control" | "card" | "pill";
3
3
  };
4
- declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
4
+ declare var __VLS_1: {};
5
+ type __VLS_Slots = {} & {
6
+ default?: (props: typeof __VLS_1) => any;
7
+ };
8
+ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
9
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
5
10
  declare const _default: typeof __VLS_export;
6
11
  export default _default;
12
+ type __VLS_WithSlots<T, S> = T & {
13
+ new (): {
14
+ $slots: S;
15
+ };
16
+ };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpSkeleton-eSFIo4kD.js";
1
+ import { _ as _sfc_main } from "../chunks/LpSkeleton-DpXLDcQm.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
package/dist/index.js CHANGED
@@ -56,7 +56,7 @@ import { _ as _49 } from "./chunks/LpServiceNode-D8uJ8sGk.js";
56
56
  import { _ as _50 } from "./chunks/LpShift-DXNsauQZ.js";
57
57
  import { default as default3 } from "./components/LpShortcutsDialog.vue.js";
58
58
  import { _ as _51 } from "./chunks/LpSidebar-7Vpy2IJc.js";
59
- import { _ as _52 } from "./chunks/LpSkeleton-eSFIo4kD.js";
59
+ import { _ as _52 } from "./chunks/LpSkeleton-DpXLDcQm.js";
60
60
  import { _ as _53 } from "./chunks/LpSlider-B1SF3pRV.js";
61
61
  import { _ as _54 } from "./chunks/LpSparkline-C6xQ2dwv.js";
62
62
  import { _ as _55 } from "./chunks/LpStat-CDanpzU0.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leavepulse/ui",
3
- "version": "0.25.0",
3
+ "version": "0.26.1",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -1,16 +1,96 @@
1
1
  <script setup lang="ts">
2
- // Loading placeholder. Sizing comes from the consumer's classes (w-/h-).
2
+ /*
3
+ * Loading placeholder, in two shapes.
4
+ *
5
+ * Bare, it is one pulsing block and the consumer's classes size it:
6
+ *
7
+ * <LpSkeleton class="h-4 w-40" />
8
+ *
9
+ * Given content, it becomes a WRAPPER instead: it stops pulsing itself and
10
+ * lends the pulse to any `.lp-skeleton-item` inside it, however deeply nested.
11
+ *
12
+ * <LpSkeleton class="space-y-3">
13
+ * <div class="flex items-center gap-3">
14
+ * <div class="lp-skeleton-item size-10 rounded-pill" />
15
+ * <div class="lp-skeleton-item h-4 w-32" />
16
+ * </div>
17
+ * </LpSkeleton>
18
+ *
19
+ * The second form exists because a real skeleton is a LAYOUT, not a stack of
20
+ * bars: a profile header is an avatar beside two lines of different widths
21
+ * above a button. Expressed as one component per block, that is a dozen
22
+ * <LpSkeleton> tags whose grid has to be rebuilt by hand and kept in step with
23
+ * the markup it stands in for. As a wrapper, the skeleton can be the real
24
+ * markup with its content swapped for plain divs — same flex, same gaps, same
25
+ * responsive classes — so it keeps matching the thing it is standing in for.
26
+ *
27
+ * Consumers had already arrived at this and built it themselves; it belongs
28
+ * here, where every app gets it.
29
+ */
30
+ import { computed, useSlots } from "vue"
31
+
3
32
  defineProps<{ rounded?: "control" | "card" | "pill" }>()
33
+
34
+ const slots = useSlots()
35
+
36
+ /* A wrapper must not pulse as a whole — the container would flash behind its
37
+ own items. Only the bare form is a placeholder in its own right. */
38
+ const isBlock = computed(() => !slots.default)
4
39
  </script>
5
40
 
6
41
  <template>
7
42
  <div
8
43
  aria-hidden="true"
9
- class="animate-pulse bg-surface-soft"
10
- :class="{
11
- 'rounded-control': rounded === 'control' || !rounded,
12
- 'rounded-card': rounded === 'card',
13
- 'rounded-pill': rounded === 'pill',
14
- }"
15
- />
44
+ class="lp-skeleton"
45
+ :class="[
46
+ // `rounded` describes the PLACEHOLDER's own shape, so it only applies to
47
+ // the bare form. A wrapper is invisible — rounding it would clip the
48
+ // layout inside without anything being drawn to justify the corner.
49
+ isBlock
50
+ ? [
51
+ 'lp-skeleton-item bg-surface-soft',
52
+ {
53
+ 'rounded-control': rounded === 'control' || !rounded,
54
+ 'rounded-card': rounded === 'card',
55
+ 'rounded-pill': rounded === 'pill',
56
+ },
57
+ ]
58
+ : '',
59
+ ]"
60
+ >
61
+ <slot />
62
+ </div>
16
63
  </template>
64
+
65
+ <style>
66
+ /*
67
+ * Unscoped: the items are written by the CONSUMER, inside the slot, so they
68
+ * carry no scope attribute of this component — a scoped rule could never reach
69
+ * them. Namespaced instead, and only ever applied under `.lp-skeleton`.
70
+ *
71
+ * One shared animation name means every item on the page pulses in phase: CSS
72
+ * animations are timed against the document, not against when each element
73
+ * mounted, so a skeleton that fills in progressively stays in step with itself.
74
+ */
75
+ .lp-skeleton-item {
76
+ animation: lp-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
77
+ }
78
+
79
+ /* Items nested anywhere inside a wrapper, at any depth. */
80
+ .lp-skeleton .lp-skeleton-item {
81
+ animation: lp-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
82
+ }
83
+
84
+ @keyframes lp-skeleton-pulse {
85
+ 50% {
86
+ opacity: 0.5;
87
+ }
88
+ }
89
+
90
+ @media (prefers-reduced-motion: reduce) {
91
+ .lp-skeleton-item {
92
+ animation: none;
93
+ opacity: 0.75;
94
+ }
95
+ }
96
+ </style>
@@ -1,22 +0,0 @@
1
- import { defineComponent, openBlock, createElementBlock, normalizeClass } from "vue";
2
- const _sfc_main = /* @__PURE__ */ defineComponent({
3
- __name: "LpSkeleton",
4
- props: {
5
- rounded: {}
6
- },
7
- setup(__props) {
8
- return (_ctx, _cache) => {
9
- return openBlock(), createElementBlock("div", {
10
- "aria-hidden": "true",
11
- class: normalizeClass(["animate-pulse bg-surface-soft", {
12
- "rounded-control": __props.rounded === "control" || !__props.rounded,
13
- "rounded-card": __props.rounded === "card",
14
- "rounded-pill": __props.rounded === "pill"
15
- }])
16
- }, null, 2);
17
- };
18
- }
19
- });
20
- export {
21
- _sfc_main as _
22
- };