quasar-ui-danx 0.4.1 → 0.4.3

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 (80) hide show
  1. package/dist/danx.es.js +7234 -6741
  2. package/dist/danx.es.js.map +1 -1
  3. package/dist/danx.umd.js +11 -5
  4. package/dist/danx.umd.js.map +1 -1
  5. package/dist/style.css +1 -1
  6. package/package.json +3 -1
  7. package/src/components/ActionTable/ActionTable.vue +31 -43
  8. package/src/components/ActionTable/Columns/ActionTableColumn.vue +19 -18
  9. package/src/components/ActionTable/Filters/CollapsableFiltersSidebar.vue +15 -14
  10. package/src/components/ActionTable/Filters/{FilterFieldList.vue → FilterList.vue} +26 -26
  11. package/src/components/ActionTable/Filters/FilterableField.vue +28 -31
  12. package/src/components/ActionTable/Filters/index.ts +2 -2
  13. package/src/components/ActionTable/Form/Fields/EditOnClickTextField.vue +71 -0
  14. package/src/components/ActionTable/Form/Fields/FieldLabel.vue +8 -13
  15. package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +34 -33
  16. package/src/components/ActionTable/Form/Fields/MultiFileField.vue +48 -44
  17. package/src/components/ActionTable/Form/Fields/NumberField.vue +60 -59
  18. package/src/components/ActionTable/Form/Fields/SelectField.vue +124 -138
  19. package/src/components/ActionTable/Form/Fields/SelectWithChildrenField.vue +28 -33
  20. package/src/components/ActionTable/Form/Fields/SingleFileField.vue +15 -15
  21. package/src/components/ActionTable/Form/Fields/SliderNumberField.vue +45 -0
  22. package/src/components/ActionTable/Form/Fields/TextField.vue +47 -66
  23. package/src/components/ActionTable/Form/Fields/index.ts +2 -0
  24. package/src/components/ActionTable/Form/RenderedForm.vue +50 -13
  25. package/src/components/ActionTable/Form/Utilities/MaxLengthCounter.vue +17 -0
  26. package/src/components/ActionTable/Form/Utilities/index.ts +1 -0
  27. package/src/components/ActionTable/Form/index.ts +1 -0
  28. package/src/components/ActionTable/Layouts/ActionTableLayout.vue +22 -16
  29. package/src/components/ActionTable/Toolbars/ActionToolbar.vue +11 -11
  30. package/src/components/ActionTable/listControls.ts +104 -166
  31. package/src/components/ActionTable/listHelpers.ts +2 -3
  32. package/src/components/ActionTable/tableColumns.ts +53 -77
  33. package/src/components/AuditHistory/AuditHistoryItemValue.vue +26 -26
  34. package/src/components/PanelsDrawer/PanelsDrawer.vue +17 -4
  35. package/src/components/PanelsDrawer/PanelsDrawerPanels.vue +6 -11
  36. package/src/components/PanelsDrawer/PanelsDrawerTabs.vue +20 -20
  37. package/src/components/Utility/Dialogs/ConfirmActionDialog.vue +39 -0
  38. package/src/components/Utility/Dialogs/ConfirmDialog.vue +57 -117
  39. package/src/components/Utility/Dialogs/DialogLayout.vue +77 -0
  40. package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +42 -36
  41. package/src/components/Utility/Dialogs/InfoDialog.vue +40 -80
  42. package/src/components/Utility/Dialogs/index.ts +1 -0
  43. package/src/components/Utility/Files/FilePreview.vue +76 -73
  44. package/src/components/Utility/Layouts/ContentDrawer.vue +24 -31
  45. package/src/components/Utility/Tools/ActionVnode.vue +3 -3
  46. package/src/components/Utility/Tools/RenderVnode.vue +20 -11
  47. package/src/components/Utility/Transitions/MaxHeightTransition.vue +26 -0
  48. package/src/components/Utility/Transitions/index.ts +1 -0
  49. package/src/config/index.ts +36 -31
  50. package/src/helpers/FileUpload.ts +295 -297
  51. package/src/helpers/FlashMessages.ts +80 -71
  52. package/src/helpers/actions.ts +102 -82
  53. package/src/helpers/download.ts +189 -189
  54. package/src/helpers/downloadPdf.ts +55 -52
  55. package/src/helpers/formats.ts +151 -109
  56. package/src/helpers/index.ts +2 -0
  57. package/src/helpers/multiFileUpload.ts +72 -58
  58. package/src/helpers/objectStore.ts +52 -0
  59. package/src/helpers/request.ts +70 -51
  60. package/src/helpers/routes.ts +29 -0
  61. package/src/helpers/storage.ts +7 -3
  62. package/src/helpers/utils.ts +47 -29
  63. package/src/styles/quasar-reset.scss +94 -68
  64. package/src/styles/themes/danx/dialogs.scss +47 -0
  65. package/src/styles/themes/danx/forms.scss +18 -0
  66. package/src/styles/themes/danx/index.scss +4 -0
  67. package/src/types/actions.d.ts +43 -0
  68. package/src/types/config.d.ts +15 -0
  69. package/src/types/controls.d.ts +99 -0
  70. package/src/types/dialogs.d.ts +32 -0
  71. package/src/types/fields.d.ts +20 -0
  72. package/src/types/files.d.ts +54 -0
  73. package/src/types/formats.d.ts +4 -0
  74. package/src/{components/ActionTable/Form/form.d.ts → types/forms.d.ts} +6 -0
  75. package/src/types/index.d.ts +12 -0
  76. package/src/types/requests.d.ts +13 -0
  77. package/src/types/shared.d.ts +15 -0
  78. package/src/types/tables.d.ts +27 -0
  79. package/types/index.d.ts +1 -1
  80. /package/src/components/ActionTable/Filters/{FilterFieldItem.vue → FilterItem.vue} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quasar-ui-danx",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "author": "Dan <dan@flytedesk.com>",
