quasar-ui-danx 0.0.10 → 0.0.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 (89) hide show
  1. package/package.json +8 -2
  2. package/src/components/ActionTable/ActionTable.vue +143 -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 +143 -0
  10. package/src/components/ActionTable/Filters/index.ts +5 -0
  11. package/src/components/ActionTable/Form/Fields/BooleanField.vue +37 -0
  12. package/src/components/ActionTable/Form/Fields/ConfirmPasswordField.vue +46 -0
  13. package/src/components/ActionTable/Form/Fields/DateField.vue +59 -0
  14. package/src/components/ActionTable/Form/Fields/DateRangeField.vue +110 -0
  15. package/src/components/ActionTable/Form/Fields/DateTimeField.vue +50 -0
  16. package/src/components/ActionTable/Form/Fields/DateTimePicker.vue +59 -0
  17. package/src/components/ActionTable/Form/Fields/EditableDiv.vue +39 -0
  18. package/src/components/ActionTable/Form/Fields/FieldLabel.vue +32 -0
  19. package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +78 -0
  20. package/src/components/ActionTable/Form/Fields/InlineDateTimeField.vue +44 -0
  21. package/src/components/ActionTable/Form/Fields/IntegerField.vue +26 -0
  22. package/src/components/ActionTable/Form/Fields/LabelValueBlock.vue +22 -0
  23. package/src/components/ActionTable/Form/Fields/LabeledInput.vue +63 -0
  24. package/src/components/ActionTable/Form/Fields/MultiFileField.vue +91 -0
  25. package/src/components/ActionTable/Form/Fields/MultiKeywordField.vue +57 -0
  26. package/src/components/ActionTable/Form/Fields/NewPasswordField.vue +39 -0
  27. package/src/components/ActionTable/Form/Fields/NumberField.vue +94 -0
  28. package/src/components/ActionTable/Form/Fields/NumberRangeField.vue +140 -0
  29. package/src/components/ActionTable/Form/Fields/SelectDrawer.vue +136 -0
  30. package/src/components/ActionTable/Form/Fields/SelectField.vue +318 -0
  31. package/src/components/ActionTable/Form/Fields/SelectWithChildrenField.vue +81 -0
  32. package/src/components/ActionTable/Form/Fields/SingleFileField.vue +78 -0
  33. package/src/components/ActionTable/Form/Fields/TextField.vue +82 -0
  34. package/src/components/ActionTable/Form/Fields/WysiwygField.vue +46 -0
  35. package/src/components/ActionTable/Form/Fields/index.ts +23 -0
  36. package/src/components/ActionTable/Form/RenderedForm.vue +76 -0
  37. package/src/components/ActionTable/Form/index.ts +2 -0
  38. package/src/components/ActionTable/RenderComponentColumn.vue +22 -0
  39. package/src/components/ActionTable/TableSummaryRow.vue +95 -0
  40. package/src/components/ActionTable/index.ts +10 -0
  41. package/src/components/ActionTable/listActions.ts +362 -0
  42. package/src/components/ActionTable/listHelpers.ts +74 -0
  43. package/src/components/ActionTable/tableColumns.ts +72 -0
  44. package/src/components/DragAndDrop/HandleDraggable.vue +29 -29
  45. package/src/components/DragAndDrop/ListItemDraggable.vue +10 -10
  46. package/src/components/DragAndDrop/index.ts +0 -1
  47. package/src/components/DragAndDrop/listDragAndDrop.ts +1 -1
  48. package/src/components/Utility/CollapsableSidebar.vue +119 -0
  49. package/src/components/Utility/ContentDrawer.vue +70 -0
  50. package/src/components/Utility/Dialogs/ConfirmDialog.vue +132 -0
  51. package/src/components/Utility/Dialogs/FullScreenDialog.vue +46 -0
  52. package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +105 -0
  53. package/src/components/Utility/Dialogs/InfoDialog.vue +92 -0
  54. package/src/components/Utility/Dialogs/InputDialog.vue +35 -0
  55. package/src/components/Utility/ImagePreview.vue +192 -0
  56. package/src/components/Utility/Popover/PopoverMenu.vue +64 -0
  57. package/src/components/Utility/Transitions/ListTransition.vue +50 -0
  58. package/src/components/Utility/Transitions/SlideTransition.vue +63 -0
  59. package/src/components/Utility/Transitions/StaggeredListTransition.vue +97 -0
  60. package/src/components/Utility/index.ts +11 -0
  61. package/src/components/index.ts +3 -0
  62. package/src/helpers/FileUpload.ts +295 -0
  63. package/src/helpers/FlashMessages.ts +79 -0
  64. package/src/helpers/array.ts +37 -0
  65. package/src/helpers/compatibility.ts +64 -0
  66. package/src/helpers/date.ts +5 -0
  67. package/src/helpers/download.ts +200 -0
  68. package/src/helpers/downloadPdf.ts +92 -0
  69. package/src/helpers/files.ts +52 -0
  70. package/src/helpers/formats.ts +183 -0
  71. package/src/helpers/http.ts +62 -0
  72. package/src/helpers/index.ts +12 -1
  73. package/src/helpers/multiFileUpload.ts +68 -0
  74. package/src/helpers/singleFileUpload.ts +54 -0
  75. package/src/helpers/storage.ts +8 -0
  76. package/src/index.esm.js +3 -4
  77. package/src/svg/FilterIcon.svg +7 -0
  78. package/src/svg/ImageIcon.svg +30 -0
  79. package/src/svg/PdfIcon.svg +21 -0
  80. package/src/svg/PercentIcon.svg +13 -0
  81. package/src/svg/TrashIcon.svg +15 -0
  82. package/src/svg/XIcon.svg +18 -0
  83. package/src/svg/index.ts +8 -0
  84. package/src/vendor/tinymce-config.ts +1 -0
  85. package/src/vue-plugin.js +7 -4
  86. package/tsconfig.json +14 -13
  87. package/src/components/DragAndDrop/Icons/index.ts +0 -2
  88. /package/src/{components/DragAndDrop/Icons → svg}/DragHandleDotsIcon.svg +0 -0
  89. /package/src/{components/DragAndDrop/Icons → svg}/DragHandleIcon.svg +0 -0
