@aerogel/core 0.0.0-next.b3caf219a503ce9b8c65ef1463132c9507f56c0a → 0.0.0-next.b656a964404fbde17d9cce7668722596098e47fd

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 (99) hide show
  1. package/dist/aerogel-core.d.ts +958 -491
  2. package/dist/aerogel-core.js +1987 -1828
  3. package/dist/aerogel-core.js.map +1 -1
  4. package/package.json +5 -2
  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 +5 -5
  11. package/src/components/contracts/Modal.ts +4 -0
  12. package/src/components/contracts/Select.ts +36 -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 +3 -3
  17. package/src/components/headless/HeadlessInputInput.vue +4 -4
  18. package/src/components/headless/HeadlessInputTextArea.vue +3 -3
  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 +104 -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 +4 -9
  33. package/src/components/ui/AdvancedOptions.vue +18 -0
  34. package/src/components/ui/AlertModal.vue +3 -3
  35. package/src/components/ui/Button.vue +56 -16
  36. package/src/components/ui/Checkbox.vue +18 -11
  37. package/src/components/ui/ConfirmModal.vue +4 -4
  38. package/src/components/ui/DropdownMenu.vue +33 -0
  39. package/src/components/ui/EditableContent.vue +82 -0
  40. package/src/components/ui/ErrorMessage.vue +15 -0
  41. package/src/components/ui/ErrorReportModal.vue +3 -3
  42. package/src/components/ui/ErrorReportModalButtons.vue +6 -8
  43. package/src/components/ui/ErrorReportModalTitle.vue +2 -2
  44. package/src/components/ui/Input.vue +9 -5
  45. package/src/components/ui/Link.vue +2 -2
  46. package/src/components/ui/LoadingModal.vue +5 -5
  47. package/src/components/ui/Markdown.vue +69 -0
  48. package/src/components/ui/Modal.vue +26 -11
  49. package/src/components/ui/ProgressBar.vue +1 -1
  50. package/src/components/ui/PromptModal.vue +6 -6
  51. package/src/components/ui/Select.vue +21 -0
  52. package/src/components/ui/SelectLabel.vue +10 -0
  53. package/src/components/ui/SelectOptions.vue +31 -0
  54. package/src/components/ui/SelectTrigger.vue +29 -0
  55. package/src/components/ui/SettingsModal.vue +15 -0
  56. package/src/components/{lib/AGStartupCrash.vue → ui/StartupCrash.vue} +3 -3
  57. package/src/components/ui/Toast.vue +42 -0
  58. package/src/components/ui/index.ts +12 -0
  59. package/src/errors/Errors.ts +4 -5
  60. package/src/index.css +38 -0
  61. package/src/lang/index.ts +3 -0
  62. package/src/lang/settings/Language.vue +48 -0
  63. package/src/lang/settings/index.ts +10 -0
  64. package/src/services/App.state.ts +11 -1
  65. package/src/services/App.ts +3 -0
  66. package/src/services/index.ts +3 -0
  67. package/src/ui/UI.state.ts +2 -2
  68. package/src/ui/UI.ts +12 -20
  69. package/src/ui/index.ts +4 -4
  70. package/src/utils/classes.ts +49 -0
  71. package/src/utils/composition/forms.ts +10 -1
  72. package/src/utils/composition/state.ts +11 -2
  73. package/src/utils/index.ts +2 -1
  74. package/src/utils/vue.ts +22 -128
  75. package/src/components/AppSnackbars.vue +0 -13
  76. package/src/components/composition.ts +0 -23
  77. package/src/components/constants.ts +0 -8
  78. package/src/components/forms/AGSelect.story.vue +0 -46
  79. package/src/components/forms/AGSelect.vue +0 -54
  80. package/src/components/forms/index.ts +0 -1
  81. package/src/components/headless/forms/AGHeadlessSelect.ts +0 -42
  82. package/src/components/headless/forms/AGHeadlessSelect.vue +0 -77
  83. package/src/components/headless/forms/AGHeadlessSelectOption.ts +0 -4
  84. package/src/components/headless/forms/AGHeadlessSelectOption.vue +0 -31
  85. package/src/components/headless/forms/AGHeadlessSelectOptions.vue +0 -19
  86. package/src/components/headless/forms/AGHeadlessSelectTrigger.vue +0 -25
  87. package/src/components/headless/forms/composition.ts +0 -10
  88. package/src/components/headless/forms/index.ts +0 -8
  89. package/src/components/headless/snackbars/AGHeadlessSnackbar.vue +0 -10
  90. package/src/components/headless/snackbars/index.ts +0 -40
  91. package/src/components/lib/AGErrorMessage.vue +0 -16
  92. package/src/components/lib/AGMarkdown.vue +0 -54
  93. package/src/components/lib/AGMeasured.vue +0 -16
  94. package/src/components/lib/index.ts +0 -4
  95. package/src/components/snackbars/AGSnackbar.vue +0 -38
  96. package/src/components/snackbars/index.ts +0 -3
  97. package/src/components/utils.ts +0 -107
  98. package/src/utils/tailwindcss.test.ts +0 -26
  99. package/src/utils/tailwindcss.ts +0 -7
