@veritree/ui 0.27.0-2 → 0.27.0-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/src/components/Disclosure/VTDisclosure.vue +2 -11
- package/src/components/Disclosure/VTDisclosureContent.vue +26 -52
- package/src/components/Disclosure/VTDisclosureDetails.vue +28 -3
- package/src/components/Disclosure/VTDisclosureHeader.vue +56 -89
- package/src/components/Disclosure/VTDisclosureIcon.vue +42 -31
package/package.json
CHANGED
|
@@ -11,11 +11,8 @@ export default {
|
|
|
11
11
|
provide() {
|
|
12
12
|
return {
|
|
13
13
|
apiDisclosure: () => {
|
|
14
|
-
const details = this.details;
|
|
15
|
-
const contents = this.contents;
|
|
16
14
|
const accordion = this.accordion;
|
|
17
|
-
const
|
|
18
|
-
const icons = this.icons;
|
|
15
|
+
const details = this.details;
|
|
19
16
|
|
|
20
17
|
const register = (objKey, item) => {
|
|
21
18
|
if (!item) return;
|
|
@@ -28,11 +25,8 @@ export default {
|
|
|
28
25
|
};
|
|
29
26
|
|
|
30
27
|
return {
|
|
31
|
-
details,
|
|
32
|
-
contents,
|
|
33
|
-
headers,
|
|
34
|
-
icons,
|
|
35
28
|
accordion,
|
|
29
|
+
details,
|
|
36
30
|
register,
|
|
37
31
|
unregister,
|
|
38
32
|
};
|
|
@@ -50,9 +44,6 @@ export default {
|
|
|
50
44
|
data() {
|
|
51
45
|
return {
|
|
52
46
|
details: [],
|
|
53
|
-
contents: [],
|
|
54
|
-
headers: [],
|
|
55
|
-
icons: [],
|
|
56
47
|
};
|
|
57
48
|
},
|
|
58
49
|
};
|
|
@@ -4,12 +4,19 @@
|
|
|
4
4
|
:class="[
|
|
5
5
|
headless
|
|
6
6
|
? 'details-content'
|
|
7
|
-
: '
|
|
8
|
-
|
|
7
|
+
: 'grid transition-[grid-template-rows] duration-300 ease-linear',
|
|
8
|
+
headless
|
|
9
|
+
? isVisible
|
|
10
|
+
? 'details-content--expanded'
|
|
11
|
+
: null
|
|
12
|
+
: isVisible
|
|
13
|
+
? 'grid-rows-[1fr]'
|
|
14
|
+
: 'grid-rows-[0fr]',
|
|
9
15
|
]"
|
|
10
|
-
:style="`--height: ${height};`"
|
|
11
16
|
>
|
|
12
|
-
<
|
|
17
|
+
<div :class="[headless ? 'detail-content__overflow' : 'overflow-hidden']">
|
|
18
|
+
<slot></slot>
|
|
19
|
+
</div>
|
|
13
20
|
</div>
|
|
14
21
|
</template>
|
|
15
22
|
|
|
@@ -24,14 +31,15 @@ export default {
|
|
|
24
31
|
type: Boolean,
|
|
25
32
|
default: false,
|
|
26
33
|
},
|
|
34
|
+
open: {
|
|
35
|
+
type: Boolean,
|
|
36
|
+
default: false,
|
|
37
|
+
},
|
|
27
38
|
},
|
|
28
39
|
|
|
29
40
|
data() {
|
|
30
41
|
return {
|
|
31
42
|
idGroup: this.apiDetails().idGroup,
|
|
32
|
-
expanded: false,
|
|
33
|
-
expandedHeight: null,
|
|
34
|
-
invisible: false,
|
|
35
43
|
};
|
|
36
44
|
},
|
|
37
45
|
|
|
@@ -40,56 +48,22 @@ export default {
|
|
|
40
48
|
return `disclosure-content-${this.idGroup}`;
|
|
41
49
|
},
|
|
42
50
|
|
|
43
|
-
|
|
44
|
-
return this.
|
|
51
|
+
isVisible() {
|
|
52
|
+
return this.apiDetails().isVisible();
|
|
45
53
|
},
|
|
46
54
|
},
|
|
47
55
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
id: this.id,
|
|
53
|
-
idGroup: this.idGroup,
|
|
54
|
-
collapse: this.collapse,
|
|
55
|
-
expand: this.expand,
|
|
56
|
-
isVisible: this.isVisible,
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
this.apiDisclosure().register('contents', content);
|
|
60
|
-
},
|
|
61
|
-
|
|
62
|
-
beforeUnmount() {
|
|
63
|
-
this.apiDisclosure().unregister('contents', this.id);
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
methods: {
|
|
67
|
-
handleVisibleHeight() {
|
|
68
|
-
// make panel position absolute and opacity 0,
|
|
69
|
-
// to have it fully opened but not visible
|
|
70
|
-
this.invisible = true;
|
|
71
|
-
|
|
72
|
-
setTimeout(() => {
|
|
73
|
-
// get computed height now since its fully opened
|
|
74
|
-
// this height will be used by a css custom property to enable the transition
|
|
75
|
-
this.expandedHeight = window.getComputedStyle(this.$el).height;
|
|
76
|
-
|
|
77
|
-
// make invisible false once the full height is set
|
|
78
|
-
this.invisible = false;
|
|
79
|
-
}, 500);
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
expand() {
|
|
83
|
-
this.expanded = true;
|
|
84
|
-
},
|
|
85
|
-
|
|
86
|
-
collapse() {
|
|
87
|
-
this.expanded = false;
|
|
56
|
+
watch: {
|
|
57
|
+
open(value) {
|
|
58
|
+
this.apiDetails().setVisible(value);
|
|
59
|
+
this.expand = value;
|
|
88
60
|
},
|
|
61
|
+
},
|
|
89
62
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
63
|
+
mounted() {
|
|
64
|
+
if (this.open) {
|
|
65
|
+
this.apiDetails().setVisible(this.open);
|
|
66
|
+
}
|
|
93
67
|
},
|
|
94
68
|
};
|
|
95
69
|
</script>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
:class="[
|
|
4
|
-
headless ? 'disclosure-details' : '
|
|
4
|
+
headless ? 'disclosure-details' : 'relative',
|
|
5
5
|
headless ? (visible ? 'is-open' : null) : null,
|
|
6
6
|
]"
|
|
7
7
|
>
|
|
@@ -26,6 +26,7 @@ export default {
|
|
|
26
26
|
const idContent = `details-content-${idGroup}`;
|
|
27
27
|
|
|
28
28
|
const isVisible = () => this.isVisible();
|
|
29
|
+
const setVisible = (visible) => this.setVisible(visible);
|
|
29
30
|
|
|
30
31
|
return {
|
|
31
32
|
idGroup,
|
|
@@ -33,6 +34,8 @@ export default {
|
|
|
33
34
|
idIcon,
|
|
34
35
|
idContent,
|
|
35
36
|
isVisible,
|
|
37
|
+
setVisible,
|
|
38
|
+
visible: this.visible,
|
|
36
39
|
};
|
|
37
40
|
},
|
|
38
41
|
};
|
|
@@ -52,6 +55,22 @@ export default {
|
|
|
52
55
|
};
|
|
53
56
|
},
|
|
54
57
|
|
|
58
|
+
watch: {
|
|
59
|
+
visible(value) {
|
|
60
|
+
if (value && this.apiDisclosure().accordion) {
|
|
61
|
+
this.apiDisclosure().details.forEach((detail) => {
|
|
62
|
+
if (detail.idGroup !== this.idGroup) {
|
|
63
|
+
detail.hide();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (detail.idGroup === this.idGroup) {
|
|
67
|
+
detail.show();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
|
|
55
74
|
mounted() {
|
|
56
75
|
const detail = {
|
|
57
76
|
idGroup: this.idGroup,
|
|
@@ -63,22 +82,28 @@ export default {
|
|
|
63
82
|
this.apiDisclosure().register('details', detail);
|
|
64
83
|
},
|
|
65
84
|
|
|
66
|
-
|
|
85
|
+
beforeDestroy() {
|
|
67
86
|
this.apiDisclosure().unregister('details', this.id);
|
|
68
87
|
},
|
|
69
88
|
|
|
70
89
|
methods: {
|
|
71
90
|
show() {
|
|
91
|
+
this.$emit('toggle', true);
|
|
72
92
|
this.visible = true;
|
|
73
93
|
},
|
|
74
94
|
|
|
75
95
|
hide() {
|
|
96
|
+
this.$emit('toggle', false);
|
|
76
97
|
this.visible = false;
|
|
77
98
|
},
|
|
78
99
|
|
|
79
100
|
isVisible() {
|
|
80
101
|
return this.visible;
|
|
81
102
|
},
|
|
103
|
+
|
|
104
|
+
setVisible(visible) {
|
|
105
|
+
this.visible = visible;
|
|
106
|
+
},
|
|
82
107
|
},
|
|
83
108
|
};
|
|
84
|
-
</script>
|
|
109
|
+
</script>
|
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<header
|
|
3
3
|
:id="id"
|
|
4
|
-
:class="
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
:aria-controls="ariaControls"
|
|
10
|
-
:aria-expanded="String(ariaExpanded)"
|
|
11
|
-
role="button"
|
|
12
|
-
tabindex="0"
|
|
13
|
-
@click.prevent="toggle"
|
|
14
|
-
@keydown.enter="toggle"
|
|
4
|
+
:class="headerClasses"
|
|
5
|
+
v-bind="ariaAttributes"
|
|
6
|
+
@click.prevent="handleClick"
|
|
7
|
+
@keydown.enter.prevent="handleKeydown"
|
|
8
|
+
@keydown.space.prevent="handleKeydown"
|
|
15
9
|
>
|
|
16
10
|
<slot></slot>
|
|
17
11
|
</header>
|
|
@@ -24,112 +18,85 @@ export default {
|
|
|
24
18
|
inject: ['apiDisclosure', 'apiDetails'],
|
|
25
19
|
|
|
26
20
|
props: {
|
|
21
|
+
/**
|
|
22
|
+
* If true, the header will not toggle visibility of the content.
|
|
23
|
+
* Useful for headers that are purely informational.
|
|
24
|
+
*/
|
|
25
|
+
actionless: {
|
|
26
|
+
type: Boolean,
|
|
27
|
+
default: false,
|
|
28
|
+
},
|
|
29
|
+
/**
|
|
30
|
+
* If true, the header will not have any visual styles applied.
|
|
31
|
+
* Useful for headless implementations where you want to style it yourself.
|
|
32
|
+
*/
|
|
27
33
|
headless: {
|
|
28
34
|
type: Boolean,
|
|
29
35
|
default: false,
|
|
30
36
|
},
|
|
31
37
|
},
|
|
32
38
|
|
|
33
|
-
data() {
|
|
34
|
-
return {
|
|
35
|
-
ariaExpanded: false,
|
|
36
|
-
};
|
|
37
|
-
},
|
|
38
|
-
|
|
39
39
|
computed: {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
headerClasses() {
|
|
41
|
+
if (this.headless) {
|
|
42
|
+
return 'details-header';
|
|
43
|
+
}
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
},
|
|
45
|
+
const classes = ['flex justify-between gap-3 text-body font-semibold'];
|
|
46
|
+
classes.push(this.actionless ? 'cursor-default' : 'cursor-pointer');
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
return this.apiDetails().idContent;
|
|
48
|
+
return classes;
|
|
50
49
|
},
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
ariaAttributes() {
|
|
52
|
+
if (this.actionless) {
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
'aria-controls': this.ariaControls,
|
|
58
|
+
'aria-expanded': this.ariaExpanded,
|
|
59
|
+
'role': 'button',
|
|
60
|
+
'tabindex': '0',
|
|
61
|
+
};
|
|
54
62
|
},
|
|
55
63
|
|
|
56
|
-
|
|
57
|
-
return this.
|
|
64
|
+
id() {
|
|
65
|
+
return this.apiDetails().idSummary;
|
|
58
66
|
},
|
|
59
67
|
|
|
60
|
-
|
|
61
|
-
return this.
|
|
68
|
+
ariaControls() {
|
|
69
|
+
return this.apiDetails().idContent;
|
|
62
70
|
},
|
|
63
71
|
|
|
64
|
-
|
|
65
|
-
return this.
|
|
72
|
+
isVisible() {
|
|
73
|
+
return this.apiDetails().visible;
|
|
66
74
|
},
|
|
67
|
-
},
|
|
68
|
-
|
|
69
|
-
mounted() {
|
|
70
|
-
this.isExpanded();
|
|
71
|
-
|
|
72
|
-
const header = {
|
|
73
|
-
id: this.id,
|
|
74
|
-
idGroup: this.idGroup,
|
|
75
|
-
isExpanded: this.isExpanded,
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
this.apiDisclosure().register('headers', header);
|
|
79
|
-
},
|
|
80
75
|
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
ariaExpanded() {
|
|
77
|
+
return String(this.isVisible);
|
|
78
|
+
},
|
|
83
79
|
},
|
|
84
80
|
|
|
85
81
|
methods: {
|
|
86
82
|
toggle() {
|
|
87
|
-
this.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
this.toggleIcons();
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
isExpanded() {
|
|
94
|
-
this.ariaExpanded = this.apiDetails().isVisible();
|
|
95
|
-
},
|
|
96
|
-
|
|
97
|
-
toggleDetails() {
|
|
98
|
-
this.details.forEach((detail) => {
|
|
99
|
-
const isSameGroup = detail.idGroup === this.idGroup;
|
|
100
|
-
const isAccordion = this.apiDisclosure().accordion;
|
|
101
|
-
|
|
102
|
-
if (isSameGroup) {
|
|
103
|
-
detail.isVisible() ? detail.hide() : detail.show();
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (isAccordion && !isSameGroup) {
|
|
107
|
-
detail.hide();
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
},
|
|
111
|
-
|
|
112
|
-
toggleContents() {
|
|
113
|
-
this.contents.forEach((content) => {
|
|
114
|
-
const isSameGroup = content.idGroup === this.idGroup;
|
|
115
|
-
const isAccordion = this.apiDisclosure().accordion;
|
|
116
|
-
|
|
117
|
-
if (isSameGroup) {
|
|
118
|
-
content.isVisible() ? content.collapse() : content.expand();
|
|
119
|
-
}
|
|
83
|
+
if (this.actionless) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
120
86
|
|
|
121
|
-
|
|
122
|
-
content.collapse();
|
|
123
|
-
}
|
|
124
|
-
});
|
|
87
|
+
this.apiDetails().setVisible(!this.isVisible);
|
|
125
88
|
},
|
|
126
89
|
|
|
127
|
-
|
|
128
|
-
this.
|
|
90
|
+
handleClick() {
|
|
91
|
+
if (!this.actionless) {
|
|
92
|
+
this.toggle();
|
|
93
|
+
}
|
|
129
94
|
},
|
|
130
95
|
|
|
131
|
-
|
|
132
|
-
this.
|
|
96
|
+
handleKeydown() {
|
|
97
|
+
if (!this.actionless) {
|
|
98
|
+
this.toggle();
|
|
99
|
+
}
|
|
133
100
|
},
|
|
134
101
|
},
|
|
135
102
|
};
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
<component
|
|
3
|
+
:is="as"
|
|
4
|
+
:class="iconClasses"
|
|
5
|
+
v-bind="ariaAttributes"
|
|
6
|
+
@click.stop="onClick"
|
|
7
|
+
@keydown.enter="onClick"
|
|
8
|
+
@keydown.space.prevent="onClick"
|
|
7
9
|
>
|
|
8
10
|
<slot><IconChevronDown /></slot>
|
|
9
|
-
</
|
|
11
|
+
</component>
|
|
10
12
|
</template>
|
|
11
13
|
|
|
12
14
|
<script>
|
|
@@ -20,43 +22,52 @@ export default {
|
|
|
20
22
|
type: Boolean,
|
|
21
23
|
default: false,
|
|
22
24
|
},
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
as: {
|
|
26
|
+
type: String,
|
|
27
|
+
default: 'span',
|
|
28
|
+
validator(value) {
|
|
29
|
+
return ['span', 'button', 'div', 'a'].includes(value);
|
|
30
|
+
},
|
|
31
|
+
},
|
|
29
32
|
},
|
|
30
33
|
|
|
31
34
|
computed: {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
ariaAttributes() {
|
|
36
|
+
if (this.as !== 'button') {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
return {
|
|
41
|
+
'aria-expanded': String(this.expanded),
|
|
42
|
+
'aria-controls': this.apiDetails().idContent,
|
|
43
|
+
};
|
|
38
44
|
},
|
|
39
|
-
},
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
iconClasses() {
|
|
47
|
+
const classes = [];
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
if (this.headless) {
|
|
50
|
+
classes.push('disclosure-icon');
|
|
51
|
+
} else {
|
|
52
|
+
classes.push('shrink-0 transition-all');
|
|
53
|
+
classes.push(this.expanded ? 'rotate-180' : 'rotate-0');
|
|
54
|
+
classes.push(this.as === 'button' ? null : 'pointer-events-none');
|
|
55
|
+
}
|
|
49
56
|
|
|
50
|
-
|
|
51
|
-
|
|
57
|
+
return classes.filter(Boolean);
|
|
58
|
+
},
|
|
52
59
|
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
expanded() {
|
|
61
|
+
return this.apiDetails().visible;
|
|
62
|
+
},
|
|
55
63
|
},
|
|
56
64
|
|
|
57
65
|
methods: {
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
onClick() {
|
|
67
|
+
// Only handle click if rendered as a button
|
|
68
|
+
if (this.as === 'button') {
|
|
69
|
+
this.apiDetails().setVisible(!this.expanded);
|
|
70
|
+
}
|
|
60
71
|
},
|
|
61
72
|
},
|
|
62
73
|
};
|