@finema/core 3.9.0 → 3.10.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.
Files changed (44) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +1 -1
  3. package/dist/runtime/components/FlexDeck/index.vue +79 -79
  4. package/dist/runtime/components/Form/FieldWrapper.vue +13 -13
  5. package/dist/runtime/components/Form/Fields.vue +13 -13
  6. package/dist/runtime/components/Form/InputCheckbox/index.vue +18 -18
  7. package/dist/runtime/components/Form/InputCheckboxGroup/index.vue +21 -21
  8. package/dist/runtime/components/Form/InputCurrency/index.vue +49 -49
  9. package/dist/runtime/components/Form/InputDateTime/index.vue +62 -62
  10. package/dist/runtime/components/Form/InputDateTimeRange/index.vue +56 -56
  11. package/dist/runtime/components/Form/InputNumber/index.vue +20 -20
  12. package/dist/runtime/components/Form/InputSelect/index.vue +46 -46
  13. package/dist/runtime/components/Form/InputSelectMultiple/index.vue +62 -62
  14. package/dist/runtime/components/Form/InputTags/index.vue +54 -54
  15. package/dist/runtime/components/Form/InputTextarea/index.vue +18 -18
  16. package/dist/runtime/components/Form/InputTime/index.vue +38 -38
  17. package/dist/runtime/components/Form/InputToggle/index.vue +17 -17
  18. package/dist/runtime/components/Form/InputUploadDropzone/index.vue +30 -30
  19. package/dist/runtime/components/Form/InputUploadDropzoneAuto/index.vue +50 -50
  20. package/dist/runtime/components/Form/InputUploadDropzoneAutoMultiple/index.vue +50 -50
  21. package/dist/runtime/components/Form/InputUploadImageAuto/index.vue +50 -50
  22. package/dist/runtime/components/Form/InputWYSIWYG/EditorImagePasteExtension.js +81 -64
  23. package/dist/runtime/components/Form/InputWYSIWYG/EditorImageUploadNode.vue +18 -18
  24. package/dist/runtime/components/Form/InputWYSIWYG/EditorLinkPopover.vue +65 -65
  25. package/dist/runtime/components/Form/InputWYSIWYG/README.md +96 -96
  26. package/dist/runtime/components/Form/InputWYSIWYG/index.vue +41 -41
  27. package/dist/runtime/components/Form/fileState/EmptyState.vue +21 -21
  28. package/dist/runtime/components/Form/fileState/FailedState.vue +33 -33
  29. package/dist/runtime/components/Form/fileState/LoadingState.vue +24 -24
  30. package/dist/runtime/components/Form/fileState/MultipleFilesState.vue +75 -75
  31. package/dist/runtime/components/Form/fileState/PreviewModal.vue +23 -23
  32. package/dist/runtime/components/Form/index.vue +5 -5
  33. package/dist/runtime/components/Image.vue +28 -28
  34. package/dist/runtime/components/Log/index.vue +17 -17
  35. package/dist/runtime/components/Table/ColumnDate.vue +1 -1
  36. package/dist/runtime/components/Table/ColumnDateTime.vue +1 -1
  37. package/dist/runtime/components/Table/ColumnImage.vue +4 -4
  38. package/dist/runtime/components/Table/ColumnText.d.vue.ts +3 -0
  39. package/dist/runtime/components/Table/ColumnText.vue +11 -1
  40. package/dist/runtime/components/Table/ColumnText.vue.d.ts +3 -0
  41. package/dist/runtime/components/Table/Pagination.vue +57 -46
  42. package/dist/runtime/components/Table/Simple.vue +16 -16
  43. package/dist/runtime/server/tsconfig.json +3 -3
  44. package/package.json +1 -1