@@ -0,0 +1,18 @@
1
+ <template>
2
+ <details class="group">
3
+ <summary
4
+ class="-ml-2 flex w-[max-content] cursor-pointer items-center rounded-lg py-2 pr-3 pl-1 hover:bg-gray-100 focus-visible:outline focus-visible:outline-gray-700"
5
+ >
6
+ <IconCheveronRight class="size-6 transition-transform group-open:rotate-90" />
7
+ <span>{{ $td('ui.advancedOptions', 'Advanced options') }}</span>
8
+ </summary>
9
+
10
+ <div class="pt-2 pl-4">
11
+ <slot />
12
+ </div>
13
+ </details>
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ import IconCheveronRight from '~icons/zondicons/cheveron-right';
18
+ </script>
@@ -1,12 +1,12 @@
1
1
  <template>
2
- <Modal :title="title">
3
- <AGMarkdown :text="message" />
2
+ <Modal :title>
3
+ <Markdown :text="message" />
4
4
  </Modal>
5
5
  </template>
6
6
 
7
7
  <script setup lang="ts">
8
8
  import Modal from '@aerogel/core/components/ui/Modal.vue';
9
- import AGMarkdown from '@aerogel/core/components/lib/AGMarkdown.vue';
9
+ import Markdown from '@aerogel/core/components/ui/Markdown.vue';
10
10
  import type { AlertModalProps } from '@aerogel/core/components/contracts/AlertModal';
11
11
 
12
12
  defineProps<AlertModalProps>();
@@ -1,38 +1,42 @@
1
1
  <template>
2
- <HeadlessButton :class="renderedClasses" v-bind="props">
2
+ <HeadlessButton :class="renderedClasses" :disabled v-bind="props">
3
3
  <slot />
4
4
  </HeadlessButton>
5
5
  </template>
6
6
 
7
7
  <script setup lang="ts">
8
8
  import HeadlessButton from '@aerogel/core/components/headless/HeadlessButton.vue';
9
- import { computedVariantClasses } from '@aerogel/core/components/utils';
9
+ import { computedVariantClasses } from '@aerogel/core/utils/classes';
10
10
  import type { ButtonProps } from '@aerogel/core/components/contracts/Button';
11
- import type { Variants } from '@aerogel/core/components/utils';
11
+ import type { Variants } from '@aerogel/core/utils/classes';
12
12
 
13
- const { class: baseClasses, size, variant, ...props } = defineProps<ButtonProps>();
13
+ const { class: baseClasses, size, variant, disabled, ...props } = defineProps<ButtonProps>();
14
14
 
15
15
  /* eslint-disable vue/max-len */
16
16
  // prettier-ignore
