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

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 (79) hide show
  1. package/dist/aerogel-core.d.ts +1025 -429
  2. package/dist/aerogel-core.js +1601 -1440
  3. package/dist/aerogel-core.js.map +1 -1
  4. package/package.json +1 -1
  5. package/src/components/AppModals.vue +1 -1
  6. package/src/components/AppOverlays.vue +2 -7
  7. package/src/components/AppToasts.vue +16 -0
  8. package/src/components/contracts/Button.ts +1 -0
  9. package/src/components/contracts/DropdownMenu.ts +11 -0
  10. package/src/components/contracts/Input.ts +4 -4
  11. package/src/components/contracts/Modal.ts +4 -0
  12. package/src/components/contracts/Select.ts +33 -0
  13. package/src/components/contracts/Toast.ts +13 -0
  14. package/src/components/contracts/index.ts +2 -0
  15. package/src/components/headless/HeadlessButton.vue +7 -2
  16. package/src/components/headless/HeadlessInputDescription.vue +1 -1
  17. package/src/components/headless/HeadlessInputInput.vue +3 -3
  18. package/src/components/headless/HeadlessInputTextArea.vue +2 -2
  19. package/src/components/headless/HeadlessModal.vue +4 -2
  20. package/src/components/headless/HeadlessModalOverlay.vue +2 -2
  21. package/src/components/headless/HeadlessModalTitle.vue +2 -2
  22. package/src/components/headless/HeadlessSelect.vue +92 -0
  23. package/src/components/headless/{forms/AGHeadlessSelectError.vue → HeadlessSelectError.vue} +3 -4
  24. package/src/components/headless/HeadlessSelectLabel.vue +25 -0
  25. package/src/components/headless/HeadlessSelectOption.vue +34 -0
  26. package/src/components/headless/HeadlessSelectOptions.vue +30 -0
  27. package/src/components/headless/HeadlessSelectTrigger.vue +22 -0
  28. package/src/components/headless/HeadlessSelectValue.vue +15 -0
  29. package/src/components/headless/HeadlessToast.vue +18 -0
  30. package/src/components/headless/HeadlessToastAction.vue +13 -0
  31. package/src/components/headless/index.ts +7 -3
  32. package/src/components/index.ts +2 -8
  33. package/src/components/ui/AdvancedOptions.vue +18 -0
  34. package/src/components/ui/AlertModal.vue +1 -1
  35. package/src/components/ui/Button.vue +54 -14
  36. package/src/components/ui/Checkbox.vue +17 -10
  37. package/src/components/ui/ConfirmModal.vue +3 -3
  38. package/src/components/ui/DropdownMenu.vue +33 -0
  39. package/src/components/ui/EditableContent.vue +82 -0
  40. package/src/components/{lib/AGErrorMessage.vue → ui/ErrorMessage.vue} +2 -3
  41. package/src/components/ui/ErrorReportModal.vue +1 -1
  42. package/src/components/ui/ErrorReportModalButtons.vue +6 -8
  43. package/src/components/ui/ErrorReportModalTitle.vue +1 -1
  44. package/src/components/ui/Input.vue +8 -4
  45. package/src/components/ui/Link.vue +2 -2
  46. package/src/components/ui/LoadingModal.vue +3 -3
  47. package/src/components/ui/Markdown.vue +10 -3
  48. package/src/components/ui/Modal.vue +23 -8
  49. package/src/components/ui/PromptModal.vue +4 -4
  50. package/src/components/ui/Select.vue +53 -0
  51. package/src/components/ui/Toast.vue +42 -0
  52. package/src/components/ui/index.ts +7 -0
  53. package/src/components/utils.ts +4 -4
  54. package/src/errors/Errors.ts +4 -5
  55. package/src/index.css +33 -0
  56. package/src/ui/UI.state.ts +2 -2
  57. package/src/ui/UI.ts +12 -20
  58. package/src/ui/index.ts +4 -4
  59. package/src/utils/vue.ts +0 -4
  60. package/src/components/AppSnackbars.vue +0 -13
  61. package/src/components/constants.ts +0 -8
  62. package/src/components/forms/AGSelect.story.vue +0 -46
  63. package/src/components/forms/AGSelect.vue +0 -54
  64. package/src/components/forms/index.ts +0 -1
  65. package/src/components/headless/forms/AGHeadlessSelect.ts +0 -42
  66. package/src/components/headless/forms/AGHeadlessSelect.vue +0 -77
  67. package/src/components/headless/forms/AGHeadlessSelectOption.ts +0 -4
  68. package/src/components/headless/forms/AGHeadlessSelectOption.vue +0 -31
  69. package/src/components/headless/forms/AGHeadlessSelectOptions.vue +0 -19
  70. package/src/components/headless/forms/AGHeadlessSelectTrigger.vue +0 -25
  71. package/src/components/headless/forms/composition.ts +0 -10
  72. package/src/components/headless/forms/index.ts +0 -8
  73. package/src/components/headless/snackbars/AGHeadlessSnackbar.vue +0 -10
  74. package/src/components/headless/snackbars/index.ts +0 -40
  75. package/src/components/lib/AGMeasured.vue +0 -16
  76. package/src/components/lib/index.ts +0 -3
  77. package/src/components/snackbars/AGSnackbar.vue +0 -38
  78. package/src/components/snackbars/index.ts +0 -3
  79. /package/src/components/{lib/AGStartupCrash.vue → ui/StartupCrash.vue} +0 -0
