@aerogel/core 0.0.0-next.eed7a057cf5b844cd9f7fc6bda2d8df49fcd6736 → 0.0.0-next.f0368a3107664018eb88652e719d2cb7d24b82e7
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/aerogel-core.d.ts +1699 -2701
- package/dist/aerogel-core.js +2544 -2037
- package/dist/aerogel-core.js.map +1 -1
- package/package.json +14 -6
- package/src/components/{AGAppLayout.vue → AppLayout.vue} +3 -3
- package/src/components/{AGAppModals.vue → AppModals.vue} +2 -3
- package/src/components/AppOverlays.vue +9 -0
- package/src/components/AppToasts.vue +16 -0
- package/src/components/contracts/AlertModal.ts +4 -0
- package/src/components/contracts/Button.ts +16 -0
- package/src/components/contracts/ConfirmModal.ts +42 -0
- package/src/components/contracts/DropdownMenu.ts +20 -0
- package/src/components/{modals/AGErrorReportModal.ts → contracts/ErrorReportModal.ts} +3 -23
- package/src/components/contracts/Input.ts +26 -0
- package/src/components/contracts/LoadingModal.ts +22 -0
- package/src/components/contracts/Modal.ts +21 -0
- package/src/components/contracts/PromptModal.ts +31 -0
- package/src/components/contracts/Select.ts +45 -0
- package/src/components/contracts/Toast.ts +13 -0
- package/src/components/contracts/index.ts +11 -0
- package/src/components/headless/HeadlessButton.vue +51 -0
- package/src/components/headless/HeadlessInput.vue +59 -0
- package/src/components/headless/{forms/AGHeadlessInputDescription.vue → HeadlessInputDescription.vue} +6 -7
- package/src/components/headless/{forms/AGHeadlessInputError.vue → HeadlessInputError.vue} +2 -6
- package/src/components/headless/{forms/AGHeadlessInputInput.vue → HeadlessInputInput.vue} +16 -25
- package/src/components/headless/{forms/AGHeadlessInputLabel.vue → HeadlessInputLabel.vue} +2 -6
- package/src/components/headless/{forms/AGHeadlessInputTextArea.vue → HeadlessInputTextArea.vue} +9 -12
- package/src/components/headless/HeadlessModal.vue +57 -0
- package/src/components/headless/HeadlessModalContent.vue +30 -0
- package/src/components/headless/HeadlessModalDescription.vue +12 -0
- package/src/components/headless/HeadlessModalOverlay.vue +12 -0
- package/src/components/headless/HeadlessModalTitle.vue +12 -0
- package/src/components/headless/HeadlessSelect.vue +120 -0
- package/src/components/headless/{forms/AGHeadlessSelectError.vue → HeadlessSelectError.vue} +3 -4
- package/src/components/headless/HeadlessSelectLabel.vue +25 -0
- package/src/components/headless/HeadlessSelectOption.vue +34 -0
- package/src/components/headless/HeadlessSelectOptions.vue +42 -0
- package/src/components/headless/HeadlessSelectTrigger.vue +22 -0
- package/src/components/headless/HeadlessSelectValue.vue +18 -0
- package/src/components/headless/HeadlessToast.vue +18 -0
- package/src/components/headless/HeadlessToastAction.vue +13 -0
- package/src/components/headless/index.ts +19 -3
- package/src/components/index.ts +6 -11
- package/src/components/ui/AdvancedOptions.vue +18 -0
- package/src/components/ui/AlertModal.vue +13 -0
- package/src/components/ui/Button.vue +98 -0
- package/src/components/ui/Checkbox.vue +56 -0
- package/src/components/ui/ConfirmModal.vue +42 -0
- package/src/components/ui/DropdownMenu.vue +32 -0
- package/src/components/ui/DropdownMenuOption.vue +14 -0
- package/src/components/ui/DropdownMenuOptions.vue +27 -0
- package/src/components/ui/EditableContent.vue +82 -0
- package/src/components/ui/ErrorMessage.vue +15 -0
- package/src/components/ui/ErrorReportModal.vue +62 -0
- package/src/components/{modals/AGErrorReportModalButtons.vue → ui/ErrorReportModalButtons.vue} +29 -22
- package/src/components/ui/ErrorReportModalTitle.vue +24 -0
- package/src/components/{forms/AGForm.vue → ui/Form.vue} +4 -5
- package/src/components/ui/Input.vue +56 -0
- package/src/components/ui/Link.vue +12 -0
- package/src/components/ui/LoadingModal.vue +32 -0
- package/src/components/ui/Markdown.vue +69 -0
- package/src/components/ui/Modal.vue +121 -0
- package/src/components/{modals/AGModalContext.vue → ui/ModalContext.vue} +7 -9
- package/src/components/ui/ProgressBar.vue +51 -0
- package/src/components/ui/PromptModal.vue +35 -0
- package/src/components/ui/Select.vue +27 -0
- package/src/components/ui/SelectLabel.vue +17 -0
- package/src/components/ui/SelectOption.vue +29 -0
- package/src/components/ui/SelectOptions.vue +35 -0
- package/src/components/ui/SelectTrigger.vue +29 -0
- package/src/components/ui/SettingsModal.vue +15 -0
- package/src/components/{lib/AGStartupCrash.vue → ui/StartupCrash.vue} +8 -8
- package/src/components/ui/Toast.vue +42 -0
- package/src/components/ui/index.ts +30 -0
- package/src/directives/index.ts +6 -0
- package/src/errors/Errors.ts +9 -10
- package/src/forms/{Form.test.ts → FormController.test.ts} +29 -4
- package/src/forms/{Form.ts → FormController.ts} +20 -11
- package/src/forms/index.ts +2 -3
- package/src/forms/utils.ts +23 -2
- package/src/index.css +72 -0
- package/src/lang/index.ts +5 -1
- package/src/lang/settings/Language.vue +48 -0
- package/src/lang/settings/index.ts +10 -0
- package/src/services/App.state.ts +11 -1
- package/src/services/App.ts +9 -1
- package/src/services/Events.test.ts +8 -8
- package/src/services/Events.ts +2 -8
- package/src/services/index.ts +4 -1
- package/src/ui/UI.state.ts +13 -6
- package/src/ui/UI.ts +70 -75
- package/src/ui/index.ts +14 -14
- package/src/utils/classes.ts +49 -0
- package/src/utils/composition/events.ts +2 -4
- package/src/utils/composition/forms.ts +20 -4
- package/src/utils/composition/state.ts +11 -2
- package/src/utils/index.ts +3 -1
- package/src/utils/types.ts +3 -0
- package/src/utils/vue.ts +22 -133
- package/src/components/AGAppOverlays.vue +0 -41
- package/src/components/AGAppSnackbars.vue +0 -13
- package/src/components/composition.ts +0 -23
- package/src/components/constants.ts +0 -8
- package/src/components/forms/AGButton.vue +0 -44
- package/src/components/forms/AGCheckbox.vue +0 -42
- package/src/components/forms/AGInput.vue +0 -42
- package/src/components/forms/AGSelect.story.vue +0 -46
- package/src/components/forms/AGSelect.vue +0 -60
- package/src/components/forms/index.ts +0 -5
- package/src/components/headless/forms/AGHeadlessButton.ts +0 -3
- package/src/components/headless/forms/AGHeadlessButton.vue +0 -62
- package/src/components/headless/forms/AGHeadlessInput.ts +0 -41
- package/src/components/headless/forms/AGHeadlessInput.vue +0 -70
- package/src/components/headless/forms/AGHeadlessSelect.ts +0 -42
- package/src/components/headless/forms/AGHeadlessSelect.vue +0 -77
- package/src/components/headless/forms/AGHeadlessSelectButton.vue +0 -24
- package/src/components/headless/forms/AGHeadlessSelectLabel.vue +0 -24
- package/src/components/headless/forms/AGHeadlessSelectOption.ts +0 -4
- package/src/components/headless/forms/AGHeadlessSelectOption.vue +0 -39
- package/src/components/headless/forms/AGHeadlessSelectOptions.ts +0 -3
- package/src/components/headless/forms/composition.ts +0 -10
- package/src/components/headless/forms/index.ts +0 -18
- package/src/components/headless/modals/AGHeadlessModal.ts +0 -36
- package/src/components/headless/modals/AGHeadlessModal.vue +0 -92
- package/src/components/headless/modals/AGHeadlessModalPanel.vue +0 -32
- package/src/components/headless/modals/AGHeadlessModalTitle.vue +0 -23
- package/src/components/headless/modals/index.ts +0 -4
- package/src/components/headless/snackbars/AGHeadlessSnackbar.vue +0 -10
- package/src/components/headless/snackbars/index.ts +0 -40
- package/src/components/interfaces.ts +0 -24
- package/src/components/lib/AGErrorMessage.vue +0 -16
- package/src/components/lib/AGLink.vue +0 -9
- package/src/components/lib/AGMarkdown.vue +0 -54
- package/src/components/lib/AGMeasured.vue +0 -16
- package/src/components/lib/AGProgressBar.vue +0 -55
- package/src/components/lib/index.ts +0 -6
- package/src/components/modals/AGAlertModal.ts +0 -18
- package/src/components/modals/AGAlertModal.vue +0 -14
- package/src/components/modals/AGConfirmModal.ts +0 -42
- package/src/components/modals/AGConfirmModal.vue +0 -26
- package/src/components/modals/AGErrorReportModal.vue +0 -54
- package/src/components/modals/AGErrorReportModalTitle.vue +0 -25
- package/src/components/modals/AGLoadingModal.ts +0 -29
- package/src/components/modals/AGLoadingModal.vue +0 -15
- package/src/components/modals/AGModal.ts +0 -11
- package/src/components/modals/AGModal.vue +0 -42
- package/src/components/modals/AGModalContext.ts +0 -8
- package/src/components/modals/AGModalTitle.vue +0 -9
- package/src/components/modals/AGPromptModal.ts +0 -41
- package/src/components/modals/AGPromptModal.vue +0 -34
- package/src/components/modals/index.ts +0 -17
- package/src/components/snackbars/AGSnackbar.vue +0 -36
- package/src/components/snackbars/index.ts +0 -3
- package/src/components/utils.ts +0 -13
- package/src/forms/composition.ts +0 -6
- package/src/utils/tailwindcss.test.ts +0 -26
- package/src/utils/tailwindcss.ts +0 -7
package/src/ui/UI.ts
CHANGED
|
@@ -6,22 +6,20 @@ import type { ObjectValues } from '@noeldemartin/utils';
|
|
|
6
6
|
import App from '@aerogel/core/services/App';
|
|
7
7
|
import Events from '@aerogel/core/services/Events';
|
|
8
8
|
import type { AcceptRefs } from '@aerogel/core/utils';
|
|
9
|
-
import type {
|
|
10
|
-
import type {
|
|
11
|
-
import type {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
AGPromptModalProps,
|
|
16
|
-
} from '@aerogel/core/components';
|
|
9
|
+
import type { AlertModalProps } from '@aerogel/core/components/contracts/AlertModal';
|
|
10
|
+
import type { ButtonVariant } from '@aerogel/core/components/contracts/Button';
|
|
11
|
+
import type { ConfirmModalCheckboxes, ConfirmModalProps } from '@aerogel/core/components/contracts/ConfirmModal';
|
|
12
|
+
import type { LoadingModalProps } from '@aerogel/core/components/contracts/LoadingModal';
|
|
13
|
+
import type { PromptModalProps } from '@aerogel/core/components/contracts/PromptModal';
|
|
14
|
+
import type { ToastAction, ToastVariant } from '@aerogel/core/components/contracts/Toast';
|
|
17
15
|
|
|
18
16
|
import Service from './UI.state';
|
|
19
17
|
import { MOBILE_BREAKPOINT, getCurrentLayout } from './utils';
|
|
20
|
-
import type {
|
|
18
|
+
import type { ModalComponent, UIModal, UIToast } from './UI.state';
|
|
21
19
|
|
|
22
20
|
interface ModalCallbacks<T = unknown> {
|
|
23
21
|
willClose(result: T | undefined): void;
|
|
24
|
-
|
|
22
|
+
hasClosed(result: T | undefined): void;
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
type ModalProperties<TComponent> = TComponent extends ModalComponent<infer TProperties, unknown> ? TProperties : never;
|
|
@@ -34,19 +32,18 @@ export const UIComponents = {
|
|
|
34
32
|
ErrorReportModal: 'error-report-modal',
|
|
35
33
|
LoadingModal: 'loading-modal',
|
|
36
34
|
PromptModal: 'prompt-modal',
|
|
37
|
-
|
|
35
|
+
Toast: 'toast',
|
|
38
36
|
StartupCrash: 'startup-crash',
|
|
37
|
+
RouterLink: 'router-link',
|
|
39
38
|
} as const;
|
|
40
39
|
|
|
41
40
|
export type UIComponent = ObjectValues<typeof UIComponents>;
|
|
42
41
|
|
|
43
|
-
export type ConfirmCheckboxes = Record<string, { label: string; default?: boolean; required?: boolean }>;
|
|
44
|
-
|
|
45
42
|
export type ConfirmOptions = AcceptRefs<{
|
|
46
43
|
acceptText?: string;
|
|
47
|
-
|
|
44
|
+
acceptVariant?: ButtonVariant;
|
|
48
45
|
cancelText?: string;
|
|
49
|
-
|
|
46
|
+
cancelVariant?: ButtonVariant;
|
|
50
47
|
actions?: Record<string, () => unknown>;
|
|
51
48
|
required?: boolean;
|
|
52
49
|
}>;
|
|
@@ -57,7 +54,8 @@ export type LoadingOptions = AcceptRefs<{
|
|
|
57
54
|
progress?: number;
|
|
58
55
|
}>;
|
|
59
56
|
|
|
60
|
-
export interface ConfirmOptionsWithCheckboxes<T extends
|
|
57
|
+
export interface ConfirmOptionsWithCheckboxes<T extends ConfirmModalCheckboxes = ConfirmModalCheckboxes>
|
|
58
|
+
extends ConfirmOptions {
|
|
61
59
|
checkboxes?: T;
|
|
62
60
|
}
|
|
63
61
|
|
|
@@ -66,16 +64,16 @@ export type PromptOptions = AcceptRefs<{
|
|
|
66
64
|
defaultValue?: string;
|
|
67
65
|
placeholder?: string;
|
|
68
66
|
acceptText?: string;
|
|
69
|
-
|
|
67
|
+
acceptVariant?: ButtonVariant;
|
|
70
68
|
cancelText?: string;
|
|
71
|
-
|
|
69
|
+
cancelVariant?: ButtonVariant;
|
|
72
70
|
trim?: boolean;
|
|
73
71
|
}>;
|
|
74
72
|
|
|
75
|
-
export interface
|
|
73
|
+
export interface ToastOptions {
|
|
76
74
|
component?: Component;
|
|
77
|
-
|
|
78
|
-
actions?:
|
|
75
|
+
variant?: ToastVariant;
|
|
76
|
+
actions?: ToastAction[];
|
|
79
77
|
}
|
|
80
78
|
|
|
81
79
|
export class UIService extends Service {
|
|
@@ -83,14 +81,18 @@ export class UIService extends Service {
|
|
|
83
81
|
private modalCallbacks: Record<string, Partial<ModalCallbacks>> = {};
|
|
84
82
|
private components: Partial<Record<UIComponent, Component>> = {};
|
|
85
83
|
|
|
84
|
+
public resolveComponent(name: UIComponent): Component | null {
|
|
85
|
+
return this.components[name] ?? null;
|
|
86
|
+
}
|
|
87
|
+
|
|
86
88
|
public requireComponent(name: UIComponent): Component {
|
|
87
|
-
return this.
|
|
89
|
+
return this.resolveComponent(name) ?? fail(`UI Component '${name}' is not defined!`);
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
public alert(message: string): void;
|
|
91
93
|
public alert(title: string, message: string): void;
|
|
92
94
|
public alert(messageOrTitle: string, message?: string): void {
|
|
93
|
-
const getProperties = ():
|
|
95
|
+
const getProperties = (): AlertModalProps => {
|
|
94
96
|
if (typeof message !== 'string') {
|
|
95
97
|
return { message: messageOrTitle };
|
|
96
98
|
}
|
|
@@ -101,14 +103,17 @@ export class UIService extends Service {
|
|
|
101
103
|
};
|
|
102
104
|
};
|
|
103
105
|
|
|
104
|
-
this.openModal(
|
|
106
|
+
this.openModal<ModalComponent<AlertModalProps>>(
|
|
107
|
+
this.requireComponent(UIComponents.AlertModal),
|
|
108
|
+
getProperties(),
|
|
109
|
+
);
|
|
105
110
|
}
|
|
106
111
|
|
|
107
112
|
/* eslint-disable max-len */
|
|
108
113
|
public async confirm(message: string, options?: ConfirmOptions): Promise<boolean>;
|
|
109
114
|
public async confirm(title: string, message: string, options?: ConfirmOptions): Promise<boolean>;
|
|
110
|
-
public async confirm<T extends
|
|
111
|
-
public async confirm<T extends
|
|
115
|
+
public async confirm<T extends ConfirmModalCheckboxes>(message: string, options?: ConfirmOptionsWithCheckboxes<T>): Promise<[boolean, Record<keyof T, boolean>]>; // prettier-ignore
|
|
116
|
+
public async confirm<T extends ConfirmModalCheckboxes>(title: string, message: string, options?: ConfirmOptionsWithCheckboxes<T>): Promise<[boolean, Record<keyof T, boolean>]>; // prettier-ignore
|
|
112
117
|
/* eslint-enable max-len */
|
|
113
118
|
|
|
114
119
|
public async confirm(
|
|
@@ -116,7 +121,7 @@ export class UIService extends Service {
|
|
|
116
121
|
messageOrOptions?: string | ConfirmOptions | ConfirmOptionsWithCheckboxes,
|
|
117
122
|
options?: ConfirmOptions | ConfirmOptionsWithCheckboxes,
|
|
118
123
|
): Promise<boolean | [boolean, Record<string, boolean>]> {
|
|
119
|
-
const getProperties = ():
|
|
124
|
+
const getProperties = (): AcceptRefs<ConfirmModalProps> => {
|
|
120
125
|
if (typeof messageOrOptions !== 'string') {
|
|
121
126
|
return {
|
|
122
127
|
...(messageOrOptions ?? {}),
|
|
@@ -132,10 +137,17 @@ export class UIService extends Service {
|
|
|
132
137
|
required: !!options?.required,
|
|
133
138
|
};
|
|
134
139
|
};
|
|
140
|
+
|
|
141
|
+
type ConfirmModalComponent = ModalComponent<
|
|
142
|
+
AcceptRefs<ConfirmModalProps>,
|
|
143
|
+
boolean | [boolean, Record<string, boolean>]
|
|
144
|
+
>;
|
|
145
|
+
|
|
135
146
|
const properties = getProperties();
|
|
136
|
-
const modal = await this.openModal<
|
|
137
|
-
|
|
138
|
-
|
|
147
|
+
const modal = await this.openModal<ConfirmModalComponent>(
|
|
148
|
+
this.requireComponent(UIComponents.ConfirmModal),
|
|
149
|
+
properties,
|
|
150
|
+
);
|
|
139
151
|
const result = await modal.beforeClose;
|
|
140
152
|
|
|
141
153
|
const confirmed = typeof result === 'object' ? result[0] : (result ?? false);
|
|
@@ -174,22 +186,22 @@ export class UIService extends Service {
|
|
|
174
186
|
options?: PromptOptions,
|
|
175
187
|
): Promise<string | null> {
|
|
176
188
|
const trim = options?.trim ?? true;
|
|
177
|
-
const getProperties = ():
|
|
189
|
+
const getProperties = (): PromptModalProps => {
|
|
178
190
|
if (typeof messageOrOptions !== 'string') {
|
|
179
191
|
return {
|
|
180
192
|
message: messageOrTitle,
|
|
181
193
|
...(messageOrOptions ?? {}),
|
|
182
|
-
} as
|
|
194
|
+
} as PromptModalProps;
|
|
183
195
|
}
|
|
184
196
|
|
|
185
197
|
return {
|
|
186
198
|
title: messageOrTitle,
|
|
187
199
|
message: messageOrOptions,
|
|
188
200
|
...(options ?? {}),
|
|
189
|
-
} as
|
|
201
|
+
} as PromptModalProps;
|
|
190
202
|
};
|
|
191
203
|
|
|
192
|
-
const modal = await this.openModal<ModalComponent<
|
|
204
|
+
const modal = await this.openModal<ModalComponent<PromptModalProps, string | null>>(
|
|
193
205
|
this.requireComponent(UIComponents.PromptModal),
|
|
194
206
|
getProperties(),
|
|
195
207
|
);
|
|
@@ -207,7 +219,7 @@ export class UIService extends Service {
|
|
|
207
219
|
operation?: Promise<T> | (() => T),
|
|
208
220
|
): Promise<T> {
|
|
209
221
|
const processOperation = (o: Promise<T> | (() => T)) => (typeof o === 'function' ? Promise.resolve(o()) : o);
|
|
210
|
-
const processArgs = (): { operationPromise: Promise<T>; props?:
|
|
222
|
+
const processArgs = (): { operationPromise: Promise<T>; props?: AcceptRefs<LoadingModalProps> } => {
|
|
211
223
|
if (typeof operationOrMessageOrOptions === 'string') {
|
|
212
224
|
return {
|
|
213
225
|
props: { message: operationOrMessageOrOptions },
|
|
@@ -229,7 +241,9 @@ export class UIService extends Service {
|
|
|
229
241
|
const modal = await this.openModal(this.requireComponent(UIComponents.LoadingModal), props);
|
|
230
242
|
|
|
231
243
|
try {
|
|
232
|
-
const
|
|
244
|
+
const result = await operationPromise;
|
|
245
|
+
|
|
246
|
+
await after({ ms: 500 });
|
|
233
247
|
|
|
234
248
|
return result;
|
|
235
249
|
} finally {
|
|
@@ -237,23 +251,15 @@ export class UIService extends Service {
|
|
|
237
251
|
}
|
|
238
252
|
}
|
|
239
253
|
|
|
240
|
-
public
|
|
241
|
-
const
|
|
254
|
+
public toast(message: string, options: ToastOptions = {}): void {
|
|
255
|
+
const { component, ...otherOptions } = options;
|
|
256
|
+
const toast: UIToast = {
|
|
242
257
|
id: uuid(),
|
|
243
|
-
properties: { message, ...
|
|
244
|
-
component: markRaw(
|
|
258
|
+
properties: { message, ...otherOptions },
|
|
259
|
+
component: markRaw(component ?? this.requireComponent(UIComponents.Toast)),
|
|
245
260
|
};
|
|
246
261
|
|
|
247
|
-
this.setState('
|
|
248
|
-
|
|
249
|
-
setTimeout(() => this.hideSnackbar(snackbar.id), 5000);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
public hideSnackbar(id: string): void {
|
|
253
|
-
this.setState(
|
|
254
|
-
'snackbars',
|
|
255
|
-
this.snackbars.filter((snackbar) => snackbar.id !== id),
|
|
256
|
-
);
|
|
262
|
+
this.setState('toasts', this.toasts.concat(toast));
|
|
257
263
|
}
|
|
258
264
|
|
|
259
265
|
public registerComponent(name: UIComponent, component: Component): void {
|
|
@@ -263,17 +269,17 @@ export class UIService extends Service {
|
|
|
263
269
|
public async openModal<TModalComponent extends ModalComponent>(
|
|
264
270
|
component: TModalComponent,
|
|
265
271
|
properties?: ModalProperties<TModalComponent>,
|
|
266
|
-
): Promise<
|
|
272
|
+
): Promise<UIModal<ModalResult<TModalComponent>>> {
|
|
267
273
|
const id = uuid();
|
|
268
274
|
const callbacks: Partial<ModalCallbacks<ModalResult<TModalComponent>>> = {};
|
|
269
|
-
const modal:
|
|
275
|
+
const modal: UIModal<ModalResult<TModalComponent>> = {
|
|
270
276
|
id,
|
|
277
|
+
closing: false,
|
|
271
278
|
properties: properties ?? {},
|
|
272
279
|
component: markRaw(component),
|
|
273
280
|
beforeClose: new Promise((resolve) => (callbacks.willClose = resolve)),
|
|
274
|
-
afterClose: new Promise((resolve) => (callbacks.
|
|
281
|
+
afterClose: new Promise((resolve) => (callbacks.hasClosed = resolve)),
|
|
275
282
|
};
|
|
276
|
-
const activeModal = this.modals.at(-1);
|
|
277
283
|
const modals = this.modals.concat(modal);
|
|
278
284
|
|
|
279
285
|
this.modalCallbacks[modal.id] = callbacks;
|
|
@@ -281,11 +287,6 @@ export class UIService extends Service {
|
|
|
281
287
|
this.setState({ modals });
|
|
282
288
|
|
|
283
289
|
await nextTick();
|
|
284
|
-
await (activeModal && Events.emit('hide-modal', { id: activeModal.id }));
|
|
285
|
-
await Promise.all([
|
|
286
|
-
activeModal || Events.emit('show-overlays-backdrop'),
|
|
287
|
-
Events.emit('show-modal', { id: modal.id }),
|
|
288
|
-
]);
|
|
289
290
|
|
|
290
291
|
return modal;
|
|
291
292
|
}
|
|
@@ -318,25 +319,23 @@ export class UIService extends Service {
|
|
|
318
319
|
this.modals.filter((m) => m.id !== id),
|
|
319
320
|
);
|
|
320
321
|
|
|
321
|
-
this.modalCallbacks[id]?.
|
|
322
|
+
this.modalCallbacks[id]?.hasClosed?.(result);
|
|
322
323
|
|
|
323
324
|
delete this.modalCallbacks[id];
|
|
324
|
-
|
|
325
|
-
const activeModal = this.modals.at(-1);
|
|
326
|
-
|
|
327
|
-
await (activeModal && Events.emit('show-modal', { id: activeModal.id }));
|
|
328
325
|
}
|
|
329
326
|
|
|
330
327
|
private watchModalEvents(): void {
|
|
331
|
-
Events.on('modal-will-close', ({ modal, result }) => {
|
|
332
|
-
this.
|
|
328
|
+
Events.on('modal-will-close', ({ modal: { id }, result }) => {
|
|
329
|
+
const modal = this.modals.find((_modal) => id === _modal.id);
|
|
333
330
|
|
|
334
|
-
if (
|
|
335
|
-
|
|
331
|
+
if (modal) {
|
|
332
|
+
modal.closing = true;
|
|
336
333
|
}
|
|
334
|
+
|
|
335
|
+
this.modalCallbacks[id]?.willClose?.(result);
|
|
337
336
|
});
|
|
338
337
|
|
|
339
|
-
Events.on('modal-closed', async ({ modal: { id }, result }) => {
|
|
338
|
+
Events.on('modal-has-closed', async ({ modal: { id }, result }) => {
|
|
340
339
|
await this.removeModal(id, result);
|
|
341
340
|
});
|
|
342
341
|
}
|
|
@@ -380,11 +379,7 @@ export default facade(UIService);
|
|
|
380
379
|
declare module '@aerogel/core/services/Events' {
|
|
381
380
|
export interface EventsPayload {
|
|
382
381
|
'close-modal': { id: string; result?: unknown };
|
|
383
|
-
'
|
|
384
|
-
'
|
|
385
|
-
'modal-closed': { modal: Modal; result?: unknown };
|
|
386
|
-
'modal-will-close': { modal: Modal; result?: unknown };
|
|
387
|
-
'show-modal': { id: string };
|
|
388
|
-
'show-overlays-backdrop': void;
|
|
382
|
+
'modal-will-close': { modal: UIModal; result?: unknown };
|
|
383
|
+
'modal-has-closed': { modal: UIModal; result?: unknown };
|
|
389
384
|
}
|
|
390
385
|
}
|
package/src/ui/index.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import type { Component } from 'vue';
|
|
2
2
|
|
|
3
|
+
import AlertModal from '@aerogel/core/components/ui/AlertModal.vue';
|
|
4
|
+
import ConfirmModal from '@aerogel/core/components/ui/ConfirmModal.vue';
|
|
5
|
+
import ErrorReportModal from '@aerogel/core/components/ui/ErrorReportModal.vue';
|
|
6
|
+
import LoadingModal from '@aerogel/core/components/ui/LoadingModal.vue';
|
|
7
|
+
import PromptModal from '@aerogel/core/components/ui/PromptModal.vue';
|
|
8
|
+
import StartupCrash from '@aerogel/core/components/ui/StartupCrash.vue';
|
|
9
|
+
import Toast from '@aerogel/core/components/ui/Toast.vue';
|
|
3
10
|
import { bootServices } from '@aerogel/core/services';
|
|
4
11
|
import { definePlugin } from '@aerogel/core/plugins';
|
|
5
12
|
|
|
6
13
|
import UI, { UIComponents } from './UI';
|
|
7
|
-
import AGAlertModal from '../components/modals/AGAlertModal.vue';
|
|
8
|
-
import AGConfirmModal from '../components/modals/AGConfirmModal.vue';
|
|
9
|
-
import AGErrorReportModal from '../components/modals/AGErrorReportModal.vue';
|
|
10
|
-
import AGLoadingModal from '../components/modals/AGLoadingModal.vue';
|
|
11
|
-
import AGPromptModal from '../components/modals/AGPromptModal.vue';
|
|
12
|
-
import AGSnackbar from '../components/snackbars/AGSnackbar.vue';
|
|
13
|
-
import AGStartupCrash from '../components/lib/AGStartupCrash.vue';
|
|
14
14
|
import type { UIComponent } from './UI';
|
|
15
15
|
|
|
16
16
|
const services = { $ui: UI };
|
|
@@ -24,13 +24,13 @@ export type UIServices = typeof services;
|
|
|
24
24
|
export default definePlugin({
|
|
25
25
|
async install(app, options) {
|
|
26
26
|
const defaultComponents = {
|
|
27
|
-
[UIComponents.AlertModal]:
|
|
28
|
-
[UIComponents.ConfirmModal]:
|
|
29
|
-
[UIComponents.ErrorReportModal]:
|
|
30
|
-
[UIComponents.LoadingModal]:
|
|
31
|
-
[UIComponents.PromptModal]:
|
|
32
|
-
[UIComponents.
|
|
33
|
-
[UIComponents.StartupCrash]:
|
|
27
|
+
[UIComponents.AlertModal]: AlertModal,
|
|
28
|
+
[UIComponents.ConfirmModal]: ConfirmModal,
|
|
29
|
+
[UIComponents.ErrorReportModal]: ErrorReportModal,
|
|
30
|
+
[UIComponents.LoadingModal]: LoadingModal,
|
|
31
|
+
[UIComponents.PromptModal]: PromptModal,
|
|
32
|
+
[UIComponents.Toast]: Toast,
|
|
33
|
+
[UIComponents.StartupCrash]: StartupCrash,
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
Object.entries({
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import clsx from 'clsx';
|
|
2
|
+
import { computed, unref } from 'vue';
|
|
3
|
+
import { cva } from 'class-variance-authority';
|
|
4
|
+
import { twMerge } from 'tailwind-merge';
|
|
5
|
+
import type { ClassValue } from 'clsx';
|
|
6
|
+
import type { ComputedRef, PropType, Ref } from 'vue';
|
|
7
|
+
import type { GetClosureArgs, GetClosureResult } from '@noeldemartin/utils';
|
|
8
|
+
|
|
9
|
+
export type CVAConfig<T> = NonNullable<GetClosureArgs<typeof cva<T>>[1]>;
|
|
10
|
+
export type CVAProps<T> = NonNullable<GetClosureArgs<GetClosureResult<typeof cva<T>>>[0]>;
|
|
11
|
+
export type RefsObject<T> = { [K in keyof T]: Ref<T[K]> | T[K] };
|
|
12
|
+
export type Variants<T extends Record<string, string | boolean>> = Required<{
|
|
13
|
+
[K in keyof T]: Exclude<T[K], undefined> extends string
|
|
14
|
+
? { [key in Exclude<T[K], undefined>]: string | null }
|
|
15
|
+
: { true: string | null; false: string | null };
|
|
16
|
+
}>;
|
|
17
|
+
|
|
18
|
+
export type ComponentPropDefinitions<T> = {
|
|
19
|
+
[K in keyof T]: {
|
|
20
|
+
type?: PropType<T[K]>;
|
|
21
|
+
default: T[K] | (() => T[K]) | null;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type PickComponentProps<TValues, TDefinitions> = {
|
|
26
|
+
[K in keyof TValues]: K extends keyof TDefinitions ? TValues[K] : never;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export function computedVariantClasses<T>(
|
|
30
|
+
value: RefsObject<{ baseClasses?: string } & CVAProps<T>>,
|
|
31
|
+
config: { baseClasses?: string } & CVAConfig<T>,
|
|
32
|
+
): ComputedRef<string> {
|
|
33
|
+
return computed(() => {
|
|
34
|
+
const { baseClasses: valueBaseClasses, ...valueRefs } = value;
|
|
35
|
+
const { baseClasses: configBaseClasses, ...configs } = config;
|
|
36
|
+
const variants = cva(configBaseClasses, configs as CVAConfig<T>);
|
|
37
|
+
const values = Object.entries(valueRefs).reduce((extractedValues, [name, valueRef]) => {
|
|
38
|
+
extractedValues[name as keyof CVAProps<T>] = unref(valueRef);
|
|
39
|
+
|
|
40
|
+
return extractedValues;
|
|
41
|
+
}, {} as CVAProps<T>);
|
|
42
|
+
|
|
43
|
+
return classes(variants(values), unref(valueBaseClasses));
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function classes(...inputs: ClassValue[]): string {
|
|
48
|
+
return twMerge(clsx(inputs));
|
|
49
|
+
}
|
|
@@ -6,7 +6,6 @@ import type {
|
|
|
6
6
|
EventWithPayload,
|
|
7
7
|
EventWithoutPayload,
|
|
8
8
|
EventsPayload,
|
|
9
|
-
UnknownEvent,
|
|
10
9
|
} from '@aerogel/core/services/Events';
|
|
11
10
|
|
|
12
11
|
export function useEvent<Event extends EventWithoutPayload>(event: Event, listener: () => unknown): void;
|
|
@@ -14,11 +13,10 @@ export function useEvent<Event extends EventWithPayload>(
|
|
|
14
13
|
event: Event,
|
|
15
14
|
listener: EventListener<EventsPayload[Event]>
|
|
16
15
|
): void;
|
|
17
|
-
export function useEvent<Payload>(event: string, listener: (payload: Payload) => unknown): void;
|
|
18
|
-
export function useEvent<Event extends string>(event: UnknownEvent<Event>, listener: EventListener): void;
|
|
19
16
|
|
|
20
17
|
export function useEvent(event: string, listener: EventListener): void {
|
|
21
|
-
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
const unsubscribe = Events.on(event as any, listener);
|
|
22
20
|
|
|
23
21
|
onUnmounted(() => unsubscribe());
|
|
24
22
|
}
|
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
import { objectWithout } from '@noeldemartin/utils';
|
|
2
|
-
import { computed, useAttrs } from 'vue';
|
|
2
|
+
import { computed, inject, onUnmounted, useAttrs } from 'vue';
|
|
3
|
+
import type { ClassValue } from 'clsx';
|
|
3
4
|
import type { ComputedRef } from 'vue';
|
|
5
|
+
import type { Nullable } from '@noeldemartin/utils';
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
import FormController from '@aerogel/core/forms/FormController';
|
|
8
|
+
import type { FormData, FormFieldDefinitions } from '@aerogel/core/forms/FormController';
|
|
9
|
+
|
|
10
|
+
export function onFormFocus(input: { name: Nullable<string> }, listener: () => unknown): void {
|
|
11
|
+
const form = inject<FormController | null>('form', null);
|
|
12
|
+
const stop = form?.on('focus', (name) => input.name === name && listener());
|
|
13
|
+
|
|
14
|
+
onUnmounted(() => stop?.());
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function useForm<const T extends FormFieldDefinitions>(fields: T): FormController<T> & FormData<T> {
|
|
18
|
+
return new FormController(fields) as FormController<T> & FormData<T>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useInputAttrs(): [ComputedRef<{}>, ComputedRef<ClassValue>] {
|
|
6
22
|
const attrs = useAttrs();
|
|
7
|
-
const
|
|
23
|
+
const classes = computed(() => attrs.class);
|
|
8
24
|
const inputAttrs = computed(() => objectWithout(attrs, 'class'));
|
|
9
25
|
|
|
10
|
-
return [inputAttrs,
|
|
26
|
+
return [inputAttrs, classes as ComputedRef<ClassValue>];
|
|
11
27
|
}
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import { debounce } from '@noeldemartin/utils';
|
|
2
|
-
import { ref, watchEffect } from 'vue';
|
|
3
|
-
import type { ComputedGetter, ComputedRef } from '
|
|
2
|
+
import { computed, ref, watch, watchEffect } from 'vue';
|
|
3
|
+
import type { ComputedGetter, ComputedRef, Ref } from 'vue';
|
|
4
4
|
|
|
5
5
|
export interface ComputedDebounceOptions<T> {
|
|
6
6
|
initial?: T;
|
|
7
7
|
delay?: number;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
export function computedAsync<T>(getter: () => Promise<T>): Ref<T | undefined> {
|
|
11
|
+
const result = ref<T>();
|
|
12
|
+
const asyncValue = computed(getter);
|
|
13
|
+
|
|
14
|
+
watch(asyncValue, async () => (result.value = await asyncValue.value), { immediate: true });
|
|
15
|
+
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
|
|
10
19
|
export function computedDebounce<T>(options: ComputedDebounceOptions<T>, getter: ComputedGetter<T>): ComputedRef<T>;
|
|
11
20
|
export function computedDebounce<T>(getter: ComputedGetter<T>): ComputedRef<T | null>;
|
|
12
21
|
export function computedDebounce<T>(
|
package/src/utils/index.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
export * from './classes';
|
|
1
2
|
export * from './composition/events';
|
|
2
3
|
export * from './composition/forms';
|
|
3
4
|
export * from './composition/hooks';
|
|
4
5
|
export * from './composition/persistent';
|
|
6
|
+
export * from './composition/state';
|
|
5
7
|
export * from './markdown';
|
|
6
|
-
export * from './
|
|
8
|
+
export * from './types';
|
|
7
9
|
export * from './vue';
|