@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.
- package/dist/aerogel-core.css +1 -0
- package/dist/aerogel-core.d.ts +1267 -599
- package/dist/aerogel-core.js +2560 -1859
- package/dist/aerogel-core.js.map +1 -1
- package/package.json +7 -2
- package/src/components/AppLayout.vue +1 -3
- package/src/components/AppModals.vue +1 -1
- package/src/components/AppOverlays.vue +2 -34
- package/src/components/AppToasts.vue +16 -0
- package/src/components/contracts/AlertModal.ts +15 -0
- package/src/components/contracts/Button.ts +1 -0
- package/src/components/contracts/ConfirmModal.ts +12 -5
- package/src/components/contracts/DropdownMenu.ts +25 -0
- package/src/components/contracts/ErrorReportModal.ts +8 -4
- package/src/components/contracts/Input.ts +9 -9
- package/src/components/contracts/LoadingModal.ts +12 -4
- package/src/components/contracts/Modal.ts +14 -2
- package/src/components/contracts/PromptModal.ts +8 -2
- package/src/components/contracts/Select.ts +45 -0
- package/src/components/contracts/Toast.ts +15 -0
- package/src/components/contracts/index.ts +5 -1
- package/src/components/headless/HeadlessButton.vue +9 -3
- package/src/components/headless/HeadlessInput.vue +3 -3
- package/src/components/headless/HeadlessInputDescription.vue +1 -1
- package/src/components/headless/HeadlessInputInput.vue +21 -10
- package/src/components/headless/HeadlessInputTextArea.vue +6 -6
- package/src/components/headless/HeadlessModal.vue +23 -50
- package/src/components/headless/HeadlessModalContent.vue +11 -5
- package/src/components/headless/HeadlessModalDescription.vue +12 -0
- package/src/components/headless/HeadlessModalOverlay.vue +2 -2
- package/src/components/headless/HeadlessModalTitle.vue +2 -2
- package/src/components/headless/HeadlessSelect.vue +120 -0
- package/src/components/headless/{forms/AGHeadlessSelectError.vue → HeadlessSelectError.vue} +3 -4
- package/src/components/headless/HeadlessSelectLabel.vue +25 -0
- package/src/components/headless/HeadlessSelectOption.vue +34 -0
- package/src/components/headless/HeadlessSelectOptions.vue +42 -0
- package/src/components/headless/HeadlessSelectTrigger.vue +22 -0
- package/src/components/headless/HeadlessSelectValue.vue +18 -0
- package/src/components/headless/HeadlessSwitch.vue +96 -0
- package/src/components/headless/HeadlessToast.vue +18 -0
- package/src/components/headless/HeadlessToastAction.vue +13 -0
- package/src/components/headless/index.ts +9 -3
- package/src/components/index.ts +4 -9
- package/src/components/ui/AdvancedOptions.vue +18 -0
- package/src/components/ui/AlertModal.vue +7 -3
- package/src/components/ui/Button.vue +74 -17
- package/src/components/ui/Checkbox.vue +21 -14
- package/src/components/ui/ConfirmModal.vue +14 -6
- package/src/components/ui/DropdownMenu.vue +32 -0
- package/src/components/ui/DropdownMenuOption.vue +22 -0
- package/src/components/ui/DropdownMenuOptions.vue +44 -0
- package/src/components/ui/EditableContent.vue +82 -0
- package/src/components/ui/ErrorLogs.vue +19 -0
- package/src/components/ui/ErrorLogsModal.vue +48 -0
- package/src/components/{lib/AGErrorMessage.vue → ui/ErrorMessage.vue} +2 -3
- package/src/components/ui/ErrorReportModal.vue +18 -7
- package/src/components/ui/ErrorReportModalButtons.vue +6 -8
- package/src/components/ui/ErrorReportModalTitle.vue +1 -1
- package/src/components/ui/Input.vue +11 -7
- package/src/components/ui/Link.vue +2 -2
- package/src/components/ui/LoadingModal.vue +8 -6
- package/src/components/ui/Markdown.vue +41 -6
- package/src/components/ui/Modal.vue +95 -19
- package/src/components/ui/ModalContext.vue +2 -1
- package/src/components/ui/ProgressBar.vue +9 -8
- package/src/components/ui/PromptModal.vue +11 -8
- package/src/components/ui/Select.vue +27 -0
- package/src/components/ui/SelectLabel.vue +21 -0
- package/src/components/ui/SelectOption.vue +29 -0
- package/src/components/ui/SelectOptions.vue +35 -0
- package/src/components/ui/SelectTrigger.vue +29 -0
- package/src/components/ui/Setting.vue +31 -0
- package/src/components/ui/SettingsModal.vue +15 -0
- package/src/components/ui/Switch.vue +11 -0
- package/src/components/ui/TextArea.vue +56 -0
- package/src/components/ui/Toast.vue +46 -0
- package/src/components/ui/index.ts +19 -0
- package/src/directives/measure.ts +11 -5
- package/src/errors/Errors.ts +21 -19
- package/src/errors/index.ts +6 -2
- package/src/errors/settings/Debug.vue +32 -0
- package/src/errors/settings/index.ts +10 -0
- package/src/forms/FormController.test.ts +32 -9
- package/src/forms/FormController.ts +27 -22
- package/src/forms/index.ts +0 -1
- package/src/forms/utils.ts +34 -34
- package/src/index.css +70 -3
- package/src/lang/index.ts +5 -1
- package/src/lang/settings/Language.vue +48 -0
- package/src/lang/settings/index.ts +10 -0
- package/src/services/App.state.ts +11 -1
- package/src/services/App.ts +9 -1
- package/src/services/Events.test.ts +8 -8
- package/src/services/Events.ts +2 -8
- package/src/services/index.ts +5 -2
- package/src/testing/index.ts +4 -0
- package/src/ui/UI.state.ts +5 -15
- package/src/ui/UI.ts +115 -99
- package/src/ui/index.ts +18 -19
- package/src/utils/classes.ts +41 -0
- package/src/utils/composition/events.ts +2 -4
- package/src/utils/composition/forms.ts +16 -1
- package/src/utils/composition/state.ts +11 -2
- package/src/utils/index.ts +3 -1
- package/src/utils/markdown.ts +35 -1
- package/src/utils/types.ts +3 -0
- package/src/utils/vue.ts +28 -129
- package/src/components/AppSnackbars.vue +0 -13
- package/src/components/composition.ts +0 -23
- package/src/components/constants.ts +0 -8
- package/src/components/contracts/shared.ts +0 -9
- package/src/components/forms/AGSelect.story.vue +0 -46
- package/src/components/forms/AGSelect.vue +0 -54
- package/src/components/forms/index.ts +0 -1
- package/src/components/headless/forms/AGHeadlessSelect.ts +0 -42
- package/src/components/headless/forms/AGHeadlessSelect.vue +0 -77
- package/src/components/headless/forms/AGHeadlessSelectOption.ts +0 -4
- package/src/components/headless/forms/AGHeadlessSelectOption.vue +0 -31
- package/src/components/headless/forms/AGHeadlessSelectOptions.vue +0 -19
- package/src/components/headless/forms/AGHeadlessSelectTrigger.vue +0 -25
- package/src/components/headless/forms/composition.ts +0 -10
- package/src/components/headless/forms/index.ts +0 -8
- package/src/components/headless/snackbars/AGHeadlessSnackbar.vue +0 -10
- package/src/components/headless/snackbars/index.ts +0 -40
- package/src/components/lib/AGMeasured.vue +0 -16
- package/src/components/lib/index.ts +0 -3
- package/src/components/snackbars/AGSnackbar.vue +0 -38
- package/src/components/snackbars/index.ts +0 -3
- package/src/components/utils.ts +0 -107
- package/src/forms/composition.ts +0 -6
- package/src/utils/tailwindcss.test.ts +0 -26
- package/src/utils/tailwindcss.ts +0 -7
- package/src/utils/vdom.ts +0 -31
- /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 {
|
|
3
|
-
import type { Directive, InjectionKey, MaybeRef,
|
|
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
|
-
|
|
14
|
-
return {
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
required: true,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
50
|
+
if (node.type === Text || node.type === Static) {
|
|
51
|
+
return node.children as string;
|
|
52
|
+
}
|
|
140
53
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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,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,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,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>
|