17
- const renderedClasses = computedVariantClasses<Variants<Pick<ButtonProps, 'size' | 'variant'>>>(
18
- { baseClasses, variant, size },
17
+ const renderedClasses = computedVariantClasses<Variants<Pick<ButtonProps, 'size' | 'variant' | 'disabled'>>>(
18
+ { baseClasses, variant, size, disabled },
19
19
  {
20
- baseClasses: 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2',
20
+ baseClasses: 'flex items-center justify-center gap-1 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2',
21
21
  variants: {
22
22
  variant: {
23
- default: 'bg-primary text-white hover:bg-primary/90 focus-visible:outline-primary',
24
- secondary: 'bg-background text-gray-900 ring-gray-300 hover:bg-accent',
25
- danger: 'bg-danger text-white hover:bg-danger/80 focus-visible:outline-danger',
26
- ghost: 'bg-background hover:bg-accent',
27
- outline: 'bg-background text-primary ring-primary hover:bg-accent',
28
- link: 'text-primary hover:underline',
23
+ default: 'bg-primary text-white focus-visible:outline-primary',
24
+ secondary: 'bg-background text-gray-900 ring-gray-300',
25
+ danger: 'bg-danger text-white focus-visible:outline-danger',
26
+ ghost: 'bg-transparent',
27
+ outline: 'bg-transparent text-primary ring-primary',
28
+ link: 'text-primary',
29
29
  },
30
30
  size: {
31
- small: 'rounded px-2 py-1 text-xs',
32
- default: 'rounded-md px-2.5 py-1.5 text-sm',
33
- large: 'rounded-md px-3 py-2 text-base',
31
+ small: 'text-xs',
32
+ default: 'text-sm',
33
+ large: 'text-base',
34
34
  icon: 'rounded-full p-2.5',
35
35
  },
36
+ disabled: {
37
+ false: null,
38
+ true: 'opacity-50 cursor-not-allowed',
39
+ },
36
40
  },
37
41
  compoundVariants: [
38
42
  {
@@ -47,10 +51,46 @@ const renderedClasses = computedVariantClasses<Variants<Pick<ButtonProps, 'size'
47
51
  variant: ['secondary', 'outline'],
48
52
  class: 'ring-1 ring-inset',
49
53
  },
54
+ {
55
+ variant: ['default', 'secondary', 'danger', 'ghost', 'outline'],
56
+ size: 'small',
57
+ class: 'rounded px-2 py-1',
58
+ },
59
+ {
60
+ variant: ['default', 'secondary', 'danger', 'ghost', 'outline'],
61
+ size: 'default',
62
+ class: 'rounded-md px-2.5 py-1.5',
63
+ },
64
+ {
65
+ variant: ['default', 'secondary', 'danger', 'ghost', 'outline'],
66
+ size: 'large',
67
+ class: 'rounded-md px-3 py-2',
68
+ },
69
+ {
70
+ variant: 'default',
71
+ disabled: false,
72
+ class: 'hover:bg-primary/90',
73
+ },
74
+ {
75
+ variant: ['secondary', 'ghost', 'outline'],
76
+ disabled: false,
77
+ class: 'hover:bg-accent',
78
+ },
79
+ {
80
+ variant: 'danger',
81
+ disabled: false,
82
+ class: 'hover:bg-danger/80',
83
+ },
84
+ {
85
+ variant: 'link',
86
+ disabled: false,
87
+ class: 'hover:underline',
88
+ },
50
89
  ],
51
90
  defaultVariants: {
52
91
  variant: 'default',
53
92
  size: 'default',
93
+ disabled: false,
54
94
  },
55
95
  },
56
96
  );
@@ -8,33 +8,39 @@
8
8
  <div class="flex h-6 items-center">
9
9
  <HeadlessInputInput v-bind="inputAttrs" type="checkbox" :class="renderedInputClasses" />
10
10
  </div>
11
- <div v-if="$slots.default" class="ml-2 text-sm leading-6">
11
+ <div v-if="$slots.default" :class="renderedLabelClasses">
12
12
  <HeadlessInputLabel class="text-gray-900">
13
13
  <slot />
14
14
  </HeadlessInputLabel>
15
- <HeadlessInputError class="text-sm text-red-600" />
15
+ <HeadlessInputError class="text-red-600" />
16
16
  </div>
17
- <div v-else-if="label" class="ml-2 text-sm leading-6">
18
- <HeadlessInputLabel />
19
- <HeadlessInputError class="text-sm text-red-600" />
17
+ <div v-else-if="label" :class="renderedLabelClasses">
18
+ <HeadlessInputLabel class="text-gray-900" />
19
+ <HeadlessInputError class="text-red-600" />
20
20
  </div>
21
21
  </HeadlessInput>
22
22
  </template>
23
23
 
24
24
  <script setup lang="ts">
25
- import { computed } from 'vue';
25
+ import { computed, useTemplateRef } from 'vue';
26
26
  import type { HTMLAttributes } from 'vue';
27
27
 
28
- import { classes } from '@aerogel/core/components/utils';
28
+ import HeadlessInput from '@aerogel/core/components/headless/HeadlessInput.vue';
29
+ import HeadlessInputError from '@aerogel/core/components/headless/HeadlessInputError.vue';
30
+ import HeadlessInputInput from '@aerogel/core/components/headless/HeadlessInputInput.vue';
31
+ import HeadlessInputLabel from '@aerogel/core/components/headless/HeadlessInputLabel.vue';
32
+ import { classes } from '@aerogel/core/utils/classes';
29
33
  import { useInputAttrs } from '@aerogel/core/utils/composition/forms';
30
- import { componentRef } from '@aerogel/core/utils/vue';
31
- import type { InputEmits, InputExpose, InputProps } from '@aerogel/core/components/contracts/Input';
34
+ import type { InputEmits, InputProps } from '@aerogel/core/components/contracts/Input';
32
35
 
33
36
  defineOptions({ inheritAttrs: false });
34
37
  defineEmits<InputEmits>();
35
- const { inputClass, ...props } = defineProps<InputProps & { inputClass?: HTMLAttributes['class'] }>();
36
38
 
37
- const $input = componentRef<InputExpose>();
39
+ const { inputClass, labelClass, ...props } = defineProps<
40
+ InputProps & { inputClass?: HTMLAttributes['class']; labelClass?: HTMLAttributes['class'] }
41
+ >();
42
+
43
+ const $input = useTemplateRef('$input');
38
44
  const [inputAttrs, rootClasses] = useInputAttrs();
39
45
  const renderedClasses = computed(() => classes('relative flex items-start', rootClasses.value));
40
46
  const renderedInputClasses = computed(() =>
@@ -46,4 +52,5 @@ const renderedInputClasses = computed(() =>
46
52
  },
47
53
  inputClass,
48
54
  ));
