quasar-ui-danx 0.4.1 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
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>