@wssto2/vue-core 0.9.4 → 0.11.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 (169) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/ActionRow.vue_vue_type_script_setup_true_lang-DybAIOHS.js +64 -0
  3. package/dist/Activity-CdkJW68R.js +169 -0
  4. package/dist/{AdaptivePageShell.vue_vue_type_script_setup_true_lang-DdJOodn8.js → AdaptivePageShell.vue_vue_type_script_setup_true_lang-D5i77H6S.js} +147 -159
  5. package/dist/{AsyncSection.vue_vue_type_script_setup_true_lang-BdeOKqz_.js → AsyncSection.vue_vue_type_script_setup_true_lang-DGzQ-Snp.js} +1 -1
  6. package/dist/BottomDockPortal.vue_vue_type_script_setup_true_lang-DmwR-2SP.js +17 -0
  7. package/dist/Button.vue_vue_type_script_setup_true_lang-C74hcb0N.js +123 -0
  8. package/dist/Changes-DxK_QfiB.js +69 -0
  9. package/dist/{CollectionPage.vue_vue_type_script_setup_true_lang-DHcekGNn.js → CollectionPage.vue_vue_type_script_setup_true_lang-x0jyJ01f.js} +19 -18
  10. package/dist/{DataTable.vue_vue_type_script_setup_true_lang-8kBsNRuq.js → DataTable.vue_vue_type_script_setup_true_lang-C_ZytnW-.js} +1 -1
  11. package/dist/{DateField.vue_vue_type_script_setup_true_lang-BhKdCVFn.js → DateField.vue_vue_type_script_setup_true_lang-CeNwBbJT.js} +1 -1
  12. package/dist/{DeadLetters-Bb5pPlOY.js → DeadLetters-DnA7LP1w.js} +5 -6
  13. package/dist/DrawnCheck.vue_vue_type_script_setup_true_lang-9DzDJlIA.js +38 -0
  14. package/dist/FormGroup.vue_vue_type_script_setup_true_lang-D6jYfNjW.js +427 -0
  15. package/dist/{FormView.vue_vue_type_script_setup_true_lang-aaNnMIOi.js → FormView.vue_vue_type_script_setup_true_lang-DvbPEiNQ.js} +2 -2
  16. package/dist/{General-BAG7S6wV.js → General-2matxl2Y.js} +23 -24
  17. package/dist/Highlight.vue_vue_type_script_setup_true_lang-DZGBmwcX.js +29 -0
  18. package/dist/{IncidentRecord-D8aOpNHo.js → IncidentRecord-Bt3G4HlX.js} +14 -15
  19. package/dist/{IncidentsList-C0cFl0Db.js → IncidentsList-2iwgqp9J.js} +3 -3
  20. package/dist/{Modal.vue_vue_type_script_setup_true_lang-BlHhdmMy.js → Modal.vue_vue_type_script_setup_true_lang-DDS0iEnn.js} +9 -8
  21. package/dist/{NewRolePage-C6Iu-EQJ.js → NewRolePage-DhGFwvk3.js} +4 -4
  22. package/dist/{NumberField.vue_vue_type_script_setup_true_lang-TGGAq8J-.js → NumberField.vue_vue_type_script_setup_true_lang-B3UrXNqH.js} +1 -1
  23. package/dist/{PersonAccess.vue_vue_type_script_setup_true_lang-qz1tmYpF.js → PersonAccess.vue_vue_type_script_setup_true_lang-DbGZSNQc.js} +56 -56
  24. package/dist/ProfilePage-CpuyDBAv.js +685 -0
  25. package/dist/ProgressTrack.vue_vue_type_script_setup_true_lang-DrOm8k--.js +30 -0
  26. package/dist/{RecordHeader.vue_vue_type_script_setup_true_lang-B9mN7UxZ.js → RecordHeader.vue_vue_type_script_setup_true_lang-DqGc3Jyo.js} +1 -1
  27. package/dist/{ResourcePage.vue_vue_type_script_setup_true_lang-Cw8mGf_B.js → ResourcePage.vue_vue_type_script_setup_true_lang-DljhsTU4.js} +3 -3
  28. package/dist/{RoleEditor.vue_vue_type_script_setup_true_lang-CJPvOVDV.js → RoleEditor.vue_vue_type_script_setup_true_lang-C0FIotFA.js} +11 -12
  29. package/dist/{RolePage-Dj_ocMnM.js → RolePage-DaNv049g.js} +10 -10
  30. package/dist/{RolesPage-PXrs4O-1.js → RolesPage-6hlzJes_.js} +6 -6
  31. package/dist/SearchInput.vue_vue_type_script_setup_true_lang-w4eAqyZ9.js +32 -0
  32. package/dist/{SectionList.vue_vue_type_script_setup_true_lang-CtdF1zuv.js → SectionList.vue_vue_type_script_setup_true_lang-DR0_NsqC.js} +1 -1
  33. package/dist/{SectionNavigator.vue_vue_type_script_setup_true_lang-B5ove3rM.js → SectionNavigator.vue_vue_type_script_setup_true_lang-Dh3sXUt_.js} +2 -2
  34. package/dist/{SegmentedField.vue_vue_type_script_setup_true_lang-ChfDFAA2.js → SegmentedField.vue_vue_type_script_setup_true_lang-zv_AxpBn.js} +1 -1
  35. package/dist/{SelectField.vue_vue_type_script_setup_true_lang-BP8nAQwo.js → SelectField.vue_vue_type_script_setup_true_lang-BZwdANL3.js} +3 -3
  36. package/dist/{ServiceAccountDialog.vue_vue_type_script_setup_true_lang-aWkKwxNe.js → ServiceAccountDialog.vue_vue_type_script_setup_true_lang-FJbWxME_.js} +20 -20
  37. package/dist/{ServiceAccountRecord-bxX9yQkC.js → ServiceAccountRecord-0-JF5lRx.js} +24 -24
  38. package/dist/{ServiceAccountsList-BXE6htxk.js → ServiceAccountsList-DcsnOcf-.js} +32 -32
  39. package/dist/{Sessions--Gvc0kxw.js → Sessions-DgWhpk-i.js} +23 -24
  40. package/dist/{SettingsPage-DzDC7gD-.js → SettingsPage-BXPWATYU.js} +28 -30
  41. package/dist/{SignIn-CTI_699e.js → SignIn-DPoUwHZd.js} +9 -10
  42. package/dist/{SignInPage-HKC1bm2E.js → SignInPage-OyrVemro.js} +4 -4
  43. package/dist/SignIns-BMDrZVVp.js +64 -0
  44. package/dist/StatusLine.vue_vue_type_script_setup_true_lang-DsdQmLq6.js +56 -0
  45. package/dist/{SwitchField.vue_vue_type_script_setup_true_lang-BMA2YXTZ.js → SwitchField.vue_vue_type_script_setup_true_lang-Ds4q0bbl.js} +1 -1
  46. package/dist/{TextField.vue_vue_type_script_setup_true_lang-BFVsqcBX.js → TextField.vue_vue_type_script_setup_true_lang-CWIeeZRE.js} +2 -2
  47. package/dist/{TextareaField.vue_vue_type_script_setup_true_lang-Dyh4Hhyl.js → TextareaField.vue_vue_type_script_setup_true_lang-D2j4s96s.js} +1 -1
  48. package/dist/{TimeField.vue_vue_type_script_setup_true_lang-gGFfQLuK.js → TimeField.vue_vue_type_script_setup_true_lang-D-Em4glH.js} +1 -1
  49. package/dist/UserRecord-BW-gdCqi.js +202 -0
  50. package/dist/UsersList-B3ZF_1Dj.js +74 -0
  51. package/dist/access/index.js +4 -4
  52. package/dist/app/index.d.ts +1 -0
  53. package/dist/app/index.js +11 -10
  54. package/dist/app/languages.d.ts +8 -0
  55. package/dist/{application-BmXW6iW_.js → application-CuzT-0vW.js} +1 -1
  56. package/dist/button/index.js +6 -5
  57. package/dist/collection/Highlight.vue.d.ts +12 -0
  58. package/dist/collection/PickerModal.vue.d.ts +26 -0
  59. package/dist/collection/definition.d.ts +6 -6
  60. package/dist/collection/http.d.ts +6 -6
  61. package/dist/collection/index.d.ts +6 -1
  62. package/dist/collection/index.js +145 -85
  63. package/dist/collection/picker.d.ts +39 -0
  64. package/dist/collection/types.d.ts +9 -4
  65. package/dist/collection/useCollection.d.ts +5 -5
  66. package/dist/content/ActivityStream.vue.d.ts +62 -0
  67. package/dist/content/CommentThread.vue.d.ts +52 -0
  68. package/dist/content/Composer.vue.d.ts +36 -0
  69. package/dist/content/index.d.ts +3 -0
  70. package/dist/content/index.js +321 -56
  71. package/dist/controls/index.js +28 -54
  72. package/dist/form/index.js +132 -134
  73. package/dist/format/index.d.ts +1 -0
  74. package/dist/format/index.js +10 -6
  75. package/dist/format/slug.d.ts +8 -0
  76. package/dist/http-Dm0uXY9O.js +95 -0
  77. package/dist/i18n/index.d.ts +15 -0
  78. package/dist/i18n/index.js +89 -63
  79. package/dist/i18n/plural.d.ts +23 -0
  80. package/dist/i18n/serverMessages.d.ts +16 -0
  81. package/dist/identity/embed.d.ts +39 -0
  82. package/dist/identity/index.d.ts +22 -1
  83. package/dist/identity/index.js +40 -21
  84. package/dist/identity/session.d.ts +9 -1
  85. package/dist/identity/shared/ChangeFeed.vue.d.ts +6 -3
  86. package/dist/identity/users/context.d.ts +11 -0
  87. package/dist/identity/users/dialogs.d.ts +10 -0
  88. package/dist/identity/users/ext.d.ts +8 -0
  89. package/dist/identity/users/feature.d.ts +22 -1
  90. package/dist/identity/users/list.d.ts +12 -1
  91. package/dist/identity/users/personSections.d.ts +15 -6
  92. package/dist/identity/users/replace.d.ts +45 -0
  93. package/dist/identity/users/words.d.ts +28 -0
  94. package/dist/incidents/api.d.ts +1 -1
  95. package/dist/incidents/index.js +1 -1
  96. package/dist/{index-Dz0LkLM8.js → index-ClJeyW3F.js} +4 -4
  97. package/dist/index-Cq9TuOqH.js +1264 -0
  98. package/dist/{index-Dm-sdJJr.js → index-kEHY7hcG.js} +31 -32
  99. package/dist/index.d.ts +1 -1
  100. package/dist/index.js +1 -1
  101. package/dist/keepAlive-ClGnsIzT.js +55 -0
  102. package/dist/modal/index.js +3 -3
  103. package/dist/modules/identity/entities.d.ts +7 -0
  104. package/dist/modules/identity/routes.d.ts +8 -4
  105. package/dist/modules/identity/schemas.d.ts +16 -0
  106. package/dist/{neighbors-DVfU9T0o.js → neighbors-OyvGi233.js} +2 -2
  107. package/dist/notifications/deadletters/list.d.ts +1 -1
  108. package/dist/notifications/index.js +1 -1
  109. package/dist/{options-QuOkMKVZ.js → options-B48UiYsG.js} +2 -2
  110. package/dist/page/index.js +11 -11
  111. package/dist/phone/index.js +1 -1
  112. package/dist/pwa/AndroidInstallBanner.vue.d.ts +18 -0
  113. package/dist/pwa/InstallQrSheet.vue.d.ts +21 -0
  114. package/dist/pwa/IosInstallGuide.vue.d.ts +19 -0
  115. package/dist/pwa/QrCode.vue.d.ts +16 -0
  116. package/dist/pwa/index.d.ts +4 -0
  117. package/dist/pwa/index.js +244 -24
  118. package/dist/resource/index.js +1 -1
  119. package/dist/rowLayout-BsKEzEME.js +650 -0
  120. package/dist/shell/DarkModeRow.vue.d.ts +15 -0
  121. package/dist/shell/OfflineNotice.vue.d.ts +2 -0
  122. package/dist/shell/QuickSearchButton.vue.d.ts +2 -0
  123. package/dist/shell/QuickSearchPalette.vue.d.ts +12 -0
  124. package/dist/shell/darkMode.d.ts +26 -0
  125. package/dist/shell/index.d.ts +10 -0
  126. package/dist/shell/index.js +954 -579
  127. package/dist/shell/offlineNotice.d.ts +3 -0
  128. package/dist/shell/quickSearch.d.ts +14 -0
  129. package/dist/shell/quickSearchControl.d.ts +69 -0
  130. package/dist/shortcut-DUR6yPWA.js +93 -0
  131. package/dist/state/BackgroundTaskCapsule.vue.d.ts +18 -0
  132. package/dist/state/backgroundTasks.d.ts +58 -0
  133. package/dist/state/index.d.ts +4 -0
  134. package/dist/state/index.js +194 -80
  135. package/dist/state/useCountUp.d.ts +9 -0
  136. package/dist/styles.css +1 -1
  137. package/dist/{suggestions-BVQhoXzS.js → suggestions-q_v3M7G6.js} +1 -1
  138. package/dist/testing/collection.d.ts +1 -1
  139. package/dist/{useCommand-Dc8Lgs9h.js → useCommand-Ch_nx1AQ.js} +4 -4
  140. package/dist/{useGroupSheet-DQ6cVG-z.js → useGroupSheet-CPjHydQV.js} +14 -15
  141. package/dist/{useRecordDialog-BTsbHY38.js → useRecordDialog-DdkauXnU.js} +1 -1
  142. package/dist/{useResourceForm-DJ4Q75pq.js → useResourceForm-Dl6YFYyz.js} +1 -1
  143. package/package.json +3 -2
  144. package/dist/ActionRow.vue_vue_type_script_setup_true_lang-Cg1lhpcA.js +0 -45
  145. package/dist/Activity-29hsZ28j.js +0 -156
  146. package/dist/Button.vue_vue_type_script_setup_true_lang-Dp7OB-eF.js +0 -192
  147. package/dist/Changes-Dxjdkd_5.js +0 -129
  148. package/dist/FormErrors.vue_vue_type_script_setup_true_lang-CIggKPtq.js +0 -295
  149. package/dist/FormGroup.vue_vue_type_script_setup_true_lang-B37dixvJ.js +0 -137
  150. package/dist/PageBar.vue_vue_type_script_setup_true_lang-qI94fjJv.js +0 -47
  151. package/dist/ProfilePage-BnohZIEg.js +0 -764
  152. package/dist/ProgressTrack.vue_vue_type_script_setup_true_lang-B6Prw9hi.js +0 -64
  153. package/dist/SectionIntro.vue_vue_type_script_setup_true_lang-CZtPrmXD.js +0 -23
  154. package/dist/SessionList.vue_vue_type_script_setup_true_lang-Cnyt5V8T.js +0 -133
  155. package/dist/SignIns-BcqBgWgN.js +0 -121
  156. package/dist/UserRecord-CtUPpbCt.js +0 -267
  157. package/dist/UsersList-BMkIgIg8.js +0 -155
  158. package/dist/definition-C7QuyV3i.js +0 -54
  159. package/dist/http-nz92KjdS.js +0 -42
  160. package/dist/identity/fieldMessages.d.ts +0 -15
  161. package/dist/identity/users/languages.d.ts +0 -4
  162. package/dist/index-CdtofdcJ.js +0 -501
  163. package/dist/keepAlive-z3IRFkQq.js +0 -46
  164. package/dist/languages-BShG9LpE.js +0 -10
  165. package/dist/rowLayout-CT8vWYgI.js +0 -663
  166. package/dist/signInEvents-DWShrJox.js +0 -30
  167. package/dist/status-BRA14qwX.js +0 -34
  168. package/dist/useWaitStatus-fsacMl4W.js +0 -24
  169. /package/dist/{fieldMessages-B6GHgi40.js → serverMessages-B6GHgi40.js} +0 -0