@@ -0,0 +1,57 @@
1
+ <template>
2
+ <div>
3
+ <SelectField
4
+ v-model="selectedFieldName"
5
+ :label="undefined"
6
+ :options="field.options"
7
+ class="mb-2"
8
+ @update:model-value="onChange"
9
+ />
10
+ <TextField
11
+ v-model="textInput"
12
+ :field="field"
13
+ :no-label="!field.label"
14
+ label-class="text-xs font-bold text-gray-dark"
15
+ parent-class="tight-label"
16
+ input-class="!py-0"
17
+ dense
18
+ type="textarea"
19
+ :debounce="500"
20
+ @update:model-value="onChange"
21
+ />
22
+ </div>
23
+ </template>
24
+
25
+ <script setup>
26
+ import { computed, ref, watch } from 'vue';
27
+ import SelectField from './SelectField';
28
+ import TextField from './TextField';
29
+
30
+ const emit = defineEmits(['update:model-value']);
31
+ const props = defineProps({
32
+ modelValue: {
33
+ type: [String, Number, Object],
34
+ default: ''
35
+ },
36
+ field: {
37
+ type: Object,
38
+ default: null
39
+ }
40
+ });
41
+
42
+ const selectedFieldName = ref(props.field.defaultOption);
43
+ const searchList = computed(() => props.modelValue && props.modelValue[selectedFieldName.value]);
44
+ const textInput = ref(formatModelValue());
45
+ 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);
48
+ }
49
+
50
+ function formatModelValue() {
51
+ return Array.isArray(searchList.value) ? searchList.value?.join(',') : '';
52
+ }
53
+
54
+ watch(() => props.modelValue, () => {
55
+ textInput.value = formatModelValue();
56
+ });
57
+ </script>
@@ -0,0 +1,39 @@
1
+ <template>
2
+ <LabeledInput
3
+ type="password"
4
+ v-bind="props"
5
+ :rules="rules"
6
+ @update:model-value="$emit('update:model-value', $event)"
7
+ />
8
+ </template>
9
+
10
+ <script setup>
11
+ import LabeledInput from './LabeledInput';
12
+
13
+ defineEmits(['update:model-value']);
14
+ const props = defineProps({
15
+ name: {
16
+ type: String,
17
+ default: 'password'
18
+ },
19
+ label: {
20
+ type: String,
21
+ default: 'Password'
22
+ },
23
+ placeholder: {
24
+ type: String,
25
+ default: 'Enter Password'
26
+ },
27
+ modelValue: {
28
+ type: [String, Number],
29
+ required: true
30
+ },
31
+ error: {
32
+ type: String,
33
+ default: null
34
+ },
35
+ disabled: Boolean
36
+ });
37
+
38
+ const rules = [(val) => val.length >= 8 || 'Please use at least 8 characters'];
39
+ </script>
@@ -0,0 +1,94 @@
1
+ <template>
2
+ <q-input
3
+ :model-value="numberVal"
4
+ :data-testid="'number-field-' + fieldOptions.id"
5
+ :placeholder="fieldOptions.placeholder"
6
+ outlined
7
+ dense
8
+ inputmode="numeric"
9
+ :input-class="{[inputClass]: true, 'text-right bg-white': !hidePrependLabel, 'text-right !text-xs text-black font-normal': hidePrependLabel}"
10
+ :class="{'no-prepend-icon w-32': hidePrependLabel, 'prepend-label': !hidePrependLabel}"
11
+ @update:model-value="onInput"
12
+ >
13
+ <template #prepend>
14
+ <FieldLabel
15
+ :field="fieldOptions"
16
+ :show-name="showName"
17
+ />
18
+ </template>
19
+ </q-input>
20
+ </template>
21
+
22
+ <script setup>
23
+ import { fNumber } from '@ui/helpers/formats';
24
+ import { computed, nextTick, ref, watch } from 'vue';
25
+ import FieldLabel from './FieldLabel';
26
+
27
+ const emit = defineEmits(['update:model-value']);
28
+ const props = defineProps({
29
+ modelValue: {
30
+ type: [String, Number],
31
+ default: ''
32
+ },
33
+ precision: {
34
+ type: Number,
35
+ default: 2
36
+ },
37
+ label: {
38
+ type: String,
39
+ default: undefined
40
+ },
41
+ field: {
42
+ type: Object,
43
+ default: null
44
+ },
45
+ inputClass: {
46
+ type: String,
47
+ default: ''
48
+ },
49
+ hidePrependLabel: Boolean,
50
+ currency: Boolean,
51
+ showName: Boolean
52
+ });
53
+
54
+ const numberVal = ref(format(props.modelValue));
55
+ watch(() => props.modelValue, () => numberVal.value = format(props.modelValue));
56
+
57
+ const fieldOptions = computed(() => props.field || { label: props.label || '', placeholder: '', id: '' });
58
+
59
+ function format(number) {
60
+ if (!number && number !== 0 && number !== '0') return number;
61
+
62
+ const minimumFractionDigits = Math.min(props.precision, ('' + number).split('.')[1]?.length || 0);
63
+ let options = {
64
+ minimumFractionDigits
65
+ };
66
+
67
+ if (props.currency) {
68
+ options = {
69
+ style: 'currency',
70
+ currency: 'USD',
71
+ minimumFractionDigits
72
+ };
73
+ }
74
+ return fNumber(number, options);
75
+ }
76
+ function onInput(value) {
77
+ let number = '';
78
+
79
+ // Prevent invalid characters
80
+ if (value.match(/[^\d.,$]/)) {
81
+ const oldVal = numberVal.value;
82
+ // XXX: To get QInput to show only the value we want
83
+ numberVal.value += ' ';
84
+ return nextTick(() => numberVal.value = oldVal);
85
+ }
86
+
87
+ if (value !== '') {
88
+ value = value.replace(/[^\d.]/g, '');
89
+ number = Number(value);
90
+ numberVal.value = format(number);
91
+ }
92
+ emit('update:model-value', number === '' ? undefined : number);
93
+ }
94
+ </script>
@@ -0,0 +1,140 @@
1
+ <template>
2
+ <div>
3
+ <div
4
+ v-if="label"
5
+ class="font-bold text-xs mb-2"
6
+ >
7
+ {{ label }}
8
+ </div>
9
+ <div class="flex items-center flex-nowrap cursor-pointer">
10
+ <component
11
+ :is="previewIcon"
12
+ class="w-5 text-blue-base"
13
+ />
14
+ <div class="text-sm ml-3 hover:text-blue-base whitespace-nowrap">
15
+ <template v-if="range">
16
+ {{ formatNum(range.from || 0) }} - {{ formatNum(range.to) }}
17
+ </template>
18
+ <template v-else>
19
+ No Limit
20
+ </template>
21
+ </div>
22
+ </div>
23
+ <q-popup-proxy>
24
+ <NumberField
25
+ v-model="range.from"
26
+ :field="minField"
27
+ @update:model-value="onSave"
28
+ />
29
+ <NumberField
30
+ v-model="range.to"
31
+ class="mt-2"
32
+ :field="maxField"
33
+ @update:model-value="onSave"
34
+ />
35
+ </q-popup-proxy>
36
+ </div>
37
+ </template>
38
+
39
+ <script setup>
40
+ import { CurrencyDollarIcon as CurrencyIcon, HashtagIcon as NumberIcon } from '@heroicons/vue/outline';
41
+ import { fCurrency, fNumber, fPercent } from '@ui/helpers';
42
+ import { PercentIcon } from '@ui/svg';
43
+ import { useDebounceFn } from '@vueuse/core';
44
+ import { computed, ref, watch } from 'vue';
45
+ import NumberField from './NumberField';
46
+
47
+ const emit = defineEmits(['update:model-value']);
48
+ const props = defineProps({
49
+ modelValue: {
50
+ type: Object,
51
+ default: null
52
+ },
53
+ label: {
54
+ type: String,
55
+ default: null
56
+ },
57
+ icon: {
58
+ type: Object,
59
+ default: null
60
+ },
61
+ currency: Boolean,
62
+ percent: Boolean,
63
+ debounce: {
64
+ type: Number,
65
+ default: 0
66
+ }
67
+ });
68
+
69
+ const symbol = computed(() => {
70
+ if (props.currency) {
71
+ return '$';
72
+ } else if (props.percent) {
73
+ return '%';
74
+ } else {
75
+ return '';
76
+ }
77
+ });
78
+ const minField = computed(() => {
79
+ return {
80
+ id: 'min-field',
81
+ name: 'from',
82
+ label: 'Min' + symbol.value,
83
+ placeholder: '0'
84
+ };
85
+ });
86
+
87
+ const maxField = computed(() => {
88
+ return {
89
+ id: 'max-field',
90
+ name: 'to',
91
+ label: 'Max' + symbol.value,
92
+ placeholder: 'No Limit'
93
+ };
94
+ });
95
+
96
+ const previewIcon = computed(() => props.icon || (props.currency ? CurrencyIcon : (props.percent ? PercentIcon : NumberIcon)));
97
+
98
+ const range = ref({});
99
+ watch(() => props.modelValue, setRange);
100
+ function setRange(val) {
101
+ const multiplier = props.percent ? 100 : 1;
102
+ range.value = {
103
+ from: (val?.from ? val.from * multiplier : undefined),
104
+ to: (val?.to ? val.to * multiplier : undefined)
105
+ };
106
+ }
107
+ setRange(props.modelValue || { from: undefined, to: undefined });
108
+
109
+ /**
110
+ * Convert the number into a nicely formatted string
111
+ * @param num
112
+ * @returns {string}
113
+ */
114
+ function formatNum(num) {
115
+ if (num === undefined) return 'No Limit';
116
+ if (props.currency) {
117
+ return fCurrency(num);
118
+ }
119
+ if (props.percent) {
120
+ return fPercent(num, { multiplier: 1, maximumFractionDigits: 2 });
121
+ }
122
+ return fNumber(num);
123
+ }
124
+
125
+ /**
126
+ * Update the modelValue only after the debounce timeout
127
+ * Empty values are converted to undefined so they will not be filtered on
128
+ * @type {(function(): void)|*}
129
+ */
130
+ const onSave = useDebounceFn(() => {
131
+ if (range.value && (range.value.from || range.value.to)) {
132
+ const multiplier = props.percent ? .01 : 1;
133
+ let newVal = {
134
+ from: (range.value.from ? range.value.from * multiplier : undefined),
135
+ to: (range.value.to ? range.value.to * multiplier : undefined)
136
+ };
137
+ emit('update:model-value', newVal);
138
+ }
139
+ }, props.debounce);
140
+ </script>
@@ -0,0 +1,136 @@
1
+ <template>
2
+ <div>
3
+ <ContentDrawer
4
+ v-model:show="showDrawer"
5
+ content-class=""
6
+ position="bottom"
7
+ :title="'Filter ' + label"
8
+ >
9
+ <div
10
+ v-for="option in formattedOptions"
11
+ :key="'select-drawer-' + option.value"
12
+ :data-dusk="'drawer-opt-' + option.value"
13
+ class="cursor-pointer hover:bg-gray-light px-8 py-3 flex items-center border-b border-gray-light"
14
+ @click="toggleSelect(option)"
15
+ >
16
+ <q-checkbox
17
+ :model-value="isSelected(option)"
18
+ class="mr-2"
19
+ />
20
+ <slot
21
+ name="option"
22
+ :opt="option"
23
+ >{{ option.label }}
24
+ </slot>
25
+ </div>
26
+ </ContentDrawer>
27
+
28
+ <q-chip
29
+ ref="select"
30
+ outline
31
+ clickable
32
+ size="16px"
33
+ @click="showDrawer = true"
34
+ >
35
+ <slot name="selected">
36
+ <slot name="label">{{ label }}:&nbsp;</slot>
37
+ <template v-if="modelValue && modelValue.length > 0">
38
+ <slot name="selection">
39
+ <template v-if="multiple">
40
+ {{ getOptionLabel(modelValue[0]) }}
41
+ <template
42
+ v-if="modelValue.length > 1"
43
+ >+ {{ modelValue.length - 1 }}
44
+ </template
45
+ >
46
+ </template>
47
+ <template v-else>
48
+ {{ getOptionLabel(modelValue) }}
49
+ </template>
50
+ </slot>
51
+ </template>
52
+ <template v-else>
53
+ <slot name="placeholder">{{ placeholder }}</slot>
54
+ </template>
55
+ </slot>
56
+ </q-chip>
57
+ </div>
58
+ </template>
59
+
60
+ <script setup>
61
+ import { ContentDrawer } from '@ui/components';
62
+ import { computed, ref } from 'vue';
63
+
64
+ const emit = defineEmits(['update:modelValue']);
65
+ const props = defineProps({
66
+ modelValue: {
67
+ type: [Object, String, Array, null],
68
+ required: true
69
+ },
70
+ options: {
71
+ type: Array,
72
+ default: () => []
73
+ },
74
+ multiple: Boolean,
75
+ label: {
76
+ type: String,
77
+ default: 'Select'
78
+ },
79
+ placeholder: {
80
+ type: String,
81
+ default: 'All'
82
+ }
83
+ });
84
+
85
+ const showDrawer = ref(false);
86
+ const formattedOptions = computed(() =>
87
+ props.options.map((opt) =>
88
+ typeof opt === 'string'
89
+ ? {
90
+ label: opt,
91
+ value: opt
92
+ }
93
+ : opt
94
+ )
95
+ );
96
+
97
+ function getOptionValue(option) {
98
+ return option.value === undefined ? option : option.value;
99
+ }
100
+
101
+ function getOptionLabel(value) {
102
+ return formattedOptions.value.find((opt) => opt.value === value).label;
103
+ }
104
+
105
+ function isSelected(option) {
106
+ const optionValue = getOptionValue(option);
107
+
108
+ if (props.multiple) {
109
+ return props.modelValue.includes(optionValue);
110
+ } else {
111
+ return props.modelValue === optionValue;
112
+ }
113
+ }
114
+
115
+ function toggleSelect(option) {
116
+ let optionValue = getOptionValue(option);
117
+
118
+ let selection = optionValue;
119
+
120
+ if (props.multiple) {
121
+ selection = [...props.modelValue];
122
+ if (isSelected(optionValue)) {
123
+ selection = selection.filter((opt) => opt !== optionValue);
124
+ } else {
125
+ selection.push(optionValue);
126
+ }
127
+ } else {
128
+ // Allow deselection on single select
129
+ if (selection === props.modelValue) {
130
+ selection = null;
131
+ }
132
+ }
133
+
134
+ emit('update:modelValue', selection);
135
+ }
136
+ </script>