55
+ const renderedLabelClasses = computed(() => classes('ml-2 text-sm leading-6', labelClass));
49
56
  </script>
@@ -1,7 +1,7 @@
1
1
  <template>
2
- <Modal v-slot="{ close }" :title="title" persistent>
3
- <Form :form="form" @submit="close([true, form.data()])">
4
- <AGMarkdown :text="message" :actions="actions" />
2
+ <Modal v-slot="{ close }" :title persistent>
3
+ <Form :form @submit="close([true, form.data()])">
4
+ <Markdown :text="message" :actions />
5
5
 
6
6
  <ul v-if="checkboxes" class="mt-4 flex flex-col text-sm text-gray-600">
7
7
  <li v-for="(checkbox, name) of checkboxes" :key="name">
@@ -31,7 +31,7 @@
31
31
 
32
32
  <script setup lang="ts">
33
33
  import Form from '@aerogel/core/components/ui/Form.vue';
34
- import AGMarkdown from '@aerogel/core/components/lib/AGMarkdown.vue';
34
+ import Markdown from '@aerogel/core/components/ui/Markdown.vue';
35
35
  import Button from '@aerogel/core/components/ui/Button.vue';
36
36
  import Modal from '@aerogel/core/components/ui/Modal.vue';
37
37
  import { useConfirmModal } from '@aerogel/core/components/contracts/ConfirmModal';
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <DropdownMenuRoot>
3
+ <DropdownMenuTrigger>
4
+ <slot />
5
+ </DropdownMenuTrigger>
6
+ <DropdownMenuPortal>
7
+ <DropdownMenuContent class="gap-y-0.5 rounded-lg bg-white p-1.5 shadow-lg ring-1 ring-black/5" :align>
8
+ <DropdownMenuItem
9
+ v-for="(option, key) in options"
10
+ :key
11
+ class="flex w-full items-center rounded-lg px-2 py-2 text-sm text-gray-900 data-[highlighted]:bg-gray-100"
12
+ @select="option.click"
13
+ >
14
+ {{ option.label }}
15
+ </DropdownMenuItem>
16
+ </DropdownMenuContent>
17
+ </DropdownMenuPortal>
18
+ </DropdownMenuRoot>
19
+ </template>
20
+
21
+ <script setup lang="ts">
22
+ import {
23
+ DropdownMenuContent,
24
+ DropdownMenuItem,
25
+ DropdownMenuPortal,
26
+ DropdownMenuRoot,
27
+ DropdownMenuTrigger,
28
+ } from 'reka-ui';
29
+
30
+ import type { DropdownMenuProps } from '@aerogel/core/components/contracts/DropdownMenu';
31
+
32
+ defineProps<DropdownMenuProps>();
33
+ </script>
@@ -0,0 +1,82 @@
1
+ <template>
2
+ <div class="relative" :class="{ 'pointer-events-none!': disabled && !editing }">
3
+ <div v-if="!editing" :class="renderedContentClass">
4
+ <slot />
5
+ </div>
6
+ <span v-else :class="renderedFillerClass">
7
+ {{ draft }}
8
+ </span>
9
+ <span v-if="type === 'number'" class="inline-block transition-[width]" :class="editing ? 'w-5' : 'w-0'" />
10
+ <form class="w-full" :aria-hidden="formAriaHidden" @submit.prevent="$input?.blur()">
11
+ <input
12
+ ref="$input"
13
+ v-model="draft"
14
+ :tabindex="tabindex ?? undefined"
15
+ :aria-label="ariaLabel ?? undefined"
16
+ :type
17
+ :class="[
18
+ renderedInputClass,
19
+ { 'opacity-0': !editing, 'appearance-textfield': !editing && type === 'number' },
20
+ ]"
21
+ @keyup="$emit('update', draft)"
22
+ @focus="startEditing()"
23
+ @blur="stopEditing()"
24
+ >
25
+ </form>
26
+ </div>
27
+ </template>
28
+
29
+ <script setup lang="ts">
30
+ import { computed, ref, watchEffect } from 'vue';
31
+ import type { HTMLAttributes } from 'vue';
32
+
33
+ import { classes } from '@aerogel/core/utils/classes';
34
+
35
+ const emit = defineEmits<{ update: [value: string | number]; save: [] }>();
36
+ const {
37
+ type = 'text',
38
+ text,
39
+ contentClass,
40
+ ariaLabel,
41
+ formAriaHidden,
42
+ tabindex,
43
+ disabled,
44
+ } = defineProps<{
45
+ type?: string;
46
+ contentClass?: HTMLAttributes['class'];
47
+ ariaLabel?: string;
48
+ formAriaHidden?: boolean;
49
+ tabindex?: string;
50
+ text: string;
51
+ disabled?: boolean;
52
+ }>();
53
+ const $input = ref<HTMLElement>();
54
+ const editing = ref<string | null>(null);
55
+ const draft = ref(text);
56
+ const renderedContentClass = computed(() => classes('inline whitespace-pre', contentClass));
57
+ const renderedFillerClass = computed(() => classes('invisible whitespace-pre', contentClass));
58
+ const renderedInputClass = computed(() =>
59
+ classes('absolute inset-0 h-full w-full resize-none border-0 bg-transparent p-0 focus:ring-0', contentClass));
60
+
61
+ function startEditing() {
62
+ editing.value = text;
63
+ }
64
+
65
+ function stopEditing() {
66
+ if (!editing.value) {
67
+ return;
68
+ }
69
+
70
+ if (type !== 'number' && draft.value.trim().length === 0) {
71
+ draft.value = editing.value;
72
+
73
+ emit('update', draft.value);
74
+ }
75
+
76
+ editing.value = null;
77
+
78
+ emit('save');
79
+ }
80
+
81
+ watchEffect(() => (draft.value = text));
82
+ </script>
@@ -0,0 +1,15 @@
1
+ <template>
2
+ <Markdown :text="message" inline />
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import { computed } from 'vue';
7
+
8
+ import { getErrorMessage } from '@aerogel/core/errors/utils';
9
+ import type { ErrorSource } from '@aerogel/core/errors/Errors.state';
10
+
11
+ import Markdown from '@aerogel/core/components/ui/Markdown.vue';
12
+
13
+ const { error } = defineProps<{ error: ErrorSource }>();
14
+ const message = computed(() => getErrorMessage(error));
15
+ </script>
@@ -33,9 +33,9 @@
33
33
  </Button>
