@veritree/ui 0.1.6 → 0.2.0
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/index.js +8 -8
- package/package-lock.json +13 -0
- package/package.json +2 -2
- package/src/Listbox/VTListbox.vue +1 -1
- package/src/Listbox/VTListboxButton.vue +4 -5
- package/src/Listbox/VTListboxOption.vue +22 -17
- package/src/Listbox/VTListboxOptions.vue +11 -12
- package/src/Modal/VTModal.vue +2 -2
- package/src/Tabs/VTTabGroup.vue +5 -5
- package/src/utils/components.js +18 -0
- package/src/utils/ids.js +0 -21
package/index.js
CHANGED
|
@@ -12,10 +12,10 @@ import VTInputUpload from './src/Input/VTInputUpload.vue';
|
|
|
12
12
|
|
|
13
13
|
import VTTextarea from './src/Textarea/VTTextarea.vue';
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
import VTListbox from './src/Listbox/VTListbox.vue';
|
|
16
|
+
import VTListboxButton from './src/Listbox/VTListboxButton.vue';
|
|
17
|
+
import VTListboxOption from './src/Listbox/VTListboxOption.vue';
|
|
18
|
+
import VTListboxOptions from './src/Listbox/VTListboxOptions.vue';
|
|
19
19
|
|
|
20
20
|
import VTModal from './src/Modal/VTModal.vue';
|
|
21
21
|
|
|
@@ -40,10 +40,10 @@ export {
|
|
|
40
40
|
VTInputFile,
|
|
41
41
|
VTInputUpload,
|
|
42
42
|
VTTextarea,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
VTListbox,
|
|
44
|
+
VTListboxButton,
|
|
45
|
+
VTListboxOption,
|
|
46
|
+
VTListboxOptions,
|
|
47
47
|
VTModal,
|
|
48
48
|
VTAccordion,
|
|
49
49
|
VTAccordionButton,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@veritree/ui",
|
|
3
|
+
"version": "0.1.9",
|
|
4
|
+
"lockfileVersion": 1,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@veritree/icons": {
|
|
8
|
+
"version": "0.12.0",
|
|
9
|
+
"resolved": "https://registry.npmjs.org/@veritree/icons/-/icons-0.12.0.tgz",
|
|
10
|
+
"integrity": "sha512-vunUKzvS9neslSf3R3y6RYQrcfRpxmp8PnhWWe2peYiyElLIJcb7zAsfCZ+I0Fg5PQ6GZG6StqWy0WF7MJ7VOg=="
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veritree/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "veritree ui library",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -11,6 +11,6 @@
|
|
|
11
11
|
"access": "public"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@veritree/icons": "^0.
|
|
14
|
+
"@veritree/icons": "^0.19.0"
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<button
|
|
3
3
|
:aria-expanded="expanded"
|
|
4
4
|
aria-haspopup="listbox"
|
|
5
|
-
class="Listbox-button
|
|
5
|
+
class="Listbox-button"
|
|
6
6
|
:data-theme="theme"
|
|
7
7
|
type="button"
|
|
8
8
|
@click.prevent="onClick"
|
|
@@ -39,13 +39,12 @@ export default {
|
|
|
39
39
|
|
|
40
40
|
data() {
|
|
41
41
|
return {
|
|
42
|
-
api: this.api(),
|
|
43
42
|
expanded: false,
|
|
44
43
|
};
|
|
45
44
|
},
|
|
46
45
|
|
|
47
46
|
mounted() {
|
|
48
|
-
this.api.registerListboxButton(this);
|
|
47
|
+
this.api().registerListboxButton(this);
|
|
49
48
|
},
|
|
50
49
|
|
|
51
50
|
methods: {
|
|
@@ -59,7 +58,7 @@ export default {
|
|
|
59
58
|
},
|
|
60
59
|
|
|
61
60
|
onClick() {
|
|
62
|
-
const listbox = this.api.getListbox();
|
|
61
|
+
const listbox = this.api().getListbox();
|
|
63
62
|
this.expanded ? listbox.hide() : listbox.show();
|
|
64
63
|
|
|
65
64
|
this.toggleExpanded();
|
|
@@ -67,7 +66,7 @@ export default {
|
|
|
67
66
|
|
|
68
67
|
onKeyDown(event) {
|
|
69
68
|
const key = event.key;
|
|
70
|
-
const listbox = this.api.getListbox();
|
|
69
|
+
const listbox = this.api().getListbox();
|
|
71
70
|
|
|
72
71
|
switch (key) {
|
|
73
72
|
case keys.down:
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<li
|
|
3
3
|
:id="id"
|
|
4
|
-
:aria-selected="selected"
|
|
5
4
|
class="Listbox-option"
|
|
6
5
|
role="option"
|
|
7
6
|
@mousedown.prevent="onMouseDown"
|
|
@@ -13,6 +12,7 @@
|
|
|
13
12
|
</template>
|
|
14
13
|
|
|
15
14
|
<script>
|
|
15
|
+
import { scrollElementIntoView } from '../utils/components';
|
|
16
16
|
import { genId } from '../utils/ids';
|
|
17
17
|
import { areObjsEqual, isObj } from '../utils/objects';
|
|
18
18
|
|
|
@@ -26,36 +26,41 @@ export default {
|
|
|
26
26
|
type: [String, Number, Object],
|
|
27
27
|
required: true,
|
|
28
28
|
},
|
|
29
|
+
selected: {
|
|
30
|
+
type: Boolean,
|
|
31
|
+
default: false,
|
|
32
|
+
},
|
|
29
33
|
},
|
|
30
34
|
|
|
31
35
|
data() {
|
|
32
36
|
return {
|
|
33
|
-
api: this.api(),
|
|
34
37
|
id: `listbox-option-${genId()}`,
|
|
35
|
-
selected: false,
|
|
36
38
|
focused: false,
|
|
37
39
|
isMousemove: false,
|
|
40
|
+
parent: null,
|
|
38
41
|
};
|
|
39
42
|
},
|
|
40
43
|
|
|
41
44
|
watch: {
|
|
42
45
|
focused(newValue) {
|
|
43
|
-
if (newValue)
|
|
44
|
-
if (!this.isMousemove) {
|
|
45
|
-
this.$el.scrollIntoView({ block: 'nearest' });
|
|
46
|
-
}
|
|
46
|
+
if (!newValue) return;
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
if (!this.parent) this.parent = this.api().getListbox();
|
|
49
|
+
if (!this.isMousemove) scrollElementIntoView(this.$el, this.parent.$el);
|
|
50
|
+
this.parent.updateActiveDescendant(this.id);
|
|
51
51
|
},
|
|
52
52
|
},
|
|
53
53
|
|
|
54
54
|
mounted() {
|
|
55
|
-
this.api.registerOption(this);
|
|
55
|
+
this.api().registerOption(this);
|
|
56
|
+
|
|
57
|
+
if (this.selected) {
|
|
58
|
+
this.select();
|
|
59
|
+
this.focus();
|
|
60
|
+
}
|
|
56
61
|
|
|
57
62
|
// todo: make sure it works with other values than objects
|
|
58
|
-
const listboxOptionSelected = this.api.getListboxValue();
|
|
63
|
+
const listboxOptionSelected = this.api().getListboxValue();
|
|
59
64
|
|
|
60
65
|
if (!listboxOptionSelected) {
|
|
61
66
|
return;
|
|
@@ -77,7 +82,7 @@ export default {
|
|
|
77
82
|
},
|
|
78
83
|
|
|
79
84
|
beforeDestroy() {
|
|
80
|
-
this.api.unregisterOption(this.id);
|
|
85
|
+
this.api().unregisterOption(this.id);
|
|
81
86
|
},
|
|
82
87
|
|
|
83
88
|
methods: {
|
|
@@ -92,15 +97,15 @@ export default {
|
|
|
92
97
|
},
|
|
93
98
|
|
|
94
99
|
select() {
|
|
95
|
-
this.selected
|
|
100
|
+
this.$el.setAttribute('aria-selected', true);
|
|
96
101
|
},
|
|
97
102
|
|
|
98
103
|
unselect() {
|
|
99
|
-
this.selected
|
|
104
|
+
this.$el.setAttribute('aria-selected', false);
|
|
100
105
|
},
|
|
101
106
|
|
|
102
107
|
onMouseDown(event) {
|
|
103
|
-
if (event.buttons === 1) this.api.selectOption(this);
|
|
108
|
+
if (event.buttons === 1) this.api().selectOption(this);
|
|
104
109
|
},
|
|
105
110
|
|
|
106
111
|
// Mousemove instead of mouseover to support keyboard navigation.
|
|
@@ -108,7 +113,7 @@ export default {
|
|
|
108
113
|
// mouseover event gets triggered.
|
|
109
114
|
onMousemove() {
|
|
110
115
|
this.isMousemove = true;
|
|
111
|
-
this.api.unfocusOptions();
|
|
116
|
+
this.api().unfocusOptions();
|
|
112
117
|
this.focus();
|
|
113
118
|
},
|
|
114
119
|
|
|
@@ -23,7 +23,6 @@ export default {
|
|
|
23
23
|
|
|
24
24
|
data() {
|
|
25
25
|
return {
|
|
26
|
-
api: this.api(),
|
|
27
26
|
visible: false,
|
|
28
27
|
filter: '',
|
|
29
28
|
};
|
|
@@ -37,7 +36,7 @@ export default {
|
|
|
37
36
|
this.handleOptionFocus();
|
|
38
37
|
});
|
|
39
38
|
} else {
|
|
40
|
-
const listboxButton = this.api.getListboxButton();
|
|
39
|
+
const listboxButton = this.api().getListboxButton();
|
|
41
40
|
|
|
42
41
|
this.$nextTick(() => {
|
|
43
42
|
listboxButton.focus();
|
|
@@ -47,7 +46,7 @@ export default {
|
|
|
47
46
|
},
|
|
48
47
|
|
|
49
48
|
mounted() {
|
|
50
|
-
this.api.registerListbox(this);
|
|
49
|
+
this.api().registerListbox(this);
|
|
51
50
|
},
|
|
52
51
|
|
|
53
52
|
methods: {
|
|
@@ -60,8 +59,8 @@ export default {
|
|
|
60
59
|
},
|
|
61
60
|
|
|
62
61
|
handleOptionFocus() {
|
|
63
|
-
const selectedIndex = this.api.getFocusedIndex();
|
|
64
|
-
if (!selectedIndex) this.api.focusFirstOption();
|
|
62
|
+
const selectedIndex = this.api().getFocusedIndex();
|
|
63
|
+
if (!selectedIndex) this.api().focusFirstOption();
|
|
65
64
|
},
|
|
66
65
|
|
|
67
66
|
updateActiveDescendant(id) {
|
|
@@ -78,31 +77,31 @@ export default {
|
|
|
78
77
|
clearTimeout(timer);
|
|
79
78
|
|
|
80
79
|
timer = setTimeout(() => {
|
|
81
|
-
this.api.focusOptionByFilter(this.filter);
|
|
80
|
+
this.api().focusOptionByFilter(this.filter);
|
|
82
81
|
this.filter = '';
|
|
83
|
-
},
|
|
82
|
+
}, 100);
|
|
84
83
|
}
|
|
85
84
|
|
|
86
85
|
switch (key) {
|
|
87
86
|
case keys.up:
|
|
88
87
|
event.preventDefault();
|
|
89
|
-
this.api.focusPreviousOption();
|
|
88
|
+
this.api().focusPreviousOption();
|
|
90
89
|
break;
|
|
91
90
|
case keys.down:
|
|
92
91
|
event.preventDefault();
|
|
93
|
-
this.api.focusNextOption();
|
|
92
|
+
this.api().focusNextOption();
|
|
94
93
|
break;
|
|
95
94
|
case keys.home:
|
|
96
95
|
event.preventDefault();
|
|
97
|
-
this.api.focusFirstOption();
|
|
96
|
+
this.api().focusFirstOption();
|
|
98
97
|
break;
|
|
99
98
|
case keys.end:
|
|
100
99
|
event.preventDefault();
|
|
101
|
-
this.api.focusLastOption();
|
|
100
|
+
this.api().focusLastOption();
|
|
102
101
|
break;
|
|
103
102
|
case keys.enter:
|
|
104
103
|
event.preventDefault();
|
|
105
|
-
this.api.selectOption();
|
|
104
|
+
this.api().selectOption();
|
|
106
105
|
break;
|
|
107
106
|
case keys.escape:
|
|
108
107
|
this.hide();
|
package/src/Modal/VTModal.vue
CHANGED
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
>
|
|
12
12
|
<div
|
|
13
13
|
v-if="visible"
|
|
14
|
-
class="fixed inset-0 z-50 flex flex-col justify-center bg-fd-700/75
|
|
14
|
+
class="fixed inset-0 z-50 flex flex-col justify-center bg-fd-700/75"
|
|
15
15
|
tabindex="-1"
|
|
16
16
|
@keyup.esc="close"
|
|
17
17
|
@click="close"
|
|
18
18
|
>
|
|
19
19
|
<div
|
|
20
|
-
class="relative mx-auto flex max-w-lg flex-col justify-center rounded bg-white
|
|
20
|
+
class="relative mx-auto flex max-w-lg flex-col justify-center rounded bg-white"
|
|
21
21
|
@click.stop
|
|
22
22
|
>
|
|
23
23
|
<div class="absolute right-4 top-4">
|
package/src/Tabs/VTTabGroup.vue
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
</div>
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
|
-
<style
|
|
7
|
+
<style scoped>
|
|
8
8
|
.TabGroup.vertical{
|
|
9
|
-
.TabList{
|
|
10
|
-
display: flex;
|
|
11
|
-
flex-direction: column;
|
|
12
|
-
}
|
|
13
9
|
display: flex;
|
|
14
10
|
}
|
|
11
|
+
.TabGroup.vertical .TabList{
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
}
|
|
15
15
|
</style>
|
|
16
16
|
|
|
17
17
|
<script>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @param {HTMLElement} el
|
|
4
|
+
* @param {HTMLElement} parent
|
|
5
|
+
*/
|
|
6
|
+
export const scrollElementIntoView = (el, parent) => {
|
|
7
|
+
// this works better than scrollIntoView
|
|
8
|
+
if (parent.scrollHeight <= parent.clientHeight) return;
|
|
9
|
+
|
|
10
|
+
const scrollBottom = parent.clientHeight + parent.scrollTop;
|
|
11
|
+
const elBottom = el.offsetTop + el.offsetHeight;
|
|
12
|
+
|
|
13
|
+
if (elBottom > scrollBottom) {
|
|
14
|
+
parent.scrollTop = elBottom - parent.clientHeight;
|
|
15
|
+
} else if (el.offsetTop < parent.scrollTop) {
|
|
16
|
+
parent.scrollTop = el.offsetTop;
|
|
17
|
+
}
|
|
18
|
+
};
|
package/src/utils/ids.js
CHANGED
|
@@ -11,24 +11,3 @@ export const genId = () => {
|
|
|
11
11
|
if (!gen) gen = idMaker();
|
|
12
12
|
return gen.next().value;
|
|
13
13
|
};
|
|
14
|
-
|
|
15
|
-
// Add leading zeros
|
|
16
|
-
// export const pad = (num, size) => {
|
|
17
|
-
// let s = num + '';
|
|
18
|
-
|
|
19
|
-
// while (s.length < size) {
|
|
20
|
-
// s = '0' + s;
|
|
21
|
-
// }
|
|
22
|
-
|
|
23
|
-
// return s;
|
|
24
|
-
// };
|
|
25
|
-
|
|
26
|
-
export const addZerosToId = (id) => {
|
|
27
|
-
let formattedId = null;
|
|
28
|
-
|
|
29
|
-
if (id < 10) formattedId = '000' + id;
|
|
30
|
-
else if (id < 100) formattedId = '00' + id;
|
|
31
|
-
else if (id < 1000) formattedId = '0' + id;
|
|
32
|
-
|
|
33
|
-
return '#' + formattedId;
|
|
34
|
-
};
|