@aerogel/core 0.0.0-next.d824b40e5d06757cd9f47c9f771d916185df4f05 → 0.0.0-next.f1f5a990033d966dc0bb12d251110fbc9350dcc7

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.
Files changed (78) hide show
  1. package/dist/aerogel-core.cjs.js +1 -1
  2. package/dist/aerogel-core.cjs.js.map +1 -1
  3. package/dist/aerogel-core.d.ts +641 -110
  4. package/dist/aerogel-core.esm.js +1 -1
  5. package/dist/aerogel-core.esm.js.map +1 -1
  6. package/histoire.config.ts +7 -0
  7. package/package.json +14 -5
  8. package/postcss.config.js +6 -0
  9. package/src/assets/histoire.css +3 -0
  10. package/src/bootstrap/bootstrap.test.ts +4 -3
  11. package/src/bootstrap/index.ts +18 -3
  12. package/src/components/AGAppLayout.vue +7 -2
  13. package/src/components/AGAppOverlays.vue +5 -1
  14. package/src/components/AGAppSnackbars.vue +1 -1
  15. package/src/components/forms/AGSelect.story.vue +28 -0
  16. package/src/components/forms/AGSelect.vue +53 -0
  17. package/src/components/forms/index.ts +5 -6
  18. package/src/components/headless/forms/AGHeadlessInput.ts +21 -1
  19. package/src/components/headless/forms/AGHeadlessInput.vue +8 -5
  20. package/src/components/headless/forms/AGHeadlessInputLabel.vue +8 -2
  21. package/src/components/headless/forms/AGHeadlessSelect.ts +39 -0
  22. package/src/components/headless/forms/AGHeadlessSelect.vue +76 -0
  23. package/src/components/headless/forms/AGHeadlessSelectButton.vue +24 -0
  24. package/src/components/headless/forms/AGHeadlessSelectError.vue +26 -0
  25. package/src/components/headless/forms/AGHeadlessSelectLabel.vue +24 -0
  26. package/src/components/headless/forms/AGHeadlessSelectOption.ts +4 -0
  27. package/src/components/headless/forms/AGHeadlessSelectOption.vue +39 -0
  28. package/src/components/headless/forms/AGHeadlessSelectOptions.ts +3 -0
  29. package/src/components/headless/forms/index.ts +9 -1
  30. package/src/components/headless/modals/AGHeadlessModal.ts +27 -0
  31. package/src/components/headless/modals/AGHeadlessModal.vue +3 -5
  32. package/src/components/headless/modals/index.ts +4 -6
  33. package/src/components/headless/snackbars/index.ts +23 -8
  34. package/src/components/index.ts +1 -1
  35. package/src/components/lib/AGMeasured.vue +15 -0
  36. package/src/components/lib/AGStartupCrash.vue +31 -0
  37. package/src/components/lib/index.ts +5 -0
  38. package/src/components/modals/AGAlertModal.ts +15 -0
  39. package/src/components/modals/AGAlertModal.vue +4 -15
  40. package/src/components/modals/AGConfirmModal.ts +27 -0
  41. package/src/components/modals/AGConfirmModal.vue +7 -11
  42. package/src/components/modals/AGErrorReportModal.ts +27 -1
  43. package/src/components/modals/AGErrorReportModal.vue +8 -16
  44. package/src/components/modals/AGErrorReportModalButtons.vue +4 -2
  45. package/src/components/modals/AGErrorReportModalTitle.vue +1 -1
  46. package/src/components/modals/AGLoadingModal.ts +23 -0
  47. package/src/components/modals/AGLoadingModal.vue +4 -8
  48. package/src/components/modals/AGModal.ts +1 -1
  49. package/src/components/modals/AGModal.vue +14 -12
  50. package/src/components/modals/index.ts +3 -0
  51. package/src/components/snackbars/AGSnackbar.vue +3 -9
  52. package/src/components/utils.ts +10 -0
  53. package/src/directives/index.ts +3 -1
  54. package/src/directives/measure.ts +12 -0
  55. package/src/errors/Errors.ts +10 -5
  56. package/src/errors/index.ts +9 -13
  57. package/src/forms/Form.ts +11 -3
  58. package/src/main.histoire.ts +1 -0
  59. package/src/services/App.state.ts +3 -3
  60. package/src/services/App.ts +10 -1
  61. package/src/services/Service.ts +8 -4
  62. package/src/services/index.ts +1 -1
  63. package/src/ui/UI.ts +77 -14
  64. package/src/ui/index.ts +6 -3
  65. package/src/utils/composition/events.ts +1 -0
  66. package/src/utils/index.ts +1 -0
  67. package/src/utils/tailwindcss.test.ts +26 -0
  68. package/src/utils/tailwindcss.ts +7 -0
  69. package/src/utils/vue.ts +13 -4
  70. package/tailwind.config.js +4 -0
  71. package/tsconfig.json +1 -1
  72. package/.eslintrc.js +0 -3
  73. package/dist/virtual.d.ts +0 -11
  74. package/src/components/basic/index.ts +0 -5
  75. package/src/types/virtual.d.ts +0 -11
  76. /package/src/components/{basic → lib}/AGErrorMessage.vue +0 -0
  77. /package/src/components/{basic → lib}/AGLink.vue +0 -0
  78. /package/src/components/{basic → lib}/AGMarkdown.vue +0 -0
