@aerogel/core 0.0.0-next.c2e6acc000e97a1020c2e232678563c53884dd0e → 0.0.0-next.c3236837f7f8fc319a4a56022accb32757b3db89

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 (134) hide show
  1. package/dist/aerogel-core.css +1 -0
  2. package/dist/aerogel-core.d.ts +1267 -599
  3. package/dist/aerogel-core.js +2560 -1859
  4. package/dist/aerogel-core.js.map +1 -1
  5. package/package.json +7 -2
  6. package/src/components/AppLayout.vue +1 -3
  7. package/src/components/AppModals.vue +1 -1
  8. package/src/components/AppOverlays.vue +2 -34
  9. package/src/components/AppToasts.vue +16 -0
  10. package/src/components/contracts/AlertModal.ts +15 -0
  11. package/src/components/contracts/Button.ts +1 -0
  12. package/src/components/contracts/ConfirmModal.ts +12 -5
  13. package/src/components/contracts/DropdownMenu.ts +25 -0
  14. package/src/components/contracts/ErrorReportModal.ts +8 -4
  15. package/src/components/contracts/Input.ts +9 -9
  16. package/src/components/contracts/LoadingModal.ts +12 -4
  17. package/src/components/contracts/Modal.ts +14 -2
  18. package/src/components/contracts/PromptModal.ts +8 -2
  19. package/src/components/contracts/Select.ts +45 -0
  20. package/src/components/contracts/Toast.ts +15 -0
  21. package/src/components/contracts/index.ts +5 -1
  22. package/src/components/headless/HeadlessButton.vue +9 -3
  23. package/src/components/headless/HeadlessInput.vue +3 -3
  24. package/src/components/headless/HeadlessInputDescription.vue +1 -1
  25. package/src/components/headless/HeadlessInputInput.vue +21 -10
  26. package/src/components/headless/HeadlessInputTextArea.vue +6 -6
  27. package/src/components/headless/HeadlessModal.vue +23 -50
  28. package/src/components/headless/HeadlessModalContent.vue +11 -5
  29. package/src/components/headless/HeadlessModalDescription.vue +12 -0
  30. package/src/components/headless/HeadlessModalOverlay.vue +2 -2
  31. package/src/components/headless/HeadlessModalTitle.vue +2 -2
  32. package/src/components/headless/HeadlessSelect.vue +120 -0
  33. package/src/components/headless/{forms/AGHeadlessSelectError.vue → HeadlessSelectError.vue} +3 -4
  34. package/src/components/headless/HeadlessSelectLabel.vue +25 -0
  35. package/src/components/headless/HeadlessSelectOption.vue +34 -0
  36. package/src/components/headless/HeadlessSelectOptions.vue +42 -0
  37. package/src/components/headless/HeadlessSelectTrigger.vue +22 -0
  38. package/src/components/headless/HeadlessSelectValue.vue +18 -0
  39. package/src/components/headless/HeadlessSwitch.vue +96 -0
  40. package/src/components/headless/HeadlessToast.vue +18 -0
  41. package/src/components/headless/HeadlessToastAction.vue +13 -0
  42. package/src/components/headless/index.ts +9 -3
  43. package/src/components/index.ts +4 -9
  44. package/src/components/ui/AdvancedOptions.vue +18 -0
  45. package/src/components/ui/AlertModal.vue +7 -3
  46. package/src/components/ui/Button.vue +74 -17
  47. package/src/components/ui/Checkbox.vue +21 -14
  48. package/src/components/ui/ConfirmModal.vue +14 -6
  49. package/src/components/ui/DropdownMenu.vue +32 -0
  50. package/src/components/ui/DropdownMenuOption.vue +22 -0
  51. package/src/components/ui/DropdownMenuOptions.vue +44 -0
  52. package/src/components/ui/EditableContent.vue +82 -0
  53. package/src/components/ui/ErrorLogs.vue +19 -0
  54. package/src/components/ui/ErrorLogsModal.vue +48 -0
  55. package/src/components/{lib/AGErrorMessage.vue → ui/ErrorMessage.vue} +2 -3
  56. package/src/components/ui/ErrorReportModal.vue +18 -7
  57. package/src/components/ui/ErrorReportModalButtons.vue +6 -8
  58. package/src/components/ui/ErrorReportModalTitle.vue +1 -1
  59. package/src/components/ui/Input.vue +11 -7
  60. package/src/components/ui/Link.vue +2 -2
  61. package/src/components/ui/LoadingModal.vue +8 -6
  62. package/src/components/ui/Markdown.vue +41 -6
  63. package/src/components/ui/Modal.vue +95 -19
  64. package/src/components/ui/ModalContext.vue +2 -1
  65. package/src/components/ui/ProgressBar.vue +9 -8
  66. package/src/components/ui/PromptModal.vue +11 -8
  67. package/src/components/ui/Select.vue +27 -0
  68. package/src/components/ui/SelectLabel.vue +21 -0
  69. package/src/components/ui/SelectOption.vue +29 -0
  70. package/src/components/ui/SelectOptions.vue +35 -0
  71. package/src/components/ui/SelectTrigger.vue +29 -0
  72. package/src/components/ui/Setting.vue +31 -0
  73. package/src/components/ui/SettingsModal.vue +15 -0
  74. package/src/components/ui/Switch.vue +11 -0
  75. package/src/components/ui/TextArea.vue +56 -0
  76. package/src/components/ui/Toast.vue +46 -0
  77. package/src/components/ui/index.ts +19 -0
  78. package/src/directives/measure.ts +11 -5
  79. package/src/errors/Errors.ts +21 -19
  80. package/src/errors/index.ts +6 -2
  81. package/src/errors/settings/Debug.vue +32 -0
  82. package/src/errors/settings/index.ts +10 -0
  83. package/src/forms/FormController.test.ts +32 -9
  84. package/src/forms/FormController.ts +27 -22
  85. package/src/forms/index.ts +0 -1
  86. package/src/forms/utils.ts +34 -34
  87. package/src/index.css +70 -3
  88. package/src/lang/index.ts +5 -1
  89. package/src/lang/settings/Language.vue +48 -0
  90. package/src/lang/settings/index.ts +10 -0
  91. package/src/services/App.state.ts +11 -1
  92. package/src/services/App.ts +9 -1
  93. package/src/services/Events.test.ts +8 -8
  94. package/src/services/Events.ts +2 -8
  95. package/src/services/index.ts +5 -2
  96. package/src/testing/index.ts +4 -0
  97. package/src/ui/UI.state.ts +5 -15
  98. package/src/ui/UI.ts +115 -99
  99. package/src/ui/index.ts +18 -19
  100. package/src/utils/classes.ts +41 -0
  101. package/src/utils/composition/events.ts +2 -4
  102. package/src/utils/composition/forms.ts +16 -1
  103. package/src/utils/composition/state.ts +11 -2
  104. package/src/utils/index.ts +3 -1
  105. package/src/utils/markdown.ts +35 -1
  106. package/src/utils/types.ts +3 -0
  107. package/src/utils/vue.ts +28 -129
  108. package/src/components/AppSnackbars.vue +0 -13
  109. package/src/components/composition.ts +0 -23
  110. package/src/components/constants.ts +0 -8
  111. package/src/components/contracts/shared.ts +0 -9
  112. package/src/components/forms/AGSelect.story.vue +0 -46
  113. package/src/components/forms/AGSelect.vue +0 -54
  114. package/src/components/forms/index.ts +0 -1
  115. package/src/components/headless/forms/AGHeadlessSelect.ts +0 -42
  116. package/src/components/headless/forms/AGHeadlessSelect.vue +0 -77
  117. package/src/components/headless/forms/AGHeadlessSelectOption.ts +0 -4
  118. package/src/components/headless/forms/AGHeadlessSelectOption.vue +0 -31
  119. package/src/components/headless/forms/AGHeadlessSelectOptions.vue +0 -19
  120. package/src/components/headless/forms/AGHeadlessSelectTrigger.vue +0 -25
  121. package/src/components/headless/forms/composition.ts +0 -10
  122. package/src/components/headless/forms/index.ts +0 -8
  123. package/src/components/headless/snackbars/AGHeadlessSnackbar.vue +0 -10
  124. package/src/components/headless/snackbars/index.ts +0 -40
  125. package/src/components/lib/AGMeasured.vue +0 -16
  126. package/src/components/lib/index.ts +0 -3
  127. package/src/components/snackbars/AGSnackbar.vue +0 -38
  128. package/src/components/snackbars/index.ts +0 -3
  129. package/src/components/utils.ts +0 -107
  130. package/src/forms/composition.ts +0 -6
  131. package/src/utils/tailwindcss.test.ts +0 -26
  132. package/src/utils/tailwindcss.ts +0 -7
  133. package/src/utils/vdom.ts +0 -31
  134. /package/src/components/{lib/AGStartupCrash.vue → ui/StartupCrash.vue} +0 -0
