@leavepulse/ui 0.75.0 → 0.76.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 (42) hide show
  1. package/README.md +44 -0
  2. package/dist/assets/{LpShortcutsDialog-BUp_BX4s.css → LpShortcutsDialog-C6uzPeMf.css} +7 -7
  3. package/dist/assets/LpTabs-CJ0rYQbx.css +18 -0
  4. package/dist/chunks/{LpAddressInput-Al3HT5ey.js → LpAddressInput-De_LTYM7.js} +1 -1
  5. package/dist/chunks/{LpAppShell-CpSSm5WT.js → LpAppShell-Cpy_t1U_.js} +1 -1
  6. package/dist/chunks/{LpConfirmDialog-DY2VjDji.js → LpConfirmDialog-CphJR6Q9.js} +4 -2
  7. package/dist/chunks/{LpConfirmHost-DvSZTJ4N.js → LpConfirmHost-QSJ0zl63.js} +1 -1
  8. package/dist/chunks/{LpDrawer-RRamxbLV.js → LpDrawer-DQZixOqJ.js} +13 -0
  9. package/dist/chunks/{LpModal-BnjZ2iem.js → LpModal-C0tGg_D_.js} +13 -0
  10. package/dist/chunks/{LpModalNav-DQpV2kdn.js → LpModalNav-iDDtT9c1.js} +2 -2
  11. package/dist/chunks/{LpSidebar-BoNLKgh-.js → LpSidebar-D6zO8vLm.js} +1 -1
  12. package/dist/components/LpAddressInput.vue.js +1 -1
  13. package/dist/components/LpAppShell.vue.js +1 -1
  14. package/dist/components/LpConfirmDialog.vue.d.ts +4 -0
  15. package/dist/components/LpConfirmDialog.vue.js +1 -1
  16. package/dist/components/LpConfirmHost.vue.js +1 -1
  17. package/dist/components/LpDrawer.vue.d.ts +16 -0
  18. package/dist/components/LpDrawer.vue.js +1 -1
  19. package/dist/components/LpModal.vue.d.ts +9 -0
  20. package/dist/components/LpModal.vue.js +1 -1
  21. package/dist/components/LpModalNav.vue.js +1 -1
  22. package/dist/components/LpShortcutsDialog.vue.d.ts +4 -0
  23. package/dist/components/LpShortcutsDialog.vue.js +6 -4
  24. package/dist/components/LpSidebar.vue.js +1 -1
  25. package/dist/components/LpTabs.vue.d.ts +9 -0
  26. package/dist/components/LpTabs.vue.js +18 -4
  27. package/dist/composables/overlayHistory.d.ts +33 -0
  28. package/dist/composables/overlayHistory.js +237 -0
  29. package/dist/composables/useOverlayHistory.d.ts +45 -0
  30. package/dist/composables/useOverlayHistory.js +84 -0
  31. package/dist/index.d.ts +3 -0
  32. package/dist/index.js +12 -8
  33. package/package.json +2 -1
  34. package/src/components/LpConfirmDialog.vue +5 -1
  35. package/src/components/LpDrawer.vue +21 -0
  36. package/src/components/LpModal.vue +21 -1
  37. package/src/components/LpShortcutsDialog.vue +5 -1
  38. package/src/components/LpTabs.vue +26 -2
  39. package/src/composables/overlayHistory.ts +381 -0
  40. package/src/composables/useOverlayHistory.ts +173 -0
  41. package/src/index.ts +3 -0
  42. package/dist/assets/LpTabs-CDchzoRL.css +0 -18