@@ -7,7 +7,9 @@ import { translateWithDefault } from '@/lang/utils';
7
7
 
8
8
  import Service from './Errors.state';
9
9
  import { Colors } from '@/components/constants';
10
+ import type { AGErrorReportModalProps } from '@/components/modals/AGErrorReportModal';
10
11
  import type { ErrorReport, ErrorReportLog, ErrorSource } from './Errors.state';
12
+ import type { ModalComponent } from '@/ui/UI.state';
11
13
 
12
14
  export class ErrorsService extends Service {
13
15
 
@@ -23,7 +25,7 @@ export class ErrorsService extends Service {
23
25
  }
24
26
 
25
27
  public async inspect(error: ErrorSource | ErrorReport[]): Promise<void> {
26
- const reports = Array.isArray(error) ? error : [await this.createErrorReport(error)];
28
+ const reports = Array.isArray(error) ? (error as ErrorReport[]) : [await this.createErrorReport(error)];
27
29
 
28
30
  if (reports.length === 0) {
29
31
  UI.alert(translateWithDefault('errors.inspectEmpty', 'Nothing to inspect!'));
@@ -31,7 +33,9 @@ export class ErrorsService extends Service {
31
33
  return;
32
34
  }
33
35
 
34
- UI.openModal(UI.requireComponent(UIComponents.ErrorReportModal), { reports });
36
+ UI.openModal<ModalComponent<AGErrorReportModalProps>>(UI.requireComponent(UIComponents.ErrorReportModal), {
37
+ reports,
38
+ });
35
39
  }
36
40
 
37
41
  public async report(error: ErrorSource, message?: string): Promise<void> {
@@ -70,9 +74,10 @@ export class ErrorsService extends Service {
70
74
  text: translateWithDefault('errors.viewDetails', 'View details'),
71
75
  dismiss: true,
72
76
  handler: () =>
73
- UI.openModal(UI.requireComponent(UIComponents.ErrorReportModal), {
74
- reports: [report],
75
- }),
77
+ UI.openModal<ModalComponent<AGErrorReportModalProps>>(
78
+ UI.requireComponent(UIComponents.ErrorReportModal),
79
+ { reports: [report] },
80
+ ),
76
81
  },
77
82
  ],
78
83
  },
@@ -1,4 +1,4 @@
1
- import { tap } from '@noeldemartin/utils';
1
+ import type { App } from 'vue';
2
2
 
3
3
  import { bootServices } from '@/services';
4
4
  import { definePlugin } from '@/plugins';
@@ -25,14 +25,12 @@ const frameworkHandler: ErrorHandler = (error) => {
25
25
  return true;
26
26
  };
27
27
 
28
- function setUpErrorHandler(baseHandler: ErrorHandler = () => false): ErrorHandler {
29
- return tap(
30
- (error) => baseHandler(error) || frameworkHandler(error),
31
- (errorHandler) => {
32
- globalThis.onerror = (message, _, __, ___, error) => errorHandler(error ?? message);
33
- globalThis.onunhandledrejection = (event) => errorHandler(event.reason);
34
- },
35
- );
28
+ function setUpErrorHandler(app: App, baseHandler: ErrorHandler = () => false): void {
29
+ const errorHandler: ErrorHandler = (error) => baseHandler(error) || frameworkHandler(error);
30
+
31
+ app.config.errorHandler = errorHandler;
32
+ globalThis.onerror = (event, _, __, ___, error) => errorHandler(error ?? event);
33
+ globalThis.onunhandledrejection = (event) => errorHandler(event.reason);
36
34
  }
37
35
 
38
36
  export type ErrorHandler = (error: ErrorSource) => boolean;
@@ -40,16 +38,14 @@ export type ErrorsServices = typeof services;
40
38
 
41
39
  export default definePlugin({
42
40
  async install(app, options) {
43
- const errorHandler = setUpErrorHandler(options.handleError);
44
-
45
- app.config.errorHandler = errorHandler;
41
+ setUpErrorHandler(app, options.handleError);
46
42
 
47
43
  await bootServices(app, services);
48
44
  },
49
45
  });
50
46
 
51
47
  declare module '@/bootstrap/options' {
52
- interface AerogelOptions {
48
+ export interface AerogelOptions {
53
49
  handleError?(error: ErrorSource): boolean;
54
50
  }
55
51
  }
package/src/forms/Form.ts CHANGED
@@ -7,6 +7,7 @@ export const FormFieldTypes = {
7
7
  String: 'string',
8
8
  Number: 'number',
9
9
  Boolean: 'boolean',
10
+ Object: 'object',
10
11
  } as const;
11
12
 
12
13
  export interface FormFieldDefinition<TType extends FormFieldType = FormFieldType, TRules extends string = string> {
@@ -36,15 +37,18 @@ export type GetFormFieldValue<TType> = TType extends typeof FormFieldTypes.Strin
36
37
  ? number
37
38
  : TType extends typeof FormFieldTypes.Boolean
38
39
  ? boolean
40
+ : TType extends typeof FormFieldTypes.Object
41
+ ? object
39
42
  : never;
40
43
 
44
+ const validForms: WeakMap<Form, ComputedRef<boolean>> = new WeakMap();
45
+
41
46
  export default class Form<Fields extends FormFieldDefinitions = FormFieldDefinitions> extends MagicObject {
42
47
 
43
48
  public errors: DeepReadonly<UnwrapNestedRefs<FormErrors<Fields>>>;
44
49
 
45
50
  private _fields: Fields;
46
51
  private _data: FormData<Fields>;
47
- private _valid: ComputedRef<boolean>;
48
52
  private _submitted: Ref<boolean>;
49
53
  private _errors: FormErrors<Fields>;
50
54
 
@@ -55,13 +59,17 @@ export default class Form<Fields extends FormFieldDefinitions = FormFieldDefinit
55
59
  this._submitted = ref(false);
56
60
  this._data = this.getInitialData(fields);
57
61
  this._errors = this.getInitialErrors(fields);
58
- this._valid = computed(() => !Object.values(this._errors).some((error) => error !== null));
62
+
63
+ validForms.set(
64
+ this,
65
+ computed(() => !Object.values(this._errors).some((error) => error !== null)),
66
+ );
59
67
 
60
68
  this.errors = readonly(this._errors);
61
69
  }
62
70
 
63
71
  public get valid(): boolean {
64
- return this._valid.value;
72
+ return !!validForms.get(this)?.value;
65
73
  }
66
74
 
67
75
  public get submitted(): boolean {
@@ -0,0 +1 @@
1
+ import './assets/histoire.css';
@@ -1,4 +1,4 @@
1
- import Build from 'virtual:aerogel';
1
+ import Aerogel from 'virtual:aerogel';
2
2
 
3
3
  import { defineServiceState } from '@/services/Service';
4
4
  import type { Plugin } from '@/plugins/Plugin';
@@ -7,8 +7,8 @@ export default defineServiceState({
7
7
  name: 'app',
8
8
  initialState: {
9
9
  plugins: {} as Record<string, Plugin>,
10
- environment: Build.environment,
11
- sourceUrl: Build.sourceUrl,
10
+ environment: Aerogel.environment,
11
+ sourceUrl: Aerogel.sourceUrl,
12
12
  isMounted: false,
13
13
  },
14
14
  computed: {
@@ -1,4 +1,4 @@
1
- import { facade } from '@noeldemartin/utils';
1
+ import { 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,15 @@ import Service from './App.state';
7
7
 
8
8
  export class AppService extends Service {
9
9
 
10
+ public async reload(queryParameters?: Record<string, string | undefined>): Promise<void> {
11
+ queryParameters && updateLocationQueryParameters(queryParameters);
12
+
13
+ location.reload();
14
+
15
+ // Stall until the reload happens
16
+ await forever();
17
+ }
18
+
10
19
  public plugin<T extends Plugin = Plugin>(name: string): T | null {
11
20
  return (this.plugins[name] as T) ?? null;
12
21
  }
@@ -162,7 +162,11 @@ export default class Service<
162
162
  return;
163
163
  }
164
164
 
165
- const storage = Storage.require<ServiceStorage>(this._name);
165
+ const storage = Storage.get<ServiceStorage>(this._name);
166
+
167
+ if (!storage) {
168
+ return;
169
+ }
166
170
 
167
171
  Storage.set(this._name, {
168
172
  ...storage,
@@ -191,14 +195,14 @@ export default class Service<
191
195
  }
192
196
 
193
197
  protected async frameworkBoot(): Promise<void> {
194
- this.restorePersistedState();
198
+ this.initializePersistedState();
195
199
  }
196
200
 
197
201
  protected async boot(): Promise<void> {
198
- // Override.
202
+ // Placeholder for overrides, don't place any functionality here.
199
203
  }
200
204
 
201
- protected restorePersistedState(): void {
205
+ protected initializePersistedState(): void {
202
206
  // TODO fix this.static()
203
207
  const persist = (this.constructor as unknown as { persist: string[] }).persist;
204
208
 
@@ -50,7 +50,7 @@ export default definePlugin({
50
50
  });
51
51
 
52
52
  declare module '@/bootstrap/options' {
53
- interface AerogelOptions {
53
+ export interface AerogelOptions {
54
54
  services?: Record<string, Service>;
55
55
  }
56
56
  }
package/src/ui/UI.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { facade, fail, uuid } from '@noeldemartin/utils';
1
+ import { after, facade, fail, 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';
@@ -8,6 +8,7 @@ import type { SnackbarAction, SnackbarColor } from '@/components/headless/snackb
8
8
 
9
9
  import Service from './UI.state';
10
10
  import type { Modal, ModalComponent, Snackbar } from './UI.state';
11
+ import type { AGAlertModalProps, AGConfirmModalProps, AGLoadingModalProps } from '@/components';
11
12
 
12
13
  interface ModalCallbacks<T = unknown> {
13
14
  willClose(result: T | undefined): void;
@@ -25,10 +26,16 @@ export const UIComponents = {
25
26
  ErrorReportModal: 'error-report-modal',
26
27
  LoadingModal: 'loading-modal',
27
28
  Snackbar: 'snackbar',
29
+ StartupCrash: 'startup-crash',
28
30
  } as const;
29
31
 
30
32
  export type UIComponent = ObjectValues<typeof UIComponents>;
31
33
 
34
+ export interface ConfirmOptions {
35
+ acceptText?: string;
36
+ cancelText?: string;
37
+ }
38
+
32
39
  export interface ShowSnackbarOptions {
33
40
  component?: Component;
34
41
  color?: SnackbarColor;
@@ -47,18 +54,45 @@ export class UIService extends Service {
47
54
  public alert(message: string): void;
48
55
  public alert(title: string, message: string): void;
49
56
  public alert(messageOrTitle: string, message?: string): void {
50
- const options = typeof message === 'string' ? { title: messageOrTitle, message } : { message: messageOrTitle };
57
+ const getProperties = (): AGAlertModalProps => {
58
+ if (typeof message !== 'string') {
59
+ return { message: messageOrTitle };
60
+ }
51
61
 
52
- this.openModal(this.requireComponent(UIComponents.AlertModal), options);
62
+ return {
63
+ title: messageOrTitle,
64
+ message,
65
+ };
66
+ };
67
+
68
+ this.openModal(this.requireComponent(UIComponents.AlertModal), getProperties());
53
69
  }
54
70
 
55
- public async confirm(message: string): Promise<boolean>;
56
- public async confirm(title: string, message: string): Promise<boolean>;
57
- public async confirm(messageOrTitle: string, message?: string): Promise<boolean> {
58
- const options = typeof message === 'string' ? { title: messageOrTitle, message } : { message: messageOrTitle };
71
+ public async confirm(message: string, options?: ConfirmOptions): Promise<boolean>;
72
+ public async confirm(title: string, message: string, options?: ConfirmOptions): Promise<boolean>;
73
+ public async confirm(
74
+ messageOrTitle: string,
75
+ messageOrOptions?: string | ConfirmOptions,
76
+ options?: ConfirmOptions,
77
+ ): Promise<boolean> {
78
+ const getProperties = (): AGConfirmModalProps => {
79
+ if (typeof messageOrOptions !== 'string') {
80
+ return {
81
+ message: messageOrTitle,
82
+ ...(messageOrOptions ?? {}),
83
+ };
84
+ }
85
+
86
+ return {
87
+ title: messageOrTitle,
88
+ message: messageOrOptions,
89
+ ...(options ?? {}),
90
+ };
91
+ };
92
+
59
93
  const modal = await this.openModal<ModalComponent<{ message: string }, boolean>>(
60
94
  this.requireComponent(UIComponents.ConfirmModal),
61
- options,
95
+ getProperties(),
62
96
  );
63
97
  const result = await modal.beforeClose;
64
98
 
@@ -68,15 +102,25 @@ export class UIService extends Service {
68
102
  public async loading<T>(operation: Promise<T>): Promise<T>;
69
103
  public async loading<T>(message: string, operation: Promise<T>): Promise<T>;
70
104
  public async loading<T>(messageOrOperation: string | Promise<T>, operation?: Promise<T>): Promise<T> {
71
- operation = typeof messageOrOperation === 'string' ? (operation as Promise<T>) : messageOrOperation;
105
+ const getProperties = (): AGLoadingModalProps => {
106
+ if (typeof messageOrOperation !== 'string') {
107
+ return {};
108
+ }
109
+
110
+ return { message: messageOrOperation };
111
+ };
112
+
113
+ const modal = await this.openModal(this.requireComponent(UIComponents.LoadingModal), getProperties());
72
114
 
73
- const message = typeof messageOrOperation === 'string' ? messageOrOperation : undefined;
74
- const modal = await this.openModal(this.requireComponent(UIComponents.LoadingModal), { message });
75
- const result = await operation;
115
+ try {
116
+ operation = typeof messageOrOperation === 'string' ? (operation as Promise<T>) : messageOrOperation;
76
117
 
77
- await this.closeModal(modal.id);
118
+ const [result] = await Promise.all([operation, after({ seconds: 1 })]);
78
119
 
79
- return result;
120
+ return result;
121
+ } finally {
122
+ await this.closeModal(modal.id);
123
+ }
80
124
  }
81
125
 
82
126
  public showSnackbar(message: string, options: ShowSnackbarOptions = {}): void {
@@ -138,6 +182,7 @@ export class UIService extends Service {
138
182
 
139
183
  protected async boot(): Promise<void> {
140
184
  this.watchModalEvents();
185
+ this.watchMountedEvent();
141
186
  }
142
187
 
143
188
  private watchModalEvents(): void {
@@ -165,6 +210,24 @@ export class UIService extends Service {
165
210
  });
166
211
  }
167
212
 
213
+ private watchMountedEvent(): void {
214
+ Events.once('application-mounted', async () => {
215
+ const splash = document.getElementById('splash');
216
+
217
+ if (!splash) {
218
+ return;
219
+ }
220
+
221
+ if (window.getComputedStyle(splash).opacity !== '0') {
222
+ splash.style.opacity = '0';
223
+
224
+ await after({ ms: 600 });
225
+ }
226
+
227
+ splash.remove();
228
+ });
229
+ }
230
+
168
231
  }
169
232
 
170
233
  export default facade(new UIService());
package/src/ui/index.ts CHANGED
@@ -9,12 +9,14 @@ 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
11
  import AGSnackbar from '../components/snackbars/AGSnackbar.vue';
12
+ import AGStartupCrash from '../components/lib/AGStartupCrash.vue';
12
13
  import type { UIComponent } from './UI';
13
14
 
14
- export { UI, UIComponents, UIComponent };
15
-
16
15
  const services = { $ui: UI };
17
16
 
17
+ export * from './UI';
18
+ export { default as UI } from './UI';
19
+
18
20
  export type UIServices = typeof services;
19
21
 
20
22
  export default definePlugin({
@@ -25,6 +27,7 @@ export default definePlugin({
25
27
  [UIComponents.ErrorReportModal]: AGErrorReportModal,
26
28
  [UIComponents.LoadingModal]: AGLoadingModal,
27
29
  [UIComponents.Snackbar]: AGSnackbar,
30
+ [UIComponents.StartupCrash]: AGStartupCrash,
28
31
  };
29
32
 
30
33
  Object.entries({
@@ -37,7 +40,7 @@ export default definePlugin({
37
40
  });
38
41
 
39
42
  declare module '@/bootstrap/options' {
40
- interface AerogelOptions {
43
+ export interface AerogelOptions {
41
44
  components?: Partial<Record<UIComponent, Component>>;
42
45
  }
43
46
  }
@@ -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 {
@@ -1,4 +1,5 @@
1
1
  export * from './composition/events';
2
2
  export * from './composition/forms';
3
3
  export * from './composition/hooks';
4
+ export * from './tailwindcss';
4
5
  export * from './vue';
@@ -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
+ });
@@ -0,0 +1,7 @@
1
+ export function removeInteractiveClasses(classes: string): string {
2
+ return classes
3
+ .split(/\s+/)
4
+ .filter((className) => !/^(hover|focus|focus-visible):/.test(className))
5
+ .join(' ')
6
+ .trim();
7
+ }
package/src/utils/vue.ts CHANGED
@@ -1,9 +1,9 @@
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> = {
6
- type: PropType<T>;
6
+ type?: PropType<T>;
7
7
  validator?(value: unknown): boolean;
8
8
  };
9
9
 
@@ -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
  }
@@ -64,7 +73,7 @@ export function injectOrFail<T>(key: InjectionKey<T> | string, errorMessage?: st
64
73
  return inject(key) ?? fail(errorMessage ?? `Could not resolve '${key}' injection key`);
65
74
  }
66
75
 
67
- export function mixedProp<T>(type: PropType<T>): OptionalProp<T | null> {
76
+ export function mixedProp<T>(type?: PropType<T>): OptionalProp<T | null> {
68
77
  return {
69
78
  type,
70
79
  default: null,
@@ -108,7 +117,7 @@ export function requiredEnumProp<Enum extends Record<string, unknown>>(
108
117
  };
109
118
  }
110
119
 
111
- export function requiredMixedProp<T>(type: PropType<T>): RequiredProp<T> {
120
+ export function requiredMixedProp<T>(type?: PropType<T>): RequiredProp<T> {
112
121
  return {
113
122
  type,
114
123
  required: true,
@@ -0,0 +1,4 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ module.exports = {
3
+ content: ['./src/**/*.{vue,ts}'],
4
+ };
package/tsconfig.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "extends": "../../tsconfig.json",
3
3
  "compilerOptions": {
4
- "types": ["unplugin-icons/types/vue3"],
4
+ "types": ["unplugin-icons/types/vue3", "@aerogel/vite/dist/virtual"],
5
5
  "baseUrl": ".",
6
6
  "paths": {
7
7
  "@/*": ["./src/*"]
package/.eslintrc.js DELETED
@@ -1,3 +0,0 @@
1
- module.exports = {
2
- extends: ['@noeldemartin/eslint-config-vue'],
3
- };
package/dist/virtual.d.ts DELETED
@@ -1,11 +0,0 @@
1
- declare module 'virtual:aerogel' {
2
- interface AerogelBuild {
3
- environment: 'production' | 'development' | 'testing';
4
- basePath?: string;
5
- sourceUrl?: string;
6
- }
7
-
8
- const build: AerogelBuild;
9
-
10
- export default build;
11
- }
@@ -1,5 +0,0 @@
1
- import AGErrorMessage from './AGErrorMessage.vue';
2
- import AGLink from './AGLink.vue';
3
- import AGMarkdown from './AGMarkdown.vue';
4
-
5
- export { AGErrorMessage, AGLink, AGMarkdown };
@@ -1,11 +0,0 @@
1
- declare module 'virtual:aerogel' {
2
- interface AerogelBuild {
3
- environment: 'production' | 'development' | 'testing';
4
- basePath?: string;
5
- sourceUrl?: string;
6
- }
7
-
8
- const build: AerogelBuild;
9
-
10
- export default build;
11
- }
File without changes
File without changes