@teammodlr/ui-kit 2.1.4 → 2.1.6
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/lib/call.d.ts +7 -0
- package/lib/components/confirm/Confirm.vue.d.ts +46 -0
- package/lib/components/{dialog/DialogProvider.vue.d.ts → confirm/ConfirmProvider.vue.d.ts} +5 -5
- package/lib/components/{dialog → confirm}/props.d.ts +2 -2
- package/lib/components/confirm/types.d.ts +27 -0
- package/lib/components/confirm/useConfirm.d.ts +2 -0
- package/lib/components/dialog/Dialog.vue.d.ts +90 -41
- package/lib/components/dialog/style/Dialog.css.d.ts +11 -0
- package/lib/components/dialog/style/index.d.ts +1 -0
- package/lib/components/dialog/types.d.ts +0 -27
- package/lib/components/index.d.ts +3 -1
- package/lib/components/select/Select.vue.d.ts +21 -1
- package/lib/components/select/style/Select.css.d.ts +6 -0
- package/lib/components/tree/Tree.vue.d.ts +103 -0
- package/lib/components/tree/TreeNode.vue.d.ts +18 -0
- package/lib/components/tree/props.d.ts +70 -0
- package/lib/components/tree/types.d.ts +53 -0
- package/lib/composables/index.d.ts +1 -1
- package/lib/composables/useMergedState.d.ts +2 -0
- package/lib/index.cjs.js +18 -2
- package/lib/index.es.js +4706 -2754
- package/lib/style.css +1 -1
- package/lib/styles/theme.css.d.ts +7 -1
- package/lib/utils/injection.d.ts +2 -0
- package/package.json +6 -4
- package/src/call.ts +34 -0
- package/src/components/confirm/Confirm.vue +60 -0
- package/src/components/{dialog/DialogProvider.vue → confirm/ConfirmProvider.vue} +18 -18
- package/src/components/confirm/examples/basic.vue +48 -0
- package/src/components/confirm/examples/index.stories.ts +29 -0
- package/src/components/confirm/examples/index.ts +1 -0
- package/src/components/{dialog → confirm}/props.ts +2 -2
- package/src/components/confirm/types.ts +34 -0
- package/src/components/confirm/useConfirm.ts +12 -0
- package/src/components/dialog/Dialog.vue +102 -44
- package/src/components/dialog/examples/basic.vue +8 -40
- package/src/components/dialog/examples/index.stories.ts +11 -22
- package/src/components/dialog/examples/index.ts +1 -1
- package/src/components/dialog/style/Dialog.css.ts +99 -0
- package/src/components/dialog/style/index.ts +1 -0
- package/src/components/dialog/types.ts +0 -34
- package/src/components/index.ts +3 -1
- package/src/components/select/Select.vue +100 -34
- package/src/components/select/examples/multiple.vue +1 -1
- package/src/components/select/style/Select.css.ts +91 -11
- package/src/components/tag/style/Tag.css.ts +0 -1
- package/src/components/tree/Tree.vue +126 -0
- package/src/components/tree/TreeNode.vue +71 -0
- package/src/components/tree/examples/basic.vue +33 -0
- package/src/components/tree/examples/index.stories.ts +16 -0
- package/src/components/tree/examples/index.ts +1 -0
- package/src/components/tree/props.ts +87 -0
- package/src/components/tree/types.ts +96 -0
- package/src/composables/index.ts +1 -1
- package/src/composables/useMergedState.ts +18 -0
- package/src/styles/theme.css.ts +3 -1
- package/src/utils/injection.ts +5 -0
- package/lib/components/dialog/useDialog.d.ts +0 -2
- package/lib/styles/typography.css.d.ts +0 -22
- package/src/components/dialog/useDialog.ts +0 -12
- package/src/styles/typography.css.ts +0 -91
- /package/lib/components/{dialog → confirm}/providerProps.d.ts +0 -0
- /package/src/components/{dialog → confirm}/examples/wrapper.vue +0 -0
- /package/src/components/{dialog → confirm}/providerProps.ts +0 -0
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<div :class="selectComputedClasses">
|
|
15
15
|
<template v-if="multiple && Array.isArray(selected) && selected.length > 0">
|
|
16
16
|
<MTag
|
|
17
|
-
v-for="value in
|
|
17
|
+
v-for="value in selectedMax"
|
|
18
18
|
:key="value"
|
|
19
19
|
:closeable="true"
|
|
20
20
|
@close="removeSelected(value)"
|
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
>
|
|
23
23
|
{{ getSelectedLabel(value) }}
|
|
24
24
|
</MTag>
|
|
25
|
+
<template v-if="selectedCountNotShown > 0">
|
|
26
|
+
<MTag :size="size">
|
|
27
|
+
+{{ selectedCountNotShown }} more
|
|
28
|
+
</MTag>
|
|
29
|
+
</template>
|
|
25
30
|
</template>
|
|
26
31
|
<template v-else-if="!multiple && selected">
|
|
27
32
|
{{ getSelectedLabel(selected) }}
|
|
@@ -40,9 +45,22 @@
|
|
|
40
45
|
>
|
|
41
46
|
<div :class="style.menu" v-if="isOpen" ref="popper" :style="computedConsistentWidth"
|
|
42
47
|
:aria-hidden="isOpen ? 'false' : 'true'">
|
|
43
|
-
<div
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
<div v-if="searchable || (multiple && selectAll)" :class="style.search">
|
|
49
|
+
<div v-if="multiple && selectAll" :class="style.checkAll">
|
|
50
|
+
<button
|
|
51
|
+
role="button"
|
|
52
|
+
type="button"
|
|
53
|
+
:class="{
|
|
54
|
+
[style.checkAllButton]: true,
|
|
55
|
+
[style.checkAllButtonActive]: computedOptions.length > 0 && computedOptions.length === selected.length
|
|
56
|
+
}"
|
|
57
|
+
@click.prevent="toggleAllItems"
|
|
58
|
+
aria-description="Select all items"
|
|
59
|
+
>
|
|
60
|
+
<svg v-if="computedOptions.length > 0 && computedOptions.length === selected.length" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path fill-rule="evenodd" d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z" clip-rule="evenodd"></path></svg>
|
|
61
|
+
</button>
|
|
62
|
+
</div>
|
|
63
|
+
<input v-if="searchable" type="text" ref="searchRef" v-model="search" placeholder="Search" :class="style.searchInput" />
|
|
46
64
|
</div>
|
|
47
65
|
<div :class="style.options">
|
|
48
66
|
<div v-for="(option, index) in computedOptions" :key="index" ref="optionsRef"
|
|
@@ -52,8 +70,12 @@
|
|
|
52
70
|
[style.optionFocused]: selectedIndex === index
|
|
53
71
|
}"
|
|
54
72
|
@click.prevent="option.onClick"
|
|
55
|
-
@hover="selectedIndex = index"
|
|
56
|
-
|
|
73
|
+
@hover="selectedIndex = index"
|
|
74
|
+
>
|
|
75
|
+
<span :class="style.optionIcon">
|
|
76
|
+
<svg v-if="option.active" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path fill-rule="evenodd" d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z" clip-rule="evenodd"></path></svg>
|
|
77
|
+
</span>
|
|
78
|
+
<span :class="style.optionLabel">{{ option.label }}</span>
|
|
57
79
|
</div>
|
|
58
80
|
<template v-if="computedOptions.length === 0">
|
|
59
81
|
<div :class="style.noResults">
|
|
@@ -86,7 +108,6 @@ import {
|
|
|
86
108
|
defineExpose,
|
|
87
109
|
type PropType
|
|
88
110
|
} from "vue";
|
|
89
|
-
import MInput from "../input/Input.vue";
|
|
90
111
|
import MTag from "../tag/Tag.vue";
|
|
91
112
|
import * as style from "./style";
|
|
92
113
|
import { scale } from "../../styles/transitions.css";
|
|
@@ -156,9 +177,16 @@ export default defineComponent({
|
|
|
156
177
|
type: Boolean,
|
|
157
178
|
default: false
|
|
158
179
|
},
|
|
180
|
+
selectAll: {
|
|
181
|
+
type: Boolean,
|
|
182
|
+
default: false
|
|
183
|
+
},
|
|
184
|
+
max: {
|
|
185
|
+
type: Number,
|
|
186
|
+
default: 5
|
|
187
|
+
}
|
|
159
188
|
},
|
|
160
189
|
components: {
|
|
161
|
-
MInput,
|
|
162
190
|
MTag
|
|
163
191
|
},
|
|
164
192
|
setup(props, {emit}) {
|
|
@@ -176,7 +204,6 @@ export default defineComponent({
|
|
|
176
204
|
const width = ref<number>(0);
|
|
177
205
|
const keyboardNavigation = ref<boolean>(false);
|
|
178
206
|
|
|
179
|
-
|
|
180
207
|
const {focus, blur} = useInteractive(container);
|
|
181
208
|
// TODO: Add debounce on keyboard movement (hover flicks it back up)
|
|
182
209
|
const {
|
|
@@ -188,7 +215,7 @@ export default defineComponent({
|
|
|
188
215
|
formLoading
|
|
189
216
|
} = useInput(props, {focus, emit, withListeners: false});
|
|
190
217
|
|
|
191
|
-
const {popper} = usePopper({
|
|
218
|
+
const {popper, updatePopper} = usePopper({
|
|
192
219
|
placement,
|
|
193
220
|
transfer,
|
|
194
221
|
reference: container,
|
|
@@ -220,6 +247,27 @@ export default defineComponent({
|
|
|
220
247
|
}
|
|
221
248
|
});
|
|
222
249
|
|
|
250
|
+
const selectedMax = computed(() => {
|
|
251
|
+
if (props.max <= 0) {
|
|
252
|
+
return selected.value;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (props.multiple && Array.isArray(selected.value) && selected.value.length > props.max) {
|
|
256
|
+
return selected.value.slice(0, props.max);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return selected.value;
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const selectedCountNotShown = computed(() => {
|
|
263
|
+
|
|
264
|
+
if (props.multiple && Array.isArray(selected.value) && props.max > 0) {
|
|
265
|
+
return selected.value.length - props.max;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return 0;
|
|
269
|
+
});
|
|
270
|
+
|
|
223
271
|
const computedConsistentWidth = computed(() => {
|
|
224
272
|
if (props.consistentWidth) {
|
|
225
273
|
return {
|
|
@@ -250,7 +298,7 @@ export default defineComponent({
|
|
|
250
298
|
}
|
|
251
299
|
|
|
252
300
|
let value = option[props.optionValue];
|
|
253
|
-
let active
|
|
301
|
+
let active: boolean;
|
|
254
302
|
if (props.multiple && Array.isArray(selected.value)) {
|
|
255
303
|
active = selected.value.includes(value);
|
|
256
304
|
} else {
|
|
@@ -263,13 +311,6 @@ export default defineComponent({
|
|
|
263
311
|
active: active,
|
|
264
312
|
onClick: () => handleOptionClick(value)
|
|
265
313
|
}
|
|
266
|
-
}).filter((option) => {
|
|
267
|
-
if (props.multiple && Array.isArray(selected.value)) {
|
|
268
|
-
// Filter out selected options
|
|
269
|
-
return !selected.value.includes(option.value);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
return true;
|
|
273
314
|
});
|
|
274
315
|
|
|
275
316
|
if (props.allowBlank) {
|
|
@@ -294,7 +335,7 @@ export default defineComponent({
|
|
|
294
335
|
}
|
|
295
336
|
|
|
296
337
|
let value = option[props.optionValue];
|
|
297
|
-
let active
|
|
338
|
+
let active: boolean;
|
|
298
339
|
if (props.multiple && Array.isArray(selected.value)) {
|
|
299
340
|
active = selected.value.includes(value);
|
|
300
341
|
} else {
|
|
@@ -307,13 +348,6 @@ export default defineComponent({
|
|
|
307
348
|
active: active,
|
|
308
349
|
onClick: () => handleOptionClick(value)
|
|
309
350
|
}
|
|
310
|
-
}).filter((option) => {
|
|
311
|
-
if (props.multiple && Array.isArray(selected.value)) {
|
|
312
|
-
// Filter out selected options
|
|
313
|
-
return !selected.value.includes(option.value);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
return true;
|
|
317
351
|
});
|
|
318
352
|
|
|
319
353
|
if (props.allowBlank) {
|
|
@@ -328,6 +362,26 @@ export default defineComponent({
|
|
|
328
362
|
return options;
|
|
329
363
|
});
|
|
330
364
|
|
|
365
|
+
function toggleAllItems() {
|
|
366
|
+
if (!props.multiple) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (computedOptions.value.length === 0) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (Array.isArray(selected.value)) {
|
|
375
|
+
if (selected.value.length === computedOptions.value.length) {
|
|
376
|
+
selected.value = [];
|
|
377
|
+
} else {
|
|
378
|
+
selected.value = computedOptions.value.map((option) => option.value);
|
|
379
|
+
}
|
|
380
|
+
} else {
|
|
381
|
+
selected.value = [];
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
331
385
|
function openSelect() {
|
|
332
386
|
if (disabled.value || loading.value) {
|
|
333
387
|
return;
|
|
@@ -379,6 +433,8 @@ export default defineComponent({
|
|
|
379
433
|
} else {
|
|
380
434
|
selected.value = [value];
|
|
381
435
|
}
|
|
436
|
+
|
|
437
|
+
updatePopper();
|
|
382
438
|
} else {
|
|
383
439
|
selected.value = value;
|
|
384
440
|
open.value = false;
|
|
@@ -396,9 +452,15 @@ export default defineComponent({
|
|
|
396
452
|
if (index !== -1) {
|
|
397
453
|
selected.value.splice(index, 1);
|
|
398
454
|
}
|
|
455
|
+
|
|
456
|
+
updatePopper();
|
|
399
457
|
}
|
|
400
458
|
}
|
|
401
459
|
|
|
460
|
+
useEventListener('resize', () => {
|
|
461
|
+
open.value = false;
|
|
462
|
+
});
|
|
463
|
+
|
|
402
464
|
function handleKeyDown(e: Event) {
|
|
403
465
|
if (computedOptions.value.length === 0) {
|
|
404
466
|
return;
|
|
@@ -464,11 +526,6 @@ export default defineComponent({
|
|
|
464
526
|
}
|
|
465
527
|
}
|
|
466
528
|
|
|
467
|
-
function focusOut(e: Event) {
|
|
468
|
-
console.log(e);
|
|
469
|
-
|
|
470
|
-
}
|
|
471
|
-
|
|
472
529
|
const transitionName = computed(() => {
|
|
473
530
|
return `m-transition__${props.transitionName}`;
|
|
474
531
|
});
|
|
@@ -495,7 +552,7 @@ export default defineComponent({
|
|
|
495
552
|
return [
|
|
496
553
|
style.base,
|
|
497
554
|
{
|
|
498
|
-
[style.selectedTags]: props.multiple,
|
|
555
|
+
[style.selectedTags]: props.multiple && Array.isArray(selected.value) && selected.value.length > 0,
|
|
499
556
|
[style.focused]: open.value,
|
|
500
557
|
// [`m-select--${props.size}`]: props.size,
|
|
501
558
|
'm-select--disabled': disabled.value || loading.value,
|
|
@@ -539,6 +596,13 @@ export default defineComponent({
|
|
|
539
596
|
|
|
540
597
|
watch(open, (value: boolean) => {
|
|
541
598
|
nextTick(() => {
|
|
599
|
+
if (!value) {
|
|
600
|
+
search.value = "";
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
if (props.searchable && searchRef.value) {
|
|
604
|
+
searchRef.value.focus();
|
|
605
|
+
}
|
|
542
606
|
if (value && optionsRef.value) {
|
|
543
607
|
const index = computedOptions.value.findIndex((i) => i.value === selected.value);
|
|
544
608
|
if (index !== -1) {
|
|
@@ -580,12 +644,13 @@ export default defineComponent({
|
|
|
580
644
|
selectRef,
|
|
581
645
|
popper,
|
|
582
646
|
focusSelect,
|
|
583
|
-
focusOut,
|
|
584
647
|
|
|
585
648
|
id,
|
|
586
649
|
isOpen,
|
|
587
650
|
open,
|
|
588
651
|
selected,
|
|
652
|
+
selectedMax,
|
|
653
|
+
selectedCountNotShown,
|
|
589
654
|
computedOptions,
|
|
590
655
|
selectedIndex,
|
|
591
656
|
search,
|
|
@@ -603,7 +668,8 @@ export default defineComponent({
|
|
|
603
668
|
inputListeners,
|
|
604
669
|
style,
|
|
605
670
|
focused,
|
|
606
|
-
scale
|
|
671
|
+
scale,
|
|
672
|
+
toggleAllItems
|
|
607
673
|
}
|
|
608
674
|
}
|
|
609
675
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { createVar, globalStyle, style, styleVariants } from "@vanilla-extract/css";
|
|
2
2
|
import { themeVars } from "../../../styles/theme.css";
|
|
3
|
-
import { fonts } from "../../../styles/typography.css";
|
|
4
3
|
|
|
5
4
|
const sizes = {
|
|
6
5
|
xs: '26px',
|
|
@@ -48,7 +47,6 @@ export const base = style({
|
|
|
48
47
|
[vars.backgroundColor]: '#FFFFFF',
|
|
49
48
|
[vars.cursor]: 'text',
|
|
50
49
|
},
|
|
51
|
-
height: vars.height,
|
|
52
50
|
minHeight: vars.height,
|
|
53
51
|
width: '100%',
|
|
54
52
|
borderRadius: vars.radius,
|
|
@@ -88,6 +86,8 @@ export const selectedTags = style({
|
|
|
88
86
|
flexWrap: 'wrap',
|
|
89
87
|
alignItems: 'center',
|
|
90
88
|
gap: themeVars.space[1],
|
|
89
|
+
paddingTop: themeVars.space[1.5],
|
|
90
|
+
paddingBottom: themeVars.space[1.5],
|
|
91
91
|
});
|
|
92
92
|
|
|
93
93
|
export const block = style({
|
|
@@ -200,6 +200,8 @@ export const search = style({
|
|
|
200
200
|
position: 'sticky',
|
|
201
201
|
top: 0,
|
|
202
202
|
zIndex: 1,
|
|
203
|
+
borderBottom: `1px solid ${themeVars.color.gray[200]}`,
|
|
204
|
+
display: 'flex',
|
|
203
205
|
});
|
|
204
206
|
|
|
205
207
|
export const searchInput = style({
|
|
@@ -208,8 +210,8 @@ export const searchInput = style({
|
|
|
208
210
|
outline: 'none',
|
|
209
211
|
width: '100%',
|
|
210
212
|
fontSize: themeVars.fontSize.base,
|
|
211
|
-
borderBottom: `1px solid ${themeVars.color.gray[200]}`,
|
|
212
213
|
color: themeVars.color.gray[800],
|
|
214
|
+
flexShrink: 1
|
|
213
215
|
});
|
|
214
216
|
|
|
215
217
|
export const options = style({
|
|
@@ -237,6 +239,7 @@ export const option = style({
|
|
|
237
239
|
transition: "background-color 0.05s ease-in-out",
|
|
238
240
|
backgroundColor: vars.optionBackgroundColor,
|
|
239
241
|
borderRadius: borderRadiuses.md,
|
|
242
|
+
userSelect: "none",
|
|
240
243
|
':hover': {
|
|
241
244
|
vars: {
|
|
242
245
|
[vars.optionColor]: themeVars.color.gray[800],
|
|
@@ -245,6 +248,7 @@ export const option = style({
|
|
|
245
248
|
}
|
|
246
249
|
});
|
|
247
250
|
|
|
251
|
+
|
|
248
252
|
export const optionFocused = style({
|
|
249
253
|
vars: {
|
|
250
254
|
[vars.optionColor]: themeVars.color.gray[800],
|
|
@@ -259,18 +263,85 @@ export const optionFocused = style({
|
|
|
259
263
|
});
|
|
260
264
|
|
|
261
265
|
export const optionSelected = style({
|
|
262
|
-
vars: {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
},
|
|
266
|
+
// vars: {
|
|
267
|
+
// [vars.optionColor]: themeVars.color.blue[50],
|
|
268
|
+
// [vars.optionBackgroundColor]: themeVars.color.blue[500],
|
|
269
|
+
// },
|
|
270
|
+
// ':hover': {
|
|
271
|
+
// vars: {
|
|
272
|
+
// [vars.optionColor]: themeVars.color.blue[50],
|
|
273
|
+
// [vars.optionBackgroundColor]: themeVars.color.blue[600],
|
|
274
|
+
// },
|
|
275
|
+
// }
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
export const optionLabel = style({
|
|
279
|
+
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
export const checkAll = style({
|
|
283
|
+
display: 'flex',
|
|
284
|
+
alignItems: 'center',
|
|
285
|
+
justifyContent: 'center',
|
|
286
|
+
width: themeVars.space[5],
|
|
287
|
+
margin: `0 ${themeVars.space[2]}`,
|
|
288
|
+
flexShrink: 0,
|
|
289
|
+
flex: 1
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
export const checkAllButton = style({
|
|
293
|
+
flexShrink: 0,
|
|
294
|
+
alignItems: 'center',
|
|
295
|
+
cursor: 'pointer',
|
|
296
|
+
justifyContent: 'center',
|
|
297
|
+
marginLeft: themeVars.space[2.5],
|
|
298
|
+
width: themeVars.space[6],
|
|
299
|
+
height: themeVars.space[6],
|
|
300
|
+
borderRadius: borderRadiuses.md,
|
|
301
|
+
color: themeVars.color.blue[500],
|
|
302
|
+
border: `2px solid ${themeVars.color.gray[200]}`,
|
|
303
|
+
padding: 0,
|
|
304
|
+
background: 'none',
|
|
305
|
+
outline: 'none',
|
|
266
306
|
':hover': {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
307
|
+
borderColor: themeVars.color.gray[300],
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
export const checkAllButtonActive = style({
|
|
312
|
+
border: `1px solid ${themeVars.color.blue[500]}`,
|
|
313
|
+
background: themeVars.color.blue[500],
|
|
314
|
+
color: 'white',
|
|
315
|
+
':hover': {
|
|
316
|
+
borderColor: themeVars.color.blue[600],
|
|
317
|
+
background: themeVars.color.blue[600],
|
|
271
318
|
}
|
|
272
319
|
});
|
|
273
320
|
|
|
321
|
+
globalStyle(`${checkAllButtonActive} > svg`, {
|
|
322
|
+
position: 'relative',
|
|
323
|
+
top: '1px',
|
|
324
|
+
width: themeVars.space[4],
|
|
325
|
+
height: themeVars.space[4],
|
|
326
|
+
color: 'white'
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
export const optionIcon = style({
|
|
330
|
+
display: 'flex',
|
|
331
|
+
alignItems: 'center',
|
|
332
|
+
justifyContent: 'center',
|
|
333
|
+
width: themeVars.space[5],
|
|
334
|
+
marginLeft: '-2px'
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
globalStyle(`${optionIcon} > svg`, {
|
|
338
|
+
position: 'relative',
|
|
339
|
+
top: '1px',
|
|
340
|
+
width: themeVars.space[5],
|
|
341
|
+
height: themeVars.space[5],
|
|
342
|
+
color: themeVars.color.blue[500],
|
|
343
|
+
});
|
|
344
|
+
|
|
274
345
|
export const noResults = style({
|
|
275
346
|
padding: `${themeVars.space[2]} 0`,
|
|
276
347
|
display: 'flex',
|
|
@@ -283,3 +354,12 @@ export const noResults = style({
|
|
|
283
354
|
color: themeVars.color.gray[500],
|
|
284
355
|
fontSize: themeVars.fontSize.small,
|
|
285
356
|
});
|
|
357
|
+
|
|
358
|
+
export const clearable = style({
|
|
359
|
+
width: themeVars.space[4],
|
|
360
|
+
height: themeVars.space[4],
|
|
361
|
+
position: 'absolute',
|
|
362
|
+
right: themeVars.space[2],
|
|
363
|
+
top: '0',
|
|
364
|
+
display: 'flex',
|
|
365
|
+
})
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div style="height: 200px;" ref="selfElRef">
|
|
3
|
+
<VVirtualList
|
|
4
|
+
ref="virtualListInstRef"
|
|
5
|
+
:items="nodesRef"
|
|
6
|
+
itemResizable
|
|
7
|
+
:itemSize="20"
|
|
8
|
+
>
|
|
9
|
+
<template v-slot:default="{ item }">
|
|
10
|
+
<TreeNode :node="item"/>
|
|
11
|
+
</template>
|
|
12
|
+
</VVirtualList>
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script lang="ts">
|
|
17
|
+
import {type VirtualListInst, VVirtualList} from 'vueuc'
|
|
18
|
+
import {computed, defineComponent, provide, ref, toRef} from 'vue'
|
|
19
|
+
import {createTreeMateOptions, treeProps} from "./props";
|
|
20
|
+
import type {Key, TmNode, TreeOption} from "./types";
|
|
21
|
+
import {
|
|
22
|
+
createTreeMate,
|
|
23
|
+
} from 'treemate'
|
|
24
|
+
import TreeNode from "./TreeNode.vue";
|
|
25
|
+
import {treeInjectionKey} from "./types";
|
|
26
|
+
import {call} from "../../call";
|
|
27
|
+
import useMergedState from "../../composables/useMergedState";
|
|
28
|
+
|
|
29
|
+
export default defineComponent({
|
|
30
|
+
components: {TreeNode, VVirtualList},
|
|
31
|
+
name: 'Tree',
|
|
32
|
+
props: treeProps,
|
|
33
|
+
setup: function (props) {
|
|
34
|
+
const selfElRef = ref<HTMLDivElement | null>(null);
|
|
35
|
+
const virtualListInstRef = ref<VirtualListInst | null>(null)
|
|
36
|
+
function getScrollContainer (): HTMLElement | null | undefined {
|
|
37
|
+
return virtualListInstRef.value?.listElRef
|
|
38
|
+
}
|
|
39
|
+
function getScrollContent (): HTMLElement | null | undefined {
|
|
40
|
+
return virtualListInstRef.value?.itemsElRef
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const displayTreeMateRef = computed(() => {
|
|
44
|
+
return createTreeMate<TreeOption>(
|
|
45
|
+
props.data,
|
|
46
|
+
createTreeMateOptions(
|
|
47
|
+
props.keyField,
|
|
48
|
+
props.childrenField,
|
|
49
|
+
'disabled',
|
|
50
|
+
props.getChildren
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const dataTreeMateRef = displayTreeMateRef;
|
|
56
|
+
const controlledExpandedKeysRef = toRef(props, 'expandedKeys')
|
|
57
|
+
const uncontrolledExpandedKeysRef = ref<Key[]>([])
|
|
58
|
+
const mergedExpandedKeysRef = useMergedState(
|
|
59
|
+
controlledExpandedKeysRef,
|
|
60
|
+
uncontrolledExpandedKeysRef
|
|
61
|
+
)
|
|
62
|
+
const nodesRef = computed(() => {
|
|
63
|
+
return displayTreeMateRef.value.getFlattenedNodes(mergedExpandedKeysRef.value);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
provide(treeInjectionKey, {
|
|
67
|
+
indentRef: toRef(props, 'indent'),
|
|
68
|
+
expandOnClickRef: toRef(props, 'expandOnClick'),
|
|
69
|
+
nodePropsRef: toRef(props, 'nodeProps'),
|
|
70
|
+
handleSwitcherClick
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// const { watchProps } = props;
|
|
74
|
+
//
|
|
75
|
+
// watchEffect(() => {
|
|
76
|
+
// initUncontrolledExpandedKeys(undefined);
|
|
77
|
+
// })
|
|
78
|
+
|
|
79
|
+
function handleSwitcherClick(node: TmNode): void {
|
|
80
|
+
console.log('i')
|
|
81
|
+
toggleExpand(node);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function toggleExpand(node: TmNode): void {
|
|
85
|
+
const {key} = node;
|
|
86
|
+
const {value: mergedExpandedKeys} = mergedExpandedKeysRef
|
|
87
|
+
const index = mergedExpandedKeys.findIndex(
|
|
88
|
+
(expandedNodeId) => expandedNodeId === key
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if (~index) {
|
|
92
|
+
const expandedKeysAfterChange = Array.from(mergedExpandedKeys);
|
|
93
|
+
expandedKeysAfterChange.splice(index, 1);
|
|
94
|
+
doUpdateExpandedKeys(expandedKeysAfterChange);
|
|
95
|
+
} else {
|
|
96
|
+
const nodeToBeExpanded = displayTreeMateRef.value.getNode(key);
|
|
97
|
+
if (!nodeToBeExpanded || nodeToBeExpanded.isLeaf) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let nextKeys: Key[] = mergedExpandedKeys.concat(key);
|
|
102
|
+
|
|
103
|
+
doUpdateExpandedKeys(nextKeys)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function getOptionsByKeys(keys: Key[]): Array<TreeOption | null> {
|
|
108
|
+
const {getNode} = dataTreeMateRef.value;
|
|
109
|
+
return keys.map((key) => getNode(key)?.rawNode || null);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function doUpdateExpandedKeys(
|
|
113
|
+
value: Key[],
|
|
114
|
+
): void {
|
|
115
|
+
uncontrolledExpandedKeysRef.value = value;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
nodesRef,
|
|
120
|
+
displayTreeMateRef,
|
|
121
|
+
selfElRef
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
</script>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
@click="handleLineClick"
|
|
4
|
+
style="display: flex;"
|
|
5
|
+
>
|
|
6
|
+
<template v-if="indentCount > 0">
|
|
7
|
+
<div v-for="i in indentCount" style="flex-grow: 0; flex-shrink: 0; height: 0;">
|
|
8
|
+
<div :key="i" style="width: 16px;"></div>
|
|
9
|
+
</div>
|
|
10
|
+
</template>
|
|
11
|
+
{{node.rawNode.label}}
|
|
12
|
+
</div>
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<script lang="ts">
|
|
16
|
+
import {ref, defineComponent, type PropType, inject, computed} from "vue";
|
|
17
|
+
import type {TmNode} from "./types";
|
|
18
|
+
import {treeInjectionKey} from "./types";
|
|
19
|
+
|
|
20
|
+
export default defineComponent({
|
|
21
|
+
name: 'TreeNode',
|
|
22
|
+
props: {
|
|
23
|
+
node: {
|
|
24
|
+
type: Object as PropType<TmNode>,
|
|
25
|
+
required: true
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
setup(props) {
|
|
29
|
+
const NTree = inject(treeInjectionKey)!;
|
|
30
|
+
const {
|
|
31
|
+
nodePropsRef,
|
|
32
|
+
indentRef,
|
|
33
|
+
handleSwitcherClick: handleSwitcherClickTree
|
|
34
|
+
} = NTree;
|
|
35
|
+
|
|
36
|
+
const selfElRef = ref<HTMLDivElement | null>(null);
|
|
37
|
+
|
|
38
|
+
function _handleClick (e: MouseEvent): void {
|
|
39
|
+
const { value: expandOnClick } = NTree.expandOnClickRef;
|
|
40
|
+
const { node } = props
|
|
41
|
+
if (!expandOnClick && !node.isLeaf) {
|
|
42
|
+
handleSwitcherClick();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function handleSwitcherClick(): void {
|
|
47
|
+
const { node } = props;
|
|
48
|
+
|
|
49
|
+
if (!node.isLastChild && !node.shallowLoaded) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
handleSwitcherClickTree(node)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const indentCount = computed(() => {
|
|
57
|
+
return props.node.level;
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
function handleLineClick (e: MouseEvent) {
|
|
61
|
+
_handleClick(e);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
selfElRef,
|
|
66
|
+
handleLineClick,
|
|
67
|
+
indentCount
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
</script>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<MTree :data="data"/>
|
|
3
|
+
</template>
|
|
4
|
+
<script lang="ts" setup>
|
|
5
|
+
import {ref} from 'vue'
|
|
6
|
+
import type {TreeOption} from "../types";
|
|
7
|
+
|
|
8
|
+
function createData (level = 4, baseKey = ''): TreeOption[] | undefined {
|
|
9
|
+
if (!level) return undefined
|
|
10
|
+
|
|
11
|
+
const result: TreeOption[] = [];
|
|
12
|
+
for (let index = 0; index < (20 - level); index++) {
|
|
13
|
+
const key = '' + baseKey + level + index;
|
|
14
|
+
result.push({
|
|
15
|
+
label: createLabel(level),
|
|
16
|
+
key,
|
|
17
|
+
children: createData(level - 1, key)
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function createLabel (level: number): string {
|
|
25
|
+
if (level === 4) return 'Out of Tao, One is born'
|
|
26
|
+
if (level === 3) return 'Out of One, Two'
|
|
27
|
+
if (level === 2) return 'Out of Two, Three'
|
|
28
|
+
if (level === 1) return 'Out of Three, the created universe'
|
|
29
|
+
return ''
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const data = ref(createData());
|
|
33
|
+
</script>
|