@aerogel/core 0.0.0-next.fd1bd21aea7a9ab8c4eab69a5f5776db5de8bf35 → 0.1.0
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 +2263 -1698
- package/dist/aerogel-core.js +3809 -0
- package/dist/aerogel-core.js.map +1 -0
- package/package.json +32 -37
- package/src/bootstrap/bootstrap.test.ts +4 -8
- package/src/bootstrap/index.ts +25 -16
- package/src/bootstrap/options.ts +1 -1
- package/src/components/AppLayout.vue +14 -0
- package/src/components/{AGAppModals.vue → AppModals.vue} +3 -4
- package/src/components/AppOverlays.vue +9 -0
- package/src/components/AppToasts.vue +16 -0
- package/src/components/contracts/AlertModal.ts +19 -0
- package/src/components/contracts/Button.ts +16 -0
- package/src/components/contracts/ConfirmModal.ts +48 -0
- package/src/components/contracts/DropdownMenu.ts +25 -0
- package/src/components/contracts/ErrorReportModal.ts +33 -0
- package/src/components/contracts/Input.ts +26 -0
- package/src/components/contracts/LoadingModal.ts +26 -0
- package/src/components/contracts/Modal.ts +21 -0
- package/src/components/contracts/PromptModal.ts +34 -0
- package/src/components/contracts/Select.ts +45 -0
- package/src/components/contracts/Toast.ts +15 -0
- package/src/components/contracts/index.ts +11 -0
- package/src/components/headless/HeadlessButton.vue +51 -0
- package/src/components/headless/HeadlessInput.vue +59 -0
- package/src/components/headless/{forms/AGHeadlessInputDescription.vue → HeadlessInputDescription.vue} +7 -8
- package/src/components/headless/{forms/AGHeadlessInputError.vue → HeadlessInputError.vue} +4 -8
- package/src/components/headless/HeadlessInputInput.vue +86 -0
- package/src/components/headless/{forms/AGHeadlessInputLabel.vue → HeadlessInputLabel.vue} +3 -7
- package/src/components/headless/{forms/AGHeadlessInputTextArea.vue → HeadlessInputTextArea.vue} +11 -11
- package/src/components/headless/HeadlessModal.vue +57 -0
- package/src/components/headless/HeadlessModalContent.vue +30 -0
- package/src/components/headless/HeadlessModalDescription.vue +12 -0
- package/src/components/headless/HeadlessModalOverlay.vue +12 -0
- package/src/components/headless/HeadlessModalTitle.vue +12 -0
- package/src/components/headless/HeadlessSelect.vue +120 -0
- package/src/components/headless/{forms/AGHeadlessSelectError.vue → HeadlessSelectError.vue} +5 -6
- 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 +20 -3
- package/src/components/index.ts +6 -9
- package/src/components/ui/AdvancedOptions.vue +18 -0
- package/src/components/ui/AlertModal.vue +17 -0
- package/src/components/ui/Button.vue +115 -0
- package/src/components/ui/Checkbox.vue +56 -0
- package/src/components/ui/ConfirmModal.vue +50 -0
- 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/ui/ErrorMessage.vue +15 -0
- package/src/components/ui/ErrorReportModal.vue +73 -0
- package/src/components/{modals/AGErrorReportModalButtons.vue → ui/ErrorReportModalButtons.vue} +34 -27
- package/src/components/ui/ErrorReportModalTitle.vue +24 -0
- package/src/components/{forms/AGForm.vue → ui/Form.vue} +4 -5
- package/src/components/ui/Input.vue +56 -0
- package/src/components/ui/Link.vue +12 -0
- package/src/components/ui/LoadingModal.vue +34 -0
- package/src/components/ui/Markdown.vue +97 -0
- package/src/components/ui/Modal.vue +131 -0
- package/src/components/ui/ModalContext.vue +31 -0
- package/src/components/ui/ProgressBar.vue +51 -0
- package/src/components/ui/PromptModal.vue +38 -0
- 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/StartupCrash.vue +76 -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 +35 -0
- package/src/directives/index.ts +9 -5
- package/src/directives/measure.ts +33 -8
- package/src/errors/Errors.state.ts +2 -1
- package/src/errors/Errors.ts +56 -33
- package/src/errors/JobCancelledError.ts +3 -0
- package/src/errors/index.ts +15 -8
- package/src/errors/settings/Debug.vue +14 -0
- package/src/errors/settings/index.ts +10 -0
- package/src/errors/utils.ts +17 -1
- package/src/forms/FormController.test.ts +113 -0
- package/src/forms/{Form.ts → FormController.ts} +73 -42
- package/src/forms/index.ts +3 -3
- package/src/forms/utils.ts +65 -24
- package/src/forms/validation.ts +50 -0
- package/src/index.css +76 -0
- package/src/jobs/Job.ts +144 -2
- package/src/jobs/index.ts +4 -1
- package/src/jobs/listeners.ts +3 -0
- package/src/jobs/status.ts +4 -0
- package/src/lang/DefaultLangProvider.ts +46 -0
- package/src/lang/Lang.state.ts +11 -0
- package/src/lang/Lang.ts +48 -21
- package/src/lang/index.ts +12 -6
- package/src/lang/settings/Language.vue +48 -0
- package/src/lang/settings/index.ts +10 -0
- package/src/plugins/Plugin.ts +1 -1
- package/src/plugins/index.ts +10 -7
- package/src/services/App.state.ts +36 -3
- package/src/services/App.ts +19 -3
- package/src/services/Cache.ts +1 -1
- package/src/services/Events.test.ts +8 -8
- package/src/services/Events.ts +16 -12
- package/src/services/Service.ts +135 -59
- package/src/services/Storage.ts +20 -0
- package/src/services/index.ts +16 -7
- package/src/services/utils.ts +18 -0
- package/src/testing/index.ts +8 -3
- package/src/testing/setup.ts +11 -0
- package/src/ui/UI.state.ts +14 -12
- package/src/ui/UI.ts +250 -123
- package/src/ui/index.ts +28 -28
- package/src/ui/utils.ts +16 -0
- package/src/utils/app.ts +7 -0
- package/src/utils/classes.ts +41 -0
- package/src/utils/composition/events.ts +4 -6
- package/src/utils/composition/forms.ts +20 -4
- package/src/utils/composition/persistent.test.ts +33 -0
- package/src/utils/composition/persistent.ts +11 -0
- package/src/utils/composition/state.test.ts +47 -0
- package/src/utils/composition/state.ts +33 -0
- package/src/utils/index.ts +6 -1
- package/src/utils/markdown.test.ts +50 -0
- package/src/utils/markdown.ts +53 -6
- package/src/utils/types.ts +3 -0
- package/src/utils/vue.ts +38 -132
- package/dist/aerogel-core.cjs.js +0 -2
- package/dist/aerogel-core.cjs.js.map +0 -1
- package/dist/aerogel-core.esm.js +0 -2
- package/dist/aerogel-core.esm.js.map +0 -1
- package/histoire.config.ts +0 -7
- package/noeldemartin.config.js +0 -5
- package/postcss.config.js +0 -6
- package/src/assets/histoire.css +0 -3
- package/src/components/AGAppLayout.vue +0 -16
- package/src/components/AGAppOverlays.vue +0 -41
- package/src/components/AGAppSnackbars.vue +0 -13
- package/src/components/constants.ts +0 -8
- package/src/components/forms/AGButton.vue +0 -44
- package/src/components/forms/AGCheckbox.vue +0 -41
- package/src/components/forms/AGInput.vue +0 -40
- package/src/components/forms/AGSelect.story.vue +0 -46
- package/src/components/forms/AGSelect.vue +0 -60
- package/src/components/forms/index.ts +0 -5
- package/src/components/headless/forms/AGHeadlessButton.vue +0 -56
- package/src/components/headless/forms/AGHeadlessInput.ts +0 -32
- package/src/components/headless/forms/AGHeadlessInput.vue +0 -57
- package/src/components/headless/forms/AGHeadlessInputInput.vue +0 -81
- package/src/components/headless/forms/AGHeadlessSelect.ts +0 -42
- package/src/components/headless/forms/AGHeadlessSelect.vue +0 -77
- package/src/components/headless/forms/AGHeadlessSelectButton.vue +0 -24
- package/src/components/headless/forms/AGHeadlessSelectLabel.vue +0 -24
- package/src/components/headless/forms/AGHeadlessSelectOption.ts +0 -4
- package/src/components/headless/forms/AGHeadlessSelectOption.vue +0 -39
- package/src/components/headless/forms/AGHeadlessSelectOptions.ts +0 -3
- package/src/components/headless/forms/composition.ts +0 -10
- package/src/components/headless/forms/index.ts +0 -17
- package/src/components/headless/modals/AGHeadlessModal.ts +0 -34
- package/src/components/headless/modals/AGHeadlessModal.vue +0 -86
- package/src/components/headless/modals/AGHeadlessModalPanel.vue +0 -28
- package/src/components/headless/modals/AGHeadlessModalTitle.vue +0 -13
- package/src/components/headless/modals/index.ts +0 -4
- package/src/components/headless/snackbars/AGHeadlessSnackbar.vue +0 -10
- package/src/components/headless/snackbars/index.ts +0 -40
- package/src/components/lib/AGErrorMessage.vue +0 -16
- package/src/components/lib/AGLink.vue +0 -9
- package/src/components/lib/AGMarkdown.vue +0 -41
- package/src/components/lib/AGMeasured.vue +0 -15
- package/src/components/lib/AGStartupCrash.vue +0 -31
- package/src/components/lib/index.ts +0 -5
- package/src/components/modals/AGAlertModal.ts +0 -15
- package/src/components/modals/AGAlertModal.vue +0 -14
- package/src/components/modals/AGConfirmModal.ts +0 -27
- package/src/components/modals/AGConfirmModal.vue +0 -26
- package/src/components/modals/AGErrorReportModal.ts +0 -46
- package/src/components/modals/AGErrorReportModal.vue +0 -54
- package/src/components/modals/AGErrorReportModalTitle.vue +0 -25
- package/src/components/modals/AGLoadingModal.ts +0 -23
- package/src/components/modals/AGLoadingModal.vue +0 -15
- package/src/components/modals/AGModal.ts +0 -10
- package/src/components/modals/AGModal.vue +0 -39
- package/src/components/modals/AGModalContext.ts +0 -8
- package/src/components/modals/AGModalContext.vue +0 -22
- package/src/components/modals/AGModalTitle.vue +0 -9
- package/src/components/modals/AGPromptModal.ts +0 -30
- package/src/components/modals/AGPromptModal.vue +0 -34
- package/src/components/modals/index.ts +0 -17
- package/src/components/snackbars/AGSnackbar.vue +0 -36
- package/src/components/snackbars/index.ts +0 -3
- package/src/components/utils.ts +0 -10
- package/src/directives/initial-focus.ts +0 -11
- package/src/forms/Form.test.ts +0 -58
- package/src/forms/composition.ts +0 -6
- package/src/main.histoire.ts +0 -1
- package/src/utils/tailwindcss.test.ts +0 -26
- package/src/utils/tailwindcss.ts +0 -7
- package/tailwind.config.js +0 -4
- package/tsconfig.json +0 -11
- package/vite.config.ts +0 -14
- /package/src/{main.ts → index.ts} +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<SelectValue v-if="$slots.default" :placeholder="select.placeholder">
|
|
3
|
+
<slot />
|
|
4
|
+
</SelectValue>
|
|
5
|
+
<SelectValue v-else :placeholder="select.placeholder" />
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
import { SelectValue } from 'reka-ui';
|
|
10
|
+
|
|
11
|
+
import { injectReactiveOrFail } from '@aerogel/core/utils/vue';
|
|
12
|
+
import type { SelectExpose } from '@aerogel/core/components/contracts/Select';
|
|
13
|
+
|
|
14
|
+
const select = injectReactiveOrFail<SelectExpose>(
|
|
15
|
+
'select',
|
|
16
|
+
'<HeadlessSelectValue> must be a child of a <HeadlessSelect>',
|
|
17
|
+
);
|
|
18
|
+
</script>
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="rootClass">
|
|
3
|
+
<label v-if="label" :for="expose.id" :class="labelClass">
|
|
4
|
+
{{ label }}
|
|
5
|
+
</label>
|
|
6
|
+
<SwitchRoot
|
|
7
|
+
:id="expose.id"
|
|
8
|
+
:name
|
|
9
|
+
:model-value="expose.value.value"
|
|
10
|
+
v-bind="$attrs"
|
|
11
|
+
:class="inputClass"
|
|
12
|
+
@update:model-value="$emit('update:modelValue', $event)"
|
|
13
|
+
>
|
|
14
|
+
<SwitchThumb :class="thumbClass" />
|
|
15
|
+
</SwitchRoot>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script setup lang="ts" generic="T extends boolean = boolean">
|
|
20
|
+
import { SwitchRoot, SwitchThumb } from 'reka-ui';
|
|
21
|
+
import { computed, inject, readonly, watchEffect } from 'vue';
|
|
22
|
+
import { uuid } from '@noeldemartin/utils';
|
|
23
|
+
import type { HTMLAttributes } from 'vue';
|
|
24
|
+
|
|
25
|
+
import type FormController from '@aerogel/core/forms/FormController';
|
|
26
|
+
import type { FormFieldValue } from '@aerogel/core/forms/FormController';
|
|
27
|
+
import type { InputEmits, InputExpose, InputProps } from '@aerogel/core/components/contracts/Input';
|
|
28
|
+
|
|
29
|
+
defineOptions({ inheritAttrs: false });
|
|
30
|
+
|
|
31
|
+
const {
|
|
32
|
+
name,
|
|
33
|
+
label,
|
|
34
|
+
description,
|
|
35
|
+
modelValue,
|
|
36
|
+
class: rootClass,
|
|
37
|
+
} = defineProps<
|
|
38
|
+
InputProps<T> & {
|
|
39
|
+
class?: HTMLAttributes['class'];
|
|
40
|
+
labelClass?: HTMLAttributes['class'];
|
|
41
|
+
inputClass?: HTMLAttributes['class'];
|
|
42
|
+
thumbClass?: HTMLAttributes['class'];
|
|
43
|
+
}
|
|
44
|
+
>();
|
|
45
|
+
const emit = defineEmits<InputEmits>();
|
|
46
|
+
const form = inject<FormController | null>('form', null);
|
|
47
|
+
const errors = computed(() => {
|
|
48
|
+
if (!form || !name) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return form.errors[name] ?? null;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const expose = {
|
|
56
|
+
id: `switch-${uuid()}`,
|
|
57
|
+
name: computed(() => name),
|
|
58
|
+
label: computed(() => label),
|
|
59
|
+
description: computed(() => description),
|
|
60
|
+
value: computed(() => {
|
|
61
|
+
if (form && name) {
|
|
62
|
+
return form.getFieldValue(name) as boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return modelValue;
|
|
66
|
+
}),
|
|
67
|
+
errors: readonly(errors),
|
|
68
|
+
required: computed(() => {
|
|
69
|
+
if (!name || !form) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return form.getFieldRules(name).includes('required');
|
|
74
|
+
}),
|
|
75
|
+
update(value) {
|
|
76
|
+
if (form && name) {
|
|
77
|
+
form.setFieldValue(name, value as FormFieldValue);
|
|
78
|
+
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
emit('update:modelValue', value);
|
|
83
|
+
},
|
|
84
|
+
} satisfies InputExpose;
|
|
85
|
+
|
|
86
|
+
defineExpose(expose);
|
|
87
|
+
|
|
88
|
+
watchEffect(() => {
|
|
89
|
+
if (!description && !errors.value) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// eslint-disable-next-line no-console
|
|
94
|
+
console.warn('Errors and description not implemented in <HeadlessSwitch>');
|
|
95
|
+
});
|
|
96
|
+
</script>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ToastRoot>
|
|
3
|
+
<slot>
|
|
4
|
+
<span v-if="message">{{ message }}</span>
|
|
5
|
+
<HeadlessToastAction v-for="(action, key) of actions" :key :action />
|
|
6
|
+
</slot>
|
|
7
|
+
</ToastRoot>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
import { ToastRoot } from 'reka-ui';
|
|
12
|
+
|
|
13
|
+
import type { ToastProps } from '@aerogel/core/components/contracts/Toast';
|
|
14
|
+
|
|
15
|
+
import HeadlessToastAction from './HeadlessToastAction.vue';
|
|
16
|
+
|
|
17
|
+
defineProps<ToastProps>();
|
|
18
|
+
</script>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component :is="action.dismiss ? ToastClose : 'button'" type="button" @click="action.click">
|
|
3
|
+
{{ action.label }}
|
|
4
|
+
</component>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { ToastClose } from 'reka-ui';
|
|
9
|
+
|
|
10
|
+
import type { ToastAction as IToastAction } from '@aerogel/core/components/contracts/Toast';
|
|
11
|
+
|
|
12
|
+
defineProps<{ action: IToastAction }>();
|
|
13
|
+
</script>
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
1
|
+
export { default as HeadlessButton } from './HeadlessButton.vue';
|
|
2
|
+
export { default as HeadlessInput } from './HeadlessInput.vue';
|
|
3
|
+
export { default as HeadlessInputDescription } from './HeadlessInputDescription.vue';
|
|
4
|
+
export { default as HeadlessInputError } from './HeadlessInputError.vue';
|
|
5
|
+
export { default as HeadlessInputInput } from './HeadlessInputInput.vue';
|
|
6
|
+
export { default as HeadlessInputLabel } from './HeadlessInputLabel.vue';
|
|
7
|
+
export { default as HeadlessInputTextArea } from './HeadlessInputTextArea.vue';
|
|
8
|
+
export { default as HeadlessModal } from './HeadlessModal.vue';
|
|
9
|
+
export { default as HeadlessModalContent } from './HeadlessModalContent.vue';
|
|
10
|
+
export { default as HeadlessModalDescription } from './HeadlessModalDescription.vue';
|
|
11
|
+
export { default as HeadlessModalOverlay } from './HeadlessModalOverlay.vue';
|
|
12
|
+
export { default as HeadlessModalTitle } from './HeadlessModalTitle.vue';
|
|
13
|
+
export { default as HeadlessSelect } from './HeadlessSelect.vue';
|
|
14
|
+
export { default as HeadlessSelectLabel } from './HeadlessSelectLabel.vue';
|
|
15
|
+
export { default as HeadlessSelectOption } from './HeadlessSelectOption.vue';
|
|
16
|
+
export { default as HeadlessSelectOptions } from './HeadlessSelectOptions.vue';
|
|
17
|
+
export { default as HeadlessSelectTrigger } from './HeadlessSelectTrigger.vue';
|
|
18
|
+
export { default as HeadlessSelectValue } from './HeadlessSelectValue.vue';
|
|
19
|
+
export { default as HeadlessSwitch } from './HeadlessSwitch.vue';
|
|
20
|
+
export { default as HeadlessToast } from './HeadlessToast.vue';
|
package/src/components/index.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export { default as AppLayout } from './AppLayout.vue';
|
|
2
|
+
export { default as AppModals } from './AppModals.vue';
|
|
3
|
+
export { default as AppOverlays } from './AppOverlays.vue';
|
|
4
|
+
export { default as AppToasts } from './AppToasts.vue';
|
|
3
5
|
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
export * from './constants';
|
|
7
|
-
export * from './forms';
|
|
6
|
+
export * from './contracts';
|
|
8
7
|
export * from './headless';
|
|
9
|
-
export * from './
|
|
10
|
-
export * from './modals';
|
|
11
|
-
export * from './snackbars';
|
|
8
|
+
export * from './ui';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<details class="group">
|
|
3
|
+
<summary
|
|
4
|
+
class="-ml-2 flex w-[max-content] 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>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Modal :title="renderedTitle" :title-hidden="titleHidden">
|
|
3
|
+
<Markdown :text="message" />
|
|
4
|
+
</Modal>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import Modal from '@aerogel/core/components/ui/Modal.vue';
|
|
9
|
+
import Markdown from '@aerogel/core/components/ui/Markdown.vue';
|
|
10
|
+
import { useAlertModal } from '@aerogel/core/components/contracts/AlertModal';
|
|
11
|
+
import type { AlertModalExpose, AlertModalProps } from '@aerogel/core/components/contracts/AlertModal';
|
|
12
|
+
|
|
13
|
+
const props = defineProps<AlertModalProps>();
|
|
14
|
+
const { renderedTitle, titleHidden } = useAlertModal(props);
|
|
15
|
+
|
|
16
|
+
defineExpose<AlertModalExpose>();
|
|
17
|
+
</script>
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<HeadlessButton :class="renderedClasses" :disabled v-bind="props">
|
|
3
|
+
<slot />
|
|
4
|
+
</HeadlessButton>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { computed } from 'vue';
|
|
9
|
+
|
|
10
|
+
import HeadlessButton from '@aerogel/core/components/headless/HeadlessButton.vue';
|
|
11
|
+
import { variantClasses } from '@aerogel/core/utils/classes';
|
|
12
|
+
import type { ButtonProps } from '@aerogel/core/components/contracts/Button';
|
|
13
|
+
import type { Variants } from '@aerogel/core/utils/classes';
|
|
14
|
+
|
|
15
|
+
const { class: baseClasses, size, variant, disabled, ...props } = defineProps<ButtonProps>();
|
|
16
|
+
|
|
17
|
+
/* eslint-disable vue/max-len */
|
|
18
|
+
// prettier-ignore
|
|
19
|
+
const renderedClasses = computed(() => variantClasses<Variants<Pick<ButtonProps, 'size' | 'variant' | 'disabled'>>>(
|
|
20
|
+
{ baseClasses, variant, size, disabled },
|
|
21
|
+
{
|
|
22
|
+
baseClasses: 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2',
|
|
23
|
+
variants: {
|
|
24
|
+
variant: {
|
|
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',
|
|
31
|
+
},
|
|
32
|
+
size: {
|
|
33
|
+
small: 'text-xs min-h-6',
|
|
34
|
+
default: 'text-sm min-h-8',
|
|
35
|
+
large: 'text-base min-h-10',
|
|
36
|
+
icon: 'rounded-full p-2.5',
|
|
37
|
+
},
|
|
38
|
+
disabled: {
|
|
39
|
+
false: null,
|
|
40
|
+
true: 'opacity-50 cursor-not-allowed',
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
compoundVariants: [
|
|
44
|
+
{
|
|
45
|
+
variant: ['default', 'secondary', 'danger', 'ghost', 'outline'],
|
|
46
|
+
class: 'flex items-center justify-center gap-1 font-medium',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
variant: ['default', 'danger'],
|
|
50
|
+
class: 'shadow-sm',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
variant: ['secondary', 'outline'],
|
|
54
|
+
class: 'ring-1 ring-inset',
|
|
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
|
+
},
|
|
106
|
+
],
|
|
107
|
+
defaultVariants: {
|
|
108
|
+
variant: 'default',
|
|
109
|
+
size: 'default',
|
|
110
|
+
disabled: false,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
));
|
|
114
|
+
/* eslint-enable vue/max-len */
|
|
115
|
+
</script>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<HeadlessInput
|
|
3
|
+
ref="$inputRef"
|
|
4
|
+
:class="renderedClasses"
|
|
5
|
+
v-bind="props"
|
|
6
|
+
@update:model-value="$emit('update:modelValue', $event)"
|
|
7
|
+
>
|
|
8
|
+
<div class="flex h-6 items-center">
|
|
9
|
+
<HeadlessInputInput v-bind="inputAttrs" type="checkbox" :class="renderedInputClasses" />
|
|
10
|
+
</div>
|
|
11
|
+
<div v-if="$slots.default" :class="renderedLabelClasses">
|
|
12
|
+
<HeadlessInputLabel class="text-gray-900">
|
|
13
|
+
<slot />
|
|
14
|
+
</HeadlessInputLabel>
|
|
15
|
+
<HeadlessInputError class="text-red-600" />
|
|
16
|
+
</div>
|
|
17
|
+
<div v-else-if="label" :class="renderedLabelClasses">
|
|
18
|
+
<HeadlessInputLabel class="text-gray-900" />
|
|
19
|
+
<HeadlessInputError class="text-red-600" />
|
|
20
|
+
</div>
|
|
21
|
+
</HeadlessInput>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
import { computed, useTemplateRef } from 'vue';
|
|
26
|
+
import type { HTMLAttributes } from 'vue';
|
|
27
|
+
|
|
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';
|
|
33
|
+
import { useInputAttrs } from '@aerogel/core/utils/composition/forms';
|
|
34
|
+
import type { InputEmits, InputProps } from '@aerogel/core/components/contracts/Input';
|
|
35
|
+
|
|
36
|
+
defineOptions({ inheritAttrs: false });
|
|
37
|
+
defineEmits<InputEmits>();
|
|
38
|
+
|
|
39
|
+
const { inputClass, labelClass, ...props } = defineProps<
|
|
40
|
+
InputProps & { inputClass?: HTMLAttributes['class']; labelClass?: HTMLAttributes['class'] }
|
|
41
|
+
>();
|
|
42
|
+
|
|
43
|
+
const $input = useTemplateRef('$inputRef');
|
|
44
|
+
const [inputAttrs, rootClasses] = useInputAttrs();
|
|
45
|
+
const renderedClasses = computed(() => classes('relative flex items-start', rootClasses.value));
|
|
46
|
+
const renderedInputClasses = computed(() =>
|
|
47
|
+
classes(
|
|
48
|
+
'size-4 rounded text-primary-600 not-checked:hover:bg-gray-200 checked:hover:text-primary-500 checked:border-0',
|
|
49
|
+
{
|
|
50
|
+
'border-gray-300 focus:ring-primary-600': !$input.value?.errors,
|
|
51
|
+
'border-red-400 border-2 focus:ring-red-600': $input.value?.errors,
|
|
52
|
+
},
|
|
53
|
+
inputClass,
|
|
54
|
+
));
|
|
55
|
+
const renderedLabelClasses = computed(() => classes('ml-2 text-sm leading-6', labelClass));
|
|
56
|
+
</script>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<template>
|
|
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 />
|
|
11
|
+
|
|
12
|
+
<ul v-if="checkboxes" class="mt-4 flex flex-col text-sm text-gray-600">
|
|
13
|
+
<li v-for="(checkbox, name) of checkboxes" :key="name">
|
|
14
|
+
<label class="flex items-center">
|
|
15
|
+
<input
|
|
16
|
+
v-model="form[name]"
|
|
17
|
+
type="checkbox"
|
|
18
|
+
:required="checkbox.required"
|
|
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"
|
|
20
|
+
>
|
|
21
|
+
<span class="ml-1.5">{{ checkbox.label }}</span>
|
|
22
|
+
</label>
|
|
23
|
+
</li>
|
|
24
|
+
</ul>
|
|
25
|
+
|
|
26
|
+
<div class="mt-4 flex flex-row-reverse gap-2">
|
|
27
|
+
<Button :variant="acceptVariant" submit>
|
|
28
|
+
{{ renderedAcceptText }}
|
|
29
|
+
</Button>
|
|
30
|
+
<Button v-if="!required" :variant="cancelVariant" @click="close(false)">
|
|
31
|
+
{{ renderedCancelText }}
|
|
32
|
+
</Button>
|
|
33
|
+
</div>
|
|
34
|
+
</Form>
|
|
35
|
+
</Modal>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<script setup lang="ts">
|
|
39
|
+
import Form from '@aerogel/core/components/ui/Form.vue';
|
|
40
|
+
import Markdown from '@aerogel/core/components/ui/Markdown.vue';
|
|
41
|
+
import Button from '@aerogel/core/components/ui/Button.vue';
|
|
42
|
+
import Modal from '@aerogel/core/components/ui/Modal.vue';
|
|
43
|
+
import { useConfirmModal } from '@aerogel/core/components/contracts/ConfirmModal';
|
|
44
|
+
import type { ConfirmModalExpose, ConfirmModalProps } from '@aerogel/core/components/contracts/ConfirmModal';
|
|
45
|
+
|
|
46
|
+
const { cancelVariant = 'secondary', ...props } = defineProps<ConfirmModalProps>();
|
|
47
|
+
const { form, renderedTitle, titleHidden, renderedAcceptText, renderedCancelText } = useConfirmModal(props);
|
|
48
|
+
|
|
49
|
+
defineExpose<ConfirmModalExpose>();
|
|
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>
|