@@ -1,7 +1,11 @@
1
1
  export { default as AlertModal } from './AlertModal.vue';
2
+ export { default as AdvancedOptions } from './AdvancedOptions.vue';
2
3
  export { default as Button } from './Button.vue';
3
4
  export { default as Checkbox } from './Checkbox.vue';
4
5
  export { default as ConfirmModal } from './ConfirmModal.vue';
6
+ export { default as DropdownMenu } from './DropdownMenu.vue';
7
+ export { default as EditableContent } from './EditableContent.vue';
8
+ export { default as ErrorMessage } from './ErrorMessage.vue';
5
9
  export { default as ErrorReportModal } from './ErrorReportModal.vue';
6
10
  export { default as ErrorReportModalButtons } from './ErrorReportModalButtons.vue';
7
11
  export { default as ErrorReportModalTitle } from './ErrorReportModalTitle.vue';
@@ -14,3 +18,6 @@ export { default as Modal } from './Modal.vue';
14
18
  export { default as ModalContext } from './ModalContext.vue';
15
19
  export { default as ProgressBar } from './ProgressBar.vue';
16
20
  export { default as PromptModal } from './PromptModal.vue';
21
+ export { default as Select } from './Select.vue';
22
+ export { default as StartupCrash } from './StartupCrash.vue';
23
+ export { default as Toast } from './Toast.vue';
@@ -13,10 +13,10 @@ import type { FormController } from '@aerogel/core/forms';
13
13
  export type CVAConfig<T> = NonNullable<GetClosureArgs<typeof cva<T>>[1]>;
14
14
  export type CVAProps<T> = NonNullable<GetClosureArgs<GetClosureResult<typeof cva<T>>>[0]>;
15
15
  export type RefsObject<T> = { [K in keyof T]: Ref<T[K]> | T[K] };
