v-uixy 1.9.1 → 1.9.3
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/package.json +1 -1
- package/templates/components/Chart/Chart.component.vue +895 -222
- package/templates/components/Chart/Chart.types.ts +7 -0
- package/templates/components/Modal/Modal.component.vue +8 -1
- package/templates/components/Modal/Modal.styles.ts +1 -1
- package/templates/components/PieChart/PieChart.component.vue +515 -152
- package/templates/components/PieChart/PieChart.types.ts +7 -1
- package/templates/components/Select/Select.component.vue +58 -32
- package/templates/components/Select/Select.styles.ts +37 -12
- package/templates/components/Select/Select.types.ts +4 -0
- package/templates/components/Select/SelectTreeItem.vue +11 -3
- package/templates/components/Select/composables/useSelect.composable.ts +244 -122
- package/templates/components/Select/index.ts +2 -0
- package/templates/components/Sheet/Sheet.component.vue +4 -2
- package/templates/components/index.ts +8 -1
- package/templates/components.json +4 -4
- package/templates/composables/useGpuAcceleration.ts +79 -0
- package/templates/composables/useOverlayLayer.ts +86 -31
- package/templates/composables/usePosition.ts +1 -1
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
export type UixyPieChartData = {
|
|
2
2
|
label: string;
|
|
3
3
|
value: number;
|
|
4
|
-
color
|
|
4
|
+
color?: string;
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
export interface UixyPieChartProps {
|
|
8
8
|
data: UixyPieChartData[];
|
|
9
|
+
resolution?: number;
|
|
9
10
|
thickness?: number;
|
|
11
|
+
total?: string;
|
|
12
|
+
legend?: boolean;
|
|
13
|
+
animate?: boolean;
|
|
14
|
+
interactive?: boolean;
|
|
15
|
+
format?: (value: number) => string;
|
|
10
16
|
}
|
|
@@ -10,37 +10,49 @@
|
|
|
10
10
|
@keydown="handleKeydownSelect"
|
|
11
11
|
v-bind="filteredAttrs"
|
|
12
12
|
>
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
{{ props.placeholder }}
|
|
18
|
-
</p>
|
|
19
|
-
<div
|
|
20
|
-
v-if="props.multiple"
|
|
21
|
-
:class="badgesWrapperStyles({ disabled: props.disabled })"
|
|
22
|
-
>
|
|
23
|
-
<uixy-badge
|
|
24
|
-
v-for="(item, index) in model as string[]"
|
|
25
|
-
data-badge
|
|
26
|
-
variant="tertiary"
|
|
27
|
-
size="xs"
|
|
28
|
-
shape="pill"
|
|
29
|
-
:key="`${item}--${index}`"
|
|
30
|
-
@click.stop="handleClickSelectedOption(item)"
|
|
13
|
+
<div :class="valueWrapperStyles()">
|
|
14
|
+
<p
|
|
15
|
+
v-if="props.placeholder && (!model || model.length === 0)"
|
|
16
|
+
class="pointer-events-none select-none truncate text-gray-500 dark:text-gray-700"
|
|
31
17
|
>
|
|
32
|
-
{{
|
|
33
|
-
</
|
|
18
|
+
{{ props.placeholder }}
|
|
19
|
+
</p>
|
|
20
|
+
<div
|
|
21
|
+
v-if="props.multiple"
|
|
22
|
+
:class="badgesWrapperStyles({ disabled: props.disabled })"
|
|
23
|
+
>
|
|
24
|
+
<uixy-badge
|
|
25
|
+
v-for="(item, index) in model as string[]"
|
|
26
|
+
data-badge
|
|
27
|
+
variant="tertiary"
|
|
28
|
+
size="xs"
|
|
29
|
+
shape="pill"
|
|
30
|
+
class="max-w-full"
|
|
31
|
+
:key="`${item}--${index}`"
|
|
32
|
+
@click.stop="handleClickSelectedOption(item)"
|
|
33
|
+
>
|
|
34
|
+
<span class="min-w-0 truncate">{{ getLabel(item) }}</span>
|
|
35
|
+
</uixy-badge>
|
|
36
|
+
</div>
|
|
37
|
+
<p v-else class="truncate">{{ getLabel(model as string) }}</p>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<div :class="trailingStyles()">
|
|
41
|
+
<span
|
|
42
|
+
v-if="hasMax"
|
|
43
|
+
:class="counterStyles({ full: isAtMax, size })"
|
|
44
|
+
aria-live="polite"
|
|
45
|
+
>
|
|
46
|
+
{{ selectedValues.length }}/{{ props.max }}
|
|
47
|
+
</span>
|
|
48
|
+
<uixy-icon
|
|
49
|
+
v-if="!props.multiple && model && minSelected === 0"
|
|
50
|
+
name="close"
|
|
51
|
+
:class="deselectIconStyles({ size })"
|
|
52
|
+
@click.stop="handleDeselect"
|
|
53
|
+
/>
|
|
54
|
+
<uixy-icon name="chevron-down" :class="iconStyles({ open, size })" />
|
|
34
55
|
</div>
|
|
35
|
-
<p v-else>{{ getLabel(model as string) }}</p>
|
|
36
|
-
|
|
37
|
-
<uixy-icon
|
|
38
|
-
v-if="props.deselectable && !props.multiple && model"
|
|
39
|
-
name="close"
|
|
40
|
-
:class="deselectIconStyles({ size })"
|
|
41
|
-
@click.stop="handleDeselect"
|
|
42
|
-
/>
|
|
43
|
-
<uixy-icon name="chevron-down" :class="iconStyles({ open, size })" />
|
|
44
56
|
</div>
|
|
45
57
|
|
|
46
58
|
<teleport to="body">
|
|
@@ -78,6 +90,7 @@
|
|
|
78
90
|
:depth="0"
|
|
79
91
|
:is-selected="isSelected"
|
|
80
92
|
:is-partially-selected="isPartiallySelected"
|
|
93
|
+
:is-blocked="isBlocked"
|
|
81
94
|
:expanded-items="effectiveExpandedItems"
|
|
82
95
|
:leaf-only="props.leafOnly"
|
|
83
96
|
:multiple="props.multiple"
|
|
@@ -105,12 +118,14 @@
|
|
|
105
118
|
<ul v-else class="flex flex-col gap-1 py-1">
|
|
106
119
|
<li
|
|
107
120
|
v-for="option in filteredOptions"
|
|
108
|
-
:tabIndex="option.disabled ? -1 : 0"
|
|
121
|
+
:tabIndex="option.disabled || isBlocked(option) ? -1 : 0"
|
|
122
|
+
:aria-disabled="isBlocked(option) || undefined"
|
|
109
123
|
:key="option.value ?? option.label"
|
|
110
124
|
:class="
|
|
111
125
|
itemStyles({
|
|
112
126
|
selected: isSelected(option),
|
|
113
127
|
disabled: !!option.disabled,
|
|
128
|
+
blocked: isBlocked(option),
|
|
114
129
|
size,
|
|
115
130
|
})
|
|
116
131
|
"
|
|
@@ -197,6 +212,9 @@
|
|
|
197
212
|
dropdownPanelStyles,
|
|
198
213
|
listItemPaddingStyles,
|
|
199
214
|
checkIconStyles,
|
|
215
|
+
valueWrapperStyles,
|
|
216
|
+
trailingStyles,
|
|
217
|
+
counterStyles,
|
|
200
218
|
} from "./Select.styles";
|
|
201
219
|
import type { UixySelectEmits, UixySelectProps } from "./Select.types";
|
|
202
220
|
import { UixyBadge } from "../Badge";
|
|
@@ -207,7 +225,10 @@
|
|
|
207
225
|
import { useOverlayLayer } from "~/composables";
|
|
208
226
|
import SelectTreeItem from "./SelectTreeItem.vue";
|
|
209
227
|
|
|
210
|
-
const props = defineProps<UixySelectProps>()
|
|
228
|
+
const props = withDefaults(defineProps<UixySelectProps>(), {
|
|
229
|
+
deselectable: true,
|
|
230
|
+
maxBehavior: "block",
|
|
231
|
+
});
|
|
211
232
|
|
|
212
233
|
const emits = defineEmits<UixySelectEmits>();
|
|
213
234
|
|
|
@@ -249,8 +270,13 @@
|
|
|
249
270
|
filteredOptions,
|
|
250
271
|
effectiveExpandedItems,
|
|
251
272
|
customAddVisible,
|
|
273
|
+
selectedValues,
|
|
274
|
+
hasMax,
|
|
275
|
+
isAtMax,
|
|
276
|
+
minSelected,
|
|
252
277
|
isSelected,
|
|
253
278
|
isPartiallySelected,
|
|
279
|
+
isBlocked,
|
|
254
280
|
getLabel,
|
|
255
281
|
toggleExpand,
|
|
256
282
|
handleOpen,
|
|
@@ -340,7 +366,7 @@
|
|
|
340
366
|
|
|
341
367
|
if (!isOpen) return;
|
|
342
368
|
|
|
343
|
-
acquireLayer();
|
|
369
|
+
acquireLayer(refTrigger.value);
|
|
344
370
|
|
|
345
371
|
void updateDropdownPosition();
|
|
346
372
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import styles from "~/utils/styles";
|
|
2
2
|
|
|
3
3
|
export const selectStyles = styles(
|
|
4
|
-
"relative select-none peer transition-colors ease-in-out duration-150 border border-gray-300 dark:border-gray-900 rounded-1 placeholder:text-gray-500 dark:placeholder:text-gray-700 dark:outline-gray-800 outline-offset-1 outline-gray-300",
|
|
4
|
+
"relative flex items-center gap-2 select-none peer transition-colors ease-in-out duration-150 border border-gray-300 dark:border-gray-900 rounded-1 placeholder:text-gray-500 dark:placeholder:text-gray-700 dark:outline-gray-800 outline-offset-1 outline-gray-300",
|
|
5
5
|
{
|
|
6
6
|
status: {
|
|
7
7
|
default:
|
|
@@ -14,35 +14,56 @@ export const selectStyles = styles(
|
|
|
14
14
|
"bg-white dark:bg-gray-1000 focus-visible:border-gray-500 dark:focus-visible:border-gray-600 cursor-pointer",
|
|
15
15
|
},
|
|
16
16
|
size: {
|
|
17
|
-
sm: "pl-2.5 pr-5 py-1.5 text-xs min-h-[32px]",
|
|
18
|
-
md: "pl-3 pr-
|
|
19
|
-
xl: "pl-3.5 pr-
|
|
17
|
+
sm: "pl-2.5 pr-1.5 py-1.5 text-xs min-h-[32px]",
|
|
18
|
+
md: "pl-3 pr-2 py-2 text-sm min-h-[38px]",
|
|
19
|
+
xl: "pl-3.5 pr-2.5 py-2.5 text-base min-h-[42px]",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
export const valueWrapperStyles = styles("min-w-0 flex-1");
|
|
25
|
+
|
|
26
|
+
export const trailingStyles = styles(
|
|
27
|
+
"ml-auto flex shrink-0 items-center gap-1.5",
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
export const counterStyles = styles(
|
|
31
|
+
"pointer-events-none shrink-0 select-none tabular-nums transition-colors duration-150 ease-in-out",
|
|
32
|
+
{
|
|
33
|
+
full: {
|
|
34
|
+
true: "text-gray-900 dark:text-gray-300",
|
|
35
|
+
false: "text-gray-500 dark:text-gray-600",
|
|
36
|
+
},
|
|
37
|
+
size: {
|
|
38
|
+
sm: "text-[10px]",
|
|
39
|
+
md: "text-xs",
|
|
40
|
+
xl: "text-sm",
|
|
20
41
|
},
|
|
21
42
|
},
|
|
22
43
|
);
|
|
23
44
|
|
|
24
45
|
export const iconStyles = styles(
|
|
25
|
-
"pointer-events-none
|
|
46
|
+
"pointer-events-none shrink-0 select-none transition-transform duration-200 ease-in-out",
|
|
26
47
|
{
|
|
27
48
|
open: {
|
|
28
49
|
true: "rotate-180",
|
|
29
50
|
false: "",
|
|
30
51
|
},
|
|
31
52
|
size: {
|
|
32
|
-
sm: "h-3.5 w-3.5
|
|
33
|
-
md: "h-4 w-4
|
|
34
|
-
xl: "h-4 w-4
|
|
53
|
+
sm: "h-3.5 w-3.5",
|
|
54
|
+
md: "h-4 w-4",
|
|
55
|
+
xl: "h-4 w-4",
|
|
35
56
|
},
|
|
36
57
|
},
|
|
37
58
|
);
|
|
38
59
|
|
|
39
60
|
export const deselectIconStyles = styles(
|
|
40
|
-
"
|
|
61
|
+
"shrink-0 cursor-pointer text-gray-500 dark:text-gray-600 hover:text-black dark:hover:text-gray-400",
|
|
41
62
|
{
|
|
42
63
|
size: {
|
|
43
|
-
sm: "h-3.5 w-3.5
|
|
44
|
-
md: "h-4 w-4
|
|
45
|
-
xl: "h-4 w-4
|
|
64
|
+
sm: "h-3.5 w-3.5",
|
|
65
|
+
md: "h-4 w-4",
|
|
66
|
+
xl: "h-4 w-4",
|
|
46
67
|
},
|
|
47
68
|
},
|
|
48
69
|
);
|
|
@@ -71,6 +92,10 @@ export const itemStyles = styles(
|
|
|
71
92
|
true: "pointer-events-none text-gray-500 dark:text-gray-700",
|
|
72
93
|
false: "",
|
|
73
94
|
},
|
|
95
|
+
blocked: {
|
|
96
|
+
true: "cursor-not-allowed text-gray-400 dark:text-gray-700 hover:bg-transparent dark:hover:bg-transparent focus-visible:bg-transparent dark:focus-visible:bg-transparent",
|
|
97
|
+
false: "",
|
|
98
|
+
},
|
|
74
99
|
size: {
|
|
75
100
|
sm: "py-1.5 px-2.5",
|
|
76
101
|
md: "py-2 px-3",
|
|
@@ -20,6 +20,8 @@ export interface UixySelectEmits {
|
|
|
20
20
|
(event: "openChange", isOpen: boolean): void;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
export type UixySelectMaxBehavior = "block" | "replace";
|
|
24
|
+
|
|
23
25
|
export type UixySelectProps = {
|
|
24
26
|
options: UixySelectOptionType[];
|
|
25
27
|
status: "valid" | "error" | "default";
|
|
@@ -31,7 +33,9 @@ export type UixySelectProps = {
|
|
|
31
33
|
helperText?: string;
|
|
32
34
|
errorText?: string;
|
|
33
35
|
autoFocus?: boolean;
|
|
36
|
+
min?: number;
|
|
34
37
|
max?: number;
|
|
38
|
+
maxBehavior?: UixySelectMaxBehavior;
|
|
35
39
|
tree?: boolean;
|
|
36
40
|
leafOnly?: boolean;
|
|
37
41
|
autoOpen?: boolean;
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<li class="flex flex-col">
|
|
3
3
|
<div
|
|
4
|
-
:tabIndex="option.disabled ? -1 : 0"
|
|
4
|
+
:tabIndex="option.disabled || isOptionBlocked ? -1 : 0"
|
|
5
|
+
:aria-disabled="isOptionBlocked || undefined"
|
|
5
6
|
:class="
|
|
6
7
|
itemStyles({
|
|
7
8
|
selected: isSelected(option),
|
|
8
9
|
disabled: !!option.disabled,
|
|
10
|
+
blocked: isOptionBlocked,
|
|
9
11
|
size: props.size ?? 'md',
|
|
10
12
|
})
|
|
11
13
|
"
|
|
12
14
|
:style="{
|
|
13
15
|
paddingLeft: `${depth * 28 + 12}px`,
|
|
14
|
-
cursor: option.disabled ? 'not-allowed' : 'pointer',
|
|
16
|
+
cursor: option.disabled || isOptionBlocked ? 'not-allowed' : 'pointer',
|
|
15
17
|
}"
|
|
16
18
|
@click.stop="handleItemClick"
|
|
17
19
|
>
|
|
@@ -65,6 +67,7 @@
|
|
|
65
67
|
:depth="depth + 1"
|
|
66
68
|
:is-selected="isSelected"
|
|
67
69
|
:is-partially-selected="isPartiallySelected"
|
|
70
|
+
:is-blocked="isBlocked"
|
|
68
71
|
:expanded-items="expandedItems"
|
|
69
72
|
:leaf-only="leafOnly"
|
|
70
73
|
:multiple="multiple"
|
|
@@ -91,6 +94,7 @@
|
|
|
91
94
|
depth: number;
|
|
92
95
|
isSelected: (option: UixySelectOptionType) => boolean;
|
|
93
96
|
isPartiallySelected?: (option: UixySelectOptionType) => boolean;
|
|
97
|
+
isBlocked?: (option: UixySelectOptionType) => boolean;
|
|
94
98
|
expandedItems: string[];
|
|
95
99
|
leafOnly?: boolean;
|
|
96
100
|
multiple?: boolean;
|
|
@@ -139,6 +143,10 @@
|
|
|
139
143
|
props.expandedItems.includes(getValue(props.option)),
|
|
140
144
|
);
|
|
141
145
|
|
|
146
|
+
const isOptionBlocked = computed(
|
|
147
|
+
() => !!props.isBlocked?.(props.option),
|
|
148
|
+
);
|
|
149
|
+
|
|
142
150
|
const isNotSelectable = computed(
|
|
143
151
|
() =>
|
|
144
152
|
props.option.disabled ||
|
|
@@ -146,7 +154,7 @@
|
|
|
146
154
|
);
|
|
147
155
|
|
|
148
156
|
const handleItemClick = () => {
|
|
149
|
-
if (props.option.disabled) return;
|
|
157
|
+
if (props.option.disabled || isOptionBlocked.value) return;
|
|
150
158
|
|
|
151
159
|
if (props.leafOnly && hasChildrenInOriginal.value && !props.multiple) {
|
|
152
160
|
emit("toggle-expand", getValue(props.option));
|