@aerogel/core 0.0.0-next.c29ffcd25bffdbed37ecce3aac1ba14cde3e9d39 → 0.0.0-next.c33ad773d3eb977461630ff22012d99eeedf46cb
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 +1840 -1813
- package/dist/aerogel-core.js +3129 -0
- package/dist/aerogel-core.js.map +1 -0
- package/package.json +27 -37
- package/src/bootstrap/bootstrap.test.ts +4 -7
- package/src/bootstrap/index.ts +14 -15
- package/src/bootstrap/options.ts +1 -1
- package/src/components/{AGAppLayout.vue → AppLayout.vue} +4 -4
- package/src/components/{AGAppModals.vue → AppModals.vue} +3 -4
- package/src/components/{AGAppOverlays.vue → AppOverlays.vue} +5 -10
- package/src/components/AppToasts.vue +16 -0
- package/src/components/composition.ts +1 -1
- package/src/components/contracts/AlertModal.ts +4 -0
- package/src/components/contracts/Button.ts +16 -0
- package/src/components/contracts/ConfirmModal.ts +41 -0
- package/src/components/contracts/DropdownMenu.ts +11 -0
- package/src/components/contracts/ErrorReportModal.ts +29 -0
- package/src/components/contracts/Input.ts +26 -0
- package/src/components/contracts/LoadingModal.ts +18 -0
- package/src/components/contracts/Modal.ts +13 -0
- package/src/components/contracts/PromptModal.ts +28 -0
- package/src/components/contracts/Select.ts +33 -0
- package/src/components/contracts/Toast.ts +13 -0
- package/src/components/contracts/index.ts +9 -0
- package/src/components/contracts/shared.ts +9 -0
- package/src/components/headless/HeadlessButton.vue +50 -0
- package/src/components/headless/HeadlessInput.vue +59 -0
- package/src/components/headless/{forms/AGHeadlessInputDescription.vue → HeadlessInputDescription.vue} +7 -8
- package/src/components/headless/{forms/AGHeadlessInputError.vue → HeadlessInputError.vue} +4 -8
- package/src/components/headless/{forms/AGHeadlessInputInput.vue → HeadlessInputInput.vue} +13 -22
- package/src/components/headless/{forms/AGHeadlessInputLabel.vue → HeadlessInputLabel.vue} +3 -7
- package/src/components/headless/{forms/AGHeadlessInputTextArea.vue → HeadlessInputTextArea.vue} +8 -11
- package/src/components/headless/{modals/AGHeadlessModal.vue → HeadlessModal.vue} +17 -23
- package/src/components/headless/HeadlessModalContent.vue +24 -0
- package/src/components/headless/HeadlessModalOverlay.vue +12 -0
- package/src/components/headless/HeadlessModalTitle.vue +12 -0
- package/src/components/headless/HeadlessSelect.vue +92 -0
- package/src/components/headless/{forms/AGHeadlessSelectError.vue → HeadlessSelectError.vue} +5 -6
- package/src/components/headless/HeadlessSelectLabel.vue +25 -0
- package/src/components/headless/HeadlessSelectOption.vue +34 -0
- package/src/components/headless/HeadlessSelectOptions.vue +30 -0
- package/src/components/headless/HeadlessSelectTrigger.vue +22 -0
- package/src/components/headless/HeadlessSelectValue.vue +15 -0
- package/src/components/headless/HeadlessToast.vue +18 -0
- package/src/components/headless/HeadlessToastAction.vue +13 -0
- package/src/components/headless/index.ts +18 -3
- package/src/components/index.ts +4 -10
- 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 +33 -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} +34 -27
- 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 +70 -0
- package/src/components/{modals/AGModalContext.vue → ui/ModalContext.vue} +7 -9
- package/src/components/ui/ProgressBar.vue +50 -0
- package/src/components/ui/PromptModal.vue +35 -0
- package/src/components/ui/Select.vue +53 -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 +23 -0
- package/src/components/utils.ts +106 -9
- package/src/directives/index.ts +9 -5
- package/src/directives/measure.ts +1 -1
- package/src/errors/Errors.state.ts +1 -1
- package/src/errors/Errors.ts +17 -18
- package/src/errors/index.ts +9 -6
- package/src/errors/utils.ts +1 -1
- package/src/forms/{Form.test.ts → FormController.test.ts} +5 -4
- package/src/forms/{Form.ts → FormController.ts} +22 -19
- package/src/forms/composition.ts +4 -4
- package/src/forms/index.ts +2 -2
- package/src/forms/utils.ts +2 -2
- package/src/index.css +41 -0
- package/src/jobs/Job.ts +2 -2
- package/src/lang/DefaultLangProvider.ts +7 -4
- package/src/lang/Lang.state.ts +1 -1
- package/src/lang/Lang.ts +1 -1
- package/src/lang/index.ts +8 -6
- package/src/plugins/Plugin.ts +1 -1
- package/src/plugins/index.ts +10 -7
- package/src/services/App.state.ts +4 -3
- package/src/services/App.ts +4 -4
- package/src/services/Cache.ts +1 -1
- package/src/services/Events.ts +2 -2
- package/src/services/Service.ts +21 -21
- package/src/services/Storage.ts +3 -3
- package/src/services/index.ts +5 -4
- package/src/services/utils.ts +2 -2
- package/src/testing/index.ts +4 -3
- package/src/testing/setup.ts +3 -19
- package/src/ui/UI.state.ts +12 -7
- package/src/ui/UI.ts +63 -58
- package/src/ui/index.ts +18 -18
- package/src/utils/composition/events.ts +2 -2
- package/src/utils/composition/forms.ts +4 -3
- package/src/utils/markdown.ts +3 -5
- package/src/utils/vdom.ts +31 -0
- package/src/utils/vue.ts +7 -16
- package/dist/aerogel-core.cjs.js +0 -2
- package/dist/aerogel-core.cjs.js.map +0 -1
- package/dist/aerogel-core.esm.js +0 -2
- package/dist/aerogel-core.esm.js.map +0 -1
- package/histoire.config.ts +0 -7
- package/noeldemartin.config.js +0 -5
- package/postcss.config.js +0 -6
- package/src/assets/histoire.css +0 -3
- package/src/components/AGAppSnackbars.vue +0 -13
- package/src/components/constants.ts +0 -8
- package/src/components/forms/AGButton.vue +0 -44
- package/src/components/forms/AGCheckbox.vue +0 -41
- package/src/components/forms/AGInput.vue +0 -40
- 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 -34
- 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/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 -45
- 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.ts +0 -49
- 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 -39
- 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/directives/initial-focus.ts +0 -11
- package/src/main.histoire.ts +0 -1
- package/tailwind.config.js +0 -4
- package/tsconfig.json +0 -11
- package/vite.config.ts +0 -17
- /package/src/{main.ts → index.ts} +0 -0
package/src/services/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { App as VueApp } from 'vue';
|
|
2
2
|
|
|
3
|
-
import { definePlugin } from '
|
|
3
|
+
import { definePlugin } from '@aerogel/core/plugins';
|
|
4
|
+
import { isDevelopment, isTesting } from '@noeldemartin/utils';
|
|
4
5
|
|
|
5
6
|
import App from './App';
|
|
6
7
|
import Cache from './Cache';
|
|
@@ -39,7 +40,7 @@ export async function bootServices(app: VueApp, services: Record<string, Service
|
|
|
39
40
|
|
|
40
41
|
Object.assign(app.config.globalProperties, services);
|
|
41
42
|
|
|
42
|
-
if (
|
|
43
|
+
if (isDevelopment() || isTesting()) {
|
|
43
44
|
Object.assign(globalThis, services);
|
|
44
45
|
}
|
|
45
46
|
}
|
|
@@ -57,12 +58,12 @@ export default definePlugin({
|
|
|
57
58
|
},
|
|
58
59
|
});
|
|
59
60
|
|
|
60
|
-
declare module '
|
|
61
|
+
declare module '@aerogel/core/bootstrap/options' {
|
|
61
62
|
export interface AerogelOptions {
|
|
62
63
|
services?: Record<string, Service>;
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
declare module '
|
|
67
|
+
declare module 'vue' {
|
|
67
68
|
interface ComponentCustomProperties extends Services {}
|
|
68
69
|
}
|
package/src/services/utils.ts
CHANGED
|
@@ -2,14 +2,14 @@ import { objectOnly } from '@noeldemartin/utils';
|
|
|
2
2
|
|
|
3
3
|
export type Replace<
|
|
4
4
|
TOriginal extends Record<string, unknown>,
|
|
5
|
-
TReplacements extends Partial<Record<keyof TOriginal, unknown
|
|
5
|
+
TReplacements extends Partial<Record<keyof TOriginal, unknown>>,
|
|
6
6
|
> = {
|
|
7
7
|
[K in keyof TOriginal]: TReplacements extends Record<K, infer Replacement> ? Replacement : TOriginal[K];
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
export function replaceExisting<
|
|
11
11
|
TOriginal extends Record<string, unknown>,
|
|
12
|
-
TReplacements extends Partial<Record<keyof TOriginal, unknown
|
|
12
|
+
TReplacements extends Partial<Record<keyof TOriginal, unknown>>,
|
|
13
13
|
>(original: TOriginal, replacements: TReplacements): Replace<TOriginal, TReplacements> {
|
|
14
14
|
return {
|
|
15
15
|
...original,
|
package/src/testing/index.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { isTesting } from '@noeldemartin/utils';
|
|
1
2
|
import type { GetClosureArgs } from '@noeldemartin/utils';
|
|
2
3
|
|
|
3
|
-
import Events from '
|
|
4
|
-
import { definePlugin } from '
|
|
4
|
+
import Events from '@aerogel/core/services/Events';
|
|
5
|
+
import { definePlugin } from '@aerogel/core/plugins';
|
|
5
6
|
|
|
6
7
|
export interface AerogelTestingRuntime {
|
|
7
8
|
on: (typeof Events)['on'];
|
|
@@ -9,7 +10,7 @@ export interface AerogelTestingRuntime {
|
|
|
9
10
|
|
|
10
11
|
export default definePlugin({
|
|
11
12
|
async install() {
|
|
12
|
-
if (
|
|
13
|
+
if (!isTesting()) {
|
|
13
14
|
return;
|
|
14
15
|
}
|
|
15
16
|
|
package/src/testing/setup.ts
CHANGED
|
@@ -1,27 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FakeLocalStorage } from '@noeldemartin/testing';
|
|
2
2
|
import { beforeEach, vi } from 'vitest';
|
|
3
3
|
|
|
4
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5
|
-
tap(globalThis, (global: any) => {
|
|
6
|
-
const localStorage: Record<string, string> = {};
|
|
7
|
-
|
|
8
|
-
global.jest = vi;
|
|
9
|
-
global.navigator = { languages: ['en'] };
|
|
10
|
-
global.localStorage = mock<Storage>({
|
|
11
|
-
getItem: (key) => localStorage[key] ?? null,
|
|
12
|
-
setItem(key, value) {
|
|
13
|
-
localStorage[key] = toString(value);
|
|
14
|
-
},
|
|
15
|
-
});
|
|
16
|
-
});
|
|
17
|
-
|
|
18
4
|
vi.mock('dompurify', async () => {
|
|
19
5
|
return { default: { sanitize: (html: string) => html } };
|
|
20
6
|
});
|
|
21
7
|
|
|
22
8
|
beforeEach(() => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
getElementById: () => null,
|
|
26
|
-
});
|
|
9
|
+
FakeLocalStorage.reset();
|
|
10
|
+
FakeLocalStorage.patchGlobal();
|
|
27
11
|
});
|
package/src/ui/UI.state.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Component } from 'vue';
|
|
2
2
|
|
|
3
|
-
import { defineServiceState } from '
|
|
3
|
+
import { defineServiceState } from '@aerogel/core/services/Service';
|
|
4
4
|
|
|
5
5
|
import { Layouts, getCurrentLayout } from './utils';
|
|
6
6
|
|
|
7
|
-
export interface
|
|
7
|
+
export interface UIModal<T = unknown> {
|
|
8
8
|
id: string;
|
|
9
9
|
properties: Record<string, unknown>;
|
|
10
10
|
component: Component;
|
|
@@ -12,14 +12,19 @@ export interface Modal<T = unknown> {
|
|
|
12
12
|
afterClose: Promise<T | undefined>;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
export interface UIModalContext {
|
|
16
|
+
modal: UIModal;
|
|
17
|
+
childIndex?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
export interface ModalComponent<
|
|
16
21
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
17
|
-
Properties extends
|
|
22
|
+
Properties extends object = object,
|
|
18
23
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
19
|
-
Result = unknown
|
|
24
|
+
Result = unknown,
|
|
20
25
|
> {}
|
|
21
26
|
|
|
22
|
-
export interface
|
|
27
|
+
export interface UIToast {
|
|
23
28
|
id: string;
|
|
24
29
|
component: Component;
|
|
25
30
|
properties: Record<string, unknown>;
|
|
@@ -28,8 +33,8 @@ export interface Snackbar {
|
|
|
28
33
|
export default defineServiceState({
|
|
29
34
|
name: 'ui',
|
|
30
35
|
initialState: {
|
|
31
|
-
modals: [] as
|
|
32
|
-
|
|
36
|
+
modals: [] as UIModal[],
|
|
37
|
+
toasts: [] as UIToast[],
|
|
33
38
|
layout: getCurrentLayout(),
|
|
34
39
|
},
|
|
35
40
|
computed: {
|
package/src/ui/UI.ts
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import { after, facade, fail, required, uuid } from '@noeldemartin/utils';
|
|
1
|
+
import { after, facade, fail, isDevelopment, required, uuid } from '@noeldemartin/utils';
|
|
2
2
|
import { markRaw, nextTick } from 'vue';
|
|
3
3
|
import type { Component } from 'vue';
|
|
4
4
|
import type { ObjectValues } from '@noeldemartin/utils';
|
|
5
5
|
|
|
6
|
-
import App from '
|
|
7
|
-
import Events from '
|
|
8
|
-
import type { AcceptRefs } from '
|
|
9
|
-
import type {
|
|
10
|
-
import type {
|
|
11
|
-
import type {
|
|
6
|
+
import App from '@aerogel/core/services/App';
|
|
7
|
+
import Events from '@aerogel/core/services/Events';
|
|
8
|
+
import type { AcceptRefs } from '@aerogel/core/utils';
|
|
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';
|
|
12
15
|
|
|
13
16
|
import Service from './UI.state';
|
|
14
17
|
import { MOBILE_BREAKPOINT, getCurrentLayout } from './utils';
|
|
15
|
-
import type {
|
|
18
|
+
import type { ModalComponent, UIModal, UIToast } from './UI.state';
|
|
16
19
|
|
|
17
20
|
interface ModalCallbacks<T = unknown> {
|
|
18
21
|
willClose(result: T | undefined): void;
|
|
@@ -20,9 +23,8 @@ interface ModalCallbacks<T = unknown> {
|
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
type ModalProperties<TComponent> = TComponent extends ModalComponent<infer TProperties, unknown> ? TProperties : never;
|
|
23
|
-
type ModalResult<TComponent> =
|
|
24
|
-
? TResult
|
|
25
|
-
: never;
|
|
26
|
+
type ModalResult<TComponent> =
|
|
27
|
+
TComponent extends ModalComponent<Record<string, unknown>, infer TResult> ? TResult : never;
|
|
26
28
|
|
|
27
29
|
export const UIComponents = {
|
|
28
30
|
AlertModal: 'alert-modal',
|
|
@@ -30,19 +32,17 @@ export const UIComponents = {
|
|
|
30
32
|
ErrorReportModal: 'error-report-modal',
|
|
31
33
|
LoadingModal: 'loading-modal',
|
|
32
34
|
PromptModal: 'prompt-modal',
|
|
33
|
-
|
|
35
|
+
Toast: 'toast',
|
|
34
36
|
StartupCrash: 'startup-crash',
|
|
35
37
|
} as const;
|
|
36
38
|
|
|
37
39
|
export type UIComponent = ObjectValues<typeof UIComponents>;
|
|
38
40
|
|
|
39
|
-
export type ConfirmCheckboxes = Record<string, { label: string; default?: boolean; required?: boolean }>;
|
|
40
|
-
|
|
41
41
|
export type ConfirmOptions = AcceptRefs<{
|
|
42
42
|
acceptText?: string;
|
|
43
|
-
|
|
43
|
+
acceptVariant?: ButtonVariant;
|
|
44
44
|
cancelText?: string;
|
|
45
|
-
|
|
45
|
+
cancelVariant?: ButtonVariant;
|
|
46
46
|
actions?: Record<string, () => unknown>;
|
|
47
47
|
required?: boolean;
|
|
48
48
|
}>;
|
|
@@ -53,7 +53,8 @@ export type LoadingOptions = AcceptRefs<{
|
|
|
53
53
|
progress?: number;
|
|
54
54
|
}>;
|
|
55
55
|
|
|
56
|
-
export interface ConfirmOptionsWithCheckboxes<T extends
|
|
56
|
+
export interface ConfirmOptionsWithCheckboxes<T extends ConfirmModalCheckboxes = ConfirmModalCheckboxes>
|
|
57
|
+
extends ConfirmOptions {
|
|
57
58
|
checkboxes?: T;
|
|
58
59
|
}
|
|
59
60
|
|
|
@@ -62,16 +63,16 @@ export type PromptOptions = AcceptRefs<{
|
|
|
62
63
|
defaultValue?: string;
|
|
63
64
|
placeholder?: string;
|
|
64
65
|
acceptText?: string;
|
|
65
|
-
|
|
66
|
+
acceptVariant?: ButtonVariant;
|
|
66
67
|
cancelText?: string;
|
|
67
|
-
|
|
68
|
+
cancelVariant?: ButtonVariant;
|
|
68
69
|
trim?: boolean;
|
|
69
70
|
}>;
|
|
70
71
|
|
|
71
|
-
export interface
|
|
72
|
+
export interface ToastOptions {
|
|
72
73
|
component?: Component;
|
|
73
|
-
|
|
74
|
-
actions?:
|
|
74
|
+
variant?: ToastVariant;
|
|
75
|
+
actions?: ToastAction[];
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
export class UIService extends Service {
|
|
@@ -86,7 +87,7 @@ export class UIService extends Service {
|
|
|
86
87
|
public alert(message: string): void;
|
|
87
88
|
public alert(title: string, message: string): void;
|
|
88
89
|
public alert(messageOrTitle: string, message?: string): void {
|
|
89
|
-
const getProperties = ():
|
|
90
|
+
const getProperties = (): AlertModalProps => {
|
|
90
91
|
if (typeof message !== 'string') {
|
|
91
92
|
return { message: messageOrTitle };
|
|
92
93
|
}
|
|
@@ -97,14 +98,17 @@ export class UIService extends Service {
|
|
|
97
98
|
};
|
|
98
99
|
};
|
|
99
100
|
|
|
100
|
-
this.openModal(
|
|
101
|
+
this.openModal<ModalComponent<AlertModalProps>>(
|
|
102
|
+
this.requireComponent(UIComponents.AlertModal),
|
|
103
|
+
getProperties(),
|
|
104
|
+
);
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
/* eslint-disable max-len */
|
|
104
108
|
public async confirm(message: string, options?: ConfirmOptions): Promise<boolean>;
|
|
105
109
|
public async confirm(title: string, message: string, options?: ConfirmOptions): Promise<boolean>;
|
|
106
|
-
public async confirm<T extends
|
|
107
|
-
public async confirm<T extends
|
|
110
|
+
public async confirm<T extends ConfirmModalCheckboxes>(message: string, options?: ConfirmOptionsWithCheckboxes<T>): Promise<[boolean, Record<keyof T, boolean>]>; // prettier-ignore
|
|
111
|
+
public async confirm<T extends ConfirmModalCheckboxes>(title: string, message: string, options?: ConfirmOptionsWithCheckboxes<T>): Promise<[boolean, Record<keyof T, boolean>]>; // prettier-ignore
|
|
108
112
|
/* eslint-enable max-len */
|
|
109
113
|
|
|
110
114
|
public async confirm(
|
|
@@ -112,7 +116,7 @@ export class UIService extends Service {
|
|
|
112
116
|
messageOrOptions?: string | ConfirmOptions | ConfirmOptionsWithCheckboxes,
|
|
113
117
|
options?: ConfirmOptions | ConfirmOptionsWithCheckboxes,
|
|
114
118
|
): Promise<boolean | [boolean, Record<string, boolean>]> {
|
|
115
|
-
const getProperties = ():
|
|
119
|
+
const getProperties = (): AcceptRefs<ConfirmModalProps> => {
|
|
116
120
|
if (typeof messageOrOptions !== 'string') {
|
|
117
121
|
return {
|
|
118
122
|
...(messageOrOptions ?? {}),
|
|
@@ -128,13 +132,20 @@ export class UIService extends Service {
|
|
|
128
132
|
required: !!options?.required,
|
|
129
133
|
};
|
|
130
134
|
};
|
|
135
|
+
|
|
136
|
+
type ConfirmModalComponent = ModalComponent<
|
|
137
|
+
AcceptRefs<ConfirmModalProps>,
|
|
138
|
+
boolean | [boolean, Record<string, boolean>]
|
|
139
|
+
>;
|
|
140
|
+
|
|
131
141
|
const properties = getProperties();
|
|
132
|
-
const modal = await this.openModal<
|
|
133
|
-
|
|
134
|
-
|
|
142
|
+
const modal = await this.openModal<ConfirmModalComponent>(
|
|
143
|
+
this.requireComponent(UIComponents.ConfirmModal),
|
|
144
|
+
properties,
|
|
145
|
+
);
|
|
135
146
|
const result = await modal.beforeClose;
|
|
136
147
|
|
|
137
|
-
const confirmed = typeof result === 'object' ? result[0] : result ?? false;
|
|
148
|
+
const confirmed = typeof result === 'object' ? result[0] : (result ?? false);
|
|
138
149
|
const checkboxes =
|
|
139
150
|
typeof result === 'object'
|
|
140
151
|
? result[1]
|
|
@@ -151,7 +162,7 @@ export class UIService extends Service {
|
|
|
151
162
|
continue;
|
|
152
163
|
}
|
|
153
164
|
|
|
154
|
-
if (confirmed &&
|
|
165
|
+
if (confirmed && isDevelopment()) {
|
|
155
166
|
// eslint-disable-next-line no-console
|
|
156
167
|
console.warn(`Confirmed confirm modal was suppressed because required '${name}' checkbox was missing`);
|
|
157
168
|
}
|
|
@@ -170,22 +181,22 @@ export class UIService extends Service {
|
|
|
170
181
|
options?: PromptOptions,
|
|
171
182
|
): Promise<string | null> {
|
|
172
183
|
const trim = options?.trim ?? true;
|
|
173
|
-
const getProperties = ():
|
|
184
|
+
const getProperties = (): PromptModalProps => {
|
|
174
185
|
if (typeof messageOrOptions !== 'string') {
|
|
175
186
|
return {
|
|
176
187
|
message: messageOrTitle,
|
|
177
188
|
...(messageOrOptions ?? {}),
|
|
178
|
-
} as
|
|
189
|
+
} as PromptModalProps;
|
|
179
190
|
}
|
|
180
191
|
|
|
181
192
|
return {
|
|
182
193
|
title: messageOrTitle,
|
|
183
194
|
message: messageOrOptions,
|
|
184
195
|
...(options ?? {}),
|
|
185
|
-
} as
|
|
196
|
+
} as PromptModalProps;
|
|
186
197
|
};
|
|
187
198
|
|
|
188
|
-
const modal = await this.openModal<ModalComponent<
|
|
199
|
+
const modal = await this.openModal<ModalComponent<PromptModalProps, string | null>>(
|
|
189
200
|
this.requireComponent(UIComponents.PromptModal),
|
|
190
201
|
getProperties(),
|
|
191
202
|
);
|
|
@@ -203,7 +214,7 @@ export class UIService extends Service {
|
|
|
203
214
|
operation?: Promise<T> | (() => T),
|
|
204
215
|
): Promise<T> {
|
|
205
216
|
const processOperation = (o: Promise<T> | (() => T)) => (typeof o === 'function' ? Promise.resolve(o()) : o);
|
|
206
|
-
const processArgs = (): { operationPromise: Promise<T>; props?:
|
|
217
|
+
const processArgs = (): { operationPromise: Promise<T>; props?: AcceptRefs<LoadingModalProps> } => {
|
|
207
218
|
if (typeof operationOrMessageOrOptions === 'string') {
|
|
208
219
|
return {
|
|
209
220
|
props: { message: operationOrMessageOrOptions },
|
|
@@ -225,7 +236,9 @@ export class UIService extends Service {
|
|
|
225
236
|
const modal = await this.openModal(this.requireComponent(UIComponents.LoadingModal), props);
|
|
226
237
|
|
|
227
238
|
try {
|
|
228
|
-
const
|
|
239
|
+
const result = await operationPromise;
|
|
240
|
+
|
|
241
|
+
await after({ ms: 500 });
|
|
229
242
|
|
|
230
243
|
return result;
|
|
231
244
|
} finally {
|
|
@@ -233,23 +246,15 @@ export class UIService extends Service {
|
|
|
233
246
|
}
|
|
234
247
|
}
|
|
235
248
|
|
|
236
|
-
public
|
|
237
|
-
const
|
|
249
|
+
public toast(message: string, options: ToastOptions = {}): void {
|
|
250
|
+
const { component, ...otherOptions } = options;
|
|
251
|
+
const toast: UIToast = {
|
|
238
252
|
id: uuid(),
|
|
239
|
-
properties: { message, ...
|
|
240
|
-
component: markRaw(
|
|
253
|
+
properties: { message, ...otherOptions },
|
|
254
|
+
component: markRaw(component ?? this.requireComponent(UIComponents.Toast)),
|
|
241
255
|
};
|
|
242
256
|
|
|
243
|
-
this.setState('
|
|
244
|
-
|
|
245
|
-
setTimeout(() => this.hideSnackbar(snackbar.id), 5000);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
public hideSnackbar(id: string): void {
|
|
249
|
-
this.setState(
|
|
250
|
-
'snackbars',
|
|
251
|
-
this.snackbars.filter((snackbar) => snackbar.id !== id),
|
|
252
|
-
);
|
|
257
|
+
this.setState('toasts', this.toasts.concat(toast));
|
|
253
258
|
}
|
|
254
259
|
|
|
255
260
|
public registerComponent(name: UIComponent, component: Component): void {
|
|
@@ -259,10 +264,10 @@ export class UIService extends Service {
|
|
|
259
264
|
public async openModal<TModalComponent extends ModalComponent>(
|
|
260
265
|
component: TModalComponent,
|
|
261
266
|
properties?: ModalProperties<TModalComponent>,
|
|
262
|
-
): Promise<
|
|
267
|
+
): Promise<UIModal<ModalResult<TModalComponent>>> {
|
|
263
268
|
const id = uuid();
|
|
264
269
|
const callbacks: Partial<ModalCallbacks<ModalResult<TModalComponent>>> = {};
|
|
265
|
-
const modal:
|
|
270
|
+
const modal: UIModal<ModalResult<TModalComponent>> = {
|
|
266
271
|
id,
|
|
267
272
|
properties: properties ?? {},
|
|
268
273
|
component: markRaw(component),
|
|
@@ -302,7 +307,7 @@ export class UIService extends Service {
|
|
|
302
307
|
}
|
|
303
308
|
}
|
|
304
309
|
|
|
305
|
-
protected async boot(): Promise<void> {
|
|
310
|
+
protected override async boot(): Promise<void> {
|
|
306
311
|
this.watchModalEvents();
|
|
307
312
|
this.watchMountedEvent();
|
|
308
313
|
this.watchViewportBreakpoints();
|
|
@@ -373,13 +378,13 @@ export class UIService extends Service {
|
|
|
373
378
|
|
|
374
379
|
export default facade(UIService);
|
|
375
380
|
|
|
376
|
-
declare module '
|
|
381
|
+
declare module '@aerogel/core/services/Events' {
|
|
377
382
|
export interface EventsPayload {
|
|
378
383
|
'close-modal': { id: string; result?: unknown };
|
|
379
384
|
'hide-modal': { id: string };
|
|
380
385
|
'hide-overlays-backdrop': void;
|
|
381
|
-
'modal-closed': { modal:
|
|
382
|
-
'modal-will-close': { modal:
|
|
386
|
+
'modal-closed': { modal: UIModal; result?: unknown };
|
|
387
|
+
'modal-will-close': { modal: UIModal; result?: unknown };
|
|
383
388
|
'show-modal': { id: string };
|
|
384
389
|
'show-overlays-backdrop': void;
|
|
385
390
|
}
|
package/src/ui/index.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import type { Component } from 'vue';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
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';
|
|
10
|
+
import { bootServices } from '@aerogel/core/services';
|
|
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({
|
|
@@ -42,12 +42,12 @@ export default definePlugin({
|
|
|
42
42
|
},
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
declare module '
|
|
45
|
+
declare module '@aerogel/core/bootstrap/options' {
|
|
46
46
|
export interface AerogelOptions {
|
|
47
47
|
components?: Partial<Record<UIComponent, Component>>;
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
declare module '
|
|
51
|
+
declare module '@aerogel/core/services' {
|
|
52
52
|
export interface Services extends UIServices {}
|
|
53
53
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { onUnmounted } from 'vue';
|
|
2
2
|
|
|
3
|
-
import Events from '
|
|
3
|
+
import Events from '@aerogel/core/services/Events';
|
|
4
4
|
import type {
|
|
5
5
|
EventListener,
|
|
6
6
|
EventWithPayload,
|
|
7
7
|
EventWithoutPayload,
|
|
8
8
|
EventsPayload,
|
|
9
9
|
UnknownEvent,
|
|
10
|
-
} from '
|
|
10
|
+
} from '@aerogel/core/services/Events';
|
|
11
11
|
|
|
12
12
|
export function useEvent<Event extends EventWithoutPayload>(event: Event, listener: () => unknown): void;
|
|
13
13
|
export function useEvent<Event extends EventWithPayload>(
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { objectWithout } from '@noeldemartin/utils';
|
|
2
2
|
import { computed, useAttrs } from 'vue';
|
|
3
|
+
import type { ClassValue } from 'clsx';
|
|
3
4
|
import type { ComputedRef } from 'vue';
|
|
4
5
|
|
|
5
|
-
export function useInputAttrs(): [ComputedRef<{}>, ComputedRef<
|
|
6
|
+
export function useInputAttrs(): [ComputedRef<{}>, ComputedRef<ClassValue>] {
|
|
6
7
|
const attrs = useAttrs();
|
|
7
|
-
const
|
|
8
|
+
const classes = computed(() => attrs.class);
|
|
8
9
|
const inputAttrs = computed(() => objectWithout(attrs, 'class'));
|
|
9
10
|
|
|
10
|
-
return [inputAttrs,
|
|
11
|
+
return [inputAttrs, classes as ComputedRef<ClassValue>];
|
|
11
12
|
}
|
package/src/utils/markdown.ts
CHANGED
|
@@ -4,8 +4,8 @@ import { Renderer, marked } from 'marked';
|
|
|
4
4
|
|
|
5
5
|
function makeRenderer(): Renderer {
|
|
6
6
|
return tap(new Renderer(), (renderer) => {
|
|
7
|
-
renderer.link = function(
|
|
8
|
-
return Renderer.prototype.link.apply(this, [
|
|
7
|
+
renderer.link = function(link) {
|
|
8
|
+
return Renderer.prototype.link.apply(this, [link]).replace('<a', '<a target="_blank"');
|
|
9
9
|
};
|
|
10
10
|
});
|
|
11
11
|
}
|
|
@@ -21,7 +21,7 @@ function renderActionLinks(html: string): string {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export function renderMarkdown(markdown: string): string {
|
|
24
|
-
let html = marked(markdown, {
|
|
24
|
+
let html = marked(markdown, { renderer: makeRenderer(), async: false });
|
|
25
25
|
|
|
26
26
|
html = safeHtml(html);
|
|
27
27
|
html = renderActionLinks(html);
|
|
@@ -30,7 +30,5 @@ export function renderMarkdown(markdown: string): string {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export function safeHtml(html: string): string {
|
|
33
|
-
// TODO improve target="_blank" exception
|
|
34
|
-
// See https://github.com/cure53/DOMPurify/issues/317
|
|
35
33
|
return DOMPurify.sanitize(html, { ADD_ATTR: ['target'] });
|
|
36
34
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Comment, Static, Text } from 'vue';
|
|
2
|
+
import { toString } from '@noeldemartin/utils';
|
|
3
|
+
import type { VNode } from 'vue';
|
|
4
|
+
|
|
5
|
+
function renderAttrs(node: VNode): string {
|
|
6
|
+
return Object.entries(node.props ?? {}).reduce((attrs, [name, value]) => {
|
|
7
|
+
return attrs + `${name}="${toString(value)}"`;
|
|
8
|
+
}, '');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function renderNode(node: VNode | string): string {
|
|
12
|
+
if (typeof node === 'string') {
|
|
13
|
+
return node;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (node.type === Comment) {
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (node.type === Text || node.type === Static) {
|
|
21
|
+
return node.children as string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (node.type === 'br') {
|
|
25
|
+
return '\n\n';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return `<${node.type} ${renderAttrs(node)}>${Array.from(node.children as Array<VNode | string>)
|
|
29
|
+
.map(renderNode)
|
|
30
|
+
.join('')}</${node.type}>`;
|
|
31
|
+
}
|
package/src/utils/vue.ts
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
import { fail } from '@noeldemartin/utils';
|
|
1
|
+
import { fail, toString } from '@noeldemartin/utils';
|
|
2
2
|
import { computed, inject, reactive, ref, watch } from 'vue';
|
|
3
3
|
import type { Directive, InjectionKey, MaybeRef, PropType, Ref, UnwrapNestedRefs } from 'vue';
|
|
4
4
|
|
|
5
|
-
type BaseProp<T> = {
|
|
6
|
-
type?: PropType<T>;
|
|
7
|
-
validator?(value: unknown): boolean;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
type RequiredProp<T> = BaseProp<T> & { required: true };
|
|
11
|
-
type OptionalProp<T> = BaseProp<T> & { default: T | (() => T) | null };
|
|
12
|
-
|
|
13
5
|
export type AcceptRefs<T> = { [K in keyof T]: T[K] | RefUnion<T[K]> };
|
|
14
|
-
export type
|
|
6
|
+
export type BaseProp<T> = { type?: PropType<T>; validator?(value: unknown): boolean };
|
|
7
|
+
export type ComponentProps<T = {}> = T & Record<string, unknown>;
|
|
8
|
+
export type OptionalProp<T> = BaseProp<T> & { default: T | (() => T) | null };
|
|
15
9
|
export type RefUnion<T> = T extends infer R ? Ref<R> : never;
|
|
10
|
+
export type RequiredProp<T> = BaseProp<T> & { required: true };
|
|
16
11
|
export type Unref<T> = { [K in keyof T]: T[K] extends MaybeRef<infer Value> ? Value : T[K] };
|
|
17
12
|
|
|
18
13
|
export function arrayProp<T>(defaultValue?: () => T[]): OptionalProp<T[]> {
|
|
@@ -29,10 +24,6 @@ export function booleanProp(defaultValue: boolean = false): OptionalProp<boolean
|
|
|
29
24
|
};
|
|
30
25
|
}
|
|
31
26
|
|
|
32
|
-
export function componentRef<T>(): Ref<UnwrapNestedRefs<T> | undefined> {
|
|
33
|
-
return ref<UnwrapNestedRefs<T>>();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
27
|
export function computedAsync<T>(getter: () => Promise<T>): Ref<T | undefined> {
|
|
37
28
|
const result = ref<T>();
|
|
38
29
|
const asyncValue = computed(getter);
|
|
@@ -69,11 +60,11 @@ export function injectReactiveOrFail<T extends object>(
|
|
|
69
60
|
key: InjectionKey<T> | string,
|
|
70
61
|
errorMessage?: string,
|
|
71
62
|
): UnwrapNestedRefs<T> {
|
|
72
|
-
return injectReactive(key) ?? fail(errorMessage ?? `Could not resolve '${key}' injection key`);
|
|
63
|
+
return injectReactive(key) ?? fail(errorMessage ?? `Could not resolve '${toString(key)}' injection key`);
|
|
73
64
|
}
|
|
74
65
|
|
|
75
66
|
export function injectOrFail<T>(key: InjectionKey<T> | string, errorMessage?: string): T {
|
|
76
|
-
return inject(key) ?? fail(errorMessage ?? `Could not resolve '${key}' injection key`);
|
|
67
|
+
return inject(key) ?? fail(errorMessage ?? `Could not resolve '${toString(key)}' injection key`);
|
|
77
68
|
}
|
|
78
69
|
|
|
79
70
|
export function listenerProp<T extends Function = Function>(): OptionalProp<T | null> {
|