@@ -79,69 +79,69 @@ const handleKeyDown = (event) => {
79
79
  </script>
80
80
 
81
81
  <template>
82
- <Popover
83
- v-model:open="open"
84
- :ui="{ content: 'p-0.5' }"
85
- >
86
- <Tooltip text="Link">
87
- <Button
88
- icon="i-lucide-link"
89
- color="neutral"
90
- active-color="primary"
91
- variant="ghost"
92
- active-variant="soft"
93
- size="sm"
94
- :active="active"
95
- :disabled="disabled"
96
- />
97
- </Tooltip>
98
-
99
- <template #content>
100
- <Input
101
- v-model="url"
102
- autofocus
103
- name="url"
104
- type="url"
105
- variant="none"
106
- placeholder="Paste a link..."
107
- @keydown="handleKeyDown"
108
- >
109
- <div class="mr-0.5 flex items-center">
110
- <Button
111
- icon="i-lucide-corner-down-left"
112
- variant="ghost"
113
- size="sm"
114
- :disabled="!url && !active"
115
- title="Apply link"
116
- @click="setLink"
117
- />
118
-
119
- <Separator
120
- orientation="vertical"
121
- class="mx-1 h-6"
122
- />
123
-
124
- <Button
125
- icon="i-lucide-external-link"
126
- color="neutral"
127
- variant="ghost"
128
- size="sm"
129
- :disabled="!url && !active"
130
- title="Open in new window"
131
- @click="openLink"
132
- />
133
-
134
- <Button
135
- icon="i-lucide-trash"
136
- color="neutral"
137
- variant="ghost"
138
- size="sm"
139
- :disabled="!url && !active"
140
- title="Remove link"
141
- @click="removeLink"
142
- />
143
- </div>
144
- </Input>
145
- </template>
146
- </Popover>
82
+ <Popover
83
+ v-model:open="open"
84
+ :ui="{ content: 'p-0.5' }"
85
+ >
86
+ <Tooltip text="Link">
87
+ <Button
88
+ icon="i-lucide-link"
89
+ color="neutral"
90
+ active-color="primary"
91
+ variant="ghost"
92
+ active-variant="soft"
93
+ size="sm"
94
+ :active="active"
95
+ :disabled="disabled"
96
+ />
97
+ </Tooltip>
98
+
99
+ <template #content>
100
+ <Input
101
+ v-model="url"
102
+ autofocus
103
+ name="url"
104
+ type="url"
105
+ variant="none"
106
+ placeholder="Paste a link..."
107
+ @keydown="handleKeyDown"
108
+ >
109
+ <div class="mr-0.5 flex items-center">
110
+ <Button
111
+ icon="i-lucide-corner-down-left"
112
+ variant="ghost"
113
+ size="sm"
114
+ :disabled="!url && !active"
115
+ title="Apply link"
116
+ @click="setLink"
117
+ />
118
+
119
+ <Separator
120
+ orientation="vertical"
121
+ class="mx-1 h-6"
122
+ />
123
+
124
+ <Button
125
+ icon="i-lucide-external-link"
126
+ color="neutral"
127
+ variant="ghost"
128
+ size="sm"
129
+ :disabled="!url && !active"
130
+ title="Open in new window"
131
+ @click="openLink"
132
+ />
133
+
134
+ <Button
135
+ icon="i-lucide-trash"
136
+ color="neutral"
137
+ variant="ghost"
138
+ size="sm"
139
+ :disabled="!url && !active"
140
+ title="Remove link"
141
+ @click="removeLink"
142
+ />
143
+ </div>
144
+ </Input>
145
+ </template>
146
+ </Popover>
147
147
  </template>
@@ -1,96 +1,96 @@
1
- # InputWYSIWYG - TipTap WYSIWYG Editor
2
-
3
- A rich text editor component built with TipTap that supports text formatting, images, and more.
4
-
5
- ## Features
6
-
7
- - **Text Formatting**: Bold, italic, underline, strikethrough, code
8
- - **Headings**: H1, H2, H3, H4
9
- - **Lists**: Bullet lists and ordered lists
10
- - **Text Alignment**: Left, center, right, justify
11
- - **Blockquotes and Code Blocks**
12
- - **Links**: Add and edit links with a popover interface
13
- - **Image Upload**: Upload images via button click
14
- - **Image Paste**: ✨ **NEW** - Copy and paste images directly from your computer
15
- - **Horizontal Rules**
16
- - **Undo/Redo**
17
-
18
- ## Image Paste Feature
19
-
20
- The editor now supports pasting images directly from your clipboard! This works in two ways:
21
-
22
- ### 1. Copy from File System
23
- - Copy an image file from your file explorer/finder
24
- - Click into the editor
25
- - Press `Cmd+V` (Mac) or `Ctrl+V` (Windows/Linux)
26
- - The image will automatically upload and insert into the editor
27
-
28
- ### 2. Copy from Screenshot
29
- - Take a screenshot (or copy an image from any application)
30
- - Click into the editor
31
- - Press `Cmd+V` (Mac) or `Ctrl+V` (Windows/Linux)
32
- - The image will automatically upload and insert into the editor
33
-
34
- ### How It Works
35
-
36
- The `ImagePaste` extension intercepts paste events and:
37
- 1. Detects if the clipboard contains image data
38
- 2. Validates the image size (if `maxSize` is configured)
39
- 3. Inserts a placeholder/loading node
40
- 4. Uploads the image to your configured endpoint
41
- 5. Replaces the placeholder with the actual image once uploaded
42
-
43
- ## Usage
44
-
45
- ```vue
46
- <template>
47
- <InputWYSIWYG
48
- name="content"
49
- label="Content"
50
- :image="{
51
- requestOptions: useRequestOptions().getFile(),
52
- uploadPathURL: '/uploads',
53
- maxSize: 5120, // 5MB in KB
54
- bodyKey: 'file',
55
- responseURL: 'url',
56
- responsePath: 'path',
57
- responseName: 'name',
58
- responseSize: 'size',
59
- responseID: 'id',
60
- }"
61
- />
62
- </template>
63
- ```
64
-
65
- ## Configuration
66
-
67
- ### Image Upload Options
68
-
69
- | Option | Type | Default | Description |
70
- |--------|------|---------|-------------|
71
- | `requestOptions` | `AxiosRequestConfig` | - | Axios configuration for upload requests |
72
- | `uploadPathURL` | `string` | - | API endpoint path for uploads |
73
- | `bodyKey` | `string` | `'file'` | Form data key for the file |
74
- | `responseURL` | `string` | `'url'` | Path to URL in upload response |
75
- | `responsePath` | `string` | `'path'` | Path to file path in response |
76
- | `responseName` | `string` | `'name'` | Path to file name in response |
77
- | `responseSize` | `string` | `'size'` | Path to file size in response |
78
- | `responseID` | `string` | `'id'` | Path to file ID in response |
79
- | `maxSize` | `number` | - | Maximum file size in KB |
80
-
81
- ## Extensions
82
-
83
- The component uses the following TipTap extensions:
84
-
85
- - **TextAlign**: For text alignment options
86
- - **ImageUpload**: Custom extension for manual image uploads via button
87
- - **ImagePaste**: Custom extension for pasting images from clipboard
88
-
89
- ## Files
90
-
91
- - `index.vue` - Main component
92
- - `EditorImageUploadExtension.ts` - Extension for manual image upload
93
- - `EditorImageUploadNode.vue` - Vue component for image upload UI
94
- - `EditorImagePasteExtension.ts` - Extension for paste image functionality
95
- - `EditorLinkPopover.vue` - Link editing popover
96
- - `types.ts` - TypeScript type definitions
1
+ # InputWYSIWYG - TipTap WYSIWYG Editor
2
+
3
+ A rich text editor component built with TipTap that supports text formatting, images, and more.
4
+
5
+ ## Features
6
+
7
+ - **Text Formatting**: Bold, italic, underline, strikethrough, code
8
+ - **Headings**: H1, H2, H3, H4
9
+ - **Lists**: Bullet lists and ordered lists
10
+ - **Text Alignment**: Left, center, right, justify
11
+ - **Blockquotes and Code Blocks**
12
+ - **Links**: Add and edit links with a popover interface
13
+ - **Image Upload**: Upload images via button click
14
+ - **Image Paste**: ✨ **NEW** - Copy and paste images directly from your computer
15
+ - **Horizontal Rules**
16
+ - **Undo/Redo**
17
+
18
+ ## Image Paste Feature
19
+
20
+ The editor now supports pasting images directly from your clipboard! This works in two ways:
21
+
22
+ ### 1. Copy from File System
23
+ - Copy an image file from your file explorer/finder
24
+ - Click into the editor
25
+ - Press `Cmd+V` (Mac) or `Ctrl+V` (Windows/Linux)
26
+ - The image will automatically upload and insert into the editor
27
+
28
+ ### 2. Copy from Screenshot
29
+ - Take a screenshot (or copy an image from any application)
30
+ - Click into the editor
31
+ - Press `Cmd+V` (Mac) or `Ctrl+V` (Windows/Linux)
32
+ - The image will automatically upload and insert into the editor
33
+
34
+ ### How It Works
35
+
36
+ The `ImagePaste` extension intercepts paste events and:
37
+ 1. Detects if the clipboard contains image data
38
+ 2. Validates the image size (if `maxSize` is configured)
39
+ 3. Inserts a placeholder/loading node
40
+ 4. Uploads the image to your configured endpoint
41
+ 5. Replaces the placeholder with the actual image once uploaded
42
+
43
+ ## Usage
44
+
45
+ ```vue
46
+ <template>
47
+ <InputWYSIWYG
48
+ name="content"
49
+ label="Content"
50
+ :image="{
51
+ requestOptions: useRequestOptions().getFile(),
52
+ uploadPathURL: '/uploads',
53
+ maxSize: 5120, // 5MB in KB
54
+ bodyKey: 'file',
55
+ responseURL: 'url',
56
+ responsePath: 'path',
57
+ responseName: 'name',
58
+ responseSize: 'size',
59
+ responseID: 'id',
60
+ }"
61
+ />
62
+ </template>
63
+ ```
64
+
65
+ ## Configuration
66
+
67
+ ### Image Upload Options
68
+
69
+ | Option | Type | Default | Description |
70
+ |--------|------|---------|-------------|
71
+ | `requestOptions` | `AxiosRequestConfig` | - | Axios configuration for upload requests |
72
+ | `uploadPathURL` | `string` | - | API endpoint path for uploads |
73
+ | `bodyKey` | `string` | `'file'` | Form data key for the file |
74
+ | `responseURL` | `string` | `'url'` | Path to URL in upload response |
75
+ | `responsePath` | `string` | `'path'` | Path to file path in response |
76
+ | `responseName` | `string` | `'name'` | Path to file name in response |
77
+ | `responseSize` | `string` | `'size'` | Path to file size in response |
78
+ | `responseID` | `string` | `'id'` | Path to file ID in response |
79
+ | `maxSize` | `number` | - | Maximum file size in KB |
80
+
81
+ ## Extensions
82
+
83
+ The component uses the following TipTap extensions:
84
+
85
+ - **TextAlign**: For text alignment options
86
+ - **ImageUpload**: Custom extension for manual image uploads via button
87
+ - **ImagePaste**: Custom extension for pasting images from clipboard
88
+
89
+ ## Files
90
+
91
+ - `index.vue` - Main component
92
+ - `EditorImageUploadExtension.ts` - Extension for manual image upload
93
+ - `EditorImageUploadNode.vue` - Vue component for image upload UI
94
+ - `EditorImagePasteExtension.ts` - Extension for paste image functionality
95
+ - `EditorLinkPopover.vue` - Link editing popover
96
+ - `types.ts` - TypeScript type definitions
@@ -1,56 +1,56 @@
1
1
  <template>
2
- <FieldWrapper v-bind="wrapperProps">
3
- <div :class="ui.container()">
4
- <Editor
5
- v-slot="{ editor }"
6
- v-model="value"
7
- content-type="html"
8
- :editable="editable"
9
- :placeholder="placeholder"
10
- :autofocus="autoFocus"
11
- class="min-h-[200px]"
2
+ <FieldWrapper v-bind="wrapperProps">
3
+ <div :class="ui.container()">
4
+ <Editor
5
+ v-slot="{ editor }"
6
+ v-model="value"
7
+ content-type="html"
8
+ :editable="editable"
9
+ :placeholder="placeholder"
10
+ :autofocus="autoFocus"
11
+ class="min-h-[200px]"
12
12
  :extensions="[
13
13
  TextAlign.configure({
14
14
  types: ['heading', 'paragraph'],
15
15
  alignments: ['left', 'center', 'right', 'justify']
16
16
  }),
17
17
  ...image.requestOptions ? [ImageUpload.configure(image), ImagePaste.configure(image)] : []
18
- ]"
18
+ ]"
19
19
  :ui="{
