nuance-ui 0.1.49 → 0.1.51
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/dist/module.json +1 -1
- package/dist/runtime/components/app-shell/app-shell-main.vue +3 -0
- package/dist/runtime/components/combobox/combobox-root.d.vue.ts +2 -2
- package/dist/runtime/components/combobox/combobox-root.vue.d.ts +2 -2
- package/dist/runtime/components/index.d.ts +1 -0
- package/dist/runtime/components/index.js +1 -0
- package/dist/runtime/components/modals/_confirm-modal/confirm-modal.d.vue.ts +28 -0
- package/dist/runtime/components/modals/_confirm-modal/confirm-modal.vue +94 -0
- package/dist/runtime/components/modals/_confirm-modal/confirm-modal.vue.d.ts +28 -0
- package/dist/runtime/components/modals/_confirm-modal/index.d.ts +8 -0
- package/dist/runtime/components/modals/_confirm-modal/index.js +6 -0
- package/dist/runtime/components/modals/index.d.ts +1 -0
- package/dist/runtime/components/modals/index.js +1 -0
- package/dist/runtime/components/modals/modal-provider.d.vue.ts +3 -0
- package/dist/runtime/components/modals/modal-provider.vue +15 -0
- package/dist/runtime/components/modals/modal-provider.vue.d.ts +3 -0
- package/dist/runtime/components/select/select.d.vue.ts +2 -2
- package/dist/runtime/components/select/select.vue.d.ts +2 -2
- package/dist/runtime/composals/index.d.ts +1 -0
- package/dist/runtime/composals/index.js +1 -0
- package/dist/runtime/composals/use-modal/docs.md +302 -0
- package/dist/runtime/composals/use-modal/index.d.ts +2 -0
- package/dist/runtime/composals/use-modal/index.js +2 -0
- package/dist/runtime/composals/use-modal/modal-manager.d.ts +83 -0
- package/dist/runtime/composals/use-modal/modal-manager.js +134 -0
- package/dist/runtime/composals/use-modal/use-modal.d.ts +27 -0
- package/dist/runtime/composals/use-modal/use-modal.js +21 -0
- package/dist/runtime/helpers/date/get-week-number.js +2 -1
- package/dist/runtime/types/index.d.ts +1 -0
- package/dist/runtime/utils/parse-theme-color/parse-theme-color.js +1 -1
- package/package.json +2 -1
package/dist/module.json
CHANGED
|
@@ -14,6 +14,9 @@ const { mod } = defineProps({
|
|
|
14
14
|
|
|
15
15
|
<style module lang="postcss">
|
|
16
16
|
.root {
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
|
|
17
20
|
min-height: 100dvh;
|
|
18
21
|
padding-top: calc(var(--app-shell-header-offset, 0rem) + var(--app-shell-padding));
|
|
19
22
|
padding-bottom: calc(var(--app-shell-footer-offset, 0rem) + var(--app-shell-padding));
|
|
@@ -25,15 +25,15 @@ type __VLS_Slots = {} & {
|
|
|
25
25
|
default?: (props: typeof __VLS_8) => any;
|
|
26
26
|
};
|
|
27
27
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
28
|
-
select: (args_0: number) => any;
|
|
29
28
|
clear: () => any;
|
|
29
|
+
select: (args_0: number) => any;
|
|
30
30
|
close: (args_0: import("./types/index.js").ComboboxDropdownEventSource) => any;
|
|
31
31
|
submit: (args_0: string, args_1: import("./types/index.js").ComboboxItem) => any;
|
|
32
32
|
open: (args_0: import("./types/index.js").ComboboxDropdownEventSource) => any;
|
|
33
33
|
"update:open": (value: boolean) => any;
|
|
34
34
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
35
|
-
onSelect?: ((args_0: number) => any) | undefined;
|
|
36
35
|
onClear?: (() => any) | undefined;
|
|
36
|
+
onSelect?: ((args_0: number) => any) | undefined;
|
|
37
37
|
onClose?: ((args_0: import("./types/index.js").ComboboxDropdownEventSource) => any) | undefined;
|
|
38
38
|
onSubmit?: ((args_0: string, args_1: import("./types/index.js").ComboboxItem) => any) | undefined;
|
|
39
39
|
onOpen?: ((args_0: import("./types/index.js").ComboboxDropdownEventSource) => any) | undefined;
|
|
@@ -25,15 +25,15 @@ type __VLS_Slots = {} & {
|
|
|
25
25
|
default?: (props: typeof __VLS_8) => any;
|
|
26
26
|
};
|
|
27
27
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
28
|
-
select: (args_0: number) => any;
|
|
29
28
|
clear: () => any;
|
|
29
|
+
select: (args_0: number) => any;
|
|
30
30
|
close: (args_0: import("./types/index.js").ComboboxDropdownEventSource) => any;
|
|
31
31
|
submit: (args_0: string, args_1: import("./types/index.js").ComboboxItem) => any;
|
|
32
32
|
open: (args_0: import("./types/index.js").ComboboxDropdownEventSource) => any;
|
|
33
33
|
"update:open": (value: boolean) => any;
|
|
34
34
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
35
|
-
onSelect?: ((args_0: number) => any) | undefined;
|
|
36
35
|
onClear?: (() => any) | undefined;
|
|
36
|
+
onSelect?: ((args_0: number) => any) | undefined;
|
|
37
37
|
onClose?: ((args_0: import("./types/index.js").ComboboxDropdownEventSource) => any) | undefined;
|
|
38
38
|
onSubmit?: ((args_0: string, args_1: import("./types/index.js").ComboboxItem) => any) | undefined;
|
|
39
39
|
onOpen?: ((args_0: import("./types/index.js").ComboboxDropdownEventSource) => any) | undefined;
|
|
@@ -20,6 +20,7 @@ export * from './input/index.js';
|
|
|
20
20
|
export * from './link/index.js';
|
|
21
21
|
export * from './loader/index.js';
|
|
22
22
|
export * from './modal/index.js';
|
|
23
|
+
export * from './modals/index.js';
|
|
23
24
|
export * from './nav-link/index.js';
|
|
24
25
|
export * from './paper.vue.js';
|
|
25
26
|
export * from './popover/index.js';
|
|
@@ -18,6 +18,7 @@ export * from "./input/index.js";
|
|
|
18
18
|
export * from "./link/index.js";
|
|
19
19
|
export * from "./loader/index.js";
|
|
20
20
|
export * from "./modal/index.js";
|
|
21
|
+
export * from "./modals/index.js";
|
|
21
22
|
export * from "./nav-link/index.js";
|
|
22
23
|
export * from "./paper.vue";
|
|
23
24
|
export * from "./popover/index.js";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { MaybePromise } from '@nui/types';
|
|
2
|
+
import type { ButtonProps } from '../../button/button.vue.js';
|
|
3
|
+
import type { ModalRootProps } from '../../modal/modal-root.vue.js';
|
|
4
|
+
type ConfirmLabels = Record<'confirm' | 'cancel', string>;
|
|
5
|
+
export interface ConfirmModalProps extends ModalRootProps {
|
|
6
|
+
/** Modal title */
|
|
7
|
+
title: string;
|
|
8
|
+
/** Description text displayed below the title */
|
|
9
|
+
description?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Custom button labels.
|
|
12
|
+
* @default { confirm: 'Confirm', cancel: 'Cancel' }
|
|
13
|
+
*/
|
|
14
|
+
labels?: ConfirmLabels;
|
|
15
|
+
/** Display variant. When `'danger'`, the confirm button turns red */
|
|
16
|
+
variant?: 'default' | 'danger';
|
|
17
|
+
/** Callback invoked on cancel */
|
|
18
|
+
onCancel?: () => void;
|
|
19
|
+
/** Callback invoked on confirm */
|
|
20
|
+
onConfirm?: () => MaybePromise<void>;
|
|
21
|
+
/** Additional props for the cancel button */
|
|
22
|
+
cancelProps?: Partial<ButtonProps>;
|
|
23
|
+
/** Additional props for the confirm button */
|
|
24
|
+
confirmProps?: Partial<ButtonProps>;
|
|
25
|
+
}
|
|
26
|
+
declare const __VLS_export: import("vue").DefineComponent<ConfirmModalProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ConfirmModalProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
27
|
+
declare const _default: typeof __VLS_export;
|
|
28
|
+
export default _default;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useModal } from "@nui/composals";
|
|
3
|
+
import { ref } from "vue";
|
|
4
|
+
import Button from "../../button/button.vue";
|
|
5
|
+
import ModalCloseButton from "../../modal/modal-close-button.vue";
|
|
6
|
+
import ModalHeader from "../../modal/modal-header.vue";
|
|
7
|
+
import ModalRoot from "../../modal/modal-root.vue";
|
|
8
|
+
import Title from "../../title.vue";
|
|
9
|
+
const {
|
|
10
|
+
title,
|
|
11
|
+
description: body,
|
|
12
|
+
labels,
|
|
13
|
+
variant = "default",
|
|
14
|
+
cancelProps,
|
|
15
|
+
confirmProps,
|
|
16
|
+
onConfirm,
|
|
17
|
+
onCancel,
|
|
18
|
+
...rest
|
|
19
|
+
} = defineProps({
|
|
20
|
+
title: { type: String, required: true },
|
|
21
|
+
description: { type: String, required: false },
|
|
22
|
+
labels: { type: Object, required: false },
|
|
23
|
+
variant: { type: String, required: false },
|
|
24
|
+
onCancel: { type: Function, required: false },
|
|
25
|
+
onConfirm: { type: Function, required: false },
|
|
26
|
+
cancelProps: { type: Object, required: false },
|
|
27
|
+
confirmProps: { type: Object, required: false },
|
|
28
|
+
centered: { type: Boolean, required: false },
|
|
29
|
+
fullScreen: { type: Boolean, required: false },
|
|
30
|
+
withinPortal: { type: Boolean, required: false },
|
|
31
|
+
yOffset: { type: void 0, required: false },
|
|
32
|
+
xOffset: { type: void 0, required: false },
|
|
33
|
+
radius: { type: [String, Number], required: false },
|
|
34
|
+
size: { type: String, required: false },
|
|
35
|
+
shadow: { type: String, required: false },
|
|
36
|
+
padding: { type: [String, Number], required: false },
|
|
37
|
+
transition: { type: String, required: false },
|
|
38
|
+
is: { type: null, required: false },
|
|
39
|
+
mod: { type: [Object, Array, null], required: false }
|
|
40
|
+
});
|
|
41
|
+
const { opened, resolve: hide } = useModal("confirm");
|
|
42
|
+
const loading = ref(false);
|
|
43
|
+
function handleCancel() {
|
|
44
|
+
hide(false);
|
|
45
|
+
onCancel?.();
|
|
46
|
+
}
|
|
47
|
+
async function hanleConfirm() {
|
|
48
|
+
try {
|
|
49
|
+
loading.value = true;
|
|
50
|
+
await onConfirm?.();
|
|
51
|
+
} finally {
|
|
52
|
+
loading.value = false;
|
|
53
|
+
hide(true);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<template>
|
|
59
|
+
<ModalRoot v-model:open='opened' v-bind='rest' size='sm'>
|
|
60
|
+
<ModalHeader>
|
|
61
|
+
<Title order='4'>
|
|
62
|
+
{{ title }}
|
|
63
|
+
</Title>
|
|
64
|
+
<ModalCloseButton />
|
|
65
|
+
</ModalHeader>
|
|
66
|
+
|
|
67
|
+
<p v-if='body' :class='$style.body'>
|
|
68
|
+
{{ body }}
|
|
69
|
+
</p>
|
|
70
|
+
|
|
71
|
+
<footer :class='$style.footer'>
|
|
72
|
+
<Button
|
|
73
|
+
variant='default'
|
|
74
|
+
v-bind='cancelProps'
|
|
75
|
+
@click='handleCancel()'
|
|
76
|
+
>
|
|
77
|
+
{{ labels?.cancel ?? "Cancel" }}
|
|
78
|
+
</Button>
|
|
79
|
+
<Button
|
|
80
|
+
variant='filled'
|
|
81
|
+
v-bind='confirmProps'
|
|
82
|
+
:color="variant === 'danger' ? 'red' : void 0"
|
|
83
|
+
:loading
|
|
84
|
+
@click='hanleConfirm()'
|
|
85
|
+
>
|
|
86
|
+
{{ labels?.confirm ?? "Confirm" }}
|
|
87
|
+
</Button>
|
|
88
|
+
</footer>
|
|
89
|
+
</ModalRoot>
|
|
90
|
+
</template>
|
|
91
|
+
|
|
92
|
+
<style module>
|
|
93
|
+
.body{color:var(--color-dimmed);margin:0}.footer{display:flex;gap:var(--spacing-sm);justify-content:flex-end;margin-top:var(--spacing-md)}
|
|
94
|
+
</style>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { MaybePromise } from '@nui/types';
|
|
2
|
+
import type { ButtonProps } from '../../button/button.vue.js';
|
|
3
|
+
import type { ModalRootProps } from '../../modal/modal-root.vue.js';
|
|
4
|
+
type ConfirmLabels = Record<'confirm' | 'cancel', string>;
|
|
5
|
+
export interface ConfirmModalProps extends ModalRootProps {
|
|
6
|
+
/** Modal title */
|
|
7
|
+
title: string;
|
|
8
|
+
/** Description text displayed below the title */
|
|
9
|
+
description?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Custom button labels.
|
|
12
|
+
* @default { confirm: 'Confirm', cancel: 'Cancel' }
|
|
13
|
+
*/
|
|
14
|
+
labels?: ConfirmLabels;
|
|
15
|
+
/** Display variant. When `'danger'`, the confirm button turns red */
|
|
16
|
+
variant?: 'default' | 'danger';
|
|
17
|
+
/** Callback invoked on cancel */
|
|
18
|
+
onCancel?: () => void;
|
|
19
|
+
/** Callback invoked on confirm */
|
|
20
|
+
onConfirm?: () => MaybePromise<void>;
|
|
21
|
+
/** Additional props for the cancel button */
|
|
22
|
+
cancelProps?: Partial<ButtonProps>;
|
|
23
|
+
/** Additional props for the confirm button */
|
|
24
|
+
confirmProps?: Partial<ButtonProps>;
|
|
25
|
+
}
|
|
26
|
+
declare const __VLS_export: import("vue").DefineComponent<ConfirmModalProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ConfirmModalProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
27
|
+
declare const _default: typeof __VLS_export;
|
|
28
|
+
export default _default;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ConfirmModalProps } from './confirm-modal.vue.js';
|
|
2
|
+
/**
|
|
3
|
+
* Opens the confirm modal from any context.
|
|
4
|
+
*
|
|
5
|
+
* Resolves with `true` if the user confirmed, rejects if cancelled.
|
|
6
|
+
*/
|
|
7
|
+
export declare const openConfirmModal: (props?: ConfirmModalProps | undefined) => Promise<boolean>;
|
|
8
|
+
export type { ConfirmModalProps };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './_confirm-modal/index.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./_confirm-modal/index.js";
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { $modals } from "@nui/composals";
|
|
3
|
+
const activeModals = $modals.modals;
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<div id='nui-modals-root'>
|
|
8
|
+
<component
|
|
9
|
+
:is='entry.component'
|
|
10
|
+
v-for='[id, entry] in activeModals'
|
|
11
|
+
:key='id'
|
|
12
|
+
v-bind='entry.props'
|
|
13
|
+
/>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
@@ -24,8 +24,8 @@ declare const __VLS_export: <Value extends string = string, Ext extends Combobox
|
|
|
24
24
|
modelValue?: ComboboxItem<Value, Ext> | null;
|
|
25
25
|
search?: string;
|
|
26
26
|
}) & {
|
|
27
|
-
onSelect?: ((args_0: number) => any) | undefined;
|
|
28
27
|
onClear?: (() => any) | undefined;
|
|
28
|
+
onSelect?: ((args_0: number) => any) | undefined;
|
|
29
29
|
onClose?: ((args_0: import("..").ComboboxDropdownEventSource) => any) | undefined;
|
|
30
30
|
onSubmit?: ((args_0: string, args_1: ComboboxItem) => any) | undefined;
|
|
31
31
|
"onUpdate:modelValue"?: ((value: ComboboxItem<Value, Ext> | null) => any) | undefined;
|
|
@@ -48,7 +48,7 @@ declare const __VLS_export: <Value extends string = string, Ext extends Combobox
|
|
|
48
48
|
} & {
|
|
49
49
|
rightSection?: (props: {}) => any;
|
|
50
50
|
};
|
|
51
|
-
emit: (((evt: "
|
|
51
|
+
emit: (((evt: "clear") => void) & ((evt: "select", args_0: number) => void) & ((evt: "close", args_0: import("..").ComboboxDropdownEventSource) => void) & ((evt: "submit", args_0: string, args_1: ComboboxItem) => void) & ((evt: "open", args_0: import("..").ComboboxDropdownEventSource) => void)) & (((event: "update:modelValue", value: ComboboxItem<Value, Ext> | null) => void) & ((event: "update:search", value: string) => void) & ((event: "update:open", value: boolean) => void));
|
|
52
52
|
}>) => import("vue").VNode & {
|
|
53
53
|
__ctx?: Awaited<typeof __VLS_setup>;
|
|
54
54
|
};
|
|
@@ -24,8 +24,8 @@ declare const __VLS_export: <Value extends string = string, Ext extends Combobox
|
|
|
24
24
|
modelValue?: ComboboxItem<Value, Ext> | null;
|
|
25
25
|
search?: string;
|
|
26
26
|
}) & {
|
|
27
|
-
onSelect?: ((args_0: number) => any) | undefined;
|
|
28
27
|
onClear?: (() => any) | undefined;
|
|
28
|
+
onSelect?: ((args_0: number) => any) | undefined;
|
|
29
29
|
onClose?: ((args_0: import("..").ComboboxDropdownEventSource) => any) | undefined;
|
|
30
30
|
onSubmit?: ((args_0: string, args_1: ComboboxItem) => any) | undefined;
|
|
31
31
|
"onUpdate:modelValue"?: ((value: ComboboxItem<Value, Ext> | null) => any) | undefined;
|
|
@@ -48,7 +48,7 @@ declare const __VLS_export: <Value extends string = string, Ext extends Combobox
|
|
|
48
48
|
} & {
|
|
49
49
|
rightSection?: (props: {}) => any;
|
|
50
50
|
};
|
|
51
|
-
emit: (((evt: "
|
|
51
|
+
emit: (((evt: "clear") => void) & ((evt: "select", args_0: number) => void) & ((evt: "close", args_0: import("..").ComboboxDropdownEventSource) => void) & ((evt: "submit", args_0: string, args_1: ComboboxItem) => void) & ((evt: "open", args_0: import("..").ComboboxDropdownEventSource) => void)) & (((event: "update:modelValue", value: ComboboxItem<Value, Ext> | null) => void) & ((event: "update:search", value: string) => void) & ((event: "update:open", value: boolean) => void));
|
|
52
52
|
}>) => import("vue").VNode & {
|
|
53
53
|
__ctx?: Awaited<typeof __VLS_setup>;
|
|
54
54
|
};
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# Modal System
|
|
2
|
+
|
|
3
|
+
Система управления модальными окнами на основе промисов. Позволяет открывать модалки императивно из любого места приложения и получать результат через `await`.
|
|
4
|
+
|
|
5
|
+
## Архитектура
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
ModalManager (singleton) — хранит состояния всех модалок, управляет жизненным циклом
|
|
9
|
+
├─ create() / createLazy() — регистрирует компонент модалки и возвращает функцию-открывашку
|
|
10
|
+
├─ show() / hide() / reject() — императивное управление
|
|
11
|
+
└─ state() — реактивное состояние для использования внутри компонента
|
|
12
|
+
|
|
13
|
+
useModal(id) — composable для компонента модалки (opened + resolve)
|
|
14
|
+
|
|
15
|
+
ModalProvider — рендерит все активные модалки в DOM
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Быстрый старт
|
|
19
|
+
|
|
20
|
+
### 1. Регистрация модалки
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
// src/runtime/components/modals/_my-modal/index.ts
|
|
24
|
+
import { $modals } from '@nui/composals'
|
|
25
|
+
|
|
26
|
+
import MyModal from './my-modal.vue'
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
export const openMyModal = $modals.create<MyModalProps, string>(
|
|
30
|
+
'my-modal',
|
|
31
|
+
MyModal,
|
|
32
|
+
)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. Компонент модалки
|
|
36
|
+
|
|
37
|
+
```vue
|
|
38
|
+
<!-- my-modal.vue -->
|
|
39
|
+
<script setup lang="ts">
|
|
40
|
+
import { useModal } from '@nui/composals'
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
defineProps<{ message: string }>()
|
|
44
|
+
|
|
45
|
+
const { opened, resolve } = useModal('my-modal')
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<template>
|
|
49
|
+
<ModalRoot v-model:open='opened'>
|
|
50
|
+
<p>{{ message }}</p>
|
|
51
|
+
<button @click="resolve('ok')">
|
|
52
|
+
OK
|
|
53
|
+
</button>
|
|
54
|
+
</ModalRoot>
|
|
55
|
+
</template>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 3. Вызов из любого места
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
const result = await openMyModal({ message: 'Hello!' })
|
|
62
|
+
// result: 'ok'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## API
|
|
68
|
+
|
|
69
|
+
### `ModalManager`
|
|
70
|
+
|
|
71
|
+
> Файл: `modal-manager.ts`
|
|
72
|
+
|
|
73
|
+
Singleton-класс, управляющий реестром и жизненным циклом модальных окон. Экспортируется как `$modals`.
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { $modals } from '@nui/composals'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### `create<TProps, TResult>(id, component)`
|
|
80
|
+
|
|
81
|
+
Регистрирует компонент модалки и возвращает типизированную функцию для её открытия.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const openModal = $modals.create<{ name: string }, boolean>(
|
|
85
|
+
'greeting',
|
|
86
|
+
GreetingModal,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
const confirmed = await openModal({ name: 'World' })
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- **id** — уникальный строковый идентификатор модалки
|
|
93
|
+
- **component** — Vue-компонент
|
|
94
|
+
- **Возвращает** — `(props?) => Promise<TResult>`
|
|
95
|
+
|
|
96
|
+
#### `createLazy<TProps, TResult>(id, loader)`
|
|
97
|
+
|
|
98
|
+
Аналогичен `create`, но компонент загружается лениво при первом открытии.
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
const openHeavyModal = $modals.createLazy<HeavyProps, void>(
|
|
102
|
+
'heavy',
|
|
103
|
+
() => import('./heavy-modal.vue'),
|
|
104
|
+
)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### `show<T>(id, props?)`
|
|
108
|
+
|
|
109
|
+
Открывает ранее зарегистрированную модалку по `id`. Возвращает `Promise<T>`.
|
|
110
|
+
|
|
111
|
+
#### `hide(id, result?)`
|
|
112
|
+
|
|
113
|
+
Закрывает модалку, резолвит промис переданным `result`.
|
|
114
|
+
|
|
115
|
+
#### `reject(id, reason?)`
|
|
116
|
+
|
|
117
|
+
Закрывает модалку, реджектит промис переданным `reason`.
|
|
118
|
+
|
|
119
|
+
#### `state<Props, Resolve, Reject>(id)`
|
|
120
|
+
|
|
121
|
+
Возвращает `ComputedRef<ModalState>` — реактивное состояние модалки. Используется внутри компонента модалки через `useModal`.
|
|
122
|
+
|
|
123
|
+
#### `modals` (getter)
|
|
124
|
+
|
|
125
|
+
`ShallowRef<Map<string, ModalState>>` — реактивная карта всех активных модалок. Используется в `ModalProvider` для рендеринга.
|
|
126
|
+
|
|
127
|
+
#### Интерфейс `ModalState`
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
interface ModalState<Props, Resolve, Reject> {
|
|
131
|
+
id: string
|
|
132
|
+
component: Component
|
|
133
|
+
props: Props
|
|
134
|
+
opened: boolean
|
|
135
|
+
resolve: (value?: Resolve) => void
|
|
136
|
+
reject: (reason?: Reject) => void
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
### `useModal(id)`
|
|
143
|
+
|
|
144
|
+
> Файл: `use-modal.ts`
|
|
145
|
+
|
|
146
|
+
Composable для использования **внутри** компонента модалки. Связывает компонент с `ModalManager` по `id`.
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
const { opened, resolve } = useModal<MyProps, ResultType>('my-modal')
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### Возвращает
|
|
153
|
+
|
|
154
|
+
| Поле | Тип | Описание |
|
|
155
|
+
|------|-----|----------|
|
|
156
|
+
| `opened` | `WritableComputedRef<boolean>` | Двусторонний binding для открытия/закрытия. При установке в `false` вызывает `reject`. |
|
|
157
|
+
| `resolve` | `(reason: Resolve) => void` | Закрывает модалку и резолвит промис переданным значением. |
|
|
158
|
+
|
|
159
|
+
#### Типизация
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
function useModal<
|
|
163
|
+
Props extends Record<string, unknown>,
|
|
164
|
+
Resolve = unknown,
|
|
165
|
+
Reject = unknown,
|
|
166
|
+
>(id: string): { opened: WritableComputedRef<boolean>, resolve: (reason: Resolve) => void }
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
> **Важно:** `id` должен совпадать с тем, что передан в `$modals.create()`.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
### `ModalProvider`
|
|
174
|
+
|
|
175
|
+
> Файл: `modal-provider.vue`
|
|
176
|
+
|
|
177
|
+
Компонент-контейнер, который рендерит все активные модалки. Должен быть размещён один раз в корне приложения (обычно в `app.vue` или layout).
|
|
178
|
+
|
|
179
|
+
```vue
|
|
180
|
+
<template>
|
|
181
|
+
<NuxtPage />
|
|
182
|
+
<ModalProvider />
|
|
183
|
+
</template>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Внутри итерирует `$modals.modals` и рендерит каждую модалку через `<component :is="..." v-bind="props" />`.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Встроенные модалки
|
|
191
|
+
|
|
192
|
+
### `ConfirmModal`
|
|
193
|
+
|
|
194
|
+
> Файл: `_confirm-modal/confirm-modal.vue`
|
|
195
|
+
|
|
196
|
+
Модалка подтверждения с кнопками «Confirm» / «Cancel». Зарегистрирована с `id = 'confirm'`.
|
|
197
|
+
|
|
198
|
+
#### Props (`ConfirmModalProps`)
|
|
199
|
+
|
|
200
|
+
| Prop | Тип | По умолчанию | Описание |
|
|
201
|
+
|------|-----|-------------|----------|
|
|
202
|
+
| `title` | `string` | — | Заголовок модалки (обязательный) |
|
|
203
|
+
| `body` | `string` | — | Текст описания |
|
|
204
|
+
| `labels` | `{ confirm: string, cancel: string }` | `{ confirm: 'Confirm', cancel: 'Cancel' }` | Тексты кнопок |
|
|
205
|
+
| `variant` | `'default' \| 'danger'` | `'default'` | При `'danger'` кнопка подтверждения становится красной |
|
|
206
|
+
| `onCancel` | `() => void` | — | Колбэк при отмене |
|
|
207
|
+
| `onConfirm` | `() => MaybePromise<void>` | — | Колбэк при подтверждении (поддерживает async) |
|
|
208
|
+
| `cancelProps` | `Partial<ButtonProps>` | — | Дополнительные props для кнопки отмены |
|
|
209
|
+
| `confirmProps` | `Partial<ButtonProps>` | — | Дополнительные props для кнопки подтверждения |
|
|
210
|
+
|
|
211
|
+
Также принимает все props от `ModalRootProps` (например `size`).
|
|
212
|
+
|
|
213
|
+
#### Использование
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import { openConfirmModal } from '@nui/components'
|
|
217
|
+
|
|
218
|
+
// Простое подтверждение
|
|
219
|
+
const confirmed = await openConfirmModal({
|
|
220
|
+
title: 'Удалить запись?',
|
|
221
|
+
body: 'Это действие нельзя отменить.',
|
|
222
|
+
variant: 'danger',
|
|
223
|
+
labels: { confirm: 'Удалить', cancel: 'Отмена' },
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
if (confirmed) {
|
|
227
|
+
// пользователь подтвердил
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
#### Результат
|
|
232
|
+
|
|
233
|
+
- **Resolve** → `true` — пользователь нажал «Confirm»
|
|
234
|
+
- **Reject** → промис реджектится — пользователь нажал «Cancel» или закрыл модалку
|
|
235
|
+
|
|
236
|
+
> **Совет:** оборачивайте вызов в `try/catch`, чтобы обработать отмену:
|
|
237
|
+
> ```ts
|
|
238
|
+
> try {
|
|
239
|
+
> const confirmed = await openConfirmModal({ title: '...' })
|
|
240
|
+
> }
|
|
241
|
+
> catch {
|
|
242
|
+
> // пользователь отменил
|
|
243
|
+
> }
|
|
244
|
+
> ```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Паттерны
|
|
249
|
+
|
|
250
|
+
### Wizard (пошаговые модалки)
|
|
251
|
+
|
|
252
|
+
Можно вызывать `openConfirmModal` последовательно для реализации пошагового сценария:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
const steps = [
|
|
256
|
+
{ title: 'Шаг 1', body: 'Начать?' },
|
|
257
|
+
{ title: 'Шаг 2', body: 'Продолжить?' },
|
|
258
|
+
{ title: 'Шаг 3', body: 'Завершить?', variant: 'danger' },
|
|
259
|
+
]
|
|
260
|
+
|
|
261
|
+
let current = 0
|
|
262
|
+
while (current < steps.length) {
|
|
263
|
+
try {
|
|
264
|
+
const confirmed = await openConfirmModal(steps[current])
|
|
265
|
+
confirmed ? current++ : current--
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
break // отмена
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Создание кастомной модалки
|
|
274
|
+
|
|
275
|
+
1. Создайте компонент в `src/runtime/components/modals/_my-modal/`
|
|
276
|
+
2. Внутри компонента используйте `useModal(id)` для получения `opened` и `resolve`
|
|
277
|
+
3. В `index.ts` зарегистрируйте через `$modals.create()` и экспортируйте функцию-открывашку
|
|
278
|
+
4. Реэкспортируйте из `src/runtime/components/modals/index.ts`
|
|
279
|
+
|
|
280
|
+
### Lazy-модалки
|
|
281
|
+
|
|
282
|
+
Для тяжёлых модалок используйте `createLazy`, чтобы компонент загружался только при первом открытии:
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
export const openHeavyModal = $modals.createLazy<HeavyProps, void>(
|
|
286
|
+
'heavy',
|
|
287
|
+
() => import('./heavy-modal.vue'),
|
|
288
|
+
)
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Асинхронный onConfirm
|
|
292
|
+
|
|
293
|
+
`ConfirmModal` поддерживает асинхронный `onConfirm` — кнопка показывает loading-состояние до завершения:
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
await openConfirmModal({
|
|
297
|
+
title: 'Сохранить?',
|
|
298
|
+
onConfirm: async () => {
|
|
299
|
+
await api.save(data)
|
|
300
|
+
},
|
|
301
|
+
})
|
|
302
|
+
```
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { Component } from 'vue';
|
|
2
|
+
/**
|
|
3
|
+
* Reactive state of a single modal within {@link ModalManager}.
|
|
4
|
+
*/
|
|
5
|
+
export interface ModalState<Props extends object = object, Resolve = unknown, Reject = unknown> {
|
|
6
|
+
/** Unique modal identifier */
|
|
7
|
+
id: string;
|
|
8
|
+
/** Vue component rendered as the modal */
|
|
9
|
+
component: Component;
|
|
10
|
+
/** Props passed when calling `show` or the opener function */
|
|
11
|
+
props: Props;
|
|
12
|
+
/** Current visibility state */
|
|
13
|
+
opened: boolean;
|
|
14
|
+
/** Resolves the promise created when the modal was opened */
|
|
15
|
+
resolve: (value?: Resolve) => void;
|
|
16
|
+
/** Rejects the promise created when the modal was opened */
|
|
17
|
+
reject: (reason?: Reject) => void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Singleton modal manager.
|
|
21
|
+
*
|
|
22
|
+
* Maintains a component registry and a reactive map of active modals.
|
|
23
|
+
* Opening a modal returns a `Promise` that resolves via `hide`
|
|
24
|
+
* or rejects via `reject` / user dismissal.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const open = $modals.create<MyProps, string>('my-modal', MyModal)
|
|
29
|
+
* const result = await open({ foo: 'bar' }) // result: string
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare class ModalManager {
|
|
33
|
+
#private;
|
|
34
|
+
private constructor();
|
|
35
|
+
/** Returns the singleton instance (creates on first access) */
|
|
36
|
+
static get instance(): ModalManager;
|
|
37
|
+
/** Reactive map of all active modals. Used by `ModalProvider` for rendering */
|
|
38
|
+
get modals(): import("vue").ShallowRef<Map<string, ModalState<object, unknown, unknown>>, Map<string, ModalState<object, unknown, unknown>>>;
|
|
39
|
+
/**
|
|
40
|
+
* Registers a modal component and returns a typed function to open it.
|
|
41
|
+
*/
|
|
42
|
+
create<TProps extends object = object, TResult = void>(
|
|
43
|
+
/** unique modal identifier */
|
|
44
|
+
id: string,
|
|
45
|
+
/** Vue component for the modal */
|
|
46
|
+
component: Component): (props?: TProps) => Promise<TResult>;
|
|
47
|
+
/**
|
|
48
|
+
* Registers a lazy modal — the component is loaded on first open.
|
|
49
|
+
*/
|
|
50
|
+
createLazy<TProps extends object = object, TResult = void>(
|
|
51
|
+
/** unique modal identifier */
|
|
52
|
+
id: string,
|
|
53
|
+
/** dynamic import function (`() => import('./my-modal.vue')`) */
|
|
54
|
+
loader: () => Promise<{
|
|
55
|
+
default: Component;
|
|
56
|
+
}>): (props?: Omit<TProps, 'modalId'>) => Promise<TResult>;
|
|
57
|
+
/**
|
|
58
|
+
* Opens a previously registered modal by its identifier.
|
|
59
|
+
*/
|
|
60
|
+
show<T = unknown>(id: string, props?: object): Promise<T>;
|
|
61
|
+
/**
|
|
62
|
+
* Closes the modal and resolves its promise with the given result.
|
|
63
|
+
*
|
|
64
|
+
* @param id — modal identifier
|
|
65
|
+
* @param result — value the promise resolves with
|
|
66
|
+
*/
|
|
67
|
+
hide(id: string, result?: any): void;
|
|
68
|
+
/**
|
|
69
|
+
* Closes the modal and rejects its promise with the given reason.
|
|
70
|
+
*
|
|
71
|
+
* @param id — modal identifier
|
|
72
|
+
* @param reason — rejection reason
|
|
73
|
+
*/
|
|
74
|
+
reject(id: string, reason?: any): void;
|
|
75
|
+
/**
|
|
76
|
+
* Returns the reactive state of a specific modal as `ComputedRef<ModalState>`.
|
|
77
|
+
*
|
|
78
|
+
* Used inside a modal component (via {@link useModal}).
|
|
79
|
+
*/
|
|
80
|
+
state<Props extends object = object, Resolve = unknown, Reject = unknown>(id: string): import("vue").ComputedRef<ModalState<Props, Resolve, Reject>>;
|
|
81
|
+
}
|
|
82
|
+
/** Global {@link ModalManager} instance for use throughout the application */
|
|
83
|
+
export declare const $modals: ModalManager;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { computed, nextTick, shallowRef, triggerRef } from "vue";
|
|
2
|
+
export class ModalManager {
|
|
3
|
+
static #instance = null;
|
|
4
|
+
/** Reactive map of active modals */
|
|
5
|
+
#modals = shallowRef(/* @__PURE__ */ new Map());
|
|
6
|
+
/** Eagerly registered components (id → Component) */
|
|
7
|
+
#registered = /* @__PURE__ */ new Map();
|
|
8
|
+
/** Lazily registered loaders (id → loader) */
|
|
9
|
+
#lazy = /* @__PURE__ */ new Map();
|
|
10
|
+
constructor() {
|
|
11
|
+
}
|
|
12
|
+
/** Returns the singleton instance (creates on first access) */
|
|
13
|
+
static get instance() {
|
|
14
|
+
if (!ModalManager.#instance)
|
|
15
|
+
ModalManager.#instance = new ModalManager();
|
|
16
|
+
return ModalManager.#instance;
|
|
17
|
+
}
|
|
18
|
+
/** Reactive map of all active modals. Used by `ModalProvider` for rendering */
|
|
19
|
+
get modals() {
|
|
20
|
+
return ModalManager.instance.#modals;
|
|
21
|
+
}
|
|
22
|
+
// ── Facade ──
|
|
23
|
+
/**
|
|
24
|
+
* Registers a modal component and returns a typed function to open it.
|
|
25
|
+
*/
|
|
26
|
+
create(id, component) {
|
|
27
|
+
this.#registered.set(id, component);
|
|
28
|
+
return (props) => this.#show(id, props ?? {});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Registers a lazy modal — the component is loaded on first open.
|
|
32
|
+
*/
|
|
33
|
+
createLazy(id, loader) {
|
|
34
|
+
this.#lazy.set(id, loader);
|
|
35
|
+
return (props) => this.#show(id, props ?? {});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Opens a previously registered modal by its identifier.
|
|
39
|
+
*/
|
|
40
|
+
async show(id, props = {}) {
|
|
41
|
+
return this.#show(id, props);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Closes the modal and resolves its promise with the given result.
|
|
45
|
+
*
|
|
46
|
+
* @param id — modal identifier
|
|
47
|
+
* @param result — value the promise resolves with
|
|
48
|
+
*/
|
|
49
|
+
hide(id, result) {
|
|
50
|
+
this.#hide(id, result);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Closes the modal and rejects its promise with the given reason.
|
|
54
|
+
*
|
|
55
|
+
* @param id — modal identifier
|
|
56
|
+
* @param reason — rejection reason
|
|
57
|
+
*/
|
|
58
|
+
reject(id, reason) {
|
|
59
|
+
this.#reject(id, reason);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Returns the reactive state of a specific modal as `ComputedRef<ModalState>`.
|
|
63
|
+
*
|
|
64
|
+
* Used inside a modal component (via {@link useModal}).
|
|
65
|
+
*/
|
|
66
|
+
state(id) {
|
|
67
|
+
return computed(() => this.#modals.value.get(id));
|
|
68
|
+
}
|
|
69
|
+
// ── Private implementation ──
|
|
70
|
+
async #show(id, props = {}) {
|
|
71
|
+
if (!this.#registered.has(id) && this.#lazy.has(id)) {
|
|
72
|
+
const loader = this.#lazy.get(id);
|
|
73
|
+
try {
|
|
74
|
+
const loaded = await loader();
|
|
75
|
+
this.#registered.set(id, loaded.default);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
return Promise.reject(err);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const component = this.#registered.get(id);
|
|
81
|
+
if (!component)
|
|
82
|
+
return Promise.reject(new Error(`Modal "${id}" is not registered`));
|
|
83
|
+
const existing = this.#modals.value.get(id);
|
|
84
|
+
return new Promise((resolve, reject) => {
|
|
85
|
+
if (existing) {
|
|
86
|
+
existing.props = props;
|
|
87
|
+
existing.opened = true;
|
|
88
|
+
existing.resolve = resolve;
|
|
89
|
+
existing.reject = reject;
|
|
90
|
+
} else {
|
|
91
|
+
this.#modals.value.set(id, {
|
|
92
|
+
id,
|
|
93
|
+
component,
|
|
94
|
+
props,
|
|
95
|
+
opened: true,
|
|
96
|
+
resolve,
|
|
97
|
+
reject
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
triggerRef(this.#modals);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
#hide(id, result) {
|
|
104
|
+
const modal = this.#modals.value.get(id);
|
|
105
|
+
if (!modal)
|
|
106
|
+
return;
|
|
107
|
+
modal.opened = false;
|
|
108
|
+
modal.resolve?.(result);
|
|
109
|
+
triggerRef(this.#modals);
|
|
110
|
+
nextTick(() => {
|
|
111
|
+
const current = this.#modals.value.get(id);
|
|
112
|
+
if (current && !current.opened) {
|
|
113
|
+
this.#modals.value.delete(id);
|
|
114
|
+
triggerRef(this.#modals);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
#reject(id, reason) {
|
|
119
|
+
const modal = this.#modals.value.get(id);
|
|
120
|
+
if (!modal)
|
|
121
|
+
return;
|
|
122
|
+
modal.opened = false;
|
|
123
|
+
modal.reject?.(reason);
|
|
124
|
+
triggerRef(this.#modals);
|
|
125
|
+
nextTick(() => {
|
|
126
|
+
const current = this.#modals.value.get(id);
|
|
127
|
+
if (current && !current.opened) {
|
|
128
|
+
this.#modals.value.delete(id);
|
|
129
|
+
triggerRef(this.#modals);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export const $modals = ModalManager.instance;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composable for use inside a modal component.
|
|
3
|
+
* Binds the component to {@link ModalManager} by identifier.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* const { opened, resolve } = useModal<MyProps, string>('my-modal')
|
|
8
|
+
* // template: <ModalRoot v-model:open="opened">
|
|
9
|
+
* // on confirm: resolve('ok')
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export declare function useModal<Props extends Record<string, unknown> = Record<string, unknown>, Resolve = unknown, Reject = unknown>(
|
|
13
|
+
/**
|
|
14
|
+
* modal identifier
|
|
15
|
+
*
|
|
16
|
+
* must match the `id` passed to `$modals.create()`
|
|
17
|
+
*/
|
|
18
|
+
id: string): {
|
|
19
|
+
/**
|
|
20
|
+
* Two-way binding for `v-model:open`.
|
|
21
|
+
*
|
|
22
|
+
* Setting to `true` reopens the modal, setting to `false` calls `reject`
|
|
23
|
+
*/
|
|
24
|
+
opened: import("vue").WritableComputedRef<boolean, boolean>;
|
|
25
|
+
/** closes the modal and resolves the promise */
|
|
26
|
+
resolve: (reason: Resolve) => void;
|
|
27
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
import { $modals } from "./modal-manager.js";
|
|
3
|
+
export function useModal(id) {
|
|
4
|
+
const state = $modals.state(id);
|
|
5
|
+
const opened = computed({
|
|
6
|
+
get: () => state.value.opened,
|
|
7
|
+
set: (opened2) => opened2 ? $modals.show(id, state.value.props) : $modals.reject(id)
|
|
8
|
+
});
|
|
9
|
+
if (!state)
|
|
10
|
+
throw new Error(`Modal ${id} is not exist`);
|
|
11
|
+
return {
|
|
12
|
+
/**
|
|
13
|
+
* Two-way binding for `v-model:open`.
|
|
14
|
+
*
|
|
15
|
+
* Setting to `true` reopens the modal, setting to `false` calls `reject`
|
|
16
|
+
*/
|
|
17
|
+
opened,
|
|
18
|
+
/** closes the modal and resolves the promise */
|
|
19
|
+
resolve: (reason) => $modals.hide(id, reason)
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { addDay, weekStart, yearStart } from "@formkit/tempo";
|
|
2
|
+
import { round } from "es-toolkit";
|
|
2
3
|
export function getWeekNumber(day, firstDayOfWeek) {
|
|
3
4
|
const firstDayOfYear = yearStart(day);
|
|
4
5
|
const firstWeekStart = weekStart(firstDayOfYear, firstDayOfWeek);
|
|
5
6
|
const yearFirstWeek = firstWeekStart < firstDayOfYear ? addDay(firstWeekStart, 7) : firstWeekStart;
|
|
6
7
|
const currentWeek = weekStart(day, firstDayOfWeek);
|
|
7
|
-
return
|
|
8
|
+
return round((+currentWeek - +yearFirstWeek) / 6048e5) + 1;
|
|
8
9
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuance-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.51",
|
|
4
4
|
"description": "A UI Library for Modern Web Apps, powered by Vue.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
"@nuxtjs/stylelint-module": "^5.2.1",
|
|
75
75
|
"@types/node": "latest",
|
|
76
76
|
"changelogen": "^0.6.2",
|
|
77
|
+
"es-toolkit": "^1.45.1",
|
|
77
78
|
"eslint": "^9.39.2",
|
|
78
79
|
"nuxt": "^4.3.0",
|
|
79
80
|
"stylelint": "^17.1.1",
|