@aerogel/core 0.0.0-next.9a02fcd3bcf698211dd7a71d4c48257c96dd7832 → 0.0.0-next.9a1c5ba39a454b316eba36ec7bdf579fed3d95d2

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 (132) hide show
  1. package/dist/aerogel-core.d.ts +2130 -1361
  2. package/dist/aerogel-core.js +2763 -0
  3. package/dist/aerogel-core.js.map +1 -0
  4. package/package.json +23 -37
  5. package/src/bootstrap/bootstrap.test.ts +4 -8
  6. package/src/bootstrap/index.ts +25 -16
  7. package/src/bootstrap/options.ts +1 -1
  8. package/src/components/AGAppLayout.vue +1 -1
  9. package/src/components/AGAppModals.vue +1 -1
  10. package/src/components/AGAppOverlays.vue +1 -1
  11. package/src/components/AGAppSnackbars.vue +1 -1
  12. package/src/components/composition.ts +23 -0
  13. package/src/components/contracts/Modal.ts +16 -0
  14. package/src/components/contracts/index.ts +2 -0
  15. package/src/components/contracts/shared.ts +9 -0
  16. package/src/components/forms/AGButton.vue +2 -2
  17. package/src/components/forms/AGCheckbox.vue +4 -3
  18. package/src/components/forms/AGForm.vue +11 -12
  19. package/src/components/forms/AGInput.vue +8 -4
  20. package/src/components/forms/AGSelect.vue +11 -17
  21. package/src/components/headless/forms/AGHeadlessButton.ts +3 -0
  22. package/src/components/headless/forms/AGHeadlessButton.vue +16 -5
  23. package/src/components/headless/forms/AGHeadlessInput.ts +18 -5
  24. package/src/components/headless/forms/AGHeadlessInput.vue +19 -6
  25. package/src/components/headless/forms/AGHeadlessInputDescription.vue +28 -0
  26. package/src/components/headless/forms/AGHeadlessInputError.vue +2 -2
  27. package/src/components/headless/forms/AGHeadlessInputInput.vue +43 -6
  28. package/src/components/headless/forms/AGHeadlessInputLabel.vue +1 -1
  29. package/src/components/headless/forms/AGHeadlessInputTextArea.vue +43 -0
  30. package/src/components/headless/forms/AGHeadlessSelect.ts +3 -3
  31. package/src/components/headless/forms/AGHeadlessSelect.vue +15 -15
  32. package/src/components/headless/forms/AGHeadlessSelectError.vue +2 -2
  33. package/src/components/headless/forms/AGHeadlessSelectOption.vue +10 -18
  34. package/src/components/headless/forms/AGHeadlessSelectOptions.vue +19 -0
  35. package/src/components/headless/forms/AGHeadlessSelectTrigger.vue +25 -0
  36. package/src/components/headless/forms/composition.ts +10 -0
  37. package/src/components/headless/forms/index.ts +6 -3
  38. package/src/components/headless/modals/AGHeadlessModal.ts +17 -18
  39. package/src/components/headless/modals/AGHeadlessModal.vue +12 -10
  40. package/src/components/headless/modals/AGHeadlessModalContent.vue +25 -0
  41. package/src/components/headless/modals/index.ts +3 -2
  42. package/src/components/headless/snackbars/index.ts +3 -3
  43. package/src/components/index.ts +2 -0
  44. package/src/components/lib/AGErrorMessage.vue +3 -3
  45. package/src/components/lib/AGMarkdown.vue +24 -6
  46. package/src/components/lib/AGMeasured.vue +3 -2
  47. package/src/components/lib/AGProgressBar.vue +55 -0
  48. package/src/components/lib/AGStartupCrash.vue +1 -1
  49. package/src/components/lib/index.ts +1 -0
  50. package/src/components/modals/AGAlertModal.ts +6 -3
  51. package/src/components/modals/AGConfirmModal.ts +19 -4
  52. package/src/components/modals/AGConfirmModal.vue +6 -5
  53. package/src/components/modals/AGErrorReportModal.ts +8 -5
  54. package/src/components/modals/AGErrorReportModal.vue +2 -2
  55. package/src/components/modals/AGErrorReportModalButtons.vue +10 -10
  56. package/src/components/modals/AGErrorReportModalTitle.vue +2 -2
  57. package/src/components/modals/AGLoadingModal.ts +11 -5
  58. package/src/components/modals/AGModal.vue +20 -19
  59. package/src/components/modals/AGModalContext.ts +1 -1
  60. package/src/components/modals/AGModalContext.vue +15 -5
  61. package/src/components/modals/AGModalTitle.vue +1 -1
  62. package/src/components/modals/AGPromptModal.ts +15 -4
  63. package/src/components/modals/AGPromptModal.vue +5 -4
  64. package/src/components/modals/index.ts +0 -1
  65. package/src/components/snackbars/AGSnackbar.vue +2 -2
  66. package/src/components/utils.ts +62 -9
  67. package/src/directives/index.ts +11 -5
  68. package/src/directives/measure.ts +34 -6
  69. package/src/errors/Errors.state.ts +1 -1
  70. package/src/errors/Errors.ts +12 -12
  71. package/src/errors/JobCancelledError.ts +3 -0
  72. package/src/errors/index.ts +9 -6
  73. package/src/errors/utils.ts +17 -1
  74. package/src/forms/Form.test.ts +32 -3
  75. package/src/forms/Form.ts +80 -20
  76. package/src/forms/composition.ts +2 -2
  77. package/src/forms/index.ts +3 -1
  78. package/src/forms/utils.ts +34 -3
  79. package/src/forms/validation.ts +19 -0
  80. package/src/{main.ts → index.ts} +1 -0
  81. package/src/jobs/Job.ts +144 -2
  82. package/src/jobs/index.ts +4 -1
  83. package/src/jobs/listeners.ts +3 -0
  84. package/src/jobs/status.ts +4 -0
  85. package/src/lang/DefaultLangProvider.ts +46 -0
  86. package/src/lang/Lang.state.ts +11 -0
  87. package/src/lang/Lang.ts +48 -21
  88. package/src/lang/index.ts +8 -6
  89. package/src/plugins/Plugin.ts +1 -1
  90. package/src/plugins/index.ts +10 -7
  91. package/src/services/App.state.ts +26 -3
  92. package/src/services/App.ts +11 -3
  93. package/src/services/Cache.ts +43 -0
  94. package/src/services/Events.ts +15 -5
  95. package/src/services/Service.ts +125 -54
  96. package/src/services/Storage.ts +20 -0
  97. package/src/services/index.ts +13 -5
  98. package/src/services/utils.ts +18 -0
  99. package/src/testing/index.ts +4 -3
  100. package/src/testing/setup.ts +11 -0
  101. package/src/ui/UI.state.ts +9 -2
  102. package/src/ui/UI.ts +157 -52
  103. package/src/ui/index.ts +5 -4
  104. package/src/ui/utils.ts +16 -0
  105. package/src/utils/composition/events.ts +2 -2
  106. package/src/utils/composition/persistent.test.ts +33 -0
  107. package/src/utils/composition/persistent.ts +11 -0
  108. package/src/utils/composition/state.test.ts +47 -0
  109. package/src/utils/composition/state.ts +24 -0
  110. package/src/utils/index.ts +2 -0
  111. package/src/utils/markdown.test.ts +50 -0
  112. package/src/utils/markdown.ts +19 -6
  113. package/src/utils/vue.ts +22 -15
  114. package/dist/aerogel-core.cjs.js +0 -2
  115. package/dist/aerogel-core.cjs.js.map +0 -1
  116. package/dist/aerogel-core.esm.js +0 -2
  117. package/dist/aerogel-core.esm.js.map +0 -1
  118. package/histoire.config.ts +0 -7
  119. package/noeldemartin.config.js +0 -5
  120. package/postcss.config.js +0 -6
  121. package/src/assets/histoire.css +0 -3
  122. package/src/components/headless/forms/AGHeadlessSelectButton.vue +0 -24
  123. package/src/components/headless/forms/AGHeadlessSelectLabel.vue +0 -24
  124. package/src/components/headless/forms/AGHeadlessSelectOptions.ts +0 -3
  125. package/src/components/headless/modals/AGHeadlessModalPanel.vue +0 -28
  126. package/src/components/headless/modals/AGHeadlessModalTitle.vue +0 -13
  127. package/src/components/modals/AGModal.ts +0 -10
  128. package/src/directives/initial-focus.ts +0 -11
  129. package/src/main.histoire.ts +0 -1
  130. package/tailwind.config.js +0 -4
  131. package/tsconfig.json +0 -11
  132. package/vite.config.ts +0 -14
