@nexdom/uimed-vue 2.0.0-beta.16 → 2.0.0-beta.17
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/AGENTS.md +15 -1
- package/dist/components.d.ts +119 -42
- package/dist/components.js +220 -87
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -165,7 +165,10 @@ Use an existing composable (e.g. `use-toast`) as the reference, and deliver in t
|
|
|
165
165
|
- All `src` code is written in English. `docs` content is written in Portuguese (aimed at Brazilian users), even though file/dir names stay in English.
|
|
166
166
|
- `docs` must not mention Vuetify
|
|
167
167
|
- A new component/composable must be listed in its guide page (`docs/guide/…`), its API page (`docs/api/…`), both sidebars in `docs/.vitepress/config.ts`, the guide index table (`docs/guide/index.md`) and the API index list (`docs/api/index.md`).
|
|
168
|
-
- Never write CSS or `<style>` blocks, and expose styling to consumers only through props.
|
|
168
|
+
- Never write CSS or `<style>` blocks, and expose styling to consumers only through props.
|
|
169
|
+
- Don't add spacing to Vuetify components: no padding or margin utility classes (`pa-*`, `pt-6`, `px-4`, `ma-2`, ...) and no spacing props overriding the defaults. Vuetify already applies Material Design 3 spacing, and overriding it breaks what it handles on its own, such as button paddings and the scrollbar of scrollable content. If the default spacing looks wrong, stop and ask instead of adjusting it.
|
|
170
|
+
- Prefer a Vuetify component's own props to building its parts by hand (e.g. `title`/`text` on `v-card` instead of `v-card-item` > `v-card-title` > `v-card-text`). A non-spacing utility class is acceptable only when no prop covers the need (e.g. `text-wrap` on a card title), with a comment saying why.
|
|
171
|
+
- Keep components thin: a component wires props, slots and events to Vuetify. Logic beyond that (focus management, queues, DOM lookups, comparisons) goes into an internal composable with its own unit tests.
|
|
169
172
|
- Path aliases: `@/*` → `src/*`, `@e2e/*` → `e2e/*`.
|
|
170
173
|
- Every public component follows this pattern to block access to internals and give it an editor-hover description:
|
|
171
174
|
|
|
@@ -200,8 +203,19 @@ Use an existing composable (e.g. `use-toast`) as the reference, and deliver in t
|
|
|
200
203
|
- With fake timers in jsdom, Vuetify transitions (e.g. of `VDialog`, `VMenu`) don't finish on their own. Emit the transition events on the Vuetify component (`wrapper.findComponent(VDialog).vm.$emit("afterLeave")`) or advance the timers with `await vi.runAllTimersAsync()`.
|
|
201
204
|
- Coverage threshold is 100%; mutation testing threshold is 100% (break at 100). Don't add code paths without covering tests. Mutants that don't compile are reported as compile errors and don't count toward the score.
|
|
202
205
|
- E2E tests (Playwright, `e2e/`) run against the built docs preview (`http://localhost:4173/uimed-vue/`). Snapshots/screenshots live under `__snapshots__`/`__screenshot__` next to each spec.
|
|
206
|
+
- Before keeping a new screenshot as a baseline, open it and check it against how plain Vuetify renders the same component: overflowing content shows its scrollbar, buttons and cards keep their native paddings, nothing touches the edges or gets clipped. A screenshot that only "looks fine" isn't enough: name what you checked in your report.
|
|
207
|
+
- Never run Stryker at the same time as another test command: they share build output and Stryker fails with `ENOENT`.
|
|
208
|
+
- Unit tests that mount several apps at once (e.g. nested dialogs) need a distinct `global.config.idPrefix` per mount, since `useId()` restarts in each app.
|
|
203
209
|
- In E2E, a paused `page.clock` also freezes transitions, so overlays never finish leaving. Use `page.clock.pauseAt` to hold time-based work (e.g. a demo action with `setTimeout`), `page.clock.fastForward` to complete it, then `page.clock.resume()` before expecting the overlay to be hidden (see `e2e/composables/use-confirm.spec.ts`).
|
|
204
210
|
|
|
211
|
+
## Public API design
|
|
212
|
+
|
|
213
|
+
The public API is hard to change once released, and every prop, event or option is something consumers can misuse and maintainers must keep. Keep it minimal:
|
|
214
|
+
|
|
215
|
+
- Only add what the issue asks for. Anything beyond it (an extra prop, event, option or variant) is a separate decision the maintainers must approve explicitly, not a default.
|
|
216
|
+
- Behaviors meant to be consistent across the library aren't per-instance options. For example, every dialog closes with `Esc`, a click outside it or the browser's back button; a flag to block that stays internal.
|
|
217
|
+
- Reuse existing contracts before creating new ones: `v-model` for open/closed state instead of extra lifecycle events such as `afterLeave`, the `actions` shape of `USection` for buttons, the option names of existing composables.
|
|
218
|
+
|
|
205
219
|
## Library documentation and dependencies
|
|
206
220
|
|
|
207
221
|
The stack is newer than most AI models' training data (Vue 3.5, Vuetify 4, Vue Router 5, Vite+ 0.2, Vitest 4, TypeScript 6, VitePress 2 alpha, Stryker 10, Playwright 1.62), so don't rely on memory for library APIs.
|
package/dist/components.d.ts
CHANGED
|
@@ -182,8 +182,8 @@ type MainProps = {
|
|
|
182
182
|
*
|
|
183
183
|
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/main | Main Guide}
|
|
184
184
|
*/
|
|
185
|
-
declare const _default$
|
|
186
|
-
declare const __VLS_export$
|
|
185
|
+
declare const _default$11: typeof __VLS_export$16;
|
|
186
|
+
declare const __VLS_export$16: __VLS_WithSlots$10<import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<MainProps & {
|
|
187
187
|
notificationsOpen?: boolean;
|
|
188
188
|
}, {}, {}, {}, {}, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {
|
|
189
189
|
"update:notificationsOpen": (value: boolean) => any;
|
|
@@ -194,7 +194,7 @@ declare const __VLS_export$15: __VLS_WithSlots$9<import("vue", { with: { "resolu
|
|
|
194
194
|
}>, {}, {}, {}, {}, string, import("vue", { with: { "resolution-mode": "import" } }).ComponentProvideOptions, false, {}, any>, {
|
|
195
195
|
default?: (props: {}) => any;
|
|
196
196
|
}>;
|
|
197
|
-
type __VLS_WithSlots$
|
|
197
|
+
type __VLS_WithSlots$10<T, S> = T & {
|
|
198
198
|
new (): {
|
|
199
199
|
$slots: S;
|
|
200
200
|
};
|
|
@@ -215,15 +215,15 @@ type __VLS_WithSlots$9<T, S> = T & {
|
|
|
215
215
|
*
|
|
216
216
|
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/button | Button Guide}
|
|
217
217
|
*/
|
|
218
|
-
declare const _default$1: typeof __VLS_export$
|
|
219
|
-
declare const __VLS_export$
|
|
218
|
+
declare const _default$1: typeof __VLS_export$15;
|
|
219
|
+
declare const __VLS_export$15: __VLS_WithSlots$9<import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<ButtonProps, {}, {}, {}, {}, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {} & {
|
|
220
220
|
click: (event: MouseEvent) => any;
|
|
221
221
|
}, string, import("vue", { with: { "resolution-mode": "import" } }).PublicProps, Readonly<ButtonProps> & Readonly<{
|
|
222
222
|
onClick?: ((event: MouseEvent) => any) | undefined;
|
|
223
223
|
}>, {}, {}, {}, {}, string, import("vue", { with: { "resolution-mode": "import" } }).ComponentProvideOptions, false, {}, any>, {
|
|
224
224
|
default?: (props: {}) => any;
|
|
225
225
|
}>;
|
|
226
|
-
type __VLS_WithSlots$
|
|
226
|
+
type __VLS_WithSlots$9<T, S> = T & {
|
|
227
227
|
new (): {
|
|
228
228
|
$slots: S;
|
|
229
229
|
};
|
|
@@ -387,8 +387,8 @@ type AutocompleteFieldProps<T = string, Multiple extends boolean = false> = {
|
|
|
387
387
|
*
|
|
388
388
|
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/autocomplete-field | AutocompleteField Guide}
|
|
389
389
|
*/
|
|
390
|
-
declare const _default: typeof __VLS_export$
|
|
391
|
-
declare const __VLS_export$
|
|
390
|
+
declare const _default: typeof __VLS_export$14;
|
|
391
|
+
declare const __VLS_export$14: <T = string, Multiple extends boolean = false>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal$2<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
392
392
|
props: import("vue").PublicProps & __VLS_PrettifyLocal$2<(AutocompleteFieldProps<T, Multiple> & {
|
|
393
393
|
modelValue?: Multiple extends true ? T[] : T;
|
|
394
394
|
}) & {
|
|
@@ -418,8 +418,8 @@ type __VLS_PrettifyLocal$2<T> = (T extends any ? { [K in keyof T]: T[K]; } : { [
|
|
|
418
418
|
*
|
|
419
419
|
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/text-field | TextField Guide}
|
|
420
420
|
*/
|
|
421
|
-
declare const _default$
|
|
422
|
-
declare const __VLS_export$
|
|
421
|
+
declare const _default$16: typeof __VLS_export$13;
|
|
422
|
+
declare const __VLS_export$13: import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<TextFieldProps & {
|
|
423
423
|
modelValue?: string;
|
|
424
424
|
}, {}, {}, {}, {}, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {
|
|
425
425
|
"update:modelValue": (value: string) => any;
|
|
@@ -481,8 +481,8 @@ type CheckboxProps<T = boolean> = {
|
|
|
481
481
|
*
|
|
482
482
|
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/checkbox | Checkbox Guide}
|
|
483
483
|
*/
|
|
484
|
-
declare const _default$2: typeof __VLS_export$
|
|
485
|
-
declare const __VLS_export$
|
|
484
|
+
declare const _default$2: typeof __VLS_export$12;
|
|
485
|
+
declare const __VLS_export$12: <T = any>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal$1<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
486
486
|
props: import("vue").PublicProps & __VLS_PrettifyLocal$1<(CheckboxProps<boolean | T> & {
|
|
487
487
|
modelValue?: T;
|
|
488
488
|
}) & {
|
|
@@ -596,8 +596,8 @@ type DateTimeFieldProps = {
|
|
|
596
596
|
*
|
|
597
597
|
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/date-time-field | DateTimeField Guide}
|
|
598
598
|
*/
|
|
599
|
-
declare const _default$7: typeof __VLS_export$
|
|
600
|
-
declare const __VLS_export$
|
|
599
|
+
declare const _default$7: typeof __VLS_export$11;
|
|
600
|
+
declare const __VLS_export$11: import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<DateTimeFieldProps & {
|
|
601
601
|
modelValue?: string;
|
|
602
602
|
}, {}, {}, {}, {}, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {
|
|
603
603
|
"update:modelValue": (value: string) => any;
|
|
@@ -636,11 +636,11 @@ type ContainerProps = {
|
|
|
636
636
|
*
|
|
637
637
|
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/layout | Container Guide}
|
|
638
638
|
*/
|
|
639
|
-
declare const _default$4: typeof __VLS_export$
|
|
640
|
-
declare const __VLS_export$
|
|
639
|
+
declare const _default$4: typeof __VLS_export$10;
|
|
640
|
+
declare const __VLS_export$10: __VLS_WithSlots$8<import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<ContainerProps, {}, {}, {}, {}, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {}, string, import("vue", { with: { "resolution-mode": "import" } }).PublicProps, Readonly<ContainerProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue", { with: { "resolution-mode": "import" } }).ComponentProvideOptions, false, {}, any>, {
|
|
641
641
|
default?: (props: {}) => any;
|
|
642
642
|
}>;
|
|
643
|
-
type __VLS_WithSlots$
|
|
643
|
+
type __VLS_WithSlots$8<T, S> = T & {
|
|
644
644
|
new (): {
|
|
645
645
|
$slots: S;
|
|
646
646
|
};
|
|
@@ -682,11 +682,11 @@ type RowProps = {
|
|
|
682
682
|
*
|
|
683
683
|
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/layout | Row Guide}
|
|
684
684
|
*/
|
|
685
|
-
declare const _default$
|
|
686
|
-
declare const __VLS_export$
|
|
685
|
+
declare const _default$12: typeof __VLS_export$9;
|
|
686
|
+
declare const __VLS_export$9: __VLS_WithSlots$7<import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<RowProps, {}, {}, {}, {}, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {}, string, import("vue", { with: { "resolution-mode": "import" } }).PublicProps, Readonly<RowProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue", { with: { "resolution-mode": "import" } }).ComponentProvideOptions, false, {}, any>, {
|
|
687
687
|
default?: (props: {}) => any;
|
|
688
688
|
}>;
|
|
689
|
-
type __VLS_WithSlots$
|
|
689
|
+
type __VLS_WithSlots$7<T, S> = T & {
|
|
690
690
|
new (): {
|
|
691
691
|
$slots: S;
|
|
692
692
|
};
|
|
@@ -738,13 +738,13 @@ type Cols = "auto" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "10"
|
|
|
738
738
|
*
|
|
739
739
|
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/layout | Column Guide}
|
|
740
740
|
*/
|
|
741
|
-
declare const _default$3: typeof __VLS_export$
|
|
742
|
-
declare const __VLS_export$
|
|
741
|
+
declare const _default$3: typeof __VLS_export$8;
|
|
742
|
+
declare const __VLS_export$8: __VLS_WithSlots$6<import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<ColumnProps, {}, {}, {}, {}, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {}, string, import("vue", { with: { "resolution-mode": "import" } }).PublicProps, Readonly<ColumnProps> & Readonly<{}>, {
|
|
743
743
|
cols: "12" | "auto" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "10" | "11";
|
|
744
744
|
}, {}, {}, {}, string, import("vue", { with: { "resolution-mode": "import" } }).ComponentProvideOptions, false, {}, any>, {
|
|
745
745
|
default?: (props: {}) => any;
|
|
746
746
|
}>;
|
|
747
|
-
type __VLS_WithSlots$
|
|
747
|
+
type __VLS_WithSlots$6<T, S> = T & {
|
|
748
748
|
new (): {
|
|
749
749
|
$slots: S;
|
|
750
750
|
};
|
|
@@ -781,15 +781,15 @@ type FormProps = {
|
|
|
781
781
|
*
|
|
782
782
|
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/form | Form Guide}
|
|
783
783
|
*/
|
|
784
|
-
declare const _default$
|
|
785
|
-
declare const __VLS_export$
|
|
784
|
+
declare const _default$10: typeof __VLS_export$7;
|
|
785
|
+
declare const __VLS_export$7: __VLS_WithSlots$5<import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<FormProps, {}, {}, {}, {}, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {} & {
|
|
786
786
|
submit: (event: SubmitEvent) => any;
|
|
787
787
|
}, string, import("vue", { with: { "resolution-mode": "import" } }).PublicProps, Readonly<FormProps> & Readonly<{
|
|
788
788
|
onSubmit?: ((event: SubmitEvent) => any) | undefined;
|
|
789
789
|
}>, {}, {}, {}, {}, string, import("vue", { with: { "resolution-mode": "import" } }).ComponentProvideOptions, false, {}, any>, {
|
|
790
790
|
default?: (props: {}) => any;
|
|
791
791
|
}>;
|
|
792
|
-
type __VLS_WithSlots$
|
|
792
|
+
type __VLS_WithSlots$5<T, S> = T & {
|
|
793
793
|
new (): {
|
|
794
794
|
$slots: S;
|
|
795
795
|
};
|
|
@@ -811,11 +811,11 @@ type __VLS_WithSlots$4<T, S> = T & {
|
|
|
811
811
|
*
|
|
812
812
|
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/section | Section Guide}
|
|
813
813
|
*/
|
|
814
|
-
declare const _default$
|
|
815
|
-
declare const __VLS_export$
|
|
814
|
+
declare const _default$14: typeof __VLS_export$6;
|
|
815
|
+
declare const __VLS_export$6: __VLS_WithSlots$4<import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<{}, {}, {}, {}, {}, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {}, string, import("vue", { with: { "resolution-mode": "import" } }).PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue", { with: { "resolution-mode": "import" } }).ComponentProvideOptions, true, {}, any>, {
|
|
816
816
|
default?: (props: {}) => any;
|
|
817
817
|
}>;
|
|
818
|
-
type __VLS_WithSlots$
|
|
818
|
+
type __VLS_WithSlots$4<T, S> = T & {
|
|
819
819
|
new (): {
|
|
820
820
|
$slots: S;
|
|
821
821
|
};
|
|
@@ -897,11 +897,11 @@ interface SectionAction extends ButtonProps {
|
|
|
897
897
|
*
|
|
898
898
|
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/section | Section Guide}
|
|
899
899
|
*/
|
|
900
|
-
declare const _default$
|
|
901
|
-
declare const __VLS_export$
|
|
900
|
+
declare const _default$13: typeof __VLS_export$5;
|
|
901
|
+
declare const __VLS_export$5: __VLS_WithSlots$3<import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<SectionProps, {}, {}, {}, {}, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {}, string, import("vue", { with: { "resolution-mode": "import" } }).PublicProps, Readonly<SectionProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue", { with: { "resolution-mode": "import" } }).ComponentProvideOptions, false, {}, any>, {
|
|
902
902
|
default?: (props: {}) => any;
|
|
903
903
|
}>;
|
|
904
|
-
type __VLS_WithSlots$
|
|
904
|
+
type __VLS_WithSlots$3<T, S> = T & {
|
|
905
905
|
new (): {
|
|
906
906
|
$slots: S;
|
|
907
907
|
};
|
|
@@ -972,8 +972,8 @@ type TableProps = {
|
|
|
972
972
|
*
|
|
973
973
|
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/table | Table Guide}
|
|
974
974
|
*/
|
|
975
|
-
declare const _default$
|
|
976
|
-
declare const __VLS_export$
|
|
975
|
+
declare const _default$15: typeof __VLS_export$4;
|
|
976
|
+
declare const __VLS_export$4: import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<TableProps, {}, {}, {}, {}, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {}, string, import("vue", { with: { "resolution-mode": "import" } }).PublicProps, Readonly<TableProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue", { with: { "resolution-mode": "import" } }).ComponentProvideOptions, false, {}, any>;
|
|
977
977
|
//#endregion
|
|
978
978
|
//#region src/components/details/types.d.ts
|
|
979
979
|
type DetailsVariant = "primary" | "secondary";
|
|
@@ -1020,11 +1020,11 @@ type DetailsProps = {
|
|
|
1020
1020
|
*
|
|
1021
1021
|
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/details | Details Guide}
|
|
1022
1022
|
*/
|
|
1023
|
-
declare const _default$8: typeof __VLS_export$
|
|
1024
|
-
declare const __VLS_export$
|
|
1023
|
+
declare const _default$8: typeof __VLS_export$3;
|
|
1024
|
+
declare const __VLS_export$3: __VLS_WithSlots$2<import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<DetailsProps, {}, {}, {}, {}, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {}, string, import("vue", { with: { "resolution-mode": "import" } }).PublicProps, Readonly<DetailsProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue", { with: { "resolution-mode": "import" } }).ComponentProvideOptions, false, {}, any>, {
|
|
1025
1025
|
default?: (props: {}) => any;
|
|
1026
1026
|
}>;
|
|
1027
|
-
type __VLS_WithSlots$
|
|
1027
|
+
type __VLS_WithSlots$2<T, S> = T & {
|
|
1028
1028
|
new (): {
|
|
1029
1029
|
$slots: S;
|
|
1030
1030
|
};
|
|
@@ -1125,8 +1125,8 @@ type DataSetSlots<T extends object> = {
|
|
|
1125
1125
|
*
|
|
1126
1126
|
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/data-set | DataSet Guide}
|
|
1127
1127
|
*/
|
|
1128
|
-
declare const _default$5: typeof __VLS_export$
|
|
1129
|
-
declare const __VLS_export$
|
|
1128
|
+
declare const _default$5: typeof __VLS_export$2;
|
|
1129
|
+
declare const __VLS_export$2: <T extends object>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
1130
1130
|
props: import("vue").PublicProps & __VLS_PrettifyLocal<(DataSetProps<T> & {
|
|
1131
1131
|
page?: number;
|
|
1132
1132
|
search?: string;
|
|
@@ -1201,12 +1201,89 @@ type DataSetItemSlots = {
|
|
|
1201
1201
|
*
|
|
1202
1202
|
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/data-set | DataSet Guide}
|
|
1203
1203
|
*/
|
|
1204
|
-
declare const _default$6: typeof __VLS_export;
|
|
1205
|
-
declare const __VLS_export: __VLS_WithSlots<import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<DataSetItemProps, {}, {}, {}, {}, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {}, string, import("vue", { with: { "resolution-mode": "import" } }).PublicProps, Readonly<DataSetItemProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue", { with: { "resolution-mode": "import" } }).ComponentProvideOptions, false, {}, any>, DataSetItemSlots>;
|
|
1204
|
+
declare const _default$6: typeof __VLS_export$1;
|
|
1205
|
+
declare const __VLS_export$1: __VLS_WithSlots$1<import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<DataSetItemProps, {}, {}, {}, {}, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {}, string, import("vue", { with: { "resolution-mode": "import" } }).PublicProps, Readonly<DataSetItemProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue", { with: { "resolution-mode": "import" } }).ComponentProvideOptions, false, {}, any>, DataSetItemSlots>;
|
|
1206
|
+
type __VLS_WithSlots$1<T, S> = T & {
|
|
1207
|
+
new (): {
|
|
1208
|
+
$slots: S;
|
|
1209
|
+
};
|
|
1210
|
+
};
|
|
1211
|
+
//#endregion
|
|
1212
|
+
//#region src/components/dialogs/dialog/types.d.ts
|
|
1213
|
+
type DialogSize = "small" | "medium" | "large";
|
|
1214
|
+
/**
|
|
1215
|
+
* Props exposed by the {@link Dialog} component.
|
|
1216
|
+
*/
|
|
1217
|
+
type DialogProps = {
|
|
1218
|
+
/**
|
|
1219
|
+
* Plain text displayed as the dialog's title, in a single line. HTML isn't interpreted.
|
|
1220
|
+
* Also used as the dialog's accessible name. Without it, the content is the accessible name.
|
|
1221
|
+
*/
|
|
1222
|
+
title?: string;
|
|
1223
|
+
/**
|
|
1224
|
+
* Buttons displayed at the bottom of the dialog, right-aligned, in the given order.
|
|
1225
|
+
* Place the main action last. When undefined or empty, no actions area is rendered.
|
|
1226
|
+
* Clicking an action doesn't close the dialog: set `v-model` to `false` in `onClick`.
|
|
1227
|
+
*/
|
|
1228
|
+
actions?: DialogButtonAction[];
|
|
1229
|
+
/**
|
|
1230
|
+
* Maximum width of the dialog.
|
|
1231
|
+
* One of `small` (400px), `medium` (560px), or `large` (800px).
|
|
1232
|
+
* @default "medium"
|
|
1233
|
+
*/
|
|
1234
|
+
size?: DialogSize;
|
|
1235
|
+
/**
|
|
1236
|
+
* Component id to use on automated tests.
|
|
1237
|
+
*/
|
|
1238
|
+
dataTestid?: string;
|
|
1239
|
+
};
|
|
1240
|
+
/**
|
|
1241
|
+
* A single action rendered inside {@link Dialog}'s actions area. Same shape as `USection`'s
|
|
1242
|
+
* actions.
|
|
1243
|
+
*/
|
|
1244
|
+
interface DialogButtonAction extends ButtonProps {
|
|
1245
|
+
/**
|
|
1246
|
+
* Text displayed on the action button.
|
|
1247
|
+
*/
|
|
1248
|
+
label: string;
|
|
1249
|
+
/**
|
|
1250
|
+
* Called when the action button is clicked. The dialog stays open.
|
|
1251
|
+
*/
|
|
1252
|
+
onClick?: (event: MouseEvent) => void;
|
|
1253
|
+
}
|
|
1254
|
+
//#endregion
|
|
1255
|
+
//#region src/components/dialogs/dialog/dialog.vue.d.ts
|
|
1256
|
+
/**
|
|
1257
|
+
* Dialog component to display content in a modal window, over the page, until it's closed.
|
|
1258
|
+
* Its title and actions stay in place while the content scrolls.
|
|
1259
|
+
*
|
|
1260
|
+
* @example
|
|
1261
|
+
* ```vue
|
|
1262
|
+
* <template>
|
|
1263
|
+
* <u-dialog v-model="open" title="Title" :actions="[{ label: 'Close', onClick: () => (open = false) }]">
|
|
1264
|
+
* Content
|
|
1265
|
+
* </u-dialog>
|
|
1266
|
+
* </template>
|
|
1267
|
+
* ```
|
|
1268
|
+
*
|
|
1269
|
+
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/dialog | Dialog Guide}
|
|
1270
|
+
*/
|
|
1271
|
+
declare const _default$9: typeof __VLS_export;
|
|
1272
|
+
declare const __VLS_export: __VLS_WithSlots<import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<DialogProps & {
|
|
1273
|
+
modelValue?: boolean;
|
|
1274
|
+
}, {}, {}, {}, {}, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {
|
|
1275
|
+
"update:modelValue": (value: boolean) => any;
|
|
1276
|
+
}, string, import("vue", { with: { "resolution-mode": "import" } }).PublicProps, Readonly<DialogProps & {
|
|
1277
|
+
modelValue?: boolean;
|
|
1278
|
+
}> & Readonly<{
|
|
1279
|
+
"onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
|
|
1280
|
+
}>, {}, {}, {}, {}, string, import("vue", { with: { "resolution-mode": "import" } }).ComponentProvideOptions, false, {}, any>, {
|
|
1281
|
+
default?: (props: {}) => any;
|
|
1282
|
+
}>;
|
|
1206
1283
|
type __VLS_WithSlots<T, S> = T & {
|
|
1207
1284
|
new (): {
|
|
1208
1285
|
$slots: S;
|
|
1209
1286
|
};
|
|
1210
1287
|
};
|
|
1211
1288
|
//#endregion
|
|
1212
|
-
export { _default as UAutocompleteField, _default$1 as UButton, _default$2 as UCheckbox, _default$3 as UColumn, _default$4 as UContainer, _default$5 as UDataSet, _default$6 as UDataSetItem, _default$7 as UDateTimeField, _default$8 as UDetails, _default$9 as
|
|
1289
|
+
export { _default as UAutocompleteField, _default$1 as UButton, _default$2 as UCheckbox, _default$3 as UColumn, _default$4 as UContainer, _default$5 as UDataSet, _default$6 as UDataSetItem, _default$7 as UDateTimeField, _default$8 as UDetails, _default$9 as UDialog, _default$10 as UForm, _default$11 as UMain, _default$12 as URow, _default$13 as USection, _default$14 as USectionContent, _default$15 as UTable, _default$16 as UTextField };
|
package/dist/components.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as messages, n as owners, r as requests, s as useVuetifyColor } from "./use-dialog-Ck2_ZW7T.js";
|
|
2
|
-
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, isRef, mergeModels, mergeProps, normalizeClass, onMounted, onScopeDispose, onUnmounted, openBlock, ref, renderList, renderSlot, resolveDynamicComponent, shallowRef, toDisplayString, toValue, unref, useId, useModel, useSlots, useTemplateRef, watch, withCtx, withModifiers } from "vue";
|
|
2
|
+
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, inject, isRef, mergeModels, mergeProps, normalizeClass, onMounted, onScopeDispose, onUnmounted, openBlock, provide, ref, renderList, renderSlot, resolveDynamicComponent, shallowRef, toDisplayString, toValue, unref, useId, useModel, useSlots, useTemplateRef, watch, withCtx, withModifiers } from "vue";
|
|
3
3
|
import { pt } from "vuetify/locale";
|
|
4
|
-
import { VApp, VAppBar, VAppBarNavIcon, VAppBarTitle, VAutocomplete, VAvatar, VBadge, VBtn, VCard, VCardActions,
|
|
4
|
+
import { VApp, VAppBar, VAppBarNavIcon, VAppBarTitle, VAutocomplete, VAvatar, VBadge, VBtn, VCard, VCardActions, VCardText, VCheckboxBtn, VCol, VCombobox, VContainer, VDataIterator, VDatePicker, VDialog, VDivider, VEmptyState, VExpansionPanel, VExpansionPanelText, VExpansionPanelTitle, VExpansionPanels, VForm, VIcon, VIconBtn, VImg, VList, VListGroup, VListItem, VListItemAction, VListSubheader, VLocaleProvider, VMain, VMenu, VNavigationDrawer, VPagination, VRow, VSkeletonLoader, VSnackbarQueue, VTable, VTextField, VTimePicker } from "vuetify/components";
|
|
5
5
|
import { formatDateTime, isEmail, isEmpty, isPhone, isUrl } from "@nexdom/shared/utils";
|
|
6
6
|
//#region src/composables/navigation/use-route-props.ts
|
|
7
7
|
function useRouteProps(route) {
|
|
@@ -16,7 +16,7 @@ function isExternalRoute(route) {
|
|
|
16
16
|
}
|
|
17
17
|
//#endregion
|
|
18
18
|
//#region src/components/app-bar/app-bar.vue?vue&type=script&setup=true&lang.ts
|
|
19
|
-
const _hoisted_1$
|
|
19
|
+
const _hoisted_1$3 = { class: "opacity-60" };
|
|
20
20
|
//#endregion
|
|
21
21
|
//#region src/components/app-bar/app-bar.vue
|
|
22
22
|
var app_bar_default = /* @__PURE__ */ defineComponent({
|
|
@@ -137,7 +137,7 @@ var app_bar_default = /* @__PURE__ */ defineComponent({
|
|
|
137
137
|
}, createSlots({ _: 2 }, [notification.when ? {
|
|
138
138
|
name: "append",
|
|
139
139
|
fn: withCtx(() => [createVNode(unref(VListItemAction), { end: "" }, {
|
|
140
|
-
default: withCtx(() => [createElementVNode("small", _hoisted_1$
|
|
140
|
+
default: withCtx(() => [createElementVNode("small", _hoisted_1$3, toDisplayString(notification.when), 1)]),
|
|
141
141
|
_: 2
|
|
142
142
|
}, 1024)]),
|
|
143
143
|
key: "0"
|
|
@@ -412,7 +412,7 @@ var text_field_default = /* @__PURE__ */ defineComponent({
|
|
|
412
412
|
});
|
|
413
413
|
//#endregion
|
|
414
414
|
//#region src/components/navigation-menu/navigation-menu.vue?vue&type=script&setup=true&lang.ts
|
|
415
|
-
const _hoisted_1$
|
|
415
|
+
const _hoisted_1$2 = { class: "px-4 pt-2" };
|
|
416
416
|
//#endregion
|
|
417
417
|
//#region src/components/navigation-menu/navigation-menu.vue
|
|
418
418
|
var navigation_menu_default = /* @__PURE__ */ defineComponent({
|
|
@@ -475,7 +475,7 @@ var navigation_menu_default = /* @__PURE__ */ defineComponent({
|
|
|
475
475
|
absolute: "",
|
|
476
476
|
temporary: ""
|
|
477
477
|
}, {
|
|
478
|
-
prepend: withCtx(() => [createElementVNode("div", _hoisted_1$
|
|
478
|
+
prepend: withCtx(() => [createElementVNode("div", _hoisted_1$2, [createVNode(text_field_default, {
|
|
479
479
|
modelValue: search.value,
|
|
480
480
|
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => search.value = $event),
|
|
481
481
|
type: "search",
|
|
@@ -664,31 +664,225 @@ var button_default = /* @__PURE__ */ defineComponent({
|
|
|
664
664
|
}
|
|
665
665
|
});
|
|
666
666
|
//#endregion
|
|
667
|
-
//#region src/components/dialogs/dialog.
|
|
667
|
+
//#region src/components/dialogs/dialog/dialog-host-key.ts
|
|
668
|
+
/**
|
|
669
|
+
* Injection key of {@link DialogHostContext}, provided by the internal `DialogHost`. Besides the
|
|
670
|
+
* role, the blocking while an action runs and the end of the leave transitions, it makes the
|
|
671
|
+
* dialog's content describe the dialog (`aria-describedby`), since `DialogHost` only displays text
|
|
672
|
+
* messages. Not part of the public API.
|
|
673
|
+
*/
|
|
674
|
+
const dialogHostKey = Symbol("dialog-host");
|
|
675
|
+
//#endregion
|
|
676
|
+
//#region src/composables/dialogs/use-dialog-focus.ts
|
|
677
|
+
/** Focus state of the closed dialogs, by their card, for the dialogs opened from them. */
|
|
678
|
+
const closedDialogs = /* @__PURE__ */ new WeakMap();
|
|
679
|
+
/** Elements that can get the focus when the dialog opens. The first one in the dialog gets it. */
|
|
680
|
+
const focusable = [
|
|
681
|
+
"button:not([disabled])",
|
|
682
|
+
"[href]",
|
|
683
|
+
"input:not([disabled]):not([type=\"hidden\"])",
|
|
684
|
+
"select:not([disabled])",
|
|
685
|
+
"textarea:not([disabled])",
|
|
686
|
+
"[tabindex]:not([tabindex=\"-1\"])"
|
|
687
|
+
].join(", ");
|
|
688
|
+
/**
|
|
689
|
+
* Moves the focus into a dialog once it opens and back to where it was when it closes, including
|
|
690
|
+
* when dialogs are opened from others. Internal to the dialog components.
|
|
691
|
+
* @param isActive - Whether the dialog is displayed. It turns `false` as soon as the dialog starts
|
|
692
|
+
* leaving, so the focus returns right away.
|
|
693
|
+
* @param card - The dialog's card, marked with `data-u-dialog`, or nothing while it isn't rendered.
|
|
694
|
+
* @returns `focusFirstElement`, to call once the dialog finishes entering.
|
|
695
|
+
*/
|
|
696
|
+
function useDialogFocus(isActive, card) {
|
|
697
|
+
/** Focus state of this dialog, shared with the dialogs opened from it. */
|
|
698
|
+
const focusState = {
|
|
699
|
+
isOpen: isActive,
|
|
700
|
+
origin: null
|
|
701
|
+
};
|
|
702
|
+
onMounted(() => onActiveChange(isActive()));
|
|
703
|
+
watch(isActive, onActiveChange);
|
|
704
|
+
function onActiveChange(active) {
|
|
705
|
+
if (active) {
|
|
706
|
+
focusState.origin = document.activeElement;
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
const element = card();
|
|
710
|
+
if (element) closedDialogs.set(element, focusState);
|
|
711
|
+
const target = findFocusTarget(focusState.origin);
|
|
712
|
+
if (hasFocus(element) && (target instanceof HTMLElement || target instanceof SVGElement)) target.focus();
|
|
713
|
+
}
|
|
714
|
+
function focusFirstElement() {
|
|
715
|
+
card()?.querySelector(focusable)?.focus();
|
|
716
|
+
}
|
|
717
|
+
return { focusFirstElement };
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Element that gets the focus back when a dialog closes: its origin or, when the origin is inside
|
|
721
|
+
* another dialog that closed meanwhile (and can't get the focus anymore), that dialog's.
|
|
722
|
+
*/
|
|
723
|
+
function findFocusTarget(origin) {
|
|
724
|
+
const card = origin?.closest("[data-u-dialog]");
|
|
725
|
+
const owner = card && closedDialogs.get(card);
|
|
726
|
+
return owner && !owner.isOpen() ? findFocusTarget(owner.origin) : origin;
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Whether the focus is on the dialog or lost (on nothing or on the page's body), so it can move.
|
|
730
|
+
* Otherwise it moved elsewhere, e.g. to another dialog opened over this one.
|
|
731
|
+
*/
|
|
732
|
+
function hasFocus(card) {
|
|
733
|
+
const active = document.activeElement;
|
|
734
|
+
if (!active) return true;
|
|
735
|
+
return !!card && (card.contains(active) || active.contains(card));
|
|
736
|
+
}
|
|
737
|
+
//#endregion
|
|
738
|
+
//#region src/components/dialogs/dialog/dialog.vue?vue&type=script&setup=true&lang.ts
|
|
739
|
+
const _hoisted_1$1 = ["id"];
|
|
740
|
+
//#endregion
|
|
741
|
+
//#region src/components/dialogs/dialog/dialog.vue
|
|
668
742
|
var dialog_default = /* @__PURE__ */ defineComponent({
|
|
669
743
|
inheritAttrs: false,
|
|
670
744
|
__name: "dialog",
|
|
745
|
+
props: /*@__PURE__*/ mergeModels({
|
|
746
|
+
title: {
|
|
747
|
+
type: String,
|
|
748
|
+
required: false
|
|
749
|
+
},
|
|
750
|
+
actions: {
|
|
751
|
+
type: Array,
|
|
752
|
+
required: false
|
|
753
|
+
},
|
|
754
|
+
size: {
|
|
755
|
+
type: String,
|
|
756
|
+
required: false
|
|
757
|
+
},
|
|
758
|
+
dataTestid: {
|
|
759
|
+
type: String,
|
|
760
|
+
required: false
|
|
761
|
+
}
|
|
762
|
+
}, {
|
|
763
|
+
"modelValue": {
|
|
764
|
+
type: Boolean,
|
|
765
|
+
default: false
|
|
766
|
+
},
|
|
767
|
+
"modelModifiers": {}
|
|
768
|
+
}),
|
|
769
|
+
emits: ["update:modelValue"],
|
|
770
|
+
setup(__props) {
|
|
771
|
+
const props = __props;
|
|
772
|
+
const model = useModel(__props, "modelValue");
|
|
773
|
+
const maxWidths = {
|
|
774
|
+
small: 400,
|
|
775
|
+
medium: 560,
|
|
776
|
+
large: 800
|
|
777
|
+
};
|
|
778
|
+
const host = inject(dialogHostKey, null);
|
|
779
|
+
const contentId = useId();
|
|
780
|
+
const card = useTemplateRef("card");
|
|
781
|
+
const title = computed(() => props.title || void 0);
|
|
782
|
+
const maxWidth = computed(() => maxWidths[props.size ?? "medium"]);
|
|
783
|
+
const role = computed(() => host?.role() ?? "dialog");
|
|
784
|
+
/**
|
|
785
|
+
* Whether the dialog is displayed. It follows `v-model`, but turns `false` as soon as the user
|
|
786
|
+
* closes the dialog, while `v-model` only does once the leave transition ends.
|
|
787
|
+
*/
|
|
788
|
+
const isActive = ref(model.value);
|
|
789
|
+
watch(model, (value) => isActive.value = value);
|
|
790
|
+
const { focusFirstElement } = useDialogFocus(() => isActive.value, () => card.value?.$el);
|
|
791
|
+
function onAfterLeave() {
|
|
792
|
+
model.value = false;
|
|
793
|
+
host?.afterLeave();
|
|
794
|
+
}
|
|
795
|
+
return (_ctx, _cache) => {
|
|
796
|
+
return openBlock(), createBlock(unref(VDialog), {
|
|
797
|
+
modelValue: isActive.value,
|
|
798
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => isActive.value = $event),
|
|
799
|
+
persistent: unref(host)?.persistent(),
|
|
800
|
+
"max-width": maxWidth.value,
|
|
801
|
+
role: role.value,
|
|
802
|
+
"aria-label": title.value,
|
|
803
|
+
"aria-labelledby": title.value ? void 0 : unref(contentId),
|
|
804
|
+
"aria-describedby": unref(host) ? unref(contentId) : void 0,
|
|
805
|
+
"data-testid": props.dataTestid,
|
|
806
|
+
scrollable: "",
|
|
807
|
+
onAfterEnter: unref(focusFirstElement),
|
|
808
|
+
onAfterLeave
|
|
809
|
+
}, {
|
|
810
|
+
default: withCtx(() => [createCommentVNode(" `data-u-dialog` identifies the card to the dialogs opened from this one "), createVNode(unref(VCard), {
|
|
811
|
+
ref_key: "card",
|
|
812
|
+
ref: card,
|
|
813
|
+
title: title.value,
|
|
814
|
+
"data-u-dialog": ""
|
|
815
|
+
}, createSlots({
|
|
816
|
+
text: withCtx(() => [createElementVNode("div", { id: unref(contentId) }, [renderSlot(_ctx.$slots, "default")], 8, _hoisted_1$1)]),
|
|
817
|
+
_: 2
|
|
818
|
+
}, [props.actions?.length ? {
|
|
819
|
+
name: "actions",
|
|
820
|
+
fn: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(props.actions, ({ label, onClick, ...action }, index) => {
|
|
821
|
+
return openBlock(), createBlock(button_default, mergeProps({ key: index }, { ref_for: true }, action, { onClick }), {
|
|
822
|
+
default: withCtx(() => [createTextVNode(toDisplayString(label), 1)]),
|
|
823
|
+
_: 2
|
|
824
|
+
}, 1040, ["onClick"]);
|
|
825
|
+
}), 128))]),
|
|
826
|
+
key: "0"
|
|
827
|
+
} : void 0]), 1032, ["title"])]),
|
|
828
|
+
_: 3
|
|
829
|
+
}, 8, [
|
|
830
|
+
"modelValue",
|
|
831
|
+
"persistent",
|
|
832
|
+
"max-width",
|
|
833
|
+
"role",
|
|
834
|
+
"aria-label",
|
|
835
|
+
"aria-labelledby",
|
|
836
|
+
"aria-describedby",
|
|
837
|
+
"data-testid",
|
|
838
|
+
"onAfterEnter"
|
|
839
|
+
]);
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
//#endregion
|
|
844
|
+
//#region src/components/dialogs/dialog-host.vue
|
|
845
|
+
var dialog_host_default = /* @__PURE__ */ defineComponent({
|
|
846
|
+
inheritAttrs: false,
|
|
847
|
+
__name: "dialog-host",
|
|
671
848
|
setup(__props) {
|
|
672
849
|
const owner = Symbol("dialog");
|
|
673
|
-
const titleId = useId();
|
|
674
|
-
const messageId = useId();
|
|
675
|
-
const actions = useTemplateRef("actions");
|
|
676
850
|
const isOwner = computed(() => owners.value[0] === owner);
|
|
677
851
|
const next = computed(() => isOwner.value ? requests.value[0] : void 0);
|
|
678
852
|
/** Dialog on display, kept until its leave transition ends. */
|
|
679
853
|
const request = shallowRef();
|
|
854
|
+
/**
|
|
855
|
+
* Changes for every dialog on display, so each one gets its own `Dialog`, open from the start.
|
|
856
|
+
* The next dialog can then open right after a dismissed one, whose `v-model` stays `true`.
|
|
857
|
+
*/
|
|
858
|
+
const key = shallowRef();
|
|
680
859
|
const isOpen = ref(false);
|
|
681
860
|
const selected = shallowRef();
|
|
682
861
|
const isRunning = computed(() => selected.value !== void 0);
|
|
683
|
-
|
|
684
|
-
|
|
862
|
+
const actions = computed(() => {
|
|
863
|
+
const current = request.value;
|
|
864
|
+
if (!current) return void 0;
|
|
865
|
+
return current.actions.map((action) => ({
|
|
866
|
+
label: action.text,
|
|
867
|
+
variant: action.variant,
|
|
868
|
+
color: action.color,
|
|
869
|
+
loading: selected.value === action,
|
|
870
|
+
disabled: isRunning.value && selected.value !== action,
|
|
871
|
+
onClick: () => void select(current, action)
|
|
872
|
+
}));
|
|
873
|
+
});
|
|
874
|
+
provide(dialogHostKey, {
|
|
875
|
+
role: () => request.value?.role,
|
|
876
|
+
persistent: () => isRunning.value,
|
|
877
|
+
afterLeave: showNext
|
|
878
|
+
});
|
|
685
879
|
onMounted(() => owners.value.push(owner));
|
|
686
880
|
onUnmounted(() => owners.value = owners.value.filter((item) => item !== owner));
|
|
687
881
|
watch(next, show);
|
|
688
882
|
function show() {
|
|
689
883
|
if (request.value) return;
|
|
690
|
-
origin = document.activeElement;
|
|
691
884
|
request.value = next.value;
|
|
885
|
+
key.value = Symbol();
|
|
692
886
|
isOpen.value = next.value !== void 0;
|
|
693
887
|
}
|
|
694
888
|
function showNext() {
|
|
@@ -701,88 +895,27 @@ var dialog_default = /* @__PURE__ */ defineComponent({
|
|
|
701
895
|
selected.value = action;
|
|
702
896
|
await current.select(action?.value);
|
|
703
897
|
selected.value = void 0;
|
|
704
|
-
isOpen.value = false;
|
|
705
|
-
if (origin instanceof HTMLElement || origin instanceof SVGElement) origin.focus();
|
|
898
|
+
if (action) isOpen.value = false;
|
|
706
899
|
}
|
|
900
|
+
/** Called once a dialog closed by the user leaves, right before the next one is displayed. */
|
|
707
901
|
function dismiss() {
|
|
708
902
|
if (request.value) select(request.value);
|
|
709
903
|
}
|
|
710
|
-
function focusFirstAction() {
|
|
711
|
-
actions.value?.$el.querySelector("button").focus();
|
|
712
|
-
}
|
|
713
904
|
return (_ctx, _cache) => {
|
|
714
|
-
return openBlock(),
|
|
905
|
+
return openBlock(), createElementBlock(Fragment, null, [createCommentVNode(" PascalCase, since `<dialog>` in the template would render the native HTML element instead "), (openBlock(), createBlock(dialog_default, {
|
|
906
|
+
key: key.value,
|
|
715
907
|
"model-value": isOpen.value,
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
"
|
|
719
|
-
"aria-describedby": unref(messageId),
|
|
720
|
-
"max-width": "560",
|
|
721
|
-
"onUpdate:modelValue": dismiss,
|
|
722
|
-
onAfterEnter: focusFirstAction,
|
|
723
|
-
onAfterLeave: showNext
|
|
908
|
+
title: request.value?.title,
|
|
909
|
+
actions: actions.value,
|
|
910
|
+
"onUpdate:modelValue": dismiss
|
|
724
911
|
}, {
|
|
725
|
-
default: withCtx(() => [
|
|
726
|
-
default: withCtx(() => [
|
|
727
|
-
request.value.title ? (openBlock(), createBlock(unref(VCardItem), {
|
|
728
|
-
key: 0,
|
|
729
|
-
class: "pt-6"
|
|
730
|
-
}, {
|
|
731
|
-
default: withCtx(() => [createCommentVNode(" Titles wrap instead of being truncated, since they're part of the message "), createVNode(unref(VCardTitle), {
|
|
732
|
-
id: unref(titleId),
|
|
733
|
-
class: "text-wrap"
|
|
734
|
-
}, {
|
|
735
|
-
default: withCtx(() => [createTextVNode(toDisplayString(request.value.title), 1)]),
|
|
736
|
-
_: 1
|
|
737
|
-
}, 8, ["id"])]),
|
|
738
|
-
_: 1
|
|
739
|
-
})) : createCommentVNode("v-if", true),
|
|
740
|
-
createCommentVNode(" Below a title, the card already removes the message's top padding "),
|
|
741
|
-
createVNode(unref(VCardText), {
|
|
742
|
-
id: unref(messageId),
|
|
743
|
-
class: normalizeClass({ "pt-6": !request.value.title })
|
|
744
|
-
}, {
|
|
745
|
-
default: withCtx(() => [createTextVNode(toDisplayString(request.value.message), 1)]),
|
|
746
|
-
_: 1
|
|
747
|
-
}, 8, ["id", "class"]),
|
|
748
|
-
request.value.actions.length > 0 ? (openBlock(), createBlock(unref(VCardActions), {
|
|
749
|
-
key: 1,
|
|
750
|
-
ref_key: "actions",
|
|
751
|
-
ref: actions,
|
|
752
|
-
class: "px-6 pt-0 pb-6"
|
|
753
|
-
}, {
|
|
754
|
-
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(request.value.actions, (action, index) => {
|
|
755
|
-
return openBlock(), createBlock(button_default, {
|
|
756
|
-
key: index,
|
|
757
|
-
variant: action.variant,
|
|
758
|
-
color: action.color,
|
|
759
|
-
loading: selected.value === action,
|
|
760
|
-
disabled: isRunning.value && selected.value !== action,
|
|
761
|
-
onClick: ($event) => select(request.value, action)
|
|
762
|
-
}, {
|
|
763
|
-
default: withCtx(() => [createTextVNode(toDisplayString(action.text), 1)]),
|
|
764
|
-
_: 2
|
|
765
|
-
}, 1032, [
|
|
766
|
-
"variant",
|
|
767
|
-
"color",
|
|
768
|
-
"loading",
|
|
769
|
-
"disabled",
|
|
770
|
-
"onClick"
|
|
771
|
-
]);
|
|
772
|
-
}), 128))]),
|
|
773
|
-
_: 1
|
|
774
|
-
}, 512)) : createCommentVNode("v-if", true)
|
|
775
|
-
]),
|
|
776
|
-
_: 1
|
|
777
|
-
})) : createCommentVNode("v-if", true)]),
|
|
912
|
+
default: withCtx(() => [createTextVNode(toDisplayString(request.value?.message), 1)]),
|
|
778
913
|
_: 1
|
|
779
914
|
}, 8, [
|
|
780
915
|
"model-value",
|
|
781
|
-
"
|
|
782
|
-
"
|
|
783
|
-
|
|
784
|
-
"aria-describedby"
|
|
785
|
-
]);
|
|
916
|
+
"title",
|
|
917
|
+
"actions"
|
|
918
|
+
]))], 2112);
|
|
786
919
|
};
|
|
787
920
|
}
|
|
788
921
|
});
|
|
@@ -849,7 +982,7 @@ var main_default = /* @__PURE__ */ defineComponent({
|
|
|
849
982
|
default: withCtx(() => [
|
|
850
983
|
renderSlot(_ctx.$slots, "default"),
|
|
851
984
|
createVNode(toast_default),
|
|
852
|
-
createVNode(
|
|
985
|
+
createVNode(dialog_host_default)
|
|
853
986
|
]),
|
|
854
987
|
_: 3
|
|
855
988
|
})]),
|
|
@@ -1983,4 +2116,4 @@ var data_set_item_default = /* @__PURE__ */ defineComponent({
|
|
|
1983
2116
|
}
|
|
1984
2117
|
});
|
|
1985
2118
|
//#endregion
|
|
1986
|
-
export { autocomplete_field_default as UAutocompleteField, button_default as UButton, checkbox_default as UCheckbox, column_default as UColumn, container_default as UContainer, data_set_default as UDataSet, data_set_item_default as UDataSetItem, date_time_field_default as UDateTimeField, details_default as UDetails, form_default as UForm, main_default as UMain, row_default as URow, section_default as USection, section_content_default as USectionContent, table_default as UTable, text_field_default as UTextField };
|
|
2119
|
+
export { autocomplete_field_default as UAutocompleteField, button_default as UButton, checkbox_default as UCheckbox, column_default as UColumn, container_default as UContainer, data_set_default as UDataSet, data_set_item_default as UDataSetItem, date_time_field_default as UDateTimeField, details_default as UDetails, dialog_default as UDialog, form_default as UForm, main_default as UMain, row_default as URow, section_default as USection, section_content_default as USectionContent, table_default as UTable, text_field_default as UTextField };
|