quasar-ui-danx 0.4.9 → 0.4.12

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 (57) hide show
  1. package/dist/danx.es.js +6509 -6230
  2. package/dist/danx.es.js.map +1 -1
  3. package/dist/danx.umd.js +7 -7
  4. package/dist/danx.umd.js.map +1 -1
  5. package/dist/style.css +1 -1
  6. package/index.d.ts +7 -0
  7. package/index.ts +1 -0
  8. package/package.json +8 -3
  9. package/src/components/ActionTable/ActionMenu.vue +26 -31
  10. package/src/components/ActionTable/ActionTable.vue +4 -1
  11. package/src/components/ActionTable/Columns/ActionTableColumn.vue +14 -6
  12. package/src/components/ActionTable/Columns/ActionTableHeaderColumn.vue +63 -42
  13. package/src/components/ActionTable/Form/ActionForm.vue +55 -0
  14. package/src/components/ActionTable/Form/Fields/EditOnClickTextField.vue +11 -5
  15. package/src/components/ActionTable/Form/Fields/FieldLabel.vue +18 -15
  16. package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +1 -0
  17. package/src/components/ActionTable/Form/Fields/LabelValueBlock.vue +44 -15
  18. package/src/components/ActionTable/Form/Fields/MultiFileField.vue +1 -1
  19. package/src/components/ActionTable/Form/Fields/MultiKeywordField.vue +12 -13
  20. package/src/components/ActionTable/Form/Fields/NumberField.vue +40 -55
  21. package/src/components/ActionTable/Form/Fields/SelectField.vue +4 -3
  22. package/src/components/ActionTable/Form/Fields/TextField.vue +31 -12
  23. package/src/components/ActionTable/Form/RenderedForm.vue +11 -10
  24. package/src/components/ActionTable/Form/index.ts +1 -0
  25. package/src/components/ActionTable/Layouts/ActionTableLayout.vue +3 -3
  26. package/src/components/ActionTable/TableSummaryRow.vue +48 -37
  27. package/src/components/ActionTable/Toolbars/ActionToolbar.vue +2 -2
  28. package/src/components/ActionTable/listControls.ts +3 -2
  29. package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +30 -5
  30. package/src/components/Utility/Files/FilePreview.vue +72 -12
  31. package/src/components/Utility/Popovers/PopoverMenu.vue +34 -29
  32. package/src/config/index.ts +2 -1
  33. package/src/helpers/FileUpload.ts +59 -8
  34. package/src/helpers/actions.ts +27 -27
  35. package/src/helpers/download.ts +8 -2
  36. package/src/helpers/formats.ts +79 -9
  37. package/src/helpers/multiFileUpload.ts +6 -4
  38. package/src/helpers/objectStore.ts +14 -17
  39. package/src/helpers/request.ts +12 -0
  40. package/src/helpers/singleFileUpload.ts +63 -55
  41. package/src/helpers/utils.ts +11 -0
  42. package/src/index.ts +1 -0
  43. package/src/styles/danx.scss +5 -0
  44. package/src/styles/index.scss +1 -0
  45. package/src/styles/quasar-reset.scss +2 -0
  46. package/src/styles/themes/danx/action-table.scss +24 -13
  47. package/src/styles/themes/danx/forms.scss +1 -19
  48. package/src/types/actions.d.ts +13 -4
  49. package/src/types/controls.d.ts +4 -4
  50. package/src/types/fields.d.ts +10 -9
  51. package/src/types/files.d.ts +10 -5
  52. package/src/types/index.d.ts +0 -1
  53. package/src/types/requests.d.ts +2 -0
  54. package/src/types/tables.d.ts +28 -22
  55. package/src/{vue-plugin.js → vue-plugin.ts} +5 -4
  56. package/tsconfig.json +1 -0
  57. package/types/index.d.ts +2 -0
package/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ declare module "quasar-ui-danx";
2
+
3
+ export * from "./src/components";
4
+ export * from "./src/config";
5
+ export * from "./src/helpers";
6
+ export * from "./src/svg";
7
+ export * from "./src/types";
package/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./src";
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "quasar-ui-danx",
3
- "version": "0.4.9",
3
+ "version": "0.4.12",
4
4
  "author": "Dan <dan@flytedesk.com>",
5
5
  "description": "DanX Vue / Quasar component library",
6
6
  "license": "MIT",
7
7
  "type": "module",