20
20
  content: '',
21
21
  base: ['min-h-[200px] w-full sm:px-3 py-1']
22
- }"
23
- :handlers="customHandlers"
24
- >
25
- <EditorToolbar
26
- :editor="editor"
27
- :items="toolbarItems"
28
- :class="ui.toolbar()"
29
- >
30
- <template #link>
31
- <EditorLinkPopover
32
- :editor="editor"
33
- auto-open
34
- />
35
- </template>
36
- </EditorToolbar>
37
- <EditorToolbar
38
- :editor="editor"
39
- :items="imageToolbarItems(editor)"
40
- layout="bubble"
22
+ }"
23
+ :handlers="customHandlers"
24
+ >
25
+ <EditorToolbar
26
+ :editor="editor"
27
+ :items="toolbarItems"
28
+ :class="ui.toolbar()"
29
+ >
30
+ <template #link>
31
+ <EditorLinkPopover
32
+ :editor="editor"
33
+ auto-open
34
+ />
35
+ </template>
36
+ </EditorToolbar>
37
+ <EditorToolbar
38
+ :editor="editor"
39
+ :items="imageToolbarItems(editor)"
40
+ layout="bubble"
41
41
  :should-show="({ editor: editor2, view }) => {
42
42
  return editor2.isActive('image') && view.hasFocus();