34
34
  </span>
35
35
  </div>
36
- <ErrorReportModalButtons :report="report" class="gap-0.5" />
36
+ <ErrorReportModalButtons :report class="gap-0.5" />
37
37
  </h2>
38
- <AGMarkdown v-if="report.description" :text="report.description" class="text-gray-600" />
38
+ <Markdown v-if="report.description" :text="report.description" class="text-gray-600" />
39
39
  </div>
40
40
  <div class="-mt-2 max-h-[80vh] overflow-auto bg-red-800/10">
41
41
  <pre class="p-4 text-xs text-red-800" v-text="details" />
@@ -48,7 +48,7 @@ import IconCheveronLeft from '~icons/zondicons/cheveron-left';
48
48
  import IconCheveronRight from '~icons/zondicons/cheveron-right';
49
49
  import IconExclamationSolid from '~icons/zondicons/exclamation-solid';
50
50
 
51
- import AGMarkdown from '@aerogel/core/components/lib/AGMarkdown.vue';
51
+ import Markdown from '@aerogel/core/components/ui/Markdown.vue';
52
52
  import Button from '@aerogel/core/components/ui/Button.vue';
53
53
  import ErrorReportModalButtons from '@aerogel/core/components/ui/ErrorReportModalButtons.vue';
54
54
  import ErrorReportModalTitle from '@aerogel/core/components/ui/ErrorReportModalTitle.vue';