5
5
  "description": "DanX Vue / Quasar component library",
6
6
  "license": "MIT",
@@ -19,6 +19,7 @@
19
19
  },
20
20
  "devDependencies": {
21
21
  "@quasar/extras": "^1.16.4",
22
+ "@types/luxon": "^3.4.2",
22
23
  "@types/node": "^20.12.7",
23
24
  "@typescript-eslint/eslint-plugin": "^7.6.0",
24
25
  "@typescript-eslint/parser": "^7.6.0",
@@ -48,6 +49,7 @@
48
49
  "@heroicons/vue": "v1",
49
50
  "@tinymce/tinymce-vue": "^5.1.1",
50
51
  "@vueuse/core": "^10.7.2",
52
+ "danx-icon": "^1.0.2",
51
53
  "exifreader": "^4.21.1",
52
54
  "gsap": "^3.12.5",
53
55
  "luxon": "^3.4.4"
@@ -8,7 +8,7 @@
8
8
  ref="actionTable"
9
9
  :selected="selectedRows"
10
10
  :pagination="pagination"
11
- :columns="columns"
11
+ :columns="tableColumns"
12
12
  :loading="loadingList"
13
13
  :rows="pagedItems?.data || []"
14
14
  :binary-state-sort="false"
@@ -18,7 +18,7 @@
18
18
  :color="color"
19
19
  @update:selected="$emit('update:selected-rows', $event)"
20
20
  @update:pagination="() => {}"
21
- @request="(e) => $emit('update:pagination', {...e.pagination, __sort: mapSortBy(e.pagination, columns)})"
21
+ @request="(e) => $emit('update:pagination', {...e.pagination, __sort: mapSortBy(e.pagination, tableColumns)})"
22
22
  >
23
23
  <template #no-data>
24
24
  <slot name="empty">
@@ -33,7 +33,7 @@
33
33
  :selected-count="selectedRows.length"
34
34
  :loading="loadingSummary"
35
35
  :summary="summary"
36
- :columns="columns"
36
+ :columns="tableColumns"
37
37
  @clear="$emit('update:selected-rows', [])"
38
38
  />
39
39
  </template>
@@ -62,10 +62,11 @@
62
62
  </div>
63
63
  </template>
64
64
 
65
- <script setup>
65
+ <script setup lang="ts">
66
66
  import { QTable } from "quasar";
67
67
  import { computed, ref } from "vue";
68
68
  import { getItem, setItem } from "../../helpers";
69
+ import { ActionTargetItem, ListControlsPagination, TableColumn } from "../../types";
69
70
  import { ActionVnode } from "../Utility";
70
71
  import { ActionTableColumn, ActionTableHeaderColumn } from "./Columns";
71
72
  import EmptyTableState from "./EmptyTableState.vue";
@@ -73,49 +74,36 @@ import { mapSortBy, registerStickyScrolling } from "./listHelpers";
73
74
  import TableSummaryRow from "./TableSummaryRow.vue";
74
75
 
75
76
  defineEmits(["update:selected-rows", "update:pagination"]);
76
- const props = defineProps({
77
- name: {
78
- type: String,
79
- required: true
80
- },
81
- label: {
82
- type: String,
83
- required: true
84
- },
85
- color: {
86
- type: String,
87
- default: "blue-600"
88
- },
89
- selectedRows: {
90
- type: Array,
91
- required: true
92
- },
93
- pagination: {
94
- type: Object,
95
- required: true
96
- },
97
- loadingList: Boolean,
98
- loadingSummary: Boolean,
99
- pagedItems: {
100
- type: Object,
101
- default: null
102
- },
103
- summary: {
104
- type: Object,
105
- default: null
106
- },
107
- columns: {
108
- type: Array,
109
- required: true
110
- },
111
- rowsPerPageOptions: {
112
- type: Array,
113
- default: () => [10, 25, 50, 100]
114
- }
77
+
78
+ export interface Props {
79
+ name: string;
80
+ label: string;
81
+ color?: string;
82
+ selectedRows: ActionTargetItem[];
83
+ pagination: ListControlsPagination;
84
+ loadingList?: boolean;
85
+ loadingSummary?: boolean;
86
+ pagedItems?: any;
87
+ summary: any;
88
+ columns: TableColumn[];
89
+ rowsPerPageOptions?: number[];
90
+ }
91
+
92
+ const props = withDefaults(defineProps<Props>(), {
93
+ color: "",
94
+ pagedItems: null,
95
+ summary: null,
96
+ loadingSummary: false,
97
+ rowsPerPageOptions: () => [10, 25, 50, 100]
115
98
  });
99
+
116
100
  const actionTable = ref(null);
117
101
  registerStickyScrolling(actionTable);
118
102
 
103
+ const tableColumns = computed<TableColumn[]>(() => props.columns.map((column) => ({
104
+ ...column,
105
+ field: column.field || column.name
106
+ })));
119
107
  const hasData = computed(() => props.pagedItems?.data?.length);
120
108
  const COLUMN_SETTINGS_KEY = `column-settings-${props.name}`;
121
109
  const columnSettings = ref(getItem(COLUMN_SETTINGS_KEY) || {});
@@ -13,6 +13,7 @@
13
13
  <a
14
14
  v-if="column.onClick"
15
15
  :class="column.innerClass"
16
+ class="dx-column-link"
16
17
  @click="column.onClick(row)"
17
18
  >
18
19
  <RenderVnode
@@ -62,14 +63,14 @@ import ActionMenu from "../ActionMenu";
62
63
  import { TitleColumnFormat } from "./";
63
64
 
64
65
  const props = defineProps({
65
- rowProps: {
66
- type: Object,
67
- required: true
68
- },
69
- settings: {
70
- type: Object,
71
- default: null
72
- }
66
+ rowProps: {
67
+ type: Object,
68
+ required: true
69
+ },
70
+ settings: {
71
+ type: Object,
72
+ default: null
73
+ }
73
74
  });
74
75
 
75
76
  const row = computed(() => props.rowProps.row);
@@ -78,18 +79,18 @@ const value = computed(() => props.rowProps.value);
78
79
  const isSaving = computed(() => row.value.isSaving?.value);
79
80
 
80
81
  const columnStyle = computed(() => {
81
- const width = props.settings?.width || column.value.width;
82
- return {
83
- width: width ? `${width}px` : undefined,
84
- minWidth: column.value.minWidth ? `${column.value.minWidth}px` : undefined
85
- };
82
+ const width = props.settings?.width || column.value.width;
83
+ return {
84
+ width: width ? `${width}px` : undefined,
85
+ minWidth: column.value.minWidth ? `${column.value.minWidth}px` : undefined
86
+ };
86
87
  });
87
88
 
88
89
  const columnClass = computed(() => ({
89
- [column.value.class || ""]: true,
90
- "is-saving": isSaving.value,
91
- "justify-end": column.value.align === "right",
92
- "justify-center": column.value.align === "center",
93
- "justify-start": column.value.align === "left"
90
+ [column.value.class || ""]: true,
91
+ "is-saving": isSaving.value,
92
+ "justify-end": column.value.align === "right",
93
+ "justify-center": column.value.align === "center",
94
+ "justify-start": column.value.align === "left"
94
95
  }));
95
96
  </script>
@@ -1,5 +1,6 @@
1
1
  <template>
2
2
  <CollapsableSidebar
3
+ class="dx-collapsable-filters-sidebar"
3
4
  :collapse="!showFilters"
4
5
  disabled
5
6
  :min-width="minWidth"
@@ -7,32 +8,32 @@
7
8
  :name="name"
8
9
  @update:collapse="$emit('update:show-filters', !$event)"
9
10
  >
10
- <FilterFieldList
11
- :filter="activeFilter"
12
- :filter-fields="filters"
11
+ <FilterList
12
+ :active-filter="activeFilter"
13
+ :filters="filters"
13
14
  @update:filter="$emit('update:active-filter', $event)"
14
15
  />
15
16
  </CollapsableSidebar>
16
17
  </template>
17
18
  <script setup lang="ts">
18
- import { FilterField, ListControlsFilter } from "src/components/ActionTable/listControls";
19
- import { FilterFieldList } from ".";
19
+ import { FilterGroup, ListControlsFilter } from "../../../types";
20
20
  import { CollapsableSidebar } from "../../Utility";
21
+ import FilterList from "./FilterList";
21
22
 
22
23
  defineEmits(["update:active-filter", "update:show-filters"]);
23
24
 
24
25
  export interface Props {
25
- name: string,
26
- showFilters?: boolean,
27
- activeFilter: ListControlsFilter,
28
- minWidth?: string,
29
- maxWidth?: string,
30
- filters?: FilterField[]
26
+ name: string,
27
+ showFilters?: boolean,
28
+ activeFilter: ListControlsFilter,
29
+ minWidth?: string,
30
+ maxWidth?: string,
31
+ filters?: FilterGroup[]
31
32
  }
32
33
 
33
34
  withDefaults(defineProps<Props>(), {
34
- minWidth: "5rem",
35
- maxWidth: "18rem",
36
- filters: () => []
35
+ minWidth: "5rem",
36
+ maxWidth: "18rem",
37
+ filters: () => []
37
38
  });
38
39
  </script>
@@ -2,14 +2,14 @@
2
2
  <QList>
3
3
  <div class="px-4 py-2 max-w-full">
4
4
  <template
5
- v-for="(group, index) in filterFields"
5
+ v-for="(group, index) in filters"
6
6
  :key="'group-' + group.name"
7
7
  >
8
8
  <template v-if="group.flat">
9
9
  <FilterableField
10
10
  v-for="field in group.fields"
11
11
  :key="'field-' + field.name"
12
- :model-value="field.calcValue ? field.calcValue(filter) : filter[field.name]"
12
+ :model-value="field.calcValue ? field.calcValue(activeFilter) : activeFilter[field.name]"
13
13
  :field="field"
14
14
  :loading="loading"
15
15
  class="mb-4"
@@ -17,7 +17,7 @@
17
17
  />
18
18
  </template>
19
19
 
20
- <FilterFieldItem
20
+ <FilterItem
21
21
  v-else
22
22
  :name="group.name"
23
23
  :count="activeCountByGroup[group.name]"
@@ -25,16 +25,16 @@
25
25
  <FilterableField
26
26
  v-for="field in group.fields"
27
27
  :key="'field-' + field.name"
28
- :model-value="field.calcValue ? field.calcValue(filter) : filter[field.name]"
28
+ :model-value="field.calcValue ? field.calcValue(activeFilter) : activeFilter[field.name]"
29
29
  :field="field"
30
30
  :loading="loading"
31
31
  class="mb-4"
32
32
  @update:model-value="updateFilter(field, $event)"
33
33
  />
34
- </FilterFieldItem>
34
+ </FilterItem>
35
35
 
36
36
  <QSeparator
37
- v-if="index < (filterFields.length - 1)"
37
+ v-if="index < (filters.length - 1)"
38
38
  class="my-2"
39
39
  />
40
40
  </template>
@@ -44,33 +44,33 @@
44
44
  <script setup>
45
45
  import { computed } from "vue";
46
46
  import FilterableField from "./FilterableField";
47
- import FilterFieldItem from "./FilterFieldItem";
47
+ import FilterItem from "./FilterItem";
48
48
 
49
49
  const emit = defineEmits(["update:filter"]);
50
50
  const props = defineProps({
51
- filterFields: {
52
- type: Array,
53
- required: true
54
- },
55
- filter: {
56
- type: Object,
57
- required: true
58
- },
59
- loading: Boolean
51
+ filters: {
52
+ type: Array,
53
+ required: true
54
+ },
55
+ activeFilter: {
56
+ type: Object,
57
+ required: true
58
+ },
59
+ loading: Boolean
60
60
  });
61
61
 
62
62
  const activeCountByGroup = computed(() => {
63
- const activeCountByGroup = {};
64
- for (const group of props.filterFields) {
65
- activeCountByGroup[group.name] = group.fields.filter(field => props.filter[field.name] !== undefined).length;
66
- }
67
- return activeCountByGroup;
63
+ const activeCountByGroup = {};
64
+ for (const group of props.filters) {
65
+ activeCountByGroup[group.name] = group.fields.filter(field => props.activeFilter[field.name] !== undefined).length;
66
+ }
67
+ return activeCountByGroup;
68
68
  });
69
69
  function updateFilter(field, value) {
70
- let fieldFilter = { [field.name]: value };
71
- if (field.filterBy) {
72
- fieldFilter = field.filterBy(value);
73
- }
74
- emit("update:filter", { ...props.filter, ...fieldFilter });
70
+ let activeFilter = { [field.name]: value };
71
+ if (field.filterBy) {
72
+ activeFilter = field.filterBy(value);
73
+ }
74
+ emit("update:filter", { ...props.activeFilter, ...activeFilter });
75
75
  }
76
76
  </script>
@@ -5,6 +5,7 @@
5
5
  v-if="field.options?.length > 0 || loading"
6
6
  :model-value="modelValue"
7
7
  :options="field.options"
8
+ :clearable="field.clearable === undefined ? true : field.clearable"
8
9
  multiple
9
10
  :loading="loading"
10
11
  :chip-limit="1"
@@ -30,6 +31,7 @@
30
31
  v-else-if="field.type === 'single-select'"
31
32
  :model-value="modelValue"
32
33
  :options="field.options"
34
+ :clearable="field.clearable === undefined ? true : field.clearable"
33
35
  :placeholder="field.placeholder"
34
36
  :loading="loading"
35
37
  :label="field.label"
@@ -46,7 +48,7 @@
46
48
  v-else-if="field.type === 'date-range'"
47
49
  :model-value="modelValue"
48
50
  :label="field.label"
49
- :inline="field.inline"
51
+ :inline="!!field.inline"
50
52
  with-time
51
53
  class="mt-2 reactive"
52
54
  @update:model-value="onUpdate"
@@ -106,43 +108,38 @@
106
108
  </template>
107
109
  </div>
108
110
  </template>
109
- <script setup>
111
+ <script setup lang="ts">
112
+ import { FormField } from "../../../types";
110
113
  import {
111
- BooleanField,
112
- DateField,
113
- DateRangeField,
114
- MultiKeywordField,
115
- NumberRangeField,
116
- SelectField,
117
- SelectWithChildrenField
114
+ BooleanField,
115
+ DateField,
116
+ DateRangeField,
117
+ MultiKeywordField,
118
+ NumberRangeField,
119
+ SelectField,
120
+ SelectWithChildrenField
118
121
  } from "../Form/Fields";
119
122
 
120
123
  const emit = defineEmits(["update:model-value"]);
121
- const props = defineProps({
122
- field: {
123
- type: Object,
124
- required: true
125
- },
126
- modelValue: {
127
- type: [String, Array, Number, Object, Boolean],
128
- default: undefined
129
- },
130
- loading: Boolean
131
- });
124
+ const props = defineProps<{
125
+ field: FormField;
126
+ modelValue?: any;
127
+ loading?: boolean;
128
+ }>();
132
129
 
133
130
  function onUpdate(val) {
134
- let newVal = val || undefined;
131
+ let newVal = val || undefined;
135
132
 
136
- switch (props.field.type) {
137
- case "multi-select":
138
- newVal = val.length > 0 ? val : undefined;
139
- break;
140
- case "single-select":
141
- case "boolean":
142
- newVal = val === null ? undefined : val;
143
- break;
144
- }
133
+ switch (props.field.type) {
134
+ case "multi-select":
135
+ newVal = (val && val.length > 0) ? val : undefined;
136
+ break;
137
+ case "single-select":
138
+ case "boolean":
139
+ newVal = val === null ? undefined : val;
140
+ break;
141
+ }
145
142
 
146
- emit("update:model-value", newVal);
143
+ emit("update:model-value", newVal);
147
144
  }
148
145
  </script>
@@ -1,6 +1,6 @@
1
1
  export { default as CollapsableFiltersSidebar } from "./CollapsableFiltersSidebar.vue";
2
2
  export { default as FilterableField } from "./FilterableField.vue";
3
- export { default as FilterFieldItem } from "./FilterFieldItem.vue";
4
- export { default as FilterFieldList } from "./FilterFieldList.vue";
3
+ export { default as FilterItem } from "./FilterItem.vue";
4
+ export { default as FilterList } from "./FilterList.vue";
5
5
  export { default as FilterListToggle } from "./FilterListToggle.vue";
6
6
  export { default as FilterToolbarLayout } from "./FilterToolbarLayout.vue";
@@ -0,0 +1,71 @@
1
+ <template>
2
+ <div
3
+ class="danx-edit-on-click-text-field flex flex-nowrap items-center rounded overflow-ellipsis"
4
+ :class="{[props.class]: true, 'is-readonly': readonly, 'cursor-pointer': !isEditing && !readonly}"
5
+ @click="onEdit"
6
+ >
7
+ <div
8
+ ref="editableBox"
9
+ :contenteditable="!readonly && isEditing"
10
+ class="flex-grow p-2 rounded outline-none border-none"
11
+ :class="{[editingClass]: isEditing}"
12
+ @input="text = $event.target.innerText"
13
+ >
14
+ {{ text }}
15
+ </div>
16
+ <div v-if="!readonly">
17
+ <QBtn
18
+ v-if="isEditing"
19
+ @click.stop="isEditing = false"
20
+ >
21
+ <DoneIcon class="w-4" />
22
+ </QBtn>
23
+ <QBtn
24
+ v-else
25
+ class="edit-icon"
26
+ >
27
+ <EditIcon class="w-4" />
28
+ </QBtn>
29
+ </div>
30
+ </div>
31
+ </template>
32
+ <script lang="ts" setup>
33
+ import { FaSolidCheck as DoneIcon, FaSolidPencil as EditIcon } from "danx-icon";
34
+ import { nextTick, ref } from "vue";
35
+
36
+ export interface Props {
37
+ class?: "hover:bg-slate-300",
38
+ editingClass?: "bg-slate-500";
39
+ readonly?: boolean;
40
+ }
41
+
42
+ const editableBox = ref<HTMLElement | null>(null);
43
+ const text = defineModel({ type: String });
44
+ const props = defineProps<Props>();
45
+ const isEditing = ref(false);
46
+ function onEdit() {
47
+ if (props.readonly) return;
48
+ isEditing.value = true;
49
+ nextTick(() => {
50
+ editableBox.value?.focus();
51
+ });
52
+ }
53
+
54
+ </script>
55
+
56
+ <style lang="scss" scoped>
57
+ .danx-edit-on-click-text-field {
58
+ @apply transition-all;
59
+
60
+ .edit-icon {
61
+ @apply opacity-0 transition-all;
62
+
63
+ }
64
+
65
+ &:hover {
66
+ .edit-icon {
67
+ @apply opacity-100;
68
+ }
69
+ }
70
+ }
71
+ </style>
@@ -11,21 +11,16 @@
11
11
  </span>
12
12
  </template>
13
13
 
14
- <script setup>
14
+ <script setup lang="ts">
15
+ import { FormField } from "src/types";
15
16
  import { computed } from "vue";
16
17
 
17
- const props = defineProps({
18
- field: {
19
- type: Object,
20
- default: null
21
- },
22
- label: {
23
- type: String,
24
- default: null
25
- },
26
- showName: Boolean,
27
- required: Boolean
28
- });
18
+ const props = defineProps<{
19
+ field?: FormField;
20
+ label?: string;
21
+ showName?: boolean;
22
+ required?: boolean;
23
+ }>();
29
24
 
30
25
  const labelText = computed(() => props.label || props.field?.label);
31
26
  const requiredLabel = computed(() => props.field?.required_group || (props.required || props.field?.required ? "*" : ""));
@@ -12,8 +12,8 @@
12
12
  ref="fileUpload"
13
13
  data-testid="file-upload"
14
14
  type="file"
15
- :accept="geolocation ? 'image/*;capture=camera' : undefined"
16
- :capture="geolocation ? 'environment' : undefined"
15
+ :accept="(geolocation && cameraOnly) ? 'image/*;capture=camera' : undefined"
16
+ :capture="(geolocation && cameraOnly) ? 'environment' : undefined"
17
17
  class="hidden"
18
18
  multiple
19
19
  @change="onAttachFiles"
@@ -28,28 +28,29 @@ import { FileUpload } from "../../../../helpers";
28
28
 
29
29
  defineExpose({ upload });
30
30
  const emit = defineEmits([
31
- "uploading",
32
- "file-progress",
33
- "file-complete",
34
- "complete"
31
+ "uploading",
32
+ "file-progress",
33
+ "file-complete",
34
+ "complete"
35
35
  ]);
36
36
  const props = defineProps({
37
- ...QBtn.props,
38
- text: {
39
- type: String,
40
- default: "Add File"
41
- },
42
- locationWaitMessage: {
43
- type: String,
44
- default: "Waiting for location..."
45
- },
46
- geolocation: Boolean
37
+ ...QBtn.props,
38
+ text: {
39
+ type: String,
40
+ default: "Add File"
41
+ },
42
+ locationWaitMessage: {
43
+ type: String,
44
+ default: "Waiting for location..."
45
+ },
46
+ cameraOnly: Boolean,
47
+ geolocation: Boolean
47
48
  });
48
49
 
49
50
  const fileUpload = ref(null);
50
51
 
51
52
  function upload() {
52
- fileUpload.value.click();
53
+ fileUpload.value.click();
53
54
  }
54
55
 
55
56
  /**
@@ -59,23 +60,23 @@ function upload() {
59
60
  * @returns {Promise<void>}
60
61
  */
61
62
  async function onAttachFiles({ target: { files } }) {
62
- emit("uploading", files);
63
- let fileUpload = new FileUpload(files)
64
- .onProgress(({ file, progress }) => {
65
- file.progress = progress;
66
- emit("file-progress", file);
67
- })
68
- .onComplete(({ file, uploadedFile }) => {
69
- emit("file-complete", { file, uploadedFile });
70
- })
71
- .onAllComplete(() => {
72
- emit("complete", fileUpload.files);
73
- });
63
+ emit("uploading", files);
64
+ let fileUpload = new FileUpload(files)
65
+ .onProgress(({ file, progress }) => {
66
+ file.progress = progress;
67
+ emit("file-progress", file);
68
+ })
69
+ .onComplete(({ file, uploadedFile }) => {
70
+ emit("file-complete", { file, uploadedFile });
71
+ })
72
+ .onAllComplete(() => {
73
+ emit("complete", fileUpload.files);
74
+ });
74
75
 
75
- if (props.geolocation) {
76
- await fileUpload.resolveLocation(props.locationWaitMessage);
77
- }
76
+ if (props.geolocation) {
77
+ await fileUpload.resolveLocation(props.locationWaitMessage);
78
+ }
78
79
 
79
- fileUpload.upload();
80
+ fileUpload.upload();
80
81
  }
81
82
  </script>