@lilydesignsystem/vue-motion-picker 0.1.2 → 0.3.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/LICENSE.md ADDED
@@ -0,0 +1,64 @@
1
+ <!-- Synced from the canonical repository by bin/sync-special-files.
2
+ Do not edit here; edit the root copy and re-run the script.
3
+ Canonical: https://github.com/LilyDesignSystem/lily-design-system/blob/main/LICENSE.md -->
4
+
5
+ # License
6
+
7
+ Lily Design System™ is free open source software.
8
+
9
+ License is any of these, at your option, or contact us for custom license options.
10
+
11
+ * [MIT](https://opensource.org/license/mit) ([SPDX: MIT](https://spdx.org/licenses/MIT.html))
12
+
13
+ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0) ([SPDX: Apache-2.0](https://spdx.org/licenses/Apache-2.0.html))
14
+
15
+ * [GNU General Public License v2.0](https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html) ([SPDX: GPL-2.0-only](https://spdx.org/licenses/GPL-2.0-only.html))
16
+
17
+ * [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0-standalone.html) ([SPDX: GPL-3.0-only](https://spdx.org/licenses/GPL-3.0-only.html))
18
+
19
+ * [BSD 3-Clause License](https://opensource.org/license/bsd-3-clause) ([SPDX: BSD-3-Clause](https://spdx.org/licenses/BSD-3-Clause.html))
20
+
21
+ ## SPDX
22
+
23
+ The machine-readable expression for the choice above is:
24
+
25
+ ```
26
+ SPDX-License-Identifier: MIT OR Apache-2.0 OR GPL-2.0-only OR GPL-3.0-only OR BSD-3-Clause
27
+ ```
28
+
29
+ Every package manifest in this repository should carry that expression, or a
30
+ single identifier from the list where the packaging format does not accept a
31
+ disjunction. You do not need permission to pick one: choose whichever member of
32
+ the list suits your project and comply with that one.
33
+
34
+ This applies to the whole repository — the component catalog, the seven headless
35
+ libraries, the seven example applications, the seven helper catalogs, the
36
+ `themes/` stylesheets, the `bin/` tooling, and the documentation.
37
+
38
+ ## Scope note
39
+
40
+ This file supersedes an earlier `CC BY-NC-SA` notice. That notice was a content
41
+ license with a non-commercial clause, it was not an open-source license, and it
42
+ contradicted both the specification and the published packages. It should not be
43
+ relied on. If you obtained Lily under it and that raises a question for you,
44
+ contact <joel@joelparkerhenderson.com>.
45
+
46
+ ## Trademarks
47
+
48
+ Lily™ and Lily Design System™ are trademarks. The trademarks are distinct from
49
+ the software license: a trademark claim (™) asserts use of the marks and is
50
+ independent of the licenses above. A license to the software is not a license to
51
+ the marks.
52
+
53
+ You may say your project uses Lily, is built with Lily, or is compatible with
54
+ Lily. Please do not use the marks as the name of your fork or product, or in a
55
+ way that suggests affiliation or endorsement. For any use beyond that, contact
56
+ <joel@joelparkerhenderson.com>.
57
+
58
+ ## Third-party references
59
+
60
+ Lily's example applications and `themes/` stylesheets take visual reference from
61
+ public-sector design systems including NHS UK, GOV.UK, and USWDS. Lily is not
62
+ affiliated with, endorsed by, or connected to any of those organisations. Their
63
+ names and marks belong to them, and this license grants nothing in respect of
64
+ them.
@@ -37,6 +37,8 @@ export type Props = {
37
37
  target?: HTMLElement | null;
38
38
  /** Optional pretty labels per slug. */
39
39
  motionLabels?: Record<string, string>;
40
+ /** Disables the button; an open popup closes. The popup's open state is `v-model:open`. */
41
+ disabled?: boolean;
40
42
  /** Extra CSS class on the root element. */
41
43
  class?: string;
42
44
  };
@@ -57,10 +59,19 @@ export declare function prefersReducedMotion(): boolean;
57
59
  export declare function nextMotionPickerId(): string;
58
60
  declare const _default: typeof __VLS_export;
59
61
  export default _default;
60
- declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
62
+ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<Props & {
63
+ /** Is the popup open? Bindable: `v-model:open` (the picker keeps its own state when none is bound). */
64
+ open?: boolean;
65
+ }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
66
+ "update:open": (value: boolean) => any;
67
+ } & {
61
68
  "update:value": (value: string) => any;
62
69
  change: (value: string) => any;
63
- }, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
70
+ }, string, import("vue").PublicProps, Readonly<Props & {
71
+ /** Is the popup open? Bindable: `v-model:open` (the picker keeps its own state when none is bound). */
72
+ open?: boolean;
73
+ }> & Readonly<{
74
+ "onUpdate:open"?: ((value: boolean) => any) | undefined;
64
75
  "onUpdate:value"?: ((value: string) => any) | undefined;
65
76
  onChange?: ((value: string) => any) | undefined;
66
77
  }>, {
@@ -70,6 +81,7 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<Props,
70
81
  name: string;
71
82
  target: HTMLElement | null;
72
83
  motionLabels: Record<string, string>;
84
+ disabled: boolean;
73
85
  class: string;
74
86
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
75
87
  default?: (props: {
package/dist/index.js CHANGED
@@ -1,16 +1,16 @@
1
- import { Fragment as e, createElementBlock as t, createElementVNode as n, createVNode as r, defineComponent as i, guardReactiveProps as a, nextTick as o, normalizeClass as s, normalizeProps as c, onBeforeUnmount as l, onMounted as u, openBlock as d, ref as f, renderList as p, renderSlot as m, toDisplayString as h, unref as g, watch as _, withCtx as v } from "vue";
2
- import { IconButton as y, Listbox as b } from "@lilydesignsystem/vue-headless";
3
- //#region lily-design-system-vue-motion-picker/MotionPicker.vue?vue&type=script&setup=true&lang.ts
4
- var x = ["name", "value"], S = [
1
+ import { Fragment as e, computed as t, createElementBlock as n, createElementVNode as r, createVNode as i, defineComponent as a, guardReactiveProps as o, mergeModels as s, nextTick as c, normalizeClass as l, normalizeProps as ee, onBeforeUnmount as u, onMounted as te, openBlock as d, ref as f, renderList as p, renderSlot as ne, toDisplayString as m, unref as h, useModel as g, watch as _, withCtx as v } from "vue";
2
+ import { IconButton as re, Listbox as y } from "@lilydesignsystem/vue-headless";
3
+ //#region MotionPicker.vue?vue&type=script&setup=true&lang.ts
4
+ var b = ["name", "value"], x = ["hidden"], ie = [
5
5
  "id",
6
6
  "aria-selected",
7
7
  "data-active",
8
8
  "onClick"
9
9
  ];
10
- function C(e) {
10
+ function S(e) {
11
11
  return e.split("-").map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join(" ");
12
12
  }
13
- function w() {
13
+ function C() {
14
14
  if (typeof window > "u" || typeof window.matchMedia != "function") return !1;
15
15
  try {
16
16
  return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
@@ -18,15 +18,15 @@ function w() {
18
18
  return !1;
19
19
  }
20
20
  }
21
- var T = 0;
22
- function E() {
23
- return T += 1, `motion-picker-${T}`;
21
+ var w = 0;
22
+ function T() {
23
+ return w += 1, `motion-picker-${w}`;
24
24
  }
25
25
  //#endregion
26
- //#region lily-design-system-vue-motion-picker/MotionPicker.vue
27
- var D = /* @__PURE__ */ i({
26
+ //#region MotionPicker.vue
27
+ var E = /* @__PURE__ */ a({
28
28
  __name: "MotionPicker",
29
- props: {
29
+ props: /*@__PURE__*/ s({
30
30
  label: {},
31
31
  motions: {},
32
32
  value: { default: "" },
@@ -35,116 +35,159 @@ var D = /* @__PURE__ */ i({
35
35
  name: { default: "motion" },
36
36
  target: { default: void 0 },
37
37
  motionLabels: { default: () => ({}) },
38
+ disabled: {
39
+ type: Boolean,
40
+ default: !1
41
+ },
38
42
  class: { default: "" }
39
- },
40
- emits: ["update:value", "change"],
41
- setup(i, { emit: T }) {
42
- let D = i, O = T, k = E(), A = `${k}-list`, j = (e) => `${k}-option-${e}`, M = f(!1), N = f(-1), P = f(null), F = f(null), I = f(null);
43
+ }, {
44
+ open: {
45
+ type: Boolean,
46
+ default: !1
47
+ },
48
+ openModifiers: {}
49
+ }),
50
+ emits: /*@__PURE__*/ s(["update:value", "change"], ["update:open"]),
51
+ setup(a, { emit: s }) {
52
+ let w = a, E = s, D = T(), O = `${D}-list`, k = (e) => `${D}-option-${e}`, A = g(a, "open");
53
+ _(() => w.disabled, (e) => {
54
+ e && (A.value = !1);
55
+ }, { immediate: !0 });
56
+ let j = `${D}-tooltip`, M = f(!1), N = f(!1), P = f(!1), F = f(!1), I = t(() => !A.value && !F.value && (M.value || N.value || P.value));
57
+ function ae() {
58
+ M.value = !0, F.value = !1;
59
+ }
60
+ function oe() {
61
+ M.value = !1;
62
+ }
43
63
  function L(e) {
44
- let t = D.motionLabels ?? {};
45
- return e in t ? t[e] : C(e);
64
+ try {
65
+ P.value = e.currentTarget.matches(":focus-visible");
66
+ } catch {
67
+ P.value = !0;
68
+ }
69
+ }
70
+ function R() {
71
+ P.value = !1, F.value = !1;
72
+ }
73
+ function z(e) {
74
+ e.key === "Escape" && I.value && (F.value = !0);
46
75
  }
47
- function R(e) {
48
- if (!(typeof document > "u" || !e)) {
49
- if ((D.target ?? document.documentElement).setAttribute("data-motion", e), D.storageKey) try {
50
- localStorage.setItem(D.storageKey, e);
76
+ function B(e) {
77
+ e.key === "Escape" && (F.value = !0);
78
+ }
79
+ let V = !1;
80
+ function H(e) {
81
+ typeof document < "u" && e !== V && (V = e, e ? document.addEventListener("keydown", B) : document.removeEventListener("keydown", B));
82
+ }
83
+ _(I, (e) => H(e), { flush: "sync" }), u(() => H(!1));
84
+ let U = f(-1), W = f(null), G = f(null), K = f(null);
85
+ function q(e) {
86
+ let t = w.motionLabels ?? {};
87
+ return e in t ? t[e] : S(e);
88
+ }
89
+ function J(e) {
90
+ if (typeof document < "u" && e) {
91
+ if ((w.target ?? document.documentElement).setAttribute("data-motion", e), w.storageKey) try {
92
+ localStorage.setItem(w.storageKey, e);
51
93
  } catch {}
52
- O("change", e);
94
+ E("change", e);
53
95
  }
54
96
  }
55
- let z = f(D.value ?? "");
56
- function B(e) {
57
- z.value = e, O("update:value", e);
97
+ let Y = f(w.value ?? "");
98
+ function se(e) {
99
+ Y.value = e, E("update:value", e);
58
100
  }
59
- _(() => D.value, (e) => {
60
- e !== void 0 && e !== z.value && (z.value = e);
61
- }), _(z, (e, t) => {
62
- e && e !== t && R(e);
101
+ _(() => w.value, (e) => {
102
+ e !== void 0 && e !== Y.value && (Y.value = e);
103
+ }), _(Y, (e, t) => {
104
+ e && e !== t && J(e);
63
105
  });
64
- async function V(e) {
65
- let t = D.motions.indexOf(z.value);
66
- N.value = D.motions.length === 0 ? -1 : e ?? (t >= 0 ? t : 0), M.value = !0, await o(), F.value?.el?.focus({ preventScroll: !0 });
106
+ async function X(e) {
107
+ let t = w.motions.indexOf(Y.value);
108
+ U.value = w.motions.length === 0 ? -1 : e ?? (t >= 0 ? t : 0), A.value = !0, await c(), G.value?.el?.focus({ preventScroll: !0 });
67
109
  }
68
- async function H(e = !0) {
69
- M.value && (M.value = !1, N.value = -1, e && (await o(), P.value?.el?.focus({ preventScroll: !0 })));
110
+ async function Z(e = !0) {
111
+ A.value && (A.value = !1, U.value = -1, e && (await c(), W.value?.el?.focus({ preventScroll: !0 })));
70
112
  }
71
- function U(e) {
72
- let t = D.motions[e];
73
- t && B(t), H();
113
+ function Q(e) {
114
+ let t = w.motions[e];
115
+ t && se(t), Z();
74
116
  }
75
- function W() {
76
- N.value < 0 || !F.value?.el || document.getElementById(j(N.value))?.scrollIntoView?.({ block: "nearest" });
117
+ function ce() {
118
+ U.value < 0 || !G.value?.el || document.getElementById(k(U.value))?.scrollIntoView?.({ block: "nearest" });
77
119
  }
78
- _(N, () => W());
79
- function G() {
80
- P.value?.el?.focus?.({ preventScroll: !0 }), H(!1);
120
+ _(U, () => ce());
121
+ function le() {
122
+ W.value?.el?.focus?.({ preventScroll: !0 }), Z(!1);
81
123
  }
82
- function K() {
83
- M.value ? H() : V();
124
+ function ue() {
125
+ M.value = !1, A.value ? Z() : X();
84
126
  }
85
- function q(e) {
86
- switch (e.key) {
127
+ function de(e) {
128
+ switch (z(e), e.key) {
87
129
  case "ArrowDown":
88
130
  case "Enter":
89
131
  case " ":
90
- e.preventDefault(), V();
132
+ e.preventDefault(), X();
91
133
  break;
92
- case "ArrowUp": e.preventDefault(), V(D.motions.length - 1);
134
+ case "ArrowUp": e.preventDefault(), X(w.motions.length - 1);
93
135
  }
94
136
  }
95
- function J(e) {
137
+ function fe(e) {
96
138
  let t = e.relatedTarget;
97
- t && I.value?.contains(t) || H(!1);
139
+ t && K.value?.contains(t) || Z(!1);
98
140
  }
99
- function Y(e) {
100
- if (!M.value) return;
141
+ function $(e) {
142
+ if (!A.value) return;
101
143
  let t = e.target;
102
- t && I.value && !I.value.contains(t) && H(!1);
144
+ t && K.value && !K.value.contains(t) && Z(!1);
103
145
  }
104
- return u(() => {
105
- document.addEventListener("click", Y);
106
- let e = z.value;
107
- if (!e && D.storageKey) try {
108
- e = localStorage.getItem(D.storageKey) ?? "";
146
+ return te(() => {
147
+ document.addEventListener("click", $);
148
+ let e = Y.value;
149
+ if (!e && w.storageKey) try {
150
+ e = localStorage.getItem(w.storageKey) ?? "";
109
151
  } catch {}
110
152
  if (!e) {
111
- let t = w() ? "reduce" : "no-preference";
112
- e = D.defaultValue ?? (D.motions.includes(t) ? t : void 0) ?? D.motions[0] ?? "";
113
- }
114
- if (e && e !== z.value) {
115
- z.value = e, O("update:value", e);
116
- return;
153
+ let t = C() ? "reduce" : "no-preference";
154
+ e = w.defaultValue ?? (w.motions.includes(t) ? t : void 0) ?? w.motions[0] ?? "";
117
155
  }
118
- e && R(e);
119
- }), l(() => {
120
- document.removeEventListener("click", Y);
121
- }), (o, l) => (d(), t("div", {
156
+ e && e !== Y.value ? (Y.value = e, E("update:value", e)) : e && J(e);
157
+ }), u(() => {
158
+ document.removeEventListener("click", $);
159
+ }), (t, s) => (d(), n("div", {
122
160
  ref_key: "rootEl",
123
- ref: I,
124
- class: s(`motion-picker ${D.class}`.trim()),
125
- onFocusout: J
161
+ ref: K,
162
+ class: l(`motion-picker ${w.class}`.trim()),
163
+ onFocusout: fe
126
164
  }, [
127
- n("input", {
165
+ r("input", {
128
166
  type: "hidden",
129
- name: i.name,
130
- value: z.value
131
- }, null, 8, x),
132
- r(g(y), {
167
+ name: a.name,
168
+ value: Y.value
169
+ }, null, 8, b),
170
+ i(h(re), {
171
+ disabled: a.disabled,
133
172
  ref_key: "buttonEl",
134
- ref: P,
173
+ ref: W,
135
174
  baseClass: "motion-picker-button",
136
- label: i.label,
175
+ label: a.label,
137
176
  "aria-haspopup": "listbox",
138
- "aria-expanded": M.value ? "true" : "false",
139
- "aria-controls": A,
140
- onClick: K,
141
- onKeydown: q
177
+ "aria-expanded": A.value ? "true" : "false",
178
+ "aria-controls": O,
179
+ onClick: ue,
180
+ onKeydown: de,
181
+ onMouseenter: ae,
182
+ onMouseleave: oe,
183
+ onFocus: L,
184
+ onBlur: R
142
185
  }, {
143
- default: v(() => [m(o.$slots, "default", c(a({
144
- value: z.value,
145
- open: M.value,
146
- labelFor: L
147
- })), () => [l[2] ||= n("svg", {
186
+ default: v(() => [ne(t.$slots, "default", ee(o({
187
+ value: Y.value,
188
+ open: A.value,
189
+ labelFor: q
190
+ })), () => [s[4] ||= r("svg", {
148
191
  class: "motion-picker-icon",
149
192
  viewBox: "0 0 16 16",
150
193
  width: "1.05rem",
@@ -155,36 +198,48 @@ var D = /* @__PURE__ */ i({
155
198
  "stroke-width": "1.6",
156
199
  "stroke-linecap": "round",
157
200
  "stroke-linejoin": "round"
158
- }, [n("path", { d: "M5 3v10M11 3v10" })], -1)])]),
201
+ }, [r("path", { d: "M5 3v10M11 3v10" })], -1)])]),
159
202
  _: 3
160
- }, 8, ["label", "aria-expanded"]),
161
- r(g(b), {
203
+ }, 8, [
204
+ "disabled",
205
+ "label",
206
+ "aria-expanded"
207
+ ]),
208
+ r("div", {
209
+ class: "motion-picker-tooltip",
210
+ role: "tooltip",
211
+ id: j,
212
+ hidden: !I.value || void 0,
213
+ onMouseenter: s[0] ||= (e) => N.value = !0,
214
+ onMouseleave: s[1] ||= (e) => N.value = !1
215
+ }, m(a.label), 41, x),
216
+ i(h(y), {
162
217
  ref_key: "listEl",
163
- ref: F,
218
+ ref: G,
164
219
  as: "ul",
165
220
  baseClass: "motion-picker-list",
166
- id: A,
167
- label: i.label,
221
+ id: O,
222
+ label: a.label,
168
223
  navigation: "active-descendant",
169
224
  clamp: "",
170
225
  typeahead: "",
171
226
  pageSize: 10,
172
- activeIndex: N.value,
173
- "onUpdate:activeIndex": l[0] ||= (e) => N.value = e,
174
- hidden: !M.value || void 0,
175
- onActivate: U,
176
- onEscape: l[1] ||= () => H(),
177
- onTabOut: G
227
+ activeIndex: U.value,
228
+ "onUpdate:activeIndex": s[2] ||= (e) => U.value = e,
229
+ hidden: !A.value || void 0,
230
+ onActivate: Q,
231
+ onEscape: s[3] ||= () => Z(),
232
+ onTabOut: le
178
233
  }, {
179
- default: v(() => [(d(!0), t(e, null, p(i.motions, (e, n) => (d(), t("li", {
234
+ default: v(() => [(d(!0), n(e, null, p(a.motions, (e, t) => (d(), n("li", {
180
235
  key: e,
181
236
  class: "motion-picker-option",
182
- id: j(n),
237
+ id: k(t),
183
238
  role: "option",
184
- "aria-selected": e === z.value ? "true" : "false",
185
- "data-active": n === N.value ? "" : void 0,
186
- onClick: (e) => U(n)
187
- }, h(L(e)), 9, S))), 128))]),
239
+ "aria-selected": e === Y.value ? "true" : "false",
240
+ "data-active": t === U.value ? "" : void 0,
241
+ onClick: (e) => Q(t)
242
+ }, m(q(e)), 9, ie))), 128))]),
188
243
  _: 1
189
244
  }, 8, [
190
245
  "label",
@@ -195,4 +250,4 @@ var D = /* @__PURE__ */ i({
195
250
  }
196
251
  });
197
252
  //#endregion
198
- export { D as MotionPicker, D as default, C as motionName, E as nextMotionPickerId, w as prefersReducedMotion };
253
+ export { E as MotionPicker, E as default, S as motionName, T as nextMotionPickerId, C as prefersReducedMotion };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lilydesignsystem/vue-motion-picker",
3
- "version": "0.1.2",
3
+ "version": "0.3.0",
4
4
  "engines": {
5
5
  "node": "=26"
6
6
  },
@@ -21,14 +21,27 @@
21
21
  "README.md"
22
22
  ],
23
23
  "scripts": {
24
- "prepublishOnly": "cd .. && npm run build"
24
+ "build": "node build.mjs",
25
+ "test": "vitest run",
26
+ "test:watch": "vitest",
27
+ "prepublishOnly": "npm run build"
25
28
  },
26
29
  "dependencies": {
27
- "@lilydesignsystem/vue-headless": "^0.2.0"
30
+ "@lilydesignsystem/vue-headless": "^0.4.0"
28
31
  },
29
32
  "peerDependencies": {
30
33
  "vue": "^3.0.0"
31
34
  },
35
+ "devDependencies": {
36
+ "@vitejs/plugin-vue": "^6.0.9",
37
+ "@vue/test-utils": "^2.5.1",
38
+ "jsdom": "^30.1.1",
39
+ "typescript": "^5.9.3",
40
+ "vite": "^8.3.1",
41
+ "vitest": "^5.0.3",
42
+ "vue": "^3.5.43",
43
+ "vue-tsc": "^3.3.11"
44
+ },
32
45
  "keywords": [
33
46
  "lily",
34
47
  "design",
@@ -42,7 +55,7 @@
42
55
  "license": "MIT OR Apache-2.0 OR GPL-2.0-only OR GPL-3.0-only OR BSD-3-Clause",
43
56
  "repository": {
44
57
  "type": "git",
45
- "url": "git+https://github.com/LilyDesignSystem/lily-design-system-vue-helpers.git",
58
+ "url": "git+https://github.com/LilyDesignSystem/lily-design-system.git",
46
59
  "directory": "lily-design-system-vue-motion-picker"
47
60
  },
48
61
  "homepage": "https://lilydesignsystem.com/",