quasar-ui-danx 0.3.48 → 0.3.50

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quasar-ui-danx",
3
- "version": "0.3.48",
3
+ "version": "0.3.50",
4
4
  "author": "Dan <dan@flytedesk.com>",
5
5
  "description": "DanX Vue / Quasar component library",
6
6
  "license": "MIT",
@@ -37,17 +37,25 @@
37
37
  <FilePreview
38
38
  v-for="file in uploadedFiles"
39
39
  :key="'file-upload-' + file.id"
40
- class="w-32 h-32 m-2 cursor-pointer bg-gray-200"
41
- :class="{'border border-dashed border-blue-600': !uploadedFiles.length}"
40
+ class="cursor-pointer bg-gray-200"
41
+ :class="{'border border-dashed border-blue-600': !uploadedFiles.length, [fileClass]: true}"
42
42
  :image="file"
43
43
  :related-files="uploadedFiles"
44
44
  downloadable
45
45
  :removable="!readonly && !disable"
46
46
  @remove="onRemove(file)"
47
- />
47
+ >
48
+ <template #below>
49
+ <slot
50
+ name="below-file"
51
+ :file="file"
52
+ />
53
+ </template>
54
+ </FilePreview>
48
55
  <FilePreview
49
56
  v-if="!disable && !readonly"
50
- class="w-32 h-32 m-2 cursor-pointer border border-dashed border-blue-600"
57
+ class="cursor-pointer border border-dashed border-blue-600"
58
+ :class="fileClass"
51
59
  disabled
52
60
  @click="$refs.file.click()"
53
61
  />
@@ -80,7 +88,11 @@ const props = defineProps({
80
88
  showName: Boolean,
81
89
  disable: Boolean,
82
90
  readonly: Boolean,
83
- hideControls: Boolean
91
+ hideControls: Boolean,
92
+ fileClass: {
93
+ type: String,
94
+ default: "w-32 h-32 m-2"
95
+ }
84
96
  });
85
97
 
86
98
  const { onComplete, onDrop, onFilesSelected, uploadedFiles, clearUploadedFiles, onRemove } = useMultiFileUpload();
@@ -24,10 +24,12 @@
24
24
  @update:model-value="onUpdate"
25
25
  >
26
26
  <template #append>
27
- <DropDownIcon
28
- class="w-4 transition"
29
- :class="isShowing ? 'rotate-180' : ''"
30
- />
27
+ <slot name="append">
28
+ <DropDownIcon
29
+ class="w-4 transition"
30
+ :class="isShowing ? 'rotate-180' : ''"
31
+ />
32
+ </slot>
31
33
  </template>
32
34
  <template #selected>
33
35
  <div
@@ -71,40 +73,40 @@ import { computed, isRef, nextTick, ref } from "vue";
71
73
 
72
74
  const emit = defineEmits(["update:model-value", "search", "update"]);
73
75
  const props = defineProps({
74
- ...QSelect.props,
75
- modelValue: {
76
- type: [Array, String, Number, Object],
77
- default: undefined
78
- },
79
- placeholder: {
80
- type: String,
81
- default: ""
82
- },
83
- selectionLabel: {
84
- type: String,
85
- default: null
86
- },
87
- chipLimit: {
88
- type: Number,
89
- default: 3
90
- },
91
- inputClass: {
92
- type: String,
93
- default: ""
94
- },
95
- selectionClass: {
96
- type: String,
97
- default: ""
98
- },
99
- options: {
100
- type: Array,
101
- default: () => []
102
- },
103
- filterable: Boolean,
104
- filterFn: {
105
- type: Function,
106
- default: null
107
- }
76
+ ...QSelect.props,
77
+ modelValue: {
78
+ type: [Array, String, Number, Object],
79
+ default: undefined
80
+ },
81
+ placeholder: {
82
+ type: String,
83
+ default: ""
84
+ },
85
+ selectionLabel: {
86
+ type: String,
87
+ default: null
88
+ },
89
+ chipLimit: {
90
+ type: Number,
91
+ default: 3
92
+ },
93
+ inputClass: {
94
+ type: String,
95
+ default: ""
96
+ },
97
+ selectionClass: {
98
+ type: String,
99
+ default: ""
100
+ },
101
+ options: {
102
+ type: Array,
103
+ default: () => []
104
+ },
105
+ filterable: Boolean,
106
+ filterFn: {
107
+ type: Function,
108
+ default: null
109
+ }
108
110
  });
