@veritree/ui 0.21.1-2 → 0.21.1-4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veritree/ui",
3
- "version": "0.21.1-2",
3
+ "version": "0.21.1-4",
4
4
  "description": "veritree ui library",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -13,7 +13,7 @@
13
13
  "dependencies": {
14
14
  "@floating-ui/dom": "^1.0.4",
15
15
  "@linusborg/vue-simple-portal": "^0.1.5",
16
- "@veritree/icons": "^0.19.0"
16
+ "@veritree/icons": "^0.39.0"
17
17
  },
18
18
  "devDependencies": {}
19
19
  }
@@ -32,10 +32,6 @@ export default {
32
32
  return `dropdown-menu-content-${this.api().id}`;
33
33
  },
34
34
 
35
- dark() {
36
- return this.api().isDark;
37
- },
38
-
39
35
  headless() {
40
36
  return this.api().isHeadless;
41
37
  },
@@ -61,13 +57,13 @@ export default {
61
57
  e.stopPropagation();
62
58
 
63
59
  if (this.visible && !this.$el.contains(e.target)) {
64
- this.componentTrigger.onClick();
60
+ this.componentTrigger.cancel();
65
61
  }
66
62
  });
67
63
  },
68
64
 
69
65
  destroyed() {
70
- document.removeEventListener('click', this.componentTrigger.onClick);
66
+ document.removeEventListener('click', this.componentTrigger.cancel);
71
67
  },
72
68
 
