quasar-ui-danx 0.0.10 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. package/package.json +3 -2
  2. package/src/components/ActionTable/ActionTable.vue +135 -0
  3. package/src/components/ActionTable/BatchActionMenu.vue +60 -0
  4. package/src/components/ActionTable/EmptyTableState.vue +33 -0
  5. package/src/components/ActionTable/Filters/CollapsableFiltersSidebar.vue +36 -0
  6. package/src/components/ActionTable/Filters/FilterGroupItem.vue +28 -0
  7. package/src/components/ActionTable/Filters/FilterGroupList.vue +76 -0
  8. package/src/components/ActionTable/Filters/FilterListToggle.vue +50 -0
  9. package/src/components/ActionTable/Filters/FilterableField.vue +141 -0
  10. package/src/components/ActionTable/Form/Fields/BooleanField.vue +37 -0
  11. package/src/components/ActionTable/Form/Fields/ConfirmPasswordField.vue +46 -0
  12. package/src/components/ActionTable/Form/Fields/DateField.vue +59 -0
  13. package/src/components/ActionTable/Form/Fields/DateRangeField.vue +110 -0
  14. package/src/components/ActionTable/Form/Fields/DateTimeField.vue +50 -0
  15. package/src/components/ActionTable/Form/Fields/DateTimePicker.vue +59 -0
  16. package/src/components/ActionTable/Form/Fields/EditableDiv.vue +39 -0
  17. package/src/components/ActionTable/Form/Fields/FieldLabel.vue +32 -0
  18. package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +78 -0
  19. package/src/components/ActionTable/Form/Fields/InlineDateTimeField.vue +44 -0
  20. package/src/components/ActionTable/Form/Fields/IntegerField.vue +26 -0
  21. package/src/components/ActionTable/Form/Fields/LabeledInput.vue +63 -0
  22. package/src/components/ActionTable/Form/Fields/MultiFileField.vue +91 -0
  23. package/src/components/ActionTable/Form/Fields/MultiKeywordField.vue +57 -0
  24. package/src/components/ActionTable/Form/Fields/NewPasswordField.vue +39 -0
  25. package/src/components/ActionTable/Form/Fields/NumberField.vue +94 -0
  26. package/src/components/ActionTable/Form/Fields/NumberRangeField.vue +140 -0
  27. package/src/components/ActionTable/Form/Fields/SelectDrawer.vue +136 -0
  28. package/src/components/ActionTable/Form/Fields/SelectField.vue +318 -0
  29. package/src/components/ActionTable/Form/Fields/SelectWithChildrenField.vue +81 -0
  30. package/src/components/ActionTable/Form/Fields/SingleFileField.vue +78 -0
  31. package/src/components/ActionTable/Form/Fields/TextField.vue +82 -0
  32. package/src/components/ActionTable/Form/Fields/WysiwygField.vue +46 -0
  33. package/src/components/ActionTable/Form/Fields/index.ts +23 -0
  34. package/src/components/ActionTable/Form/RenderedForm.vue +74 -0
  35. package/src/components/ActionTable/RenderComponentColumn.vue +22 -0
  36. package/src/components/ActionTable/TableSummaryRow.vue +95 -0
  37. package/src/components/ActionTable/index.ts +15 -0
  38. package/src/components/ActionTable/listActions.ts +361 -0
  39. package/src/components/ActionTable/tableColumns.ts +72 -0
  40. package/src/components/ActionTable/tableHelpers.ts +83 -0
  41. package/src/components/Utility/CollapsableSidebar.vue +119 -0
  42. package/src/components/Utility/ContentDrawer.vue +70 -0
  43. package/src/components/Utility/Dialogs/ConfirmDialog.vue +132 -0
  44. package/src/components/Utility/Dialogs/FullScreenDialog.vue +46 -0
  45. package/src/components/Utility/Dialogs/InfoDialog.vue +92 -0
  46. package/src/components/Utility/Dialogs/InputDialog.vue +35 -0
  47. package/src/components/Utility/Transitions/ListTransition.vue +50 -0
  48. package/src/components/Utility/Transitions/SlideTransition.vue +63 -0
  49. package/src/components/Utility/Transitions/StaggeredListTransition.vue +97 -0
  50. package/src/components/Utility/index.ts +9 -0
  51. package/src/components/index.ts +3 -0
  52. package/src/helpers/FileUpload.ts +294 -0
  53. package/src/helpers/FlashMessages.ts +79 -0
  54. package/src/helpers/array.ts +37 -0
  55. package/src/helpers/compatibility.ts +64 -0
  56. package/src/helpers/date.ts +5 -0
  57. package/src/helpers/download.ts +192 -0
  58. package/src/helpers/downloadPdf.ts +92 -0
  59. package/src/helpers/files.ts +52 -0
  60. package/src/helpers/formats.ts +183 -0
  61. package/src/helpers/http.ts +62 -0
  62. package/src/helpers/index.ts +10 -1
  63. package/src/helpers/multiFileUpload.ts +68 -0
  64. package/src/helpers/singleFileUpload.ts +54 -0
  65. package/src/helpers/storage.ts +8 -0