43
- }"
44
- />
45
-
46
- <EditorSuggestionMenu
47
- :editor="editor"
48
- :items="suggestionItems"
49
- :append-to="appendToBody"
50
- />
51
- </Editor>
52
- </div>
53
- </FieldWrapper>
43
+ }"
44
+ />
45
+
46
+ <EditorSuggestionMenu
47
+ :editor="editor"
48
+ :items="suggestionItems"
49
+ :append-to="appendToBody"
50
+ />
51
+ </Editor>
52
+ </div>
53
+ </FieldWrapper>
54
54
  </template>
55
55
 
56
56
  <script setup>
@@ -1,25 +1,25 @@
1
1
  <template>
2
- <div :class="theme.placeholderWrapper()">
3
- <Icon
4
- :name="icons.uploadIcon"
5
- :class="theme.labelIcon()"
6
- />
7
- <div :class="theme.labelWrapper()">
8
- <p
9
- class="text-primary cursor-pointer font-bold"
10
- @click="$emit('openFile')"
11
- >
12
- {{ selectFileLabel }}
13
- </p>
14
- <p>{{ selectFileSubLabel }}</p>
15
- </div>
16
- <p
17
- v-if="placeholder"
18
- :class="theme.placeholder()"
19
- >
20
- {{ placeholder }}
21
- </p>
22
- </div>
2
+ <div :class="theme.placeholderWrapper()">
3
+ <Icon
4
+ :name="icons.uploadIcon"
5
+ :class="theme.labelIcon()"
6
+ />
7
+ <div :class="theme.labelWrapper()">
8
+ <p
9
+ class="text-primary cursor-pointer font-bold"
10
+ @click="$emit('openFile')"
11
+ >
12
+ {{ selectFileLabel }}
13
+ </p>
14
+ <p>{{ selectFileSubLabel }}</p>
15
+ </div>
16
+ <p
17
+ v-if="placeholder"
18
+ :class="theme.placeholder()"
19
+ >
20
+ {{ placeholder }}
21
+ </p>
22
+ </div>
23
23
  </template>