@@ -7,7 +7,7 @@
7
7
  class="group whitespace-nowrap"
8
8
  :href="button.url"
9
9
  :title="$td(`errors.report_${button.id}`, button.description)"
10
- @click="button.handler"
10
+ @click="button.click"
11
11
  >
12
12
  <span class="sr-only">{{ $td(`errors.report_${button.id}`, button.description) }}</span>
13
13
  <component :is="button.iconComponent" class="size-4" aria-hidden="true" />
@@ -36,7 +36,7 @@ interface ErrorReportModalButtonsDefaultSlotProps {
36
36
  description: string;
37
37
  iconComponent: Component;
38
38
  url?: string;
39
- handler?(): void;
39
+ click?(): void;
40
40
  }
41
41
 
42
42
  defineSlots<{
@@ -75,19 +75,17 @@ const buttons = computed(() =>
75
75
  id: 'clipboard',
76
76
  description: 'Copy to clipboard',
77
77
  iconComponent: IconCopy,
78
- async handler() {
78
+ async click() {
79
79
  await navigator.clipboard.writeText(`${summary.value}\n\n${props.report.details}`);
80
80
 
81
- UI.showSnackbar(
82
- translateWithDefault('errors.copiedToClipboard', 'Debug information copied to clipboard'),
83
- );
81
+ UI.toast(translateWithDefault('errors.copiedToClipboard', 'Debug information copied to clipboard'));
84
82
  },
85
83
  },
86
84
  {
87
85
  id: 'console',
88
86
  description: 'Log to console',
89
87
  iconComponent: IconConsole,
90
- handler() {
88
+ click() {
91
89
  const error = props.report.error ?? props.report;
92
90
 
93
91
  (window as { error?: unknown }).error = error;
@@ -95,7 +93,7 @@ const buttons = computed(() =>
95
93
  // eslint-disable-next-line no-console
96
94
  console.error(error);
97
95
 
98
- UI.showSnackbar(
96
+ UI.toast(
99
97
  translateWithDefault(
100
98
  'errors.addedToConsole',
101
99
  'You can now use the **error** variable in the console',
@@ -1,11 +1,11 @@
1
1
  <template>
2
- <AGMarkdown :text="text" inline />
2
+ <Markdown :text inline />
3
3
  </template>
4
4
 
5
5
  <script setup lang="ts">
6
6
  import { computed } from 'vue';
7
7
 
8
- import AGMarkdown from '@aerogel/core/components/lib/AGMarkdown.vue';
8
+ import Markdown from '@aerogel/core/components/ui/Markdown.vue';
9
9
  import type { ErrorReport } from '@aerogel/core/errors';
10
10
 
11
11
  const { totalReports, currentReport, report } = defineProps<{
@@ -21,20 +21,24 @@
21
21
  <script setup lang="ts">
22
22
  import IconExclamationSolid from '~icons/zondicons/exclamation-solid';
23
23
 
24
- import { computed } from 'vue';
24
+ import { computed, useTemplateRef } from 'vue';
25
25
  import type { HTMLAttributes } from 'vue';
26
26
 
27
- import { classes } from '@aerogel/core/components/utils';
27
+ import HeadlessInput from '@aerogel/core/components/headless/HeadlessInput.vue';
28
+ import HeadlessInputLabel from '@aerogel/core/components/headless/HeadlessInputLabel.vue';
29
+ import HeadlessInputInput from '@aerogel/core/components/headless/HeadlessInputInput.vue';
30
+ import HeadlessInputDescription from '@aerogel/core/components/headless/HeadlessInputDescription.vue';
31
+ import HeadlessInputError from '@aerogel/core/components/headless/HeadlessInputError.vue';
32
+ import { classes } from '@aerogel/core/utils/classes';
28
33
  import { useInputAttrs } from '@aerogel/core/utils/composition/forms';
29
- import { componentRef } from '@aerogel/core/utils/vue';
30
- import type { InputEmits, InputExpose, InputProps } from '@aerogel/core/components/contracts/Input';
34
+ import type { InputEmits, InputProps } from '@aerogel/core/components/contracts/Input';
31
35
 
32
36
  defineOptions({ inheritAttrs: false });
33
37
  defineEmits<InputEmits>();
34
38
  const { label, inputClass, wrapperClass, ...props } = defineProps<
35
39
  InputProps & { inputClass?: HTMLAttributes['class']; wrapperClass?: HTMLAttributes['class'] }
36
40
  >();
37
- const $input = componentRef<InputExpose>();
41
+ const $input = useTemplateRef('$input');
38
42
  const [inputAttrs, rootClasses] = useInputAttrs();
39
43
  const renderedWrapperClasses = computed(() =>
40
44
  classes('relative rounded-md shadow-2xs', { 'mt-1': label }, wrapperClass));
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <Button variant="link" v-bind="props">
2
+ <Button variant="link" v-bind="$props">
3
3
  <slot />
4
4
  </Button>
5
5
  </template>
@@ -8,5 +8,5 @@
8
8
  import Button from '@aerogel/core/components/ui/Button.vue';
9
9
  import type { ButtonProps } from '@aerogel/core/components/contracts/Button';
10
10
 
11
- const props = defineProps<Omit<ButtonProps, 'variant'>>();
11
+ defineProps<Omit<ButtonProps, 'variant'>>();
12
12
  </script>
@@ -3,24 +3,24 @@
3
3
  persistent
4
4
  class="flex"
5
5
  wrapper-class="w-auto"
6
- :title="title"
6
+ :title
7
7
  :class="{ 'flex-col-reverse': showProgress, 'items-center justify-center gap-2': !showProgress }"
8
8
  >
9
9
  <ProgressBar
10
10
  v-if="showProgress"
11
- :progress="progress"
12
- :job="job"
11
+ :progress
12
+ :job
13
13
  class="min-w-[min(400px,80vw)]"
14
14
  />
15
15
  <IconSpinner v-else class="text-primary mr-1 size-6" />
16
- <AGMarkdown :text="renderedMessage" />
16
+ <Markdown :text="renderedMessage" />
17
17
  </Modal>
18
18
  </template>
19
19
 
20
20
  <script setup lang="ts">
21
21
  import IconSpinner from '~icons/svg-spinners/90-ring-with-bg';
22
22
 
23
- import AGMarkdown from '@aerogel/core/components/lib/AGMarkdown.vue';
23
+ import Markdown from '@aerogel/core/components/ui/Markdown.vue';
24
24
  import Modal from '@aerogel/core/components/ui/Modal.vue';
25
25
  import ProgressBar from '@aerogel/core/components/ui/ProgressBar.vue';
26
26
  import { useLoadingModal } from '@aerogel/core/components/contracts/LoadingModal';
@@ -0,0 +1,69 @@
1
+ <template>
2
+ <root />
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import { computed, h, useAttrs } from 'vue';
7
+ import { isInstanceOf } from '@noeldemartin/utils';
8
+ import type { VNode } from 'vue';
9
+
10
+ import { renderMarkdown } from '@aerogel/core/utils/markdown';
11
+ import { translate, translateWithDefault } from '@aerogel/core/lang';
12
+ import { renderVNode } from '@aerogel/core/utils/vue';
13
+
14
+ const { as, inline, langKey, langParams, langDefault, text, actions } = defineProps<{
15
+ as?: string;
16
+ inline?: boolean;
17
+ langKey?: string;
18
+ langParams?: number | Record<string, unknown>;
19
+ langDefault?: string;
20
+ text?: string;
21
+ actions?: Record<string, () => unknown>;
22
+ }>();
23
+
24
+ const attrs = useAttrs();
25
+ const slots = defineSlots<{ default?(): VNode[] }>();
26
+ const markdown = computed(() => {
27
+ if (slots.default) {
28
+ return slots.default().map(renderVNode).join('');
29
+ }
30
+
31
+ return (
32
+ text ??
33
+ (langKey &&
34
+ (langDefault
35
+ ? translateWithDefault(langKey, langDefault, langParams ?? {})
36
+ : translate(langKey, langParams ?? {})))
37
+ );
38
+ });
39
+ const html = computed(() => {
40
+ if (!markdown.value) {
41
+ return null;
42
+ }
43
+
44
+ let renderedHtml = renderMarkdown(markdown.value);
45
+
46
+ if (inline) {
47
+ renderedHtml = renderedHtml.replace('<p>', '<span>').replace('</p>', '</span>');
48
+ }
49
+
50
+ return renderedHtml;
51
+ });
52
+ const root = () =>
53
+ h(as ?? (inline ? 'span' : 'div'), {
54
+ innerHTML: html.value,
55
+ onClick,
56
+ ...attrs,
57
+ class: `${attrs.class ?? ''} ${inline ? '' : 'prose'}`,
58
+ });
59
+
60
+ async function onClick(event: Event) {
61
+ const { target } = event;
62
+
63
+ if (isInstanceOf(target, HTMLElement) && target.dataset.markdownAction) {
64
+ actions?.[target.dataset.markdownAction]?.();
65
+
66
+ return;
67
+ }
68
+ }
69
+ </script>