@aerogel/core 0.0.0-next.7035064d9ec6a82a936ee8dfcc4b58ed2e25a399 → 0.0.0-next.73a6df428477c011a1aebe0a932ebef0aa5e1b16

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 (207) hide show
  1. package/dist/aerogel-core.css +1 -0
  2. package/dist/aerogel-core.d.ts +2078 -1925
  3. package/dist/aerogel-core.js +3809 -0
  4. package/dist/aerogel-core.js.map +1 -0
  5. package/package.json +32 -37
  6. package/src/bootstrap/bootstrap.test.ts +4 -7
  7. package/src/bootstrap/index.ts +14 -15
  8. package/src/bootstrap/options.ts +1 -1
  9. package/src/components/AppLayout.vue +14 -0
  10. package/src/components/{AGAppModals.vue → AppModals.vue} +3 -4
  11. package/src/components/AppOverlays.vue +9 -0
  12. package/src/components/AppToasts.vue +16 -0
  13. package/src/components/contracts/AlertModal.ts +19 -0
  14. package/src/components/contracts/Button.ts +16 -0
  15. package/src/components/contracts/ConfirmModal.ts +48 -0
  16. package/src/components/contracts/DropdownMenu.ts +25 -0
  17. package/src/components/contracts/ErrorReportModal.ts +33 -0
  18. package/src/components/contracts/Input.ts +26 -0
  19. package/src/components/contracts/LoadingModal.ts +26 -0
  20. package/src/components/contracts/Modal.ts +21 -0
  21. package/src/components/contracts/PromptModal.ts +34 -0
  22. package/src/components/contracts/Select.ts +45 -0
  23. package/src/components/contracts/Toast.ts +15 -0
  24. package/src/components/contracts/index.ts +11 -0
  25. package/src/components/headless/HeadlessButton.vue +51 -0
  26. package/src/components/headless/HeadlessInput.vue +59 -0
  27. package/src/components/headless/{forms/AGHeadlessInputDescription.vue → HeadlessInputDescription.vue} +7 -8
  28. package/src/components/headless/{forms/AGHeadlessInputError.vue → HeadlessInputError.vue} +4 -8
  29. package/src/components/headless/HeadlessInputInput.vue +86 -0
  30. package/src/components/headless/{forms/AGHeadlessInputLabel.vue → HeadlessInputLabel.vue} +3 -7
  31. package/src/components/headless/{forms/AGHeadlessInputTextArea.vue → HeadlessInputTextArea.vue} +10 -13
  32. package/src/components/headless/HeadlessModal.vue +57 -0
  33. package/src/components/headless/HeadlessModalContent.vue +30 -0
  34. package/src/components/headless/HeadlessModalDescription.vue +12 -0
  35. package/src/components/headless/HeadlessModalOverlay.vue +12 -0
  36. package/src/components/headless/HeadlessModalTitle.vue +12 -0
  37. package/src/components/headless/HeadlessSelect.vue +120 -0
  38. package/src/components/headless/{forms/AGHeadlessSelectError.vue → HeadlessSelectError.vue} +5 -6
  39. package/src/components/headless/HeadlessSelectLabel.vue +25 -0
  40. package/src/components/headless/HeadlessSelectOption.vue +34 -0
  41. package/src/components/headless/HeadlessSelectOptions.vue +42 -0
  42. package/src/components/headless/HeadlessSelectTrigger.vue +22 -0
  43. package/src/components/headless/HeadlessSelectValue.vue +18 -0
  44. package/src/components/headless/HeadlessSwitch.vue +96 -0
  45. package/src/components/headless/HeadlessToast.vue +18 -0
  46. package/src/components/headless/HeadlessToastAction.vue +13 -0
  47. package/src/components/headless/index.ts +20 -3
  48. package/src/components/index.ts +6 -11
  49. package/src/components/ui/AdvancedOptions.vue +18 -0
  50. package/src/components/ui/AlertModal.vue +17 -0
  51. package/src/components/ui/Button.vue +115 -0
  52. package/src/components/ui/Checkbox.vue +56 -0
  53. package/src/components/ui/ConfirmModal.vue +50 -0
  54. package/src/components/ui/DropdownMenu.vue +32 -0
  55. package/src/components/ui/DropdownMenuOption.vue +22 -0
  56. package/src/components/ui/DropdownMenuOptions.vue +44 -0
  57. package/src/components/ui/EditableContent.vue +82 -0
  58. package/src/components/ui/ErrorLogs.vue +19 -0
  59. package/src/components/ui/ErrorLogsModal.vue +48 -0
  60. package/src/components/ui/ErrorMessage.vue +15 -0
  61. package/src/components/ui/ErrorReportModal.vue +73 -0
  62. package/src/components/{modals/AGErrorReportModalButtons.vue → ui/ErrorReportModalButtons.vue} +34 -27
  63. package/src/components/ui/ErrorReportModalTitle.vue +24 -0
  64. package/src/components/{forms/AGForm.vue → ui/Form.vue} +4 -5
  65. package/src/components/ui/Input.vue +56 -0
  66. package/src/components/ui/Link.vue +12 -0
  67. package/src/components/ui/LoadingModal.vue +34 -0
  68. package/src/components/ui/Markdown.vue +97 -0
  69. package/src/components/ui/Modal.vue +131 -0
  70. package/src/components/{modals/AGModalContext.vue → ui/ModalContext.vue} +8 -9
  71. package/src/components/ui/ProgressBar.vue +51 -0
  72. package/src/components/ui/PromptModal.vue +38 -0
  73. package/src/components/ui/Select.vue +27 -0
  74. package/src/components/ui/SelectLabel.vue +21 -0
  75. package/src/components/ui/SelectOption.vue +29 -0
  76. package/src/components/ui/SelectOptions.vue +35 -0
  77. package/src/components/ui/SelectTrigger.vue +29 -0
  78. package/src/components/ui/Setting.vue +31 -0
  79. package/src/components/ui/SettingsModal.vue +15 -0
  80. package/src/components/ui/StartupCrash.vue +76 -0
  81. package/src/components/ui/Switch.vue +11 -0
  82. package/src/components/ui/TextArea.vue +56 -0
  83. package/src/components/ui/Toast.vue +46 -0
  84. package/src/components/ui/index.ts +35 -0
  85. package/src/directives/index.ts +9 -5
  86. package/src/directives/measure.ts +12 -6
  87. package/src/errors/Errors.state.ts +2 -1
  88. package/src/errors/Errors.ts +56 -33
  89. package/src/errors/index.ts +15 -8
  90. package/src/errors/settings/Debug.vue +14 -0
  91. package/src/errors/settings/index.ts +10 -0
  92. package/src/errors/utils.ts +1 -1
  93. package/src/forms/{Form.test.ts → FormController.test.ts} +37 -10
  94. package/src/forms/{Form.ts → FormController.ts} +51 -38
  95. package/src/forms/index.ts +2 -3
  96. package/src/forms/utils.ts +59 -34
  97. package/src/forms/validation.ts +31 -0
  98. package/src/index.css +76 -0
  99. package/src/jobs/Job.ts +2 -2
  100. package/src/jobs/listeners.ts +1 -1
  101. package/src/lang/DefaultLangProvider.ts +7 -4
  102. package/src/lang/Lang.state.ts +1 -1
  103. package/src/lang/Lang.ts +1 -1
  104. package/src/lang/index.ts +12 -6
  105. package/src/lang/settings/Language.vue +48 -0
  106. package/src/lang/settings/index.ts +10 -0
  107. package/src/plugins/Plugin.ts +1 -1
  108. package/src/plugins/index.ts +10 -7
  109. package/src/services/App.state.ts +15 -4
  110. package/src/services/App.ts +12 -4
  111. package/src/services/Cache.ts +1 -1
  112. package/src/services/Events.test.ts +8 -8
  113. package/src/services/Events.ts +4 -10
  114. package/src/services/Service.ts +32 -27
  115. package/src/services/Storage.ts +3 -3
  116. package/src/services/index.ts +10 -6
  117. package/src/services/utils.ts +2 -2
  118. package/src/testing/index.ts +8 -3
  119. package/src/testing/setup.ts +3 -19
  120. package/src/ui/UI.state.ts +8 -13
  121. package/src/ui/UI.ts +148 -116
  122. package/src/ui/index.ts +27 -28
  123. package/src/utils/app.ts +7 -0
  124. package/src/utils/classes.ts +41 -0
  125. package/src/utils/composition/events.ts +4 -6
  126. package/src/utils/composition/forms.ts +20 -4
  127. package/src/utils/composition/state.ts +11 -2
  128. package/src/utils/index.ts +5 -1
  129. package/src/utils/markdown.ts +37 -5
  130. package/src/utils/types.ts +3 -0
  131. package/src/utils/vue.ts +31 -137
  132. package/dist/aerogel-core.cjs.js +0 -2
  133. package/dist/aerogel-core.cjs.js.map +0 -1
  134. package/dist/aerogel-core.esm.js +0 -2
  135. package/dist/aerogel-core.esm.js.map +0 -1
  136. package/histoire.config.ts +0 -7
  137. package/noeldemartin.config.js +0 -5
  138. package/postcss.config.js +0 -6
  139. package/src/assets/histoire.css +0 -3
  140. package/src/components/AGAppLayout.vue +0 -16
  141. package/src/components/AGAppOverlays.vue +0 -41
  142. package/src/components/AGAppSnackbars.vue +0 -13
  143. package/src/components/composition.ts +0 -23
  144. package/src/components/constants.ts +0 -8
  145. package/src/components/forms/AGButton.vue +0 -44
  146. package/src/components/forms/AGCheckbox.vue +0 -41
  147. package/src/components/forms/AGInput.vue +0 -40
  148. package/src/components/forms/AGSelect.story.vue +0 -46
  149. package/src/components/forms/AGSelect.vue +0 -60
  150. package/src/components/forms/index.ts +0 -5
  151. package/src/components/headless/forms/AGHeadlessButton.ts +0 -3
  152. package/src/components/headless/forms/AGHeadlessButton.vue +0 -62
  153. package/src/components/headless/forms/AGHeadlessInput.ts +0 -34
  154. package/src/components/headless/forms/AGHeadlessInput.vue +0 -70
  155. package/src/components/headless/forms/AGHeadlessInputInput.vue +0 -84
  156. package/src/components/headless/forms/AGHeadlessSelect.ts +0 -42
  157. package/src/components/headless/forms/AGHeadlessSelect.vue +0 -77
  158. package/src/components/headless/forms/AGHeadlessSelectButton.vue +0 -24
  159. package/src/components/headless/forms/AGHeadlessSelectLabel.vue +0 -24
  160. package/src/components/headless/forms/AGHeadlessSelectOption.ts +0 -4
  161. package/src/components/headless/forms/AGHeadlessSelectOption.vue +0 -39
  162. package/src/components/headless/forms/AGHeadlessSelectOptions.ts +0 -3
  163. package/src/components/headless/forms/composition.ts +0 -10
  164. package/src/components/headless/forms/index.ts +0 -18
  165. package/src/components/headless/modals/AGHeadlessModal.ts +0 -36
  166. package/src/components/headless/modals/AGHeadlessModal.vue +0 -92
  167. package/src/components/headless/modals/AGHeadlessModalPanel.vue +0 -32
  168. package/src/components/headless/modals/AGHeadlessModalTitle.vue +0 -23
  169. package/src/components/headless/modals/index.ts +0 -4
  170. package/src/components/headless/snackbars/AGHeadlessSnackbar.vue +0 -10
  171. package/src/components/headless/snackbars/index.ts +0 -40
  172. package/src/components/interfaces.ts +0 -24
  173. package/src/components/lib/AGErrorMessage.vue +0 -16
  174. package/src/components/lib/AGLink.vue +0 -9
  175. package/src/components/lib/AGMarkdown.vue +0 -54
  176. package/src/components/lib/AGMeasured.vue +0 -16
  177. package/src/components/lib/AGProgressBar.vue +0 -30
  178. package/src/components/lib/AGStartupCrash.vue +0 -31
  179. package/src/components/lib/index.ts +0 -6
  180. package/src/components/modals/AGAlertModal.ts +0 -18
  181. package/src/components/modals/AGAlertModal.vue +0 -14
  182. package/src/components/modals/AGConfirmModal.ts +0 -41
  183. package/src/components/modals/AGConfirmModal.vue +0 -26
  184. package/src/components/modals/AGErrorReportModal.ts +0 -49
  185. package/src/components/modals/AGErrorReportModal.vue +0 -54
  186. package/src/components/modals/AGErrorReportModalTitle.vue +0 -25
  187. package/src/components/modals/AGLoadingModal.ts +0 -29
  188. package/src/components/modals/AGLoadingModal.vue +0 -15
  189. package/src/components/modals/AGModal.ts +0 -11
  190. package/src/components/modals/AGModal.vue +0 -39
  191. package/src/components/modals/AGModalContext.ts +0 -8
  192. package/src/components/modals/AGModalTitle.vue +0 -9
  193. package/src/components/modals/AGPromptModal.ts +0 -41
  194. package/src/components/modals/AGPromptModal.vue +0 -34
  195. package/src/components/modals/index.ts +0 -17
  196. package/src/components/snackbars/AGSnackbar.vue +0 -36
  197. package/src/components/snackbars/index.ts +0 -3
  198. package/src/components/utils.ts +0 -10
  199. package/src/directives/initial-focus.ts +0 -11
  200. package/src/forms/composition.ts +0 -6
  201. package/src/main.histoire.ts +0 -1
  202. package/src/utils/tailwindcss.test.ts +0 -26
  203. package/src/utils/tailwindcss.ts +0 -7
  204. package/tailwind.config.js +0 -4
  205. package/tsconfig.json +0 -11
  206. package/vite.config.ts +0 -17
  207. /package/src/{main.ts → index.ts} +0 -0
