quasar-ui-danx 0.4.32 → 0.4.35
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/dist/danx.es.js +2483 -2432
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +51 -51
- package/dist/danx.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/ActionTable/Form/Fields/EditableDiv.vue +15 -11
- package/src/components/ActionTable/Form/Fields/SelectField.vue +4 -1
- package/src/components/ActionTable/Form/Fields/SelectOrCreateField.vue +16 -11
- package/src/components/ActionTable/controls.ts +1 -1
- package/src/components/DragAndDrop/ListItemDraggable.vue +19 -7
- package/src/helpers/actions.ts +25 -21
- package/src/helpers/formats.ts +83 -34
- package/src/types/controls.d.ts +5 -0
package/package.json
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
<template>
|
2
2
|
<div class="inline-block relative">
|
3
3
|
<div
|
4
|
-
contenteditable
|
5
|
-
class="relative inline-block transition duration-300 outline-none outline-offset-0 border-none
|
4
|
+
:contenteditable="readonly ? 'false' : 'true'"
|
5
|
+
class="relative inline-block transition duration-300 outline-none outline-offset-0 border-none rounded-sm z-10 min-w-10 min-h-10"
|
6
6
|
:style="{minWidth, minHeight}"
|
7
7
|
:class="contentClass"
|
8
8
|
@input="onInput"
|
@@ -12,7 +12,7 @@
|
|
12
12
|
{{ text }}
|
13
13
|
</div>
|
14
14
|
<div
|
15
|
-
v-if="!text && placeholder && !hasFocus"
|
15
|
+
v-if="!text && placeholder && !hasFocus && !readonly"
|
16
16
|
ref="placeholderDiv"
|
17
17
|
class="text-gray-600 absolute-top-left whitespace-nowrap z-1 pointer-events-none"
|
18
18
|
>
|
@@ -31,6 +31,7 @@ const props = withDefaults(defineProps<{
|
|
31
31
|
color?: string;
|
32
32
|
textColor?: string;
|
33
33
|
debounceDelay?: number;
|
34
|
+
readonly?: boolean;
|
34
35
|
placeholder?: string;
|
35
36
|
}>(), {
|
36
37
|
modelValue: "",
|
@@ -43,16 +44,16 @@ const props = withDefaults(defineProps<{
|
|
43
44
|
});
|
44
45
|
|
45
46
|
const text = ref(props.modelValue);
|
46
|
-
const placeholderDiv = ref(null);
|
47
|
-
const minWidth = ref(0);
|
48
|
-
const minHeight = ref(0);
|
47
|
+
const placeholderDiv = ref<Element | null>(null);
|
48
|
+
const minWidth = ref<string>("0");
|
49
|
+
const minHeight = ref<string>("0");
|
49
50
|
const hasFocus = ref(false);
|
50
51
|
|
51
52
|
onMounted(() => {
|
52
53
|
// Set the min-width to the width of the placeholder
|
53
54
|
if (placeholderDiv.value) {
|
54
|
-
minWidth.value = placeholderDiv.value
|
55
|
-
minHeight.value = placeholderDiv.value
|
55
|
+
minWidth.value = placeholderDiv.value?.offsetWidth + "px";
|
56
|
+
minHeight.value = placeholderDiv.value?.offsetHeight + "px";
|
56
57
|
}
|
57
58
|
});
|
58
59
|
|
@@ -72,9 +73,12 @@ function onInput(e) {
|
|
72
73
|
}
|
73
74
|
|
74
75
|
const contentClass = computed(() => [
|
75
|
-
|
76
|
-
|
77
|
-
|
76
|
+
...(props.readonly ? [] : [
|
77
|
+
`hover:bg-${props.color} focus:bg-${props.color}`,
|
78
|
+
`hover:text-${props.textColor} focus:text-${props.textColor}`,
|
79
|
+
`hover:outline-${props.color} focus:outline-${props.color}`,
|
80
|
+
"focus:outline-4 hover:outline-4"
|
81
|
+
]),
|
78
82
|
text.value ? "" : "!bg-none"
|
79
83
|
]);
|
80
84
|
</script>
|
@@ -22,6 +22,7 @@
|
|
22
22
|
label=""
|
23
23
|
:input-class="{'is-hidden': !isShowing, [inputClass]: true}"
|
24
24
|
class="max-w-full dx-select-field"
|
25
|
+
:class="selectClass"
|
25
26
|
@filter="onFilter"
|
26
27
|
@clear="onClear"
|
27
28
|
@popup-show="onShow"
|
@@ -82,6 +83,7 @@ export interface Props extends QSelectProps {
|
|
82
83
|
selectionLabel?: string | ((option) => string);
|
83
84
|
chipLimit?: number;
|
84
85
|
inputClass?: string;
|
86
|
+
selectClass?: string;
|
85
87
|
selectionClass?: string;
|
86
88
|
options?: unknown[];
|
87
89
|
filterable?: boolean;
|
@@ -97,6 +99,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
97
99
|
selectionLabel: null,
|
98
100
|
chipLimit: 3,
|
99
101
|
inputClass: "",
|
102
|
+
selectClass: "",
|
100
103
|
selectionClass: "",
|
101
104
|
options: () => [],
|
102
105
|
filterFn: null,
|
@@ -151,7 +154,7 @@ const selectedValue = computed(() => {
|
|
151
154
|
} else {
|
152
155
|
if (props.modelValue === null) return "__null__";
|
153
156
|
|
154
|
-
if (props.selectByObject) return props.modelValue
|
157
|
+
if (props.selectByObject) return props.modelValue?.value || props.modelValue?.id;
|
155
158
|
|
156
159
|
return props.modelValue;
|
157
160
|
}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<template>
|
2
2
|
<div class="flex items-stretch flex-nowrap gap-x-4">
|
3
3
|
<QBtn
|
4
|
-
class="
|
4
|
+
:class="createClass"
|
5
5
|
:loading="loading"
|
6
6
|
@click="$emit('create')"
|
7
7
|
>
|
@@ -11,6 +11,14 @@
|
|
11
11
|
/>
|
12
12
|
{{ createText }}
|
13
13
|
</QBtn>
|
14
|
+
<ShowHideButton
|
15
|
+
v-if="showEdit"
|
16
|
+
v-model="editing"
|
17
|
+
:disable="!canEdit"
|
18
|
+
:label="editText"
|
19
|
+
:class="editClass"
|
20
|
+
:show-icon="EditIcon"
|
21
|
+
/>
|
14
22
|
<SelectField
|
15
23
|
v-model="selected"
|
16
24
|
class="flex-grow"
|
@@ -19,17 +27,10 @@
|
|
19
27
|
:select-by-object="selectByObject"
|
20
28
|
:option-label="optionLabel"
|
21
29
|
/>
|
22
|
-
<ShowHideButton
|
23
|
-
v-if="showEdit"
|
24
|
-
v-model="editing"
|
25
|
-
:disable="!canEdit"
|
26
|
-
:label="editText"
|
27
|
-
class="bg-sky-800 w-1/5"
|
28
|
-
/>
|
29
30
|
</div>
|
30
31
|
</template>
|
31
32
|
<script setup lang="ts">
|
32
|
-
import { FaSolidPlus as CreateIcon } from "danx-icon";
|
33
|
+
import { FaSolidPencil as EditIcon, FaSolidPlus as CreateIcon } from "danx-icon";
|
33
34
|
import { QSelectOption } from "quasar";
|
34
35
|
import { ActionTargetItem } from "../../../../types";
|
35
36
|
import { ShowHideButton } from "../../../Utility/Buttons";
|
@@ -47,10 +48,14 @@ withDefaults(defineProps<{
|
|
47
48
|
optionLabel?: string;
|
48
49
|
createText?: string;
|
49
50
|
editText?: string;
|
51
|
+
createClass?: string;
|
52
|
+
editClass?: string;
|
50
53
|
clearable?: boolean;
|
51
54
|
}>(), {
|
52
55
|
optionLabel: "label",
|
53
|
-
createText: "
|
54
|
-
editText: "
|
56
|
+
createText: "",
|
57
|
+
editText: "",
|
58
|
+
createClass: "bg-green-900 px-4",
|
59
|
+
editClass: "bg-sky-800 px-4"
|
55
60
|
});
|
56
61
|
</script>
|
@@ -73,7 +73,7 @@ export function useControls(name: string, options: ListControlsOptions): ListCon
|
|
73
73
|
|
74
74
|
async function loadList() {
|
75
75
|
if (!isInitialized || options.isListEnabled === false) return;
|
76
|
-
|
76
|
+
isLoadingList.value = true;
|
77
77
|
try {
|
78
78
|
setPagedItems(await options.routes.list(pager.value));
|
79
79
|
} catch (e) {
|
@@ -1,19 +1,23 @@
|
|
1
1
|
<template>
|
2
2
|
<div
|
3
|
-
:class="{'cursor-move': !showHandle}"
|
4
|
-
draggable="true"
|
5
|
-
@dragstart.stop="dragAndDrop.dragStart"
|
6
|
-
@dragend="dragAndDrop.dragEnd"
|
3
|
+
:class="{'cursor-move': !showHandle && !disabled}"
|
4
|
+
:draggable="disabled ? undefined : 'true'"
|
5
|
+
@dragstart.stop="disabled ? null : dragAndDrop.dragStart"
|
6
|
+
@dragend="disabled ? null : dragAndDrop.dragEnd"
|
7
7
|
>
|
8
8
|
<div :class="contentClass">
|
9
9
|
<div
|
10
10
|
v-if="showHandle"
|
11
|
-
class="
|
12
|
-
:class="handleClass"
|
11
|
+
:class="resolvedHandleClass"
|
13
12
|
>
|
13
|
+
<div
|
14
|
+
v-if="disabled"
|
15
|
+
:class="handleSize"
|
16
|
+
/>
|
14
17
|
<SvgImg
|
18
|
+
v-else
|
15
19
|
:svg="DragHandleIcon"
|
16
|
-
class="
|
20
|
+
:class="handleSize"
|
17
21
|
alt="drag-handle"
|
18
22
|
/>
|
19
23
|
</div>
|
@@ -24,6 +28,7 @@
|
|
24
28
|
</div>
|
25
29
|
</template>
|
26
30
|
<script setup lang="ts">
|
31
|
+
import { computed } from "vue";
|
27
32
|
import { DragHandleDotsIcon as DragHandleIcon } from "../../svg";
|
28
33
|
import { SvgImg } from "../Utility";
|
29
34
|
import { ListDragAndDrop } from "./listDragAndDrop";
|
@@ -37,14 +42,21 @@ const props = withDefaults(defineProps<{
|
|
37
42
|
changeDropZone?: boolean;
|
38
43
|
contentClass?: string | object;
|
39
44
|
handleClass?: string | object;
|
45
|
+
handleSize?: string;
|
40
46
|
listItems?: any[];
|
47
|
+
disabled?: boolean;
|
41
48
|
}>(), {
|
42
49
|
direction: "vertical",
|
50
|
+
handleSize: "w-4 h-4",
|
43
51
|
handleClass: "",
|
44
52
|
contentClass: "flex flex-nowrap items-center",
|
45
53
|
listItems: () => []
|
46
54
|
});
|
47
55
|
|
56
|
+
const resolvedHandleClass = computed(() => ({
|
57
|
+
"cursor-move": !props.disabled,
|
58
|
+
...(typeof props.handleClass === "string" ? { [props.handleClass]: true } : props.handleClass)
|
59
|
+
}));
|
48
60
|
const dragAndDrop = new ListDragAndDrop()
|
49
61
|
.setDropZone(props.dropZone)
|
50
62
|
.setOptions({ showPlaceholder: true, allowDropZoneChange: props.changeDropZone, direction: props.direction })
|
package/src/helpers/actions.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import { useDebounceFn } from "@vueuse/core";
|
2
2
|
import { FaSolidCopy as CopyIcon, FaSolidPencil as EditIcon, FaSolidTrash as DeleteIcon } from "danx-icon";
|
3
3
|
import { uid } from "quasar";
|
4
|
-
import { h, isReactive, Ref, shallowRef } from "vue";
|
4
|
+
import { h, isReactive, Ref, shallowReactive, shallowRef } from "vue";
|
5
5
|
import { ConfirmActionDialog, CreateNewWithNameDialog } from "../components";
|
6
6
|
import type { ActionGlobalOptions, ActionOptions, ActionTarget, ListController, ResourceAction } from "../types";
|
7
7
|
import { FlashMessages } from "./FlashMessages";
|
@@ -48,35 +48,39 @@ export function useActions(actions: ActionOptions[], globalOptions: ActionGlobal
|
|
48
48
|
*/
|
49
49
|
function getAction(actionName: string, actionOptions?: Partial<ActionOptions>): ResourceAction {
|
50
50
|
/// Resolve the action options or resource action based on the provided input
|
51
|
-
|
51
|
+
let resourceAction: Partial<ResourceAction> = actions.find(a => a.name === actionName) || { name: actionName };
|
52
52
|
|
53
53
|
if (actionOptions) {
|
54
|
-
Object.assign(
|
54
|
+
Object.assign(resourceAction, actionOptions);
|
55
55
|
}
|
56
56
|
|
57
57
|
// If the action is already reactive, return it
|
58
|
-
if (isReactive(
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
__type: "__Action:" + namespace
|
69
|
-
});
|
58
|
+
if (!isReactive(resourceAction) || !("__type" in resourceAction)) {
|
59
|
+
resourceAction = storeObject({
|
60
|
+
onAction: globalOptions?.routes?.applyAction,
|
61
|
+
onBatchAction: globalOptions?.routes?.batchAction,
|
62
|
+
onBatchSuccess: globalOptions?.controls?.clearSelectedRows,
|
63
|
+
...globalOptions,
|
64
|
+
...resourceAction,
|
65
|
+
isApplying: false,
|
66
|
+
__type: "__Action:" + namespace
|
67
|
+
});
|
70
68
|
|
71
|
-
|
72
|
-
|
73
|
-
resourceAction.trigger = useDebounceFn((target, input) => performAction(resourceAction, target, input), baseOptions.debounce);
|
69
|
+
// Splice the resourceAction in place of the action in the actions list
|
70
|
+
actions.splice(actions.findIndex(a => a.name === actionName), 1, resourceAction as ResourceAction);
|
74
71
|
}
|
75
72
|
|
76
|
-
//
|
77
|
-
|
73
|
+
// Return a clone of the action so it can be modified without affecting the original
|
74
|
+
const clonedAction = shallowReactive({ ...resourceAction }) as ResourceAction;
|
75
|
+
|
76
|
+
// Assign Trigger function if it doesn't exist
|
77
|
+
if (clonedAction.debounce) {
|
78
|
+
clonedAction.trigger = useDebounceFn((target, input) => performAction(clonedAction, target, input), clonedAction.debounce);
|
79
|
+
} else {
|
80
|
+
clonedAction.trigger = (target, input) => performAction(clonedAction, target, input);
|
81
|
+
}
|
78
82
|
|
79
|
-
return
|
83
|
+
return clonedAction;
|
80
84
|
}
|
81
85
|
|
82
86
|
/**
|
package/src/helpers/formats.ts
CHANGED
@@ -29,40 +29,6 @@ export function remoteDateTime(dateTimeString: string) {
|
|
29
29
|
return DateTime.fromSQL(dateTimeString, { zone: "local" }).setZone(SERVER_TZ);
|
30
30
|
}
|
31
31
|
|
32
|
-
/**
|
33
|
-
* Parses a date string into a Luxon DateTime object
|
34
|
-
*/
|
35
|
-
export function parseDateTime(dateTime: string | DateTime | null): DateTime<boolean> | null {
|
36
|
-
if (typeof dateTime === "string") {
|
37
|
-
return parseSqlDateTime(dateTime) || parseQDate(dateTime) || parseQDateTime(dateTime);
|
38
|
-
}
|
39
|
-
return dateTime || DateTime.fromSQL("0000-00-00 00:00:00");
|
40
|
-
}
|
41
|
-
|
42
|
-
/**
|
43
|
-
* Parses a SQL formatted date string into a Luxon DateTime object
|
44
|
-
*/
|
45
|
-
export function parseSqlDateTime(dateTime: string) {
|
46
|
-
const parsed = DateTime.fromSQL(dateTime.replace("T", " ").replace(/\//g, "-"));
|
47
|
-
return parsed.isValid ? parsed : null;
|
48
|
-
}
|
49
|
-
|
50
|
-
/**
|
51
|
-
* Parses a Quasar formatted date string into a Luxon DateTime object
|
52
|
-
*/
|
53
|
-
export function parseQDate(date: string, format = "yyyy/MM/dd"): DateTime<boolean> | null {
|
54
|
-
const parsed = DateTime.fromFormat(date, format);
|
55
|
-
return parsed.isValid ? parsed : null;
|
56
|
-
}
|
57
|
-
|
58
|
-
/**
|
59
|
-
* Parses a Quasar formatted date/time string into a Luxon DateTime object
|
60
|
-
*/
|
61
|
-
export function parseQDateTime(date: string, format = "yyyy/MM/dd HH:mm:ss"): DateTime<boolean> | null {
|
62
|
-
const parsed = DateTime.fromFormat(date, format);
|
63
|
-
return parsed.isValid ? parsed : null;
|
64
|
-
}
|
65
|
-
|
66
32
|
/**
|
67
33
|
* Formats a Luxon DateTime object into a Quasar formatted date string
|
68
34
|
* @param date
|
@@ -119,6 +85,89 @@ export function fDate(dateTime: string | DateTime | null, { empty = "--", format
|
|
119
85
|
return formatted || empty;
|
120
86
|
}
|
121
87
|
|
88
|
+
|
89
|
+
/**
|
90
|
+
* Parses a date string into a Luxon DateTime object
|
91
|
+
*/
|
92
|
+
export function parseDateTime(dateTime: string | DateTime | null): DateTime<boolean> | null {
|
93
|
+
if (typeof dateTime === "string") {
|
94
|
+
return parseGenericDateTime(dateTime);
|
95
|
+
}
|
96
|
+
return dateTime || DateTime.fromSQL("0000-00-00 00:00:00");
|
97
|
+
}
|
98
|
+
|
99
|
+
/**
|
100
|
+
* Parses a SQL formatted date string into a Luxon DateTime object
|
101
|
+
*/
|
102
|
+
export function parseSqlDateTime(dateTime: string) {
|
103
|
+
const parsed = DateTime.fromSQL(dateTime.replace("T", " ").replace(/\//g, "-"));
|
104
|
+
return parsed.isValid ? parsed : null;
|
105
|
+
}
|
106
|
+
|
107
|
+
/**
|
108
|
+
* Parses a Quasar formatted date string into a Luxon DateTime object
|
109
|
+
*/
|
110
|
+
export function parseQDate(date: string, format = "yyyy/MM/dd"): DateTime<boolean> | null {
|
111
|
+
const parsed = DateTime.fromFormat(date, format);
|
112
|
+
return parsed.isValid ? parsed : null;
|
113
|
+
}
|
114
|
+
|
115
|
+
/**
|
116
|
+
* Parses a Quasar formatted date/time string into a Luxon DateTime object
|
117
|
+
*/
|
118
|
+
export function parseQDateTime(date: string, format = "yyyy/MM/dd HH:mm:ss"): DateTime<boolean> | null {
|
119
|
+
const parsed = DateTime.fromFormat(date, format);
|
120
|
+
return parsed.isValid ? parsed : null;
|
121
|
+
}
|
122
|
+
|
123
|
+
/**
|
124
|
+
* Parses a date string in various formats into a Luxon DateTime object.
|
125
|
+
* Tries a list of common formats until one works.
|
126
|
+
*
|
127
|
+
* @param {string} dateTimeString - The date string to parse.
|
128
|
+
* @param {string} [defaultZone="local"] - The default time zone to use if not specified.
|
129
|
+
* @returns {DateTime | null} - A Luxon DateTime object if parsing succeeds, otherwise null.
|
130
|
+
*/
|
131
|
+
export function parseGenericDateTime(dateTimeString: string, defaultZone = "local"): DateTime | null {
|
132
|
+
if (!dateTimeString) return null;
|
133
|
+
|
134
|
+
const formats = [
|
135
|
+
"yyyy-MM-dd", // ISO date
|
136
|
+
"yyyy-MM-dd HH:mm:ss", // ISO date with time
|
137
|
+
"MM/dd/yyyy", // US-style date
|
138
|
+
"dd/MM/yyyy", // European-style date
|
139
|
+
"MM/dd/yy", // US short date
|
140
|
+
"dd/MM/yy", // European short date
|
141
|
+
"yyyy/MM/dd", // Alternative ISO
|
142
|
+
"MM-dd-yyyy", // US with dashes
|
143
|
+
"dd-MM-yyyy", // European with dashes
|
144
|
+
"M/d/yyyy", // US date without leading zeros
|
145
|
+
"d/M/yyyy", // European date without leading zeros
|
146
|
+
"yyyyMMdd" // Compact ISO
|
147
|
+
];
|
148
|
+
|
149
|
+
for (const format of formats) {
|
150
|
+
const parsed = DateTime.fromFormat(dateTimeString, format, { zone: defaultZone });
|
151
|
+
if (parsed.isValid) {
|
152
|
+
return parsed;
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
// Fallback to ISO parsing for strings like "2022-11-18T10:10:10Z"
|
157
|
+
const isoParsed = DateTime.fromISO(dateTimeString, { zone: defaultZone });
|
158
|
+
if (isoParsed.isValid) {
|
159
|
+
return isoParsed;
|
160
|
+
}
|
161
|
+
|
162
|
+
// Fallback to SQL parsing for strings like "2022-11-18 10:10:10"
|
163
|
+
const sqlParsed = DateTime.fromSQL(dateTimeString, { zone: defaultZone });
|
164
|
+
if (sqlParsed.isValid) {
|
165
|
+
return sqlParsed;
|
166
|
+
}
|
167
|
+
|
168
|
+
return null;
|
169
|
+
}
|
170
|
+
|
122
171
|
/**
|
123
172
|
* Formats a number of seconds into Hours / Minutes / Seconds or just Minutes and Seconds
|
124
173
|
*
|
package/src/types/controls.d.ts
CHANGED
@@ -62,6 +62,11 @@ export interface ListControlsPagination {
|
|
62
62
|
rowsPerPage?: number;
|
63
63
|
perPage?: number;
|
64
64
|
filter?: ListControlsFilter;
|
65
|
+
fields?: ControlsFieldsList;
|
66
|
+
}
|
67
|
+
|
68
|
+
export interface ControlsFieldsList {
|
69
|
+
[key: string]: boolean | ControlsFieldsList;
|
65
70
|
}
|
66
71
|
|
67
72
|
export interface PagedItems<T = ActionTargetItem> {
|