@aerogel/core 0.0.0-next.f86b4b09f066c4aef21796a37dbc8417b7dce3cd → 0.0.0-next.f8c757d83e1e0d001a2836fa45aba318ec17b9b9

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 (196) hide show
  1. package/dist/aerogel-core.d.ts +1973 -1688
  2. package/dist/aerogel-core.js +3234 -0
  3. package/dist/aerogel-core.js.map +1 -0
  4. package/package.json +30 -37
  5. package/src/bootstrap/bootstrap.test.ts +4 -7
  6. package/src/bootstrap/index.ts +14 -15
  7. package/src/bootstrap/options.ts +1 -1
  8. package/src/components/{AGAppLayout.vue → AppLayout.vue} +4 -4
  9. package/src/components/{AGAppModals.vue → AppModals.vue} +3 -4
  10. package/src/components/{AGAppOverlays.vue → AppOverlays.vue} +5 -10
  11. package/src/components/AppToasts.vue +16 -0
  12. package/src/components/contracts/AlertModal.ts +4 -0
  13. package/src/components/contracts/Button.ts +16 -0
  14. package/src/components/contracts/ConfirmModal.ts +41 -0
  15. package/src/components/contracts/DropdownMenu.ts +18 -0
  16. package/src/components/contracts/ErrorReportModal.ts +29 -0
  17. package/src/components/contracts/Input.ts +26 -0
  18. package/src/components/contracts/LoadingModal.ts +18 -0
  19. package/src/components/contracts/Modal.ts +13 -0
  20. package/src/components/contracts/PromptModal.ts +30 -0
  21. package/src/components/contracts/Select.ts +44 -0
  22. package/src/components/contracts/Toast.ts +13 -0
  23. package/src/components/contracts/index.ts +9 -0
  24. package/src/components/contracts/shared.ts +9 -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/{forms/AGHeadlessInputInput.vue → HeadlessInputInput.vue} +16 -25
  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/{modals/AGHeadlessModal.vue → HeadlessModal.vue} +18 -18
  33. package/src/components/headless/HeadlessModalContent.vue +24 -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 +113 -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 +37 -0
  42. package/src/components/headless/HeadlessSelectTrigger.vue +22 -0
  43. package/src/components/headless/HeadlessSelectValue.vue +18 -0
  44. package/src/components/headless/HeadlessToast.vue +18 -0
  45. package/src/components/headless/HeadlessToastAction.vue +13 -0
  46. package/src/components/headless/index.ts +19 -3
  47. package/src/components/index.ts +6 -11
  48. package/src/components/ui/AdvancedOptions.vue +18 -0
  49. package/src/components/ui/AlertModal.vue +13 -0
  50. package/src/components/ui/Button.vue +98 -0
  51. package/src/components/ui/Checkbox.vue +56 -0
  52. package/src/components/ui/ConfirmModal.vue +42 -0
  53. package/src/components/ui/DropdownMenu.vue +27 -0
  54. package/src/components/ui/DropdownMenuOption.vue +14 -0
  55. package/src/components/ui/DropdownMenuOptions.vue +27 -0
  56. package/src/components/ui/EditableContent.vue +82 -0
  57. package/src/components/ui/ErrorMessage.vue +15 -0
  58. package/src/components/ui/ErrorReportModal.vue +62 -0
  59. package/src/components/{modals/AGErrorReportModalButtons.vue → ui/ErrorReportModalButtons.vue} +34 -27
  60. package/src/components/ui/ErrorReportModalTitle.vue +24 -0
  61. package/src/components/{forms/AGForm.vue → ui/Form.vue} +4 -5
  62. package/src/components/ui/Input.vue +56 -0
  63. package/src/components/ui/Link.vue +12 -0
  64. package/src/components/ui/LoadingModal.vue +32 -0
  65. package/src/components/ui/Markdown.vue +69 -0
  66. package/src/components/ui/Modal.vue +75 -0
  67. package/src/components/ui/ModalContext.vue +30 -0
  68. package/src/components/ui/ProgressBar.vue +50 -0
  69. package/src/components/ui/PromptModal.vue +35 -0
  70. package/src/components/ui/Select.vue +25 -0
  71. package/src/components/ui/SelectLabel.vue +17 -0
  72. package/src/components/ui/SelectOption.vue +29 -0
  73. package/src/components/ui/SelectOptions.vue +30 -0
  74. package/src/components/ui/SelectTrigger.vue +29 -0
  75. package/src/components/ui/SettingsModal.vue +15 -0
  76. package/src/components/{lib/AGStartupCrash.vue → ui/StartupCrash.vue} +8 -8
  77. package/src/components/ui/Toast.vue +42 -0
  78. package/src/components/ui/index.ts +30 -0
  79. package/src/directives/index.ts +9 -5
  80. package/src/directives/measure.ts +1 -1
  81. package/src/errors/Errors.state.ts +1 -1
  82. package/src/errors/Errors.ts +17 -18
  83. package/src/errors/JobCancelledError.ts +3 -0
  84. package/src/errors/index.ts +9 -6
  85. package/src/errors/utils.ts +1 -1
  86. package/src/forms/{Form.test.ts → FormController.test.ts} +5 -4
  87. package/src/forms/{Form.ts → FormController.ts} +22 -19
  88. package/src/forms/composition.ts +4 -4
  89. package/src/forms/index.ts +2 -2
  90. package/src/forms/utils.ts +2 -2
  91. package/src/index.css +46 -0
  92. package/src/jobs/Job.ts +144 -2
  93. package/src/jobs/index.ts +4 -1
  94. package/src/jobs/listeners.ts +3 -0
  95. package/src/jobs/status.ts +4 -0
  96. package/src/lang/DefaultLangProvider.ts +7 -4
  97. package/src/lang/Lang.state.ts +1 -1
  98. package/src/lang/Lang.ts +1 -1
  99. package/src/lang/index.ts +11 -6
  100. package/src/lang/settings/Language.vue +48 -0
  101. package/src/lang/settings/index.ts +10 -0
  102. package/src/plugins/Plugin.ts +1 -1
  103. package/src/plugins/index.ts +10 -7
  104. package/src/services/App.state.ts +21 -5
  105. package/src/services/App.ts +7 -4
  106. package/src/services/Cache.ts +1 -1
  107. package/src/services/Events.ts +15 -5
  108. package/src/services/Service.ts +116 -53
  109. package/src/services/Storage.ts +20 -0
  110. package/src/services/index.ts +14 -5
  111. package/src/services/utils.ts +18 -0
  112. package/src/testing/index.ts +4 -3
  113. package/src/testing/setup.ts +5 -13
  114. package/src/ui/UI.state.ts +12 -7
  115. package/src/ui/UI.ts +126 -84
  116. package/src/ui/index.ts +18 -18
  117. package/src/utils/classes.ts +49 -0
  118. package/src/utils/composition/events.ts +2 -2
  119. package/src/utils/composition/forms.ts +14 -4
  120. package/src/utils/composition/persistent.test.ts +33 -0
  121. package/src/utils/composition/persistent.ts +11 -0
  122. package/src/utils/composition/state.ts +11 -2
  123. package/src/utils/index.ts +4 -1
  124. package/src/utils/markdown.test.ts +50 -0
  125. package/src/utils/markdown.ts +19 -6
  126. package/src/utils/vue.ts +28 -136
  127. package/dist/aerogel-core.cjs.js +0 -2
  128. package/dist/aerogel-core.cjs.js.map +0 -1
  129. package/dist/aerogel-core.esm.js +0 -2
  130. package/dist/aerogel-core.esm.js.map +0 -1
  131. package/histoire.config.ts +0 -7
  132. package/noeldemartin.config.js +0 -5
  133. package/postcss.config.js +0 -6
  134. package/src/assets/histoire.css +0 -3
  135. package/src/components/AGAppSnackbars.vue +0 -13
  136. package/src/components/composition.ts +0 -23
  137. package/src/components/constants.ts +0 -8
  138. package/src/components/forms/AGButton.vue +0 -44
  139. package/src/components/forms/AGCheckbox.vue +0 -41
  140. package/src/components/forms/AGInput.vue +0 -40
  141. package/src/components/forms/AGSelect.story.vue +0 -46
  142. package/src/components/forms/AGSelect.vue +0 -60
  143. package/src/components/forms/index.ts +0 -5
  144. package/src/components/headless/forms/AGHeadlessButton.ts +0 -3
  145. package/src/components/headless/forms/AGHeadlessButton.vue +0 -62
  146. package/src/components/headless/forms/AGHeadlessInput.ts +0 -34
  147. package/src/components/headless/forms/AGHeadlessInput.vue +0 -70
  148. package/src/components/headless/forms/AGHeadlessSelect.ts +0 -42
  149. package/src/components/headless/forms/AGHeadlessSelect.vue +0 -77
  150. package/src/components/headless/forms/AGHeadlessSelectButton.vue +0 -24
  151. package/src/components/headless/forms/AGHeadlessSelectLabel.vue +0 -24
  152. package/src/components/headless/forms/AGHeadlessSelectOption.ts +0 -4
  153. package/src/components/headless/forms/AGHeadlessSelectOption.vue +0 -39
  154. package/src/components/headless/forms/AGHeadlessSelectOptions.ts +0 -3
  155. package/src/components/headless/forms/composition.ts +0 -10
  156. package/src/components/headless/forms/index.ts +0 -18
  157. package/src/components/headless/modals/AGHeadlessModal.ts +0 -34
  158. package/src/components/headless/modals/AGHeadlessModalPanel.vue +0 -28
  159. package/src/components/headless/modals/AGHeadlessModalTitle.vue +0 -13
  160. package/src/components/headless/modals/index.ts +0 -4
  161. package/src/components/headless/snackbars/AGHeadlessSnackbar.vue +0 -10
  162. package/src/components/headless/snackbars/index.ts +0 -40
  163. package/src/components/interfaces.ts +0 -24
  164. package/src/components/lib/AGErrorMessage.vue +0 -16
  165. package/src/components/lib/AGLink.vue +0 -9
  166. package/src/components/lib/AGMarkdown.vue +0 -41
  167. package/src/components/lib/AGMeasured.vue +0 -16
  168. package/src/components/lib/index.ts +0 -5
  169. package/src/components/modals/AGAlertModal.ts +0 -15
  170. package/src/components/modals/AGAlertModal.vue +0 -14
  171. package/src/components/modals/AGConfirmModal.ts +0 -35
  172. package/src/components/modals/AGConfirmModal.vue +0 -26
  173. package/src/components/modals/AGErrorReportModal.ts +0 -46
  174. package/src/components/modals/AGErrorReportModal.vue +0 -54
  175. package/src/components/modals/AGErrorReportModalTitle.vue +0 -25
  176. package/src/components/modals/AGLoadingModal.ts +0 -23
  177. package/src/components/modals/AGLoadingModal.vue +0 -15
  178. package/src/components/modals/AGModal.ts +0 -10
  179. package/src/components/modals/AGModal.vue +0 -39
  180. package/src/components/modals/AGModalContext.ts +0 -8
  181. package/src/components/modals/AGModalContext.vue +0 -22
  182. package/src/components/modals/AGModalTitle.vue +0 -9
  183. package/src/components/modals/AGPromptModal.ts +0 -36
  184. package/src/components/modals/AGPromptModal.vue +0 -34
  185. package/src/components/modals/index.ts +0 -17
  186. package/src/components/snackbars/AGSnackbar.vue +0 -36
  187. package/src/components/snackbars/index.ts +0 -3
  188. package/src/components/utils.ts +0 -10
  189. package/src/directives/initial-focus.ts +0 -11
  190. package/src/main.histoire.ts +0 -1
  191. package/src/utils/tailwindcss.test.ts +0 -26
  192. package/src/utils/tailwindcss.ts +0 -7
  193. package/tailwind.config.js +0 -4
  194. package/tsconfig.json +0 -11
  195. package/vite.config.ts +0 -17
  196. /package/src/{main.ts → index.ts} +0 -0