8
- "main": "dist/danx.es.js",
9
- "module": "dist/danx.es.js",
8
+ "main": "./dist/danx.umd.js",
9
+ "module": "./dist/danx.es.js",
10
+ "types": "types/index.d.ts",
10
11
  "scripts": {
11
12
  "dev": "cd dev && quasar dev && cd ..",
12
13
  "build": "vite build",
@@ -17,6 +18,10 @@
17
18
  "type": "git",
18
19
  "url": "https://github.com/flytedan/quasar-ui-danx"
19
20
  },
21
+ "peerDependencies": {
22
+ "quasar": "^2.0.0",
23
+ "vue": "^3.4.21"
24
+ },
20
25
  "devDependencies": {
21
26
  "@quasar/extras": "^1.16.4",
22
27
  "@types/luxon": "^3.4.2",
@@ -3,53 +3,48 @@
3
3
  class="px-2 flex dx-action-button"
4
4
  :items="activeActions"
5
5
  :disabled="!hasTarget"
6
- :tooltip="!hasTarget ? tooltip : null"
7
- :loading="isSaving || loading"
6
+ :tooltip="!hasTarget ? tooltip : ''"
7
+ :loading="isSaving || !!loading"
8
8
  :loading-component="loadingComponent"
9
9
  @action-item="onAction"
10
10
  />
11
11
  </template>
12
- <script setup>
12
+ <script setup lang="ts">
13
13
  import { computed, ref } from "vue";
14
+ import { ActionTarget, ResourceAction } from "../../types";
14
15
  import { PopoverMenu } from "../Utility";
15
16
 
16
- const props = defineProps({
17
- actions: {
18
- type: Array,
19
- required: true
20
- },
21
- target: {
22
- type: [Array, Object],
23
- default: () => []
24
- },
25
- tooltip: {
26
- type: String,
27
- default: "First select records to perform a batch action"
28
- },
29
- loading: Boolean,
30
- loadingComponent: {
31
- type: [Function, Object],
32
- default: undefined
33
- }
17
+ const emit = defineEmits(["success"]);
18
+ const props = withDefaults(defineProps<{
19
+ actions: ResourceAction[],
20
+ target?: ActionTarget,
21
+ tooltip?: string,
22
+ loading?: boolean,
23
+ loadingComponent?: any
24
+ }>(), {
25
+ target: () => [],
26
+ tooltip: "First select records to perform a batch action",
27
+ loadingComponent: undefined
34
28
  });
35
29
 
36
30
  const hasTarget = computed(() => Array.isArray(props.target) ? props.target.length > 0 : !!props.target);
37
31
 
38
32
  const activeActions = computed(() => props.actions.filter(action => {
39
- if (Array.isArray(props.target)) {
40
- return action.batchEnabled ? action.batchEnabled(props.target) : true;
41
- }
33
+ if (Array.isArray(props.target)) {
34
+ return action.batchEnabled ? action.batchEnabled(props.target) : true;
35
+ }
42
36
 
43
- return action.enabled ? action.enabled(props.target) : true;
37
+ return action.enabled ? action.enabled(props.target) : true;
44
38
  }));
45
39
 
46
40
  const isSaving = ref(false);
47
41
  async function onAction(action) {
48
- if (!action.trigger) {
49
- throw new Error("Action must have a trigger function! Make sure you are using useActions() or implement your own trigger function.");
50
- }
51
- isSaving.value = true;
52
- await action.trigger(props.target);
53
- isSaving.value = false;
42
+ if (!action.trigger) {
43
+ throw new Error("Action must have a trigger function! Make sure you are using useActions() or implement your own trigger function.");
44
+ }
45
+ isSaving.value = true;
46
+ await action.trigger(props.target);
47
+ isSaving.value = false;
48
+ emit("success", action);
54
49
  }
55
50
  </script>
@@ -31,6 +31,7 @@
31
31
  :label="label"
32
32
  :item-count="summary?.count || 0"
33
33
  :selected-count="selectedRows.length"
34
+ :sticky-colspan="summaryColSpan"
34
35
  :loading="loadingSummary"
35
36
  :summary="summary"
36
37
  :columns="tableColumns"
@@ -87,6 +88,7 @@ export interface Props {
87
88
  summary: any;
88
89
  columns: TableColumn[];
89
90
  rowsPerPageOptions?: number[];
91
+ summaryColSpan?: number;
90
92
  }
91
93
 
92
94
  const props = withDefaults(defineProps<Props>(), {
@@ -94,7 +96,8 @@ const props = withDefaults(defineProps<Props>(), {
94
96
  pagedItems: null,
95
97
  summary: null,
96
98
  loadingSummary: false,
97
- rowsPerPageOptions: () => [10, 25, 50, 100]
99
+ rowsPerPageOptions: () => [10, 25, 50, 100],
100
+ summaryColSpan: null
98
101
  });
99
102
 
100
103
  const actionTable = ref(null);
@@ -3,13 +3,18 @@
3
3
  :key="rowProps.key"
4
4
  :props="rowProps"
5
5
  :style="columnStyle"
6
+ class="dx-column"
7
+ :class="column.columnClass"
6
8
  >
7
9
  <div :style="columnStyle">
8
10
  <div
9
11
  class="flex items-center flex-nowrap"
10
- :class="columnClass"
12
+ :class="wrapClass"
11
13
  >
12
- <div class="flex-grow overflow-hidden">
14
+ <div
15
+ v-if="!column.hideContent"
16
+ class="flex-grow overflow-hidden"
17
+ >
13
18
  <a
14
19
  v-if="column.onClick"
15
20
  :class="column.innerClass"
@@ -25,6 +30,7 @@
25
30
  <div
26
31
  v-else
27
32
  :class="column.innerClass"
33
+ class="dx-column-text"
28
34
  >
29
35
  <RenderVnode
30
36
  v-if="column.vnode"
@@ -42,7 +48,8 @@
42
48
  </div>
43
49
  <div
44
50
  v-if="column.actionMenu"
45
- class="flex flex-shrink-0 pl-2"
51
+ class="flex flex-shrink-0"
52
+ :class="{'ml-2': !column.hideContent}"
46
53
  >
47
54
  <ActionMenu
48
55
  class="dx-column-action-menu"
@@ -55,12 +62,13 @@
55
62
  </div>
56
63
  </QTd>
57
64
  </template>
58
- <script setup>
65
+ <script setup lang="ts">
59
66
  import { QTd } from "quasar";
60
67
  import { computed } from "vue";
61
68
  import { RenderVnode } from "../../Utility";
62
69
  import ActionMenu from "../ActionMenu";
63
70
  import { TitleColumnFormat } from "./";
71
+ import { TableColumn } from "./../../../types";
64
72
 
65
73
  const props = defineProps({
66
74
  rowProps: {
@@ -74,7 +82,7 @@ const props = defineProps({
74
82
  });
75
83
 
76
84
  const row = computed(() => props.rowProps.row);
77
- const column = computed(() => props.rowProps.col);
85
+ const column = computed<TableColumn>(() => props.rowProps.col);
78
86
  const value = computed(() => props.rowProps.value);
79
87
  const isSaving = computed(() => row.value.isSaving?.value);
80
88
 
@@ -86,7 +94,7 @@ const columnStyle = computed(() => {
86
94
  };
87
95
  });
88
96
 
89
- const columnClass = computed(() => ({
97
+ const wrapClass = computed(() => ({
90
98
  [column.value.class || ""]: true,
91
99
  "is-saving": isSaving.value,
92
100
  "justify-end": column.value.align === "right",
@@ -3,7 +3,7 @@
3
3
  :key="rowProps.key"
4
4
  :props="rowProps"
5
5
  :data-drop-zone="isResizeable && `resize-column-` + column.name"
6
- :class="isResizeable && cls['handle-drop-zone']"
6
+ :class="columnClass"
7
7
  :style="columnStyle"
8
8
  >
9
9
  {{ column.label }}
@@ -17,67 +17,88 @@
17
17
  </HandleDraggable>
18
18
  </QTh>
19
19
  </template>
20
- <script setup>
20
+ <script setup lang="ts">
21
21
  import { QTh } from "quasar";
22
- import { computed } from "vue";
22
+ import { computed, useCssModule } from "vue";
23
23
  import { DragHandleIcon as RowResizeIcon } from "../../../svg";
24
+ import { TableColumn } from "../../../types";
24
25
  import { HandleDraggable } from "../../DragAndDrop";
25
26
 
26
27
  const emit = defineEmits(["update:model-value"]);
27
28
  const props = defineProps({
28
- modelValue: {
29
- type: Object,
30
- required: true
31
- },
32
- name: {
33
- type: String,
34
- required: true
35
- },
36
- rowProps: {
37
- type: Object,
38
- required: true
39
- }
29
+ modelValue: {
30
+ type: Object,
31
+ required: true
32
+ },
33
+ name: {
34
+ type: String,
35
+ required: true
36
+ },
37
+ rowProps: {
38
+ type: Object,
39
+ required: true
40
+ }
40
41
  });
41
42
 
42
- const column = computed(() => props.rowProps.col);
43
+ const column = computed<TableColumn>(() => props.rowProps.col);
43
44
  const isResizeable = computed(() => column.value.resizeable);
44
45
 
45
46
  const columnStyle = computed(() => {
46
- const width = props.settings?.width || column.value.width;
47
- return {
48
- width: width ? `${width}px` : undefined,
49
- minWidth: column.value.minWidth ? `${column.value.minWidth}px` : undefined,
50
- ...(column.value.headerStyle || {})
51
- };
47
+ const width = props.settings?.width || column.value.width;
48
+ return {
49
+ width: width ? `${width}px` : undefined,
50
+ minWidth: column.value.minWidth ? `${column.value.minWidth}px` : undefined,
51
+ ...(column.value.headerStyle || {})
52
+ };
52
53
  });
53
54
 
55
+ const clsModule = useCssModule("cls");
56
+ const columnClass = computed(() => {
57
+ const colCls = {
58
+ [clsModule["handle-drop-zone"]]: isResizeable.value,
59
+ "dx-column-shrink": column.value.shrink
60
+ };
61
+
62
+ const headerClass = column.value.headerClass;
63
+ if (headerClass) {
64
+ if (typeof headerClass === "string") {
65
+ colCls[headerClass] = true;
66
+ } else {
67
+ Object.keys(headerClass).forEach((key) => {
68
+ colCls[key] = headerClass[key];
69
+ });
70
+ }
71
+ }
72
+
73
+ return colCls;
74
+ });
54
75
 
55
76
  function onResizeColumn(val) {
56
- const settings = {
57
- ...props.modelValue,
58
- [column.value.name]: {
59
- width: Math.max(Math.min(val.distance + val.startDropZoneSize, column.value.maxWidth || 500), column.value.minWidth || 80)
60
- }
61
- };
62
- emit("update:model-value", settings);
77
+ const settings = {
78
+ ...props.modelValue,
79
+ [column.value.name]: {
80
+ width: Math.max(Math.min(val.distance + val.startDropZoneSize, column.value.maxWidth || 500), column.value.minWidth || 80)
81
+ }
82
+ };
83
+ emit("update:model-value", settings);
63
84
  }
64
85
  </script>
65
86
 
66
87
  <style lang="scss" module="cls">
67
88
  .handle-drop-zone {
68
- .resize-handle {
69
- position: absolute;
70
- top: 0;
71
- right: -.45em;
72
- width: .9em;
73
- opacity: 0;
74
- transition: all .3s;
75
- }
89
+ .resize-handle {
90
+ position: absolute;
91
+ top: 0;
92
+ right: -.45em;
93
+ width: .9em;
94
+ opacity: 0;
95
+ transition: all .3s;
96
+ }
76
97
 
77
- &:hover {
78
- .resize-handle {
79
- opacity: 1;
80
- }
81
- }
98
+ &:hover {
99
+ .resize-handle {
100
+ opacity: 1;
101
+ }
102
+ }
82
103
  }
83
104
  </style>
@@ -0,0 +1,55 @@
1
+ <template>
2
+ <div>
3
+ <RenderedForm
4
+ v-bind="renderedFormProps"
5
+ v-model:values="input"
6
+ empty-value=""
7
+ :saving="action.isApplying"
8
+ @update:values="action.trigger(target, input)"
9
+ />
10
+ </div>
11
+ </template>
12
+ <script setup lang="ts">
13
+ import { ref, watch } from "vue";
14
+ import { ActionTargetItem, AnyObject, Form, ResourceAction } from "../../../types";
15
+ import RenderedForm from "./RenderedForm.vue";
16
+
17
+ interface ActionFormProps {
18
+ action: ResourceAction;
19
+ target: ActionTargetItem;
20
+ form: Form;
21
+ noLabel?: boolean;
22
+ showName?: boolean;
23
+ disable?: boolean;
24
+ readonly?: boolean;
25
+ clearable?: boolean;
26
+ fieldClass?: string;
27
+ savingClass?: string;
28
+ }
29
+
30
+ const props = defineProps<ActionFormProps>();
31
+ const renderedFormProps = {
32
+ form: props.form,
33
+ noLabel: props.noLabel,
34
+ showName: props.showName,
35
+ disable: props.disable,
36
+ readonly: props.readonly,
37
+ clearable: props.clearable,
38
+ fieldClass: props.fieldClass,
39
+ savingClass: props.savingClass
40
+ };
41
+
42
+ const input: AnyObject = ref({ ...props.target });
43
+ const fieldStatus: AnyObject = {};
44
+
45
+ // Only update field values from target changes when the field is not already being saved
46
+ watch(() => props.target, (target: ActionTargetItem) => {
47
+ if (!target) return;
48
+
49
+ for (let field of props.form.fields) {
50
+ if (!fieldStatus[field.name]?.isSaving) {
51
+ input.value[field.name] = target[field.name];
52
+ }
53
+ }
54
+ });
55
+ </script>
@@ -3,12 +3,13 @@
3
3
  class="danx-edit-on-click-text-field flex flex-nowrap items-center rounded overflow-ellipsis"
4
4
  :class="{[props.class]: true, 'is-readonly': readonly, 'cursor-pointer': !isEditing && !readonly}"
5
5
  @click="onEdit"
6
+ @focusout="isEditing = false"
6
7
  >
7
8
  <div
8
9
  ref="editableBox"
9
10
  :contenteditable="!readonly && isEditing"
10
11
  class="flex-grow p-2 rounded outline-none border-none"
11
- :class="{[editingClass]: isEditing}"
12
+ :class="{[editingClass]: isEditing, [inputClass]: true}"
12
13
  @input="text = $event.target.innerText"
13
14
  >
14
15
  {{ text }}
@@ -33,15 +34,20 @@
33
34
  import { FaSolidCheck as DoneIcon, FaSolidPencil as EditIcon } from "danx-icon";
34
35
  import { nextTick, ref } from "vue";
35
36
 
36
- export interface Props {
37
- class?: "hover:bg-slate-300",
38
- editingClass?: "bg-slate-500";
37
+ export interface EditOnClickTextFieldProps {
38
+ class?: string;
39
+ editingClass?: string;
40
+ inputClass?: string;
39
41
  readonly?: boolean;
40
42
  }
41
43
 
42
44
  const editableBox = ref<HTMLElement | null>(null);
43
45
  const text = defineModel({ type: String });
44
- const props = defineProps<Props>();
46
+ const props = withDefaults(defineProps<EditOnClickTextFieldProps>(), {
47
+ class: "hover:bg-slate-300",
48
+ editingClass: "bg-slate-500",
49
+ inputClass: "whitespace-normal"
50
+ });
45
51
  const isEditing = ref(false);
46
52
  function onEdit() {
47
53
  if (props.readonly) return;
@@ -1,27 +1,30 @@
1
1
  <template>
2
- <span class="dx-field-label">
2
+ <div class="dx-field-label">
3
3
  <slot>
4
- {{ labelText }}
5
- <template v-if="showName">({{ field?.name }})</template>
4
+ <div class="dx-field-label-text">
5
+ <div class="dx-field-label-label">
6
+ {{ label }}
7
+ </div>
8
+ <div
9
+ v-if="name"
10
+ class="dx-field-label-name"
11
+ >
12
+ ({{ name }})
13
+ </div>
14
+ </div>
6
15
  </slot>
7
16
  <span
8
- v-if="requiredLabel"
17
+ v-if="required"
9
18
  class="dx-field-required"
10
- >{{ requiredLabel }}</span>
11
- </span>
19
+ >{{ requiredLabel || "*" }}</span>
20
+ </div>
12
21
  </template>
13
22
 
14
23
  <script setup lang="ts">
15
- import { computed } from "vue";
16
- import { FormField } from "../../../../types";
17
-
18
- const props = defineProps<{
19
- field?: FormField;
24
+ defineProps<{
20
25
  label?: string;
21
- showName?: boolean;
26
+ name?: string;
22
27
  required?: boolean;
28
+ requiredLabel?: string;
23
29
  }>();
24
-
25
- const labelText = computed(() => props.label || props.field?.label);
26
- const requiredLabel = computed(() => props.field?.required_group || (props.required || props.field?.required ? "*" : ""));
27
30
  </script>
@@ -62,6 +62,7 @@ function upload() {
62
62
  async function onAttachFiles({ target: { files } }) {
63
63
  emit("uploading", files);
64
64
  let fileUpload = new FileUpload(files)
65
+ .prepare()
65
66
  .onProgress(({ file, progress }) => {
66
67
  file.progress = progress;
67
68
  emit("file-progress", file);
@@ -1,22 +1,51 @@
1
1
  <template>
2
2
  <div>
3
- <div class="text-xs font-bold">{{ label }}</div>
4
- <div :class="{'mt-2': !dense, 'mt-1': dense, 'text-no-wrap': nowrap}">
5
- <slot>{{ value || "-" }}</slot>
3
+ <div class="text-xs font-bold">
4
+ {{ label }}
5
+ </div>
6
+ <div :class="valueClass">
7
+ <a
8
+ v-if="url"
9
+ target="_blank"
10
+ :href="url"
11
+ :class="valueClass"
12
+ >
13
+ <slot>{{ formattedValue }}</slot>
14
+ </a>
15
+ <template v-else>
16
+ <slot>{{ formattedValue }}</slot>
17
+ </template>
6
18
  </div>
7
19
  </div>
8
20
  </template>
9
- <script setup>
10
- defineProps({
11
- label: {
12
- type: String,
13
- required: true
14
- },
15
- value: {
16
- type: [String, Number],
17
- default: "-"
18
- },
19
- dense: Boolean,
20
- nowrap: Boolean
21
+ <script setup lang="ts">
22
+ import { computed } from "vue";
23
+ import { fBoolean, fNumber } from "../../../../helpers";
24
+
25
+ export interface LabelValueBlockProps {
26
+ label: string;
27
+ value: string | number | boolean;
28
+ url?: string;
29
+ dense?: boolean;
30
+ nowrap?: boolean;
31
+ }
32
+
33
+ const props = withDefaults(defineProps<LabelValueBlockProps>(), {
34
+ value: "",
35
+ url: ""
36
+ });
37
+
38
+ const valueClass = computed(() => ({ "mt-2": !props.dense, "mt-1": props.dense, "text-no-wrap": props.nowrap }));
39
+ const formattedValue = computed(() => {
40
+ switch (typeof props.value) {
41
+ case "boolean":
42
+ return fBoolean(props.value);
43
+
44
+ case "number":
45
+ return fNumber(props.value);
46
+
47
+ default:
48
+ return props.value || "-";
49
+ }
21
50
  });
22
51
  </script>
@@ -20,7 +20,7 @@
20
20
  @change="onFilesSelected"
21
21
  >
22
22
 
23
- <div class="max-w-[50em] flex items-stretch justify-start">
23
+ <div class="flex items-stretch justify-start">
24
24
  <FilePreview
25
25
  v-for="file in uploadedFiles"
26
26
  :key="'file-upload-' + file.id"
@@ -12,7 +12,6 @@
12
12
  :field="field"
13
13
  :no-label="!field.label"
14
14
  label-class="text-xs font-bold text-zinc-800"
15
- parent-class="tight-label"
16
15
  input-class="!py-0"
17
16
  dense
18
17
  type="textarea"
@@ -29,29 +28,29 @@ import TextField from "./TextField";
29
28
 
30
29
  const emit = defineEmits(["update:model-value"]);
31
30
  const props = defineProps({
32
- modelValue: {
33
- type: [String, Number, Object],
34
- default: ""
35
- },
36
- field: {
37
- type: Object,
38
- default: null
39
- }
31
+ modelValue: {
32
+ type: [String, Number, Object],
33
+ default: ""
34
+ },
35
+ field: {
36
+ type: Object,
37
+ default: null
38
+ }
40
39
  });
41
40
 
42
41
  const selectedFieldName = ref(props.field.defaultOption);
43
42
  const searchList = computed(() => props.modelValue && props.modelValue[selectedFieldName.value]);
44
43
  const textInput = ref(formatModelValue());
45
44
  function onChange() {
46
- textInput.value = textInput.value?.replace(/\n/g, ",").replace(/,{2,}/g, ",") || "";
47
- emit("update:model-value", textInput.value ? { [selectedFieldName.value]: textInput.value.split(",") } : undefined);
45
+ textInput.value = textInput.value?.replace(/\n/g, ",").replace(/,{2,}/g, ",") || "";
46
+ emit("update:model-value", textInput.value ? { [selectedFieldName.value]: textInput.value.split(",") } : undefined);
48
47
  }
49
48
 
50
49
  function formatModelValue() {
51
- return Array.isArray(searchList.value) ? searchList.value?.join(",") : "";
50
+ return Array.isArray(searchList.value) ? searchList.value?.join(",") : "";
52
51
  }
53
52
 
54
53
  watch(() => props.modelValue, () => {
55
- textInput.value = formatModelValue();
54
+ textInput.value = formatModelValue();
56
55
  });
57
56
  </script>