109
111
 
110
112
  const selectField = ref(null);
@@ -119,27 +121,27 @@ const isShowing = ref(false);
119
121
  * @type {ComputedRef<{selectionLabel: string, label: string, value: string|*}[]>}
120
122
  */
121
123
  const computedOptions = computed(() => {
122
- let options = props.options;
123
- if (props.placeholder && !props.multiple && !props.filterable) {
124
- options = [{ label: props.placeholder, value: null }, ...props.options];
125
- }
126
- options = options.map((o) => {
127
- let opt = isRef(o) ? o.value : o;
128
- return {
129
- label: resolveLabel(opt),
130
- value: resolveValue(opt),
131
- selectionLabel: resolveSelectionLabel(opt)
132
- };
133
- });
134
- return options;
124
+ let options = props.options;
125
+ if (props.placeholder && !props.multiple && !props.filterable) {
126
+ options = [{ label: props.placeholder, value: null }, ...props.options];
127
+ }
128
+ options = options.map((o) => {
129
+ let opt = isRef(o) ? o.value : o;
130
+ return {
131
+ label: resolveLabel(opt),
132
+ value: resolveValue(opt),
133
+ selectionLabel: resolveSelectionLabel(opt)
134
+ };
135
+ });
136
+ return options;
135
137
  });
136
138
 
137
139
  const filteredOptions = computed(() => {
138
- if (filter.value && !props.filterFn) {
139
- return computedOptions.value.filter(o => o.label.toLocaleLowerCase().indexOf(filter.value.toLowerCase()) > -1);
140
- } else {
141
- return computedOptions.value;
142
- }
140
+ if (filter.value && !props.filterFn) {
141
+ return computedOptions.value.filter(o => o.label.toLocaleLowerCase().indexOf(filter.value.toLowerCase()) > -1);
142
+ } else {
143
+ return computedOptions.value;
144
+ }
143
145
  });
144
146
 
145
147
  /**
@@ -147,14 +149,14 @@ const filteredOptions = computed(() => {
147
149
  * @type {ComputedRef<unknown>}
148
150
  */
149
151
  const selectedValue = computed(() => {
150
- if (props.multiple) {
151
- const arrVal = Array.isArray(props.modelValue) ? props.modelValue : [];
152
- return arrVal.map((v) => {
153
- return v === null ? "__null__" : v;
154
- }) || [];
155
- } else {
156
- return props.modelValue === null ? "__null__" : props.modelValue;
157
- }
152
+ if (props.multiple) {
153
+ const arrVal = Array.isArray(props.modelValue) ? props.modelValue : [];
154
+ return arrVal.map((v) => {
155
+ return v === null ? "__null__" : v;
156
+ }) || [];
157
+ } else {
158
+ return props.modelValue === null ? "__null__" : props.modelValue;
159
+ }
158
160
  });
159
161
 
160
162
  /**
@@ -162,13 +164,13 @@ const selectedValue = computed(() => {
162
164
  * @type {ComputedRef<*>}
163
165
  */
164
166
  const selectedOptions = computed(() => {
165
- let values = selectedValue.value;
166
- if (!props.multiple) {
167
- values = (values || values === 0) ? [values] : [];
168
- }
169
- return computedOptions.value.filter((o) => {
170
- return values.includes(o.value) || values.map(v => typeof v === "object" && v.id).includes(o.value?.id);
171
- });
167
+ let values = selectedValue.value;
168
+ if (!props.multiple) {
169
+ values = (values || values === 0) ? [values] : [];
170
+ }
171
+ return computedOptions.value.filter((o) => {
172
+ return values.includes(o.value) || values.map(v => typeof v === "object" && v.id).includes(o.value?.id);
173
+ });
172
174
  });
173
175
 
174
176
  /**
@@ -177,12 +179,12 @@ const selectedOptions = computed(() => {
177
179
  * @type {ComputedRef<unknown>}
178
180
  */