package/README.md CHANGED
@@ -83,6 +83,50 @@ app.use(lpConfig({
83
83
 
84
84
  A component's own `labels` prop wins over the configuration.
85
85
 
86
+ ### Back and Forward for overlays
87
+
88
+ `provideOverlayHistory()` (at the app root, like `provideGlass()`) makes the
89
+ browser's Back button close overlays instead of leaving the page:
90
+
91
+ ```ts
92
+ import { provideOverlayHistory } from "@leavepulse/ui"
93
+
94
+ // in App.vue's setup; pass the router so a Back that only closes a dialog is not a navigation
95
+ provideOverlayHistory(true, { router })
96
+ ```
97
+
98
+ - Opening an `LpModal` or `LpDrawer` (and what is built on them:
99
+ `LpConfirmDialog`, `LpShortcutsDialog`) adds one history entry. Back closes
100
+ the topmost overlay; nested ones close one per press, top first.
101
+ - Closing through the UI (close button, Escape, the scrim, the app setting
102
+ `open` to false) takes the entry back out, so Back is never spent on a step
103
+ that shows nothing. A `guard` that keeps the overlay open on Back keeps its
104
+ entry too.
105
+ - `LpTabs` inside an open overlay record each switch: Back returns to the
106
+ previous tab before it closes the overlay. Page-level tabs stay out (an app
107
+ keeps those in its URL); `history` on `LpTabs` opts out inside an overlay or
108
+ in on a page without a router.
109
+ - Forward after Back does nothing by default: the step is undone at once, so
110
+ no dead entry is left. The kit only sees a boolean `open`, and what the app
111
+ put into the overlay may be gone by then. `history="reopen"` (or
112
+ `provideOverlayHistory("reopen")`) re-opens it on Forward by emitting
113
+ `update:open` true while the overlay is still mounted.
114
+ - `history` on a single overlay wins over the app-wide mode: `false` keeps an
115
+ overlay out of history, for one the app already opens from its route query
116
+ (it would otherwise take two entries for one dialog).
117
+ - A real navigation (a link, `router.push` to another path) closes the open
118
+ overlays, and the entries they leave under the new page are skipped, so Back
119
+ from it lands on the page in one press. A push that keeps the path (a query
120
+ or hash, such as an overlay driven by the route query) leaves them open.
121
+
122
+ The URL never changes and the router's own `history.state` data is copied into
123
+ each entry. The `router` option is vue-router's instance (anything with a
124
+ `listening` switch works): without it the router sees each Back that closes an
125
+ overlay as a visit to the page it is already on (guards and `afterEach` run
126
+ again, nothing remounts). A Forward onto a kit entry still reaches it that
127
+ way, because the entry it starts from belongs to the router. Plain
128
+ `pushState` and `popstate` only, so WebKit (Safari, WebKitGTK) works too.
129
+
86
130
  ### Nuxt
87
131
 
88
132
  ```ts
@@ -1,12 +1,12 @@
1
1
 
2
2
  /* Two columns where there is room: the list is long enough that one column
3
3
  pushes the last group off a laptop screen. */
4
- .shortcut-groups[data-v-611c415f] {
4
+ .shortcut-groups[data-v-f948ed00] {
5
5
  display: grid;
6
6
  gap: 18px;
7
7
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
8
8
  }
9
- .shortcut-group h3[data-v-611c415f] {
9
+ .shortcut-group h3[data-v-f948ed00] {
10
10
  margin: 0 0 6px;
11
11
  color: var(--color-muted);
12
12
  font-size: 0.7rem;
@@ -14,25 +14,25 @@
14
14
  letter-spacing: 0.06em;
15
15
  text-transform: uppercase;
16
16
  }
17
- .shortcut-group dl[data-v-611c415f] {
17
+ .shortcut-group dl[data-v-f948ed00] {
18
18
  display: flex;
19
19
  flex-direction: column;
20
20
  gap: 1px;
21
21
  margin: 0;
22
22
  }
23
- .shortcut-row[data-v-611c415f] {
23
+ .shortcut-row[data-v-f948ed00] {
24
24
  display: flex;
25
25
  align-items: center;
26
26
  justify-content: space-between;
27
27
  gap: 12px;
28
28
  padding: 5px 0;
29
29
  }
30
- .shortcut-row dt[data-v-611c415f] {
30
+ .shortcut-row dt[data-v-f948ed00] {
31
31
  color: var(--color-ink);
32
32
  font-size: 0.82rem;
33
33
  min-width: 0;
34
34
  }
35
- .shortcut-conflict[data-v-611c415f] {
35
+ .shortcut-conflict[data-v-f948ed00] {
36
36
  display: inline-flex;
37
37
  width: 14px;
38
38
  height: 14px;
@@ -47,7 +47,7 @@
47
47
  vertical-align: middle;
48
48
  cursor: help;
49
49
  }
50
- .shortcut-row dd[data-v-611c415f] {
50
+ .shortcut-row dd[data-v-f948ed00] {
51
51
  display: flex;
52
52
  flex-shrink: 0;
53
53
  gap: 3px;
@@ -0,0 +1,18 @@
1
+
2
+ .lp-tab-badge-enter-active[data-v-1d673b52],
3
+ .lp-tab-badge-leave-active[data-v-1d673b52] {
4
+ transition:
5
+ opacity var(--duration-fast) var(--ease-emphasized),
6
+ transform var(--duration-fast) var(--ease-emphasized);
7
+ }
8
+ .lp-tab-badge-enter-from[data-v-1d673b52],
9
+ .lp-tab-badge-leave-to[data-v-1d673b52] {
10
+ opacity: 0;
11
+ transform: scale(0.6);
12
+ }
13
+ @media (prefers-reduced-motion: reduce) {
14
+ .lp-tab-badge-enter-active[data-v-1d673b52],
15
+ .lp-tab-badge-leave-active[data-v-1d673b52] {
16
+ transition: none;
17
+ }
18
+ }
@@ -7,7 +7,7 @@ import { _ as _sfc_main$3 } from "./LpButton-CFb44IjC.js";
7
7
  import { _ as _sfc_main$1 } from "./LpFormField-Ch69Jd-X.js";
8
8
  import { _ as _sfc_main$4 } from "./LpIcon--5WSb3jJ.js";
9
9
  import { _ as _sfc_main$5 } from "./LpInput-Tw-yxT4I.js";
10
- import { _ as _sfc_main$7 } from "./LpModal-BnjZ2iem.js";
10
+ import { _ as _sfc_main$7 } from "./LpModal-C0tGg_D_.js";
11
11
  import { _ as _sfc_main$6 } from "./LpSelect-Cxq4yBGd.js";
12
12
  import { useFieldControl } from "../composables/useField.js";
13
13
  import { useLabels } from "../composables/useLpConfig.js";
@@ -7,7 +7,7 @@ import { _ as _sfc_main$3 } from "./LpButton-CFb44IjC.js";
7
7
  import { _ as _sfc_main$4 } from "./LpIcon--5WSb3jJ.js";
8
8
  import { _ as _sfc_main$6 } from "./LpScrollArea-Hz3-JWUG.js";
9
9
  import { _ as _sfc_main$5 } from "./LpShift-CCq7dofl.js";
10
- import { _ as _sfc_main$1 } from "./LpSidebar-BoNLKgh-.js";
10
+ import { _ as _sfc_main$1 } from "./LpSidebar-D6zO8vLm.js";
11
11
  import { useMergedAttrs, mergeClass } from "../composables/useMergedClass.js";
12
12
  const _hoisted_1 = ["href"];
13
13
  const _hoisted_2 = { class: "flex min-w-0 flex-1 flex-col" };
@@ -2,7 +2,7 @@ import { defineComponent, computed, ref, watch, openBlock, createBlock, createSl
2
2
  import { useLabels } from "../composables/useLpConfig.js";
3
3
  import { _ as _sfc_main$2 } from "./LpButton-CFb44IjC.js";
4
4
  import { _ as _sfc_main$3 } from "./LpIcon--5WSb3jJ.js";
5
- import { _ as _sfc_main$1 } from "./LpModal-BnjZ2iem.js";
5
+ import { _ as _sfc_main$1 } from "./LpModal-C0tGg_D_.js";
6
6
  const _hoisted_1 = { class: "inline-flex items-center gap-2.5" };
7
7
  const _hoisted_2 = {
8
8
  key: 0,
@@ -23,6 +23,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
23
23
  loadingLabel: {},
24
24
  action: {},
25
25
  glass: { type: Boolean, default: void 0 },
26
+ history: { type: [Boolean, String], default: void 0 },
26
27
  labels: {}
27
28
  },
28
29
  emits: ["update:open", "confirm", "cancel"],
@@ -76,6 +77,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
76
77
  return (_ctx, _cache) => {
77
78
  return openBlock(), createBlock(_sfc_main$1, {
78
79
  glass: __props.glass,
80
+ history: __props.history,
79
81
  open: __props.open,
80
82
  title: __props.title,
81
83
  description: __props.description,
@@ -130,7 +132,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
130
132
  ]),
131
133
  key: "0"
132
134
  } : void 0
133
- ]), 1032, ["glass", "open", "title", "description"]);
135
+ ]), 1032, ["glass", "history", "open", "title", "description"]);
134
136
  };
135
137
  }
136
138
  });
@@ -1,6 +1,6 @@
1
1
  import { defineComponent, computed, ref, watch, openBlock, createBlock, createCommentVNode } from "vue";
2
2
  import { confirmQueue, settleConfirm } from "../components/confirm.js";
3
- import { _ as _sfc_main$1 } from "./LpConfirmDialog-DY2VjDji.js";
3
+ import { _ as _sfc_main$1 } from "./LpConfirmDialog-CphJR6Q9.js";
4
4
  const _sfc_main = /* @__PURE__ */ defineComponent({
5
5
  __name: "LpConfirmHost",
6
6
  props: {
@@ -4,6 +4,7 @@ import { CLOSE_ICON } from "../components/dropdown.js";
4
4
  import { prefersReducedMotion } from "../composables/easing.js";
5
5
  import { useCloseGuard } from "../composables/useCloseGuard.js";
6
6
  import { useGlass } from "../composables/useGlass.js";
7
+ import { useOverlayHistory } from "../composables/useOverlayHistory.js";
7
8
  import { useLabels } from "../composables/useLpConfig.js";
8
9
  import { usePersisted } from "../composables/usePersisted.js";
9
10
  import { _ as _sfc_main$1 } from "./LpIcon--5WSb3jJ.js";
@@ -48,6 +49,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
48
49
  guard: { type: [Boolean, Function] },
49
50
  glass: { type: Boolean, default: void 0 },
50
51
  ariaLabel: {},
52
+ history: { type: [Boolean, String], default: void 0 },
51
53
  labels: {}
52
54
  },
53
55
  emits: ["update:open"],
@@ -87,6 +89,17 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
87
89
  if (await allowClose()) emit("update:open", false);
88
90
  }
89
91
  __expose({ close: requestClose });
92
+ useOverlayHistory({
93
+ mode: () => props.history,
94
+ open: () => !!props.open,
95
+ close: async () => {
96
+ if (!await allowClose()) return false;
97
+ emit("update:open", false);
98
+ return true;
99
+ },
100
+ reopen: () => emit("update:open", true),
101
+ navigated: () => emit("update:open", false)
102
+ });
90
103
  function guardClose(event) {
91
104
  if (!armed()) return;
92
105
  event.preventDefault();
@@ -2,6 +2,7 @@ import { defineComponent, ref, computed, useSlots, watch, onBeforeUnmount, openB
2
2
  import { DialogRoot, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription, DialogClose } from "reka-ui";
3
3
  import { useCloseGuard } from "../composables/useCloseGuard.js";
4
4
  import { useGlass } from "../composables/useGlass.js";
5
+ import { useOverlayHistory } from "../composables/useOverlayHistory.js";
5
6
  import { useLabels } from "../composables/useLpConfig.js";
6
7
  import { useShift } from "../composables/useShift.js";
7
8
  import { useModalLayer } from "../components/modalDepth.js";
@@ -47,6 +48,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
47
48
  role: {},
48
49
  dismissible: { type: Boolean, default: true },
49
50
  guard: { type: [Boolean, Function], default: void 0 },
51
+ history: { type: [Boolean, String], default: void 0 },
50
52
  labels: {}
51
53
  },
52
54
  emits: ["update:open"],
@@ -64,6 +66,17 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
64
66
  function close() {
65
67
  void onOpenChange(false);
66
68
  }
69
+ useOverlayHistory({
70
+ mode: () => props.history,
71
+ open: () => !!props.open,
72
+ close: async () => {
73
+ if (!await allowClose()) return false;
74
+ emit("update:open", false);
75
+ return true;
76
+ },
77
+ reopen: () => emit("update:open", true),
78
+ navigated: () => emit("update:open", false)
79
+ });
67
80
  __expose({ close });
68
81
  function onEscape(event) {
69
82
  guardOutside(event);
@@ -3,8 +3,8 @@ import { useGlass } from "../composables/useGlass.js";
3
3
  import { useLabels } from "../composables/useLpConfig.js";
4
4
  import { _ as _sfc_main$2 } from "./LpButton-CFb44IjC.js";
5
5
  import { _ as _sfc_main$3 } from "./LpIcon--5WSb3jJ.js";
6
- import { _ as _sfc_main$1 } from "./LpModal-BnjZ2iem.js";
7
- import { _ as _sfc_main$4 } from "./LpSidebar-BoNLKgh-.js";
6
+ import { _ as _sfc_main$1 } from "./LpModal-C0tGg_D_.js";
7
+ import { _ as _sfc_main$4 } from "./LpSidebar-D6zO8vLm.js";
8
8
  const _hoisted_1 = { class: "flex items-center gap-2" };
9
9
  const _sfc_main = /* @__PURE__ */ defineComponent({
10
10
  __name: "LpModalNav",
@@ -2,7 +2,7 @@ import { defineComponent, computed, ref, useId, openBlock, createElementBlock, F
2
2
  import { useHotkeys, parseCombo } from "../composables/useHotkeys.js";
3
3
  import { usePersisted } from "../composables/usePersisted.js";
4
4
  import { useLabels } from "../composables/useLpConfig.js";
5
- import { _ as _sfc_main$5 } from "./LpDrawer-RRamxbLV.js";
5
+ import { _ as _sfc_main$5 } from "./LpDrawer-DQZixOqJ.js";
6
6
  import { _ as _sfc_main$2 } from "./LpIcon--5WSb3jJ.js";
7
7
  import { _ as _sfc_main$3 } from "./LpInput-Tw-yxT4I.js";
8
8
  import { _ as _sfc_main$1 } from "./LpTooltip-BcBvIx4z.js";
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpAddressInput-Al3HT5ey.js";
1
+ import { _ as _sfc_main } from "../chunks/LpAddressInput-De_LTYM7.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpAppShell-CpSSm5WT.js";
1
+ import { _ as _sfc_main } from "../chunks/LpAppShell-Cpy_t1U_.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,3 +1,4 @@
1
+ import type { OverlayHistoryMode } from "../composables/useOverlayHistory";
1
2
  export type ConfirmLabels = {
2
3
  confirm: string;
3
4
  cancel: string;
@@ -44,6 +45,8 @@ type __VLS_Props = {
44
45
  action?: () => unknown;
45
46
  /** Frosted glass panel, passed to LpModal. Follows `provideGlass` when unset. */
46
47
  glass?: boolean;
48
+ /** Browser history for the dialog, passed to LpModal. Follows `provideOverlayHistory` when unset. */
49
+ history?: OverlayHistoryMode;
47
50
  labels?: Partial<ConfirmLabels>;
48
51
  };
49
52
  declare var __VLS_16: {};
@@ -59,6 +62,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
59
62
  "onUpdate:open"?: ((value: boolean) => any) | undefined;
60
63
  onConfirm?: (() => any) | undefined;
61
64
  }>, {
65
+ history: OverlayHistoryMode;
62
66
  glass: boolean;
63
67
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
64
68
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpConfirmDialog-DY2VjDji.js";
1
+ import { _ as _sfc_main } from "../chunks/LpConfirmDialog-CphJR6Q9.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpConfirmHost-DvSZTJ4N.js";
1
+ import { _ as _sfc_main } from "../chunks/LpConfirmHost-QSJ0zl63.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,5 @@
1
1
  import { type CloseGuard } from "../composables/useCloseGuard";
2
+ import { type OverlayHistoryMode } from "../composables/useOverlayHistory";
2
3
  declare const _default: typeof __VLS_export;
3
4
  export default _default;
4
5
  declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
@@ -86,6 +87,13 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
86
87
  glass?: boolean;
87
88
  /** Accessible name for a drawer without a visible `title`. */
88
89
  ariaLabel?: string;
90
+ /**
91
+ * Browser Back closes it, and opening it adds a history entry. Unset
92
+ * follows `provideOverlayHistory` (off without one); `"reopen"` also
93
+ * opens it again on Forward; `false` keeps it out of history, for an
94
+ * overlay the app already drives from its route.
95
+ */
96
+ history?: OverlayHistoryMode;
89
97
  labels?: Partial<{
90
98
  close: string;
91
99
  }>;
@@ -178,12 +186,20 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
178
186
  glass?: boolean;
179
187
  /** Accessible name for a drawer without a visible `title`. */
180
188
  ariaLabel?: string;
189
+ /**
190
+ * Browser Back closes it, and opening it adds a history entry. Unset
191
+ * follows `provideOverlayHistory` (off without one); `"reopen"` also
192
+ * opens it again on Forward; `false` keeps it out of history, for an
193
+ * overlay the app already drives from its route.
194
+ */
195
+ history?: OverlayHistoryMode;
181
196
  labels?: Partial<{
182
197
  close: string;
183
198
  }>;
184
199
  }> & Readonly<{
185
200
  "onUpdate:open"?: ((value: boolean) => any) | undefined;
186
201
  }>, {
202
+ history: OverlayHistoryMode;
187
203
  size: "sm" | "md" | "lg" | "xl";
188
204
  side: "left" | "right";
189
205
  glass: boolean;
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpDrawer-RRamxbLV.js";
1
+ import { _ as _sfc_main } from "../chunks/LpDrawer-DQZixOqJ.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,5 @@
1
1
  import { type CloseGuard } from "../composables/useCloseGuard";
2
+ import { type OverlayHistoryMode } from "../composables/useOverlayHistory";
2
3
  type __VLS_Props = {
3
4
  open?: boolean;
4
5
  title?: string;
@@ -52,6 +53,13 @@ type __VLS_Props = {
52
53
  * form's state, `:guard="form.dirty"`. See `useCloseGuard`.
53
54
  */
54
55
  guard?: CloseGuard;
56
+ /**
57
+ * Browser Back closes it, and opening it adds a history entry. Unset
58
+ * follows `provideOverlayHistory` (off without one); `"reopen"` also
59
+ * opens it again on Forward; `false` keeps it out of history, for an
60
+ * overlay the app already drives from its route.
61
+ */
62
+ history?: OverlayHistoryMode;
55
63
  labels?: Partial<{
56
64
  close: string;
57
65
  }>;
@@ -91,6 +99,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
91
99
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
92
100
  "onUpdate:open"?: ((value: boolean) => any) | undefined;
93
101
  }>, {
102
+ history: OverlayHistoryMode;
94
103
  size: "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "full";
95
104
  autoFocus: "auto" | "none" | string;
96
105
  glass: boolean;
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpModal-BnjZ2iem.js";
1
+ import { _ as _sfc_main } from "../chunks/LpModal-C0tGg_D_.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpModalNav-DQpV2kdn.js";
1
+ import { _ as _sfc_main } from "../chunks/LpModalNav-iDDtT9c1.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,3 +1,4 @@
1
+ import type { OverlayHistoryMode } from "../composables/useOverlayHistory";
1
2
  import { type ShortcutGroup } from "../composables/useHotkeys";
2
3
  type __VLS_Props = {
3
4
  open?: boolean;
@@ -8,6 +9,8 @@ type __VLS_Props = {
8
9
  description?: string;
9
10
  /** Frosted glass panel, passed to LpModal. Follows `provideGlass` when unset. */
10
11
  glass?: boolean;
12
+ /** Browser history for the dialog, passed to LpModal. Follows `provideOverlayHistory` when unset. */
13
+ history?: OverlayHistoryMode;
11
14
  /**
12
15
  * A key that toggles the reference, e.g. "F1". Bound only while the dialog
13
16
  * is mounted, also inside inputs and over other overlays, and the browser's
@@ -20,6 +23,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
20
23
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
21
24
  "onUpdate:open"?: ((value: boolean) => any) | undefined;
22
25
  }>, {
26
+ history: OverlayHistoryMode;
23
27
  title: string;
24
28
  open: boolean;
25
29
  glass: boolean;
@@ -1,10 +1,10 @@
1
1
  import { defineComponent, computed, openBlock, createBlock, unref, withCtx, createElementVNode, createElementBlock, Fragment, renderList, toDisplayString, createTextVNode, createCommentVNode, createVNode } from "vue";
2
2
  import LpKeys from "./LpKeys.vue.js";
3
- import { _ as _sfc_main$1 } from "../chunks/LpModal-BnjZ2iem.js";
3
+ import { _ as _sfc_main$1 } from "../chunks/LpModal-C0tGg_D_.js";
4
4
  import { mergeShortcutGroups, activeShortcuts, useHotkeys } from "../composables/useHotkeys.js";
5
5
  import { useLabels } from "../composables/useLpConfig.js";
6
6
  import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
7
- import '../assets/LpShortcutsDialog-BUp_BX4s.css';const _hoisted_1 = { class: "shortcut-groups" };
7
+ import '../assets/LpShortcutsDialog-C6uzPeMf.css';const _hoisted_1 = { class: "shortcut-groups" };
8
8
  const _hoisted_2 = ["aria-label", "title"];
9
9
  const _sfc_main = /* @__PURE__ */ defineComponent({
10
10
  __name: "LpShortcutsDialog",
@@ -15,6 +15,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
15
15
  title: { default: void 0 },
16
16
  description: {},
17
17
  glass: { type: Boolean, default: void 0 },
18
+ history: { type: [Boolean, String], default: void 0 },
18
19
  hotkey: {}
19
20
  },
20
21
  emits: ["update:open"],
@@ -42,6 +43,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
42
43
  return (_ctx, _cache) => {
43
44
  return openBlock(), createBlock(_sfc_main$1, {
44
45
  glass: props.glass,
46
+ history: props.history,
45
47
  open: props.open,
46
48
  size: "lg",
47
49
  title: props.title ?? unref(text).title,
@@ -85,11 +87,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
85
87
  ])
86
88
  ]),
87
89
  _: 1
88
- }, 8, ["glass", "open", "title", "description"]);
90
+ }, 8, ["glass", "history", "open", "title", "description"]);
89
91
  };
90
92
  }
91
93
  });
92
- const LpShortcutsDialog = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-611c415f"]]);
94
+ const LpShortcutsDialog = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-f948ed00"]]);
93
95
  export {
94
96
  LpShortcutsDialog as default
95
97
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpSidebar-BoNLKgh-.js";
1
+ import { _ as _sfc_main } from "../chunks/LpSidebar-D6zO8vLm.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -52,6 +52,14 @@ type __VLS_Props = {
52
52
  * icon keep their label.
53
53
  */
54
54
  compact?: boolean;
55
+ /**
56
+ * Record each switch as a browser history entry, so Back returns to the
57
+ * previous tab. Unset follows the overlay the tabs sit in (a modal or a
58
+ * drawer with history on, see `provideOverlayHistory`) and is off
59
+ * elsewhere: a page's own tabs belong in its URL. `false` opts out inside
60
+ * an overlay, `true` opts in on a page that keeps no URL of its own.
61
+ */
62
+ history?: boolean;
55
63
  };
56
64
  declare var __VLS_67: {
57
65
  value: string;
@@ -66,6 +74,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
66
74
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
67
75
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
68
76
  }>, {
77
+ history: boolean;
69
78
  block: boolean;
70
79
  variant: "contained" | "plain" | "underline" | "bottom";
71
80
  accent: boolean;
@@ -3,13 +3,14 @@ import { usePaletteScope, usePaletteCommands } from "../composables/usePaletteCo
3
3
  import { Motion } from "motion-v";
4
4
  import { TabsRoot, TabsList, TabsTrigger, TabsContent } from "reka-ui";
5
5
  import { usePillTransition } from "../composables/usePillTransition.js";
6
+ import { useStepHistory } from "../composables/useOverlayHistory.js";
6
7
  import { prefersReducedMotion } from "../composables/easing.js";
7
8
  import { _ as _sfc_main$1 } from "../chunks/LpContextMenu-rx2tCjwk.js";
8
9
  import { _ as _sfc_main$3 } from "../chunks/LpSkeleton-BB6XIV-w.js";
9
10
  import { _ as _sfc_main$2 } from "../chunks/LpTabFace-DPCwKkH2.js";
10
11
  import { pickTab, BOTTOM_BAR, BOTTOM_ITEM, BOTTOM_MARKER } from "./tabs.js";
11
12
  import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
12
- import '../assets/LpTabs-CDchzoRL.css';const _sfc_main = /* @__PURE__ */ defineComponent({
13
+ import '../assets/LpTabs-CJ0rYQbx.css';const _sfc_main = /* @__PURE__ */ defineComponent({
13
14
  __name: "LpTabs",
14
15
  props: {
15
16
  modelValue: {},
@@ -20,7 +21,8 @@ import '../assets/LpTabs-CDchzoRL.css';const _sfc_main = /* @__PURE__ */ defineC
20
21
  block: { type: Boolean, default: false },
21
22
  label: {},
22
23
  hoverFollow: { type: Boolean, default: true },
23
- compact: { type: Boolean, default: false }
24
+ compact: { type: Boolean, default: false },
25
+ history: { type: Boolean, default: void 0 }
24
26
  },
25
27
  emits: ["update:modelValue"],
26
28
  setup(__props, { emit: __emit }) {
@@ -54,10 +56,22 @@ import '../assets/LpTabs-CDchzoRL.css';const _sfc_main = /* @__PURE__ */ defineC
54
56
  row.scrollTo({ left, behavior: prefersReducedMotion() ? "auto" : "smooth" });
55
57
  });
56
58
  });
57
- function select(value) {
59
+ function show(value) {
58
60
  if (props.modelValue === void 0) current.value = value;
59
61
  emit("update:modelValue", value);
60
62
  }
63
+ const { record } = useStepHistory({
64
+ mode: () => props.history,
65
+ set: show,
66
+ has: (value) => props.items.some((item) => item.value === value && !item.disabled)
67
+ });
68
+ function select(value) {
69
+ const from = current.value;
70
+ show(value);
71
+ void nextTick(() => {
72
+ if (from !== void 0 && current.value === value) record(from, value);
73
+ });
74
+ }
61
75
  const pillId = `lp-tab-indicator-${useId()}`;
62
76
  const hovered = ref(null);
63
77
  const pillTransition = usePillTransition();
@@ -195,7 +209,7 @@ import '../assets/LpTabs-CDchzoRL.css';const _sfc_main = /* @__PURE__ */ defineC
195
209
  };
196
210
  }
197
211
  });
198
- const LpTabs = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-9f51a497"]]);
212
+ const LpTabs = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-1d673b52"]]);
199
213
  export {
200
214
  LpTabs as default
201
215
  };
@@ -0,0 +1,33 @@
1
+ export interface HistoryEntry {
2
+ /** Back left this entry: undo what it recorded (close the overlay, restore the tab). */
3
+ back: () => void;
4
+ /** Forward came back to it: redo it, and say whether that happened. */
5
+ forward?: () => boolean;
6
+ /** The page moved on to another path while this entry was live. */
7
+ navigated?: () => void;
8
+ /** The component that pushed it is still mounted. */
9
+ alive: boolean;
10
+ }
11
+ /**
12
+ * What the kit needs of a router: vue-router's `listening` switch, or the same
13
+ * flag on any router that skips its popstate handling while it is false.
14
+ */
15
+ export interface HistoryRouter {
16
+ listening: boolean;
17
+ }
18
+ /** The app's router, told not to listen while a step only concerns the kit. */
19
+ export declare function setHistoryRouter(router: HistoryRouter | null): void;
20
+ /** Record an entry on top of the history, under the current address. */
21
+ export declare function pushEntry(entry: HistoryEntry): void;
22
+ /**
23
+ * The thing behind an entry ended without Back (a close button, Escape, the
24
+ * app setting `open` to false, an unmount): take the entry and everything
25
+ * pushed above it out of history, so Back is never spent on a dead step.
26
+ * When the page is not sitting on the kit's entries (another push came on top),
27
+ * the entry stays in place and is skipped when Back or Forward lands on it.
28
+ */
29
+ export declare function dropEntry(entry: HistoryEntry): void;
30
+ /** Whether the entry is live: pushed and not yet popped or dropped. */
31
+ export declare function isLive(entry: HistoryEntry): boolean;
32
+ /** Live entries, bottom first. For tests and devtools. */
33
+ export declare function liveEntries(): number;