package/src/utils/vue.ts CHANGED
@@ -1,59 +1,26 @@
1
1
  import { fail, toString } from '@noeldemartin/utils';
2
- import { computed, inject, reactive, ref, watch } from 'vue';
3
- import type { Directive, InjectionKey, MaybeRef, PropType, Ref, UnwrapNestedRefs } from 'vue';
2
+ import { Comment, Static, Text, inject, reactive } from 'vue';
3
+ import type { Directive, InjectionKey, MaybeRef, Ref, UnwrapNestedRefs, VNode } from 'vue';
4
4
 
5
5
  export type AcceptRefs<T> = { [K in keyof T]: T[K] | RefUnion<T[K]> };
6
- export type BaseProp<T> = { type?: PropType<T>; validator?(value: unknown): boolean };
7
- export type ComponentProps<T = {}> = T & Record<string, unknown>;
8
- export type OptionalProp<T> = BaseProp<T> & { default: T | (() => T) | null };
9
6
  export type RefUnion<T> = T extends infer R ? Ref<R> : never;
10
- export type RequiredProp<T> = BaseProp<T> & { required: true };
11
7
  export type Unref<T> = { [K in keyof T]: T[K] extends MaybeRef<infer Value> ? Value : T[K] };
12
8
 
13
- export function arrayProp<T>(defaultValue?: () => T[]): OptionalProp<T[]> {
14
- return {
15
- type: Array as PropType<T[]>,
16
- default: defaultValue ?? (() => []),
17
- };
9
+ function renderVNodeAttrs(node: VNode): string {
10
+ return Object.entries(node.props ?? {}).reduce((attrs, [name, value]) => {
11
+ return attrs + `${name}="${toString(value)}"`;
12
+ }, '');
18
13
  }
19
14
 
20
- export function booleanProp(defaultValue: boolean = false): OptionalProp<boolean> {
21
- return {
22
- type: Boolean,
23
- default: defaultValue,
24
- };
25
- }
26
-
27
- export function componentRef<T>(): Ref<UnwrapNestedRefs<T> | undefined> {
28
- return ref<UnwrapNestedRefs<T>>();
29
- }
30
-
31
- export function computedAsync<T>(getter: () => Promise<T>): Ref<T | undefined> {
32
- const result = ref<T>();
33
- const asyncValue = computed(getter);
34
-
35
- watch(asyncValue, async () => (result.value = await asyncValue.value), { immediate: true });
36
-
37
- return result;
38
- }
39
-
40
- export function defineDirective(directive: Directive): Directive {
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ export function defineDirective<TValue = any, TModifiers extends string = string>(
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
+ directive: Directive<any, TValue, TModifiers>,
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ ): Directive<any, TValue, TModifiers> {
41
21
  return directive;
42
22
  }
43
23
 
44
- export function enumProp<Enum extends Record<string, unknown>>(
45
- enumeration: Enum,
46
- defaultValue?: Enum[keyof Enum],
47
- ): OptionalProp<Enum[keyof Enum]> {
48
- const values = Object.values(enumeration) as Enum[keyof Enum][];
49
-
50
- return {
51
- type: String as unknown as PropType<Enum[keyof Enum]>,
52
- default: defaultValue ?? values[0] ?? null,
53
- validator: (value) => values.includes(value as Enum[keyof Enum]),
54
- };
55
- }
56
-
57
24
  export function injectReactive<T extends object>(key: InjectionKey<T> | string): UnwrapNestedRefs<T> | undefined {
58
25
  const value = inject(key);
59
26
 
@@ -71,92 +38,24 @@ export function injectOrFail<T>(key: InjectionKey<T> | string, errorMessage?: st
71
38
  return inject(key) ?? fail(errorMessage ?? `Could not resolve '${toString(key)}' injection key`);
72
39
  }
73
40
 
74
- export function listenerProp<T extends Function = Function>(): OptionalProp<T | null> {
75
- return {
76
- type: Function as PropType<T>,
77
- default: null,
78
- };
79
- }
80
-
81
- export function mixedProp<T>(type?: PropType<T>): OptionalProp<T | null>;
82
- export function mixedProp<T>(type: PropType<T>, defaultValue: T): OptionalProp<T>;
83
- export function mixedProp<T>(type?: PropType<T>, defaultValue?: T): OptionalProp<T | null> {
84
- return {
85
- type,
86
- default: defaultValue ?? null,
87
- };
88
- }
89
-
90
- export function numberProp(): OptionalProp<number | null>;
91
- export function numberProp(defaultValue: number): OptionalProp<number>;
92
- export function numberProp(defaultValue: number | null = null): OptionalProp<number | null> {
93
- return {
94
- type: Number,
95
- default: defaultValue,
96
- };
97
- }
98
-
99
- export function objectProp<T = Object>(): OptionalProp<T | null>;
100
- export function objectProp<T>(defaultValue: () => T): OptionalProp<T>;
101
- export function objectProp<T = Object>(defaultValue: (() => T) | null = null): OptionalProp<T | null> {
102
- return {
103
- type: Object,
104
- default: defaultValue,
105
- };
106
- }
107
-
108
- export function requiredArrayProp<T>(): RequiredProp<T[]> {
109
- return {
110
- type: Array as PropType<T[]>,
111
- required: true,
112
- };
113
- }
114
-
115
- export function requiredEnumProp<Enum extends Record<string, unknown>>(
116
- enumeration: Enum,
117
- ): RequiredProp<Enum[keyof Enum]> {
118
- const values = Object.values(enumeration);
41
+ export function renderVNode(node: VNode | string): string {
42
+ if (typeof node === 'string') {
43
+ return node;
44
+ }
119
45
 
120
- return {
121
- type: String as unknown as PropType<Enum[keyof Enum]>,
122
- required: true,
123
- validator: (value) => values.includes(value),
124
- };
125
- }
126
-
127
- export function requiredMixedProp<T>(type?: PropType<T>): RequiredProp<T> {
128
- return {
129
- type,
130
- required: true,
131
- };
132
- }
46
+ if (node.type === Comment) {
47
+ return '';
48
+ }
133
49
 
134
- export function requiredNumberProp(): RequiredProp<number> {
135
- return {
136
- type: Number,
137
- required: true,
138
- };
139
- }
50
+ if (node.type === Text || node.type === Static) {
51
+ return node.children as string;
52
+ }
140
53
 
141
- export function requiredObjectProp<T = Object>(): RequiredProp<T> {
142
- return {
143
- type: Object,
144
- required: true,
145
- };
146
- }
147
-
148
- export function requiredStringProp(): RequiredProp<string> {
149
- return {
150
- type: String,
151
- required: true,
152
- };
153
- }
54
+ if (node.type === 'br') {
55
+ return '\n\n';
56
+ }
154
57
 
155
- export function stringProp(): OptionalProp<string | null>;
156
- export function stringProp(defaultValue: string): OptionalProp<string>;
157
- export function stringProp(defaultValue: string | null = null): OptionalProp<string | null> {
158
- return {
159
- type: String,
160
- default: defaultValue,
161
- };
58
+ return `<${node.type} ${renderVNodeAttrs(node)}>${Array.from(node.children as Array<VNode | string>)
59
+ .map(renderVNode)
60
+ .join('')}</${node.type}>`;
162
61
  }
@@ -1,13 +0,0 @@
1
- <template>
2
- <div aria-live="assertive" class="pointer-events-none fixed inset-0 z-50 flex items-end px-4 py-6 sm:p-6">
3
- <div class="flex w-full flex-col items-end space-y-4">
4
- <component
5
- :is="snackbar.component"
6
- v-for="snackbar of $ui.snackbars"
7
- :id="snackbar.id"
8
- :key="snackbar.id"
9
- v-bind="snackbar.properties"
10
- />
11
- </div>
12
- </div>
13
- </template>
@@ -1,23 +0,0 @@
1
- import { customRef } from 'vue';
2
- import type { Ref } from 'vue';
3
-
4
- import { getElement } from '@aerogel/core/components/utils';
5
-
6
- export function elementRef(): Ref<HTMLElement | undefined> {
7
- return customRef((track, trigger) => {
8
- let value: HTMLElement | undefined = undefined;
9
-
10
- return {
11
- get() {
12
- track();
13
-
14
- return value;
15
- },
16
- set(newValue) {
17
- value = getElement(newValue);
18
-
19
- trigger();
20
- },
21
- };
22
- });
23
- }
@@ -1,8 +0,0 @@
1
- export const Colors = {
2
- Primary: 'primary',
3
- Secondary: 'secondary',
4
- Danger: 'danger',
5
- Clear: 'clear',
6
- } as const;
7
-
8
- export type Color = (typeof Colors)[keyof typeof Colors];
@@ -1,9 +0,0 @@
1
- import type { Ref } from 'vue';
2
-
3
- export interface HasElement {
4
- $el: Readonly<Ref<HTMLElement | undefined>>;
5
- }
6
-
7
- export interface __SetsElement {
8
- __setElement(element?: HTMLElement): void;
9
- }
@@ -1,46 +0,0 @@
1
- <template>
2
- <Story>
3
- <div class="h-96">
4
- <AGSelect v-model="bestMugiwara" :label="bestMugiwaraLabel" :options="bestMugiwaraOptions" />
5
- <AGSelect
6
- v-model="bestFood"
7
- :label="bestFoodLabel"
8
- :options="bestFoodOptions"
9
- options-text="name"
10
- />
11
- </div>
12
- </Story>
13
- </template>
14
-
15
- <script setup lang="ts">
16
- import { ref } from 'vue';
17
-
18
- import AGSelect from './AGSelect.vue';
19
-
20
- const bestMugiwara = ref(null);
21
- const bestFood = ref(null);
22
- const bestMugiwaraLabel = 'Who\'s the best Mugiwara?';
23
- const bestFoodLabel = 'What\'s the best food?';
24
- const bestMugiwaraOptions = [
25
- 'Monkey D. Luffy',
26
- 'Roronoa Zoro',
27
- 'Nami',
28
- 'Usopp',
29
- 'Sanji',
30
- 'Tony Tony Chopper',
31
- 'Nico Robin',
32
- 'Franky',
33
- 'Brook',
34
- 'Jinbe',
35
- ];
36
- const bestFoodOptions = [
37
- {
38
- id: 'ramen',
39
- name: 'Ramen',
40
- },
41
- {
42
- id: 'pizza',
43
- name: 'Pizza',
44
- },
45
- ];
46
- </script>
@@ -1,54 +0,0 @@
1
- <template>
2
- <AGHeadlessSelect
3
- v-bind="props"
4
- ref="$select"
5
- as="div"
6
- @update:model-value="$emit('update:modelValue', $event)"
7
- >
8
- <div class="relative" :class="{ 'mt-2': $select?.label }">
9
- <AGHeadlessSelectTrigger
10
- class="relative w-full cursor-default bg-white py-1.5 pr-10 pl-3 text-left text-gray-900 ring-1 ring-gray-300 ring-inset focus:ring-2 focus:ring-indigo-600 focus:outline-hidden"
11
- text-class="block truncate"
12
- :class="{
13
- 'ring-1 ring-red-500': $select?.errors,
14
- }"
15
- >
16
- <template #icon>
17
- <span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
18
- <IconCheveronDown class="size-5 text-gray-400" />
19
- </span>
20
- </template>
21
- </AGHeadlessSelectTrigger>
22
- <AGHeadlessSelectOptions
23
- class="absolute z-10 mt-1 max-h-60 w-full overflow-auto border border-gray-300 bg-white py-1 text-base ring-1 ring-black/5 focus:outline-hidden"
24
- >
25
- <AGHeadlessSelectOption
26
- v-for="(option, index) in $select?.options ?? []"
27
- :key="index"
28
- :value="option"
29
- class="relative block cursor-default truncate py-2 pr-9 pl-3 text-gray-900 select-none data-[highlighted]:bg-indigo-600 data-[highlighted]:text-white data-[state=checked]:font-semibold data-[state=unchecked]:font-normal"
30
- />
31
- </AGHeadlessSelectOptions>
32
- </div>
33
- <AGHeadlessSelectError class="mt-2 text-sm text-red-600" />
34
- </AGHeadlessSelect>
35
- </template>
36
-
37
- <script setup lang="ts">
38
- import IconCheveronDown from '~icons/zondicons/cheveron-down';
39
-
40
- import { componentRef } from '@aerogel/core/utils/vue';
41
- import { useSelectEmits, useSelectProps } from '@aerogel/core/components/headless/forms/AGHeadlessSelect';
42
- import type { IAGHeadlessSelect } from '@aerogel/core/components/headless/forms/AGHeadlessSelect';
43
-
44
- import AGHeadlessSelect from '../headless/forms/AGHeadlessSelect.vue';
45
- import AGHeadlessSelectTrigger from '../headless/forms/AGHeadlessSelectTrigger.vue';
46
- import AGHeadlessSelectError from '../headless/forms/AGHeadlessSelectError.vue';
47
- import AGHeadlessSelectOption from '../headless/forms/AGHeadlessSelectOption.vue';
48
- import AGHeadlessSelectOptions from '../headless/forms/AGHeadlessSelectOptions.vue';
49
-
50
- defineEmits(useSelectEmits());
51
-
52
- const props = defineProps(useSelectProps());
53
- const $select = componentRef<IAGHeadlessSelect>();
54
- </script>
@@ -1 +0,0 @@
1
- export { default as AGSelect } from './AGSelect.vue';
@@ -1,42 +0,0 @@
1
- import type { ComputedRef, DeepReadonly, ExtractPropTypes, Ref } from 'vue';
2
- import type { Writable } from '@noeldemartin/utils';
3
-
4
- import { mixedProp, requiredArrayProp, stringProp } from '@aerogel/core/utils/vue';
5
- import { extractComponentProps } from '@aerogel/core/components/utils';
6
- import type { FormFieldValue } from '@aerogel/core/forms/FormController';
7
-
8
- export interface IAGHeadlessSelect {
9
- id: string;
10
- label: ComputedRef<string | null>;
11
- noSelectionText: ComputedRef<string>;
12
- buttonText: ComputedRef<string>;
13
- renderText: ComputedRef<(value: FormFieldValue) => string>;
14
- selectedOption: ComputedRef<FormFieldValue | null>;
15
- options: ComputedRef<FormFieldValue[]>;
16
- errors: DeepReadonly<Ref<string[] | null>>;
17
- update(value: FormFieldValue): void;
18
- }
19
-
20
- export const selectProps = {
21
- name: stringProp(),
22
- label: stringProp(),
23
- options: requiredArrayProp<FormFieldValue>(),
24
- noSelectionText: stringProp(),
25
- optionsText: mixedProp<string | ((option: FormFieldValue) => string)>(),
26
- };
27
-
28
- export const selectEmits = ['update:modelValue'] as const;
29
-
30
- export function useSelectProps(): typeof selectProps {
31
- return selectProps;
32
- }
33
-
34
- export function useSelectEmits(): Writable<typeof selectEmits> {
35
- return [...selectEmits];
36
- }
37
-
38
- export function extractSelectProps<T extends ExtractPropTypes<typeof selectProps>>(
39
- props: T,
40
- ): Pick<T, keyof typeof selectProps> {
41
- return extractComponentProps(props, selectProps);
42
- }
@@ -1,77 +0,0 @@
1
- <template>
2
- <SelectRoot v-slot="{ open }: ComponentProps" :model-value="selectedOption" @update:model-value="update($event)">
3
- <slot :model-value="modelValue" :open="open" />
4
- </SelectRoot>
5
- </template>
6
-
7
- <script setup lang="ts">
8
- import { computed, inject, provide } from 'vue';
9
- import { toString, uuid } from '@noeldemartin/utils';
10
- import { SelectRoot } from 'reka-ui';
11
- import type { AcceptableValue } from 'reka-ui';
12
-
13
- import { mixedProp } from '@aerogel/core/utils/vue';
14
- import { translateWithDefault } from '@aerogel/core/lang/utils';
15
- import type FormController from '@aerogel/core/forms/FormController';
16
- import type { FormFieldValue } from '@aerogel/core/forms/FormController';
17
- import type { ComponentProps } from '@aerogel/core/utils/vue';
18
-
19
- import { useSelectEmits, useSelectProps } from './AGHeadlessSelect';
20
- import type { IAGHeadlessSelect } from './AGHeadlessSelect';
21
-
22
- const emit = defineEmits(useSelectEmits());
23
- const props = defineProps({
24
- modelValue: mixedProp<FormFieldValue>(),
25
- ...useSelectProps(),
26
- });
27
- const renderText = computed(() => {
28
- if (typeof props.optionsText === 'function') {
29
- return props.optionsText;
30
- }
31
-
32
- if (typeof props.optionsText === 'string') {
33
- return (option: FormFieldValue): string => toString(option[props.optionsText as keyof FormFieldValue]);
34
- }
35
-
36
- return (option: FormFieldValue) => toString(option);
37
- });
38
- const form = inject<FormController | null>('form', null);
39
- const noSelectionText = computed(() => props.noSelectionText ?? translateWithDefault('select.noSelection', '-'));
40
- const selectedOption = computed(
41
- () => (form && props.name ? form.getFieldValue(props.name) : props.modelValue) as AcceptableValue,
42
- );
43
- const errors = computed(() => {
44
- if (!form || !props.name) {
45
- return null;
46
- }
47
-
48
- return form.errors[props.name] ?? null;
49
- });
50
-
51
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
- function update(value: any) {
53
- if (form && props.name) {
54
- form.setFieldValue(props.name, value);
55
-
56
- return;
57
- }
58
-
59
- emit('update:modelValue', value);
60
- }
61
-
62
- const api: IAGHeadlessSelect = {
63
- id: `select-${uuid()}`,
64
- noSelectionText,
65
- selectedOption,
66
- errors,
67
- options: computed(() => props.options),
68
- label: computed(() => props.label),
69
- buttonText: computed(() =>
70
- selectedOption.value === null ? noSelectionText.value : renderText.value(selectedOption.value)),
71
- renderText,
72
- update,
73
- };
74
-
75
- provide<IAGHeadlessSelect>('select', api);
76
- defineExpose<IAGHeadlessSelect>(api);
77
- </script>
@@ -1,4 +0,0 @@
1
- export type IAGHeadlessSelectOptionSlotProps = {
2
- active: boolean;
3
- selected: boolean;
4
- };
@@ -1,31 +0,0 @@
1
- <template>
2
- <SelectItem :value="value" as="template">
3
- <SelectItemText>
4
- <slot>
5
- {{ select.renderText(value) }}
6
- </slot>
7
- </SelectItemText>
8
- </SelectItem>
9
- </template>
10
-
11
- <script setup lang="ts">
12
- import { SelectItem, SelectItemText } from 'reka-ui';
13
-
14
- import { injectReactiveOrFail, requiredMixedProp, stringProp } from '@aerogel/core/utils/vue';
15
-
16
- import type { IAGHeadlessSelect } from './AGHeadlessSelect';
17
-
18
- defineProps({
19
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
- value: requiredMixedProp<any>(),
21
- selectedClass: stringProp(),
22
- unselectedClass: stringProp(),
23
- activeClass: stringProp(),
24
- inactiveClass: stringProp(),
25
- });
26
-
27
- const select = injectReactiveOrFail<IAGHeadlessSelect>(
28
- 'select',
29
- '<AGHeadlessSelectOption> must be a child of a <AGHeadlessSelect>',
30
- );
31
- </script>
@@ -1,19 +0,0 @@
1
- <template>
2
- <SelectPortal>
3
- <SelectContent :class="classes">
4
- <SelectViewport>
5
- <slot />
6
- </SelectViewport>
7
- </SelectContent>
8
- </SelectPortal>
9
- </template>
10
-
11
- <script setup lang="ts">
12
- import { computed } from 'vue';
13
- import { SelectContent, SelectPortal, SelectViewport } from 'reka-ui';
14
-
15
- import { stringProp } from '@aerogel/core/utils/vue';
16
-
17
- const props = defineProps({ class: stringProp('') });
18
- const classes = computed(() => props.class);
19
- </script>
@@ -1,25 +0,0 @@
1
- <template>
2
- <SelectTrigger>
3
- <SelectValue>
4
- <slot>
5
- <span :class="textClass">{{ select?.buttonText }}</span>
6
- </slot>
7
- <slot name="icon" />
8
- </SelectValue>
9
- </SelectTrigger>
10
- </template>
11
-
12
- <script setup lang="ts">
13
- import { SelectTrigger, SelectValue } from 'reka-ui';
14
-
15
- import { injectReactiveOrFail, stringProp } from '@aerogel/core/utils/vue';
16
-
17
- import type { IAGHeadlessSelect } from './AGHeadlessSelect';
18
-
19
- defineProps({ textClass: stringProp() });
20
-
21
- const select = injectReactiveOrFail<IAGHeadlessSelect>(
22
- 'select',
23
- '<AGHeadlessSelectTrigger> must be a child of a <AGHeadlessSelect>',
24
- );
25
- </script>
@@ -1,10 +0,0 @@
1
- import { inject, onUnmounted } from 'vue';
2
-
3
- import type FormController from '@aerogel/core/forms/FormController';
4
-
5
- export function onFormFocus(input: { name: string | null }, listener: () => unknown): void {
6
- const form = inject<FormController | null>('form', null);
7
- const stop = form?.on('focus', (name) => input.name === name && listener());
8
-
9
- onUnmounted(() => stop?.());
10
- }
@@ -1,8 +0,0 @@
1
- export * from './composition';
2
- export * from './AGHeadlessSelect';
3
- export * from './AGHeadlessSelectOption';
4
- export { default as AGHeadlessSelect } from './AGHeadlessSelect.vue';
5
- export { default as AGHeadlessSelectTrigger } from './AGHeadlessSelectTrigger.vue';
6
- export { default as AGHeadlessSelectError } from './AGHeadlessSelectError.vue';
7
- export { default as AGHeadlessSelectOption } from './AGHeadlessSelectOption.vue';
8
- export { default as AGHeadlessSelectOptions } from './AGHeadlessSelectOptions.vue';
@@ -1,10 +0,0 @@
1
- <template>
2
- <div class="pointer-events-auto">
3
- <slot />
4
- </div>
5
- </template>
6
-
7
- <script setup lang="ts">
8
- // Stub import to fix build (otherwise, this file doesn't seem to be treated as a module).
9
- import 'virtual:aerogel';
10
- </script>
@@ -1,40 +0,0 @@
1
- import type { ExtractPropTypes } from 'vue';
2
- import type { ObjectWithoutEmpty } from '@noeldemartin/utils';
3
-
4
- import UI from '@aerogel/core/ui/UI';
5
- import { arrayProp, enumProp, requiredStringProp } from '@aerogel/core/utils/vue';
6
- import { Colors } from '@aerogel/core/components/constants';
7
- import { objectWithout } from '@noeldemartin/utils';
8
-
9
- export { default as AGHeadlessSnackbar } from './AGHeadlessSnackbar.vue';
10
-
11
- export const SnackbarColors = objectWithout(Colors, ['Primary', 'Clear']);
12
- export const snackbarProps = {
13
- id: requiredStringProp(),
14
- message: requiredStringProp(),
15
- actions: arrayProp<SnackbarAction>(() => []),
16
- color: enumProp(SnackbarColors, Colors.Secondary),
17
- };
18
-
19
- export interface SnackbarAction {
20
- text: string;
21
- dismiss?: boolean;
22
- handler?(): unknown;
23
- }
24
-
25
- export type SnackbarColor = (typeof SnackbarColors)[keyof typeof SnackbarColors];
26
- export type AGSnackbarProps = ObjectWithoutEmpty<ExtractPropTypes<typeof snackbarProps>>;
27
-
28
- export function useSnackbarProps(): typeof snackbarProps {
29
- return snackbarProps;
30
- }
31
-
32
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
33
- export function useSnackbar(props: ExtractPropTypes<typeof snackbarProps>) {
34
- function activate(action: SnackbarAction): void {
35
- action.handler?.();
36
- action.dismiss && UI.hideSnackbar(props.id);
37
- }
38
-
39
- return { activate };
40
- }
@@ -1,16 +0,0 @@
1
- <template>
2
- <component :is="as" v-measure="() => (measured = true)" :class="{ 'invisible! absolute! w-auto!': !measured }">
3
- <slot />
4
- </component>
5
- </template>
6
-
7
- <script setup lang="ts">
8
- import { ref } from 'vue';
9
-
10
- import { stringProp } from '@aerogel/core/utils/vue';
11
-
12
- defineProps({ as: stringProp('span') });
13
-
14
- // TODO use v-measure.css
15
- const measured = ref(false);
16
- </script>
@@ -1,3 +0,0 @@
1
- export { default as AGErrorMessage } from './AGErrorMessage.vue';
2
- export { default as AGMeasured } from './AGMeasured.vue';
3
- export { default as AGStartupCrash } from './AGStartupCrash.vue';