179
181
  const selectedLabel = computed(() => {
180
- if (props.filterable && isShowing.value) return "";
182
+ if (props.filterable && isShowing.value) return "";
181
183
 
182
- if (!selectedOptions.value || selectedOptions.value.length === 0) {
183
- return props.placeholder || "(Select Option)";
184
- }
185
- return selectedOptions.value[0].selectionLabel;
184
+ if (!selectedOptions.value || selectedOptions.value.length === 0) {
185
+ return props.placeholder || "(Select Option)";
186
+ }
187
+ return selectedOptions.value[0].selectionLabel;
186
188
  });
187
189
 
188
190
  /**
@@ -190,7 +192,7 @@ const selectedLabel = computed(() => {
190
192
  * @type {ComputedRef<*>}
191
193
  */
192
194
  const chipOptions = computed(() => {
193
- return selectedOptions.value.slice(0, props.chipLimit);
195
+ return selectedOptions.value.slice(0, props.chipLimit);
194
196
  });
195
197
 
196
198
  /**
@@ -199,16 +201,16 @@ const chipOptions = computed(() => {
199
201
  * @returns {*|string}
200
202
  */
201
203
  function resolveLabel(option) {
202
- if (typeof option === "string") {
203
- return option;
204
- }
205
- if (typeof props.optionLabel === "string") {
206
- return option[props.optionLabel];
207
- }
208
- if (typeof props.optionLabel === "function") {
209
- return props.optionLabel(option);
210
- }
211
- return option?.label;
204
+ if (typeof option === "string") {
205
+ return option;
206
+ }
207
+ if (typeof props.optionLabel === "string") {
208
+ return option[props.optionLabel];
209
+ }
210
+ if (typeof props.optionLabel === "function") {
211
+ return props.optionLabel(option);
212
+ }
213
+ return option?.label;
212
214
  }
213
215
 
214
216
  /**
@@ -218,16 +220,16 @@ function resolveLabel(option) {
218
220
  * @returns {*|{default: null, type: String | StringConstructor}|string}
219
221
  */
220
222
  function resolveSelectionLabel(option) {
221
- if (typeof option === "string") {
222
- return option;
223
- }
224
- if (typeof props.selectionLabel === "string") {
225
- return option[props.selectionLabel];
226
- }
227
- if (typeof props.selectionLabel === "function") {
228
- return props.selectionLabel(option);
229
- }
230
- return option?.selectionLabel || option?.label;
223
+ if (typeof option === "string") {
224
+ return option;
225
+ }
226
+ if (typeof props.selectionLabel === "string") {
227
+ return option[props.selectionLabel];
228
+ }
229
+ if (typeof props.selectionLabel === "function") {
230
+ return props.selectionLabel(option);
231
+ }
232
+ return option?.selectionLabel || option?.label;
231
233
  }
232
234
 
233
235
  /**
@@ -236,17 +238,17 @@ function resolveSelectionLabel(option) {
236
238
  * @returns {string|*|string}
237
239
  */
238
240
  function resolveValue(option) {
239
- if (!option || typeof option === "string") {
240
- return option;
241
- }
242
- let value = option.value;
243
- if (typeof props.optionValue === "string") {
244
- value = option[props.optionValue];
245
- } else if (typeof props.optionValue === "function") {
246
- value = props.optionValue(option);
247
- }
248
- // Note the __null__ special case here. See the onUpdate function for more details
249
- return value === null ? "__null__" : value;
241
+ if (!option || typeof option === "string") {
242
+ return option;
243
+ }
244
+ let value = option.value;
245
+ if (typeof props.optionValue === "string") {
246
+ value = option[props.optionValue];
247
+ } else if (typeof props.optionValue === "function") {
248
+ value = props.optionValue(option);
249
+ }
250
+ // Note the __null__ special case here. See the onUpdate function for more details
251
+ return value === null ? "__null__" : value;
250
252
  }
251
253
 
252
254
  /**
@@ -256,14 +258,14 @@ function resolveValue(option) {
256
258
  * @param value
257
259
  */
258
260
  function onUpdate(value) {
259
- if (Array.isArray(value)) {
260
- value = value.map((v) => v === "__null__" ? null : v);
261
- }
261
+ if (Array.isArray(value)) {
262
+ value = value.map((v) => v === "__null__" ? null : v);
263
+ }
262
264
 
263
- value = value === "__null__" ? null : value;
265
+ value = value === "__null__" ? null : value;
264
266
 
265
- emit("update", value);
266
- emit("update:model-value", value);
267
+ emit("update", value);
268
+ emit("update:model-value", value);
267
269
  }