@@ -11,9 +11,10 @@ import {
11
11
  import type { Constructor, Nullable } from '@noeldemartin/utils';
12
12
  import type { Store } from 'pinia';
13
13
 
14
- import ServiceBootError from '@/errors/ServiceBootError';
15
- import { defineServiceStore } from '@/services/store';
16
- import type { Unref } from '@/utils/vue';
14
+ import ServiceBootError from '@aerogel/core/errors/ServiceBootError';
15
+ import { appNamespace } from '@aerogel/core/utils/app';
16
+ import { defineServiceStore } from '@aerogel/core/services/store';
17
+ import type { Unref } from '@aerogel/core/utils/vue';
17
18
 
18
19
  export type ServiceState = Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any
19
20
  export type DefaultServiceState = any; // eslint-disable-line @typescript-eslint/no-explicit-any
@@ -32,7 +33,7 @@ export type StateWatchers<TService extends Service, TState extends ServiceState>
32
33
  export type ServiceWithState<
33
34
  State extends ServiceState = ServiceState,
34
35
  ComputedState extends ServiceState = {},
35
- ServiceStorage = Partial<State>
36
+ ServiceStorage = Partial<State>,
36
37
  > = Constructor<Unref<State>> &
37
38
  Constructor<ComputedState> &
38
39
  Constructor<Service<Unref<State>, ComputedState, Unref<ServiceStorage>>>;
@@ -40,7 +41,7 @@ export type ServiceWithState<
40
41
  export function defineServiceState<
41
42
  State extends ServiceState = ServiceState,
42
43
  ComputedState extends ServiceState = {},
43
- ServiceStorage = Partial<State>
44
+ ServiceStorage = Partial<State>,
44
45
  >(options: {
45
46
  name: string;
46
47
  initialState: State | (() => State);
@@ -52,17 +53,17 @@ export function defineServiceState<
52
53
  }): ServiceWithState<State, ComputedState, ServiceStorage> {
53
54
  return class extends Service<Unref<State>, ComputedState, ServiceStorage> {
54
55
 
55
- public static persist = (options.persist as string[]) ?? [];
56
+ public static override persist = (options.persist as string[]) ?? [];
56
57
 
57
- protected usesStore(): boolean {
58
+ protected override usesStore(): boolean {
58
59
  return true;
59
60
  }
60
61
 
61
- protected getName(): string | null {
62
+ protected override getName(): string | null {
62
63
  return options.name ?? null;
63
64
  }
64
65
 
65
- protected getInitialState(): Unref<State> {
66
+ protected override getInitialState(): Unref<State> {
66
67
  if (typeof options.initialState === 'function') {
67
68
  return options.initialState();
68
69
  }
@@ -86,19 +87,19 @@ export function defineServiceState<
86
87
  }, {} as Unref<State>);
87
88
  }
88
89
 
89
- protected getComputedStateDefinition(): ComputedStateDefinition<Unref<State>, ComputedState> {
90
+ protected override getComputedStateDefinition(): ComputedStateDefinition<Unref<State>, ComputedState> {
90
91
  return (options.computed ?? {}) as ComputedStateDefinition<Unref<State>, ComputedState>;
91
92
  }
92
93
 
93
- protected getStateWatchers(): StateWatchers<Service, Unref<State>> {
94
+ protected override getStateWatchers(): StateWatchers<Service, Unref<State>> {
94
95
  return (options.watch ?? {}) as StateWatchers<Service, Unref<State>>;
95
96
  }
96
97
 
97
- protected serializePersistedState(state: Partial<State>): ServiceStorage {
98
+ protected override serializePersistedState(state: Partial<State>): ServiceStorage {
98
99
  return options.serialize?.(state) ?? (state as ServiceStorage);
99
100
  }
100
101
 
101
- protected deserializePersistedState(state: ServiceStorage): Partial<State> {
102
+ protected override deserializePersistedState(state: ServiceStorage): Partial<State> {
102
103
  return options.restore?.(state) ?? (state as Partial<State>);
103
104
  }
104
105
 
@@ -108,7 +109,7 @@ export function defineServiceState<
108
109
  export default class Service<
109
110
  State extends ServiceState = DefaultServiceState,
110
111
  ComputedState extends ServiceState = {},
111
- ServiceStorage = Partial<State>
112
+ ServiceStorage = Partial<State>,
112
113
  > extends MagicObject {
113
114
 
114
115
  public static persist: string[] = [];
@@ -142,9 +143,9 @@ export default class Service<
142
143
  return this._booted;
143
144
  }
144
145
 
145
- public static<T extends typeof Service>(): T;
146
- public static<T extends typeof Service, K extends keyof T>(property: K): T[K];
147
- public static<T extends typeof Service, K extends keyof T>(property?: K): T | T[K] {
146
+ public override static<T extends typeof Service>(): T;
147
+ public override static<T extends typeof Service, K extends keyof T>(property: K): T[K];
148
+ public override static<T extends typeof Service, K extends keyof T>(property?: K): T | T[K] {
148
149
  return super.static<T, K>(property as K);
149
150
  }
150
151
 
@@ -164,7 +165,7 @@ export default class Service<
164
165
  }
165
166
 
166
167
  public hasPersistedState(): boolean {
167
- return Storage.has(this._name);
168
+ return Storage.has(this.storageKey);
168
169
  }
169
170
 
170
171
  public hasState<P extends keyof State>(property: P): boolean {
@@ -182,10 +183,10 @@ export default class Service<
182
183
  const store = this._store as any;
183
184
 
184
185
  if (property) {
185
- return store ? store[property] : undefined;
186
+ return store ? store[property] : (undefined as State[P]);
186
187
  }
187
188
 
188
- return store ? store : {};
189
+ return store ? store : ({} as State);
189
190
  }
190
191
 
191
192
  public setState<P extends keyof State>(property: P, value: State[P]): void;
@@ -219,7 +220,7 @@ export default class Service<
219
220
  this.onPersistentStateUpdated(state);
220
221
  }
221
222
 
222
- protected __get(property: string): unknown {
223
+ protected override __get(property: string): unknown {
223
224
  if (this.hasState(property)) {
224
225
  return this.getState(property);
225
226
  }
@@ -227,10 +228,14 @@ export default class Service<
227
228
  return super.__get(property);
228
229
  }
229
230
 
230
- protected __set(property: string, value: unknown): void {
231
+ protected override __set(property: string, value: unknown): void {
231
232
  this.setState({ [property]: value } as Partial<State>);
232
233
  }
233
234
 
235
+ protected get storageKey(): string {
236
+ return `${appNamespace()}:${this._name}`;
237
+ }
238
+
234
239
  protected onStateUpdated(update: Partial<State>, old: Partial<State>): void {
235
240
  const persisted = objectOnly(update, this.static('persist'));
236
241
 
@@ -250,13 +255,13 @@ export default class Service<
250
255
  }
251
256
 
252
257
  protected onPersistentStateUpdated(persisted: Partial<State>): void {
253
- const storage = Storage.get<ServiceStorage>(this._name);
258
+ const storage = Storage.get<ServiceStorage>(this.storageKey);
254
259
 
255
260
  if (!storage) {
256
261
  return;
257
262
  }
258
263
 
259
- Storage.set(this._name, {
264
+ Storage.set(this.storageKey, {
260
265
  ...storage,
261
266
  ...this.serializePersistedState(objectDeepClone(persisted) as Partial<State>),
262
267
  });
@@ -303,14 +308,14 @@ export default class Service<
303
308
  return;
304
309
  }
305
310
 
306
- if (Storage.has(this._name)) {
307
- const persisted = Storage.require<ServiceStorage>(this._name);
311
+ if (Storage.has(this.storageKey)) {
312
+ const persisted = Storage.require<ServiceStorage>(this.storageKey);
308
313
  this.setState(this.deserializePersistedState(persisted));
309
314
 
310
315
  return;
311
316
  }
312
317
 
313
- Storage.set(this._name, objectOnly(this.getState(), this.static('persist')));
318
+ Storage.set(this.storageKey, objectOnly(this.getState(), this.static('persist')));
314
319
  }
315
320
 
316
321
  protected requireStore(): Store<string, State, ComputedState, {}> {
@@ -1,7 +1,7 @@
1
1
  import { facade } from '@noeldemartin/utils';
2
2
 
3
- import Events from '@/services/Events';
4
- import Service from '@/services/Service';
3
+ import Events from '@aerogel/core/services/Events';
4
+ import Service from '@aerogel/core/services/Service';
5
5
 
6
6
  export class StorageService extends Service {
7
7
 
@@ -13,7 +13,7 @@ export class StorageService extends Service {
13
13
 
14
14
  export default facade(StorageService);
15
15
 
16
- declare module '@/services/Events' {
16
+ declare module '@aerogel/core/services/Events' {
17
17
  export interface EventsPayload {
18
18
  'purge-storage': void;
19
19
  }
@@ -1,6 +1,7 @@
1
- import type { App as VueApp } from 'vue';
1
+ import type { App as AppInstance } from 'vue';
2
2
 
3
- import { definePlugin } from '@/plugins';
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';
@@ -8,6 +9,7 @@ import Events from './Events';
8
9
  import Service from './Service';
9
10
  import Storage from './Storage';
10
11
  import { getPiniaStore } from './store';
12
+ import type { AppSetting } from './App.state';
11
13
 
12
14
  export * from './App';
13
15
  export * from './Cache';
@@ -28,7 +30,7 @@ export type DefaultServices = typeof defaultServices;
28
30
 
29
31
  export interface Services extends DefaultServices {}
30
32
 
31
- export async function bootServices(app: VueApp, services: Record<string, Service>): Promise<void> {
33
+ export async function bootServices(app: AppInstance, services: Record<string, Service>): Promise<void> {
32
34
  await Promise.all(
33
35
  Object.entries(services).map(async ([name, service]) => {
34
36
  await service
@@ -39,7 +41,7 @@ export async function bootServices(app: VueApp, services: Record<string, Service
39
41
 
40
42
  Object.assign(app.config.globalProperties, services);
41
43
 
42
- if (App.development || App.testing) {
44
+ if (isDevelopment() || isTesting()) {
43
45
  Object.assign(globalThis, services);
44
46
  }
45
47
  }
@@ -52,17 +54,19 @@ export default definePlugin({
52
54
  };
53
55
 
54
56
  app.use(getPiniaStore());
57
+ options.settings?.forEach((setting) => App.addSetting(setting));
55
58
 
56
59
  await bootServices(app, services);
57
60
  },
58
61
  });
59
62
 
60
- declare module '@/bootstrap/options' {
63
+ declare module '@aerogel/core/bootstrap/options' {
61
64
  export interface AerogelOptions {
62
65
  services?: Record<string, Service>;
66
+ settings?: AppSetting[];
63
67
  }
64
68
  }
65
69
 
66
- declare module '@vue/runtime-core' {
70
+ declare module 'vue' {
67
71
  interface ComponentCustomProperties extends Services {}
68
72
  }
@@ -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,
@@ -1,20 +1,25 @@
1
+ import { isTesting } from '@noeldemartin/utils';
1
2
  import type { GetClosureArgs } from '@noeldemartin/utils';
2
3
 
3
- import Events from '@/services/Events';
4
- import { definePlugin } from '@/plugins';
4
+ import Events from '@aerogel/core/services/Events';
5
+ import { App } from '@aerogel/core/services';
6
+ import { definePlugin } from '@aerogel/core/plugins';
7
+ import type { Services } from '@aerogel/core/services';
5
8
 
6
9
  export interface AerogelTestingRuntime {
7
10
  on: (typeof Events)['on'];
11
+ service<T extends keyof Services>(name: T): Services[T] | null;
8
12
  }
9
13
 
10
14
  export default definePlugin({
11
15
  async install() {
12
- if (import.meta.env.MODE !== 'testing') {
16
+ if (!isTesting()) {
13
17
  return;
14
18
  }
15
19
 
16
20
  globalThis.testingRuntime = {
17
21
  on: ((...args: GetClosureArgs<(typeof Events)['on']>) => Events.on(...args)) as (typeof Events)['on'],
22
+ service: (name) => App.service(name),
18
23
  };
19
24
  },
20
25
  });
@@ -1,27 +1,11 @@
1
- import { mock, tap, toString } from '@noeldemartin/utils';
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
- vi.stubGlobal('document', {
24
- querySelector: () => null,
25
- getElementById: () => null,
26
- });
9
+ FakeLocalStorage.reset();
10
+ FakeLocalStorage.patchGlobal();
27
11
  });
@@ -1,25 +1,19 @@
1
1
  import type { Component } from 'vue';
2
2
 
3
- import { defineServiceState } from '@/services/Service';
3
+ import { defineServiceState } from '@aerogel/core/services/Service';
4
4
 
5
5
  import { Layouts, getCurrentLayout } from './utils';
6
6
 
7
- export interface Modal<T = unknown> {
7
+ export interface UIModal<T = unknown> {
8
8
  id: string;
9
9
  properties: Record<string, unknown>;
10
10
  component: Component;
11
+ closing: boolean;
11
12
  beforeClose: Promise<T | undefined>;
12
13
  afterClose: Promise<T | undefined>;
13
14
  }
14
15
 
15
- export interface ModalComponent<
16
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
17
- Properties extends Record<string, unknown> = Record<string, unknown>,
18
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
19
- Result = unknown
20
- > {}
21
-
22
- export interface Snackbar {
16
+ export interface UIToast {
23
17
  id: string;
24
18
  component: Component;
25
19
  properties: Record<string, unknown>;
@@ -28,12 +22,13 @@ export interface Snackbar {
28
22
  export default defineServiceState({
29
23
  name: 'ui',
30
24
  initialState: {
31
- modals: [] as Modal[],
32
- snackbars: [] as Snackbar[],
25
+ modals: [] as UIModal[],
26
+ toasts: [] as UIToast[],
33
27
  layout: getCurrentLayout(),
34
28
  },
35
29
  computed: {
36
- mobile: ({ layout }) => layout === Layouts.Mobile,
37
30
  desktop: ({ layout }) => layout === Layouts.Desktop,
31
+ mobile: ({ layout }) => layout === Layouts.Mobile,
32
+ openModals: ({ modals }) => modals.filter(({ closing }) => !closing),
38
33
  },
39
34
  });