@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finema/core",
3
- "version": "2.14.1",
3
+ "version": "2.15.0",
4
4
  "configKey": "core",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.1",
package/dist/module.mjs CHANGED
@@ -3,7 +3,7 @@ import defu from 'defu';
3
3
  import * as theme from '../dist/runtime/theme/index.js';
4
4
 
5
5
  const name = "@finema/core";
6
- const version = "2.14.1";
6
+ const version = "2.15.0";
7
7
 
8
8
  const nuxtAppOptions = {
9
9
  head: {
@@ -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]: void 0,
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]: void 0,
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
+ }>;
@@ -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
+ }>;
@@ -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";
@@ -0,0 +1,6 @@
1
+ export declare const radioGroupTheme: {
2
+ slots: {
3
+ item: string;
4
+ label: string;
5
+ };
6
+ };
@@ -0,0 +1,6 @@
1
+ export const radioGroupTheme = {
2
+ slots: {
3
+ item: "cursor-pointer",
4
+ label: "cursor-pointer"
5
+ }
6
+ };
@@ -1,6 +1,6 @@
1
1
  export const uploadFileDropzoneTheme = {
2
2
  slots: {
3
- base: "relative w-full text-base p-4 transition rounded-lg flex items-center justify-center ring-1 bg-white ring-gray-300",
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-lg w-full",
24
- onPreviewPreviewImgWrapper: "flex-shrink-0 w-16 h-16 rounded-lg overflow-hidden bg-gray-100",
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-lg",
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.14.1",
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",