quasar-ui-danx 0.0.10 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. package/package.json +3 -2
  2. package/src/components/ActionTable/ActionTable.vue +135 -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 +141 -0
  10. package/src/components/ActionTable/Form/Fields/BooleanField.vue +37 -0
  11. package/src/components/ActionTable/Form/Fields/ConfirmPasswordField.vue +46 -0
  12. package/src/components/ActionTable/Form/Fields/DateField.vue +59 -0
  13. package/src/components/ActionTable/Form/Fields/DateRangeField.vue +110 -0
  14. package/src/components/ActionTable/Form/Fields/DateTimeField.vue +50 -0
  15. package/src/components/ActionTable/Form/Fields/DateTimePicker.vue +59 -0
  16. package/src/components/ActionTable/Form/Fields/EditableDiv.vue +39 -0
  17. package/src/components/ActionTable/Form/Fields/FieldLabel.vue +32 -0
  18. package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +78 -0
  19. package/src/components/ActionTable/Form/Fields/InlineDateTimeField.vue +44 -0
  20. package/src/components/ActionTable/Form/Fields/IntegerField.vue +26 -0
  21. package/src/components/ActionTable/Form/Fields/LabeledInput.vue +63 -0
  22. package/src/components/ActionTable/Form/Fields/MultiFileField.vue +91 -0
  23. package/src/components/ActionTable/Form/Fields/MultiKeywordField.vue +57 -0
  24. package/src/components/ActionTable/Form/Fields/NewPasswordField.vue +39 -0
  25. package/src/components/ActionTable/Form/Fields/NumberField.vue +94 -0
  26. package/src/components/ActionTable/Form/Fields/NumberRangeField.vue +140 -0
  27. package/src/components/ActionTable/Form/Fields/SelectDrawer.vue +136 -0
  28. package/src/components/ActionTable/Form/Fields/SelectField.vue +318 -0
  29. package/src/components/ActionTable/Form/Fields/SelectWithChildrenField.vue +81 -0
  30. package/src/components/ActionTable/Form/Fields/SingleFileField.vue +78 -0
  31. package/src/components/ActionTable/Form/Fields/TextField.vue +82 -0
  32. package/src/components/ActionTable/Form/Fields/WysiwygField.vue +46 -0
  33. package/src/components/ActionTable/Form/Fields/index.ts +23 -0
  34. package/src/components/ActionTable/Form/RenderedForm.vue +74 -0
  35. package/src/components/ActionTable/RenderComponentColumn.vue +22 -0
  36. package/src/components/ActionTable/TableSummaryRow.vue +95 -0
  37. package/src/components/ActionTable/index.ts +15 -0
  38. package/src/components/ActionTable/listActions.ts +361 -0
  39. package/src/components/ActionTable/tableColumns.ts +72 -0
  40. package/src/components/ActionTable/tableHelpers.ts +83 -0
  41. package/src/components/Utility/CollapsableSidebar.vue +119 -0
  42. package/src/components/Utility/ContentDrawer.vue +70 -0
  43. package/src/components/Utility/Dialogs/ConfirmDialog.vue +132 -0
  44. package/src/components/Utility/Dialogs/FullScreenDialog.vue +46 -0
  45. package/src/components/Utility/Dialogs/InfoDialog.vue +92 -0
  46. package/src/components/Utility/Dialogs/InputDialog.vue +35 -0
  47. package/src/components/Utility/Transitions/ListTransition.vue +50 -0
  48. package/src/components/Utility/Transitions/SlideTransition.vue +63 -0
  49. package/src/components/Utility/Transitions/StaggeredListTransition.vue +97 -0
  50. package/src/components/Utility/index.ts +9 -0
  51. package/src/components/index.ts +3 -0
  52. package/src/helpers/FileUpload.ts +294 -0
  53. package/src/helpers/FlashMessages.ts +79 -0
  54. package/src/helpers/array.ts +37 -0
  55. package/src/helpers/compatibility.ts +64 -0
  56. package/src/helpers/date.ts +5 -0
  57. package/src/helpers/download.ts +192 -0
  58. package/src/helpers/downloadPdf.ts +92 -0
  59. package/src/helpers/files.ts +52 -0
  60. package/src/helpers/formats.ts +183 -0
  61. package/src/helpers/http.ts +62 -0
  62. package/src/helpers/index.ts +10 -1
  63. package/src/helpers/multiFileUpload.ts +68 -0
  64. package/src/helpers/singleFileUpload.ts +54 -0
  65. package/src/helpers/storage.ts +8 -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 SelectField from "danx/src/components/ActionTable/Form/Fields/SelectField";
27
+ import TextField from "danx/src/components/ActionTable/Form/Fields/TextField";
28
+ import { computed, ref, watch } from "vue";
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 "danx/src/components/ActionTable/Form/Fields/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
+ <QInput
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
+ </QInput>
20
+ </template>
21
+
22
+ <script setup>
23
+ import FieldLabel from "danx/src/components/ActionTable/Form/Fields/FieldLabel";
24
+ import { fNumber } from "danx/src/helpers/formats";
25
+ import { computed, nextTick, ref, watch } from "vue";
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
+ <QPopupProxy>
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
+ </QPopupProxy>
36
+ </div>
37
+ </template>
38
+
39
+ <script setup>
40
+ import { PercentIcon } from "@/svg";
41
+ import { CurrencyDollarIcon as CurrencyIcon, HashtagIcon as NumberIcon } from "@heroicons/vue/outline";
42
+ import { useDebounceFn } from "@vueuse/core";
43
+ import NumberField from "danx/src/components/ActionTable/Form/Fields/NumberField";
44
+ import { fCurrency, fNumber, fPercent } from "danx/src/helpers/formats";
45
+ import { computed, ref, watch } from "vue";
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
+ <QCheckbox
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
+ <QChip
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
+ </QChip>
57
+ </div>
58
+ </template>
59
+
60
+ <script setup>
61
+ import { ContentDrawer } from "danx/src/components/Utility";
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>