@@ -1,10 +1,10 @@
1
1
  <template>
2
2
  <slot :id="`${input.id}-description`">
3
- <AGMarkdown
3
+ <Markdown
4
4
  v-if="show"
5
5
  v-bind="$attrs"
6
6
  :id="`${input.id}-description`"
7
- :text="text"
7
+ :text
8
8
  />
9
9
  </slot>
10
10
  </template>
@@ -12,16 +12,15 @@
12
12
  <script setup lang="ts">
13
13
  import { computed } from 'vue';
14
14
 
15
- import { injectReactiveOrFail } from '@/utils/vue';
16
-
17
- import AGMarkdown from '../../lib/AGMarkdown.vue';
18
- import type { IAGHeadlessInput } from './AGHeadlessInput';
15
+ import Markdown from '@aerogel/core/components/ui/Markdown.vue';
16
+ import { injectReactiveOrFail } from '@aerogel/core/utils/vue';
17
+ import type { InputExpose } from '@aerogel/core/components/contracts/Input';
19
18
 
20
19
  defineOptions({ inheritAttrs: false });
21
20
 
22
- const input = injectReactiveOrFail<IAGHeadlessInput>(
21
+ const input = injectReactiveOrFail<InputExpose>(
23
22
  'input',
24
- '<AGHeadlessInputDescription> must be a child of a <AGHeadlessInput>',
23
+ '<HeadlessInputDescription> must be a child of a <HeadlessInput>',
25
24
  );
26
25
  const text = computed(() => (typeof input.description === 'string' ? input.description : ''));
27
26
  const show = computed(() => !!input.description);
@@ -7,15 +7,11 @@
7
7
  <script setup lang="ts">
8
8
  import { computed } from 'vue';
9
9
 
10
- import { injectReactiveOrFail } from '@/utils/vue';
11
- import { translateWithDefault } from '@/lang/utils';
10
+ import { injectReactiveOrFail } from '@aerogel/core/utils/vue';
11
+ import { translateWithDefault } from '@aerogel/core/lang/utils';
12
+ import type { InputExpose } from '@aerogel/core/components/contracts/Input';
12
13
 
13
- import type { IAGHeadlessInput } from './AGHeadlessInput';
14
-
15
- const input = injectReactiveOrFail<IAGHeadlessInput>(
16
- 'input',
17
- '<AGHeadlessInputError> must be a child of a <AGHeadlessInput>',
18
- );
14
+ const input = injectReactiveOrFail<InputExpose>('input', '<HeadlessInputError> must be a child of a <HeadlessInput>');
19
15
  const errorMessage = computed(() => {
20
16
  if (!input.errors) {
21
17
  return null;
@@ -1,42 +1,34 @@
1
1
  <template>
2
2
  <input
3
3
  :id="input.id"
4
- ref="$input"
5
- :name="name"
6
- :type="type"
4
+ ref="$inputRef"
5
+ :name
6
+ :type
7
+ :checked
7
8
  :required="input.required ?? undefined"
8
9
  :aria-invalid="input.errors ? 'true' : 'false'"
9
10
  :aria-describedby="
10
11
  input.errors ? `${input.id}-error` : input.description ? `${input.id}-description` : undefined
11
12
  "
12
- :checked="checked"
13
13
  @input="update"
14
14
  >
15
15
  </template>
16
16
 
17
17
  <script setup lang="ts">
18
- import { computed, ref, watchEffect } from 'vue';
18
+ import { computed, useTemplateRef, watchEffect } from 'vue';
19
19
 
20
- import { injectReactiveOrFail, stringProp } from '@/utils/vue';
21
- import type { __SetsElement } from '@/components/interfaces';
22
- import type { FormFieldValue } from '@/forms/Form';
23
- import type { IAGHeadlessInput } from '@/components/headless/forms/AGHeadlessInput';
20
+ import { injectReactiveOrFail } from '@aerogel/core/utils/vue';
21
+ import { onFormFocus } from '@aerogel/core/utils/composition/forms';
22
+ import type { FormFieldValue } from '@aerogel/core/forms/FormController';
23
+ import type { InputExpose } from '@aerogel/core/components/contracts/Input';
24
24
 
25
- import { onFormFocus } from './composition';
26
-
27
- const props = defineProps({
28
- type: stringProp('text'),
29
- });
30
-
31
- const $input = ref<HTMLInputElement>();
32
- const input = injectReactiveOrFail<IAGHeadlessInput>(
33
- 'input',
34
- '<AGHeadlessInputInput> must be a child of a <AGHeadlessInput>',
35
- );
25
+ const { type = 'text' } = defineProps<{ type?: string }>();
26
+ const $input = useTemplateRef('$inputRef');
27
+ const input = injectReactiveOrFail<InputExpose>('input', '<HeadlessInputInput> must be a child of a <HeadlessInput>');
36
28
  const name = computed(() => input.name ?? undefined);
37
29
  const value = computed(() => input.value);
38
30
  const checked = computed(() => {
39
- if (props.type !== 'checkbox') {
31
+ if (type !== 'checkbox') {
40
32
  return;
41
33
  }
42
34
 
@@ -56,7 +48,7 @@ function getValue(): FormFieldValue | null {
56
48
  return null;
57
49
  }
58
50
 
59
- switch (props.type) {
51
+ switch (type) {
60
52
  case 'checkbox':
61
53
  return $input.value.checked;
62
54
  case 'date':
@@ -67,18 +59,17 @@ function getValue(): FormFieldValue | null {
67
59
  }
68
60
 
69
61
  onFormFocus(input, () => $input.value?.focus());
70
- watchEffect(() => (input as unknown as __SetsElement).__setElement($input.value));
71
62
  watchEffect(() => {
72
63
  if (!$input.value) {
73
64
  return;
74
65
  }
75
66
 
76
- if (props.type === 'date') {
67
+ if (type === 'date') {
77
68
  $input.value.valueAsDate = value.value as Date;
78
69
 
79
70
  return;
80
71
  }
81
72
 
82
- $input.value.value = value.value as string;
73
+ $input.value.value = (value.value as string) ?? null;
83
74
  });
84
75
  </script>
@@ -9,14 +9,10 @@
9
9
  <script setup lang="ts">
10
10
  import { computed, useSlots } from 'vue';
11
11
 
12
- import { injectReactiveOrFail } from '@/utils/vue';
12
+ import { injectReactiveOrFail } from '@aerogel/core/utils/vue';
13
+ import type { InputExpose } from '@aerogel/core/components/contracts/Input';
13
14
 
14
- import type { IAGHeadlessInput } from './AGHeadlessInput';
15
-
16
- const input = injectReactiveOrFail<IAGHeadlessInput>(
17
- 'input',
18
- '<AGHeadlessInputLabel> must be a child of a <AGHeadlessInput>',
19
- );
15
+ const input = injectReactiveOrFail<InputExpose>('input', '<HeadlessInputLabel> must be a child of a <HeadlessInput>');
20
16
  const slots = useSlots();
21
17
  const show = computed(() => !!(input.label || slots.default));
22
18
  </script>
@@ -1,10 +1,10 @@
1
1
  <template>
2
2
  <textarea
3
3
  :id="input.id"
4
- ref="$textArea"
5
- :name="name"
4
+ ref="$textAreaRef"
5
+ :name
6
+ :value
6
7
  :required="input.required ?? undefined"
7
- :value="value"
8
8
  :aria-invalid="input.errors ? 'true' : 'false'"
9
9
  :aria-describedby="
10
10
  input.errors ? `${input.id}-error` : input.description ? `${input.id}-description` : undefined
@@ -14,18 +14,16 @@
14
14
  </template>
15
15
 
16
16
  <script setup lang="ts">
17
- import { computed, ref, watchEffect } from 'vue';
17
+ import { computed, useTemplateRef } from 'vue';
18
18
 
19
- import { injectReactiveOrFail } from '@/utils/vue';
20
- import type { IAGHeadlessInput } from '@/components/headless/forms/AGHeadlessInput';
21
- import type { __SetsElement } from '@/components/interfaces';
19
+ import { onFormFocus } from '@aerogel/core/utils/composition/forms';
20
+ import { injectReactiveOrFail } from '@aerogel/core/utils/vue';
21
+ import type { InputExpose } from '@aerogel/core/components/contracts/Input';
22
22
 
23
- import { onFormFocus } from './composition';
24
-
25
- const $textArea = ref<HTMLTextAreaElement>();
26
- const input = injectReactiveOrFail<IAGHeadlessInput>(
23
+ const $textArea = useTemplateRef('$textAreaRef');
24
+ const input = injectReactiveOrFail<InputExpose>(
27
25
  'input',
28
- '<AGHeadlessInputTextArea> must be a child of a <AGHeadlessInput>',
26
+ '<HeadlessInputTextArea> must be a child of a <HeadlessInput>',
29
27
  );
30
28
  const name = computed(() => input.name ?? undefined);
31
29
  const value = computed(() => input.value as string);
@@ -38,6 +36,5 @@ function update() {
38
36
  input.update($textArea.value.value);
39
37
  }
40
38
 
41
- watchEffect(() => (input as unknown as __SetsElement).__setElement($textArea.value));
42
39
  onFormFocus(input, () => $textArea.value?.focus());
43
40
  </script>
@@ -1,29 +1,30 @@
1
1
  <template>
2
- <Dialog ref="$root" :open="true" @close="cancellable && close()">
3
- <slot :close="close" />
4
- </Dialog>
2
+ <DialogRoot ref="$rootRef" open @update:open="persistent || close()">
3
+ <DialogPortal>
4
+ <slot :close="close" />
5
+ </DialogPortal>
6
+ </DialogRoot>
5
7
  </template>
6
8
 
7
9
  <script setup lang="ts">
8
- import { ref, toRef } from 'vue';
9
- import { Dialog } from '@headlessui/vue';
10
- import type { VNode } from 'vue';
10
+ import { ref, useTemplateRef } from 'vue';
11
+ import { DialogPortal, DialogRoot } from 'reka-ui';
11
12
 
12
- import Events from '@/services/Events';
13
- import { useEvent } from '@/utils/composition/events';
14
- import { injectReactiveOrFail } from '@/utils/vue';
15
- import type { IAGModalContext } from '@/components/modals/AGModalContext';
13
+ import Events from '@aerogel/core/services/Events';
14
+ import { useEvent } from '@aerogel/core/utils/composition/events';
15
+ import { injectReactiveOrFail } from '@aerogel/core/utils/vue';
16
+ import type { UIModalContext } from '@aerogel/core/ui/UI.state';
17
+ import type { ModalExpose, ModalProps, ModalSlots } from '@aerogel/core/components/contracts/Modal';
16
18
 
17
- import { useModalProps } from './AGHeadlessModal';
18
- import type { IAGHeadlessModal, IAGHeadlessModalDefaultSlotProps } from './AGHeadlessModal';
19
+ defineProps<ModalProps>();
20
+ defineSlots<ModalSlots>();
19
21
 
20
- const props = defineProps(useModalProps());
21
- const $root = ref<{ $el?: HTMLElement } | null>(null);
22
+ const $root = useTemplateRef('$rootRef');
22
23
  const hidden = ref(true);
23
24
  const closed = ref(false);
24
- const { modal } = injectReactiveOrFail<IAGModalContext>(
25
+ const { modal } = injectReactiveOrFail<UIModalContext>(
25
26
  'modal',
26
- 'could not obtain modal reference from <AGHeadlessModal>, ' +
27
+ 'could not obtain modal reference from <HeadlessModal>, ' +
27
28
  'did you render this component manually? Show it using $ui.openModal() instead',
28
29
  );
29
30
 
@@ -81,6 +82,5 @@ useEvent('show-modal', async ({ id }) => {
81
82
  await show();
82
83
  });
83
84
 
84
- defineSlots<{ default(props: IAGHeadlessModalDefaultSlotProps): VNode[] }>();
85
- defineExpose<IAGHeadlessModal>({ close, cancellable: toRef(props, 'cancellable') });
85
+ defineExpose<ModalExpose>({ close });
86
86
  </script>
@@ -0,0 +1,24 @@
1
+ <template>
2
+ <DialogContent>
3
+ <slot />
4
+
5
+ <ModalContext v-if="childModal" :child-index="childIndex + 1" :modal="childModal" />
6
+ </DialogContent>
7
+ </template>
8
+
9
+ <script setup lang="ts">
10
+ import { computed } from 'vue';
11
+ import { DialogContent } from 'reka-ui';
12
+
13
+ import ModalContext from '@aerogel/core/components/ui/ModalContext.vue';
14
+ import UI from '@aerogel/core/ui/UI';
15
+ import { injectReactiveOrFail } from '@aerogel/core/utils/vue';
16
+ import type { UIModalContext } from '@aerogel/core/ui/UI.state';
17
+
18
+ const { childIndex = 0 } = injectReactiveOrFail<UIModalContext>(
19
+ 'modal',
20
+ 'could not obtain modal reference from <HeadlessModalContent>, ' +
21
+ 'did you render this component manually? Show it using $ui.openModal() instead',
22
+ );
23
+ const childModal = computed(() => UI.modals[childIndex] ?? null);
24
+ </script>
@@ -0,0 +1,12 @@
1
+ <template>
2
+ <DialogDescription v-bind="$props">
3
+ <slot />
4
+ </DialogDescription>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { DialogDescription } from 'reka-ui';
9
+ import type { DialogDescriptionProps } from 'reka-ui';
10
+
11
+ defineProps<DialogDescriptionProps>();
12
+ </script>
@@ -0,0 +1,12 @@
1
+ <template>
2
+ <DialogOverlay v-bind="$props">
3
+ <slot />
4
+ </DialogOverlay>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { DialogOverlay } from 'reka-ui';
9
+ import type { DialogOverlayProps } from 'reka-ui';
10
+
11
+ defineProps<DialogOverlayProps>();
12
+ </script>
@@ -0,0 +1,12 @@
1
+ <template>
2
+ <DialogTitle v-bind="$props">
3
+ <slot />
4
+ </DialogTitle>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { DialogTitle } from 'reka-ui';
9
+ import type { DialogTitleProps } from 'reka-ui';
10
+
11
+ defineProps<DialogTitleProps>();
12
+ </script>
@@ -0,0 +1,113 @@
1
+ <template>
2
+ <SelectRoot v-slot="{ open }" :model-value="acceptableValue" @update:model-value="update($event)">
3
+ <component :is="as" v-bind="$attrs">
4
+ <slot :model-value :open>
5
+ <HeadlessSelectTrigger />
6
+ <HeadlessSelectOptions />
7
+ </slot>
8
+ </component>
9
+ </SelectRoot>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { computed, inject, provide, readonly } from 'vue';
14
+ import { value as evaluate, toString, uuid } from '@noeldemartin/utils';
15
+ import { SelectRoot } from 'reka-ui';
16
+ import type { AcceptableValue } from 'reka-ui';
17
+
18
+ import { translateWithDefault } from '@aerogel/core/lang';
19
+ import { hasSelectOptionLabel } from '@aerogel/core/components/contracts/Select';
20
+ import type FormController from '@aerogel/core/forms/FormController';
21
+ import type { SelectEmits, SelectExpose, SelectProps } from '@aerogel/core/components/contracts/Select';
22
+ import type { FormFieldValue } from '@aerogel/core/forms/FormController';
23
+
24
+ import HeadlessSelectTrigger from './HeadlessSelectTrigger.vue';
25
+ import HeadlessSelectOptions from './HeadlessSelectOptions.vue';
26
+
27
+ defineOptions({ inheritAttrs: false });
28
+
29
+ const {
30
+ name,
31
+ as = 'div',
32
+ label,
33
+ options,
34
+ labelClass,
35
+ optionsClass,
36
+ renderOption,
37
+ description,
38
+ placeholder,
39
+ modelValue,
40
+ align,
41
+ side,
42
+ } = defineProps<SelectProps>();
43
+ const emit = defineEmits<SelectEmits>();
44
+ const form = inject<FormController | null>('form', null);
45
+ const computedValue = computed(() => {
46
+ if (form && name) {
47
+ return form.getFieldValue(name);
48
+ }
49
+
50
+ return modelValue;
51
+ });
52
+ const acceptableValue = computed(() => computedValue.value as AcceptableValue);
53
+ const errors = computed(() => {
54
+ if (!form || !name) {
55
+ return null;
56
+ }
57
+
58
+ return form.errors[name] ?? null;
59
+ });
60
+ const computedOptions = computed(() => {
61
+ if (!options) {
62
+ return null;
63
+ }
64
+
65
+ return options.map((option) => ({
66
+ key: uuid(),
67
+ label: renderOption
68
+ ? renderOption(option)
69
+ : hasSelectOptionLabel(option)
70
+ ? evaluate(option.label as string)
71
+ : toString(option),
72
+ value: option as AcceptableValue,
73
+ }));
74
+ });
75
+ const expose = {
76
+ labelClass,
77
+ optionsClass,
78
+ align,
79
+ side,
80
+ value: computedValue,
81
+ id: `select-${uuid()}`,
82
+ name: computed(() => name),
83
+ label: computed(() => label),
84
+ description: computed(() => description),
85
+ placeholder: computed(() => placeholder ?? translateWithDefault('ui.select', 'Select an option')),
86
+ options: computedOptions,
87
+ selectedOption: computed(() => computedOptions.value?.find((option) => option.value === modelValue)),
88
+ errors: readonly(errors),
89
+ required: computed(() => {
90
+ if (!name || !form) {
91
+ return;
92
+ }
93
+
94
+ return form.getFieldRules(name).includes('required');
95
+ }),
96
+ update(value) {
97
+ if (form && name) {
98
+ form.setFieldValue(name, value as FormFieldValue);
99
+
100
+ return;
101
+ }
102
+
103
+ emit('update:modelValue', value);
104
+ },
105
+ } satisfies SelectExpose;
106
+
107
+ function update(value: AcceptableValue) {
108
+ expose.update(value as FormFieldValue);
109
+ }
110
+
111
+ provide('select', expose);
112
+ defineExpose(expose);
113
+ </script>
@@ -7,14 +7,13 @@
7
7
  <script setup lang="ts">
8
8
  import { computed } from 'vue';
9
9
 
10
- import { injectReactiveOrFail } from '@/utils/vue';
11
- import { translateWithDefault } from '@/lang/utils';
10
+ import { injectReactiveOrFail } from '@aerogel/core/utils/vue';
11
+ import { translateWithDefault } from '@aerogel/core/lang/utils';
12
+ import type { SelectExpose } from '@aerogel/core/components/contracts/Select';
12
13
 
13
- import type { IAGHeadlessSelect } from './AGHeadlessSelect';
14
-
15
- const select = injectReactiveOrFail<IAGHeadlessSelect>(
14
+ const select = injectReactiveOrFail<SelectExpose>(
16
15
  'select',
17
- '<AGHeadlessSelectError> must be a child of a <AGHeadlessSelect>',
16
+ '<HeadlessSelectError> must be a child of a <HeadlessSelect>',
18
17
  );
19
18
  const errorMessage = computed(() => {
20
19
  if (!select.errors) {
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <Label v-if="show" :for="select.id" v-bind="$props">
3
+ <slot>
4
+ {{ select.label }}
5
+ </slot>
6
+ </Label>
7
+ </template>
8
+
9
+ <script setup lang="ts">
10
+ import { computed, useSlots } from 'vue';
11
+ import { Label } from 'reka-ui';
12
+ import type { LabelProps } from 'reka-ui';
13
+
14
+ import { injectReactiveOrFail } from '@aerogel/core/utils/vue';
15
+ import type { SelectExpose } from '@aerogel/core/components/contracts/Select';
16
+
17
+ defineProps<Omit<LabelProps, 'for'>>();
18
+
19
+ const select = injectReactiveOrFail<SelectExpose>(
20
+ 'select',
21
+ '<HeadlessSelectLabel> must be a child of a <HeadlessSelect>',
22
+ );
23
+ const slots = useSlots();
24
+ const show = computed(() => !!(select.label || slots.default));
25
+ </script>
@@ -0,0 +1,34 @@
1
+ <template>
2
+ <SelectItem v-bind="$props">
3
+ <SelectItemText>
4
+ <slot>
5
+ {{ renderedLabel }}
6
+ </slot>
7
+ </SelectItemText>
8
+ </SelectItem>
9
+ </template>
10
+
11
+ <script setup lang="ts">
12
+ import { computed } from 'vue';
13
+ import { SelectItem, SelectItemText } from 'reka-ui';
14
+ import { toString } from '@noeldemartin/utils';
15
+ import type { SelectItemProps } from 'reka-ui';
16
+
17
+ import { injectReactiveOrFail } from '@aerogel/core/utils/vue';
18
+ import type { SelectExpose } from '@aerogel/core/components/contracts/Select';
19
+
20
+ const { value } = defineProps<SelectItemProps>();
21
+ const select = injectReactiveOrFail<SelectExpose>(
22
+ 'select',
23
+ '<HeadlessSelectOption> must be a child of a <HeadlessSelect>',
24
+ );
25
+ const renderedLabel = computed(() => {
26
+ const itemOption = select.options?.find((option) => option.value === value);
27
+
28
+ if (itemOption) {
29
+ return itemOption.label;
30
+ }
31
+
32
+ return toString(value);
33
+ });
34
+ </script>
@@ -0,0 +1,37 @@
1
+ <template>
2
+ <SelectPortal>
3
+ <SelectContent
4
+ position="popper"
5
+ :class="$props.class"
6
+ :align="select.align"
7
+ :side="select.side"
8
+ >
9
+ <SelectViewport :class="innerClass">
10
+ <slot>
11
+ <HeadlessSelectOption
12
+ v-for="option in select.options ?? []"
13
+ :key="option.key"
14
+ :value="option.value"
15
+ />
16
+ </slot>
17
+ </SelectViewport>
18
+ </SelectContent>
19
+ </SelectPortal>
20
+ </template>
21
+
22
+ <script setup lang="ts">
23
+ import { SelectContent, SelectPortal, SelectViewport } from 'reka-ui';
24
+ import type { HTMLAttributes } from 'vue';
25
+
26
+ import { injectReactiveOrFail } from '@aerogel/core/utils';
27
+ import type { SelectExpose } from '@aerogel/core/components/contracts/Select';
28
+
29
+ import HeadlessSelectOption from './HeadlessSelectOption.vue';
30
+
31
+ defineProps<{ class?: HTMLAttributes['class']; innerClass?: HTMLAttributes['class'] }>();
32
+
33
+ const select = injectReactiveOrFail<SelectExpose>(
34
+ 'select',
35
+ '<HeadlessSelectOptions> must be a child of a <HeadlessSelect>',
36
+ );
37
+ </script>
@@ -0,0 +1,22 @@
1
+ <template>
2
+ <SelectTrigger :id="select.id">
3
+ <slot>
4
+ <HeadlessSelectValue :placeholder="select.placeholder" />
5
+ <SelectIcon />
6
+ </slot>
7
+ </SelectTrigger>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { SelectIcon, SelectTrigger } from 'reka-ui';
12
+
13
+ import { injectReactiveOrFail } from '@aerogel/core/utils';
14
+ import type { SelectExpose } from '@aerogel/core/components/contracts/Select';
15
+
16
+ import HeadlessSelectValue from './HeadlessSelectValue.vue';
17
+
18
+ const select = injectReactiveOrFail<SelectExpose>(
19
+ 'select',
20
+ '<HeadlessSelectTrigger> must be a child of a <HeadlessSelect>',
21
+ );
22
+ </script>
@@ -0,0 +1,18 @@
1
+ <template>
2
+ <SelectValue v-if="$slots.default" :placeholder="select.placeholder">
3
+ <slot />
4
+ </SelectValue>
5
+ <SelectValue v-else :placeholder="select.placeholder" />
6
+ </template>
7
+
8
+ <script setup lang="ts">
9
+ import { SelectValue } from 'reka-ui';
10
+
11
+ import { injectReactiveOrFail } from '@aerogel/core/utils/vue';
12
+ import type { SelectExpose } from '@aerogel/core/components/contracts/Select';
13
+
14
+ const select = injectReactiveOrFail<SelectExpose>(
15
+ 'select',
16
+ '<HeadlessSelectValue> must be a child of a <HeadlessSelect>',
17
+ );
18
+ </script>
@@ -0,0 +1,18 @@
1
+ <template>
2
+ <ToastRoot>
3
+ <slot>
4
+ <span v-if="message">{{ message }}</span>
5
+ <HeadlessToastAction v-for="(action, key) of actions" :key :action />
6
+ </slot>
7
+ </ToastRoot>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { ToastRoot } from 'reka-ui';
12
+
13
+ import type { ToastProps } from '@aerogel/core/components/contracts/Toast';
14
+
15
+ import HeadlessToastAction from './HeadlessToastAction.vue';
16
+
17
+ defineProps<ToastProps>();
18
+ </script>
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <component :is="action.dismiss ? ToastClose : 'button'" type="button" @click="action.click">
3
+ {{ action.label }}
4
+ </component>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { ToastClose } from 'reka-ui';
9
+
10
+ import type { ToastAction as IToastAction } from '@aerogel/core/components/contracts/Toast';
11
+
12
+ defineProps<{ action: IToastAction }>();
13
+ </script>