268
270
 
269
271
  /** XXX: This tells us when we should apply the filter. QSelect likes to trigger a new filter everytime you open the dropdown
@@ -277,19 +279,19 @@ const shouldFilter = ref(false);
277
279
  * @param update
278
280
  */
279
281
  async function onFilter(val, update) {
280
- if (!props.filterFn) {
281
- filter.value = val;
282
- await nextTick(update);
283
- } else {
284
- update();
285
- if (shouldFilter.value === false) return;
286
- if (val !== null && val !== filter.value) {
287
- filter.value = val;
288
- if (props.filterFn) {
289
- await props.filterFn(val);
290
- }
291
- }
292
- }
282
+ if (!props.filterFn) {
283
+ filter.value = val;
284
+ await nextTick(update);
285
+ } else {
286
+ update();
287
+ if (shouldFilter.value === false) return;
288
+ if (val !== null && val !== filter.value) {
289
+ filter.value = val;
290
+ if (props.filterFn) {
291
+ await props.filterFn(val);
292
+ }
293
+ }
294
+ }
293
295
  }
294
296
 
295
297
  /**
@@ -297,29 +299,29 @@ async function onFilter(val, update) {
297
299
  * See the onUpdate function for more details
298
300
  */
299
301
  function onClear() {
300
- emit("update:model-value", undefined);
301
- emit("update", undefined);
302
+ emit("update:model-value", undefined);
303
+ emit("update", undefined);
302
304
  }
303
305
 
304
306
  /**
305
307
  * Handle behavior when showing the dropdown
306
308
  */
307
309
  function onShow() {
308
- isShowing.value = true;
310
+ isShowing.value = true;
309
311
 
310
- // XXX: See description on shouldFilter declaration. Only allow filtering after dropdown is ALREADY opened
311
- shouldFilter.value = false;
312
- nextTick(() => {
313
- shouldFilter.value = true;
314
- selectField.value.focus();
315
- });
312
+ // XXX: See description on shouldFilter declaration. Only allow filtering after dropdown is ALREADY opened
313
+ shouldFilter.value = false;
314
+ nextTick(() => {
315
+ shouldFilter.value = true;
316
+ selectField.value.focus();
317
+ });
316
318
  }
317
319
 
318
320
  /**
319
321
  * Handle behavior when hiding the dropdown
320
322
  */
321
323
  function onHide() {
322
- isShowing.value = false;
323
- shouldFilter.value = false;
324
+ isShowing.value = false;
325
+ shouldFilter.value = false;
324
326
  }
325
327
  </script>
@@ -77,10 +77,18 @@
77
77
  :show-name="showName"
78
78
  :rows="field.type === 'TEXTAREA' ? 5 : 1"
79
79
  :clearable="field.clearable || clearable"
80
+ :file-class="fileClass"
80
81
  :disable="disable"
81
82
  :readonly="readonly"
82
83
  @update:model-value="onInput(field.name, $event)"
83
- />
84
+ >
85
+ <template #below-file="{file}">
86
+ <slot
87
+ name="below-file"
88
+ :file="file"
89
+ />
90
+ </template>
91
+ </Component>
84
92
  </div>
85
93
  <ConfirmDialog
86
94
  v-if="variationToEdit !== false"
@@ -145,7 +153,11 @@ const props = defineProps({
145
153
  type: [String, Number, Boolean],
146
154
  default: undefined
147
155
  },
148
- canModifyVariations: Boolean
156
+ canModifyVariations: Boolean,
157
+ fileClass: {
158
+ type: String,
159
+ default: ""
160
+ }
149
161
  });
150
162
 