@@ -0,0 +1,318 @@
1
+ <template>
2
+ <div>
3
+ <QSelect
4
+ ref="selectField"
5
+ v-bind="$props"
6
+ :model-value="selectedValue"
7
+ outlined
8
+ hide-dropdown-icon
9
+ dense
10
+ emit-value
11
+ :use-input="filterable"
12
+ :hide-selected="filterable && isShowing && !$props.multiple"
13
+ :input-debounce="100"
14
+ :options="filteredOptions"
15
+ option-label="label"
16
+ option-value="value"
17
+ placeholder=""
18
+ :input-class="{'is-hidden': !isShowing, [inputClass]: true}"
19
+ class="max-w-full"
20
+ @filter="onFilter"
21
+ @clear="onClear"
22
+ @popup-show="onShow"
23
+ @popup-hide="onHide"
24
+ @update:model-value="onUpdate"
25
+ >
26
+ <template #append>
27
+ <DropDownIcon
28
+ class="w-4 transition"
29
+ :class="isShowing ? 'rotate-180' : ''"
30
+ />
31
+ </template>
32
+ <template #selected>
33
+ <div
34
+ v-if="$props.multiple"
35
+ class="flex gap-y-1 overflow-hidden"
36
+ :class="{'flex-nowrap gap-y-0': chipLimit === 1, [selectionClass]: true}"
37
+ >
38
+ <template v-if="chipOptions.length > 0">
39
+ <QChip
40
+ v-for="chipOption in chipOptions"
41
+ :key="'selected-' + chipOption.label"
42
+ class="!mr-1"
43
+ >{{ chipOption.label }}
44
+ </QChip>
45
+ <QChip
46
+ v-if="selectedOptions.length > chipOptions.length"
47
+ class="!mr-1"
48
+ >
49
+ +{{ selectedOptions.length - chipOptions.length }}
50
+ </QChip>
51
+ </template>
52
+ <template v-else>
53
+ {{ placeholder }}
54
+ </template>
55
+ </div>
56
+ <div
57
+ v-else
58
+ :class="selectionClass"
59
+ >{{ selectedLabel }}
60
+ </div>
61
+ </template>
62
+ </QSelect>
63
+ </div>
64
+ </template>
65
+ <script setup>
66
+ import { ChevronDownIcon as DropDownIcon } from "@heroicons/vue/outline";
67
+ import { QSelect } from "quasar";
68
+ import { computed, isRef, nextTick, ref } from "vue";
69
+
70
+ const emit = defineEmits(["update:model-value", "search"]);
71
+ const props = defineProps({
72
+ ...QSelect.props,
73
+ modelValue: {
74
+ type: [Array, String, Number, Object],
75
+ default: undefined
76
+ },
77
+ placeholder: {
78
+ type: String,
79
+ default: ""
80
+ },
81
+ selectionLabel: {
82
+ type: String,
83
+ default: null
84
+ },
85
+ chipLimit: {
86
+ type: Number,
87
+ default: 3
88
+ },
89
+ inputClass: {
90
+ type: String,
91
+ default: ""
92
+ },
93
+ selectionClass: {
94
+ type: String,
95
+ default: ""
96
+ },
97
+ options: {
98
+ type: Array,
99
+ default: () => []
100
+ },
101
+ filterable: Boolean,
102
+ filterFn: {
103
+ type: Function,
104
+ default: null
105
+ }
106
+ });
107
+
108
+ const selectField = ref(null);
109
+
110
+ // The filter applied to the dropdown list of options
111
+ const filter = ref(null);
112
+ // Is showing the dropdown list
113
+ const isShowing = ref(false);
114
+
115
+ /**
116
+ * The options formatted so each has a label, value and selectionLabel
117
+ * @type {ComputedRef<{selectionLabel: string, label: string, value: string|*}[]>}
118
+ */
119
+ const computedOptions = computed(() => {
120
+ let options = props.options;
121
+ if (props.placeholder && !props.multiple && !props.filterable) {
122
+ options = [{ label: props.placeholder, value: null }, ...props.options];
123
+ }
124
+ options = options.map((o) => {
125
+ let opt = isRef(o) ? o.value : o;
126
+ return {
127
+ label: resolveLabel(opt),
128
+ value: resolveValue(opt),
129
+ selectionLabel: resolveSelectionLabel(opt)
130
+ };
131
+ });
132
+ return options;
133
+ });
134
+
135
+ const filteredOptions = computed(() => {
136
+ if (filter.value && !props.filterFn) {
137
+ return computedOptions.value.filter(o => o.label.toLocaleLowerCase().indexOf(filter.value.toLowerCase()) > -1);
138
+ } else {
139
+ return computedOptions.value;
140
+ }
141
+ });
142
+
143
+ /**
144
+ * The value selected. Basically a wrapper to better handle null values (see onUpdate function for more details)
145
+ * @type {ComputedRef<unknown>}
146
+ */
147
+ const selectedValue = computed(() => {
148
+ if (props.multiple) {
149
+ const arrVal = Array.isArray(props.modelValue) ? props.modelValue : [];
150
+ return arrVal.map((v) => {
151
+ return v === null ? "__null__" : v;
152
+ }) || [];
153
+ } else {
154
+ return props.modelValue === null ? "__null__" : props.modelValue;
155
+ }
156
+ });
157
+
158
+ /**
159
+ * The selected options. The list of options from the computedOptions list that are selected as defined by the modelValue
160
+ * @type {ComputedRef<*>}
161
+ */
162
+ const selectedOptions = computed(() => {
163
+ let values = selectedValue.value;
164
+ if (!props.multiple) {
165
+ values = (values || values === 0) ? [values] : [];
166
+ }
167
+ return computedOptions.value.filter((o) => {
168
+ return values.includes(o.value) || values.map(v => typeof v === "object" && v.id).includes(o.value?.id);
169
+ });
170
+ });
171
+
172
+ /**
173
+ * The label to display in the input field of the dropdown for a selected state, non-selected state or an active filtering state.
174
+ * NOTE: Only applies to single select (not multiselect)
175
+ * @type {ComputedRef<unknown>}
176
+ */
177
+ const selectedLabel = computed(() => {
178
+ if (props.filterable && isShowing.value) return "";
179
+
180
+ if (!selectedOptions.value || selectedOptions.value.length === 0) {
181
+ return props.placeholder || "(Select Option)";
182
+ }
183
+ return selectedOptions.value[0].selectionLabel;
184
+ });
185
+
186
+ /**
187
+ * The options to display as chips (limited by chipLimit prop)
188
+ * @type {ComputedRef<*>}
189
+ */
190
+ const chipOptions = computed(() => {
191
+ return selectedOptions.value.slice(0, props.chipLimit);
192
+ });
193
+
194
+ /**
195
+ * Resolve the label of the option. This will display as the text in the dropdown list
196
+ * @param option
197
+ * @returns {*|string}
198
+ */
199
+ function resolveLabel(option) {
200
+ if (typeof option === "string") {
201
+ return option;
202
+ }
203
+ if (typeof props.optionLabel === "string") {
204
+ return option[props.optionLabel];
205
+ }
206
+ if (typeof props.optionLabel === "function") {
207
+ return props.optionLabel(option);
208
+ }
209
+ return option?.label;
210
+ }
211
+
212
+ /**
213
+ * Resolve the label to display in the input field of the dropdown when an option is selected
214
+ * NOTE: Does not apply to multiselect, single select only
215
+ * @param option
216
+ * @returns {*|{default: null, type: String | StringConstructor}|string}
217
+ */
218
+ function resolveSelectionLabel(option) {
219
+ if (typeof option === "string") {
220
+ return option;
221
+ }
222
+ if (typeof props.selectionLabel === "string") {
223
+ return option[props.selectionLabel];
224
+ }
225
+ if (typeof props.selectionLabel === "function") {
226
+ return props.selectionLabel(option);
227
+ }
228
+ return option?.selectionLabel || option?.label;
229
+ }
230
+
231
+ /**
232
+ * Resolve the value of the option using either defaults or the provided optionValue prop
233
+ * @param option
234
+ * @returns {string|*|string}
235
+ */
236
+ function resolveValue(option) {
237
+ if (typeof option === "string") {
238
+ return option;
239
+ }
240
+ let value = option.value;
241
+ if (typeof props.optionValue === "string") {
242
+ value = option[props.optionValue];
243
+ } else if (typeof props.optionValue === "function") {
244
+ value = props.optionValue(option);
245
+ }
246
+ // Note the __null__ special case here. See the onUpdate function for more details
247
+ return value === null ? "__null__" : value;
248
+ }
249
+
250
+ /**
251
+ * Handle the update event from the QSelect
252
+ * NOTE: casts all null values as a special __null__ string as the null character is handled the same as undefined by QSelect.
253
+ * SelectField differentiates between these 2 to provide a null value selected state vs undefined is no value selected.
254
+ * @param value
255
+ */
256
+ function onUpdate(value) {
257
+ if (Array.isArray(value)) {
258
+ value = value.map((v) => v === "__null__" ? null : v);
259
+ }
260
+ emit("update:model-value", value === "__null__" ? null : value);
261
+ }
262
+
263
+ /** XXX: This tells us when we should apply the filter. QSelect likes to trigger a new filter everytime you open the dropdown
264
+ * But most of the time when you open the dropdown it is already filtered and annoying to try and clear the previous filter
265
+ **/
266
+ const shouldFilter = ref(false);
267
+
268
+ /**
269
+ * Filter the options list. Do this asynchronously for the update so that the QSelect can update its internal state first, then update SelectField
270
+ * @param val
271
+ * @param update
272
+ */
273
+ async function onFilter(val, update) {
274
+ if (!props.filterFn) {
275
+ filter.value = val;
276
+ await nextTick(update);
277
+ } else {
278
+ update();
279
+ if (shouldFilter.value === false) return;
280
+ if (val !== null && val !== filter.value) {
281
+ filter.value = val;
282
+ if (props.filterFn) {
283
+ await props.filterFn(val);
284
+ }
285
+ }
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Clear the selected value using undefined. SelectField differentiates between null and undefined.
291
+ * See the onUpdate function for more details
292
+ */
293
+ function onClear() {
294
+ emit("update:model-value", undefined);
295
+ }
296
+
297
+ /**
298
+ * Handle behavior when showing the dropdown
299
+ */
300
+ function onShow() {
301
+ isShowing.value = true;
302
+
303
+ // XXX: See description on shouldFilter declaration. Only allow filtering after dropdown is ALREADY opened
304
+ shouldFilter.value = false;
305
+ nextTick(() => {
306
+ shouldFilter.value = true;
307
+ selectField.value.focus();
308
+ });
309
+ }
310
+
311
+ /**
312
+ * Handle behavior when hiding the dropdown
313
+ */
314
+ function onHide() {
315
+ isShowing.value = false;
316
+ shouldFilter.value = false;
317
+ }
318
+ </script>
@@ -0,0 +1,81 @@
1
+ <template>
2
+ <div>
3
+ <template v-if="!loading && !options.length">
4
+ <div class="text-gray-silver">No options available</div>
5
+ </template>
6
+ <SelectField
7
+ v-model="selectedOption"
8
+ :options="options"
9
+ :label="label"
10
+ :placeholder="placeholder"
11
+ :option-value="opt => opt"
12
+ :loading="loading"
13
+ @update:model-value="onSelectOption"
14
+ />
15
+ <div v-if="selectedOption">
16
+ <QCheckbox
17
+ v-for="child in selectedOption.children"
18
+ :key="child.id"
19
+ :model-value="selectedChildren.includes(child.id)"
20
+ :field="child"
21
+ class="mt-3"
22
+ @update:model-value="onSelectChild(child)"
23
+ >
24
+ <div>{{ child.label }}</div>
25
+ <div class="text-xs text-gray-silver">{{ child.name }}</div>
26
+ </QCheckbox>
27
+ </div>
28
+ </div>
29
+ </template>
30
+ <script setup>
31
+ import SelectField from "danx/src/components/ActionTable/Form/Fields/SelectField";
32
+ import { remove } from "danx/src/helpers/array";
33
+ import { ref, watch } from "vue";
34
+
35
+ const emit = defineEmits(["update:model-value"]);
36
+ const props = defineProps({
37
+ modelValue: {
38
+ type: Array,
39
+ default: () => ([])
40
+ },
41
+ label: {
42
+ type: String,
43
+ default: "Selection"
44
+ },
45
+ placeholder: {
46
+ type: String,
47
+ default: "Select an option"
48
+ },
49
+ options: {
50
+ type: Array,
51
+ default: () => []
52
+ },
53
+ loading: Boolean
54
+ });
55
+
56
+ function resolveSelectedOption() {
57
+ if (props.modelValue?.length > 0) {
58
+ return props.options.find((option) => option.children.find(child => props.modelValue.includes(child.id)));
59
+ }
60
+
61
+ return null;
62
+ }
63
+ const selectedOption = ref(resolveSelectedOption());
64
+ const selectedChildren = ref(props.modelValue || []);
65
+ function onSelectChild(child) {
66
+ if (selectedChildren.value.includes(child.id)) {
67
+ selectedChildren.value = remove(selectedChildren.value, child.id);
68
+ } else {
69
+ selectedChildren.value.push(child.id);
70
+ }
71
+ emit("update:model-value", selectedChildren.value.length > 0 ? selectedChildren.value : undefined);
72
+ }
73
+ function onSelectOption() {
74
+ selectedChildren.value = [];
75
+ emit("update:model-value", undefined);
76
+ }
77
+ watch(() => props.modelValue, (value) => {
78
+ selectedOption.value = resolveSelectedOption();
79
+ selectedChildren.value = value || [];
80
+ });
81
+ </script>
@@ -0,0 +1,78 @@
1
+ <template>
2
+ <div
3
+ class="max-w-full relative overflow-auto"
4
+ :class="{'p-4 border rounded border-gray-medium text-center': !readonly}"
5
+ @dragover.prevent
6
+ @drop.prevent="onDrop"
7
+ >
8
+ <FieldLabel
9
+ :field="field"
10
+ :show-name="showName"
11
+ class="text-sm font-semibold"
12
+ />
13
+ <div
14
+ v-if="!disable && !readonly"
15
+ class="text-sm mt-2"
16
+ >
17
+ <a
18
+ class="text-blue-base"
19
+ @click="$refs.file.click()"
20
+ >Upload</a>
21
+ <a
22
+ v-if="uploadedFile"
23
+ class="ml-3 text-red-dark"
24
+ @click="onClear"
25
+ >Clear</a>
26
+ <input
27
+ ref="file"
28
+ class="hidden"
29
+ type="file"
30
+ @change="onFileSelected"
31
+ />
32
+ </div>
33
+
34
+ <ImagePreview
35
+ v-if="!readonly || uploadedFile"
36
+ class="w-32 cursor-pointer mt-2"
37
+ :class="{'border border-dashed border-blue-base': !uploadedFile, 'mx-auto': !readonly}"
38
+ :image="uploadedFile"
39
+ downloadable
40
+ @click="!disable && $refs.file.click()"
41
+ />
42
+ <div
43
+ v-else-if="readonly"
44
+ class="py-1"
45
+ >--
46
+ </div>
47
+ </div>
48
+ </template>
49
+
50
+ <script setup>
51
+ import ImagePreview from "components/Common/ImagePreview";
52
+ import FieldLabel from "danx/src/components/ActionTable/Form/Fields/FieldLabel";
53
+ import { useSingleFileUpload } from "src/helpers/singleFileUpload";
54
+ import { onMounted } from "vue";
55
+
56
+ const emit = defineEmits(["update:model-value"]);
57
+ const props = defineProps({
58
+ modelValue: {
59
+ type: [Object, String],
60
+ default: null
61
+ },
62
+ field: {
63
+ type: Object,
64
+ required: true
65
+ },
66
+ showName: Boolean,
67
+ disable: Boolean,
68
+ readonly: Boolean
69
+ });
70
+ const { onComplete, onDrop, onFileSelected, uploadedFile, onClear } = useSingleFileUpload();
71
+ onComplete(() => emit("update:model-value", uploadedFile.value));
72
+
73
+ onMounted(() => {
74
+ if (props.modelValue) {
75
+ uploadedFile.value = props.modelValue;
76
+ }
77
+ });
78
+ </script>
@@ -0,0 +1,82 @@
1
+ <template>
2
+ <div>
3
+ <QInput
4
+ v-if="!readonly"
5
+ :data-dusk="'text-field-' + field?.id"
6
+ :data-testid="'text-field-' + field?.id"
7
+ :placeholder="field?.placeholder"
8
+ outlined
9
+ dense
10
+ :disable="disabled"
11
+ :label-slot="!noLabel"
12
+ :input-class="inputClass"
13
+ :class="parentClass"
14
+ stack-label
15
+ :type="type"
16
+ :model-value="modelValue"
17
+ :debounce="debounce"
18
+ @keydown.enter="$emit('submit')"
19
+ @update:model-value="$emit('update:model-value', $event)"
20
+ >
21
+ <template #label>
22
+ <FieldLabel
23
+ :field="field"
24
+ :label="label"
25
+ :show-name="showName"
26
+ :class="labelClass"
27
+ />
28
+ </template>
29
+ </QInput>
30
+ <div v-if="readonly">
31
+ <LabelValueBlock
32
+ :label="label || field.label"
33
+ :value="modelValue"
34
+ />
35
+ </div>
36
+ </div>
37
+ </template>
38
+
39
+ <script setup>
40
+ import LabelValueBlock from "components/Common/LabelValueBlock";
41
+ import FieldLabel from "danx/src/components/ActionTable/Form/Fields/FieldLabel";
42
+
43
+ defineEmits(["update:model-value", "submit"]);
44
+ defineProps({
45
+ modelValue: {
46
+ type: [String, Number],
47
+ default: ""
48
+ },
49
+ field: {
50
+ type: Object,
51
+ default: null
52
+ },
53
+ type: {
54
+ type: String,
55
+ default: "text"
56
+ },
57
+ label: {
58
+ type: String,
59
+ default: null
60
+ },
61
+ labelClass: {
62
+ type: String,
63
+ default: "text-sm text-gray-shadow"
64
+ },
65
+ parentClass: {
66
+ type: String,
67
+ default: ""
68
+ },
69
+ inputClass: {
70
+ type: String,
71
+ default: ""
72
+ },
73
+ noLabel: Boolean,
74
+ showName: Boolean,
75
+ disabled: Boolean,
76
+ readonly: Boolean,
77
+ debounce: {
78
+ type: [String, Number],
79
+ default: 0
80
+ }
81
+ });
82
+ </script>
@@ -0,0 +1,46 @@
1
+ <template>
2
+ <div>
3
+ <FieldLabel
4
+ v-if="!noLabel"
5
+ :field="field"
6
+ :show-name="showName"
7
+ class="text-sm font-semibold text-gray-shadow block mb-2"
8
+ />
9
+ <template v-if="readonly">
10
+ <div
11
+ class="border border-gray-300 rounded-md p-2 bg-gray-100"
12
+ v-html="modelValue"
13
+ />
14
+ </template>
15
+ <TinyMceEditor
16
+ v-else
17
+ class="mt-2"
18
+ :api-key="apiKey"
19
+ :disabled="disable"
20
+ :model-value="modelValue"
21
+ @update:model-value="$emit('update:model-value', $event)"
22
+ />
23
+ </div>
24
+ </template>
25
+
26
+ <script setup>
27
+ import { default as TinyMceEditor } from "@tinymce/tinymce-vue";
28
+ import { apiKey } from "components/Cms/SlideshowEditor/ContentEditor/tinymce-config";
29
+ import FieldLabel from "danx/src/components/ActionTable/Form/Fields/FieldLabel";
30
+
31
+ defineEmits(["update:model-value"]);
32
+ defineProps({
33
+ modelValue: {
34
+ type: [String, Number],
35
+ default: null
36
+ },
37
+ field: {
38
+ type: Object,
39
+ required: true
40
+ },
41
+ noLabel: Boolean,
42
+ showName: Boolean,
43
+ disable: Boolean,
44
+ readonly: Boolean
45
+ });
46
+ </script>
@@ -0,0 +1,23 @@
1
+ export { default as BooleanField } from "./BooleanField";
2
+ export { default as ConfirmPasswordField } from "./ConfirmPasswordField";
3
+ export { default as DateField } from "./DateField";
4
+ export { default as DateRangeField } from "./DateRangeField";
5
+ export { default as DateTimeField } from "./DateTimeField";
6
+ export { default as DateTimePicker } from "./DateTimePicker";
7
+ export { default as EditableDiv } from "./EditableDiv";
8
+ export { default as FieldLabel } from "./FieldLabel";
9
+ export { default as FileUploadButton } from "./FileUploadButton";
10
+ export { default as InlineDateTimeField } from "./InlineDateTimeField";
11
+ export { default as IntegerField } from "./IntegerField";
12
+ export { default as LabeledInput } from "./LabeledInput";
13
+ export { default as MultiFileField } from "./MultiFileField";
14
+ export { default as MultiKeywordField } from "./MultiKeywordField";
15
+ export { default as NewPasswordField } from "./NewPasswordField";
16
+ export { default as NumberField } from "./NumberField";
17
+ export { default as NumberRangeField } from "./NumberRangeField";
18
+ export { default as SelectDrawer } from "./SelectDrawer";
19
+ export { default as SelectField } from "./SelectField";
20
+ export { default as SelectWithChildrenField } from "./SelectWithChildrenField";
21
+ export { default as SingleFileField } from "./SingleFileField";
22
+ export { default as TextField } from "./TextField";
23
+ export { default as WysiwygField } from "./WysiwygField";
@@ -0,0 +1,74 @@
1
+ <template>
2
+ <div class="rendered-form">
3
+ <div
4
+ v-for="(field, index) in mappedFields"
5
+ :key="field.id"
6
+ :class="{ 'mt-4': index > 0 }"
7
+ >
8
+ <Component
9
+ :is="field.component"
10
+ v-model="fieldValues[field.name]"
11
+ :field="field"
12
+ :label="field.label || undefined"
13
+ :no-label="noLabel"
14
+ :show-name="showName"
15
+ :disable="disable"
16
+ :readonly="readonly"
17
+ @update:model-value="onInput(field.name, $event)"
18
+ />
19
+ </div>
20
+ </div>
21
+ </template>
22
+ <script setup>
23
+ import BooleanField from "danx/src/components/ActionTable/Form/Fields/BooleanField";
24
+ import DateField from "danx/src/components/ActionTable/Form/Fields/DateField";
25
+ import DateRangeField from "danx/src/components/ActionTable/Form/Fields/DateRangeField";
26
+ import IntegerField from "danx/src/components/ActionTable/Form/Fields/IntegerField";
27
+ import MultiFileField from "danx/src/components/ActionTable/Form/Fields/MultiFileField";
28
+ import NumberField from "danx/src/components/ActionTable/Form/Fields/NumberField";
29
+ import SingleFileField from "danx/src/components/ActionTable/Form/Fields/SingleFileField";
30
+ import TextField from "danx/src/components/ActionTable/Form/Fields/TextField";
31
+ import WysiwygField from "danx/src/components/ActionTable/Form/Fields/WysiwygField";
32
+ import { reactive } from "vue";
33
+
34
+ const emit = defineEmits(["update:values"]);
35
+ const props = defineProps({
36
+ values: {
37
+ type: Object,
38
+ default: null
39
+ },
40
+ fields: {
41
+ type: Array,
42
+ required: true
43
+ },
44
+ noLabel: Boolean,
45
+ showName: Boolean,
46
+ disable: Boolean,
47
+ readonly: Boolean
48
+ });
49
+
50
+ const FORM_FIELD_MAP = {
51
+ BOOLEAN: BooleanField,
52
+ DATE: DateField,
53
+ DATE_RANGE: DateRangeField,
54
+ INTEGER: IntegerField,
55
+ NUMBER: NumberField,
56
+ TEXT: TextField,
57
+ SINGLE_FILE: SingleFileField,
58
+ MULTI_FILE: MultiFileField,
59
+ WYSIWYG: WysiwygField
60
+ };
61
+
62
+ const mappedFields = props.fields.map((field) => ({
63
+ placeholder: `Enter ${field.label}`,
64
+ ...field,
65
+ component: FORM_FIELD_MAP[field.type],
66
+ default: field.type === "BOOLEAN" ? false : ""
67
+ }));
68
+
69
+ const fieldValues = reactive(props.values || {});
70
+
71
+ function onInput(key, value) {
72
+ emit("update:values", { ...fieldValues, [key]: value });
73
+ }
74
+ </script>