@@ -1,14 +1,17 @@
1
1
  import type { ExtractPropTypes } from 'vue';
2
- import type { ObjectWithoutEmpty } from '@noeldemartin/utils';
2
+ import type { ObjectWithout, Pretty } from '@noeldemartin/utils';
3
3
 
4
- import { requiredStringProp, stringProp } from '@/utils';
4
+ import { requiredStringProp, stringProp } from '@aerogel/core/utils';
5
+ import type { AcceptRefs } from '@aerogel/core/utils';
5
6
 
6
7
  export const alertModalProps = {
7
8
  title: stringProp(),
8
9
  message: requiredStringProp(),
9
10
  };
10
11
 
11
- export type AGAlertModalProps = ObjectWithoutEmpty<ExtractPropTypes<typeof alertModalProps>>;
12
+ export type AGAlertModalProps = Pretty<
13
+ AcceptRefs<ObjectWithout<ExtractPropTypes<typeof alertModalProps>, null | undefined>>
14
+ >;
12
15
 
13
16
  export function useAlertModalProps(): typeof alertModalProps {
14
17
  return alertModalProps;
@@ -1,18 +1,33 @@
1
1
  import { computed } from 'vue';
2
2
  import type { ExtractPropTypes } from 'vue';
3
- import type { ObjectWithoutEmpty } from '@noeldemartin/utils';
3
+ import type { ObjectWithout, Pretty, SubPartial } from '@noeldemartin/utils';
4
4
 
5
- import { requiredStringProp, stringProp } from '@/utils';
6
- import { translateWithDefault } from '@/lang';
5
+ import { Colors } from '@aerogel/core/components/constants';
6
+ import { booleanProp, enumProp, objectProp, requiredStringProp, stringProp } from '@aerogel/core/utils';
7
+ import { translateWithDefault } from '@aerogel/core/lang';
8
+ import type { AcceptRefs } from '@aerogel/core/utils';
9
+ import type { ConfirmCheckboxes } from '@aerogel/core/ui';
7
10
 
8
11
  export const confirmModalProps = {
9
12
  title: stringProp(),
10
13
  message: requiredStringProp(),
11
14
  acceptText: stringProp(),
15
+ acceptColor: enumProp(Colors, Colors.Primary),
12
16
  cancelText: stringProp(),
17
+ cancelColor: enumProp(Colors, Colors.Clear),
18
+ checkboxes: objectProp<ConfirmCheckboxes>(),
19
+ actions: objectProp<Record<string, () => unknown>>(),
20
+ required: booleanProp(false),
13
21
  };
14
22
 
15
- export type AGConfirmModalProps = ObjectWithoutEmpty<ExtractPropTypes<typeof confirmModalProps>>;
23
+ export type AGConfirmModalProps = Pretty<
24
+ AcceptRefs<
25
+ SubPartial<
26
+ ObjectWithout<ExtractPropTypes<typeof confirmModalProps>, null | undefined>,
27
+ 'acceptColor' | 'cancelColor'
28
+ >
29
+ >
30
+ >;
16
31
 
17
32
  export function useConfirmModalProps(): typeof confirmModalProps {
18
33
  return confirmModalProps;
@@ -1,12 +1,12 @@
1
1
  <template>
2
- <AGModal v-slot="{ close }: IAGModalDefaultSlotProps" :cancellable="false" :title="title">
3
- <AGMarkdown :text="message" />
2
+ <AGModal v-slot="{ close }: IModalDefaultSlotProps" :cancellable="false" :title="title">
3
+ <AGMarkdown :text="message" :actions="actions" />
4
4
 
5
5
  <div class="mt-2 flex flex-row-reverse gap-2">
6
- <AGButton @click="close(true)">
6
+ <AGButton :color="acceptColor" @click="close(true)">
7
7
  {{ renderedAcceptText }}
8
8
  </AGButton>
9
- <AGButton color="secondary" @click="close()">
9
+ <AGButton v-if="!required" :color="cancelColor" @click="close()">
10
10
  {{ renderedCancelText }}
11
11
  </AGButton>
12
12
  </div>
@@ -14,9 +14,10 @@
14
14
  </template>
15
15
 
16
16
  <script setup lang="ts">
17
+ import type { IModalDefaultSlotProps } from '@aerogel/core/components/contracts/Modal';
18
+
17
19
  import AGModal from './AGModal.vue';
18
20
  import { useConfirmModal, useConfirmModalProps } from './AGConfirmModal';
19
- import type { IAGModalDefaultSlotProps } from './AGModal';
20
21
 
21
22
  import AGButton from '../forms/AGButton.vue';
22
23
  import AGMarkdown from '../lib/AGMarkdown.vue';
@@ -1,10 +1,11 @@
1
1
  import { computed, ref } from 'vue';
2
2
  import type { Component, ExtractPropTypes } from 'vue';
3
- import type { ObjectWithoutEmpty } from '@noeldemartin/utils';
3
+ import type { ObjectWithout, Pretty } from '@noeldemartin/utils';
4
4
 
5
- import { requiredArrayProp } from '@/utils/vue';
6
- import { translateWithDefault } from '@/lang';
7
- import type { ErrorReport } from '@/errors';
5
+ import { requiredArrayProp } from '@aerogel/core/utils/vue';
6
+ import { translateWithDefault } from '@aerogel/core/lang';
7
+ import type { AcceptRefs } from '@aerogel/core/utils/vue';
8
+ import type { ErrorReport } from '@aerogel/core/errors';
8
9
 
9
10
  export interface IAGErrorReportModalButtonsDefaultSlotProps {
10
11
  id: string;
@@ -18,7 +19,9 @@ export const errorReportModalProps = {
18
19
  reports: requiredArrayProp<ErrorReport>(),
19
20
  };
20
21
 
21
- export type AGErrorReportModalProps = ObjectWithoutEmpty<ExtractPropTypes<typeof errorReportModalProps>>;
22
+ export type AGErrorReportModalProps = Pretty<
23
+ AcceptRefs<ObjectWithout<ExtractPropTypes<typeof errorReportModalProps>, null | undefined>>
24
+ >;
22
25
 
23
26
  export function useErrorReportModalProps(): typeof errorReportModalProps {
24
27
  return errorReportModalProps;
@@ -16,7 +16,7 @@
16
16
  :aria-label="previousReportText"
17
17
  @click="activeReportIndex--"
18
18
  >
19
- <IconCheveronLeft aria-hidden="true" class="h-4 w-4" />
19
+ <IconCheveronLeft aria-hidden="true" class="size-4" />
20
20
  </AGButton>
21
21
  <AGButton
22
22
  color="clear"
@@ -25,7 +25,7 @@
25
25
  :aria-label="nextReportText"
26
26
  @click="activeReportIndex++"
27
27
  >
28
- <IconCheveronRight aria-hidden="true" class="h-4 w-4" />
28
+ <IconCheveronRight aria-hidden="true" class="size-4" />
29
29
  </AGButton>
30
30
  </template>
31
31
  </div>
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="flex">
3
- <slot v-for="(button, i) of buttons" v-bind="(button as unknown as ComponentProps)" :key="i">
3
+ <slot v-for="(button, i) of buttons" v-bind="button as unknown as ComponentProps" :key="i">
4
4
  <AGButton
5
5
  color="clear"
6
6
  :url="button.url"
@@ -8,7 +8,7 @@
8
8
  :aria-label="$td(`errors.report_${button.id}`, button.description)"
9
9
  @click="button.handler"
10
10
  >
11
- <component :is="button.iconComponent" class="h-4 w-4" aria-hidden="true" />
11
+ <component :is="button.iconComponent" class="size-4" aria-hidden="true" />
12
12
  </AGButton>
13
13
  </slot>
14
14
  </div>
@@ -22,12 +22,12 @@ import IconGitHub from '~icons/mdi/github';
22
22
  import { computed } from 'vue';
23
23
  import { stringExcerpt, tap } from '@noeldemartin/utils';
24
24
 
25
- import App from '@/services/App';
26
- import UI from '@/ui/UI';
27
- import { requiredObjectProp } from '@/utils/vue';
28
- import { translateWithDefault } from '@/lang/utils';
29
- import type { ComponentProps } from '@/utils/vue';
30
- import type { ErrorReport } from '@/errors';
25
+ import App from '@aerogel/core/services/App';
26
+ import UI from '@aerogel/core/ui/UI';
27
+ import { requiredObjectProp } from '@aerogel/core/utils/vue';
28
+ import { translateWithDefault } from '@aerogel/core/lang/utils';
29
+ import type { ComponentProps } from '@aerogel/core/utils/vue';
30
+ import type { ErrorReport } from '@aerogel/core/errors';
31
31
 
32
32
  import AGButton from '../forms/AGButton.vue';
33
33
  import type { IAGErrorReportModalButtonsDefaultSlotProps } from './AGErrorReportModal';
@@ -94,8 +94,8 @@ const buttons = computed(() =>
94
94
  );
95
95
  },
96
96
  },
97
- ],
98
- (reportButtons: IAGErrorReportModalButtonsDefaultSlotProps[]) => {
97
+ ] as IAGErrorReportModalButtonsDefaultSlotProps[],
98
+ (reportButtons) => {
99
99
  if (!githubReportUrl.value) {
100
100
  return;
101
101
  }
@@ -5,8 +5,8 @@
5
5
  <script setup lang="ts">
6
6
  import { computed } from 'vue';
7
7
 
8
- import { numberProp, requiredObjectProp } from '@/utils/vue';
9
- import type { ErrorReport } from '@/errors';
8
+ import { numberProp, requiredObjectProp } from '@aerogel/core/utils/vue';
9
+ import type { ErrorReport } from '@aerogel/core/errors';
10
10
 
11
11
  import AGMarkdown from '../lib/AGMarkdown.vue';
12
12
 
@@ -1,15 +1,20 @@
1
1
  import { computed } from 'vue';
2
2
  import type { ExtractPropTypes } from 'vue';
3
- import type { ObjectWithoutEmpty } from '@noeldemartin/utils';
3
+ import type { ObjectWithout } from '@noeldemartin/utils';
4
4
 
5
- import { stringProp } from '@/utils';
6
- import { translateWithDefault } from '@/lang';
5
+ import { numberProp, stringProp } from '@aerogel/core/utils';
6
+ import { translateWithDefault } from '@aerogel/core/lang';
7
+ import type { AcceptRefs } from '@aerogel/core/utils';
7
8
 
8
9
  export const loadingModalProps = {
10
+ title: stringProp(),
9
11
  message: stringProp(),
12
+ progress: numberProp(),
10
13
  };
11
14
 
12
- export type AGLoadingModalProps = ObjectWithoutEmpty<ExtractPropTypes<typeof loadingModalProps>>;
15
+ export type AGLoadingModalProps = AcceptRefs<
16
+ ObjectWithout<ExtractPropTypes<typeof loadingModalProps>, null | undefined>
17
+ >;
13
18
 
14
19
  export function useLoadingModalProps(): typeof loadingModalProps {
15
20
  return loadingModalProps;
@@ -18,6 +23,7 @@ export function useLoadingModalProps(): typeof loadingModalProps {
18
23
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
19
24
  export function useLoadingModal(props: ExtractPropTypes<typeof loadingModalProps>) {
20
25
  const renderedMessage = computed(() => props.message ?? translateWithDefault('ui.loading', 'Loading...'));
26
+ const showProgress = computed(() => typeof props.progress === 'number');
21
27
 
22
- return { renderedMessage };
28
+ return { renderedMessage, showProgress };
23
29
  }
@@ -3,37 +3,38 @@
3
3
  ref="$modal"
4
4
  v-slot="{ close }: IAGHeadlessModalDefaultSlotProps"
5
5
  v-bind="props"
6
- class="relative z-50"
6
+ class="relative"
7
7
  >
8
- <div class="fixed inset-0 flex items-center justify-center p-8">
9
- <AGHeadlessModalPanel class="flex max-h-full max-w-full flex-col overflow-hidden bg-white p-4">
10
- <AGHeadlessModalTitle v-if="title" class="mb-2 text-lg font-semibold">
11
- <AGMarkdown :text="title" inline />
12
- </AGHeadlessModalTitle>
13
- <div class="flex max-h-full flex-col overflow-auto" v-bind="$attrs">
14
- <slot :close="close" />
15
- </div>
16
- </AGHeadlessModalPanel>
17
- </div>
8
+ <AGHeadlessModalContent
9
+ class="fixed top-1/2 left-1/2 z-50 max-w-lg -translate-x-1/2 -translate-y-1/2 bg-white p-4"
10
+ >
11
+ <AGHeadlessModalTitle v-if="title" class="mb-2 text-lg font-semibold">
12
+ <AGMarkdown :text="title" inline />
13
+ </AGHeadlessModalTitle>
14
+ <div class="flex max-h-full flex-col overflow-auto" v-bind="$attrs">
15
+ <slot :close="close" />
16
+ </div>
17
+ </AGHeadlessModalContent>
18
18
  </AGHeadlessModal>
19
19
  </template>
20
20
 
21
21
  <script setup lang="ts">
22
22
  import { ref } from 'vue';
23
23
 
24
- import { useModalExpose, useModalProps } from '@/components/headless/modals/AGHeadlessModal';
25
- import type { IAGHeadlessModal, IAGHeadlessModalDefaultSlotProps } from '@/components/headless/modals/AGHeadlessModal';
26
-
27
- import type { IAGModal } from './AGModal';
24
+ import { AGHeadlessModalTitle } from '@aerogel/core/components/headless/modals';
25
+ import { modalExpose, modalProps } from '@aerogel/core/components/headless/modals/AGHeadlessModal';
26
+ import type {
27
+ IAGHeadlessModal,
28
+ IAGHeadlessModalDefaultSlotProps,
29
+ } from '@aerogel/core/components/headless/modals/AGHeadlessModal';
28
30
 
29
31
  import AGHeadlessModal from '../headless/modals/AGHeadlessModal.vue';
30
- import AGHeadlessModalPanel from '../headless/modals/AGHeadlessModalPanel.vue';
31
- import AGHeadlessModalTitle from '../headless/modals/AGHeadlessModalTitle.vue';
32
+ import AGHeadlessModalContent from '../headless/modals/AGHeadlessModalContent.vue';
32
33
  import AGMarkdown from '../lib/AGMarkdown.vue';
33
34
 
34
- const props = defineProps(useModalProps());
35
+ const props = defineProps(modalProps());
35
36
  const $modal = ref<IAGHeadlessModal>();
36
37
 
37
38
  defineOptions({ inheritAttrs: false });
38
- defineExpose<IAGModal>(useModalExpose($modal));
39
+ defineExpose(modalExpose($modal));
39
40
  </script>
@@ -1,6 +1,6 @@
1
1
  import type { Ref } from 'vue';
2
2
 
3
- import type { Modal } from '@/ui/UI.state';
3
+ import type { Modal } from '@aerogel/core/ui/UI.state';
4
4
 
5
5
  export interface IAGModalContext {
6
6
  modal: Ref<Modal>;
@@ -1,18 +1,28 @@
1
1
  <template>
2
- <component :is="modal.component" v-bind="modal.properties" />
2
+ <component :is="modal.component" v-bind="modalProperties" />
3
3
  </template>
4
4
 
5
5
  <script setup lang="ts">
6
- import { provide, toRef } from 'vue';
6
+ import { computed, provide, toRef, unref } from 'vue';
7
7
 
8
- import { requiredNumberProp, requiredObjectProp } from '@/utils/vue';
9
- import type { Modal } from '@/ui/UI.state';
8
+ import { numberProp, requiredObjectProp } from '@aerogel/core/utils/vue';
9
+ import type { Modal } from '@aerogel/core/ui/UI.state';
10
10
 
11
11
  import type { IAGModalContext } from './AGModalContext';
12
12
 
13
13
  const props = defineProps({
14
14
  modal: requiredObjectProp<Modal>(),
15
- childIndex: requiredNumberProp(),
15
+ childIndex: numberProp(0),
16
+ });
17
+
18
+ const modalProperties = computed(() => {
19
+ const properties = {} as typeof props.modal.properties;
20
+
21
+ for (const property in props.modal.properties) {
22
+ properties[property] = unref(props.modal.properties[property]);
23
+ }
24
+
25
+ return properties;
16
26
  });
17
27
 
18
28
  provide<IAGModalContext>('modal', {
@@ -5,5 +5,5 @@
5
5
  </template>
6
6
 
7
7
  <script setup lang="ts">
8
- import AGHeadlessModalTitle from '../headless/modals/AGHeadlessModalTitle.vue';
8
+ import { AGHeadlessModalTitle } from '@aerogel/core/components/headless/modals';
9
9
  </script>
@@ -1,9 +1,11 @@
1
1
  import { computed } from 'vue';
2
2
  import type { ExtractPropTypes } from 'vue';
3
- import type { ObjectWithoutEmpty } from '@noeldemartin/utils';
3
+ import type { ObjectWithout, Pretty, SubPartial } from '@noeldemartin/utils';
4
4
 
5
- import { requiredStringProp, stringProp } from '@/utils';
6
- import { translateWithDefault } from '@/lang';
5
+ import { Colors } from '@aerogel/core/components/constants';
6
+ import { enumProp, requiredStringProp, stringProp } from '@aerogel/core/utils';
7
+ import { translateWithDefault } from '@aerogel/core/lang';
8
+ import type { AcceptRefs } from '@aerogel/core/utils';
7
9
 
8
10
  export const promptModalProps = {
9
11
  title: stringProp(),
@@ -12,10 +14,19 @@ export const promptModalProps = {
12
14
  defaultValue: stringProp(),
13
15
  placeholder: stringProp(),
14
16
  acceptText: stringProp(),
17
+ acceptColor: enumProp(Colors, Colors.Primary),
15
18
  cancelText: stringProp(),
19
+ cancelColor: enumProp(Colors, Colors.Clear),
16
20
  };
17
21
 
18
- export type AGPromptModalProps = ObjectWithoutEmpty<ExtractPropTypes<typeof promptModalProps>>;
22
+ export type AGPromptModalProps = Pretty<
23
+ AcceptRefs<
24
+ SubPartial<
25
+ ObjectWithout<ExtractPropTypes<typeof promptModalProps>, null | undefined>,
26
+ 'acceptColor' | 'cancelColor'
27
+ >
28
+ >
29
+ >;
19
30
 
20
31
  export function usePromptModalProps(): typeof promptModalProps {
21
32
  return promptModalProps;
@@ -1,15 +1,15 @@
1
1
  <template>
2
- <AGModal v-slot="{ close }: IAGModalDefaultSlotProps" :cancellable="false" :title="title">
2
+ <AGModal v-slot="{ close }: IModalDefaultSlotProps" :cancellable="false" :title="title">
3
3
  <AGMarkdown :text="message" />
4
4
 
5
5
  <AGForm :form="form" @submit="close(form.draft)">
6
6
  <AGInput name="draft" :placeholder="placeholder" :label="label" />
7
7
 
8
8
  <div class="mt-2 flex flex-row-reverse gap-2">
9
- <AGButton submit>
9
+ <AGButton :color="acceptColor" submit>
10
10
  {{ renderedAcceptText }}
11
11
  </AGButton>
12
- <AGButton color="secondary" @click="close()">
12
+ <AGButton :color="cancelColor" @click="close()">
13
13
  {{ renderedCancelText }}
14
14
  </AGButton>
15
15
  </div>
@@ -18,9 +18,10 @@
18
18
  </template>
19
19
 
20
20
  <script setup lang="ts">
21
+ import type { IModalDefaultSlotProps } from '@aerogel/core/components/contracts/Modal';
22
+
21
23
  import AGModal from './AGModal.vue';
22
24
  import { usePromptModal, usePromptModalProps } from './AGPromptModal';
23
- import type { IAGModalDefaultSlotProps } from './AGModal';
24
25
 
25
26
  import AGButton from '../forms/AGButton.vue';
26
27
  import AGForm from '../forms/AGForm.vue';
@@ -2,7 +2,6 @@ export * from './AGAlertModal';
2
2
  export * from './AGConfirmModal';
3
3
  export * from './AGErrorReportModal';
4
4
  export * from './AGLoadingModal';
5
- export * from './AGModal';
6
5
  export * from './AGModalContext';
7
6
  export * from './AGPromptModal';
8
7
 
@@ -15,8 +15,8 @@
15
15
  <script setup lang="ts">
16
16
  import { computed } from 'vue';
17
17
 
18
- import { Colors } from '@/components/constants';
19
- import { useSnackbar, useSnackbarProps } from '@/components/headless/snackbars';
18
+ import { Colors } from '@aerogel/core/components/constants';
19
+ import { useSnackbar, useSnackbarProps } from '@aerogel/core/components/headless/snackbars';
20
20
 
21
21
  import AGButton from '../forms/AGButton.vue';
22
22
  import AGHeadlessSnackbar from '../headless/snackbars/AGHeadlessSnackbar.vue';
@@ -1,10 +1,63 @@
1
- export function extractComponentProps<T extends Record<string, unknown>>(
2
- values: Record<string, unknown>,
3
- definitions: Record<string, unknown>,
4
- ): T {
5
- return Object.keys(definitions).reduce((extracted, prop) => {
6
- extracted[prop] = values[prop];
7
-
8
- return extracted;
9
- }, {} as Record<string, unknown>) as T;
1
+ import { isObject } from '@noeldemartin/utils';
2
+ import { customRef } from 'vue';
3
+ import type { ExtractPropTypes, PropType, Ref, UnwrapNestedRefs } from 'vue';
4
+
5
+ import type { HasElement } from '@aerogel/core/components/contracts/shared';
6
+
7
+ export type ComponentPropDefinitions<T> = {
8
+ [K in keyof T]: {
9
+ type?: PropType<T[K]>;
10
+ default: T[K] | (() => T[K]) | null;
11
+ };
12
+ };
13
+
14
+ export type PickComponentProps<TValues, TDefinitions> = {
15
+ [K in keyof TValues]: K extends keyof TDefinitions ? TValues[K] : never;
16
+ };
17
+
18
+ export function elementRef(): Ref<HTMLElement | undefined> {
19
+ return customRef((track, trigger) => {
20
+ let value: HTMLElement | undefined = undefined;
21
+
22
+ return {
23
+ get() {
24
+ track();
25
+
26
+ return value;
27
+ },
28
+ set(newValue) {
29
+ value = getElement(newValue);
30
+
31
+ trigger();
32
+ },
33
+ };
34
+ });
35
+ }
36
+
37
+ export function extractComponentProps<TDefinitions extends {}, TValues extends ExtractPropTypes<TDefinitions>>(
38
+ values: TValues,
39
+ definitions: TDefinitions,
40
+ ): PickComponentProps<TValues, TDefinitions> {
41
+ return Object.keys(definitions).reduce(
42
+ (extracted, prop) => {
43
+ extracted[prop] = values[prop as keyof TValues];
44
+
45
+ return extracted;
46
+ },
47
+ {} as Record<string, unknown>,
48
+ ) as PickComponentProps<TValues, TDefinitions>;
49
+ }
50
+
51
+ export function getElement(value: unknown): HTMLElement | undefined {
52
+ if (value instanceof HTMLElement) {
53
+ return value;
54
+ }
55
+
56
+ if (hasElement(value)) {
57
+ return value.$el;
58
+ }
59
+ }
60
+
61
+ export function hasElement(value: unknown): value is UnwrapNestedRefs<HasElement> {
62
+ return isObject(value) && '$el' in value;
10
63
  }
@@ -1,15 +1,15 @@
1
1
  import type { Directive } from 'vue';
2
2
 
3
- import { definePlugin } from '@/plugins';
3
+ import { definePlugin } from '@aerogel/core/plugins';
4
4
 
5
- import initialFocus from './initial-focus';
6
5
  import measure from './measure';
7
6
 
8
7
  const builtInDirectives: Record<string, Directive> = {
9
- 'initial-focus': initialFocus,
10
- 'measure': measure,
8
+ measure: measure,
11
9
  };
12
10
 
11
+ export * from './measure';
12
+
13
13
  export default definePlugin({
14
14
  install(app, options) {
15
15
  const directives = {
@@ -23,8 +23,14 @@ export default definePlugin({
23
23
  },
24
24
  });
25
25
 
26
- declare module '@/bootstrap/options' {
26
+ declare module '@aerogel/core/bootstrap/options' {
27
27
  export interface AerogelOptions {
28
28
  directives?: Record<string, Directive>;
29
29
  }
30
30
  }
31
+
32
+ declare module 'vue' {
33
+ interface ComponentCustomDirectives {
34
+ measure: Directive<string, string>;
35
+ }
36
+ }
@@ -1,12 +1,40 @@
1
- import { defineDirective } from '@/utils/vue';
1
+ import { defineDirective } from '@aerogel/core/utils/vue';
2
+ import { tap } from '@noeldemartin/utils';
3
+
4
+ const resizeObservers: WeakMap<HTMLElement, ResizeObserver> = new WeakMap();
5
+
6
+ export interface ElementSize {
7
+ width: number;
8
+ height: number;
9
+ }
10
+
11
+ export type MeasureDirectiveListener = (size: ElementSize) => unknown;
2
12
 
3
13
  export default defineDirective({
4
- mounted(element: HTMLElement, { value }: { value?: () => unknown }) {
5
- const sizes = element.getBoundingClientRect();
14
+ mounted(element: HTMLElement, { value }) {
15
+ // TODO replace with argument when typed properly
16
+ const modifiers = { css: true, watch: true };
17
+
18
+ const listener = typeof value === 'function' ? (value as MeasureDirectiveListener) : null;
19
+ const update = () => {
20
+ const sizes = element.getBoundingClientRect();
6
21
 
7
- element.style.setProperty('--width', `${sizes.width}px`);
8
- element.style.setProperty('--height', `${sizes.height}px`);
22
+ if (modifiers.css) {
23
+ element.style.setProperty('--width', `${sizes.width}px`);
24
+ element.style.setProperty('--height', `${sizes.height}px`);
25
+ }
9
26
 
10
- value?.();
27
+ listener?.({ width: sizes.width, height: sizes.height });
28
+ };
29
+
30
+ if (modifiers.watch) {
31
+ resizeObservers.set(element, tap(new ResizeObserver(update)).observe(element));
32
+ }
33
+
34
+ update();
35
+ },
36
+ unmounted(element) {
37
+ resizeObservers.get(element)?.unobserve(element);
38
+ resizeObservers.delete(element);
11
39
  },
12
40
  });
@@ -1,6 +1,6 @@
1
1
  import type { JSError } from '@noeldemartin/utils';
2
2
 
3
- import { defineServiceState } from '@/services';
3
+ import { defineServiceState } from '@aerogel/core/services';
4
4
 
5
5
  export type ErrorSource = string | Error | JSError | unknown;
6
6
 
@@ -1,16 +1,16 @@
1
- import { JSError, facade, isObject, objectWithoutEmpty, toString } from '@noeldemartin/utils';
1
+ import { JSError, facade, isDevelopment, isObject, isTesting, objectWithoutEmpty, toString } from '@noeldemartin/utils';
2
2
 
3
- import App from '@/services/App';
4
- import ServiceBootError from '@/errors/ServiceBootError';
5
- import UI, { UIComponents } from '@/ui/UI';
6
- import { translateWithDefault } from '@/lang/utils';
3
+ import App from '@aerogel/core/services/App';
4
+ import ServiceBootError from '@aerogel/core/errors/ServiceBootError';
5
+ import UI, { UIComponents } from '@aerogel/core/ui/UI';
6
+ import { translateWithDefault } from '@aerogel/core/lang/utils';
7
7
 
8
8
  import Service from './Errors.state';
9
- import { Colors } from '@/components/constants';
10
- import { Events } from '@/services';
11
- import type { AGErrorReportModalProps } from '@/components/modals/AGErrorReportModal';
9
+ import { Colors } from '@aerogel/core/components/constants';
10
+ import { Events } from '@aerogel/core/services';
11
+ import type { AGErrorReportModalProps } from '@aerogel/core/components/modals/AGErrorReportModal';
12
12
  import type { ErrorReport, ErrorReportLog, ErrorSource } from './Errors.state';
13
- import type { ModalComponent } from '@/ui/UI.state';
13
+ import type { ModalComponent } from '@aerogel/core/ui/UI.state';
14
14
 
15
15
  export class ErrorsService extends Service {
16
16
 
@@ -42,11 +42,11 @@ export class ErrorsService extends Service {
42
42
  public async report(error: ErrorSource, message?: string): Promise<void> {
43
43
  await Events.emit('error', { error, message });
44
44
 
45
- if (App.testing) {
45
+ if (isTesting('unit')) {
46
46
  throw error;
47
47
  }
48
48
 
49
- if (App.development) {
49
+ if (isDevelopment()) {
50
50
  this.logError(error);
51
51
  }
52
52
 
@@ -178,7 +178,7 @@ export class ErrorsService extends Service {
178
178
 
179
179
  export default facade(ErrorsService);
180
180
 
181
- declare module '@/services/Events' {
181
+ declare module '@aerogel/core/services/Events' {
182
182
  export interface EventsPayload {
183
183
  error: { error: ErrorSource; message?: string };
184
184
  }