@finema/core 2.14.1 → 2.15.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/module.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/components/Form/Fields.vue +23 -15
- package/dist/runtime/components/Form/InputRadio/index.vue +56 -0
- package/dist/runtime/components/Form/InputRadio/index.vue.d.ts +11 -0
- package/dist/runtime/components/Form/InputRadio/types.d.ts +19 -0
- package/dist/runtime/components/Form/InputRadio/types.js +0 -0
- package/dist/runtime/components/Form/InputWYSIWYG/UploadImageForm.vue +38 -0
- package/dist/runtime/components/Form/InputWYSIWYG/UploadImageForm.vue.d.ts +11 -0
- package/dist/runtime/components/Form/InputWYSIWYG/index.vue +281 -0
- package/dist/runtime/components/Form/InputWYSIWYG/index.vue.d.ts +6 -0
- package/dist/runtime/components/Form/InputWYSIWYG/types.d.ts +52 -0
- package/dist/runtime/components/Form/InputWYSIWYG/types.js +0 -0
- package/dist/runtime/components/Form/types.d.ts +3 -1
- package/dist/runtime/styles/main.css +1 -1
- package/dist/runtime/theme/index.d.ts +2 -0
- package/dist/runtime/theme/index.js +2 -0
- package/dist/runtime/theme/radioGroup.d.ts +6 -0
- package/dist/runtime/theme/radioGroup.js +6 -0
- package/dist/runtime/theme/uploadFileDropzone.js +4 -4
- package/dist/runtime/theme/wysiwyg.d.ts +53 -0
- package/dist/runtime/theme/wysiwyg.js +53 -0
- package/package.json +3 -2
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
3
|
:class="[theme.base({
|
|
4
4
|
class: [$props.class, ui?.base]
|
|
5
|
-
})]"
|
|
6
|
-
>
|
|
7
|
-
<component
|
|
8
|
-
:is="componentMap[option.type]?.component"
|
|
9
|
-
v-for="option in options.filter((item) => !item.isHide)"
|
|
10
|
-
:key="option.props.name"
|
|
11
|
-
:class="option.class"
|
|
12
|
-
:form="form"
|
|
13
|
-
v-bind="{ ...getFieldBinding(option), ...componentMap[option.type]?.props }"
|
|
14
|
-
v-on="option.on ?? {}"
|
|
15
|
-
/>
|
|
16
|
-
</div>
|
|
5
|
+
})]"
|
|
6
|
+
>
|
|
7
|
+
<component
|
|
8
|
+
:is="componentMap[option.type]?.component"
|
|
9
|
+
v-for="option in options.filter((item) => !item.isHide)"
|
|
10
|
+
:key="option.props.name"
|
|
11
|
+
:class="option.class"
|
|
12
|
+
:form="form"
|
|
13
|
+
v-bind="{ ...getFieldBinding(option), ...componentMap[option.type]?.props }"
|
|
14
|
+
v-on="option.on ?? {}"
|
|
15
|
+
/>
|
|
16
|
+
</div>
|
|
17
17
|
</template>
|
|
18
18
|
|
|
19
19
|
<script setup>
|
|
@@ -25,9 +25,11 @@ import FormInputToggle from "./InputToggle/index.vue";
|
|
|
25
25
|
import FormInputCheckbox from "./InputCheckbox/index.vue";
|
|
26
26
|
import FormInputSelect from "./InputSelect/index.vue";
|
|
27
27
|
import FormInputSelectMultiple from "./InputSelectMultiple/index.vue";
|
|
28
|
+
import FormInputRadio from "./InputRadio/index.vue";
|
|
28
29
|
import FormInputDateTime from "./InputDateTime/index.vue";
|
|
29
30
|
import FormInputDateTimeRange from "./InputDateTimeRange/index.vue";
|
|
30
31
|
import FormInputUploadDropzoneAuto from "./InputUploadDropzoneAuto/index.vue";
|
|
32
|
+
import FormInputWYSIWYG from "./InputWYSIWYG/index.vue";
|
|
31
33
|
import { INPUT_TYPES } from "#core/components/Form/types";
|
|
32
34
|
import { formTheme } from "#core/theme/form";
|
|
33
35
|
import { useUiConfig } from "#core/composables/useConfig";
|
|
@@ -84,7 +86,10 @@ const componentMap = {
|
|
|
84
86
|
}
|
|
85
87
|
},
|
|
86
88
|
[INPUT_TYPES.STATIC]: void 0,
|
|
87
|
-
[INPUT_TYPES.RADIO]:
|
|
89
|
+
[INPUT_TYPES.RADIO]: {
|
|
90
|
+
component: FormInputRadio,
|
|
91
|
+
props: {}
|
|
92
|
+
},
|
|
88
93
|
[INPUT_TYPES.DATE_TIME]: {
|
|
89
94
|
component: FormInputDateTime,
|
|
90
95
|
props: {}
|
|
@@ -115,7 +120,10 @@ const componentMap = {
|
|
|
115
120
|
},
|
|
116
121
|
[INPUT_TYPES.UPLOAD_DROPZONE_AUTO_MULTIPLE]: void 0,
|
|
117
122
|
[INPUT_TYPES.UPLOAD_DROPZONE_IMAGE_AUTO_MULTIPLE]: void 0,
|
|
118
|
-
[INPUT_TYPES.WYSIWYG]:
|
|
123
|
+
[INPUT_TYPES.WYSIWYG]: {
|
|
124
|
+
component: FormInputWYSIWYG,
|
|
125
|
+
props: {}
|
|
126
|
+
},
|
|
119
127
|
[INPUT_TYPES.TAGS]: void 0
|
|
120
128
|
};
|
|
121
129
|
const theme = computed(() => useUiConfig(formTheme, "form")({
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<FieldWrapper v-bind="wrapperProps">
|
|
3
|
+
<RadioGroup
|
|
4
|
+
:model-value="value"
|
|
5
|
+
:items="options"
|
|
6
|
+
:variant="variant"
|
|
7
|
+
:orientation="orientation"
|
|
8
|
+
:indicator="indicator"
|
|
9
|
+
:legend="legend"
|
|
10
|
+
:disabled="wrapperProps.disabled"
|
|
11
|
+
:required="wrapperProps.required"
|
|
12
|
+
:name="wrapperProps.name"
|
|
13
|
+
value-key="value"
|
|
14
|
+
label-key="label"
|
|
15
|
+
description-key="description"
|
|
16
|
+
:ui="ui"
|
|
17
|
+
@update:model-value="onChange"
|
|
18
|
+
/>
|
|
19
|
+
</FieldWrapper>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup>
|
|
23
|
+
import FieldWrapper from "#core/components/Form/FieldWrapper.vue";
|
|
24
|
+
import { useFieldHOC } from "#core/composables/useForm";
|
|
25
|
+
const emits = defineEmits(["change"]);
|
|
26
|
+
const props = defineProps({
|
|
27
|
+
variant: { type: String, required: false, default: "list" },
|
|
28
|
+
orientation: { type: String, required: false, default: "vertical" },
|
|
29
|
+
indicator: { type: String, required: false, default: "start" },
|
|
30
|
+
options: { type: Array, required: true },
|
|
31
|
+
legend: { type: String, required: false },
|
|
32
|
+
form: { type: Object, required: false },
|
|
33
|
+
name: { type: String, required: true },
|
|
34
|
+
errorMessage: { type: String, required: false },
|
|
35
|
+
label: { type: null, required: false },
|
|
36
|
+
description: { type: String, required: false },
|
|
37
|
+
hint: { type: String, required: false },
|
|
38
|
+
rules: { type: null, required: false },
|
|
39
|
+
autoFocus: { type: Boolean, required: false },
|
|
40
|
+
placeholder: { type: String, required: false },
|
|
41
|
+
disabled: { type: Boolean, required: false },
|
|
42
|
+
readonly: { type: Boolean, required: false },
|
|
43
|
+
required: { type: Boolean, required: false },
|
|
44
|
+
help: { type: String, required: false },
|
|
45
|
+
ui: { type: null, required: false }
|
|
46
|
+
});
|
|
47
|
+
const {
|
|
48
|
+
value,
|
|
49
|
+
wrapperProps,
|
|
50
|
+
handleChange
|
|
51
|
+
} = useFieldHOC(props);
|
|
52
|
+
const onChange = (newValue) => {
|
|
53
|
+
handleChange(newValue);
|
|
54
|
+
emits("change", newValue);
|
|
55
|
+
};
|
|
56
|
+
</script>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { IRadioFieldProps } from '#core/components/Form/InputRadio/types';
|
|
2
|
+
declare const _default: import("vue").DefineComponent<IRadioFieldProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
3
|
+
change: (value: any) => any;
|
|
4
|
+
}, string, import("vue").PublicProps, Readonly<IRadioFieldProps> & Readonly<{
|
|
5
|
+
onChange?: ((value: any) => any) | undefined;
|
|
6
|
+
}>, {
|
|
7
|
+
orientation: "horizontal" | "vertical";
|
|
8
|
+
variant: "list" | "card" | "table";
|
|
9
|
+
indicator: "start" | "end" | "hidden";
|
|
10
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
11
|
+
export default _default;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { IFieldProps, IFormFieldBase, INPUT_TYPES } from '#core/components/Form/types';
|
|
2
|
+
import type { IOption } from '#core/types/common';
|
|
3
|
+
export type RadioOption = IOption & {
|
|
4
|
+
label?: string;
|
|
5
|
+
value?: any;
|
|
6
|
+
description?: string;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
class?: any;
|
|
9
|
+
};
|
|
10
|
+
export interface IRadioFieldProps extends IFieldProps {
|
|
11
|
+
variant?: 'list' | 'card' | 'table';
|
|
12
|
+
orientation?: 'horizontal' | 'vertical';
|
|
13
|
+
indicator?: 'start' | 'end' | 'hidden';
|
|
14
|
+
options: RadioOption[];
|
|
15
|
+
legend?: string;
|
|
16
|
+
}
|
|
17
|
+
export type IRadioField = IFormFieldBase<INPUT_TYPES.RADIO, IRadioFieldProps, {
|
|
18
|
+
change?: (value: any) => void;
|
|
19
|
+
}>;
|
|
File without changes
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="upload-image-form">
|
|
3
|
+
<input
|
|
4
|
+
ref="fileInput"
|
|
5
|
+
type="file"
|
|
6
|
+
accept="image/*"
|
|
7
|
+
@change="handleFileSelect"
|
|
8
|
+
/>
|
|
9
|
+
<button
|
|
10
|
+
type="button"
|
|
11
|
+
@click="handleUpload"
|
|
12
|
+
>
|
|
13
|
+
Upload
|
|
14
|
+
</button>
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script setup>
|
|
19
|
+
import { ref } from "vue";
|
|
20
|
+
const props = defineProps({
|
|
21
|
+
options: { type: Object, required: false }
|
|
22
|
+
});
|
|
23
|
+
const emit = defineEmits(["submit"]);
|
|
24
|
+
const fileInput = ref();
|
|
25
|
+
const selectedFile = ref(null);
|
|
26
|
+
const handleFileSelect = (event) => {
|
|
27
|
+
const target = event.target;
|
|
28
|
+
const file = target.files?.[0];
|
|
29
|
+
if (file) {
|
|
30
|
+
selectedFile.value = file;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const handleUpload = async () => {
|
|
34
|
+
if (!selectedFile.value) return;
|
|
35
|
+
const url = URL.createObjectURL(selectedFile.value);
|
|
36
|
+
emit("submit", url);
|
|
37
|
+
};
|
|
38
|
+
</script>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
options?: {
|
|
3
|
+
requestOptions?: any;
|
|
4
|
+
};
|
|
5
|
+
}
|
|
6
|
+
declare const _default: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
7
|
+
submit: (url: string) => any;
|
|
8
|
+
}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
|
|
9
|
+
onSubmit?: ((url: string) => any) | undefined;
|
|
10
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
11
|
+
export default _default;
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<FieldWrapper v-bind="wrapperProps">
|
|
3
|
+
<div :class="ui.container()">
|
|
4
|
+
<div
|
|
5
|
+
v-if="showToolbar"
|
|
6
|
+
:class="ui.toolbar()"
|
|
7
|
+
>
|
|
8
|
+
<div
|
|
9
|
+
v-for="(items, index) in menuItems"
|
|
10
|
+
:key="index"
|
|
11
|
+
:class="ui.toolbarGroup()"
|
|
12
|
+
>
|
|
13
|
+
<button
|
|
14
|
+
v-for="item in items"
|
|
15
|
+
:key="item.name"
|
|
16
|
+
:class="[ui.menuItem(), { [ui.menuItemActive()]: item.isActive?.() }]"
|
|
17
|
+
type="button"
|
|
18
|
+
:title="item.title"
|
|
19
|
+
@click="item.action"
|
|
20
|
+
>
|
|
21
|
+
<Icon
|
|
22
|
+
:name="item.icon"
|
|
23
|
+
:class="ui.icon()"
|
|
24
|
+
/>
|
|
25
|
+
</button>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
<ClientOnly>
|
|
29
|
+
<EditorContent
|
|
30
|
+
:editor="editor"
|
|
31
|
+
:class="ui.editorContent()"
|
|
32
|
+
/>
|
|
33
|
+
<template #fallback>
|
|
34
|
+
<div
|
|
35
|
+
class="min-h-[200px]"
|
|
36
|
+
v-html="value"
|
|
37
|
+
/>
|
|
38
|
+
</template>
|
|
39
|
+
</ClientOnly>
|
|
40
|
+
</div>
|
|
41
|
+
</FieldWrapper>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<script setup>
|
|
45
|
+
import { computed, watch } from "vue";
|
|
46
|
+
import { EditorContent, useEditor } from "@tiptap/vue-3";
|
|
47
|
+
import StarterKit from "@tiptap/starter-kit";
|
|
48
|
+
import Underline from "@tiptap/extension-underline";
|
|
49
|
+
import TextAlign from "@tiptap/extension-text-align";
|
|
50
|
+
import Link from "@tiptap/extension-link";
|
|
51
|
+
import Image from "@tiptap/extension-image";
|
|
52
|
+
import Youtube from "@tiptap/extension-youtube";
|
|
53
|
+
import { useFieldHOC } from "#core/composables/useForm";
|
|
54
|
+
import FieldWrapper from "#core/components/Form/FieldWrapper.vue";
|
|
55
|
+
import { wysiwygTheme } from "#core/theme/wysiwyg";
|
|
56
|
+
import { useUiConfig } from "#core/composables/useConfig";
|
|
57
|
+
const props = defineProps({
|
|
58
|
+
editable: { type: Boolean, required: false },
|
|
59
|
+
autofocus: { type: Boolean, required: false },
|
|
60
|
+
content: { type: String, required: false },
|
|
61
|
+
toolbar: { type: Object, required: false },
|
|
62
|
+
minHeight: { type: [String, Number], required: false },
|
|
63
|
+
maxHeight: { type: [String, Number], required: false },
|
|
64
|
+
size: { type: String, required: false, default: "md" },
|
|
65
|
+
color: { type: String, required: false, default: "gray" },
|
|
66
|
+
imageUpload: { type: Object, required: false },
|
|
67
|
+
linkOptions: { type: Object, required: false },
|
|
68
|
+
containerUi: { type: null, required: false },
|
|
69
|
+
image: { type: Object, required: false },
|
|
70
|
+
form: { type: Object, required: false },
|
|
71
|
+
name: { type: String, required: true },
|
|
72
|
+
errorMessage: { type: String, required: false },
|
|
73
|
+
label: { type: null, required: false },
|
|
74
|
+
description: { type: String, required: false },
|
|
75
|
+
hint: { type: String, required: false },
|
|
76
|
+
rules: { type: null, required: false },
|
|
77
|
+
autoFocus: { type: Boolean, required: false },
|
|
78
|
+
placeholder: { type: String, required: false },
|
|
79
|
+
disabled: { type: Boolean, required: false },
|
|
80
|
+
readonly: { type: Boolean, required: false },
|
|
81
|
+
required: { type: Boolean, required: false },
|
|
82
|
+
help: { type: String, required: false },
|
|
83
|
+
ui: { type: null, required: false }
|
|
84
|
+
});
|
|
85
|
+
const {
|
|
86
|
+
value,
|
|
87
|
+
wrapperProps
|
|
88
|
+
} = useFieldHOC(props);
|
|
89
|
+
const ui = computed(() => useUiConfig(wysiwygTheme, "wysiwyg")({
|
|
90
|
+
size: props.size,
|
|
91
|
+
color: props.color
|
|
92
|
+
}));
|
|
93
|
+
const showToolbar = computed(() => {
|
|
94
|
+
if (!props.toolbar) return true;
|
|
95
|
+
return Object.values(props.toolbar).some(Boolean);
|
|
96
|
+
});
|
|
97
|
+
const editor = useEditor({
|
|
98
|
+
content: value.value,
|
|
99
|
+
extensions: [
|
|
100
|
+
StarterKit,
|
|
101
|
+
Underline,
|
|
102
|
+
TextAlign.configure({
|
|
103
|
+
types: ["heading", "paragraph"]
|
|
104
|
+
}),
|
|
105
|
+
Link.configure({
|
|
106
|
+
openOnClick: false
|
|
107
|
+
}),
|
|
108
|
+
Image,
|
|
109
|
+
Youtube
|
|
110
|
+
],
|
|
111
|
+
editorProps: {
|
|
112
|
+
attributes: {
|
|
113
|
+
class: "prose px-4 py-2 focus:outline-none min-h-[200px]"
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
onUpdate: ({
|
|
117
|
+
editor: editor2
|
|
118
|
+
}) => {
|
|
119
|
+
value.value = editor2.getHTML();
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
watch(value, (newValue) => {
|
|
123
|
+
if (editor.value && newValue !== editor.value.getHTML()) {
|
|
124
|
+
editor.value.commands.setContent(newValue);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
const toolbarConfig = {
|
|
128
|
+
format: [
|
|
129
|
+
{
|
|
130
|
+
key: "bold",
|
|
131
|
+
name: "bold",
|
|
132
|
+
icon: "ph:text-b-bold",
|
|
133
|
+
action: () => editor.value?.chain().focus().toggleBold().run(),
|
|
134
|
+
isActive: () => editor.value?.isActive("bold") || false,
|
|
135
|
+
title: "\u0E15\u0E31\u0E27\u0E2B\u0E19\u0E32"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
key: "italic",
|
|
139
|
+
name: "italic",
|
|
140
|
+
icon: "ph:text-italic",
|
|
141
|
+
action: () => editor.value?.chain().focus().toggleItalic().run(),
|
|
142
|
+
isActive: () => editor.value?.isActive("italic") || false,
|
|
143
|
+
title: "\u0E15\u0E31\u0E27\u0E40\u0E2D\u0E35\u0E22\u0E07"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
key: "underline",
|
|
147
|
+
name: "underline",
|
|
148
|
+
icon: "ph:text-underline",
|
|
149
|
+
action: () => editor.value?.chain().focus().toggleUnderline().run(),
|
|
150
|
+
isActive: () => editor.value?.isActive("underline") || false,
|
|
151
|
+
title: "\u0E02\u0E35\u0E14\u0E40\u0E2A\u0E49\u0E19\u0E43\u0E15\u0E49"
|
|
152
|
+
}
|
|
153
|
+
],
|
|
154
|
+
list: [
|
|
155
|
+
{
|
|
156
|
+
key: "bulletList",
|
|
157
|
+
name: "bullet-list",
|
|
158
|
+
icon: "ph:list-bullets",
|
|
159
|
+
action: () => editor.value?.chain().focus().toggleBulletList().run(),
|
|
160
|
+
isActive: () => editor.value?.isActive("bulletList") || false,
|
|
161
|
+
title: "\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23\u0E2A\u0E31\u0E0D\u0E25\u0E31\u0E01\u0E29\u0E13\u0E4C"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
key: "orderedList",
|
|
165
|
+
name: "ordered-list",
|
|
166
|
+
icon: "ph:list-numbers",
|
|
167
|
+
action: () => editor.value?.chain().focus().toggleOrderedList().run(),
|
|
168
|
+
isActive: () => editor.value?.isActive("orderedList") || false,
|
|
169
|
+
title: "\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23\u0E25\u0E33\u0E14\u0E31\u0E1A"
|
|
170
|
+
}
|
|
171
|
+
],
|
|
172
|
+
textAlign: [
|
|
173
|
+
{
|
|
174
|
+
key: "textAlign",
|
|
175
|
+
name: "align-left",
|
|
176
|
+
icon: "ph:text-align-left",
|
|
177
|
+
action: () => editor.value?.chain().focus().setTextAlign("left").run(),
|
|
178
|
+
isActive: () => editor.value?.isActive({
|
|
179
|
+
textAlign: "left"
|
|
180
|
+
}) || false,
|
|
181
|
+
title: "\u0E08\u0E31\u0E14\u0E0A\u0E34\u0E14\u0E0B\u0E49\u0E32\u0E22"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
key: "textAlign",
|
|
185
|
+
name: "align-center",
|
|
186
|
+
icon: "ph:text-align-center",
|
|
187
|
+
action: () => editor.value?.chain().focus().setTextAlign("center").run(),
|
|
188
|
+
isActive: () => editor.value?.isActive({
|
|
189
|
+
textAlign: "center"
|
|
190
|
+
}) || false,
|
|
191
|
+
title: "\u0E08\u0E31\u0E14\u0E01\u0E36\u0E48\u0E07\u0E01\u0E25\u0E32\u0E07"
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
key: "textAlign",
|
|
195
|
+
name: "align-right",
|
|
196
|
+
icon: "ph:text-align-right",
|
|
197
|
+
action: () => editor.value?.chain().focus().setTextAlign("right").run(),
|
|
198
|
+
isActive: () => editor.value?.isActive({
|
|
199
|
+
textAlign: "right"
|
|
200
|
+
}) || false,
|
|
201
|
+
title: "\u0E08\u0E31\u0E14\u0E0A\u0E34\u0E14\u0E02\u0E27\u0E32"
|
|
202
|
+
}
|
|
203
|
+
],
|
|
204
|
+
media: [
|
|
205
|
+
{
|
|
206
|
+
key: "link",
|
|
207
|
+
name: "link",
|
|
208
|
+
icon: "ph:link-simple",
|
|
209
|
+
action: () => {
|
|
210
|
+
const url = window.prompt("URL");
|
|
211
|
+
if (url) {
|
|
212
|
+
editor.value?.chain().focus().setLink({
|
|
213
|
+
href: url
|
|
214
|
+
}).run();
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
isActive: () => editor.value?.isActive("link") || false,
|
|
218
|
+
title: "\u0E41\u0E17\u0E23\u0E01\u0E25\u0E34\u0E07\u0E01\u0E4C"
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
key: "image",
|
|
222
|
+
name: "image",
|
|
223
|
+
icon: "ph:image",
|
|
224
|
+
action: () => {
|
|
225
|
+
const url = window.prompt("URL \u0E23\u0E39\u0E1B\u0E20\u0E32\u0E1E");
|
|
226
|
+
if (url) {
|
|
227
|
+
editor.value?.chain().focus().setImage({
|
|
228
|
+
src: url
|
|
229
|
+
}).run();
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
title: "\u0E41\u0E17\u0E23\u0E01\u0E23\u0E39\u0E1B\u0E20\u0E32\u0E1E"
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
key: "youtube",
|
|
236
|
+
name: "video",
|
|
237
|
+
icon: "ph:video-camera",
|
|
238
|
+
action: () => {
|
|
239
|
+
const url = window.prompt("URL \u0E27\u0E34\u0E14\u0E35\u0E42\u0E2D");
|
|
240
|
+
if (url) {
|
|
241
|
+
editor.value?.chain().focus().setYoutubeVideo({
|
|
242
|
+
src: url
|
|
243
|
+
}).run();
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
title: "\u0E41\u0E17\u0E23\u0E01\u0E27\u0E34\u0E14\u0E35\u0E42\u0E2D"
|
|
247
|
+
}
|
|
248
|
+
],
|
|
249
|
+
history: [
|
|
250
|
+
{
|
|
251
|
+
key: "undo",
|
|
252
|
+
name: "undo",
|
|
253
|
+
icon: "ph:arrow-counter-clockwise",
|
|
254
|
+
action: () => editor.value?.chain().focus().undo().run(),
|
|
255
|
+
title: "\u0E40\u0E25\u0E34\u0E01\u0E17\u0E33"
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
key: "redo",
|
|
259
|
+
name: "redo",
|
|
260
|
+
icon: "ph:arrow-clockwise",
|
|
261
|
+
action: () => editor.value?.chain().focus().redo().run(),
|
|
262
|
+
title: "\u0E17\u0E33\u0E43\u0E2B\u0E21\u0E48"
|
|
263
|
+
}
|
|
264
|
+
]
|
|
265
|
+
};
|
|
266
|
+
const menuItems = computed(() => {
|
|
267
|
+
const items = [];
|
|
268
|
+
Object.entries(toolbarConfig).forEach(([groupKey, groupItems]) => {
|
|
269
|
+
const enabledItems = groupItems.filter((item) => {
|
|
270
|
+
if (groupKey === "textAlign") {
|
|
271
|
+
return props.toolbar?.textAlign;
|
|
272
|
+
}
|
|
273
|
+
return props.toolbar?.[item.key];
|
|
274
|
+
});
|
|
275
|
+
if (enabledItems.length > 0) {
|
|
276
|
+
items.push(enabledItems);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
return items;
|
|
280
|
+
});
|
|
281
|
+
</script>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { IWYSIWYGFieldProps } from '#core/components/Form/InputWYSIWYG/types';
|
|
2
|
+
declare const _default: import("vue").DefineComponent<IWYSIWYGFieldProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<IWYSIWYGFieldProps> & Readonly<{}>, {
|
|
3
|
+
size: "xs" | "sm" | "md" | "lg" | "xl";
|
|
4
|
+
color: "primary" | "gray";
|
|
5
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
6
|
+
export default _default;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Editor } from '@tiptap/vue-3';
|
|
2
|
+
import type { IFieldProps, IFormFieldBase, INPUT_TYPES } from '#core/components/Form/types';
|
|
3
|
+
export interface IWYSIWYGFieldProps extends IFieldProps {
|
|
4
|
+
editable?: boolean;
|
|
5
|
+
autofocus?: boolean;
|
|
6
|
+
content?: string;
|
|
7
|
+
toolbar?: {
|
|
8
|
+
bold?: boolean;
|
|
9
|
+
italic?: boolean;
|
|
10
|
+
underline?: boolean;
|
|
11
|
+
strike?: boolean;
|
|
12
|
+
code?: boolean;
|
|
13
|
+
heading?: boolean | number[];
|
|
14
|
+
paragraph?: boolean;
|
|
15
|
+
bulletList?: boolean;
|
|
16
|
+
orderedList?: boolean;
|
|
17
|
+
blockquote?: boolean;
|
|
18
|
+
codeBlock?: boolean;
|
|
19
|
+
horizontalRule?: boolean;
|
|
20
|
+
link?: boolean;
|
|
21
|
+
image?: boolean;
|
|
22
|
+
youtube?: boolean;
|
|
23
|
+
textAlign?: boolean;
|
|
24
|
+
undo?: boolean;
|
|
25
|
+
redo?: boolean;
|
|
26
|
+
};
|
|
27
|
+
minHeight?: string | number;
|
|
28
|
+
maxHeight?: string | number;
|
|
29
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
30
|
+
color?: 'primary' | 'gray';
|
|
31
|
+
imageUpload?: {
|
|
32
|
+
enabled?: boolean;
|
|
33
|
+
uploadUrl?: string;
|
|
34
|
+
maxSize?: number;
|
|
35
|
+
accept?: string[];
|
|
36
|
+
headers?: Record<string, string>;
|
|
37
|
+
};
|
|
38
|
+
linkOptions?: {
|
|
39
|
+
openOnClick?: boolean;
|
|
40
|
+
HTMLAttributes?: Record<string, any>;
|
|
41
|
+
};
|
|
42
|
+
containerUi?: any;
|
|
43
|
+
image?: {
|
|
44
|
+
requestOptions?: any;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export type IWYSIWYGField = IFormFieldBase<INPUT_TYPES.WYSIWYG, IWYSIWYGFieldProps, {
|
|
48
|
+
change?: (content: string) => void;
|
|
49
|
+
update?: (content: string, editor: Editor) => void;
|
|
50
|
+
focus?: (editor: Editor) => void;
|
|
51
|
+
blur?: (editor: Editor) => void;
|
|
52
|
+
}>;
|
|
File without changes
|
|
@@ -10,6 +10,8 @@ import type { ICheckboxField } from '#core/components/Form/InputCheckbox/types';
|
|
|
10
10
|
import type { ISelectMultipleField } from '#core/components/Form/InputSelectMultiple/types';
|
|
11
11
|
import type { INumberField } from '#core/components/Form/InputNumber/types';
|
|
12
12
|
import type { IDateTimeField } from '#core/components/Form/InputDateTime/date_time_field.types';
|
|
13
|
+
import type { IRadioField } from '#core/components/Form/InputRadio/types';
|
|
14
|
+
import type { IWYSIWYGField } from '#core/components/Form/InputWYSIWYG/types';
|
|
13
15
|
export declare const enum INPUT_TYPES {
|
|
14
16
|
TEXT = "TEXT",
|
|
15
17
|
NUMBER = "NUMBER",
|
|
@@ -62,7 +64,7 @@ export interface IFormFieldBase<I extends INPUT_TYPES, P extends IFieldProps, O>
|
|
|
62
64
|
props: P;
|
|
63
65
|
on?: O;
|
|
64
66
|
}
|
|
65
|
-
export type IFormField = ITextField | INumberField | ITextareaField | IToggleField | ISelectField | ICheckboxField | ISelectMultipleField | IDateTimeField | IDateTimeRangeField | IUploadDropzoneAutoField | IFormFieldBase<INPUT_TYPES.COMPONENT, any, any>;
|
|
67
|
+
export type IFormField = ITextField | INumberField | ITextareaField | IToggleField | ISelectField | ICheckboxField | ISelectMultipleField | IRadioField | IDateTimeField | IDateTimeRangeField | IUploadDropzoneAutoField | IWYSIWYGField | IFormFieldBase<INPUT_TYPES.COMPONENT, any, any>;
|
|
66
68
|
export interface IFileValue {
|
|
67
69
|
url: string;
|
|
68
70
|
path?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
@import "tailwindcss";@import "@nuxt/ui";@theme static{--font-sans:"Noto Sans Thai","Noto Sans Thai Looped","Public Sans",sans-serif;--color-main:#232c5a;--color-main-50:#f4f4f7;--color-main-100:#e9eaef;--color-main-200:#c8cad6;--color-main-300:#a7abbd;--color-main-400:#656b8c;--color-main-500:#232c5a;--color-main-600:#202851;--color-main-700:#151a36;--color-main-800:#101429;--color-main-900:#0b0d1b;--color-main-950:#0b0d1b;--color-secondary:#ee8b36;--color-secondary-50:#fdf1e7;--color-secondary-100:#f9d6b8;--color-secondary-200:#f5bb89;--color-secondary-300:#f1a05a;--color-secondary-400:#ed852b;--color-secondary-500:#d46b12;--color-secondary-600:#a5540e;--color-secondary-700:#763c0a;--color-secondary-800:#472406;--color-secondary-900:#180c02;--color-info:#0d8cee;--color-info-50:#f3f9fe;--color-info-100:#e7f4fd;--color-info-200:#ebf6ff;--color-info-300:#9ed1f8;--color-info-400:#56aff3;--color-info-500:#0d8cee;--color-info-600:#0c7ed6;--color-info-700:#08548f;--color-info-800:#063f6b;--color-info-900:#042a47;--color-error:#f25555;--color-error-50:#fef7f7;--color-error-100:#feeeee;--color-error-200:#ffdfdf;--color-error-300:#fabbbb;--color-error-400:#f68888;--color-error-500:#f25555;--color-error-600:#da4d4d;--color-error-700:#913333;--color-error-800:#6d2626;--color-error-900:#491a1a;--color-success:#3fb061;--color-success-50:#f5fbf7;--color-success-100:#ecf7ef;--color-success-200:#daeee0;--color-success-300:#b2dfc0;--color-success-400:#79c890;--color-success-500:#3fb061;--color-success-600:#399e57;--color-success-700:#266a3a;--color-success-800:#1c4f2c;--color-success-900:#13351d;--color-warning:#ff9a35;--color-warning-50:#fffaf5;--color-warning-100:#fff5eb;--color-warning-200:#fef1cc;--color-warning-300:#ffd7ae;--color-warning-400:#ffb872;--color-warning-500:#ff9a35;--color-warning-600:#e68b30;--color-warning-700:#995c20;--color-warning-800:#734518;--color-warning-900:#4d2e10}::-webkit-scrollbar{-webkit-appearance:none;height:10px;width:10px}::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.3);border-radius:4px;box-shadow:0 0 1px hsla(0,0%,100%,.5)}:root{--dp-font-family:inherit!important}.dp__theme_light{--dp-primary-color:var(--color-main)!important;--dp-primary-disabled-color:var(--color-main-200)!important}#__nuxt,body,html{@apply w-full h-full}
|
|
1
|
+
@import "tailwindcss";@import "@nuxt/ui";@plugin "@tailwindcss/typography";@source inline("prose");@theme static{--font-sans:"Noto Sans Thai","Noto Sans Thai Looped","Public Sans",sans-serif;--color-main:#232c5a;--color-main-50:#f4f4f7;--color-main-100:#e9eaef;--color-main-200:#c8cad6;--color-main-300:#a7abbd;--color-main-400:#656b8c;--color-main-500:#232c5a;--color-main-600:#202851;--color-main-700:#151a36;--color-main-800:#101429;--color-main-900:#0b0d1b;--color-main-950:#0b0d1b;--color-secondary:#ee8b36;--color-secondary-50:#fdf1e7;--color-secondary-100:#f9d6b8;--color-secondary-200:#f5bb89;--color-secondary-300:#f1a05a;--color-secondary-400:#ed852b;--color-secondary-500:#d46b12;--color-secondary-600:#a5540e;--color-secondary-700:#763c0a;--color-secondary-800:#472406;--color-secondary-900:#180c02;--color-info:#0d8cee;--color-info-50:#f3f9fe;--color-info-100:#e7f4fd;--color-info-200:#ebf6ff;--color-info-300:#9ed1f8;--color-info-400:#56aff3;--color-info-500:#0d8cee;--color-info-600:#0c7ed6;--color-info-700:#08548f;--color-info-800:#063f6b;--color-info-900:#042a47;--color-error:#f25555;--color-error-50:#fef7f7;--color-error-100:#feeeee;--color-error-200:#ffdfdf;--color-error-300:#fabbbb;--color-error-400:#f68888;--color-error-500:#f25555;--color-error-600:#da4d4d;--color-error-700:#913333;--color-error-800:#6d2626;--color-error-900:#491a1a;--color-success:#3fb061;--color-success-50:#f5fbf7;--color-success-100:#ecf7ef;--color-success-200:#daeee0;--color-success-300:#b2dfc0;--color-success-400:#79c890;--color-success-500:#3fb061;--color-success-600:#399e57;--color-success-700:#266a3a;--color-success-800:#1c4f2c;--color-success-900:#13351d;--color-warning:#ff9a35;--color-warning-50:#fffaf5;--color-warning-100:#fff5eb;--color-warning-200:#fef1cc;--color-warning-300:#ffd7ae;--color-warning-400:#ffb872;--color-warning-500:#ff9a35;--color-warning-600:#e68b30;--color-warning-700:#995c20;--color-warning-800:#734518;--color-warning-900:#4d2e10}::-webkit-scrollbar{-webkit-appearance:none;height:10px;width:10px}::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.3);border-radius:4px;box-shadow:0 0 1px hsla(0,0%,100%,.5)}:root{--dp-font-family:inherit!important}.dp__theme_light{--dp-primary-color:var(--color-main)!important;--dp-primary-disabled-color:var(--color-main-200)!important}#__nuxt,body,html{@apply w-full h-full}
|
|
@@ -11,3 +11,5 @@ export { inputNumberTheme as inputNumber } from './inputNumber.js';
|
|
|
11
11
|
export { iconsTheme as icons } from './icons.js';
|
|
12
12
|
export { uploadFileDropzoneTheme as uploadFileDropzone } from './uploadFileDropzone.js';
|
|
13
13
|
export { dateTimeTheme as dateTime } from './dateTime.js';
|
|
14
|
+
export { radioGroupTheme as radioGroup } from './radioGroup.js';
|
|
15
|
+
export { wysiwygTheme as wysiwyg } from './wysiwyg.js';
|
|
@@ -11,3 +11,5 @@ export { inputNumberTheme as inputNumber } from "./inputNumber.js";
|
|
|
11
11
|
export { iconsTheme as icons } from "./icons.js";
|
|
12
12
|
export { uploadFileDropzoneTheme as uploadFileDropzone } from "./uploadFileDropzone.js";
|
|
13
13
|
export { dateTimeTheme as dateTime } from "./dateTime.js";
|
|
14
|
+
export { radioGroupTheme as radioGroup } from "./radioGroup.js";
|
|
15
|
+
export { wysiwygTheme as wysiwyg } from "./wysiwyg.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const uploadFileDropzoneTheme = {
|
|
2
2
|
slots: {
|
|
3
|
-
base: "relative w-full text-base p-4 transition rounded-
|
|
3
|
+
base: "relative w-full text-base p-4 transition rounded-md flex items-center justify-center ring-1 bg-white ring-gray-300",
|
|
4
4
|
wrapper: "flex flex-col items-center w-full",
|
|
5
5
|
disabled: "bg-gray-100 border-none grayscale cursor-not-allowed",
|
|
6
6
|
failed: "border-error",
|
|
@@ -20,13 +20,13 @@ export const uploadFileDropzoneTheme = {
|
|
|
20
20
|
onLoadingTextWrapper: "flex-1 min-w-0 flex items-center justify-between",
|
|
21
21
|
onLoadingLoadingIconClass: "size-10 text-primary animate-spin",
|
|
22
22
|
// Preview state
|
|
23
|
-
onPreviewWrapper: "flex items-center space-x-4 rounded-
|
|
24
|
-
onPreviewPreviewImgWrapper: "flex-shrink-0 w-16 h-16 rounded-
|
|
23
|
+
onPreviewWrapper: "flex items-center space-x-4 rounded-md w-full",
|
|
24
|
+
onPreviewPreviewImgWrapper: "flex-shrink-0 w-16 h-16 rounded-md overflow-hidden bg-gray-100",
|
|
25
25
|
onPreviewPreviewImgClass: "w-full h-full object-cover",
|
|
26
26
|
onPreviewPreviewFileClass: "size-8 text-gray-400 m-auto mt-4",
|
|
27
27
|
onPreviewTextWrapper: "flex-1 min-w-0 flex items-center justify-between",
|
|
28
28
|
// Failed state
|
|
29
|
-
onFailedWrapper: "flex items-start space-x-4 w-full rounded-
|
|
29
|
+
onFailedWrapper: "flex items-start space-x-4 w-full rounded-md",
|
|
30
30
|
onFailedFailedImgWrapper: "flex-shrink-0",
|
|
31
31
|
onFailedFailedIconClass: "size-12",
|
|
32
32
|
onFailedTextWrapper: "flex-1 min-w-0 flex items-start justify-between",
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export declare const wysiwygTheme: {
|
|
2
|
+
slots: {
|
|
3
|
+
container: string;
|
|
4
|
+
toolbar: string;
|
|
5
|
+
toolbarGroup: string;
|
|
6
|
+
menuItem: string;
|
|
7
|
+
menuItemActive: string;
|
|
8
|
+
icon: string;
|
|
9
|
+
editorContent: string;
|
|
10
|
+
};
|
|
11
|
+
variants: {
|
|
12
|
+
size: {
|
|
13
|
+
xs: {
|
|
14
|
+
button: string;
|
|
15
|
+
icon: string;
|
|
16
|
+
select: string;
|
|
17
|
+
};
|
|
18
|
+
sm: {
|
|
19
|
+
button: string;
|
|
20
|
+
icon: string;
|
|
21
|
+
select: string;
|
|
22
|
+
};
|
|
23
|
+
md: {
|
|
24
|
+
button: string;
|
|
25
|
+
icon: string;
|
|
26
|
+
select: string;
|
|
27
|
+
};
|
|
28
|
+
lg: {
|
|
29
|
+
button: string;
|
|
30
|
+
icon: string;
|
|
31
|
+
select: string;
|
|
32
|
+
};
|
|
33
|
+
xl: {
|
|
34
|
+
button: string;
|
|
35
|
+
icon: string;
|
|
36
|
+
select: string;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
color: {
|
|
40
|
+
primary: {
|
|
41
|
+
button: string;
|
|
42
|
+
};
|
|
43
|
+
gray: {
|
|
44
|
+
button: string;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
compoundVariants: never[];
|
|
49
|
+
defaultVariants: {
|
|
50
|
+
size: string;
|
|
51
|
+
color: string;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export const wysiwygTheme = {
|
|
2
|
+
slots: {
|
|
3
|
+
container: "border border-gray-200 rounded focus:ring-primary-500 relative block w-full resize-none rounded-md border-0 bg-white p-0 pb-3 text-sm text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:outline-none focus:ring-2 disabled:cursor-not-allowed disabled:opacity-75",
|
|
4
|
+
toolbar: "flex flex-wrap border py-2 px-2 gap-1 border-gray-300 bg-white rounded-t-md",
|
|
5
|
+
toolbarGroup: "flex items-center border-r border-gray-200 pr-2",
|
|
6
|
+
menuItem: "px-1 py-1 rounded-md hover:bg-gray-100 transition-colors flex justify-center items-center cursor-pointer flex-wrap",
|
|
7
|
+
menuItemActive: "bg-primary-100 text-primary-600",
|
|
8
|
+
icon: "size-5",
|
|
9
|
+
editorContent: ""
|
|
10
|
+
},
|
|
11
|
+
variants: {
|
|
12
|
+
size: {
|
|
13
|
+
xs: {
|
|
14
|
+
button: "px-1.5 py-0.5 text-xs",
|
|
15
|
+
icon: "h-3 w-3",
|
|
16
|
+
select: "px-1.5 py-0.5 text-xs"
|
|
17
|
+
},
|
|
18
|
+
sm: {
|
|
19
|
+
button: "px-2 py-1 text-sm",
|
|
20
|
+
icon: "h-3.5 w-3.5",
|
|
21
|
+
select: "px-2 py-1 text-xs"
|
|
22
|
+
},
|
|
23
|
+
md: {
|
|
24
|
+
button: "px-2 py-1 text-sm",
|
|
25
|
+
icon: "h-4 w-4",
|
|
26
|
+
select: "px-2 py-1 text-xs"
|
|
27
|
+
},
|
|
28
|
+
lg: {
|
|
29
|
+
button: "px-3 py-1.5 text-base",
|
|
30
|
+
icon: "h-5 w-5",
|
|
31
|
+
select: "px-3 py-1.5 text-sm"
|
|
32
|
+
},
|
|
33
|
+
xl: {
|
|
34
|
+
button: "px-4 py-2 text-lg",
|
|
35
|
+
icon: "h-6 w-6",
|
|
36
|
+
select: "px-4 py-2 text-base"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
color: {
|
|
40
|
+
primary: {
|
|
41
|
+
button: "hover:bg-blue-50 hover:border-blue-300"
|
|
42
|
+
},
|
|
43
|
+
gray: {
|
|
44
|
+
button: "hover:bg-gray-100 hover:border-gray-300"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
compoundVariants: [],
|
|
49
|
+
defaultVariants: {
|
|
50
|
+
size: "md",
|
|
51
|
+
color: "gray"
|
|
52
|
+
}
|
|
53
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finema/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.0",
|
|
4
4
|
"repository": "https://gitlab.finema.co/finema/ui-kit",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Finema Dev Core Team",
|
|
@@ -67,13 +67,14 @@
|
|
|
67
67
|
"url-join": "^5.0.0"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@hyoban/eslint-plugin-tailwindcss": "^4.0.0-alpha.12",
|
|
71
70
|
"@eslint/js": "^9.26.0",
|
|
71
|
+
"@hyoban/eslint-plugin-tailwindcss": "^4.0.0-alpha.12",
|
|
72
72
|
"@nuxt/devtools": "^2.4.1",
|
|
73
73
|
"@nuxt/eslint-config": "^1.3.1",
|
|
74
74
|
"@nuxt/module-builder": "^1.0.1",
|
|
75
75
|
"@nuxt/schema": "^3.17.3",
|
|
76
76
|
"@nuxt/test-utils": "^3.19.0",
|
|
77
|
+
"@tailwindcss/typography": "^0.5.0-alpha.3",
|
|
77
78
|
"@types/node": "latest",
|
|
78
79
|
"changelogen": "^0.5.7",
|
|
79
80
|
"eslint": "^9.26.0",
|