24
24
 
25
25
  <script setup>
@@ -1,37 +1,37 @@
1
1
  <template>
2
- <div :class="theme.onFailedWrapper()">
3
- <div :class="theme.onFailedFailedImgWrapper()">
4
- <Icon
5
- :name="getFileIcon(selectedFile)"
6
- :class="theme.onFailedFailedIconClass()"
7
- />
8
- </div>
9
- <div :class="theme.onFailedTextWrapper()">
10
- <div class="truncate">
11
- <h1 class="truncate font-bold">
12
- {{ selectedFile.name }}
13
- </h1>
14
- <p class="text-error truncate font-light">
15
- {{ uploadFailedLabel }}
16
- </p>
17
- <Button
18
- variant="link"
19
- :icon="icons.actionRetryIcon"
20
- :class="theme.actionRetryBtnClass()"
21
- color="primary"
22
- @click="$emit('retry')"
23
- >
24
- {{ retryLabel }}
25
- </Button>
26
- </div>
27
- <Icon
28
- :name="icons.actionDeleteIcon"
29
- :class="theme.actionDeleteIconClass()"
30
- title="ลบไฟล์"
31
- @click="$emit('delete')"
32
- />
33
- </div>
34
- </div>
2
+ <div :class="theme.onFailedWrapper()">
3
+ <div :class="theme.onFailedFailedImgWrapper()">
4
+ <Icon
5
+ :name="getFileIcon(selectedFile)"
6
+ :class="theme.onFailedFailedIconClass()"
7
+ />
8
+ </div>
9
+ <div :class="theme.onFailedTextWrapper()">
10
+ <div class="truncate">
11
+ <h1 class="truncate font-bold">
12
+ {{ selectedFile.name }}
13
+ </h1>
14
+ <p class="text-error truncate font-light">
15
+ {{ uploadFailedLabel }}
16
+ </p>
17
+ <Button
18
+ variant="link"
19
+ :icon="icons.actionRetryIcon"
20
+ :class="theme.actionRetryBtnClass()"
21
+ color="primary"
22
+ @click="$emit('retry')"
23
+ >
24
+ {{ retryLabel }}
25
+ </Button>
26
+ </div>
27
+ <Icon
28
+ :name="icons.actionDeleteIcon"
29
+ :class="theme.actionDeleteIconClass()"
30
+ title="ลบไฟล์"
31
+ @click="$emit('delete')"
32
+ />
33
+ </div>
34
+ </div>
35
35
  </template>
