@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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Modal :title="title">
|
|
2
|
+
<Modal :title="renderedTitle" :title-hidden="titleHidden">
|
|
3
3
|
<Markdown :text="message" />
|
|
4
4
|
</Modal>
|
|
5
5
|
</template>
|
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
<script setup lang="ts">
|
|
8
8
|
import Modal from '@aerogel/core/components/ui/Modal.vue';
|
|
9
9
|
import Markdown from '@aerogel/core/components/ui/Markdown.vue';
|
|
10
|
-
import
|
|
10
|
+
import { useAlertModal } from '@aerogel/core/components/contracts/AlertModal';
|
|
11
|
+
import type { AlertModalExpose, AlertModalProps } from '@aerogel/core/components/contracts/AlertModal';
|
|
11
12
|
|
|
12
|
-
defineProps<AlertModalProps>();
|
|
13
|
+
const props = defineProps<AlertModalProps>();
|
|
14
|
+
const { renderedTitle, titleHidden } = useAlertModal(props);
|
|
15
|
+
|
|
16
|
+
defineExpose<AlertModalExpose>();
|
|
13
17
|
</script>
|
|
@@ -1,43 +1,49 @@
|
|
|
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
|
+
import { computed } from 'vue';
|
|
9
|
+
|
|
8
10
|
import HeadlessButton from '@aerogel/core/components/headless/HeadlessButton.vue';
|
|
9
|
-
import {
|
|
11
|
+
import { variantClasses } from '@aerogel/core/utils/classes';
|
|
10
12
|
import type { ButtonProps } from '@aerogel/core/components/contracts/Button';
|
|
11
|
-
import type { Variants } from '@aerogel/core/
|
|
13
|
+
import type { Variants } from '@aerogel/core/utils/classes';
|
|
12
14
|
|
|
13
|
-
const { class: baseClasses, size, variant, ...props } = defineProps<ButtonProps>();
|
|
15
|
+
const { class: baseClasses, size, variant, disabled, ...props } = defineProps<ButtonProps>();
|
|
14
16
|
|
|
15
17
|
/* eslint-disable vue/max-len */
|
|
16
18
|
// prettier-ignore
|
|
17
|
-
const renderedClasses =
|
|
18
|
-
{ baseClasses, variant, size },
|
|
19
|
+
const renderedClasses = computed(() => variantClasses<Variants<Pick<ButtonProps, 'size' | 'variant' | 'disabled'>>>(
|
|
20
|
+
{ baseClasses, variant, size, disabled },
|
|
19
21
|
{
|
|
20
22
|
baseClasses: 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2',
|
|
21
23
|
variants: {
|
|
22
24
|
variant: {
|
|
23
|
-
default: 'bg-primary text-white
|
|
24
|
-
secondary: 'bg-background text-gray-900 ring-gray-300
|
|
25
|
-
danger: 'bg-
|
|
26
|
-
ghost: 'bg-
|
|
27
|
-
outline: 'bg-
|
|
28
|
-
link: 'text-
|
|
25
|
+
default: 'bg-primary-600 text-white focus-visible:outline-primary-600',
|
|
26
|
+
secondary: 'bg-background text-gray-900 ring-gray-300',
|
|
27
|
+
danger: 'bg-red-600 text-white focus-visible:outline-red-600',
|
|
28
|
+
ghost: 'bg-transparent',
|
|
29
|
+
outline: 'bg-transparent text-primary-600 ring-primary-600',
|
|
30
|
+
link: 'text-links',
|
|
29
31
|
},
|
|
30
32
|
size: {
|
|
31
|
-
small: '
|
|
32
|
-
default: '
|
|
33
|
-
large: '
|
|
33
|
+
small: 'text-xs min-h-6',
|
|
34
|
+
default: 'text-sm min-h-8',
|
|
35
|
+
large: 'text-base min-h-10',
|
|
34
36
|
icon: 'rounded-full p-2.5',
|
|
35
37
|
},
|
|
38
|
+
disabled: {
|
|
39
|
+
false: null,
|
|
40
|
+
true: 'opacity-50 cursor-not-allowed',
|
|
41
|
+
},
|
|
36
42
|
},
|
|
37
43
|
compoundVariants: [
|
|
38
44
|
{
|
|
39
45
|
variant: ['default', 'secondary', 'danger', 'ghost', 'outline'],
|
|
40
|
-
class: 'font-medium',
|
|
46
|
+
class: 'flex items-center justify-center gap-1 font-medium',
|
|
41
47
|
},
|
|
42
48
|
{
|
|
43
49
|
variant: ['default', 'danger'],
|
|
@@ -47,12 +53,63 @@ const renderedClasses = computedVariantClasses<Variants<Pick<ButtonProps, 'size'
|
|
|
47
53
|
variant: ['secondary', 'outline'],
|
|
48
54
|
class: 'ring-1 ring-inset',
|
|
49
55
|
},
|
|
56
|
+
{
|
|
57
|
+
variant: ['default', 'secondary', 'danger', 'ghost', 'outline'],
|
|
58
|
+
size: 'small',
|
|
59
|
+
class: 'rounded px-2 py-1',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
variant: ['default', 'secondary', 'danger', 'ghost', 'outline'],
|
|
63
|
+
size: 'default',
|
|
64
|
+
class: 'rounded-md px-2.5 py-1.5',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
variant: ['default', 'secondary', 'danger', 'ghost', 'outline'],
|
|
68
|
+
size: 'large',
|
|
69
|
+
class: 'rounded-md px-3 py-2',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
variant: 'default',
|
|
73
|
+
disabled: false,
|
|
74
|
+
class: 'hover:bg-primary-500',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
variant: ['secondary', 'ghost', 'outline'],
|
|
78
|
+
disabled: false,
|
|
79
|
+
class: 'hover:bg-gray-50',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
variant: 'danger',
|
|
83
|
+
disabled: false,
|
|
84
|
+
class: 'hover:bg-red-500',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
variant: 'link',
|
|
88
|
+
disabled: false,
|
|
89
|
+
class: 'hover:underline',
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
variant: 'link',
|
|
93
|
+
size: 'small',
|
|
94
|
+
class: 'leading-6',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
variant: 'link',
|
|
98
|
+
size: 'default',
|
|
99
|
+
class: 'leading-8',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
variant: 'link',
|
|
103
|
+
size: 'large',
|
|
104
|
+
class: 'leading-10',
|
|
105
|
+
},
|
|
50
106
|
],
|
|
51
107
|
defaultVariants: {
|
|
52
108
|
variant: 'default',
|
|
53
109
|
size: 'default',
|
|
110
|
+
disabled: false,
|
|
54
111
|
},
|
|
55
112
|
},
|
|
56
|
-
);
|
|
113
|
+
));
|
|
57
114
|
/* eslint-enable vue/max-len */
|
|
58
115
|
</script>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<HeadlessInput
|
|
3
|
-
ref="$
|
|
3
|
+
ref="$inputRef"
|
|
4
4
|
:class="renderedClasses"
|
|
5
5
|
v-bind="props"
|
|
6
6
|
@update:model-value="$emit('update:modelValue', $event)"
|
|
@@ -8,42 +8,49 @@
|
|
|
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="
|
|
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-
|
|
15
|
+
<HeadlessInputError class="text-red-600" />
|
|
16
16
|
</div>
|
|
17
|
-
<div v-else-if="label" class="
|
|
18
|
-
<HeadlessInputLabel />
|
|
19
|
-
<HeadlessInputError class="text-
|
|
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
|
|
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 {
|
|
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
|
|
39
|
+
const { inputClass, labelClass, ...props } = defineProps<
|
|
40
|
+
InputProps & { inputClass?: HTMLAttributes['class']; labelClass?: HTMLAttributes['class'] }
|
|
41
|
+
>();
|
|
42
|
+
|
|
43
|
+
const $input = useTemplateRef('$inputRef');
|
|
38
44
|
const [inputAttrs, rootClasses] = useInputAttrs();
|
|
39
45
|
const renderedClasses = computed(() => classes('relative flex items-start', rootClasses.value));
|
|
40
46
|
const renderedInputClasses = computed(() =>
|
|
41
47
|
classes(
|
|
42
|
-
'size-4 rounded text-primary hover:bg-gray-200 checked:hover:
|
|
48
|
+
'size-4 rounded text-primary-600 not-checked:hover:bg-gray-200 checked:hover:text-primary-500 checked:border-0',
|
|
43
49
|
{
|
|
44
|
-
'border-gray-300 focus:ring-primary': !$input.value?.errors,
|
|
50
|
+
'border-gray-300 focus:ring-primary-600': !$input.value?.errors,
|
|
45
51
|
'border-red-400 border-2 focus:ring-red-600': $input.value?.errors,
|
|
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,13 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
<!-- @vue-generic {import('@aerogel/core/ui/UI').ModalExposeResult<ConfirmModalExpose>} -->
|
|
3
|
+
<Modal
|
|
4
|
+
v-slot="{ close }"
|
|
5
|
+
:title="renderedTitle"
|
|
6
|
+
:title-hidden="titleHidden"
|
|
7
|
+
persistent
|
|
8
|
+
>
|
|
9
|
+
<Form :form @submit="close([true, form.data()])">
|
|
10
|
+
<Markdown :text="message" :actions />
|
|
5
11
|
|
|
6
12
|
<ul v-if="checkboxes" class="mt-4 flex flex-col text-sm text-gray-600">
|
|
7
13
|
<li v-for="(checkbox, name) of checkboxes" :key="name">
|
|
@@ -10,7 +16,7 @@
|
|
|
10
16
|
v-model="form[name]"
|
|
11
17
|
type="checkbox"
|
|
12
18
|
:required="checkbox.required"
|
|
13
|
-
class="border-primary text-primary hover:bg-primary
|
|
19
|
+
class="border-primary-600 text-primary-600 hover:bg-primary-50 hover:checked:bg-primary-500 focus:ring-primary-600 focus-visible:ring-primary-600 rounded border-2"
|
|
14
20
|
>
|
|
15
21
|
<span class="ml-1.5">{{ checkbox.label }}</span>
|
|
16
22
|
</label>
|
|
@@ -35,8 +41,10 @@ import Markdown from '@aerogel/core/components/ui/Markdown.vue';
|
|
|
35
41
|
import Button from '@aerogel/core/components/ui/Button.vue';
|
|
36
42
|
import Modal from '@aerogel/core/components/ui/Modal.vue';
|
|
37
43
|
import { useConfirmModal } from '@aerogel/core/components/contracts/ConfirmModal';
|
|
38
|
-
import type { ConfirmModalProps } from '@aerogel/core/components/contracts/ConfirmModal';
|
|
44
|
+
import type { ConfirmModalExpose, ConfirmModalProps } from '@aerogel/core/components/contracts/ConfirmModal';
|
|
39
45
|
|
|
40
46
|
const { cancelVariant = 'secondary', ...props } = defineProps<ConfirmModalProps>();
|
|
41
|
-
const { form, renderedAcceptText, renderedCancelText } = useConfirmModal(props);
|
|
47
|
+
const { form, renderedTitle, titleHidden, renderedAcceptText, renderedCancelText } = useConfirmModal(props);
|
|
48
|
+
|
|
49
|
+
defineExpose<ConfirmModalExpose>();
|
|
42
50
|
</script>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<DropdownMenuRoot>
|
|
3
|
+
<DropdownMenuTrigger>
|
|
4
|
+
<slot />
|
|
5
|
+
</DropdownMenuTrigger>
|
|
6
|
+
<DropdownMenuPortal>
|
|
7
|
+
<slot name="options">
|
|
8
|
+
<DropdownMenuOptions />
|
|
9
|
+
</slot>
|
|
10
|
+
</DropdownMenuPortal>
|
|
11
|
+
</DropdownMenuRoot>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup lang="ts">
|
|
15
|
+
import { DropdownMenuPortal, DropdownMenuRoot, DropdownMenuTrigger } from 'reka-ui';
|
|
16
|
+
import { computed, provide } from 'vue';
|
|
17
|
+
import type { AcceptRefs } from '@aerogel/core/utils';
|
|
18
|
+
|
|
19
|
+
import type { DropdownMenuExpose, DropdownMenuProps } from '@aerogel/core/components/contracts/DropdownMenu';
|
|
20
|
+
|
|
21
|
+
import DropdownMenuOptions from './DropdownMenuOptions.vue';
|
|
22
|
+
|
|
23
|
+
const { align, side, options } = defineProps<DropdownMenuProps>();
|
|
24
|
+
const expose = {
|
|
25
|
+
align,
|
|
26
|
+
side,
|
|
27
|
+
options: computed(() => options?.filter(Boolean)),
|
|
28
|
+
} satisfies AcceptRefs<DropdownMenuExpose>;
|
|
29
|
+
|
|
30
|
+
provide('dropdown-menu', expose);
|
|
31
|
+
defineExpose(expose);
|
|
32
|
+
</script>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<DropdownMenuItem :class="renderedClasses" v-bind="props" @select="$emit('select')">
|
|
3
|
+
<slot />
|
|
4
|
+
</DropdownMenuItem>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { classes } from '@aerogel/core/utils';
|
|
9
|
+
import { computed } from 'vue';
|
|
10
|
+
import { DropdownMenuItem } from 'reka-ui';
|
|
11
|
+
import type { HTMLAttributes } from 'vue';
|
|
12
|
+
import type { PrimitiveProps } from 'reka-ui';
|
|
13
|
+
|
|
14
|
+
defineEmits<{ select: [] }>();
|
|
15
|
+
|
|
16
|
+
const { class: rootClass, ...props } = defineProps<{ class?: HTMLAttributes['class'] } & PrimitiveProps>();
|
|
17
|
+
const renderedClasses = computed(() =>
|
|
18
|
+
classes(
|
|
19
|
+
'flex w-full items-center gap-2 rounded-lg px-2 py-2 text-sm text-gray-900 data-[highlighted]:bg-gray-100',
|
|
20
|
+
rootClass,
|
|
21
|
+
));
|
|
22
|
+
</script>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<DropdownMenuContent
|
|
3
|
+
class="gap-y-0.5 rounded-lg bg-white p-1.5 shadow-lg ring-1 ring-black/5"
|
|
4
|
+
:align="dropdownMenu.align"
|
|
5
|
+
:side="dropdownMenu.side"
|
|
6
|
+
>
|
|
7
|
+
<slot>
|
|
8
|
+
<DropdownMenuOption
|
|
9
|
+
v-for="(option, key) in dropdownMenu.options"
|
|
10
|
+
:key
|
|
11
|
+
:as="option.route || option.href ? HeadlessButton : undefined"
|
|
12
|
+
:class="option.class"
|
|
13
|
+
v-bind="
|
|
14
|
+
option.route || option.href
|
|
15
|
+
? {
|
|
16
|
+
href: option.href,
|
|
17
|
+
route: option.route,
|
|
18
|
+
routeParams: option.routeParams,
|
|
19
|
+
routeQuery: option.routeQuery,
|
|
20
|
+
}
|
|
21
|
+
: {}
|
|
22
|
+
"
|
|
23
|
+
@select="option.click?.()"
|
|
24
|
+
>
|
|
25
|
+
{{ option.label }}
|
|
26
|
+
</DropdownMenuOption>
|
|
27
|
+
</slot>
|
|
28
|
+
</DropdownMenuContent>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script setup lang="ts">
|
|
32
|
+
import { DropdownMenuContent } from 'reka-ui';
|
|
33
|
+
|
|
34
|
+
import { injectReactiveOrFail } from '@aerogel/core/utils';
|
|
35
|
+
import type { DropdownMenuExpose } from '@aerogel/core/components/contracts/DropdownMenu';
|
|
36
|
+
|
|
37
|
+
import DropdownMenuOption from './DropdownMenuOption.vue';
|
|
38
|
+
import HeadlessButton from '../headless/HeadlessButton.vue';
|
|
39
|
+
|
|
40
|
+
const dropdownMenu = injectReactiveOrFail<DropdownMenuExpose>(
|
|
41
|
+
'dropdown-menu',
|
|
42
|
+
'<DropdownMenuOptions> must be a child of a <DropdownMenu>',
|
|
43
|
+
);
|
|
44
|
+
</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="$inputRef"
|
|
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, useTemplateRef, 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 = useTemplateRef('$inputRef');
|
|
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,19 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Button
|
|
3
|
+
v-if="$errors.logs.length > 0"
|
|
4
|
+
size="icon"
|
|
5
|
+
variant="ghost"
|
|
6
|
+
:title="$td('errors.viewLogs', 'View error logs')"
|
|
7
|
+
:aria-label="$td('errors.viewLogs', 'View error logs')"
|
|
8
|
+
@click="$ui.modal(ErrorLogsModal)"
|
|
9
|
+
>
|
|
10
|
+
<IconWarning class="size-6 text-red-500" />
|
|
11
|
+
</Button>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup lang="ts">
|
|
15
|
+
import IconWarning from '~icons/ion/warning';
|
|
16
|
+
|
|
17
|
+
import Button from '@aerogel/core/components/ui/Button.vue';
|
|
18
|
+
import ErrorLogsModal from '@aerogel/core/components/ui/ErrorLogsModal.vue';
|
|
19
|
+
</script>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Modal :title="$td('errors.report', 'Errors ({count})', { count: $errors.logs.length })">
|
|
3
|
+
<ol>
|
|
4
|
+
<li
|
|
5
|
+
v-for="(log, index) of $errors.logs"
|
|
6
|
+
:key="index"
|
|
7
|
+
class="mb-2 flex max-w-prose min-w-56 justify-between py-2 last:mb-0"
|
|
8
|
+
>
|
|
9
|
+
<div>
|
|
10
|
+
<h3 class="font-medium">
|
|
11
|
+
{{ log.report.title }}
|
|
12
|
+
</h3>
|
|
13
|
+
<time :datetime="log.date.toISOString()" class="text-xs text-gray-700">
|
|
14
|
+
{{ log.date.toLocaleTimeString() }}
|
|
15
|
+
</time>
|
|
16
|
+
<Markdown
|
|
17
|
+
class="text-sm text-gray-500"
|
|
18
|
+
:text="log.report.description ?? getErrorMessage(log.report)"
|
|
19
|
+
/>
|
|
20
|
+
</div>
|
|
21
|
+
<Button
|
|
22
|
+
size="icon"
|
|
23
|
+
variant="ghost"
|
|
24
|
+
:aria-label="$td('errors.viewDetails', 'View details')"
|
|
25
|
+
:title="$td('errors.viewDetails', 'View details')"
|
|
26
|
+
class="self-center"
|
|
27
|
+
@click="
|
|
28
|
+
$errors.inspect(
|
|
29
|
+
log.report,
|
|
30
|
+
$errors.logs.map(({ report }) => report)
|
|
31
|
+
)
|
|
32
|
+
"
|
|
33
|
+
>
|
|
34
|
+
<IconViewShow class="size-4" aria-hidden="true" />
|
|
35
|
+
</Button>
|
|
36
|
+
</li>
|
|
37
|
+
</ol>
|
|
38
|
+
</Modal>
|
|
39
|
+
</template>
|
|
40
|
+
|
|
41
|
+
<script setup lang="ts">
|
|
42
|
+
import IconViewShow from '~icons/zondicons/view-show';
|
|
43
|
+
|
|
44
|
+
import Button from '@aerogel/core/components/ui/Button.vue';
|
|
45
|
+
import Modal from '@aerogel/core/components/ui/Modal.vue';
|
|
46
|
+
import Markdown from '@aerogel/core/components/ui/Markdown.vue';
|
|
47
|
+
import { getErrorMessage } from '@aerogel/core/errors';
|
|
48
|
+
</script>
|
|
@@ -5,12 +5,11 @@
|
|
|
5
5
|
<script setup lang="ts">
|
|
6
6
|
import { computed } from 'vue';
|
|
7
7
|
|
|
8
|
-
import { requiredObjectProp } from '@aerogel/core/utils/vue';
|
|
9
8
|
import { getErrorMessage } from '@aerogel/core/errors/utils';
|
|
10
9
|
import type { ErrorSource } from '@aerogel/core/errors/Errors.state';
|
|
11
10
|
|
|
12
11
|
import Markdown from '@aerogel/core/components/ui/Markdown.vue';
|
|
13
12
|
|
|
14
|
-
const
|
|
15
|
-
const message = computed(() => getErrorMessage(
|
|
13
|
+
const { error } = defineProps<{ error: ErrorSource }>();
|
|
14
|
+
const message = computed(() => getErrorMessage(error));
|
|
16
15
|
</script>
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Modal
|
|
2
|
+
<Modal
|
|
3
|
+
:title="$td('errors.report', 'Error report')"
|
|
4
|
+
title-hidden
|
|
5
|
+
close-hidden
|
|
6
|
+
class="p-0"
|
|
7
|
+
wrapper-class="sm:w-auto sm:min-w-lg sm:max-w-[80vw]"
|
|
8
|
+
>
|
|
3
9
|
<div class="px-4 pt-5 pb-4">
|
|
4
10
|
<h2 class="flex justify-between gap-4">
|
|
5
11
|
<div class="flex items-center gap-2">
|
|
6
12
|
<IconExclamationSolid class="size-5 text-red-600" />
|
|
7
13
|
<ErrorReportModalTitle
|
|
8
14
|
class="text-lg leading-6 font-semibold text-gray-900"
|
|
9
|
-
:report="
|
|
15
|
+
:report="activeReport"
|
|
10
16
|
:current-report="activeReportIndex + 1"
|
|
11
17
|
:total-reports="reports.length"
|
|
12
18
|
/>
|
|
@@ -33,11 +39,11 @@
|
|
|
33
39
|
</Button>
|
|
34
40
|
</span>
|
|
35
41
|
</div>
|
|
36
|
-
<ErrorReportModalButtons :report="
|
|
42
|
+
<ErrorReportModalButtons :report="activeReport" class="gap-0.5" />
|
|
37
43
|
</h2>
|
|
38
|
-
<Markdown v-if="
|
|
44
|
+
<Markdown v-if="activeReport.description" :text="activeReport.description" class="text-gray-600" />
|
|
39
45
|
</div>
|
|
40
|
-
<div class="-mt-2 max-h-[
|
|
46
|
+
<div class="-mt-2 max-h-[75vh] overflow-auto bg-red-800/10">
|
|
41
47
|
<pre class="p-4 text-xs text-red-800" v-text="details" />
|
|
42
48
|
</div>
|
|
43
49
|
</Modal>
|
|
@@ -54,9 +60,14 @@ import ErrorReportModalButtons from '@aerogel/core/components/ui/ErrorReportModa
|
|
|
54
60
|
import ErrorReportModalTitle from '@aerogel/core/components/ui/ErrorReportModalTitle.vue';
|
|
55
61
|
import Modal from '@aerogel/core/components/ui/Modal.vue';
|
|
56
62
|
import { useErrorReportModal } from '@aerogel/core/components/contracts/ErrorReportModal';
|
|
57
|
-
import type {
|
|
63
|
+
import type {
|
|
64
|
+
ErrorReportModalExpose,
|
|
65
|
+
ErrorReportModalProps,
|
|
66
|
+
} from '@aerogel/core/components/contracts/ErrorReportModal';
|
|
58
67
|
|
|
59
68
|
const props = defineProps<ErrorReportModalProps>();
|
|
60
69
|
|
|
61
|
-
const { activeReportIndex, details, nextReportText, previousReportText,
|
|
70
|
+
const { activeReportIndex, details, nextReportText, previousReportText, activeReport } = useErrorReportModal(props);
|
|
71
|
+
|
|
72
|
+
defineExpose<ErrorReportModalExpose>();
|
|
62
73
|
</script>
|
|
@@ -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.
|
|
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
|
-
|
|
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
|
|
78
|
+
async click() {
|
|
79
79
|
await navigator.clipboard.writeText(`${summary.value}\n\n${props.report.details}`);
|
|
80
80
|
|
|
81
|
-
UI.
|
|
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
|
-
|
|
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.
|
|
96
|
+
UI.toast(
|
|
99
97
|
translateWithDefault(
|
|
100
98
|
'errors.addedToConsole',
|
|
101
99
|
'You can now use the **error** variable in the console',
|