151
163
  const FORM_FIELD_MAP = {
@@ -1,78 +1,83 @@
1
1
  <template>
2
2
  <div
3
- class="relative flex justify-center bg-gray-100 overflow-hidden"
3
+ class="relative flex justify-center"
4
4
  :class="{'rounded-2xl': !square}"
5
5
  >
6
- <template v-if="computedImage">
7
- <div
8
- class="grow h-full"
9
- @click="showPreview = true"
10
- >
6
+ <div
7
+ class=" bg-gray-100 overflow-hidden flex justify-center relative w-full h-full"
8
+ :class="{'rounded-2xl': !square}"
9
+ >
10
+ <template v-if="computedImage">
11
11
  <div
12
- v-if="isVideo"
13
- class="relative max-h-full max-w-full w-full flex justify-center"
12
+ class="grow h-full"
13
+ @click="showPreview = true"
14
14
  >
15
- <video
16
- class="max-h-full"
17
- preload="auto"
15
+ <div
16
+ v-if="isVideo"
17
+ class="relative max-h-full max-w-full w-full flex justify-center"
18
18
  >
19
- <source
20
- :src="previewUrl + '#t=0.1'"
21
- :type="mimeType"
19
+ <video
20
+ class="max-h-full"
21
+ preload="auto"
22
22
  >
23
- </video>
24
- <button :class="cls['play-button']">
25
- <PlayIcon class="w-16" />
26
- </button>
23
+ <source
24
+ :src="previewUrl + '#t=0.1'"
25
+ :type="mimeType"
26
+ >
27
+ </video>
28
+ <button :class="cls['play-button']">
29
+ <PlayIcon class="w-16" />
30
+ </button>
31
+ </div>
32
+ <QImg
33
+ v-if="thumbUrl || isPreviewable"
34
+ fit="fill"
35
+ class="non-selectable max-h-full max-w-full h-full"
36
+ :src="(thumbUrl || previewUrl) + '#t=0.1'"
37
+ preload="auto"
38
+ data-testid="previewed-image"
39
+ />
40
+ <div
41
+ v-else
42
+ class="flex items-center justify-center h-full"
43
+ >
44
+ <PdfIcon
45
+ v-if="isPdf"
46
+ class="w-24"
47
+ />
48
+ <TextFileIcon
49
+ v-else
50
+ class="w-24"
51
+ />
52
+ </div>
27
53
  </div>
28
- <QImg
29
- v-if="thumbUrl || isPreviewable"
30
- fit="fill"
31
- class="non-selectable max-h-full max-w-full h-full"
32
- :src="(thumbUrl || previewUrl) + '#t=0.1'"
33
- preload="auto"
34
- data-testid="previewed-image"
35
- />
36
54
  <div
37
- v-else
38
- class="flex items-center justify-center h-full"
55
+ v-if="$slots['action-button']"
56
+ :class="cls['action-button']"
39
57
  >
40
- <PdfIcon
41
- v-if="isPdf"
42
- class="w-24"
43
- />
44
- <TextFileIcon
45
- v-else
46
- class="w-24"
58
+ <slot name="action-button" />
59
+ </div>
60
+ <div
61
+ v-if="image && image.progress !== undefined"
62
+ class="absolute-bottom w-full"
63
+ >
64
+ <QLinearProgress
65
+ :value="image.progress"
66
+ size="15px"
67
+ color="green-600"
68
+ stripe
47
69
  />
48
70
  </div>
49
- </div>
50
- <div
51
- v-if="$slots['action-button']"
52
- :class="cls['action-button']"
53
- >
54
- <slot name="action-button" />
55
- </div>
56
- <div
57
- v-if="image && image.progress !== undefined"
58
- class="absolute-bottom w-full"
59
- >
60
- <QLinearProgress
61
- :value="image.progress"
62
- size="15px"
63
- color="green-600"
64
- stripe
65
- />
66
- </div>
67
- </template>
68
- <template v-else>
69
- <slot name="missing">
70
- <component
71
- :is="missingIcon"
72
- class="w-full h-full p-2 text-gray-300"
73
- />
74
- </slot>
75
- </template>
71
+ </template>
72
+ <template v-else>
73
+ <slot name="missing">
74
+ <component
75
+ :is="missingIcon"
76
+ class="w-full h-full p-2 text-gray-300"
77
+ />
78
+ </slot>
79
+ </template>
80
+ </div>
76
81
 
77
82
  <div class="absolute top-1 right-1 flex items-center justify-between space-x-1">
78
83
  <QBtn
@@ -104,6 +109,10 @@
104
109
  </QBtn>
105
110
  </div>
106
111
 
112
+ <div class="below-file absolute-bottom">
113
+ <slot name="below" />
114
+ </div>
115
+
107
116
  <FullScreenCarouselDialog
108
117
  v-if="showPreview && !disabled"
109
118
  :files="relatedFiles || [computedImage]"