@@ -0,0 +1,3 @@
1
+ import { type Feature } from "../app/feature";
2
+ /** The offline notice as an opt-in feature: mounts in the shell's `host` slot (on every page, signed in or not). */
3
+ export declare function offlineNotice(): Feature;
@@ -0,0 +1,14 @@
1
+ import { type Feature } from "../app/feature";
2
+ import type { QuickSearchOptions } from "./quickSearchControl";
3
+ /**
4
+ * The quick search (Ctrl or ⌘ + K): one field that finds records from anywhere, as an opt-in feature.
5
+ * It puts a search field in the shell's `search` slot (the sidebar, the phone drawer) and mounts the
6
+ * palette once in the `host` slot; the shortcut is listed in the shortcut help.
7
+ *
8
+ * createApplication({ platform, features: [identity, quickSearch({ search: (query, signal) => api.search(query, signal) })], shell: backofficeShell() })
9
+ *
10
+ * The app maps its server's answer to groups of rows and decides where a row goes (`to`). Typing is
11
+ * debounced; an answer to an older query is dropped and the previous results stay until the new ones
12
+ * arrive, so the list does not flicker. ↑ ↓ move, ↵ opens, Ctrl or ⌘ + ↵ opens in a new tab, Esc closes.
13
+ */
14
+ export declare function quickSearch(options: QuickSearchOptions): Feature;
@@ -0,0 +1,69 @@
1
+ import { type MaybeRefOrGetter, type Ref } from "vue";
2
+ import type { RouteLocationRaw } from "vue-router";
3
+ import type { IconName } from "../icon";
4
+ import type { Tone } from "../state";
5
+ /** One result the palette can open. */
6
+ export interface QuickSearchRow {
7
+ /** Unique among the rows shown; a test hook too (`data-test="quick-search-row-<key>"`). */
8
+ readonly key: string;
9
+ readonly title: string;
10
+ /** A second line (an identifier, a status text). */
11
+ readonly meta?: string;
12
+ /** Draws `meta` in a monospace face: a VIN, a plate. */
13
+ readonly mono?: boolean;
14
+ readonly icon: IconName;
15
+ /** The icon tile: `neutral` (default), `accent` (the kind of record people look for most) or `action` (something to do, not a record). */
16
+ readonly tile?: "neutral" | "accent" | "action";
17
+ readonly badge?: {
18
+ readonly label: string;
19
+ readonly tone: Tone;
20
+ };
21
+ readonly to: RouteLocationRaw;
22
+ }
23
+ /** Rows under one heading. */
24
+ export interface QuickSearchGroup {
25
+ readonly key: string;
26
+ /** The heading, in the app's language. */
27
+ readonly label: string;
28
+ readonly rows: readonly QuickSearchRow[];
29
+ }
30
+ /** What the server answered for one query. */
31
+ export interface QuickSearchAnswer {
32
+ readonly groups: readonly QuickSearchGroup[];
33
+ /** Names of the groups that could not be searched this time (already translated): the palette says some results are missing. */
34
+ readonly unavailable?: readonly string[];
35
+ }
36
+ export interface QuickSearchOptions {
37
+ /** Looks `query` up (trimmed, at least `minLength` characters). Latest query wins: `signal` is aborted when a newer one starts or the palette closes. */
38
+ search: (query: string, signal: AbortSignal) => Promise<QuickSearchAnswer>;
39
+ /** Groups the app derives from the query alone, shown under the results at once (a VIN's "enter this vehicle"). They never count as results: with only these, "nothing found" still shows. */
40
+ suggest?: (query: string) => readonly QuickSearchGroup[];
41
+ /** The rows to show before anything is typed (recently opened ones). Read each time the palette opens. */
42
+ recent?: () => readonly QuickSearchRow[];
43
+ /** A row was chosen: remember it as recent. */
44
+ onChoose?: (row: QuickSearchRow) => void;
45
+ /**
46
+ * The field's placeholder ("Search a vehicle, VIN, customer…"). The three texts are read each time the palette renders, so an
47
+ * application whose messages load lazily passes a getter that follows the language: `placeholder: () => t("search.placeholder")`.
48
+ */
49
+ placeholder?: MaybeRefOrGetter<string>;
50
+ /** Shown before anything is typed and no recent rows exist: what can be searched. */
51
+ hint?: MaybeRefOrGetter<string>;
52
+ /** Shown under "Nothing for …": what the search covers. */
53
+ noneHint?: MaybeRefOrGetter<string>;
54
+ /** Characters before a search runs. Default 2. */
55
+ minLength?: number;
56
+ /** Milliseconds of quiet after typing before a search runs. Default 180. */
57
+ delayMs?: number;
58
+ }
59
+ export interface QuickSearchControl {
60
+ readonly isOpen: Readonly<Ref<boolean>>;
61
+ open(): void;
62
+ close(): void;
63
+ }
64
+ /**
65
+ * Opens and closes the quick-search palette: for a trigger of your own (a phone top-bar button). The
66
+ * state belongs to the document, like the dialog stack, so the button, the shortcut and the palette
67
+ * share it wherever they are mounted.
68
+ */
69
+ export declare function useQuickSearch(): QuickSearchControl;
@@ -0,0 +1,93 @@
1
+ import { ref as c, watch as p, toValue as u, onScopeDispose as d, getCurrentInstance as w, watchEffect as K, computed as g, triggerRef as f, shallowRef as E } from "vue";
2
+ import { p as v } from "./perDocument-DUuFrdGU.js";
3
+ import { t as k } from "./dialogStack-B5G6xHq0.js";
4
+ const h = 300, C = 600;
5
+ function N(e, t = {}) {
6
+ const s = c(!1), o = c(0), a = c(!1);
7
+ let r = [], n = null;
8
+ function i() {
9
+ r.forEach((l) => clearTimeout(l)), r = [], n && clearInterval(n), n = null;
10
+ }
11
+ function y() {
12
+ i(), s.value = !1, o.value = 0, a.value = !1, r.push(setTimeout(() => s.value = !0, h)), t.slowAfterMs && r.push(setTimeout(() => a.value = !0, t.slowAfterMs)), n = setInterval(() => o.value += 1, 1e3);
13
+ }
14
+ return p(
15
+ () => u(e),
16
+ (l) => {
17
+ l ? y() : (i(), s.value = !1, a.value = !1);
18
+ },
19
+ { immediate: !0 }
20
+ ), d(i), { visible: s, elapsed: o, slow: a };
21
+ }
22
+ const S = /^[^\p{L}\p{N}\s]$/u;
23
+ function b(e, t) {
24
+ const s = t.shiftKey === void 0 && S.test(t.key) || e.shiftKey === (t.shiftKey ?? !1), o = t.mod ? e.ctrlKey !== e.metaKey : e.ctrlKey === (t.ctrlKey ?? !1) && e.metaKey === (t.metaKey ?? !1);
25
+ return e.key.toLowerCase() === t.key.toLowerCase() && s && o && e.altKey === (t.altKey ?? !1);
26
+ }
27
+ function A(e) {
28
+ return e instanceof HTMLElement && (e.isContentEditable || /^(input|textarea|select)$/i.test(e.tagName));
29
+ }
30
+ function B(e, t) {
31
+ const s = w();
32
+ K((o) => {
33
+ const a = u(e);
34
+ if (!a) return;
35
+ if (a.label !== void 0) {
36
+ const n = L(a);
37
+ o(n.remove);
38
+ }
39
+ const r = (n) => {
40
+ n.repeat || !b(n, a) || !(a.mod || a.ctrlKey || a.altKey || a.metaKey) && A(n.target) || M(s) && t(n) !== !1 && n.preventDefault();
41
+ };
42
+ window.addEventListener("keydown", r), o(() => window.removeEventListener("keydown", r));
43
+ });
44
+ }
45
+ function M(e) {
46
+ const t = k();
47
+ if (t === void 0) return !0;
48
+ const s = e?.subTree.el;
49
+ return !!t && !!s && t.contains(s);
50
+ }
51
+ const T = Symbol("vue-core:shortcuts"), m = () => v(T, () => ({ items: [], changed: E(0) }));
52
+ function L(e) {
53
+ const t = m();
54
+ return t.items = [...t.items, e], f(t.changed), {
55
+ remove() {
56
+ t.items = t.items.filter((s) => s !== e), f(t.changed);
57
+ }
58
+ };
59
+ }
60
+ function D(e) {
61
+ const t = typeof navigator < "u" && /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent), s = t ? { ctrl: "⌃", alt: "⌥", shift: "⇧", meta: "⌘" } : { ctrl: "Ctrl", alt: "Alt", shift: "Shift", meta: "Meta" }, o = e.mod ? !t : e.ctrlKey, a = e.mod ? t : e.metaKey, r = R[e.key] ?? (e.key.length === 1 ? e.key.toUpperCase() : e.key);
62
+ return [
63
+ ...o ? [s.ctrl] : [],
64
+ ...e.altKey ? [s.alt] : [],
65
+ ...e.shiftKey ? [s.shift] : [],
66
+ ...a ? [s.meta] : [],
67
+ r
68
+ ];
69
+ }
70
+ const R = { ArrowLeft: "←", ArrowRight: "→", ArrowUp: "↑", ArrowDown: "↓", Escape: "Esc", " ": "Space" };
71
+ function P() {
72
+ const e = m();
73
+ return g(() => {
74
+ e.changed.value;
75
+ const t = /* @__PURE__ */ new Map();
76
+ for (const s of e.items) {
77
+ const o = typeof s.label == "function" ? s.label() : s.label ?? "";
78
+ if (o === "") continue;
79
+ const a = s.group ?? null, r = t.get(`${a}\0${o}`) ?? { group: a, label: o, keys: [] }, n = D(s);
80
+ r.keys.some((i) => i.join("+") === n.join("+")) || r.keys.push(n), t.set(`${a}\0${o}`, r);
81
+ }
82
+ return [...t.values()];
83
+ });
84
+ }
85
+ export {
86
+ C as D,
87
+ h as W,
88
+ P as a,
89
+ N as b,
90
+ b as m,
91
+ D as s,
92
+ B as u
93
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The capsule that follows work the user left (UI decision D23, MOTION_PLAN M2): pinned above the
3
+ * bottom edge (in the bottom dock), it shows a running task (spinner, what it is, what it is
4
+ * doing, the seconds) and, when it ends, turns into "… is ready · Open" (or the failure, with the
5
+ * same way back). It goes away on Open, on the close button, or by itself `hideAfterMs` after the
6
+ * end. A page that shows the task in place hides it (`useBackgroundTasks().watchTask`). Render it
7
+ * once in the shell, where `installBackgroundTasks(app)` has been called.
8
+ *
9
+ * <BackgroundTaskCapsule />
10
+ */
11
+ type __VLS_Props = {
12
+ /** How long an ended task stays, so it can be read and acted on. */
13
+ hideAfterMs?: number;
14
+ };
15
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
16
+ hideAfterMs: number;
17
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
+ export default _default;
@@ -0,0 +1,58 @@
1
+ import { type App, type ComputedRef, type Ref } from "vue";
2
+ import type { RouteLocationRaw } from "vue-router";
3
+ /**
4
+ * Work that outlives the page it started on (UI decision D23, MOTION_PLAN M2): a slow request the
5
+ * user may walk away from. The request is owned here, not by the page, so leaving the page neither
6
+ * loses its answer nor hides that it is running: the page that shows the task in place `watchTask`s
7
+ * it, and while nobody does, the `BackgroundTaskCapsule` follows it and, when it ends, says so with
8
+ * a way back (`route`).
9
+ *
10
+ * installBackgroundTasks(app); // once, in the composition root
11
+ * const tasks = useBackgroundTasks(); // in setup
12
+ * const quote = await tasks.run(
13
+ * { id: `quote:${id}`, title: "Quote · Golf", detail: "Calculating…", doneTitle: "The quote is ready", failedTitle: "The quote failed", route: { name: "quote", params: { id } } },
14
+ * () => api.quote(id),
15
+ * (result) => `${result.total} €`,
16
+ * );
17
+ */
18
+ export type BackgroundTaskStatus = "running" | "done" | "failed";
19
+ export type BackgroundTask = {
20
+ /** Stable per subject, e.g. "quote:42": one task per subject at a time (a second `run` replaces the first). */
21
+ id: string;
22
+ /** What it is, e.g. "Quote · VW Golf". */
23
+ title: string;
24
+ /** What it is doing, while running. */
25
+ detail: string;
26
+ /** Where the result is. */
27
+ route: RouteLocationRaw;
28
+ /** The capsule's words when it ends. */
29
+ doneTitle: string;
30
+ failedTitle: string;
31
+ /** `Date.now()` when it started. */
32
+ startedAt: number;
33
+ status: BackgroundTaskStatus;
34
+ /** A line about the outcome ("12,400 €", or the error's message). */
35
+ outcome?: string;
36
+ };
37
+ export type BackgroundTaskSpec = Omit<BackgroundTask, "startedAt" | "status" | "outcome">;
38
+ export type BackgroundTasks = {
39
+ readonly tasks: Readonly<Ref<readonly BackgroundTask[]>>;
40
+ find(id: string): BackgroundTask | undefined;
41
+ isRunning(id: string): boolean;
42
+ /**
43
+ * Runs `work` as the task `spec.id` and resolves with its result (rejects with its error).
44
+ * `outcome` turns the result into the capsule's line.
45
+ */
46
+ run<T>(spec: BackgroundTaskSpec, work: () => Promise<T>, outcome?: (result: T) => string | undefined): Promise<T>;
47
+ /** A page shows this task in place: no capsule for it while the page is there. Returns the stop function. */
48
+ watchTask(id: string): () => void;
49
+ /** Takes the task off: a finished one stays until someone has (the page that shows it, or the capsule). */
50
+ dismiss(id: string): void;
51
+ /** The task the capsule follows: the latest one no page shows. */
52
+ readonly unwatched: ComputedRef<BackgroundTask | null>;
53
+ };
54
+ /** A set of background tasks of one application. Use `installBackgroundTasks` in an application; this is for tests and for a subtree. */
55
+ export declare function createBackgroundTasks(): BackgroundTasks;
56
+ export declare const backgroundTasksKey: import("vue").InjectionKey<BackgroundTasks>, useBackgroundTasks: () => BackgroundTasks;
57
+ /** Installs the background tasks of an application (a new set unless one is given) and returns them, for code outside components. */
58
+ export declare function installBackgroundTasks(app: App, tasks?: BackgroundTasks): BackgroundTasks;
@@ -12,3 +12,7 @@ export { default as StatusLine } from "./StatusLine.vue";
12
12
  export type { Tone } from "./tone";
13
13
  export { useLoad, type Load, type LoadContext, type LoadOptions } from "./useLoad";
14
14
  export { DONE_BEAT_MS, WAIT_VISIBLE_AFTER_MS, useWaitStatus } from "./useWaitStatus";
15
+ export { default as BackgroundTaskCapsule } from "./BackgroundTaskCapsule.vue";
16
+ export { backgroundTasksKey, createBackgroundTasks, installBackgroundTasks, useBackgroundTasks } from "./backgroundTasks";
17
+ export type { BackgroundTask, BackgroundTasks, BackgroundTaskSpec, BackgroundTaskStatus } from "./backgroundTasks";
18
+ export { useCountUp } from "./useCountUp";
@@ -1,104 +1,218 @@
1
- import { _ as D, a as I, b as W, i as F, v as O } from "../AsyncSection.vue_vue_type_script_setup_true_lang-BdeOKqz_.js";
2
- import { _ as H } from "../Badge.vue_vue_type_script_setup_true_lang-DJyZc0C7.js";
3
- import { _ as R } from "../Banner.vue_vue_type_script_setup_true_lang-sv0phngk.js";
4
- import { _ } from "../ProgressTrack.vue_vue_type_script_setup_true_lang-B6Prw9hi.js";
5
- import { a as j } from "../ProgressTrack.vue_vue_type_script_setup_true_lang-B6Prw9hi.js";
6
- import { _ as G } from "../EmptyState.vue_vue_type_script_setup_true_lang-DbZiO2RF.js";
7
- import { defineComponent as m, computed as k, openBlock as e, createElementBlock as a, normalizeClass as p, createVNode as d, unref as i, createElementVNode as u, renderSlot as f, createBlock as S, toDisplayString as l, createCommentVNode as c } from "vue";
8
- import { _ as x } from "../Icon.vue_vue_type_script_lang-BhhsFYwp.js";
9
- import { useI18n as g } from "vue-i18n";
10
- import { u as K } from "../useLoad-M7V-AVwR.js";
11
- import { D as X, W as Y, u as Z } from "../useWaitStatus-fsacMl4W.js";
12
- const $ = ["amber", "lime", "teal", "cyan", "blue", "indigo", "violet", "fuchsia", "pink"], V = /* @__PURE__ */ m({
1
+ import { _ as de, a as me, b as fe, i as pe, v as ve } from "../AsyncSection.vue_vue_type_script_setup_true_lang-DGzQ-Snp.js";
2
+ import { _ as xe } from "../Badge.vue_vue_type_script_setup_true_lang-DJyZc0C7.js";
3
+ import { _ as _e } from "../Banner.vue_vue_type_script_setup_true_lang-sv0phngk.js";
4
+ import { _ as A } from "../DrawnCheck.vue_vue_type_script_setup_true_lang-9DzDJlIA.js";
5
+ import { _ as ye } from "../EmptyState.vue_vue_type_script_setup_true_lang-DbZiO2RF.js";
6
+ import { defineComponent as S, computed as w, openBlock as d, createElementBlock as f, normalizeClass as M, createVNode as h, unref as t, createElementVNode as p, renderSlot as C, ref as g, watch as B, onBeforeUnmount as E, createBlock as y, withCtx as b, Transition as F, toDisplayString as x, Fragment as z, createCommentVNode as D, toValue as T, onScopeDispose as $ } from "vue";
7
+ import { _ } from "../Icon.vue_vue_type_script_lang-BhhsFYwp.js";
8
+ import { _ as be } from "../ProgressTrack.vue_vue_type_script_setup_true_lang-DrOm8k--.js";
9
+ import { _ as Se } from "../StatusLine.vue_vue_type_script_setup_true_lang-DsdQmLq6.js";
10
+ import { u as Ae } from "../useLoad-M7V-AVwR.js";
11
+ import { D as Ce, W as Ee, b as Fe } from "../shortcut-DUR6yPWA.js";
12
+ import { useI18n as L } from "vue-i18n";
13
+ import { useRouter as N } from "vue-router";
14
+ import { _ as I } from "../BottomDockPortal.vue_vue_type_script_setup_true_lang-DmwR-2SP.js";
15
+ import { d as V } from "../context-Ch1r4fdN.js";
16
+ const se = ["amber", "lime", "teal", "cyan", "blue", "indigo", "violet", "fuchsia", "pink"], oe = /* @__PURE__ */ S({
13
17
  __name: "FieldNote",
14
18
  props: {
15
19
  tone: { default: "info" },
16
20
  icon: { default: void 0 }
17
21
  },
18
- setup(s) {
19
- const t = s, o = {
22
+ setup(a) {
23
+ const s = a, r = {
20
24
  info: { text: "text-content-muted", icon: "informationLine" },
21
25
  warning: { text: "text-status-warning-content", icon: "alertTriangle" },
22
26
  critical: { text: "text-status-danger-content", icon: "errorWarningLine" }
23
- }, n = k(() => o[t.tone]);
24
- return (r, B) => (e(), a("p", {
25
- class: p(["mt-1 flex items-start gap-1.5 text-xs", n.value.text])
27
+ }, i = w(() => r[s.tone]);
28
+ return (u, e) => (d(), f("p", {
29
+ class: M(["mt-1 flex items-start gap-1.5 text-xs", i.value.text])
26
30
  }, [
27
- d(i(x), {
28
- name: t.icon ?? n.value.icon,
31
+ h(t(_), {
32
+ name: s.icon ?? i.value.icon,
29
33
  size: 14,
30
34
  class: "mt-px"
31
35
  }, null, 8, ["name"]),
32
- u("span", null, [
33
- f(r.$slots, "default")
36
+ p("span", null, [
37
+ C(u.$slots, "default")
34
38
  ])
35
39
  ], 2));
36
40
  }
37
- }), h = ["data-done"], y = {
38
- class: "flex min-w-0 items-center gap-2.5 text-subheadline",
41
+ });
42
+ function W() {
43
+ const a = g([]), s = g({}), r = (n) => a.value.find((c) => c.id === n);
44
+ function i(n, c) {
45
+ a.value = a.value.map((l) => l.id === n ? { ...l, ...c } : l);
46
+ }
47
+ async function u(n, c, l) {
48
+ a.value = [...a.value.filter((o) => o.id !== n.id), { ...n, startedAt: Date.now(), status: "running" }];
49
+ try {
50
+ const o = await c();
51
+ return i(n.id, { status: "done", outcome: l?.(o) }), o;
52
+ } catch (o) {
53
+ throw i(n.id, { status: "failed", outcome: o instanceof Error ? o.message : void 0 }), o;
54
+ }
55
+ }
56
+ function e(n) {
57
+ return s.value = { ...s.value, [n]: (s.value[n] ?? 0) + 1 }, () => {
58
+ s.value = { ...s.value, [n]: Math.max(0, (s.value[n] ?? 1) - 1) };
59
+ };
60
+ }
61
+ return {
62
+ tasks: a,
63
+ find: r,
64
+ isRunning: (n) => r(n)?.status === "running",
65
+ run: u,
66
+ watchTask: e,
67
+ dismiss: (n) => {
68
+ a.value = a.value.filter((c) => c.id !== n);
69
+ },
70
+ unwatched: w(() => [...a.value].reverse().find((n) => !(s.value[n.id] ?? 0)) ?? null)
71
+ };
72
+ }
73
+ const [R, j] = V("vue-core.backgroundTasks");
74
+ function re(a, s = W()) {
75
+ return a.provide(R, s), s;
76
+ }
77
+ const q = ["data-status"], O = {
78
+ class: "flex min-h-13 w-full max-w-md items-center gap-3 rounded-full bg-surface-inverse py-1.5 pr-1.5 pl-4 text-content-inverse shadow-float",
39
79
  role: "status",
40
80
  "aria-live": "polite"
41
- }, v = {
42
- key: 0,
43
- class: "text-content-link"
44
- }, E = {
45
- key: 2,
46
- class: "ml-auto shrink-0 pl-2 text-footnote tabular-nums text-content-muted",
47
- "data-status-elapsed": ""
48
- }, w = {
49
- key: 0,
50
- class: "pl-6.5 text-footnote"
51
- }, z = /* @__PURE__ */ m({
52
- __name: "StatusLine",
81
+ }, U = {
82
+ key: 1,
83
+ class: "flex size-6 shrink-0 animate-pop items-center justify-center rounded-full bg-control-on text-white"
84
+ }, H = { class: "flex min-w-0 flex-1 flex-col" }, K = {
85
+ class: "truncate text-subheadline font-semibold",
86
+ "data-test": "background-task-title"
87
+ }, P = {
88
+ key: 3,
89
+ class: "shrink-0 pr-3 text-footnote tabular-nums opacity-80"
90
+ }, G = ["aria-label"], ie = /* @__PURE__ */ S({
91
+ __name: "BackgroundTaskCapsule",
53
92
  props: {
54
- text: {},
55
- done: { type: Boolean },
56
- elapsed: {}
93
+ hideAfterMs: { default: 12e3 }
57
94
  },
58
- setup(s) {
59
- const t = s, { t: o } = g();
60
- return (n, r) => (e(), a("div", {
61
- class: "flex min-w-0 flex-col gap-1",
62
- "data-done": t.done || void 0
63
- }, [
64
- u("div", y, [
65
- t.done ? (e(), a("span", v, [
66
- d(_, { size: 16 })
67
- ])) : (e(), S(i(x), {
68
- key: 1,
69
- name: "loader4Line",
70
- size: 16,
71
- class: "animate-spin text-content-link"
72
- })),
73
- (e(), a("span", {
74
- key: t.text,
75
- class: p(["min-w-0 animate-text-swap", t.done ? "font-semibold text-content-link" : "text-shimmer"]),
76
- "data-status-text": ""
77
- }, l(t.text), 3)),
78
- t.elapsed != null ? (e(), a("span", E, l(i(o)("core.wait.seconds", { count: t.elapsed })), 1)) : c("", !0)
95
+ setup(a) {
96
+ const s = a, { t: r } = L(), i = N(), u = j(), e = u.unwatched, n = g(Date.now()), c = setInterval(() => n.value = Date.now(), 1e3), l = w(() => e.value ? Math.max(0, Math.floor((n.value - e.value.startedAt) / 1e3)) : 0);
97
+ let o = null;
98
+ B(() => [e.value?.id, e.value?.status], ([m, v]) => {
99
+ o && clearTimeout(o), o = null, m && v && v !== "running" && (o = setTimeout(() => u.dismiss(m), s.hideAfterMs));
100
+ }), E(() => {
101
+ clearInterval(c), o && clearTimeout(o);
102
+ });
103
+ function k() {
104
+ const m = e.value;
105
+ m && (u.dismiss(m.id), i.push(m.route));
106
+ }
107
+ return (m, v) => (d(), y(t(I), null, {
108
+ default: b(() => [
109
+ h(F, {
110
+ "enter-active-class": "transition duration-motion-sheet ease-motion-sheet",
111
+ "enter-from-class": "translate-y-6 scale-95 opacity-0",
112
+ "leave-active-class": "transition duration-motion-normal ease-motion-standard",
113
+ "leave-to-class": "translate-y-4 opacity-0"
114
+ }, {
115
+ default: b(() => [
116
+ t(e) ? (d(), f("div", {
117
+ key: 0,
118
+ class: "dock-safe-area pointer-events-auto order-first mb-3 flex justify-center px-3",
119
+ "data-test": "background-task",
120
+ "data-status": t(e).status
121
+ }, [
122
+ p("div", O, [
123
+ t(e).status === "running" ? (d(), y(t(_), {
124
+ key: 0,
125
+ name: "loader4Line",
126
+ size: 18,
127
+ class: "shrink-0 animate-spin text-content-inverse-accent"
128
+ })) : t(e).status === "done" ? (d(), f("span", U, [
129
+ h(A, { size: 13 })
130
+ ])) : (d(), y(t(_), {
131
+ key: 2,
132
+ name: "errorWarningLine",
133
+ size: 18,
134
+ class: "shrink-0 text-content-inverse-danger"
135
+ })),
136
+ p("span", H, [
137
+ p("span", K, x(t(e).status === "running" ? t(e).title : t(e).status === "done" ? t(e).doneTitle : t(e).failedTitle), 1),
138
+ (d(), f("span", {
139
+ key: `${t(e).status}:${t(e).outcome ?? t(e).detail}`,
140
+ class: "truncate text-footnote opacity-80 animate-text-swap"
141
+ }, x(t(e).status === "running" ? t(e).detail : t(e).outcome ?? t(e).title), 1))
142
+ ]),
143
+ t(e).status === "running" ? (d(), f("span", P, x(t(r)("core.wait.seconds", { count: l.value })), 1)) : (d(), f(z, { key: 4 }, [
144
+ p("button", {
145
+ type: "button",
146
+ class: "hit-target shrink-0 cursor-pointer rounded-full bg-content-inverse-accent px-4 py-2 text-subheadline font-semibold text-surface-inverse",
147
+ "data-test": "background-task-open",
148
+ onClick: k
149
+ }, x(t(r)("core.actions.open")), 1),
150
+ p("button", {
151
+ type: "button",
152
+ class: "hit-target flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-full text-content-inverse opacity-70",
153
+ "aria-label": t(r)("core.actions.close"),
154
+ "data-test": "background-task-dismiss",
155
+ onClick: v[0] || (v[0] = (Q) => t(u).dismiss(t(e).id))
156
+ }, [
157
+ h(t(_), {
158
+ name: "close",
159
+ size: 14
160
+ })
161
+ ], 8, G)
162
+ ], 64))
163
+ ])
164
+ ], 8, q)) : D("", !0)
165
+ ]),
166
+ _: 1
167
+ })
79
168
  ]),
80
- n.$slots.default ? (e(), a("div", w, [
81
- f(n.$slots, "default")
82
- ])) : c("", !0)
83
- ], 8, h));
169
+ _: 1
170
+ }));
84
171
  }
85
172
  });
173
+ function J() {
174
+ return typeof window < "u" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
175
+ }
176
+ function ue(a, s = 700) {
177
+ const r = g(T(a));
178
+ let i = 0;
179
+ function u() {
180
+ i && cancelAnimationFrame(i), i = 0;
181
+ }
182
+ return B(() => T(a), (e, n) => {
183
+ if (u(), n !== 0 || e === 0 || J() || typeof requestAnimationFrame > "u") {
184
+ r.value = e;
185
+ return;
186
+ }
187
+ const c = performance.now(), l = (o) => {
188
+ const k = Math.min(1, (o - c) / s);
189
+ r.value = Math.round(e * (1 - Math.pow(1 - k, 3))), i = k < 1 ? requestAnimationFrame(l) : 0;
190
+ };
191
+ r.value = 0, i = requestAnimationFrame(l);
192
+ }), $(u), r;
193
+ }
86
194
  export {
87
- D as AsyncSection,
88
- H as Badge,
89
- R as Banner,
90
- X as DONE_BEAT_MS,
91
- _ as DrawnCheck,
92
- G as EmptyState,
93
- V as FieldNote,
94
- $ as HUES,
95
- j as ProgressTrack,
96
- I as Skeleton,
97
- z as StatusLine,
98
- Y as WAIT_VISIBLE_AFTER_MS,
99
- W as asyncState,
100
- F as isEmptyValue,
101
- K as useLoad,
102
- Z as useWaitStatus,
103
- O as valueOf
195
+ de as AsyncSection,
196
+ ie as BackgroundTaskCapsule,
197
+ xe as Badge,
198
+ _e as Banner,
199
+ Ce as DONE_BEAT_MS,
200
+ A as DrawnCheck,
201
+ ye as EmptyState,
202
+ oe as FieldNote,
203
+ se as HUES,
204
+ be as ProgressTrack,
205
+ me as Skeleton,
206
+ Se as StatusLine,
207
+ Ee as WAIT_VISIBLE_AFTER_MS,
208
+ fe as asyncState,
209
+ R as backgroundTasksKey,
210
+ W as createBackgroundTasks,
211
+ re as installBackgroundTasks,
212
+ pe as isEmptyValue,
213
+ j as useBackgroundTasks,
214
+ ue as useCountUp,
215
+ Ae as useLoad,
216
+ Fe as useWaitStatus,
217
+ ve as valueOf
104
218
  };
@@ -0,0 +1,9 @@
1
+ import { type MaybeRefOrGetter } from "vue";
2
+ /**
3
+ * A number that counts up to its value when the value arrives (UI decision D23: a result
4
+ * revealed, such as a price that was being calculated). Only a change from nothing (0) counts; a
5
+ * later change, and any change under reduced motion, shows at once.
6
+ *
7
+ * const shown = useCountUp(() => quote.value?.total ?? 0); // <span>{{ format.money(shown) }}</span>
8
+ */
9
+ export declare function useCountUp(target: MaybeRefOrGetter<number>, durationMs?: number): import("vue").Ref<number, number>;