@wssto2/vue-core 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -9
- package/dist/{AdaptivePageShell.vue_vue_type_script_setup_true_lang-CKghARkW.js → AdaptivePageShell.vue_vue_type_script_setup_true_lang-CeKK8_g5.js} +2 -2
- package/dist/{AlertDialog.vue_vue_type_script_setup_true_lang-BZb4qKXo.js → AlertDialog.vue_vue_type_script_setup_true_lang-e4hzkzg3.js} +1 -1
- package/dist/{AsyncSection.vue_vue_type_script_setup_true_lang-B82bl36A.js → AsyncSection.vue_vue_type_script_setup_true_lang-CWLB3xsR.js} +1 -1
- package/dist/Badge.vue_vue_type_script_setup_true_lang-DJyZc0C7.js +51 -0
- package/dist/Button.vue_vue_type_script_setup_true_lang-B1eZgLae.js +182 -0
- package/dist/CountryFlag.vue_vue_type_script_setup_true_lang-Kry_vPPZ.js +299 -0
- package/dist/{DiscardChangesModal.vue_vue_type_script_setup_true_lang-Bx5O1RZJ.js → DiscardChangesModal.vue_vue_type_script_setup_true_lang-DyhYHRyz.js} +1 -1
- package/dist/{LeaveGuardRoot.vue_vue_type_script_setup_true_lang-DH6EB8br.js → LeaveGuardRoot.vue_vue_type_script_setup_true_lang-CvwoT1JF.js} +1 -1
- package/dist/{Menu.vue_vue_type_script_setup_true_lang-D44EHXQZ.js → Menu.vue_vue_type_script_setup_true_lang-CrMj6KPD.js} +1 -1
- package/dist/{Modal.vue_vue_type_script_setup_true_lang-DYQgaUtj.js → Modal.vue_vue_type_script_setup_true_lang-BQZM49gC.js} +138 -128
- package/dist/{PhotoViewer.vue_vue_type_script_setup_true_lang-DDVQXJRB.js → PhotoViewer.vue_vue_type_script_setup_true_lang-ElDizaW2.js} +1 -1
- package/dist/{SectionList.vue_vue_type_script_setup_true_lang-DGcKX_OJ.js → SectionList.vue_vue_type_script_setup_true_lang-L9ita_Kx.js} +2 -2
- package/dist/SelectField.vue_vue_type_script_setup_true_lang-DZTrU8NJ.js +661 -0
- package/dist/{Sheet.vue_vue_type_script_setup_true_lang-BLBD5ps8.js → Sheet.vue_vue_type_script_setup_true_lang-CtkJ-4e3.js} +1 -1
- package/dist/{Tooltip.vue_vue_type_script_setup_true_lang-BJWPVyfT.js → Tooltip.vue_vue_type_script_setup_true_lang-OOD_XNWx.js} +1 -1
- package/dist/app/application.d.ts +5 -0
- package/dist/app/environment.d.ts +2 -0
- package/dist/app/index.js +75 -72
- package/dist/button/index.d.ts +1 -1
- package/dist/button/index.js +5 -3
- package/dist/button/shortcut.d.ts +28 -2
- package/dist/collection/CollectionPage.vue.d.ts +1 -0
- package/dist/collection/CollectionRows.vue.d.ts +59 -0
- package/dist/collection/CollectionTable.vue.d.ts +1 -1
- package/dist/collection/DataTable.vue.d.ts +47 -0
- package/dist/collection/RowActions.vue.d.ts +19 -0
- package/dist/collection/columns.d.ts +2 -0
- package/dist/collection/index.d.ts +4 -2
- package/dist/collection/index.js +1752 -1610
- package/dist/collection/rowLayout.d.ts +10 -0
- package/dist/collection/slots.d.ts +6 -3
- package/dist/describeError-l5T9b8Uv.js +41 -0
- package/dist/dialogStack-DZZg55w0.js +31 -0
- package/dist/form/CommandDialog.vue.d.ts +12 -1
- package/dist/form/FormGroup.vue.d.ts +1 -1
- package/dist/form/MultiSelectField.vue.d.ts +4 -1
- package/dist/form/NumberField.vue.d.ts +6 -0
- package/dist/form/OptionList.vue.d.ts +6 -2
- package/dist/form/SelectField.vue.d.ts +4 -1
- package/dist/form/index.d.ts +2 -0
- package/dist/form/index.js +1940 -2008
- package/dist/form/suggestions.d.ts +11 -0
- package/dist/form/useOptions.d.ts +60 -0
- package/dist/i18n/describeError.d.ts +31 -0
- package/dist/i18n/index.d.ts +26 -0
- package/dist/i18n/index.js +22 -20
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/internal/CountryFlag.vue.d.ts +10 -0
- package/dist/modal/Modal.vue.d.ts +4 -0
- package/dist/modal/ShortcutHelp.vue.d.ts +2 -0
- package/dist/modal/index.d.ts +2 -0
- package/dist/modal/index.js +64 -6
- package/dist/modal/placement.d.ts +22 -0
- package/dist/{options-CjSz2dQK.js → options-DAkBtYkI.js} +3 -3
- package/dist/overlay/Popover.vue.d.ts +3 -0
- package/dist/overlay/dialogStack.d.ts +9 -0
- package/dist/overlay/index.d.ts +1 -1
- package/dist/overlay/index.js +18 -17
- package/dist/page/index.js +4 -4
- package/dist/phone/index.js +247 -540
- package/dist/platform/AccessGate.vue.d.ts +50 -0
- package/dist/platform/index.d.ts +2 -0
- package/dist/platform/index.js +86 -66
- package/dist/resource/index.js +42 -42
- package/dist/shell/AccountMenuItem.vue.d.ts +3 -0
- package/dist/shell/NavigationList.vue.d.ts +1 -1
- package/dist/shell/index.js +406 -394
- package/dist/state/Badge.vue.d.ts +17 -10
- package/dist/state/hue.d.ts +8 -0
- package/dist/state/index.d.ts +2 -0
- package/dist/state/index.js +92 -56
- package/dist/state/useLoad.d.ts +41 -0
- package/dist/styles.css +1 -1
- package/dist/testing/app.d.ts +61 -0
- package/dist/testing/async.d.ts +12 -0
- package/dist/testing/collection.d.ts +18 -0
- package/dist/testing/format.d.ts +9 -0
- package/dist/testing/i18n.d.ts +16 -0
- package/dist/testing/index.d.ts +14 -0
- package/dist/testing/index.js +178 -0
- package/dist/testing/media.d.ts +23 -0
- package/dist/testing/platform.d.ts +40 -0
- package/dist/testing/transport.d.ts +40 -0
- package/dist/{toast-BTeX0r66.js → toast-DvrHQ6Ag.js} +45 -43
- package/dist/{useDialog-wmyNx2j_.js → useDialog-CXR3hN0G.js} +1 -1
- package/package.json +5 -1
- package/src/styles/theme.css +60 -0
- package/dist/Badge.vue_vue_type_script_setup_true_lang-DKc6ugmr.js +0 -31
- package/dist/Button.vue_vue_type_script_setup_true_lang-3f0emVNU.js +0 -141
- package/dist/SelectField.vue_vue_type_script_setup_true_lang-SyFWE33A.js +0 -422
- package/dist/dialogStack-DiTG1rZc.js +0 -26
- package/dist/phone/CountryFlag.vue.d.ts +0 -10
- /package/dist/{phone → internal}/flagLoaders.d.ts +0 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { deferred, settle } from "./async";
|
|
2
|
+
export type { Deferred } from "./async";
|
|
3
|
+
export { createTestApp, stubRoutes, withSetup } from "./app";
|
|
4
|
+
export type { TestApp, TestAppOptions } from "./app";
|
|
5
|
+
export { fakeLoader, listPage } from "./collection";
|
|
6
|
+
export { testFormatting } from "./format";
|
|
7
|
+
export { createTestI18n } from "./i18n";
|
|
8
|
+
export type { TestI18nOptions, TestMessages } from "./i18n";
|
|
9
|
+
export { mockMedia } from "./media";
|
|
10
|
+
export type { MediaState } from "./media";
|
|
11
|
+
export { createTestPlatform, createTestSession, heldAccess } from "./platform";
|
|
12
|
+
export type { TestPlatformOptions, TestSessionOptions } from "./platform";
|
|
13
|
+
export { jsonResponse, routedTransport, scriptedTransport } from "./transport";
|
|
14
|
+
export type { Answer, RecordedCall } from "./transport";
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { nextTick as b, createApp as j, defineComponent as m, h as u } from "vue";
|
|
2
|
+
import { createMemoryHistory as P, createRouter as E, RouterView as $ } from "vue-router";
|
|
3
|
+
import { a as A, i as O } from "../platform-jqlKVJ2e.js";
|
|
4
|
+
import { createI18n as k } from "vue-i18n";
|
|
5
|
+
import { coreMessages as M } from "../i18n/index.js";
|
|
6
|
+
import { i as R, c as S } from "../format-B8mJm946.js";
|
|
7
|
+
function K() {
|
|
8
|
+
let e, t;
|
|
9
|
+
return { promise: new Promise((s, r) => {
|
|
10
|
+
e = s, t = r;
|
|
11
|
+
}), resolve: e, reject: t };
|
|
12
|
+
}
|
|
13
|
+
async function Q(e = 5) {
|
|
14
|
+
for (let t = 0; t < e; t++)
|
|
15
|
+
await new Promise((n) => setTimeout(n, 0)), await b();
|
|
16
|
+
}
|
|
17
|
+
const f = (e) => typeof e == "object" && e !== null && !Array.isArray(e);
|
|
18
|
+
function g(e, t) {
|
|
19
|
+
const n = { ...e };
|
|
20
|
+
for (const [s, r] of Object.entries(t)) {
|
|
21
|
+
const a = n[s];
|
|
22
|
+
n[s] = f(r) && f(a) ? g(a, r) : r;
|
|
23
|
+
}
|
|
24
|
+
return n;
|
|
25
|
+
}
|
|
26
|
+
function x(e = {}) {
|
|
27
|
+
const t = { ...M };
|
|
28
|
+
for (const [n, s] of Object.entries(e.messages ?? {})) t[n] = g(t[n] ?? {}, s);
|
|
29
|
+
return k({ legacy: !1, locale: e.locale ?? "en", fallbackLocale: "en", messages: t });
|
|
30
|
+
}
|
|
31
|
+
function L(e) {
|
|
32
|
+
return { install: (t) => R(t, S({ locale: () => e.global.locale.value })) };
|
|
33
|
+
}
|
|
34
|
+
function C(e, t, n = "all", s) {
|
|
35
|
+
const r = t === void 0 ? { level: e } : { level: e, id: t };
|
|
36
|
+
return { scope: r, qualifier: n, clauses: s ?? [{ scope: r, qualifier: n }] };
|
|
37
|
+
}
|
|
38
|
+
function B(e = {}) {
|
|
39
|
+
const { permissions: t = [] } = e, n = Array.isArray(t) ? Object.fromEntries(t.map((r) => [r, C("organization")])) : { ...t }, s = { root: e.root ?? !1, permissions: n, unavailable: e.unavailable ?? [] };
|
|
40
|
+
return {
|
|
41
|
+
user: e.user ?? { id: 1 },
|
|
42
|
+
expiresAt: e.expiresAt ?? null,
|
|
43
|
+
access: s,
|
|
44
|
+
...e.navigation ? { navigation: e.navigation } : {}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const F = async (e, t) => {
|
|
48
|
+
throw new Error(`createTestPlatform: an unexpected request ${(t.method ?? "GET").toUpperCase()} ${e}. Pass a \`transport\` that answers it (scriptedTransport or routedTransport).`);
|
|
49
|
+
};
|
|
50
|
+
function U(e = {}) {
|
|
51
|
+
const { user: t, permissions: n, unavailable: s, root: r, navigation: a, expiresAt: o, transport: c, config: i, session: l, ...T } = e, p = t === null ? null : B({ user: t, permissions: n, unavailable: s, root: r, navigation: a, expiresAt: o }), y = l ?? { load: async () => p, signOut: async () => {
|
|
52
|
+
} }, d = A({
|
|
53
|
+
...T,
|
|
54
|
+
config: { apiBase: "", locale: "en", appName: "Test app", capabilities: [], ...i },
|
|
55
|
+
transport: c ?? F,
|
|
56
|
+
session: y
|
|
57
|
+
});
|
|
58
|
+
return !l && p && d.session.establish(p), d;
|
|
59
|
+
}
|
|
60
|
+
const z = m({ name: "TestBlankPage", render: () => u("div") }), h = m({ name: "TestRouteStub", render: () => u("div", [u($)]) });
|
|
61
|
+
function G(e) {
|
|
62
|
+
return e.map((t) => {
|
|
63
|
+
const { component: n, components: s, children: r, ...a } = t, o = { ...a };
|
|
64
|
+
return r && (o.children = G(r)), s ? o.components = Object.fromEntries(Object.keys(s).map((c) => [c, h])) : n !== void 0 && (o.component = h), o;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function H(e = {}) {
|
|
68
|
+
const t = e.platform ?? U();
|
|
69
|
+
let n = e.router;
|
|
70
|
+
if (!n) {
|
|
71
|
+
const a = P();
|
|
72
|
+
a.replace(e.location ?? "/"), n = E({ history: a, routes: [...e.routes ?? [{ path: "/:pathMatch(.*)*", component: z }]] });
|
|
73
|
+
}
|
|
74
|
+
const s = x({ locale: e.locale, messages: e.messages });
|
|
75
|
+
return { plugins: [n, s, L(s), { install: (a) => O(a, t) }, ...e.plugins ?? []], platform: t, router: n, i18n: s };
|
|
76
|
+
}
|
|
77
|
+
function W(e, t = H()) {
|
|
78
|
+
let n;
|
|
79
|
+
const s = j(
|
|
80
|
+
m({
|
|
81
|
+
setup() {
|
|
82
|
+
return n = e(), () => u("div");
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
);
|
|
86
|
+
for (const r of t.plugins) s.use(r);
|
|
87
|
+
return s.mount(document.createElement("div")), { result: n, app: s, unmount: () => s.unmount() };
|
|
88
|
+
}
|
|
89
|
+
function X(e, t = {}) {
|
|
90
|
+
return {
|
|
91
|
+
rows: e,
|
|
92
|
+
total: e.length,
|
|
93
|
+
page: 1,
|
|
94
|
+
pageSize: 25,
|
|
95
|
+
lastPage: e.length === 0 ? 0 : 1,
|
|
96
|
+
from: e.length === 0 ? 0 : 1,
|
|
97
|
+
to: e.length,
|
|
98
|
+
...t
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function Y(e) {
|
|
102
|
+
const t = [];
|
|
103
|
+
return { load: (s, r) => (t.push({ query: s, signal: r.signal }), Promise.resolve(e(s, r, t.length))), calls: t };
|
|
104
|
+
}
|
|
105
|
+
function Z(e = {}) {
|
|
106
|
+
const t = { compact: !1, narrow: !1, reducedMotion: !1, standalone: !1, ...e }, n = window.matchMedia, s = /* @__PURE__ */ new Set(), r = (o) => o.includes("prefers-reduced-motion") ? t.reducedMotion : o.includes("display-mode: standalone") ? t.standalone : o.includes("min-width: 48rem") ? !t.compact : o.includes("max-width: 1023px") ? t.narrow || t.compact : o.includes("pointer: coarse") || o.includes("max-width: 47.999rem") ? t.compact : !1, a = (o) => o.split(",").some(r);
|
|
107
|
+
return window.matchMedia = ((o) => {
|
|
108
|
+
const c = { query: o, last: a(o), listeners: /* @__PURE__ */ new Set() };
|
|
109
|
+
return s.add(c), {
|
|
110
|
+
media: o,
|
|
111
|
+
get matches() {
|
|
112
|
+
return a(o);
|
|
113
|
+
},
|
|
114
|
+
addEventListener: (i, l) => c.listeners.add(l),
|
|
115
|
+
removeEventListener: (i, l) => c.listeners.delete(l)
|
|
116
|
+
};
|
|
117
|
+
}), {
|
|
118
|
+
state: t,
|
|
119
|
+
set(o) {
|
|
120
|
+
Object.assign(t, o);
|
|
121
|
+
for (const c of s) {
|
|
122
|
+
const i = a(c.query);
|
|
123
|
+
i !== c.last && (c.last = i, c.listeners.forEach((l) => l()));
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
restore() {
|
|
127
|
+
window.matchMedia = n;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const q = (e, t, n = {}) => new Response(t === void 0 ? null : JSON.stringify(t), { status: e, headers: { "Content-Type": "application/json", ...n } }), w = (e, t) => ({
|
|
132
|
+
url: e,
|
|
133
|
+
method: (t.method ?? "GET").toUpperCase(),
|
|
134
|
+
headers: new Headers(t.headers),
|
|
135
|
+
init: t
|
|
136
|
+
});
|
|
137
|
+
async function v(e, t) {
|
|
138
|
+
if (e instanceof Error) throw e;
|
|
139
|
+
return typeof e == "function" ? e(t) : e.clone();
|
|
140
|
+
}
|
|
141
|
+
function ee(...e) {
|
|
142
|
+
const t = [], n = [...e];
|
|
143
|
+
return { transport: async (r, a) => {
|
|
144
|
+
const o = w(r, a);
|
|
145
|
+
t.push(o);
|
|
146
|
+
const c = n.length > 1 ? n.shift() : n[0];
|
|
147
|
+
if (c === void 0) throw new Error("scriptedTransport: no answer left");
|
|
148
|
+
return v(c, o);
|
|
149
|
+
}, calls: t };
|
|
150
|
+
}
|
|
151
|
+
function te(e) {
|
|
152
|
+
const t = [];
|
|
153
|
+
return { transport: async (s, r) => {
|
|
154
|
+
const a = w(s, r);
|
|
155
|
+
t.push(a);
|
|
156
|
+
const { pathname: o, search: c } = new URL(s, "http://test.invalid"), i = e[`${a.method} ${o}${c}`] ?? e[`${a.method} ${o}`];
|
|
157
|
+
if (i === void 0) throw new Error(`routedTransport: no route for ${a.method} ${o}${c}; it has ${Object.keys(e).join(", ") || "none"}`);
|
|
158
|
+
return v(i, a);
|
|
159
|
+
}, calls: t };
|
|
160
|
+
}
|
|
161
|
+
export {
|
|
162
|
+
H as createTestApp,
|
|
163
|
+
x as createTestI18n,
|
|
164
|
+
U as createTestPlatform,
|
|
165
|
+
B as createTestSession,
|
|
166
|
+
K as deferred,
|
|
167
|
+
Y as fakeLoader,
|
|
168
|
+
C as heldAccess,
|
|
169
|
+
q as jsonResponse,
|
|
170
|
+
X as listPage,
|
|
171
|
+
Z as mockMedia,
|
|
172
|
+
te as routedTransport,
|
|
173
|
+
ee as scriptedTransport,
|
|
174
|
+
Q as settle,
|
|
175
|
+
G as stubRoutes,
|
|
176
|
+
L as testFormatting,
|
|
177
|
+
W as withSetup
|
|
178
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** What the mocked `matchMedia` answers. */
|
|
2
|
+
export interface MediaState {
|
|
3
|
+
/** Phones and touch-first screens (the `compact:` condition). */
|
|
4
|
+
compact: boolean;
|
|
5
|
+
/** Below 1024 px, phones and tablets: where lists turn into phone rows. */
|
|
6
|
+
narrow: boolean;
|
|
7
|
+
reducedMotion: boolean;
|
|
8
|
+
/** Running as an installed app (`display-mode: standalone`). */
|
|
9
|
+
standalone: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Replaces `window.matchMedia` for a test: the library's conditions (compact, narrow, reduced motion, standalone) are answered from
|
|
13
|
+
* `state`, everything else is false. Change `state` through `set` to fire `change` listeners.
|
|
14
|
+
* Returns `restore`: call it in `afterEach`.
|
|
15
|
+
*
|
|
16
|
+
* const media = mockMedia({ compact: true });
|
|
17
|
+
* afterEach(() => media.restore());
|
|
18
|
+
*/
|
|
19
|
+
export declare function mockMedia(initial?: Partial<MediaState>): {
|
|
20
|
+
state: MediaState;
|
|
21
|
+
set(next: Partial<MediaState>): void;
|
|
22
|
+
restore(): void;
|
|
23
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type AccessClause, type AccessQualifier, type BootstrapConfig, type HeldAccess, type NavigationNode, type Platform, type PlatformOptions, type SessionAdapter, type SessionSnapshot, type SessionUser } from "../platform";
|
|
2
|
+
import type { Transport } from "../client";
|
|
3
|
+
/**
|
|
4
|
+
* How a permission is held: at a level of your scope hierarchy (`organization`, `dealer`, ...) with an
|
|
5
|
+
* optional id, for `all` records, only the user's `own` or those of their `own_location`. For the
|
|
6
|
+
* `permissions` record of `createTestSession`; a plain permission name there means "everything, at the organization".
|
|
7
|
+
*/
|
|
8
|
+
export declare function heldAccess(level: string, id?: number, qualifier?: AccessQualifier, clauses?: readonly AccessClause[]): HeldAccess;
|
|
9
|
+
export interface TestSessionOptions<U extends SessionUser = SessionUser> {
|
|
10
|
+
/** Default `{ id: 1 }`. */
|
|
11
|
+
user?: U;
|
|
12
|
+
/** Permissions held at the organization for every record (names), or exactly how each is held (`heldAccess`). */
|
|
13
|
+
permissions?: readonly string[] | Readonly<Record<string, HeldAccess>>;
|
|
14
|
+
/** Granted by a role but switched off for the tenant: not held. */
|
|
15
|
+
unavailable?: readonly string[];
|
|
16
|
+
/** Some binding is at the root scope. */
|
|
17
|
+
root?: boolean;
|
|
18
|
+
navigation?: readonly NavigationNode[];
|
|
19
|
+
expiresAt?: Date | null;
|
|
20
|
+
}
|
|
21
|
+
/** What `/auth/me` would say for a user: for `session.establish(...)` mid-test, or an adapter that answers it. */
|
|
22
|
+
export declare function createTestSession<U extends SessionUser = SessionUser>(options?: TestSessionOptions<U>): SessionSnapshot<U>;
|
|
23
|
+
export interface TestPlatformOptions<U extends SessionUser = SessionUser> extends Omit<TestSessionOptions<U>, "user">, Omit<PlatformOptions<U, BootstrapConfig>, "config" | "transport" | "session"> {
|
|
24
|
+
/** `null`: nobody is signed in (the session answers "none" when asked). Default: the user of `createTestSession`. */
|
|
25
|
+
user?: U | null;
|
|
26
|
+
/** Answers the requests the code under test makes: `scriptedTransport` or `routedTransport`. Default: any request fails the test. */
|
|
27
|
+
transport?: Transport;
|
|
28
|
+
/** Fields of the bootstrap config over the test's (`apiBase: ""`, `locale: "en"`, `appName: "Test app"`). */
|
|
29
|
+
config?: Partial<BootstrapConfig>;
|
|
30
|
+
/** A session adapter of your own; then `user`, `permissions` and the like are not used and the session starts unasked. */
|
|
31
|
+
session?: SessionAdapter<U>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A platform for a test: an HTTP client over a fake transport, a session that is already signed in
|
|
35
|
+
* (no request) and the access it grants. Independent of every other platform, as production's.
|
|
36
|
+
*
|
|
37
|
+
* const platform = createTestPlatform({ user: { id: 7 }, permissions: ["tickets:view"], transport });
|
|
38
|
+
* platform.access.can("tickets:view"); // true, at once
|
|
39
|
+
*/
|
|
40
|
+
export declare function createTestPlatform<U extends SessionUser = SessionUser>(options?: TestPlatformOptions<U>): Platform<U>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Transport } from "../client";
|
|
2
|
+
/** One request a fake transport received. */
|
|
3
|
+
export interface RecordedCall {
|
|
4
|
+
/** The URL as the client sent it, with the API base and the query string. */
|
|
5
|
+
readonly url: string;
|
|
6
|
+
/** Upper case; `GET` when the client set none. */
|
|
7
|
+
readonly method: string;
|
|
8
|
+
readonly headers: Headers;
|
|
9
|
+
/** The request as `fetch` received it (body, signal). */
|
|
10
|
+
readonly init: RequestInit;
|
|
11
|
+
}
|
|
12
|
+
/** What a fake transport answers: a response (served again for every matching call), a failure to throw, or a function of the call. */
|
|
13
|
+
export type Answer = Response | Error | ((call: RecordedCall) => Response | Promise<Response>);
|
|
14
|
+
/** A JSON response, as go-core answers: `jsonResponse(404, { error: "…" })`. A body of `undefined` is an empty one (204). */
|
|
15
|
+
export declare const jsonResponse: (status: number, body?: unknown, headers?: Record<string, string>) => Response;
|
|
16
|
+
/**
|
|
17
|
+
* A transport answering from a queue, one answer per call in order (the last one repeats), and
|
|
18
|
+
* recording what it was sent. For a test about a sequence: a retry, a 401 then a renewal, a race.
|
|
19
|
+
*
|
|
20
|
+
* const { transport, calls } = scriptedTransport(jsonResponse(503, {}), jsonResponse(200, { id: 1 }));
|
|
21
|
+
*/
|
|
22
|
+
export declare function scriptedTransport(...answers: Answer[]): {
|
|
23
|
+
transport: Transport;
|
|
24
|
+
calls: RecordedCall[];
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* A transport answering by route, whatever the order: keys are `"METHOD /path"` (the path with the
|
|
28
|
+
* API base, the query string optional: `"GET /api/v1/tickets"` matches `/api/v1/tickets?page=2` unless a
|
|
29
|
+
* key with that exact query exists). A request nothing matches fails the test with the routes it
|
|
30
|
+
* knows, so a view that asks for something unexpected is never answered by accident.
|
|
31
|
+
*
|
|
32
|
+
* const { transport, calls } = routedTransport({
|
|
33
|
+
* "GET /tickets": jsonResponse(200, { data: [] }),
|
|
34
|
+
* "POST /tickets": (call) => jsonResponse(201, { id: 7 }),
|
|
35
|
+
* });
|
|
36
|
+
*/
|
|
37
|
+
export declare function routedTransport(routes: Readonly<Record<string, Answer>>): {
|
|
38
|
+
transport: Transport;
|
|
39
|
+
calls: RecordedCall[];
|
|
40
|
+
};
|
|
@@ -1,77 +1,79 @@
|
|
|
1
|
-
import { defineComponent as
|
|
2
|
-
import { f as
|
|
3
|
-
import { u as
|
|
4
|
-
import { toast as
|
|
5
|
-
const
|
|
1
|
+
import { defineComponent as D, useId as F, ref as c, watch as B, nextTick as C, onUnmounted as M, computed as U, openBlock as m, createElementBlock as w, normalizeClass as f, createElementVNode as j, renderSlot as $, createVNode as G, Transition as H, withCtx as K, normalizeStyle as P, unref as n, createCommentVNode as I } from "vue";
|
|
2
|
+
import { f as L } from "./useDialog-CXR3hN0G.js";
|
|
3
|
+
import { u as q, a as p } from "./anchored-Cj4--8dT.js";
|
|
4
|
+
import { toast as l } from "vue-sonner";
|
|
5
|
+
const J = ["aria-label"], N = 12, Q = 640, te = /* @__PURE__ */ D({
|
|
6
6
|
__name: "Popover",
|
|
7
7
|
props: {
|
|
8
8
|
label: {},
|
|
9
9
|
placement: { default: "bottom" },
|
|
10
10
|
width: { default: "lg" },
|
|
11
11
|
arrow: { type: Boolean, default: !0 },
|
|
12
|
+
matchTriggerWidth: { type: Boolean, default: !1 },
|
|
12
13
|
block: { type: Boolean, default: !1 },
|
|
13
14
|
autofocus: { type: Boolean, default: !0 }
|
|
14
15
|
},
|
|
15
16
|
emits: ["presented", "dismissed"],
|
|
16
|
-
setup(t, { expose: o, emit:
|
|
17
|
-
const s = t, v =
|
|
18
|
-
reference: () =>
|
|
17
|
+
setup(t, { expose: o, emit: O }) {
|
|
18
|
+
const s = t, v = O, R = { sm: "w-56", md: "w-72", lg: "w-80", auto: "w-max" }, h = `popover-${F()}`, a = c(!1), i = c(null), d = c(null), g = c(null), { style: S, arrowStyle: z, resolvedPlacement: u } = q(d, {
|
|
19
|
+
reference: () => i.value?.firstElementChild ?? i.value,
|
|
19
20
|
placement: () => s.placement,
|
|
20
21
|
offset: 10,
|
|
21
|
-
padding:
|
|
22
|
-
arrow:
|
|
23
|
-
fit: (e,
|
|
24
|
-
|
|
22
|
+
padding: N,
|
|
23
|
+
arrow: g,
|
|
24
|
+
fit: (e, _) => {
|
|
25
|
+
const T = i.value?.firstElementChild ?? i.value;
|
|
26
|
+
Object.assign(e.style, window.innerWidth < Q ? { width: `${window.innerWidth - N * 2}px`, maxWidth: "", minWidth: "" } : { width: "", maxWidth: `${Math.max(0, _.width)}px`, minWidth: s.matchTriggerWidth && T ? `${Math.round(T.getBoundingClientRect().width)}px` : "" });
|
|
25
27
|
}
|
|
26
28
|
});
|
|
27
|
-
function
|
|
29
|
+
function y() {
|
|
28
30
|
a.value || (a.value = !0, v("presented"));
|
|
29
31
|
}
|
|
30
32
|
function r(e = !1) {
|
|
31
|
-
a.value && (a.value = !1, v("dismissed"), e &&
|
|
33
|
+
a.value && (a.value = !1, v("dismissed"), e && C(() => L(i.value)[0]?.focus()));
|
|
32
34
|
}
|
|
33
35
|
function x() {
|
|
34
|
-
a.value ? r() :
|
|
36
|
+
a.value ? r() : y();
|
|
35
37
|
}
|
|
36
38
|
function b(e) {
|
|
37
39
|
e.key === "Escape" && (e.preventDefault(), e.stopPropagation(), r(!0));
|
|
38
40
|
}
|
|
39
41
|
function k(e) {
|
|
40
|
-
return e instanceof Node && (!!d.value?.contains(e) || !!
|
|
42
|
+
return e instanceof Node && (!!d.value?.contains(e) || !!i.value?.contains(e));
|
|
41
43
|
}
|
|
42
44
|
function E(e) {
|
|
43
45
|
k(e.target) || r();
|
|
44
46
|
}
|
|
45
|
-
function
|
|
47
|
+
function V(e) {
|
|
46
48
|
e.relatedTarget && !k(e.relatedTarget) && r();
|
|
47
49
|
}
|
|
48
|
-
|
|
49
|
-
e && s.autofocus &&
|
|
50
|
+
B(d, (e) => {
|
|
51
|
+
e && s.autofocus && C(() => (L(e)[0] ?? e).focus({ preventScroll: !0 }));
|
|
50
52
|
});
|
|
51
|
-
function
|
|
53
|
+
function W() {
|
|
52
54
|
window.removeEventListener("keydown", b, !0), document.removeEventListener("pointerdown", E, !0);
|
|
53
55
|
}
|
|
54
|
-
|
|
55
|
-
e ? (window.addEventListener("keydown", b, !0), document.addEventListener("pointerdown", E, !0)) :
|
|
56
|
-
}),
|
|
57
|
-
const
|
|
56
|
+
B(a, (e) => {
|
|
57
|
+
e ? (window.addEventListener("keydown", b, !0), document.addEventListener("pointerdown", E, !0)) : W();
|
|
58
|
+
}), M(W);
|
|
59
|
+
const A = U(() => ({
|
|
58
60
|
"aria-expanded": a.value,
|
|
59
61
|
"aria-haspopup": "dialog",
|
|
60
62
|
"aria-controls": h
|
|
61
63
|
}));
|
|
62
|
-
return o({ present:
|
|
64
|
+
return o({ present: y, dismiss: () => r(), toggle: x }), (e, _) => (m(), w("span", {
|
|
63
65
|
class: f(s.block ? "flex w-full" : "inline-flex"),
|
|
64
|
-
onFocusout:
|
|
66
|
+
onFocusout: V
|
|
65
67
|
}, [
|
|
66
68
|
j("span", {
|
|
67
69
|
ref_key: "anchor",
|
|
68
|
-
ref:
|
|
70
|
+
ref: i,
|
|
69
71
|
class: f(s.block ? "flex w-full" : "inline-flex")
|
|
70
72
|
}, [
|
|
71
73
|
$(e.$slots, "trigger", {
|
|
72
74
|
presented: a.value,
|
|
73
75
|
toggle: x,
|
|
74
|
-
attrs:
|
|
76
|
+
attrs: A.value
|
|
75
77
|
})
|
|
76
78
|
], 2),
|
|
77
79
|
G(H, {
|
|
@@ -89,8 +91,8 @@ const q = ["aria-label"], L = 12, J = 640, ee = /* @__PURE__ */ A({
|
|
|
89
91
|
role: "dialog",
|
|
90
92
|
"aria-label": s.label,
|
|
91
93
|
tabindex: "-1",
|
|
92
|
-
class: f(["z-1000 rounded-menu bg-surface-overlay p-3 text-content-strong shadow-float outline-none",
|
|
93
|
-
style:
|
|
94
|
+
class: f(["z-1000 rounded-menu bg-surface-overlay p-3 text-content-strong shadow-float outline-none", R[s.width]]),
|
|
95
|
+
style: P(n(S))
|
|
94
96
|
}, [
|
|
95
97
|
$(e.$slots, "default", {
|
|
96
98
|
dismiss: () => r(!0)
|
|
@@ -98,7 +100,7 @@ const q = ["aria-label"], L = 12, J = 640, ee = /* @__PURE__ */ A({
|
|
|
98
100
|
s.arrow ? (m(), w("span", {
|
|
99
101
|
key: 0,
|
|
100
102
|
ref_key: "arrowEl",
|
|
101
|
-
ref:
|
|
103
|
+
ref: g,
|
|
102
104
|
"aria-hidden": "true",
|
|
103
105
|
class: f(["absolute size-2.5 rotate-45 bg-surface-overlay", {
|
|
104
106
|
"-bottom-[5px]": n(p)(n(u)) === "bottom",
|
|
@@ -106,24 +108,24 @@ const q = ["aria-label"], L = 12, J = 640, ee = /* @__PURE__ */ A({
|
|
|
106
108
|
"-left-[5px]": n(p)(n(u)) === "left",
|
|
107
109
|
"-right-[5px]": n(p)(n(u)) === "right"
|
|
108
110
|
}]),
|
|
109
|
-
style:
|
|
110
|
-
}, null, 6)) :
|
|
111
|
-
], 14,
|
|
111
|
+
style: P(n(z))
|
|
112
|
+
}, null, 6)) : I("", !0)
|
|
113
|
+
], 14, J)) : I("", !0)
|
|
112
114
|
]),
|
|
113
115
|
_: 3
|
|
114
116
|
})
|
|
115
117
|
], 34));
|
|
116
118
|
}
|
|
117
|
-
}),
|
|
118
|
-
success: (t, o) =>
|
|
119
|
-
info: (t, o) =>
|
|
120
|
-
warning: (t, o) =>
|
|
121
|
-
error: (t, o) =>
|
|
122
|
-
loading: (t, o) =>
|
|
119
|
+
}), oe = {
|
|
120
|
+
success: (t, o) => l.success(t, o),
|
|
121
|
+
info: (t, o) => l.info(t, o),
|
|
122
|
+
warning: (t, o) => l.warning(t, o),
|
|
123
|
+
error: (t, o) => l.error(t, o),
|
|
124
|
+
loading: (t, o) => l.loading(t, o),
|
|
123
125
|
/** Dismisses one toast, or all of them without an id. */
|
|
124
|
-
dismiss: (t) => void
|
|
126
|
+
dismiss: (t) => void l.dismiss(t)
|
|
125
127
|
};
|
|
126
128
|
export {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
+
te as _,
|
|
130
|
+
oe as t
|
|
129
131
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wssto2/vue-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Shared Vue 3 library for go-core applications: app shell, list / record / form pages and their controls.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vue",
|
|
@@ -107,6 +107,10 @@
|
|
|
107
107
|
"types": "./dist/phone/index.d.ts",
|
|
108
108
|
"import": "./dist/phone/index.js"
|
|
109
109
|
},
|
|
110
|
+
"./testing": {
|
|
111
|
+
"types": "./dist/testing/index.d.ts",
|
|
112
|
+
"import": "./dist/testing/index.js"
|
|
113
|
+
},
|
|
110
114
|
"./styles.css": "./dist/styles.css",
|
|
111
115
|
"./tailwind.css": "./src/styles/tailwind.css",
|
|
112
116
|
"./fonts.css": "./src/styles/fonts.css",
|
package/src/styles/theme.css
CHANGED
|
@@ -226,6 +226,29 @@
|
|
|
226
226
|
--app-status-neutral-surface: #F1F3F2;
|
|
227
227
|
--app-status-neutral-solid: #5B6763;
|
|
228
228
|
|
|
229
|
+
/* Categories: nine hues for telling things apart (a lead's source, a kind), never for a state. Each is a
|
|
230
|
+
content / surface pair like the status roles and is checked for text contrast (AA, 4.5:1) in
|
|
231
|
+
src/state/categoryHues.test.ts. Red, green, orange-brown and sky blue belong to the status roles above,
|
|
232
|
+
so no hue here borrows them. Not for the brand to change. */
|
|
233
|
+
--app-category-amber-content: #766202;
|
|
234
|
+
--app-category-amber-surface: #F6F0DA;
|
|
235
|
+
--app-category-lime-content: #466E00;
|
|
236
|
+
--app-category-lime-surface: #EAF5E0;
|
|
237
|
+
--app-category-teal-content: #057168;
|
|
238
|
+
--app-category-teal-surface: #DBF7F3;
|
|
239
|
+
--app-category-cyan-content: #036E7F;
|
|
240
|
+
--app-category-cyan-surface: #DAF6FC;
|
|
241
|
+
--app-category-blue-content: #3260B9;
|
|
242
|
+
--app-category-blue-surface: #E9F1FE;
|
|
243
|
+
--app-category-indigo-content: #5E57B9;
|
|
244
|
+
--app-category-indigo-surface: #EEEFFF;
|
|
245
|
+
--app-category-violet-content: #7D4BAA;
|
|
246
|
+
--app-category-violet-surface: #F4ECFE;
|
|
247
|
+
--app-category-fuchsia-content: #9B4089;
|
|
248
|
+
--app-category-fuchsia-surface: #FEE9F8;
|
|
249
|
+
--app-category-pink-content: #A73967;
|
|
250
|
+
--app-category-pink-surface: #FFEAF0;
|
|
251
|
+
|
|
229
252
|
/* Icon tiles: brand = the application's programmes and primary status icons
|
|
230
253
|
(tile-brand above), anchor = integrations and navigation (also person and
|
|
231
254
|
record avatars), neutral = system and export */
|
|
@@ -342,6 +365,25 @@
|
|
|
342
365
|
--app-status-neutral-surface: #293530;
|
|
343
366
|
--app-status-neutral-solid: #5B6763;
|
|
344
367
|
|
|
368
|
+
--app-category-amber-content: #D6BD5C;
|
|
369
|
+
--app-category-amber-surface: #362D0D;
|
|
370
|
+
--app-category-lime-content: #A4CD79;
|
|
371
|
+
--app-category-lime-surface: #253317;
|
|
372
|
+
--app-category-teal-content: #4CD7C8;
|
|
373
|
+
--app-category-teal-surface: #053631;
|
|
374
|
+
--app-category-cyan-content: #4AD2EC;
|
|
375
|
+
--app-category-cyan-surface: #04343D;
|
|
376
|
+
--app-category-blue-content: #9BBEFF;
|
|
377
|
+
--app-category-blue-surface: #202E47;
|
|
378
|
+
--app-category-indigo-content: #B4B5FF;
|
|
379
|
+
--app-category-indigo-surface: #2A2A46;
|
|
380
|
+
--app-category-violet-content: #D1A9FB;
|
|
381
|
+
--app-category-violet-surface: #342742;
|
|
382
|
+
--app-category-fuchsia-content: #EEA0DB;
|
|
383
|
+
--app-category-fuchsia-surface: #3D2437;
|
|
384
|
+
--app-category-pink-content: #FC9CBD;
|
|
385
|
+
--app-category-pink-surface: #42232E;
|
|
386
|
+
|
|
345
387
|
--app-tile-neutral: #4F5B57;
|
|
346
388
|
|
|
347
389
|
--app-shadow-group: 0 0 0 1px rgb(210 235 226 / 0.04);
|
|
@@ -456,6 +498,24 @@
|
|
|
456
498
|
--color-status-neutral-content: var(--app-status-neutral-content);
|
|
457
499
|
--color-status-neutral-surface: var(--app-status-neutral-surface);
|
|
458
500
|
--color-status-neutral-solid: var(--app-status-neutral-solid);
|
|
501
|
+
--color-category-amber-content: var(--app-category-amber-content);
|
|
502
|
+
--color-category-amber-surface: var(--app-category-amber-surface);
|
|
503
|
+
--color-category-lime-content: var(--app-category-lime-content);
|
|
504
|
+
--color-category-lime-surface: var(--app-category-lime-surface);
|
|
505
|
+
--color-category-teal-content: var(--app-category-teal-content);
|
|
506
|
+
--color-category-teal-surface: var(--app-category-teal-surface);
|
|
507
|
+
--color-category-cyan-content: var(--app-category-cyan-content);
|
|
508
|
+
--color-category-cyan-surface: var(--app-category-cyan-surface);
|
|
509
|
+
--color-category-blue-content: var(--app-category-blue-content);
|
|
510
|
+
--color-category-blue-surface: var(--app-category-blue-surface);
|
|
511
|
+
--color-category-indigo-content: var(--app-category-indigo-content);
|
|
512
|
+
--color-category-indigo-surface: var(--app-category-indigo-surface);
|
|
513
|
+
--color-category-violet-content: var(--app-category-violet-content);
|
|
514
|
+
--color-category-violet-surface: var(--app-category-violet-surface);
|
|
515
|
+
--color-category-fuchsia-content: var(--app-category-fuchsia-content);
|
|
516
|
+
--color-category-fuchsia-surface: var(--app-category-fuchsia-surface);
|
|
517
|
+
--color-category-pink-content: var(--app-category-pink-content);
|
|
518
|
+
--color-category-pink-surface: var(--app-category-pink-surface);
|
|
459
519
|
|
|
460
520
|
--color-tile-brand: var(--app-tile-brand);
|
|
461
521
|
--color-tile-brand-foreground: var(--app-tile-brand-foreground);
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { defineComponent as c, openBlock as n, createElementBlock as s, normalizeClass as a, createCommentVNode as u, renderSlot as d } from "vue";
|
|
2
|
-
const l = /* @__PURE__ */ c({
|
|
3
|
-
__name: "Badge",
|
|
4
|
-
props: {
|
|
5
|
-
tone: { default: "neutral" },
|
|
6
|
-
dot: { type: Boolean, default: !1 }
|
|
7
|
-
},
|
|
8
|
-
setup(o) {
|
|
9
|
-
const t = o, e = {
|
|
10
|
-
neutral: { badge: "bg-status-neutral-surface text-status-neutral-content", dot: "bg-status-neutral-content" },
|
|
11
|
-
info: { badge: "bg-status-info-surface text-status-info-content", dot: "bg-status-info-content" },
|
|
12
|
-
positive: { badge: "bg-status-success-surface text-status-success-content", dot: "bg-status-success-content" },
|
|
13
|
-
warning: { badge: "bg-status-warning-surface text-status-warning-content", dot: "bg-status-warning-content" },
|
|
14
|
-
critical: { badge: "bg-status-danger-surface text-status-danger-content", dot: "bg-status-danger-content" },
|
|
15
|
-
context: { badge: "bg-surface-cell text-content", dot: "bg-content-disabled" }
|
|
16
|
-
};
|
|
17
|
-
return (r, g) => (n(), s("span", {
|
|
18
|
-
class: a(["inline-flex items-center gap-x-1.5 whitespace-nowrap rounded-md px-2 py-0.5 text-xs font-medium ring-1 ring-inset ring-current/20", e[t.tone].badge])
|
|
19
|
-
}, [
|
|
20
|
-
t.dot ? (n(), s("span", {
|
|
21
|
-
key: 0,
|
|
22
|
-
class: a(["size-1.5 shrink-0 rounded-full", e[t.tone].dot]),
|
|
23
|
-
"aria-hidden": "true"
|
|
24
|
-
}, null, 2)) : u("", !0),
|
|
25
|
-
d(r.$slots, "default")
|
|
26
|
-
], 2));
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
export {
|
|
30
|
-
l as _
|
|
31
|
-
};
|