@livestorm/ui-vue 2.0.1 → 2.0.3

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 { _ as xe, a as he, b as ke, c as Ge, d as He, e as Re, f as Ee, I as We, g as Ne, h as qe, i as Ke, j as Qe, k as Ve, l as Xe, X as Ye, $ as Ze, Z as Je, t as eo, x as oo, u as to, v as no, w as _o, n as io, z as ro, m as co, o as lo, p as so, r as ao, s as uo, y as po, A as mo, D as vo, F as bo, G as Uo, K as go, L as fo, M as So, N as To, O as yo, P as jo, Q as Oo, R as Mo, S as Po, T as zo, V as $o, W as Bo, Y as Do, a2 as Co, a5 as Io, a4 as Lo, a7 as Ao, a8 as wo, aa as Fo, ab as xo, ae as ho, ag as ko, ah as Go, ai as Ho, aj as Ro, ak as Eo, an as Wo, ao as No, ap as r, aq as c, ar as l, as as s, at as a, au as u, av as p, aw as m, ax as v, ay as b, az as U, aA as g, aB as d, aC as f, aD as S, aE as T, aF as y } from "./UiTimelineItem.vue_vue_type_style_index_0_lang-BN5208td.mjs";
1
+ import { _ as xe, a as he, b as ke, c as Ge, d as He, e as Re, f as Ee, I as We, g as Ne, h as qe, i as Ke, j as Qe, k as Ve, l as Xe, X as Ye, $ as Ze, Z as Je, t as eo, x as oo, u as to, v as no, w as _o, n as io, z as ro, m as co, o as lo, p as so, r as ao, s as uo, y as po, A as mo, D as vo, F as bo, G as Uo, K as go, L as fo, M as So, N as To, O as yo, P as jo, Q as Oo, R as Mo, S as Po, T as zo, V as $o, W as Bo, Y as Do, a2 as Co, a5 as Io, a4 as Lo, a7 as Ao, a8 as wo, aa as Fo, ab as xo, ae as ho, ag as ko, ah as Go, ai as Ho, aj as Ro, ak as Eo, an as Wo, ao as No, ap as r, aq as c, ar as l, as as s, at as a, au as u, av as p, aw as m, ax as v, ay as b, az as U, aA as g, aB as d, aC as f, aD as S, aE as T, aF as y } from "./UiTimelineItem.vue_vue_type_style_index_0_lang-UNNkatTS.mjs";
2
2
  import { b as qo, c as Ko, a as Qo, _ as Vo } from "./a11y--y7uaY4j.mjs";
3
3
  import { RenderSlot as j } from "./slot.mjs";
4
4
  const O = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as e, a as s, b as o, c as U, d as r, e as t, f as n, g as p, h as l, U as u, i as d, j as g, k as T, l as S, m, n as b, o as C, p as M, q as I, r as c, s as B, t as D, u as P, v as L, w as v, x as A, y as w, z as F, A as x, B as h, C as H, D as f, E as k, F as G, G as y, H as R, I as j, J as z, K as E, L as W, M as _, N as q, O as J, P as K, Q as N, R as O, S as Q, T as V, V as X, W as Y, X as Z, Y as $, Z as aa, $ as ia, a0 as ea, a1 as sa, a2 as oa, a3 as Ua, a4 as ra, a5 as ta, a6 as na, a7 as pa, a8 as la, a9 as ua, aa as da, ab as ga, ac as Ta, ad as Sa, ae as ma, af as ba, ag as Ca, ah as Ma, ai as Ia, aj as ca, ak as Ba, al as Da, am as Pa, an as La, ao as va } from "./UiTimelineItem.vue_vue_type_style_index_0_lang-BN5208td.mjs";
1
+ import { _ as e, a as s, b as o, c as U, d as r, e as t, f as n, g as p, h as l, U as u, i as d, j as g, k as T, l as S, m, n as b, o as C, p as M, q as I, r as c, s as B, t as D, u as P, v as L, w as v, x as A, y as w, z as F, A as x, B as h, C as H, D as f, E as k, F as G, G as y, H as R, I as j, J as z, K as E, L as W, M as _, N as q, O as J, P as K, Q as N, R as O, S as Q, T as V, V as X, W as Y, X as Z, Y as $, Z as aa, $ as ia, a0 as ea, a1 as sa, a2 as oa, a3 as Ua, a4 as ra, a5 as ta, a6 as na, a7 as pa, a8 as la, a9 as ua, aa as da, ab as ga, ac as Ta, ad as Sa, ae as ma, af as ba, ag as Ca, ah as Ma, ai as Ia, aj as ca, ak as Ba, al as Da, am as Pa, an as La, ao as va } from "./UiTimelineItem.vue_vue_type_style_index_0_lang-UNNkatTS.mjs";
2
2
  import { _ as wa, a as Fa, b as xa, c as ha } from "./a11y--y7uaY4j.mjs";