16
- export type Variants<T extends Record<string, string>> = Required<{
17
- [K in keyof T]: {
18
- [key in T[K]]: string;
19
- };
16
+ export type Variants<T extends Record<string, string | boolean>> = Required<{
17
+ [K in keyof T]: Exclude<T[K], undefined> extends string
18
+ ? { [key in Exclude<T[K], undefined>]: string | null }
19
+ : { true: string | null; false: string | null };
20
20
  }>;
21
21
 
22
22
  export type ComponentPropDefinitions<T> = {
@@ -4,7 +4,6 @@ import App from '@aerogel/core/services/App';
4
4
  import ServiceBootError from '@aerogel/core/errors/ServiceBootError';
5
5
  import UI, { UIComponents } from '@aerogel/core/ui/UI';
6
6
  import { translateWithDefault } from '@aerogel/core/lang/utils';
7
- import { Colors } from '@aerogel/core/components/constants';
8
7
  import { Events } from '@aerogel/core/services';
9
8
  import type { ErrorReportModalProps } from '@aerogel/core/components/contracts/ErrorReportModal';
10
9
  import type { ModalComponent } from '@aerogel/core/ui/UI.state';
@@ -71,16 +70,16 @@ export class ErrorsService extends Service {
71
70
  date: new Date(),
72
71
  };
73
72
 
74
- UI.showSnackbar(
73
+ UI.toast(
75
74
  message ??
76
75
  translateWithDefault('errors.notice', 'Something went wrong, but it\'s not your fault. Try again!'),
77
76
  {
78
- color: Colors.Danger,
77
+ variant: 'danger',
79
78
  actions: [
80
79
  {
81
- text: translateWithDefault('errors.viewDetails', 'View details'),
80
+ label: translateWithDefault('errors.viewDetails', 'View details'),
82
81
  dismiss: true,
83
- handler: () =>
82
+ click: () =>
84
83
  UI.openModal<ModalComponent<ErrorReportModalProps>>(
85
84
  UI.requireComponent(UIComponents.ErrorReportModal),
86
85
  { reports: [report] },
package/src/index.css CHANGED
@@ -1,4 +1,6 @@
1
1
  @source './';
2
+ @source '../../plugin-local-first/src';
3
+ @source '../../plugin-solid/src';
2
4
 
3
5
  @theme {
4
6
  --color-background: oklch(1 0 0);
@@ -6,3 +8,34 @@
6
8
  --color-danger: oklch(0.577 0.245 27.325);
7
9
  --color-accent: oklch(0.97 0 0);
8
10
  }
11
+
12
+ .clickable {
13
+ position: relative;
14
+ }
15
+
16
+ .clickable::after {
17
+ --clickable-size: 44px;
18
+ --clickable-inset-by: min(0px, calc((100% - var(--clickable-size)) / 2));
19
+
20
+ content: '';
21
+ position: absolute;
22
+ top: var(--clickable-inset-by);
23
+ left: var(--clickable-inset-by);
24
+ right: var(--clickable-inset-by);
25
+ bottom: var(--clickable-inset-by);
26
+ }
27
+
28
+ input[type='number'].appearance-textfield {
29
+ appearance: textfield;
30
+ }
31
+
32
+ input[type='number'].appearance-textfield::-webkit-outer-spin-button,
33
+ input[type='number'].appearance-textfield::-webkit-inner-spin-button {
34
+ appearance: none;
35
+ }
36
+
37
+ button[data-markdown-action] {
38
+ color: var(--tw-prose-links);
39
+ text-decoration: underline;
40
+ font-weight: 500;
41
+ }
@@ -24,7 +24,7 @@ export interface ModalComponent<
24
24
  Result = unknown,
25
25
  > {}
26
26
 
27
- export interface Snackbar {
27
+ export interface UIToast {
28
28
  id: string;
29
29
  component: Component;
30
30
  properties: Record<string, unknown>;
@@ -34,7 +34,7 @@ export default defineServiceState({
34
34
  name: 'ui',
35
35
  initialState: {
36
36
  modals: [] as UIModal[],
37
- snackbars: [] as Snackbar[],
37
+ toasts: [] as UIToast[],
38
38
  layout: getCurrentLayout(),
39
39
  },
40
40
  computed: {
package/src/ui/UI.ts CHANGED
@@ -11,11 +11,11 @@ import type { ButtonVariant } from '@aerogel/core/components/contracts/Button';
11
11
  import type { ConfirmModalCheckboxes, ConfirmModalProps } from '@aerogel/core/components/contracts/ConfirmModal';
12
12
  import type { LoadingModalProps } from '@aerogel/core/components/contracts/LoadingModal';
13
13
  import type { PromptModalProps } from '@aerogel/core/components/contracts/PromptModal';
14
- import type { SnackbarAction, SnackbarColor } from '@aerogel/core/components/headless/snackbars';
14
+ import type { ToastAction, ToastVariant } from '@aerogel/core/components/contracts/Toast';
15
15
 
16
16
  import Service from './UI.state';
17
17
  import { MOBILE_BREAKPOINT, getCurrentLayout } from './utils';
18
- import type { ModalComponent, Snackbar, UIModal } from './UI.state';
18
+ import type { ModalComponent, UIModal, UIToast } from './UI.state';
19
19
 
20
20
  interface ModalCallbacks<T = unknown> {
21
21
  willClose(result: T | undefined): void;
@@ -32,7 +32,7 @@ export const UIComponents = {
32
32
  ErrorReportModal: 'error-report-modal',
33
33
  LoadingModal: 'loading-modal',
34
34
  PromptModal: 'prompt-modal',
35
- Snackbar: 'snackbar',
35
+ Toast: 'toast',
36
36
  StartupCrash: 'startup-crash',
37
37
  } as const;
38
38
 
@@ -69,10 +69,10 @@ export type PromptOptions = AcceptRefs<{
69
69
  trim?: boolean;
70
70
  }>;
71
71
 
72
- export interface ShowSnackbarOptions {
72
+ export interface ToastOptions {
73
73
  component?: Component;
74
- color?: SnackbarColor;
75
- actions?: SnackbarAction[];
74
+ variant?: ToastVariant;
75
+ actions?: ToastAction[];
76
76
  }
77
77
 
78
78
  export class UIService extends Service {
@@ -246,23 +246,15 @@ export class UIService extends Service {
246
246
  }
247
247
  }
248
248
 
249
- public showSnackbar(message: string, options: ShowSnackbarOptions = {}): void {
250
- const snackbar: Snackbar = {
249
+ public toast(message: string, options: ToastOptions = {}): void {
250
+ const { component, ...otherOptions } = options;
251
+ const toast: UIToast = {
251
252
  id: uuid(),
252
- properties: { message, ...options },
253
- component: markRaw(options.component ?? this.requireComponent(UIComponents.Snackbar)),
253
+ properties: { message, ...otherOptions },
254
+ component: markRaw(component ?? this.requireComponent(UIComponents.Toast)),
254
255
  };
255
256
 
256
- this.setState('snackbars', this.snackbars.concat(snackbar));
257
-
258
- setTimeout(() => this.hideSnackbar(snackbar.id), 5000);
259
- }
260
-
261
- public hideSnackbar(id: string): void {
262
- this.setState(
263
- 'snackbars',
264
- this.snackbars.filter((snackbar) => snackbar.id !== id),
265
- );
257
+ this.setState('toasts', this.toasts.concat(toast));
266
258
  }
267
259
 
268
260
  public registerComponent(name: UIComponent, component: Component): void {
package/src/ui/index.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import type { Component } from 'vue';
2
2
 
3
- import AGSnackbar from '@aerogel/core/components/snackbars/AGSnackbar.vue';
4
- import AGStartupCrash from '@aerogel/core/components/lib/AGStartupCrash.vue';
5
3
  import AlertModal from '@aerogel/core/components/ui/AlertModal.vue';
6
4
  import ConfirmModal from '@aerogel/core/components/ui/ConfirmModal.vue';
7
5
  import ErrorReportModal from '@aerogel/core/components/ui/ErrorReportModal.vue';
8
6
  import LoadingModal from '@aerogel/core/components/ui/LoadingModal.vue';
9
7
  import PromptModal from '@aerogel/core/components/ui/PromptModal.vue';
8
+ import StartupCrash from '@aerogel/core/components/ui/StartupCrash.vue';
9
+ import Toast from '@aerogel/core/components/ui/Toast.vue';
10
10
  import { bootServices } from '@aerogel/core/services';
11
11
  import { definePlugin } from '@aerogel/core/plugins';
12
12
 
@@ -29,8 +29,8 @@ export default definePlugin({
29
29
  [UIComponents.ErrorReportModal]: ErrorReportModal,
30
30
  [UIComponents.LoadingModal]: LoadingModal,
31
31
  [UIComponents.PromptModal]: PromptModal,
32
- [UIComponents.Snackbar]: AGSnackbar,
33
- [UIComponents.StartupCrash]: AGStartupCrash,
32
+ [UIComponents.Toast]: Toast,
33
+ [UIComponents.StartupCrash]: StartupCrash,
34
34
  };
35
35
 
36
36
  Object.entries({
package/src/utils/vue.ts CHANGED
@@ -24,10 +24,6 @@ export function booleanProp(defaultValue: boolean = false): OptionalProp<boolean
24
24
  };
25
25
  }
26
26
 
27
- export function componentRef<T>(): Ref<UnwrapNestedRefs<T> | undefined> {
28
- return ref<UnwrapNestedRefs<T>>();
29
- }
30
-
31
27
  export function computedAsync<T>(getter: () => Promise<T>): Ref<T | undefined> {
32
28
  const result = ref<T>();
33
29
  const asyncValue = computed(getter);
@@ -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,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,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';