@aerogel/core 0.0.0-next.a56c0f4966eb71571173f8502f3f36d357ceebc7 → 0.0.0-next.a68f133e2c9a1ae9ba84b4e2e42df909289e5fba
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.cjs.js +1 -1
- package/dist/aerogel-core.cjs.js.map +1 -1
- package/dist/aerogel-core.d.ts +615 -133
- package/dist/aerogel-core.esm.js +1 -1
- package/dist/aerogel-core.esm.js.map +1 -1
- package/package.json +4 -4
- package/src/bootstrap/bootstrap.test.ts +3 -3
- package/src/bootstrap/index.ts +13 -3
- package/src/bootstrap/options.ts +3 -0
- package/src/components/AGAppLayout.vue +3 -2
- package/src/components/AGAppOverlays.vue +5 -1
- package/src/components/forms/AGCheckbox.vue +7 -1
- package/src/components/forms/AGInput.vue +8 -6
- package/src/components/forms/AGSelect.story.vue +21 -3
- package/src/components/forms/AGSelect.vue +10 -3
- package/src/components/headless/forms/AGHeadlessInput.ts +21 -1
- package/src/components/headless/forms/AGHeadlessInput.vue +4 -1
- package/src/components/headless/forms/AGHeadlessInputLabel.vue +8 -2
- package/src/components/headless/forms/AGHeadlessSelect.ts +21 -22
- package/src/components/headless/forms/AGHeadlessSelect.vue +24 -24
- package/src/components/headless/forms/AGHeadlessSelectLabel.vue +4 -1
- package/src/components/headless/forms/AGHeadlessSelectOption.vue +6 -6
- package/src/components/headless/modals/AGHeadlessModal.ts +27 -0
- package/src/components/headless/modals/AGHeadlessModal.vue +3 -5
- package/src/components/headless/modals/index.ts +4 -6
- package/src/components/headless/snackbars/index.ts +23 -8
- package/src/components/lib/AGMeasured.vue +15 -0
- package/src/components/lib/index.ts +1 -0
- package/src/components/modals/AGAlertModal.ts +15 -0
- package/src/components/modals/AGAlertModal.vue +3 -14
- package/src/components/modals/AGConfirmModal.ts +27 -0
- package/src/components/modals/AGConfirmModal.vue +7 -11
- package/src/components/modals/AGErrorReportModal.ts +27 -1
- package/src/components/modals/AGErrorReportModal.vue +7 -15
- package/src/components/modals/AGErrorReportModalButtons.vue +4 -2
- package/src/components/modals/AGLoadingModal.ts +23 -0
- package/src/components/modals/AGLoadingModal.vue +3 -7
- package/src/components/modals/AGModal.ts +2 -2
- package/src/components/modals/AGModal.vue +14 -12
- package/src/components/modals/AGPromptModal.ts +30 -0
- package/src/components/modals/AGPromptModal.vue +34 -0
- package/src/components/modals/index.ts +13 -19
- package/src/components/snackbars/AGSnackbar.vue +2 -8
- package/src/components/utils.ts +10 -0
- package/src/directives/index.ts +3 -1
- package/src/directives/measure.ts +12 -0
- package/src/errors/Errors.ts +17 -8
- package/src/errors/index.ts +1 -11
- package/src/forms/Form.ts +1 -0
- package/src/lang/Lang.ts +1 -1
- package/src/services/App.state.ts +1 -2
- package/src/services/App.ts +21 -3
- package/src/services/Events.ts +1 -1
- package/src/services/Service.ts +44 -14
- package/src/services/index.ts +2 -1
- package/src/services/store.ts +8 -5
- package/src/ui/UI.ts +93 -12
- package/src/ui/index.ts +8 -3
- package/src/utils/composition/events.ts +1 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/tailwindcss.test.ts +26 -0
- package/src/utils/tailwindcss.ts +7 -0
- package/src/utils/vue.ts +10 -1
package/src/errors/index.ts
CHANGED
|
@@ -10,16 +10,6 @@ export { Errors, ErrorSource, ErrorReport, ErrorReportLog };
|
|
|
10
10
|
|
|
11
11
|
const services = { $errors: Errors };
|
|
12
12
|
const frameworkHandler: ErrorHandler = (error) => {
|
|
13
|
-
if (!Errors.instance) {
|
|
14
|
-
// eslint-disable-next-line no-console
|
|
15
|
-
console.warn('Errors service hasn\'t been initialized properly!');
|
|
16
|
-
|
|
17
|
-
// eslint-disable-next-line no-console
|
|
18
|
-
console.error(error);
|
|
19
|
-
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
13
|
Errors.report(error);
|
|
24
14
|
|
|
25
15
|
return true;
|
|
@@ -45,7 +35,7 @@ export default definePlugin({
|
|
|
45
35
|
});
|
|
46
36
|
|
|
47
37
|
declare module '@/bootstrap/options' {
|
|
48
|
-
interface AerogelOptions {
|
|
38
|
+
export interface AerogelOptions {
|
|
49
39
|
handleError?(error: ErrorSource): boolean;
|
|
50
40
|
}
|
|
51
41
|
}
|
package/src/forms/Form.ts
CHANGED
|
@@ -18,6 +18,7 @@ export interface FormFieldDefinition<TType extends FormFieldType = FormFieldType
|
|
|
18
18
|
|
|
19
19
|
export type FormFieldDefinitions = Record<string, FormFieldDefinition>;
|
|
20
20
|
export type FormFieldType = ObjectValues<typeof FormFieldTypes>;
|
|
21
|
+
export type FormFieldValue = GetFormFieldValue<FormFieldType>;
|
|
21
22
|
|
|
22
23
|
export type FormData<T> = {
|
|
23
24
|
-readonly [k in keyof T]: T[k] extends FormFieldDefinition<infer TType, infer TRules>
|
package/src/lang/Lang.ts
CHANGED
|
@@ -9,10 +9,9 @@ export default defineServiceState({
|
|
|
9
9
|
plugins: {} as Record<string, Plugin>,
|
|
10
10
|
environment: Aerogel.environment,
|
|
11
11
|
sourceUrl: Aerogel.sourceUrl,
|
|
12
|
-
isMounted: false,
|
|
13
12
|
},
|
|
14
13
|
computed: {
|
|
15
14
|
development: (state) => state.environment === 'development',
|
|
16
|
-
testing: (state) => state.environment === 'testing',
|
|
15
|
+
testing: (state) => state.environment === 'test' || state.environment === 'testing',
|
|
17
16
|
},
|
|
18
17
|
});
|
package/src/services/App.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { facade, forever, updateLocationQueryParameters } from '@noeldemartin/utils';
|
|
1
|
+
import { PromisedValue, facade, forever, updateLocationQueryParameters } from '@noeldemartin/utils';
|
|
2
2
|
|
|
3
3
|
import Events from '@/services/Events';
|
|
4
4
|
import type { Plugin } from '@/plugins';
|
|
@@ -7,6 +7,23 @@ import Service from './App.state';
|
|
|
7
7
|
|
|
8
8
|
export class AppService extends Service {
|
|
9
9
|
|
|
10
|
+
public readonly ready = new PromisedValue<void>();
|
|
11
|
+
public readonly mounted = new PromisedValue<void>();
|
|
12
|
+
|
|
13
|
+
public isReady(): boolean {
|
|
14
|
+
return this.ready.isResolved();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public isMounted(): boolean {
|
|
18
|
+
return this.mounted.isResolved();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public async whenReady<T>(callback: () => T): Promise<T> {
|
|
22
|
+
const result = await this.ready.then(callback);
|
|
23
|
+
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
|
|
10
27
|
public async reload(queryParameters?: Record<string, string | undefined>): Promise<void> {
|
|
11
28
|
queryParameters && updateLocationQueryParameters(queryParameters);
|
|
12
29
|
|
|
@@ -21,9 +38,10 @@ export class AppService extends Service {
|
|
|
21
38
|
}
|
|
22
39
|
|
|
23
40
|
protected async boot(): Promise<void> {
|
|
24
|
-
Events.once('application-
|
|
41
|
+
Events.once('application-ready', () => this.ready.resolve());
|
|
42
|
+
Events.once('application-mounted', () => this.mounted.resolve());
|
|
25
43
|
}
|
|
26
44
|
|
|
27
45
|
}
|
|
28
46
|
|
|
29
|
-
export default facade(
|
|
47
|
+
export default facade(AppService);
|
package/src/services/Events.ts
CHANGED
package/src/services/Service.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { MagicObject, PromisedValue, Storage, isEmpty, objectDeepClone, objectOnly } from '@noeldemartin/utils';
|
|
2
2
|
import type { Constructor } from '@noeldemartin/utils';
|
|
3
|
+
import type { MaybeRef } from 'vue';
|
|
3
4
|
import type { Store } from 'pinia';
|
|
4
5
|
|
|
5
6
|
import ServiceBootError from '@/errors/ServiceBootError';
|
|
@@ -8,9 +9,12 @@ import { defineServiceStore } from '@/services/store';
|
|
|
8
9
|
export type ServiceState = Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
9
10
|
export type DefaultServiceState = any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
10
11
|
export type ServiceConstructor<T extends Service = Service> = Constructor<T> & typeof Service;
|
|
12
|
+
export type UnrefServiceState<State extends ServiceState> = {
|
|
13
|
+
[K in keyof State]: State[K] extends MaybeRef<infer T> ? T : State[K];
|
|
14
|
+
};
|
|
11
15
|
|
|
12
16
|
export type ComputedStateDefinition<TState extends ServiceState, TComputedState extends ServiceState> = {
|
|
13
|
-
[K in keyof TComputedState]: (state: TState) => TComputedState[K];
|
|
17
|
+
[K in keyof TComputedState]: (state: UnrefServiceState<TState>) => TComputedState[K];
|
|
14
18
|
} & ThisType<{
|
|
15
19
|
readonly [K in keyof TComputedState]: TComputedState[K];
|
|
16
20
|
}>;
|
|
@@ -20,12 +24,14 @@ export function defineServiceState<
|
|
|
20
24
|
ComputedState extends ServiceState = {}
|
|
21
25
|
>(options: {
|
|
22
26
|
name: string;
|
|
23
|
-
initialState: State;
|
|
27
|
+
initialState: State | (() => State);
|
|
24
28
|
persist?: (keyof State)[];
|
|
25
29
|
computed?: ComputedStateDefinition<State, ComputedState>;
|
|
26
30
|
serialize?: (state: Partial<State>) => Partial<State>;
|
|
27
|
-
}): Constructor<State
|
|
28
|
-
|
|
31
|
+
}): Constructor<UnrefServiceState<State>> &
|
|
32
|
+
Constructor<ComputedState> &
|
|
33
|
+
Constructor<Service<UnrefServiceState<State>, ComputedState, Partial<UnrefServiceState<State>>>> {
|
|
34
|
+
return class extends Service<UnrefServiceState<State>, ComputedState> {
|
|
29
35
|
|
|
30
36
|
public static persist = (options.persist as string[]) ?? [];
|
|
31
37
|
|
|
@@ -37,21 +43,41 @@ export function defineServiceState<
|
|
|
37
43
|
return options.name ?? null;
|
|
38
44
|
}
|
|
39
45
|
|
|
40
|
-
protected getInitialState(): State {
|
|
41
|
-
|
|
46
|
+
protected getInitialState(): UnrefServiceState<State> {
|
|
47
|
+
if (typeof options.initialState === 'function') {
|
|
48
|
+
return options.initialState();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return Object.entries(options.initialState).reduce((state, [key, value]) => {
|
|
52
|
+
try {
|
|
53
|
+
value = structuredClone(value);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
// eslint-disable-next-line no-console
|
|
56
|
+
console.warn(
|
|
57
|
+
`Could not clone '${key}' state from ${this.getName()} service, ` +
|
|
58
|
+
'this may cause problems if you\'re using multiple instances of the service ' +
|
|
59
|
+
'(for example, in unit tests).\n' +
|
|
60
|
+
'To fix this problem, declare your initialState as a function instead.',
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
state[key as keyof State] = value;
|
|
65
|
+
|
|
66
|
+
return state;
|
|
67
|
+
}, {} as UnrefServiceState<State>);
|
|
42
68
|
}
|
|
43
69
|
|
|
44
|
-
protected getComputedStateDefinition(): ComputedStateDefinition<State
|
|
45
|
-
return options.computed ??
|
|
70
|
+
protected getComputedStateDefinition(): ComputedStateDefinition<UnrefServiceState<State>, ComputedState> {
|
|
71
|
+
return (options.computed ?? {}) as ComputedStateDefinition<UnrefServiceState<State>, ComputedState>;
|
|
46
72
|
}
|
|
47
73
|
|
|
48
74
|
protected serializePersistedState(state: Partial<State>): Partial<State> {
|
|
49
75
|
return options.serialize?.(state) ?? state;
|
|
50
76
|
}
|
|
51
77
|
|
|
52
|
-
} as unknown as Constructor<State
|
|
78
|
+
} as unknown as Constructor<UnrefServiceState<State>> &
|
|
53
79
|
Constructor<ComputedState> &
|
|
54
|
-
Constructor<Service<State
|
|
80
|
+
Constructor<Service<UnrefServiceState<State>, ComputedState, Partial<UnrefServiceState<State>>>>;
|
|
55
81
|
}
|
|
56
82
|
|
|
57
83
|
export default class Service<
|
|
@@ -162,7 +188,11 @@ export default class Service<
|
|
|
162
188
|
return;
|
|
163
189
|
}
|
|
164
190
|
|
|
165
|
-
const storage = Storage.
|
|
191
|
+
const storage = Storage.get<ServiceStorage>(this._name);
|
|
192
|
+
|
|
193
|
+
if (!storage) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
166
196
|
|
|
167
197
|
Storage.set(this._name, {
|
|
168
198
|
...storage,
|
|
@@ -191,14 +221,14 @@ export default class Service<
|
|
|
191
221
|
}
|
|
192
222
|
|
|
193
223
|
protected async frameworkBoot(): Promise<void> {
|
|
194
|
-
this.
|
|
224
|
+
this.initializePersistedState();
|
|
195
225
|
}
|
|
196
226
|
|
|
197
227
|
protected async boot(): Promise<void> {
|
|
198
|
-
//
|
|
228
|
+
// Placeholder for overrides, don't place any functionality here.
|
|
199
229
|
}
|
|
200
230
|
|
|
201
|
-
protected
|
|
231
|
+
protected initializePersistedState(): void {
|
|
202
232
|
// TODO fix this.static()
|
|
203
233
|
const persist = (this.constructor as unknown as { persist: string[] }).persist;
|
|
204
234
|
|
package/src/services/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { getPiniaStore } from './store';
|
|
|
10
10
|
export * from './App';
|
|
11
11
|
export * from './Events';
|
|
12
12
|
export * from './Service';
|
|
13
|
+
export * from './store';
|
|
13
14
|
|
|
14
15
|
export { App, Events, Service };
|
|
15
16
|
|
|
@@ -50,7 +51,7 @@ export default definePlugin({
|
|
|
50
51
|
});
|
|
51
52
|
|
|
52
53
|
declare module '@/bootstrap/options' {
|
|
53
|
-
interface AerogelOptions {
|
|
54
|
+
export interface AerogelOptions {
|
|
54
55
|
services?: Record<string, Service>;
|
|
55
56
|
}
|
|
56
57
|
}
|
package/src/services/store.ts
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
+
import { tap } from '@noeldemartin/utils';
|
|
1
2
|
import { createPinia, defineStore, setActivePinia } from 'pinia';
|
|
2
3
|
import type { DefineStoreOptions, Pinia, StateTree, Store, _GettersTree } from 'pinia';
|
|
3
4
|
|
|
4
5
|
let _store: Pinia | null = null;
|
|
5
6
|
|
|
6
7
|
function initializePiniaStore(): Pinia {
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
return _store ?? resetPiniaStore();
|
|
9
|
+
}
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
export function resetPiniaStore(): Pinia {
|
|
12
|
+
return tap(createPinia(), (store) => {
|
|
13
|
+
_store = store;
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
setActivePinia(store);
|
|
16
|
+
});
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
export function getPiniaStore(): Pinia {
|
package/src/ui/UI.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { ObjectValues } from '@noeldemartin/utils';
|
|
|
5
5
|
|
|
6
6
|
import Events from '@/services/Events';
|
|
7
7
|
import type { SnackbarAction, SnackbarColor } from '@/components/headless/snackbars';
|
|
8
|
+
import type { AGAlertModalProps, AGConfirmModalProps, AGLoadingModalProps, AGPromptModalProps } from '@/components';
|
|
8
9
|
|
|
9
10
|
import Service from './UI.state';
|
|
10
11
|
import type { Modal, ModalComponent, Snackbar } from './UI.state';
|
|
@@ -24,11 +25,26 @@ export const UIComponents = {
|
|
|
24
25
|
ConfirmModal: 'confirm-modal',
|
|
25
26
|
ErrorReportModal: 'error-report-modal',
|
|
26
27
|
LoadingModal: 'loading-modal',
|
|
28
|
+
PromptModal: 'prompt-modal',
|
|
27
29
|
Snackbar: 'snackbar',
|
|
30
|
+
StartupCrash: 'startup-crash',
|
|
28
31
|
} as const;
|
|
29
32
|
|
|
30
33
|
export type UIComponent = ObjectValues<typeof UIComponents>;
|
|
31
34
|
|
|
35
|
+
export interface ConfirmOptions {
|
|
36
|
+
acceptText?: string;
|
|
37
|
+
cancelText?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface PromptOptions {
|
|
41
|
+
label?: string;
|
|
42
|
+
defaultValue?: string;
|
|
43
|
+
placeholder?: string;
|
|
44
|
+
acceptText?: string;
|
|
45
|
+
cancelText?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
32
48
|
export interface ShowSnackbarOptions {
|
|
33
49
|
component?: Component;
|
|
34
50
|
color?: SnackbarColor;
|
|
@@ -47,33 +63,98 @@ export class UIService extends Service {
|
|
|
47
63
|
public alert(message: string): void;
|
|
48
64
|
public alert(title: string, message: string): void;
|
|
49
65
|
public alert(messageOrTitle: string, message?: string): void {
|
|
50
|
-
const
|
|
66
|
+
const getProperties = (): AGAlertModalProps => {
|
|
67
|
+
if (typeof message !== 'string') {
|
|
68
|
+
return { message: messageOrTitle };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
title: messageOrTitle,
|
|
73
|
+
message,
|
|
74
|
+
};
|
|
75
|
+
};
|
|
51
76
|
|
|
52
|
-
this.openModal(this.requireComponent(UIComponents.AlertModal),
|
|
77
|
+
this.openModal(this.requireComponent(UIComponents.AlertModal), getProperties());
|
|
53
78
|
}
|
|
54
79
|
|
|
55
|
-
public async confirm(message: string): Promise<boolean>;
|
|
56
|
-
public async confirm(title: string, message: string): Promise<boolean>;
|
|
57
|
-
public async confirm(
|
|
58
|
-
|
|
59
|
-
|
|
80
|
+
public async confirm(message: string, options?: ConfirmOptions): Promise<boolean>;
|
|
81
|
+
public async confirm(title: string, message: string, options?: ConfirmOptions): Promise<boolean>;
|
|
82
|
+
public async confirm(
|
|
83
|
+
messageOrTitle: string,
|
|
84
|
+
messageOrOptions?: string | ConfirmOptions,
|
|
85
|
+
options?: ConfirmOptions,
|
|
86
|
+
): Promise<boolean> {
|
|
87
|
+
const getProperties = (): AGConfirmModalProps => {
|
|
88
|
+
if (typeof messageOrOptions !== 'string') {
|
|
89
|
+
return {
|
|
90
|
+
message: messageOrTitle,
|
|
91
|
+
...(messageOrOptions ?? {}),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
title: messageOrTitle,
|
|
97
|
+
message: messageOrOptions,
|
|
98
|
+
...(options ?? {}),
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const modal = await this.openModal<ModalComponent<AGConfirmModalProps, boolean>>(
|
|
60
103
|
this.requireComponent(UIComponents.ConfirmModal),
|
|
61
|
-
|
|
104
|
+
getProperties(),
|
|
62
105
|
);
|
|
63
106
|
const result = await modal.beforeClose;
|
|
64
107
|
|
|
65
108
|
return result ?? false;
|
|
66
109
|
}
|
|
67
110
|
|
|
111
|
+
public async prompt(message: string, options?: PromptOptions): Promise<string | null>;
|
|
112
|
+
public async prompt(title: string, message: string, options?: PromptOptions): Promise<string | null>;
|
|
113
|
+
public async prompt(
|
|
114
|
+
messageOrTitle: string,
|
|
115
|
+
messageOrOptions?: string | PromptOptions,
|
|
116
|
+
options?: PromptOptions,
|
|
117
|
+
): Promise<string | null> {
|
|
118
|
+
const getProperties = (): AGPromptModalProps => {
|
|
119
|
+
if (typeof messageOrOptions !== 'string') {
|
|
120
|
+
return {
|
|
121
|
+
message: messageOrTitle,
|
|
122
|
+
...(messageOrOptions ?? {}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
title: messageOrTitle,
|
|
128
|
+
message: messageOrOptions,
|
|
129
|
+
...(options ?? {}),
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const modal = await this.openModal<ModalComponent<AGPromptModalProps, string | null>>(
|
|
134
|
+
this.requireComponent(UIComponents.PromptModal),
|
|
135
|
+
getProperties(),
|
|
136
|
+
);
|
|
137
|
+
const result = await modal.beforeClose;
|
|
138
|
+
|
|
139
|
+
return result ?? null;
|
|
140
|
+
}
|
|
141
|
+
|
|
68
142
|
public async loading<T>(operation: Promise<T>): Promise<T>;
|
|
69
143
|
public async loading<T>(message: string, operation: Promise<T>): Promise<T>;
|
|
70
144
|
public async loading<T>(messageOrOperation: string | Promise<T>, operation?: Promise<T>): Promise<T> {
|
|
71
|
-
|
|
145
|
+
const getProperties = (): AGLoadingModalProps => {
|
|
146
|
+
if (typeof messageOrOperation !== 'string') {
|
|
147
|
+
return {};
|
|
148
|
+
}
|
|
72
149
|
|
|
73
|
-
|
|
74
|
-
|
|
150
|
+
return { message: messageOrOperation };
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const modal = await this.openModal(this.requireComponent(UIComponents.LoadingModal), getProperties());
|
|
75
154
|
|
|
76
155
|
try {
|
|
156
|
+
operation = typeof messageOrOperation === 'string' ? (operation as Promise<T>) : messageOrOperation;
|
|
157
|
+
|
|
77
158
|
const [result] = await Promise.all([operation, after({ seconds: 1 })]);
|
|
78
159
|
|
|
79
160
|
return result;
|
|
@@ -189,7 +270,7 @@ export class UIService extends Service {
|
|
|
189
270
|
|
|
190
271
|
}
|
|
191
272
|
|
|
192
|
-
export default facade(
|
|
273
|
+
export default facade(UIService);
|
|
193
274
|
|
|
194
275
|
declare module '@/services/Events' {
|
|
195
276
|
export interface EventsPayload {
|
package/src/ui/index.ts
CHANGED
|
@@ -8,13 +8,16 @@ import AGAlertModal from '../components/modals/AGAlertModal.vue';
|
|
|
8
8
|
import AGConfirmModal from '../components/modals/AGConfirmModal.vue';
|
|
9
9
|
import AGErrorReportModal from '../components/modals/AGErrorReportModal.vue';
|
|
10
10
|
import AGLoadingModal from '../components/modals/AGLoadingModal.vue';
|
|
11
|
+
import AGPromptModal from '../components/modals/AGPromptModal.vue';
|
|
11
12
|
import AGSnackbar from '../components/snackbars/AGSnackbar.vue';
|
|
13
|
+
import AGStartupCrash from '../components/lib/AGStartupCrash.vue';
|
|
12
14
|
import type { UIComponent } from './UI';
|
|
13
15
|
|
|
14
|
-
export { UI, UIComponents, UIComponent };
|
|
15
|
-
|
|
16
16
|
const services = { $ui: UI };
|
|
17
17
|
|
|
18
|
+
export * from './UI';
|
|
19
|
+
export { default as UI } from './UI';
|
|
20
|
+
|
|
18
21
|
export type UIServices = typeof services;
|
|
19
22
|
|
|
20
23
|
export default definePlugin({
|
|
@@ -24,7 +27,9 @@ export default definePlugin({
|
|
|
24
27
|
[UIComponents.ConfirmModal]: AGConfirmModal,
|
|
25
28
|
[UIComponents.ErrorReportModal]: AGErrorReportModal,
|
|
26
29
|
[UIComponents.LoadingModal]: AGLoadingModal,
|
|
30
|
+
[UIComponents.PromptModal]: AGPromptModal,
|
|
27
31
|
[UIComponents.Snackbar]: AGSnackbar,
|
|
32
|
+
[UIComponents.StartupCrash]: AGStartupCrash,
|
|
28
33
|
};
|
|
29
34
|
|
|
30
35
|
Object.entries({
|
|
@@ -37,7 +42,7 @@ export default definePlugin({
|
|
|
37
42
|
});
|
|
38
43
|
|
|
39
44
|
declare module '@/bootstrap/options' {
|
|
40
|
-
interface AerogelOptions {
|
|
45
|
+
export interface AerogelOptions {
|
|
41
46
|
components?: Partial<Record<UIComponent, Component>>;
|
|
42
47
|
}
|
|
43
48
|
}
|
|
@@ -14,6 +14,7 @@ export function useEvent<Event extends EventWithPayload>(
|
|
|
14
14
|
event: Event,
|
|
15
15
|
listener: EventListener<EventsPayload[Event]>
|
|
16
16
|
): void;
|
|
17
|
+
export function useEvent<Payload>(event: string, listener: (payload: Payload) => unknown): void;
|
|
17
18
|
export function useEvent<Event extends string>(event: UnknownEvent<Event>, listener: EventListener): void;
|
|
18
19
|
|
|
19
20
|
export function useEvent(event: string, listener: EventListener): void {
|
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { removeInteractiveClasses } from './tailwindcss';
|
|
4
|
+
|
|
5
|
+
describe('TailwindCSS utils', () => {
|
|
6
|
+
|
|
7
|
+
it('Removes interactive classes', () => {
|
|
8
|
+
const cases: [string, string][] = [
|
|
9
|
+
['text-red hover:text-green', 'text-red'],
|
|
10
|
+
['text-red hover:text-green text-lg', 'text-red text-lg'],
|
|
11
|
+
[
|
|
12
|
+
`
|
|
13
|
+
text-red text-lg
|
|
14
|
+
focus:text-yellow
|
|
15
|
+
hover:focus:text-black
|
|
16
|
+
`,
|
|
17
|
+
'text-red text-lg',
|
|
18
|
+
],
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
cases.forEach(([original, expected]) => {
|
|
22
|
+
expect(removeInteractiveClasses(original)).toEqual(expected);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
});
|
package/src/utils/vue.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { fail } from '@noeldemartin/utils';
|
|
2
|
-
import { inject, reactive, ref } from 'vue';
|
|
2
|
+
import { computed, inject, reactive, ref, watch } from 'vue';
|
|
3
3
|
import type { Directive, InjectionKey, PropType, Ref, UnwrapNestedRefs } from 'vue';
|
|
4
4
|
|
|
5
5
|
type BaseProp<T> = {
|
|
@@ -30,6 +30,15 @@ export function componentRef<T>(): Ref<UnwrapNestedRefs<T> | undefined> {
|
|
|
30
30
|
return ref<UnwrapNestedRefs<T>>();
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
export function computedAsync<T>(getter: () => Promise<T>): Ref<T | undefined> {
|
|
34
|
+
const result = ref<T>();
|
|
35
|
+
const asyncValue = computed(getter);
|
|
36
|
+
|
|
37
|
+
watch(asyncValue, async () => (result.value = await asyncValue.value), { immediate: true });
|
|
38
|
+
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
33
42
|
export function defineDirective(directive: Directive): Directive {
|
|
34
43
|
return directive;
|
|
35
44
|
}
|