3
3
  export {
4
4
  e as UiActionBar,
package/dist/layouts.mjs CHANGED
@@ -1,22 +1,23 @@
1
- import { defineComponent as I, computed as w, ref as C, onMounted as M, resolveComponent as E, createElementBlock as n, openBlock as t, createElementVNode as v, createCommentVNode as a, createBlock as y, resolveDynamicComponent as P, normalizeProps as z, guardReactiveProps as H, Fragment as k, normalizeClass as b, normalizeStyle as N, toDisplayString as f, withKeys as S, withCtx as h, createTextVNode as B, getCurrentInstance as D, createVNode as L, Transition as V, TransitionGroup as A, renderList as j, unref as x, mergeProps as K } from "vue";
2
- import { u as R } from "./toastDSV2-wMh-UMzW.mjs";
3
- import { u as U } from "./useThemeAndModes-DY5SDC0a.mjs";
4
- const F = ["data-testid"], G = {
1
+ import { defineComponent as I, computed as w, ref as C, onMounted as M, resolveComponent as E, createElementBlock as n, openBlock as t, createElementVNode as v, createCommentVNode as a, createBlock as y, resolveDynamicComponent as P, normalizeProps as z, guardReactiveProps as H, Fragment as k, normalizeClass as b, normalizeStyle as N, toDisplayString as f, withKeys as S, withCtx as h, createTextVNode as B, getCurrentInstance as D, createVNode as L, Transition as V, TransitionGroup as A, renderList as R, unref as x, mergeProps as j } from "vue";
2
+ import { useResponsive as K } from "./responsive.mjs";
3
+ import { u as U } from "./toastDSV2-wMh-UMzW.mjs";
4
+ import { u as F } from "./useThemeAndModes-CUK56j0z.mjs";
5
+ const G = ["data-testid"], O = {
5
6
  key: 1,
6
7
  class: "absolute h-1 bg-grey-100 top-0 left-0 w-full"
7
- }, O = { class: "shrink-0" }, W = { class: "flex flex-col flex-1 sm:row-start-1 min-w-0 wrap-break-word" }, X = {
8
+ }, W = { class: "shrink-0" }, X = { class: "flex flex-col flex-1 sm:row-start-1 min-w-0 wrap-break-word" }, Y = {
8
9
  key: 0,
9
10
  class: "text-content-text-md-bold text-text-neutral-base"
10
- }, Y = {
11
+ }, q = {
11
12
  key: 1,
12
13
  class: "text-content-text-md-regular"
13
- }, q = {
14
+ }, J = {
14
15
  key: 2,
15
16
  class: "text-content-text-md-regular text-text-neutral-subtle mt-1"
16
- }, J = { class: "row-start-1 h-4 w-8 justify-self-end" }, Q = {
17
+ }, Q = { class: "row-start-1 h-4 w-8 justify-self-end" }, Z = {
17
18
  key: 2,
18
19
  class: "row-start-3 col-start-1 col-end-3 w-full grid sm:flex sm:col-start-2 mt-3 gap-2 justify-self-end"
19
- }, Z = /* @__PURE__ */ I({
20
+ }, ee = /* @__PURE__ */ I({
20
21
  __name: "ToastMessage",
21
22
  props: {
22
23
  message: {},
@@ -70,7 +71,7 @@ const F = ["data-testid"], G = {
70
71
  e.time && setTimeout(() => {
71
72
  c.value = !0;
72
73
  }, 100);
73
- const r = C(), { addElementToWatch: i } = U();
74
+ const r = C(), { addElementToWatch: i } = F();
74
75
  return M(() => {
75
76
  r.value && i(
76
77
  "content",
@@ -117,23 +118,23 @@ const F = ["data-testid"], G = {
117
118
  transitionDuration: `${e.time / 1e3}s`
118
119
  })
119
120
  }, null, 6)) : a("", !0)
120
- ], 2)) : e.progessIndeterminate ? (t(), n("div", G, [...o[5] || (o[5] = [
121
+ ], 2)) : e.progessIndeterminate ? (t(), n("div", O, [...o[5] || (o[5] = [
121
122
  v("div", { class: "animate-progress-indeterminate w-full h-full bg-red-600 origin-left-right" }, null, -1)
122
123
  ])])) : a("", !0)
123
124
  ], 64)) : a("", !0),
124
- v("div", O, [
125
+ v("div", W, [
125
126
  g.value ? (t(), n("div", {
126
127
  key: 0,
127
128
  class: b(["w-4 h-4 mt-0.5 mr-3 shrink-0 mb-2 sm:mb-0", g.value])
128
129
  }, null, 2)) : a("", !0)
129
130
  ]),
130
- v("div", W, [
131
- e.title || e.message ? (t(), n("span", X, f(e.title || e.message), 1)) : a("", !0),
132
- e.title && e.message ? (t(), n("p", Y, f(e.message), 1)) : a("", !0),
133
- e.info ? (t(), n("p", q, f(e.info), 1)) : a("", !0)
131
+ v("div", X, [
132
+ e.title || e.message ? (t(), n("span", Y, f(e.title || e.message), 1)) : a("", !0),
133
+ e.title && e.message ? (t(), n("p", q, f(e.message), 1)) : a("", !0),
134
+ e.info ? (t(), n("p", J, f(e.info), 1)) : a("", !0)
134
135
  ])
135
136
  ], 64)),
136
- v("div", J, [
137
+ v("div", Q, [
137
138
  !e.locked && e.closable ? (t(), y($, {
138
139
  key: 0,
139
140
  "data-testid": `toast-close-${String(e.id)}`,
@@ -147,7 +148,7 @@ const F = ["data-testid"], G = {
147
148
  onKeydown: o[1] || (o[1] = S((d) => l("close"), ["enter"]))
148
149
  }, null, 8, ["data-testid"])) : a("", !0)
149
150
  ]),
150
- e.confirmLabel || e.cancelLabel ? (t(), n("div", Q, [
151
+ e.confirmLabel || e.cancelLabel ? (t(), n("div", Z, [
151
152
  e.confirmLabel ? (t(), y($, {
152
153
  key: 0,
153
154
  href: e.confirmHref,
@@ -177,20 +178,20 @@ const F = ["data-testid"], G = {
177
178
  _: 1
178
179
  })) : a("", !0)
179
180
  ])) : a("", !0)
180
- ], 40, F);
181
+ ], 40, G);
181
182
  };
182
183
  }
183
- }), ee = {
184
+ }), te = {
184
185
  key: 0,
185
186
  "data-testid": "layout-toasts-locked",
186
187
  class: "bg-alpha-black-300 fixed inset-0 z-[8000]"
187
- }, te = 3, ae = /* @__PURE__ */ I({
188
+ }, oe = 3, le = /* @__PURE__ */ I({
188
189
  __name: "LayoutToasts",
189
190
  setup(u) {
190
191
  const p = D();
191
192
  if (!p)
192
193
  throw new Error("No current instance found");
193
- const e = w(() => p.appContext.config.globalProperties.$responsive), { toasts: l, closeToast: g, cancelCloseTimer: c, openToast: m } = R(), _ = w(() => l.value.some((r) => r.locked));
194
+ const e = w(() => p.appContext.config.globalProperties.$responsive ?? K()), { toasts: l, closeToast: g, cancelCloseTimer: c, openToast: m } = U(), _ = w(() => l.value.some((r) => r.locked));
194
195
  function T(r) {
195
196
  const i = r.getBoundingClientRect(), { style: s } = r;
196
197
  s.top = `${i.top}px`, s.left = `${i.left}px`, s.width = `${i.width}px`, s.height = `${i.height}px`;
@@ -201,7 +202,7 @@ const F = ["data-testid"], G = {
201
202
  appear: ""
202
203
  }, {
203
204
  default: h(() => [
204
- _.value ? (t(), n("div", ee)) : a("", !0)
205
+ _.value ? (t(), n("div", te)) : a("", !0)
205
206
  ]),
206
207
  _: 1
207
208
  }),
@@ -212,11 +213,11 @@ const F = ["data-testid"], G = {
212
213
  onBeforeLeave: T
213
214
  }, {
214
215
  default: h(() => [
215
- (t(!0), n(k, null, j(x(l).slice(0, te), (s) => (t(), n("div", {
216
+ (t(!0), n(k, null, R(x(l).slice(0, oe), (s) => (t(), n("div", {
216
217
  key: s.id,
217
218
  class: "w-full sm:max-w-[400px]"
218
219
  }, [
219
- L(Z, K({ ref_for: !0 }, s, {
220
+ L(ee, j({ ref_for: !0 }, s, {
220
221
  class: "bg-surface-neutral-bg-main p-3 rounded-2xl",
221
222
  onClose: (o) => x(g)(s.id),
222
223
  onMouseenter: (o) => x(c)(s.id),
@@ -232,5 +233,5 @@ const F = ["data-testid"], G = {
232
233
  }
233
234
  });
234
235
  export {
235
- ae as LayoutToasts
236
+ le as LayoutToasts
236
237
  };
@@ -1,25 +1,39 @@
1
- import { computed as r, reactive as d } from "vue";
2
- let e;
3
- function c() {
4
- return e;
1
+ import { reactive as p, computed as a } from "vue";
2
+ import { BREAKPOINT_LG as c, BREAKPOINT_XL_PLUS as f, BREAKPOINT_MD as h } from "./breakpoints.mjs";
3
+ let n;
4
+ const l = 1280, u = 800, m = {
5
+ mobile: (e) => e.width < h,
6
+ tablet: (e) => e.width < c,
7
+ smallDesktop: (e) => e.width < f,
8
+ desktop: (e) => e.width >= c
9
+ };
10
+ function w(e = {}, { deferViewportSync: o = !1 } = {}) {
11
+ const s = typeof window < "u", r = s && !o, t = p({
12
+ width: r ? window.innerWidth : l,
13
+ height: r ? window.innerHeight : u
14
+ }), d = { ...m, ...e };
15
+ for (const i in d)
16
+ t[i] = a(() => d[i](t));
17
+ if (s) {
18
+ const i = () => {
19
+ t.width = window.innerWidth, t.height = window.innerHeight;
20
+ };
21
+ o && queueMicrotask(i), window.addEventListener("resize", i);
22
+ }
23
+ return t;
24
+ }
25
+ function g() {
26
+ return n || (n = w({}, { deferViewportSync: !0 })), n;
5
27
  }
6
- const w = {
7
- install(o, s) {
8
- const i = Object.assign({}, {
28
+ const R = {
29
+ install(e, o) {
30
+ const s = Object.assign({}, {
9
31
  computed: {}
10
- }, s), n = {};
11
- for (const t in i.computed)
12
- n[t] = r(() => i.computed[t](e));
13
- e = d({
14
- width: window.innerWidth,
15
- height: window.innerHeight,
16
- ...n
17
- }), o.config.globalProperties.$responsive = e, window.addEventListener("resize", () => {
18
- e.width = window.innerWidth, e.height = window.innerHeight;
19
- });
32
+ }, o);
33
+ n = w(s.computed), e.config.globalProperties.$responsive = n;
20
34
  }
21
35
  };
22
36
  export {
23
- w as default,
24
- c as useResponsive
37
+ R as default,
38
+ g as useResponsive
25
39
  };
@@ -1,4 +1,12 @@
1
- export function useResponsive(): any;
1
+ /**
2
+ * Viewport state shared by the whole app. Lazily created on first use, so
3
+ * components work with no plugin registration: installing the Responsive
4
+ * plugin is only needed to customize the flags or to read `$responsive`
5
+ * in templates.
6
+ *
7
+ * @returns {import('./responsive.types').ResponsiveGlobalComponentProperty}
8
+ */
9
+ export function useResponsive(): import("./responsive.types").ResponsiveGlobalComponentProperty;
2
10
  declare namespace _default {
3
11
  function install(app: any, options: any): void;
4
12
  }
@@ -1,58 +1,57 @@
1
- import { useStorage as g } from "@vueuse/core";
2
- import { ref as E, computed as d, watch as f } from "vue";
3
- import { useRoute as b } from "vue-router";
1
+ import { useStorage as A } from "@vueuse/core";
2
+ import { ref as E, computed as d, watch as f, getCurrentInstance as b } from "vue";
4
3
  const T = {
5
4
  LIVESTORM: "Livestorm",
6
5
  CUSTOMER1: "customer1",
7
6
  CUSTOMER2: "customer2",
8
7
  CUSTOMER3: "customer3"
9
- }, v = {
8
+ }, p = {
10
9
  LIGHT: "light",
11
10
  DARK: "dark"
12
- }, y = Object.values(T), C = Object.values(v);
11
+ }, y = Object.values(T), C = Object.values(p);
13
12
  Object.fromEntries(
14
13
  Object.entries(T).map(([e, t]) => [e.toLowerCase(), t])
15
14
  );
16
15
  const I = ["signup", "signin", "forgot-password", "reset-password"], R = {
17
16
  theme: T.LIVESTORM,
18
- mode: v.DARK,
19
- contentMode: v.LIGHT
20
- }, i = E(/* @__PURE__ */ new Map()), p = E(/* @__PURE__ */ new Map()), o = g(
17
+ mode: p.DARK,
18
+ contentMode: p.LIGHT
19
+ }, i = E(/* @__PURE__ */ new Map()), v = E(/* @__PURE__ */ new Map()), o = A(
21
20
  "themeAndMode",
22
21
  R
23
22
  );
24
- function G({ themeOverride: e, modeOverride: t, contentModeOverride: n } = {}) {
23
+ function k({ themeOverride: e, modeOverride: t, contentModeOverride: n } = {}) {
25
24
  const s = !!(e || t || n), a = e || o.value.theme, r = t || o.value.mode, S = n || o.value.contentMode;
26
25
  function O() {
27
26
  document.querySelector("body").setAttribute("data-colors-primitive", a), document.querySelector("body").setAttribute("data-colors-semantic", r), H();
28
27
  }
29
- function h() {
30
- const m = () => t || o.value.mode, c = () => n || o.value.contentMode;
28
+ function g() {
29
+ const l = () => t || o.value.mode, c = () => n || o.value.contentMode;
31
30
  f(
32
31
  () => Array.from(i.value.values()),
33
- () => D({ mode: m(), hasOverrides: s }),
32
+ () => D({ mode: l(), hasOverrides: s }),
34
33
  { immediate: !0 }
35
34
  ), f(
36
- () => Array.from(p.value.values()),
35
+ () => Array.from(v.value.values()),
37
36
  () => M({ mode: c(), hasOverrides: s }),
38
37
  { immediate: !0 }
39
38
  );
40
- const l = b();
39
+ const m = b();
41
40
  f(
42
- () => l?.name,
41
+ () => m?.appContext.config.globalProperties.$route?.name,
43
42
  (u) => {
44
43
  u && I.includes(u.toString()) && M({ mode: c(), hasOverrides: s });
45
44
  },
46
45
  { immediate: !0 }
47
46
  );
48
47
  }
49
- function A(m, { selector: c, element: l, modeOverride: u }) {
50
- (m === "mode" ? i : p).value.set(c, { element: l, modeOverride: u });
48
+ function h(l, { selector: c, element: m, modeOverride: u }) {
49
+ (l === "mode" ? i : v).value.set(c, { element: m, modeOverride: u });
51
50
  }
52
51
  return {
53
52
  initOnAppMounted: O,
54
- registerWatchers: h,
55
- addElementToWatch: A,
53
+ registerWatchers: g,
54
+ addElementToWatch: h,
56
55
  updateContentMode: w,
57
56
  updateTheme: L,
58
57
  updateMode: _,
@@ -87,7 +86,7 @@ function D({ mode: e, hasOverrides: t = !1 }) {
87
86
  }
88
87
  function M({ mode: e, hasOverrides: t = !1, ignoreElementOverrides: n = !1 }) {
89
88
  t || (o.value.contentMode = e);
90
- for (const { element: s, modeOverride: a } of p.value.values()) {
89
+ for (const { element: s, modeOverride: a } of v.value.values()) {
91
90
  const r = n ? e : a || e;
92
91
  s.setAttribute("data-colors-semantic", r);
93
92
  }
@@ -105,5 +104,5 @@ function H() {
105
104
  }
106
105
  }
107
106
  export {
108
- G as u
107
+ k as u
109
108
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livestorm/ui-vue",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "Livestorm UI components for Vue 3",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -39,9 +39,8 @@
39
39
  "mitt": "^3.0.1",
40
40
  "reka-ui": "2.9.10",
41
41
  "tailwindcss": "^4.3.2",
42
- "vue-safe-teleport": "^0.1.2",
43
- "@livestorm/ui-icons": "^2.0.1",
44
- "@livestorm/ui-tokens": "^2.0.1"
42
+ "@livestorm/ui-icons": "^2.0.3",
43
+ "@livestorm/ui-tokens": "^2.0.3"
45
44
  },
46
45
  "devDependencies": {
47
46
  "@chromatic-com/storybook": "^5.2.1",
@@ -80,6 +79,11 @@
80
79
  "vue": "^3.5.30",
81
80
  "vue-router": "^5.0.3"
82
81
  },
82
+ "peerDependenciesMeta": {
83
+ "vue-router": {
84
+ "optional": true
85
+ }
86
+ },
83
87
  "exports": {
84
88
  ".": {
85
89
  "types": "./dist/types/index.d.ts",
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, getCurrentInstance } from 'vue'
3
3
  import type { ResponsiveGlobalComponentProperty } from '../../utils/responsive.types'
4
+ import { useResponsive } from '../../utils/responsive'
4
5
  import { useToasts, type ToastOptions } from './toastDSV2'
5
6
  import ToastMessage from './ToastMessage.vue'
6
7
 
@@ -8,7 +9,9 @@ const app = getCurrentInstance()
8
9
  if (!app) {
9
10
  throw new Error('No current instance found')
10
11
  }
11
- const responsive = computed(() => app.appContext.config.globalProperties.$responsive as ResponsiveGlobalComponentProperty)
12
+ // Prefer the app-installed $responsive (it may carry custom flags); fall
13
+ // back to the shared singleton so no plugin registration is required.
14
+ const responsive = computed(() => (app.appContext.config.globalProperties.$responsive ?? useResponsive()) as ResponsiveGlobalComponentProperty)
12
15
 
13
16
  const MAX_ITEMS_DISPLAYED = 3
14
17
 
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, onBeforeUnmount, onMounted, ref, watch, getCurrentInstance, type Slot } from 'vue'
3
3
  import { BREAKPOINT_LG } from '../../utils/breakpoints'
4
+ import { useResponsive } from '../../utils/responsive'
4
5
  import useThemeAndModes from '../../composables/useThemeAndModes'
5
6
  import { useMobileMenu } from '../../composables/useMobileMenu'
6
7
  import UiLoader from '../UiLoader/UiLoader.vue'
@@ -83,7 +84,9 @@ const app = getCurrentInstance()
83
84
  if (!app) {
84
85
  throw new Error('No current instance found')
85
86
  }
86
- const responsive = computed(() => app.appContext.config.globalProperties.$responsive)
87
+ // Prefer the app-installed $responsive (it may carry custom flags); fall
88
+ // back to the shared singleton so no plugin registration is required.
89
+ const responsive = computed(() => app.appContext.config.globalProperties.$responsive ?? useResponsive())
87
90
  const displayLeftSlot = computed(() => responsive.value.width >= BREAKPOINT_LG)
88
91
 
89
92
  const UiPage = ref<HTMLElement | undefined>()
@@ -1,6 +1,5 @@
1
1
  import { useStorage } from '@vueuse/core'
2
- import { computed, ref, watch } from 'vue'
3
- import { useRoute } from 'vue-router'
2
+ import { computed, getCurrentInstance, ref, watch } from 'vue'
4
3
  import {
5
4
  THEME_LIST,
6
5
  MODE_OPTIONS,
@@ -82,10 +81,15 @@ export default function useThemeAndModes (
82
81
  { immediate: true }
83
82
  )
84
83
 
85
- const route = useRoute()
84
+ // vue-router is an optional peer: read the route from the app's global
85
+ // properties (which the router installs as $route) instead of importing
86
+ // vue-router, so the package resolves without it. Without a router the
87
+ // getter stays undefined and this watcher is simply inert. Same pattern
88
+ // as the $responsive fallback in UiPage/LayoutToasts.
89
+ const instance = getCurrentInstance()
86
90
 
87
91
  watch(
88
- () => route?.name,
92
+ () => instance?.appContext.config.globalProperties.$route?.name,
89
93
  (routeName) => {
90
94
  if (!routeName) { return }
91
95
  if (ROUTES_TO_WATCH.includes(routeName.toString() as typeof ROUTES_TO_WATCH[number])) {
@@ -1,9 +1,75 @@
1
1
  import { reactive, computed } from 'vue'
2
2
  import generateFlags from './devices'
3
+ import { BREAKPOINT_MD, BREAKPOINT_LG, BREAKPOINT_XL_PLUS } from './breakpoints'
3
4
 
4
5
  let responsive
5
6
 
7
+ // Under SSR there is no viewport: the server renders a desktop-sized static
8
+ // snapshot. To keep hydration safe, the client's lazy singleton starts from
9
+ // the SAME snapshot (identical trees during the hydration pass) and syncs to
10
+ // the real viewport in a microtask, which runs after hydration completes but
11
+ // before the first paint.
12
+ const SSR_WIDTH = 1280
13
+ const SSR_HEIGHT = 800
14
+
15
+ // Default flags, derived from the design system breakpoints. An app installing
16
+ // the Responsive plugin can override any of them via `options.computed`.
17
+ const DEFAULT_FLAGS = {
18
+ mobile: state => state.width < BREAKPOINT_MD,
19
+ tablet: state => state.width < BREAKPOINT_LG,
20
+ smallDesktop: state => state.width < BREAKPOINT_XL_PLUS,
21
+ desktop: state => state.width >= BREAKPOINT_LG,
22
+ }
23
+
24
+ function createResponsive (customComputed = {}, { deferViewportSync = false } = {}) {
25
+ const isClient = typeof window !== 'undefined'
26
+ const readViewportNow = isClient && !deferViewportSync
27
+
28
+ const state = reactive({
29
+ width: readViewportNow ? window.innerWidth : SSR_WIDTH,
30
+ height: readViewportNow ? window.innerHeight : SSR_HEIGHT,
31
+ })
32
+
33
+ const derived = { ...DEFAULT_FLAGS, ...customComputed }
34
+ for (const key in derived) {
35
+ // Assigning a computed ref onto a reactive object stores it unwrapped:
36
+ // consumers read plain booleans while reactivity is preserved.
37
+ state[key] = computed(() => derived[key](state))
38
+ }
39
+
40
+ if (isClient) {
41
+ const sync = () => {
42
+ state.width = window.innerWidth
43
+ state.height = window.innerHeight
44
+ }
45
+ if (deferViewportSync) {
46
+ // Runs after the synchronous hydration pass but before the first
47
+ // paint: server HTML and client hydration tree stay identical, then a
48
+ // normal reactive update applies the real viewport with no visible
49
+ // flash for client-only apps either.
50
+ queueMicrotask(sync)
51
+ }
52
+ window.addEventListener('resize', sync)
53
+ }
54
+
55
+ return state
56
+ }
57
+
58
+ /**
59
+ * Viewport state shared by the whole app. Lazily created on first use, so
60
+ * components work with no plugin registration: installing the Responsive
61
+ * plugin is only needed to customize the flags or to read `$responsive`
62
+ * in templates.
63
+ *
64
+ * @returns {import('./responsive.types').ResponsiveGlobalComponentProperty}
65
+ */
6
66
  export function useResponsive () {
67
+ if (!responsive) {
68
+ // Deferred viewport sync keeps SSR hydration safe; the plugin path below
69
+ // keeps its historical immediate initialization (it installs client-side
70
+ // at app boot, before anything rendered).
71
+ responsive = createResponsive({}, { deferViewportSync: true })
72
+ }
7
73
  return responsive
8
74
  }
9
75
 
@@ -13,11 +79,6 @@ export default {
13
79
  computed: {},
14
80
  }, options)
15
81
 
16
- const computedProps = {}
17
- for (const key in finalOptions.computed) {
18
- computedProps[key] = computed(() => finalOptions.computed[key](responsive))
19
- }
20
-
21
82
  if (import.meta.env.RESPONSIVE_V2) {
22
83
  let device
23
84
  const defaultUserAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.39 Safari/537.36'
@@ -44,18 +105,11 @@ export default {
44
105
  }
45
106
  app.config.globalProperties.$responsive = responsive
46
107
  } else {
47
- responsive = reactive({
48
- width: window.innerWidth,
49
- height: window.innerHeight,
50
- ...computedProps,
51
- })
52
-
108
+ // The plugin-created instance replaces the lazy singleton so that
109
+ // useResponsive() and $responsive stay the same object, with the
110
+ // app's custom computed applied on top of the default flags.
111
+ responsive = createResponsive(finalOptions.computed)
53
112
  app.config.globalProperties.$responsive = responsive
54
-
55
- window.addEventListener('resize', () => {
56
- responsive.width = window.innerWidth
57
- responsive.height = window.innerHeight
58
- })
59
113
  }
60
114
  },
61
115
  }