quasar-ui-danx 0.0.10 → 0.0.12

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