quasar-ui-danx 0.0.10 → 0.0.11

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 (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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quasar-ui-danx",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "author": "Dan <dan@flytedesk.com>",
5
5
  "description": "DanX Vue / Quasar component library",
6
6
  "license": "MIT",
@@ -62,6 +62,7 @@
62
62
  "last 4 iOS versions"
63
63
  ],
64
64
  "dependencies": {
65
- "@vueuse/core": "^10.7.2"
65
+ "@vueuse/core": "^10.7.2",
66
+ "tailwindcss": "^3.4.1"
66
67
  }
67
68
  }
@@ -0,0 +1,135 @@
1
+ <!--suppress VueUnrecognizedSlot -->
2
+ <template>
3
+ <QTable
4
+ ref="actionTable"
5
+ :selected="selectedRows"
6
+ :pagination="quasarPagination"
7
+ :columns="columns"
8
+ :loading="isLoadingList"
9
+ :rows="pagedItems?.data || []"
10
+ selection="multiple"
11
+ :rows-per-page-options="[25,50,100]"
12
+ class="sticky-column sticky-header w-full !border-0"
13
+ color="blue-base"
14
+ @update:selected="$emit('update:selected-rows', $event)"
15
+ @update:pagination="() => {}"
16
+ @request="$emit('update:quasar-pagination', $event.pagination)"
17
+ >
18
+ <template #no-data>
19
+ <slot name="empty">
20
+ <EmptyTableState :text="`There are no ${label.toLowerCase()} matching the applied filter`" />
21
+ </slot>
22
+ </template>
23
+ <template #top-row>
24
+ <TableSummaryRow
25
+ :label="label"
26
+ :item-count="summary?.count || 0"
27
+ :selected-count="selectedRows.length"
28
+ :loading="isLoadingSummary"
29
+ :summary="summary"
30
+ :columns="columns"
31
+ @clear="$emit('update:selected-rows', [])"
32
+ />
33
+ </template>
34
+ <template #header-cell="rowProps">
35
+ <QTh
36
+ :key="rowProps.key"
37
+ :props="rowProps"
38
+ :data-drop-zone="`resize-column-` + rowProps.col.name"
39
+ >
40
+ {{ rowProps.col.label }}
41
+ <HandleDraggable
42
+ v-if="rowProps.col.resizeable"
43
+ :drop-zone="`resize-column-` + rowProps.col.name"
44
+ class="resize-handle"
45
+ @resize="rowProps.col.onResize"
46
+ >
47
+ <RowResizeIcon class="w-4 text-neutral-base" />
48
+ </HandleDraggable>
49
+ </QTh>
50
+ </template>
51
+ <template #body-cell="rowProps">
52
+ <QTd :key="rowProps.key" :props="rowProps">
53
+ <template v-if="rowProps.col.component">
54
+ <RenderComponentColumn
55
+ :row-props="rowProps"
56
+ @action="$emit('action', $event)"
57
+ />
58
+ </template>
59
+ <template v-else-if="rowProps.col.fieldList">
60
+ <div v-for="field in rowProps.col.fieldList" :key="field">
61
+ {{ rowProps.row[field] }}
62
+ </div>
63
+ </template>
64
+ <template v-else-if="rowProps.col.filterOnClick">
65
+ <a @click="$emit('filter', rowProps.col.filterOnClick(rowProps.row))">
66
+ {{ rowProps.value }}
67
+ </a>
68
+ </template>
69
+ <template v-else>
70
+ <slot v-bind="{name: rowProps.col.name, row: rowProps.row, value: rowProps.value}">
71
+ {{ rowProps.value }}
72
+ </slot>
73
+ </template>
74
+ </QTd>
75
+ </template>
76
+ </QTable>
77
+ </template>
78
+
79
+ <script setup>
80
+ import { DragHandleIcon as RowResizeIcon } from "@/svg";
81
+ import { EmptyTableState, registerStickyScrolling, RenderComponentColumn, TableSummaryRow } from "danx/src/components";
82
+ import { HandleDraggable } from "quasar-ui-danx/src";
83
+ import { ref } from "vue";
84
+
85
+ defineEmits(["action", "filter", "update:quasar-pagination", "update:selected-rows"]);
86
+ defineProps({
87
+ label: {
88
+ type: String,
89
+ required: true
90
+ },
91
+ selectedRows: {
92
+ type: Array,
93
+ required: true
94
+ },
95
+ quasarPagination: {
96
+ type: Object,
97
+ required: true
98
+ },
99
+ isLoadingList: Boolean,
100
+ pagedItems: {
101
+ type: Object,
102
+ default: null
103
+ },
104
+ isLoadingSummary: Boolean,
105
+ summary: {
106
+ type: Object,
107
+ default: null
108
+ },
109
+ columns: {
110
+ type: Array,
111
+ required: true
112
+ }
113
+ });
114
+ const actionTable = ref(null);
115
+ registerStickyScrolling(actionTable);
116
+ </script>
117
+
118
+ <style lang="scss" scoped>
119
+ [data-drop-zone] {
120
+ .resize-handle {
121
+ position: absolute;
122
+ top: 0;
123
+ right: -.45em;
124
+ width: .9em;
125
+ opacity: 0;
126
+ transition: all .3s;
127
+ }
128
+
129
+ &:hover {
130
+ .resize-handle {
131
+ opacity: 1;
132
+ }
133
+ }
134
+ }
135
+ </style>
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <div>
3
+ <PopoverMenu
4
+ class="bg-neutral-plus-6 px-4 h-full flex"
5
+ :items="items"
6
+ :disabled="selectedRows.length === 0"
7
+ @action="onAction"
8
+ />
9
+ <QTooltip v-if="selectedRows.length === 0">
10
+ Batch actions require a selection
11
+ </QTooltip>
12
+ <slot>
13
+ <Component
14
+ :is="activeComponent.is"
15
+ v-if="activeComponent"
16
+ v-bind="activeComponent.props"
17
+ :is-saving="isSaving"
18
+ @close="activeAction = false"
19
+ @confirm="onConfirmAction"
20
+ />
21
+ </slot>
22
+ </div>
23
+ </template>
24
+ <script setup>
25
+ import PopoverMenu from "components/Common/Menu/PopoverMenu";
26
+ import { computed, ref } from "vue";
27
+
28
+ const emit = defineEmits(["action"]);
29
+ const props = defineProps({
30
+ items: {
31
+ type: Array,
32
+ required: true
33
+ },
34
+ selectedRows: {
35
+ type: Array,
36
+ required: true
37
+ },
38
+ applyBatchAction: {
39
+ type: Function,
40
+ required: true
41
+ },
42
+ isSaving: Boolean
43
+ });
44
+
45
+
46
+ const activeAction = ref(null);
47
+ const activeComponent = computed(() => (props.items.find(i => i.action === activeAction.value)?.component || (() => null))(props.selectedRows));
48
+
49
+ function onAction(action) {
50
+ activeAction.value = action;
51
+ emit("action", action);
52
+ }
53
+ async function onConfirmAction(input) {
54
+ const result = await props.applyBatchAction(input || activeComponent.value.input());
55
+
56
+ if (result?.success) {
57
+ activeAction.value = null;
58
+ }
59
+ }
60
+ </script>
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <div class="flex items-center justify-center text-sm py-14 w-full">
3
+ <div v-if="loading">
4
+ <slot name="loading">{{ loadingText }}
5
+ <QSpinnerBall class="w-4 ml-2" />
6
+ </slot>
7
+ </div>
8
+ <div v-if="saving">
9
+ <slot name="saving">{{ savingText }}
10
+ <QSpinnerBall class="w-4 ml-2" />
11
+ </slot>
12
+ </div>
13
+ <slot>{{ text }}</slot>
14
+ </div>
15
+ </template>
16
+ <script setup>
17
+ defineProps({
18
+ loading: Boolean,
19
+ saving: Boolean,
20
+ text: {
21
+ type: String,
22
+ default: "No records found"
23
+ },
24
+ loadingText: {
25
+ type: String,
26
+ default: "Loading..."
27
+ },
28
+ savingText: {
29
+ type: String,
30
+ default: "Saving..."
31
+ }
32
+ });
33
+ </script>
@@ -0,0 +1,36 @@
1
+ <template>
2
+ <CollapsableSidebar
3
+ :collapse="!showFilters"
4
+ disabled
5
+ min-width="0"
6
+ max-width="18rem"
7
+ name="admin-ads"
8
+ @update:collapse="$emit('update:show-filters', !$event)"
9
+ >
10
+ <FilterGroupList
11
+ :filter="filter"
12
+ :filter-groups="filterGroups"
13
+ @update:filter="$emit('update:filter', $event)"
14
+ />
15
+ </CollapsableSidebar>
16
+ </template>
17
+ <script setup>
18
+ import { CollapsableSidebar, FilterGroupList } from "danx/src/components";
19
+
20
+ defineEmits(["update:filter", "update:show-filters"]);
21
+ defineProps({
22
+ name: {
23
+ type: String,
24
+ required: true
25
+ },
26
+ showFilters: Boolean,
27
+ filter: {
28
+ type: Object,
29
+ default: null
30
+ },
31
+ filterGroups: {
32
+ type: Array,
33
+ default: () => []
34
+ }
35
+ });
36
+ </script>
@@ -0,0 +1,28 @@
1
+ <template>
2
+ <QExpansionItem>
3
+ <template #header>
4
+ <div class="flex items-center flex-nowrap flex-grow">
5
+ <div class="whitespace-nowrap flex-grow text-left text-sm font-bold">{{ name }}</div>
6
+ <QBadge
7
+ :label="count"
8
+ rounded
9
+ class="ml-2 transition-all"
10
+ :class="{'bg-gray-base': !count, 'bg-blue-base': count}"
11
+ />
12
+ </div>
13
+ </template>
14
+ <slot />
15
+ </QExpansionItem>
16
+ </template>
17
+ <script setup>
18
+ defineProps({
19
+ name: {
20
+ type: String,
21
+ required: true
22
+ },
23
+ count: {
24
+ type: Number,
25
+ required: true
26
+ }
27
+ });
28
+ </script>
@@ -0,0 +1,76 @@
1
+ <template>
2
+ <QList>
3
+ <div class="px-4 py-2 max-w-full">
4
+ <template
5
+ v-for="(group, index) in filterGroups"
6
+ :key="'group-' + group.name"
7
+ >
8
+ <template v-if="group.flat">
9
+ <FilterableField
10
+ v-for="field in group.fields"
11
+ :key="'field-' + field.name"
12
+ :model-value="field.calcValue ? field.calcValue(filter) : filter[field.name]"
13
+ :field="field"
14
+ :loading="loading"
15
+ class="mb-4"
16
+ @update:model-value="updateFilter(field, $event)"
17
+ />
18
+ </template>
19
+
20
+ <FilterGroupItem
21
+ v-else
22
+ :name="group.name"
23
+ :count="activeCountByGroup[group.name]"
24
+ >
25
+ <FilterableField
26
+ v-for="field in group.fields"
27
+ :key="'field-' + field.name"
28
+ :model-value="field.calcValue ? field.calcValue(filter) : filter[field.name]"
29
+ :field="field"
30
+ :loading="loading"
31
+ class="mb-4"
32
+ @update:model-value="updateFilter(field, $event)"
33
+ />
34
+ </FilterGroupItem>
35
+
36
+ <QSeparator
37
+ v-if="index < (filterGroups.length - 1)"
38
+ class="my-2"
39
+ />
40
+ </template>
41
+ </div>
42
+ </QList>
43
+ </template>
44
+ <script setup>
45
+ import FilterableField from "danx/src/components/ActionTable/Filters/FilterableField";
46
+ import FilterGroupItem from "danx/src/components/ActionTable/Filters/FilterGroupItem";
47
+ import { computed } from "vue";
48
+
49
+ const emit = defineEmits(["update:filter"]);
50
+ const props = defineProps({
51
+ filterGroups: {
52
+ type: Array,
53
+ required: true
54
+ },
55
+ filter: {
56
+ type: Object,
57
+ required: true
58
+ },
59
+ loading: Boolean
60
+ });
61
+
62
+ const activeCountByGroup = computed(() => {
63
+ const activeCountByGroup = {};
64
+ for (const group of props.filterGroups) {
65
+ activeCountByGroup[group.name] = group.fields.filter(field => props.filter[field.name] !== undefined).length;
66
+ }
67
+ return activeCountByGroup;
68
+ });
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 });
75
+ }
76
+ </script>
@@ -0,0 +1,50 @@
1
+ <template>
2
+ <div class="flex items-center" :class="{'w-72': showFilters}">
3
+ <div class="flex-grow">
4
+ <QBtn
5
+ class="btn-blue-highlight"
6
+ :class="{'highlighted': showFilters}"
7
+ @click="$emit('update:show-filters', !showFilters)"
8
+ >
9
+ <FilterIcon class="w-5 mr-2" />
10
+ <QBadge
11
+ :label="'' + activeCount"
12
+ rounded
13
+ :color="activeCount > 0 ? 'blue-base' : 'gray-base'"
14
+ />
15
+ </QBtn>
16
+ </div>
17
+ <a
18
+ v-if="activeCount > 0"
19
+ class="text-blue-base hover:text-blue-plus-1 text-sm ml-4"
20
+ @click="$emit('update:filter', {})"
21
+ >Clear All</a>
22
+ </div>
23
+ </template>
24
+ <script setup>
25
+ import { FilterIcon } from "@/svg";
26
+ import { computed } from "vue";
27
+
28
+ defineEmits(["update:show-filters", "update:filter"]);
29
+ const props = defineProps({
30
+ filter: {
31
+ type: Object,
32
+ required: true
33
+ },
34
+ filterGroups: {
35
+ type: Array,
36
+ required: true
37
+ },
38
+ showFilters: Boolean
39
+ });
40
+
41
+ const filterNameDictionary = computed(() => {
42
+ return props.filterGroups.reduce((acc, fg) => {
43
+ fg.fields.forEach(f => {
44
+ acc[f.name] = true;
45
+ });
46
+ return acc;
47
+ }, {});
48
+ });
49
+ const activeCount = computed(() => Object.keys(props.filter).filter(key => props.filter[key] !== undefined && filterNameDictionary.value[key]).length);
50
+ </script>
@@ -0,0 +1,141 @@
1
+ <template>
2
+ <div>
3
+ <template v-if="field.type === 'multi-select'">
4
+ <SelectField
5
+ v-if="field.options?.length > 0 || loading"
6
+ :model-value="modelValue"
7
+ :options="field.options"
8
+ multiple
9
+ :loading="loading"
10
+ :chip-limit="1"
11
+ filterable
12
+ :placeholder="field.placeholder"
13
+ :label="field.label"
14
+ @update:model-value="onUpdate"
15
+ />
16
+ <div
17
+ v-else
18
+ class="mt-2"
19
+ >
20
+ <div class="text-xs font-bold">{{ field.label }}</div>
21
+ <div class="text-sm ml-3 py-2">No Available Options</div>
22
+ </div>
23
+ </template>
24
+
25
+ <SelectField
26
+ v-else-if="field.type === 'single-select'"
27
+ :model-value="modelValue"
28
+ :options="field.options"
29
+ :placeholder="field.placeholder"
30
+ :loading="loading"
31
+ :label="field.label"
32
+ @update:model-value="onUpdate"
33
+ />
34
+ <DateField
35
+ v-else-if="field.type === 'date'"
36
+ :model-value="modelValue"
37
+ :label="field.label"
38
+ class="mt-2"
39
+ @update:model-value="onUpdate"
40
+ />
41
+ <DateRangeField
42
+ v-else-if="field.type === 'date-range'"
43
+ :model-value="modelValue"
44
+ :label="field.label"
45
+ :inline="field.inline"
46
+ with-time
47
+ class="mt-2 reactive"
48
+ @update:model-value="onUpdate"
49
+ />
50
+ <NumberRangeField
51
+ v-else-if="field.type === 'number-range'"
52
+ :model-value="modelValue"
53
+ :label="field.label"
54
+ class="mt-2"
55
+ :debounce="1000"
56
+ @update:model-value="onUpdate"
57
+ />
58
+ <NumberRangeField
59
+ v-else-if="field.type === 'currency-range'"
60
+ :model-value="modelValue"
61
+ :label="field.label"
62
+ class="mt-2"
63
+ :debounce="1000"
64
+ currency
65
+ @update:model-value="onUpdate"
66
+ />
67
+ <NumberRangeField
68
+ v-else-if="field.type === 'percent-range'"
69
+ :model-value="modelValue"
70
+ :label="field.label"
71
+ class="mt-2"
72
+ :debounce="1000"
73
+ percent
74
+ @update:model-value="onUpdate"
75
+ />
76
+ <BooleanField
77
+ v-else-if="field.type === 'boolean'"
78
+ :field="field"
79
+ :model-value="modelValue"
80
+ class="mt-2"
81
+ label-class="text-xs font-bold"
82
+ @update:model-value="onUpdate"
83
+ />
84
+ <MultiKeywordField
85
+ v-else-if="field.type === 'multi-keywords'"
86
+ :model-value="modelValue"
87
+ :field="field"
88
+ @update:model-value="onUpdate"
89
+ />
90
+ <SelectWithChildrenField
91
+ v-else-if="field.type === 'select-with-children'"
92
+ :model-value="modelValue"
93
+ :options="field.options"
94
+ :loading="loading"
95
+ :label="field.label"
96
+ :placeholder="field.placeholder"
97
+ @update:model-value="onUpdate"
98
+ />
99
+ <template v-else>
100
+ Field &quot;{{ field.name }}&quot;: Unknown filter type {{ field.type }}
101
+ </template>
102
+ </div>
103
+ </template>
104
+ <script setup>
105
+ import BooleanField from "danx/src/components/ActionTable/Form/Fields/BooleanField";
106
+ import DateField from "danx/src/components/ActionTable/Form/Fields/DateField";
107
+ import DateRangeField from "danx/src/components/ActionTable/Form/Fields/DateRangeField";
108
+ import MultiKeywordField from "danx/src/components/ActionTable/Form/Fields/MultiKeywordField";
109
+ import NumberRangeField from "danx/src/components/ActionTable/Form/Fields/NumberRangeField";
110
+ import SelectField from "danx/src/components/ActionTable/Form/Fields/SelectField";
111
+ import SelectWithChildrenField from "danx/src/components/ActionTable/Form/Fields/SelectWithChildrenField";
112
+
113
+ const emit = defineEmits(["update:model-value"]);
114
+ const props = defineProps({
115
+ field: {
116
+ type: Object,
117
+ required: true
118
+ },
119
+ modelValue: {
120
+ type: [String, Array, Number, Object, Boolean],
121
+ default: undefined
122
+ },
123
+ loading: Boolean
124
+ });
125
+
126
+ function onUpdate(val) {
127
+ let newVal = val || undefined;
128
+
129
+ switch (props.field.type) {
130
+ case "multi-select":
131
+ newVal = val.length > 0 ? val : undefined;
132
+ break;
133
+ case "single-select":
134
+ case "boolean":
135
+ newVal = val === null ? undefined : val;
136
+ break;
137
+ }
138
+
139
+ emit("update:model-value", newVal);
140
+ }
141
+ </script>
@@ -0,0 +1,37 @@
1
+ <template>
2
+ <QToggle
3
+ :data-testid="'boolean-field-' + field.id"
4
+ :model-value="modelValue"
5
+ :toggle-indeterminate="toggleIndeterminate"
6
+ :indeterminate-value="undefined"
7
+ @update:model-value="$emit('update:model-value', $event)"
8
+ >
9
+ <FieldLabel
10
+ :field="field"
11
+ :show-name="showName"
12
+ :class="labelClass"
13
+ />
14
+ </QToggle>
15
+ </template>
16
+
17
+ <script setup>
18
+ import FieldLabel from "./FieldLabel";
19
+
20
+ defineEmits(["update:model-value"]);
21
+ defineProps({
22
+ modelValue: {
23
+ type: [Boolean],
24
+ default: undefined
25
+ },
26
+ field: {
27
+ type: Object,
28
+ required: true
29
+ },
30
+ labelClass: {
31
+ type: String,
32
+ default: "text-sm"
33
+ },
34
+ showName: Boolean,
35
+ toggleIndeterminate: Boolean
36
+ });
37
+ </script>
@@ -0,0 +1,46 @@
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_confirmation"
18
+ },
19
+ label: {
20
+ type: String,
21
+ default: "Confirm Password"
22
+ },
23
+ placeholder: {
24
+ type: String,
25
+ default: "Confirm Password"
26
+ },
27
+ modelValue: {
28
+ type: [String, Number],
29
+ required: true
30
+ },
31
+ error: {
32
+ type: String,
33
+ default: null
34
+ },
35
+ password: {
36
+ type: String,
37
+ default: ""
38
+ },
39
+ disabled: Boolean
40
+ });
41
+
42
+ const rules = [
43
+ (val) =>
44
+ val === props.password || "The password and confirmation do not match"
45
+ ];
46
+ </script>