73
69
  methods: {
@@ -88,8 +84,6 @@ export default {
88
84
  this.visible = false;
89
85
 
90
86
  this.$nextTick(() => {
91
- this.componentTrigger.focus();
92
- this.componentTrigger.hideExpanded();
93
87
  this.component.clearActive();
94
88
  this.$emit('hidden');
95
89
  });
@@ -87,10 +87,6 @@ export default {
87
87
  componentTrigger() {
88
88
  return this.api().componentTrigger;
89
89
  },
90
-
91
- componentContent() {
92
- return this.api().componentContent;
93
- },
94
90
  },
95
91
 
96
92
  mounted() {
@@ -169,12 +165,11 @@ export default {
169
165
  this.items[goToIndex].focus();
170
166
  },
171
167
 
172
- /**
173
- * Hides content/menu and focus on trigger
174
- */
175
168
  leaveMenu() {
176
- if (this.componentContent) this.componentContent.hide();
177
- if (this.componentTrigger) this.componentTrigger.focus();
169
+ if (this.componentTrigger) {
170
+ this.componentTrigger.cancel();
171
+ this.componentTrigger.focus();
172
+ }
178
173
  },
179
174
 
180
175
  onKeyEsc() {
@@ -4,8 +4,8 @@
4
4
  :aria-haspopup="hasPopup"
5
5
  :aria-expanded="expanded"
6
6
  :aria-controls="controls"
7
- @keydown.down.prevent="onKeyArrowDown"
8
- @keydown.up.prevent="onKeyArrowUp"
7
+ @keydown.down.prevent="onKeyDownOrUp"
8
+ @keydown.up.prevent="onKeyDownOrUp"
9
9
  @keydown.esc.stop="onKeyesc"
10
10
  >
11
11
  <slot></slot>
@@ -49,14 +49,20 @@ export default {
49
49
  },
50
50
  },
51
51
 
52
+ watch: {
53
+ expanded() {
54
+ this.toggleAriaHasPopup();
55
+ if (this.expanded) {
56
+ }
57
+ },
58
+ },
59
+
52
60
  mounted() {
53
61
  const trigger = {
54
- toggleExpanded: this.toggleExpanded,
55
- hideExpanded: this.hideExpanded,
62
+ id: this.id,
56
63
  el: this.$el,
64
+ cancel: this.cancel,
57
65
  focus: this.focus,
58
- id: this.id,
59
- onClick: this.onClick,
60
66
  };
61
67
 
62
68
  this.api().registerTrigger(trigger);
@@ -90,128 +96,88 @@ export default {
90
96
  });
91
97
  },
92
98
 
93
- /**
94
- * Shows content/menu if not already visible
95
- */
96
- showContent() {
97
- this.expanded = true;
98
- this.componentContent.show();
99
- },
99
+ init(e) {
100
+ if (!this.componentContent) {
101
+ return;
102
+ }
100
103
 
101
- /**
102
- * Focus slot element if it exists and toggle expanded
103
- */
104
- focus() {
105
- if (this.trigger) this.trigger.focus();
106
- },
104
+ if (this.expanded) {
105
+ this.cancel();
106
+ return;
107
+ }
107
108
 
108
- //
109
- toggleExpanded() {
110
- if (!this.expanded) return;
111
- this.expanded = false;
112
- },
109
+ this.expanded = true;
113
110
 
114
- /**
115
- * Sets aria expanded attribute/state to false
116
- */
117
- hideExpanded() {
118
- this.expanded = false;
111
+ // delay stop propagation to close other visible
112
+ // dropdowns and delay click event to control
113
+ // this dropdown visibility
114
+ setTimeout(() => e.stopImmediatePropagation(), 50);
115
+ setTimeout(() => this.showComponentContent(), 100);
119
116
  },
120
117
 
121
- /**
122
- * Toggles aria popup/controls attribute/state
123
- */
124
- toggleHasPopup() {
125
- if (!this.componentContent) return;
126
-
127
- this.hasPopup = !this.hasPopup;
128
-
129
- if (!this.hasPopup) {
130
- this.controls = null;
118
+ cancel() {
119
+ if (!this.componentContent) {
131
120
  return;
132
121
  }
133
122
 
134
- this.controls = this.componentContent.id;
123
+ this.expanded = false;
124
+
125
+ this.hideComponentContent();
135
126
  },
136
127
 
137
- /**
138
- * 1. Set aria expanded attribute/state to false
139
- * 2. Close the menu
140
- */
141
- hide() {
142
- this.hideExpanded();
128
+ focus() {
129
+ if (this.trigger) this.trigger.focus();
130
+ },
143
131
 
144
- this.$nextTick(() => {
145
- this.componentContent.hide();
146
- });
132
+ showComponentContent() {
133
+ this.componentContent.show();
147
134
  },
148
135
 
149
- /**
150
- * On click, do the following:
151
- *
152
- * 1. Toggle aria expanded attribute/state
153
- * 2. Open the menu if it's closed
154
- * 3. Close the menu if it's open
155
- */
156
- onClick(e) {
157
- if (!this.componentContent) return;
136
+ hideComponentContent() {
137
+ this.componentContent.hide();
138
+ },
158
139
 
140
+ toggleAriaHasPopup() {
159
141
  if (this.expanded) {
160
- this.componentContent.hide();
142
+ this.hasPopup = this.componentContent !== null;
143
+ this.controls = this.hasPopup ? this.componentContent.id : null;
144
+
161
145
  return;
162
146
  }
163
147
 
164
- // delay stop propagation to close other visible
165
- // dropdowns and delay click event to control
166
- // this dropdown visibility
167
- setTimeout(() => e.stopImmediatePropagation(), 50);
168
- setTimeout(() => this.showContent(), 100);
148
+ this.hasPopup = null;
149
+ this.controls = null;
169
150
  },
170
151
 
171
- /**
172
- * On key arrow down, do the following:
173
- *
174
- * 1. if the menu is not expanded, expand it and focus the first menu item
175
- * 2. if the menu is expanded, focus the first menu item
176
- */
177
- onKeyArrowDown() {
178
- if (!this.componentContent) return;
179
-
180
- this.showContent();
181
-
182
- // settimeout here is delaying the focusing the element
183
- // since it is not rendered yet. All items will only
184
- // be available when the content is fully visible.
185
- this.$nextTick(() => {
186
- setTimeout(() => this.firstMenuItem.focus(), 150);
187
- });
152
+ onClick(e) {
153
+ this.init(e);
188
154
  },
189
155
 
190
- /**
191
- * On key arrow up, do the following:
192
- *
193
- * 1. if the menu is not expanded, expand it and focus the last menu item
194
- * 2. if the menu is expanded, focus the last menu item
195
- */
196
- onKeyArrowUp() {
197
- if (!this.componentContent) return;
156
+ onKeyDownOrUp(e) {
157
+ if (!this.expanded) {
158
+ this.$el.click(e);
159
+ }
198
160
 
199
- this.showContent();
161
+ const keyCode = e.code;
162
+ const listItemPosition =
163
+ keyCode === 'ArrowDown'
164
+ ? 'firstMenuItem'
165
+ : keyCode === 'ArrowUp'
166
+ ? 'lastMenuItem'
167
+ : null;
200
168
 
201
169
  // settimeout here is delaying the focusing the element
202
170
  // since it is not rendered yet. All items will only
203
171
  // be available when the content is fully visible.
204
172
  this.$nextTick(() => {
205
- setTimeout(() => this.lastMenuItem.focus(), 150);
173
+ setTimeout(() => this[listItemPosition].focus(), 100);
206
174
  });
207
175
  },
208
176
 
209
- onKeyesc() {
210
- if (!this.componentContent) return;
211
-
212
- if (this.expanded) {
213
- this.componentContent.hide();
214
- }
177
+ // change it to a better name or move the methods inside to another function
178
+ onKeyEsc() {
179
+ this.cancel();
180
+ this.focus();
215
181
  },
216
182
  },
217
183
  };
@@ -4,7 +4,7 @@
4
4
  :class="[
5
5
  headless
6
6
  ? 'form-control'
7
- : 'border border-solid py-2 px-3 rounded text-inherit',
7
+ : 'border border-solid py-2 px-3 rounded text-inherit max-w-full',
8
8
  headless
9
9
  ? `form-control--${variant}`
10
10
  : isError
@@ -33,14 +33,14 @@ export default {
33
33
  type: [String, Number, Object, Array],
34
34
  default: null,
35
35
  },
36
- variant: {
37
- type: [String, Object, Function],
38
- default: '',
39
- },
40
36
  headless: {
41
37
  type: Boolean,
42
38
  default: false,
43
39
  },
40
+ variant: {
41
+ type: [String, Object, Function],
42
+ default: '',
43
+ },
44
44
  },
45
45
 
46
46
  computed: {
@@ -57,10 +57,6 @@ export default {
57
57
  componentTrigger() {
58
58
  return this.api().componentTrigger;
59
59
  },
60
-
61
- search() {
62
- return this.api().search;
63
- },
64
60
  },
65
61
 
66
62
  mounted() {
@@ -82,36 +78,37 @@ export default {
82
78
  e.stopPropagation();
83
79
 
84
80
  if (this.visible && !this.$el.contains(e.target)) {
85
- this.componentTrigger.onClick();
81
+ this.componentTrigger.cancel();
86
82
  }
87
83
  });
88
84
  },
89
85
 
90
86
  destroyed() {
91
87
  // T-162 Create a directive or mixin for this
92
- document.removeEventListener('click', this.componentTrigger.onClick);
88
+ document.removeEventListener('click', this.componentTrigger.cancel);
93
89
  },
94
90
 
95
91
  methods: {
96
92
  show() {
97
- if (this.visible) return;
93
+ if (this.visible) {
94
+ return;
95
+ }
98
96
 
99
97
  this.visible = true;
100
98
 
101
99
  this.$nextTick(() => {
102
100
  this.component.setActive();
103
- if (this.search) this.search.el.focus();
104
101
  });
105
102
  },
106
103
 
107
104
  hide() {
108
- if (!this.visible) return;
105
+ if (!this.visible) {
106
+ return;
107
+ }
109
108
 
110
109
  this.visible = false;
111
110
 
112
111
  this.$nextTick(() => {
113
- this.componentTrigger.focus();
114
- this.componentTrigger.toggleExpanded();
115
112
  this.component.clearActive();
116
113
  });
117
114
  },
@@ -196,7 +196,10 @@ export default {
196
196
  * Hides componentContent/menu and focus on componentTrigger
197
197
  */
198
198
  leaveMenu() {
199
- if (this.componentContent) this.componentContent.hide();
199
+ if (this.componentTrigger) {
200
+ this.componentTrigger.cancel();
201
+ this.componentTrigger.focus();
202
+ }
200
203
  },
201
204
 
202
205
  onKeyEsc() {
@@ -4,6 +4,7 @@
4
4
  :class="{ 'listbox-search': headless, 'form-control mb-1': !headless }"
5
5
  type="text"
6
6
  @input="onChange"
7
+ @click.stop
7
8
  @keydown.down.prevent="focusNextItem"
8
9
  @keydown.up.prevent="focusPreviousItem"
9
10
  @keydown.home.prevent="focusFirstItem"
@@ -35,8 +36,8 @@ export default {
35
36
  },
36
37
 
37
38
  computed: {
38
- componentContent() {
39
- return this.api().componentContent;
39
+ componentTrigger() {
40
+ return this.api().componentTrigger;
40
41
  },
41
42
 
42
43
  list() {
@@ -58,6 +59,7 @@ export default {
58
59
  };
59
60
 
60
61
  this.api().registerSearch(search);
62
+ this.$nextTick(() => setTimeout(() => this.$el.focus(), 150));
61
63
  },
62
64
 
63
65
  beforeDestroy() {
@@ -127,7 +129,10 @@ export default {
127
129
  },
128
130
 
129
131
  hide() {
130
- if (this.componentContent) this.componentContent.hide();
132
+ if (!this.componentTrigger) {
133
+ this.componentTrigger.cancel();
134
+ this.componentTrigger.focus();
135
+ }
131
136
  },
132
137
  },
133
138
  };
@@ -3,18 +3,20 @@
3
3
  :id="id"
4
4
  :aria-expanded="expanded"
5
5
  :aria-haspopup="hasPopup"
6
- :class="{
7
- 'listbox-button': headless,
8
- 'flex w-full justify-between rounded-md border border-solid py-2 px-3':
9
- !headless,
10
- 'border-gray-300 text-gray-500': !dark && !headless,
11
- 'border-white/70 text-white focus-visible:ring-2 focus-visible:ring-white':
12
- dark && !headless,
13
- }"
6
+ :class="[
7
+ headless
8
+ ? 'listbox-button'
9
+ : 'flex w-full justify-between border border-solid py-2 px-3 rounded text-inherit max-w-full',
10
+ headless
11
+ ? `listbox-button--${variant}`
12
+ : isError
13
+ ? 'border-error-300'
14
+ : 'border-gray-300',
15
+ ]"
14
16
  type="button"
15
17
  @click.prevent="onClick"
16
- @keydown.down.prevent="onKeyArrowDown"
17
- @keydown.up.prevent="onKeyArrowUp"
18
+ @keydown.down.prevent="onKeyDownOrUp"
19
+ @keydown.up.prevent="onKeyDownOrUp"
18
20
  @keydown.esc.stop="onKeyEsc"
19
21
  >
20
22
  <span
@@ -49,10 +51,18 @@ export default {
49
51
  inject: ['api'],
50
52
 
51
53
  props: {
54
+ disabled: {
55
+ type: Boolean,
56
+ default: false,
57
+ },
52
58
  headless: {
53
59
  type: Boolean,
54
60
  default: false,
55
61
  },
62
+ variant: {
63
+ type: [String, Object, Function],
64
+ default: '',
65
+ },
56
66
  },
57
67
 
58
68
  data() {
@@ -67,8 +77,8 @@ export default {
67
77
  return `listbox-trigger-${this.api().id}`;
68
78
  },
69
79
 
70
- dark() {
71
- return this.api().isDark;
80
+ isError() {
81
+ return this.variant === 'error';
72
82
  },
73
83
 
74
84
  componentContent() {
@@ -90,101 +100,85 @@ export default {
90
100
 
91
101
  mounted() {
92
102
  const trigger = {
93
- toggleExpanded: this.toggleExpanded,
94
103
  el: this.$el,
104
+ cancel: this.cancel,
95
105
  focus: this.focus,
96
106
  id: this.id,
97
- onClick: this.onClick,
98
107
  };
99
108
 
100
109
  this.api().registerTrigger(trigger);
101
110
  },
102
111
 
103
112
  methods: {
104
- /**
105
- * Shows content/menu if not already visible
106
- */
107
- showContent() {
108
- this.expanded = true;
109
- this.componentContent.show();
110
- },
111
-
112
- focus() {
113
- this.$el.focus();
114
- },
115
-
116
- toggleExpanded() {
117
- if (!this.expanded) return;
118
- this.expanded = false;
119
- },
120
-
121
- /**
122
- * On click, do the following:
123
- *
124
- * 1. Toggle aria expanded attribute/state
125
- * 2. Open the menu if it's closed
126
- * 3. Close the menu if it's open
127
- */
128
- onClick(e) {
129
- if (!this.componentContent) return;
113
+ init(e) {
114
+ if (!this.componentContent) {
115
+ return;
116
+ }
130
117
 
131
118
  if (this.expanded) {
132
- this.componentContent.hide();
119
+ this.cancel();
133
120
  return;
134
121
  }
135
122
 
123
+ this.expanded = true;
124
+
136
125
  // delay stop propagation to close other visible
137
126
  // dropdowns and delay click event to control
138
127
  // this dropdown visibility
139
128
  setTimeout(() => e.stopImmediatePropagation(), 50);
140
- setTimeout(() => this.showContent(), 100);
129
+ setTimeout(() => this.showComponentContent(), 100);
141
130
  },
142
131
 
143
- /**
144
- * On key arrow down, do the following:
145
- *
146
- * 1. if the menu is not expanded, expand it and focus the first menu item
147
- * 2. if the menu is expanded, focus the first menu item
148
- */
149
- onKeyArrowDown() {
150
- if (!this.componentContent) return;
132
+ cancel() {
133
+ if (!this.componentContent) {
134
+ return;
135
+ }
136
+ this.expanded = false;
151
137
 
152
- this.showContent();
138
+ this.hideComponentContent();
139
+ },
153
140
 
154
- // settimeout here is delaying the focusing the element
155
- // since it is not rendered yet. All items will only
156
- // be available when the content is fully visible.
157
- this.$nextTick(() => {
158
- setTimeout(() => this.firstMenuItem.focus(), 150);
159
- });
141
+ focus() {
142
+ this.$el.focus();
160
143
  },
161
144
 
162
- /**
163
- * On key arrow up, do the following:
164
- *
165
- * 1. if the menu is not expanded, expand it and focus the last menu item
166
- * 2. if the menu is expanded, focus the last menu item
167
- */
168
- onKeyArrowUp() {
169
- if (!this.componentContent) return;
145
+ showComponentContent() {
146
+ this.componentContent.show();
147
+ },
170
148
 
171
- this.showContent();
149
+ hideComponentContent() {
150
+ this.componentContent.hide();
151
+ },
152
+
153
+ onClick(e) {
154
+ this.init(e);
155
+ },
156
+
157
+ onKeyDownOrUp(e) {
158
+ if (!this.expanded) {
159
+ this.$el.click(e);
160
+ }
161
+
162
+ const keyCode = e.code;
163
+ const listItemPosition =
164
+ keyCode === 'ArrowDown'
165
+ ? 'firstMenuItem'
166
+ : keyCode === 'ArrowUp'
167
+ ? 'lastMenuItem'
168
+ : null;
172
169
 
173
170
  // settimeout here is delaying the focusing the element
174
171
  // since it is not rendered yet. All items will only
175
172
  // be available when the content is fully visible.
176
173
  this.$nextTick(() => {
177
- setTimeout(() => this.lastMenuItem.focus(), 150);
174
+ setTimeout(() => this[listItemPosition].focus(), 100);
178
175
  });
179
176
  },
180
177
 
178
+ // change it to a better name or move the methods inside to another function
181
179
  onKeyEsc() {
182
- if (!this.componentContent) return;
183
-
184
- if (this.expanded) {
185
- this.toggleExpanded();
186
- this.componentContent.hide();
187
- }
180
+ this.cancel();
181
+ this.focus();
188
182
  },
189
183
  },
190
184
  };