36
36
 
37
37
  <script setup>
@@ -1,28 +1,28 @@
1
1
  <template>
2
- <div :class="theme.onLoadingWrapper()">
3
- <div :class="theme.onLoadingPlaceholderWrapper()">
4
- <Icon
5
- :name="getFileIcon(selectedFile)"
6
- :class="theme.onLoadingPlaceholderIconClass()"
7
- />
8
- </div>
9
- <div :class="theme.onLoadingTextWrapper()">
10
- <div class="truncate">
11
- <h1 class="truncate font-bold">
12
- {{ selectedFile.name }}
13
- </h1>
14
- <p class="truncate font-light text-gray-400">
15
- {{ getFileSize(selectedFile) }} - {{ percent }}% {{ uploadingLabel }}
16
- </p>
17
- </div>
18
- <div>
19
- <Icon
20
- :name="icons.loadingIcon"
21
- :class="theme.onLoadingLoadingIconClass()"
22
- />
23
- </div>
24
- </div>
25
- </div>
2
+ <div :class="theme.onLoadingWrapper()">
3
+ <div :class="theme.onLoadingPlaceholderWrapper()">
4
+ <Icon
5
+ :name="getFileIcon(selectedFile)"
6
+ :class="theme.onLoadingPlaceholderIconClass()"
7
+ />
8
+ </div>
9
+ <div :class="theme.onLoadingTextWrapper()">
10
+ <div class="truncate">
11
+ <h1 class="truncate font-bold">
12
+ {{ selectedFile.name }}
13
+ </h1>
14
+ <p class="truncate font-light text-gray-400">
15
+ {{ getFileSize(selectedFile) }} - {{ percent }}% {{ uploadingLabel }}
16
+ </p>
17
+ </div>
18
+ <div>
19
+ <Icon
20
+ :name="icons.loadingIcon"
21
+ :class="theme.onLoadingLoadingIconClass()"
22
+ />
23
+ </div>
24
+ </div>
25
+ </div>
26
26
  </template>
27
27
 
28
28
  <script setup>