@natachah/vanilla-frontend 0.1.17 → 0.1.19

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.
@@ -94,12 +94,13 @@
94
94
  --anchor-decoration
95
95
  --anchor-color
96
96
  --anchor-hover-color
97
+ --anchor-focus-color
97
98
  --anchor-active-color
98
99
  --anchor-disabled-opacity
99
- --anchor-focus-size
100
- --anchor-focus-style
101
- --anchor-focus-color
102
- --anchor-focus-offset
100
+ --anchor-outline-size
101
+ --anchor-outline-style
102
+ --anchor-outline-color
103
+ --anchor-outline-offset
103
104
  </doc-code>
104
105
  </div>
105
106
 
@@ -42,13 +42,16 @@
42
42
  --button-padding-block
43
43
  --button-transition
44
44
  --button-decoration
45
- --button-focus-size
46
- --button-focus-style
47
- --button-focus-color
48
- --button-focus-offset
45
+ --button-outline-size
46
+ --button-outline-style
47
+ --button-outline-color
48
+ --button-outline-offset
49
49
  --button-hover-color
50
50
  --button-hover-background
51
51
  --button-hover-border-color
52
+ --button-focus-color
53
+ --button-focus-background
54
+ --button-focus-border-color
52
55
  --button-active-color
53
56
  --button-active-background
54
57
  --button-active-border-color
@@ -211,7 +211,7 @@
211
211
  <tbody>
212
212
  <tr>
213
213
  <td data-label="Method">
214
- <p>open()</p>
214
+ <p>open(e)</p>
215
215
  </td>
216
216
  <td data-label="Description">
217
217
  <p>This method will open the dialog</p>
@@ -65,13 +65,16 @@
65
65
  --disclosure-padding-block
66
66
  --disclosure-transition
67
67
  --disclosure-decoration
68
- --disclosure-focus-size
69
- --disclosure-focus-style
70
- --disclosure-focus-color
71
- --disclosure-focus-offset
68
+ --disclosure-outline-size
69
+ --disclosure-outline-style
70
+ --disclosure-outline-color
71
+ --disclosure-outline-offset
72
72
  --disclosure-hover-color
73
73
  --disclosure-hover-background
74
74
  --disclosure-hover-border-color
75
+ --disclosure-focus-color
76
+ --disclosure-focus-background
77
+ --disclosure-focus-border-color
75
78
  --disclosure-active-color
76
79
  --disclosure-active-background
77
80
  --disclosure-active-border-color
@@ -130,7 +133,7 @@
130
133
  </doc-code>
131
134
  <doc-code id="scss" data-type="scss" role="tabpanel">
132
135
  $outline-variations: (
133
- disclosure
136
+ disclosure
134
137
  );
135
138
  </doc-code>
136
139
  </div>
@@ -224,7 +227,7 @@
224
227
  </doc-code>
225
228
  <doc-code id="scss" data-type="scss" role="tabpanel">
226
229
  $color-variations: (
227
- disclosure: (primary)
230
+ disclosure: (primary)
228
231
  );
229
232
  </doc-code>
230
233
  </div>
@@ -69,13 +69,16 @@
69
69
  --dropdown-padding-block
70
70
  --dropdown-transition
71
71
  --dropdown-decoration
72
- --dropdown-focus-size
73
- --dropdown-focus-style
74
- --dropdown-focus-color
75
- --dropdown-focus-offset
72
+ --dropdown-outline-size
73
+ --dropdown-outline-style
74
+ --dropdown-outline-color
75
+ --dropdown-outline-offset
76
76
  --dropdown-hover-color
77
77
  --dropdown-hover-background
78
78
  --dropdown-hover-border-color
79
+ --dropdown-focus-color
80
+ --dropdown-focus-background
81
+ --dropdown-focus-border-color
79
82
  --dropdown-active-color
80
83
  --dropdown-active-background
81
84
  --dropdown-active-border-color
@@ -47,10 +47,10 @@
47
47
  --form-padding-block
48
48
  --form-transition
49
49
  --form-decoration
50
- --form-focus-size
51
- --form-focus-style
52
- --form-focus-color
53
- --form-focus-offset
50
+ --form-outline-size
51
+ --form-outline-style
52
+ --form-outline-color
53
+ --form-outline-offset
54
54
  --form-disabled-opacity
55
55
  </doc-code>
56
56
  </div>
@@ -291,16 +291,55 @@
291
291
  <p>As every design is different, there is no default style for in/valid elements</p>
292
292
  </blockquote>
293
293
 
294
- <doc-code>
295
- <input type="text" aria-describedby="invalidMsg" aria-invalid="true" value="My bad value">
296
- <small id="invalidMsg">
297
- <span aria-live="assertive">Incorrect value !</span>
298
- </small>
299
- <input type="text" aria-describedby="validMsg" aria-invalid="false" value="My good value">
294
+ <doc-demo>
295
+ <div>
296
+ <label for="invalidInput">Invalid input</label>
297
+ <input id="invalidInput" type="text" aria-describedby="invalidMsg" aria-invalid="true" value="My bad value">
298
+ <small id="invalidMsg">
299
+ <span aria-live="assertive">Incorrect value !</span>
300
+ </small>
301
+ </div>
302
+ <label for="validInput">Valid input</label>
303
+ <input id="validInput" type="text" aria-describedby="validMsg" aria-invalid="false" value="My good value">
300
304
  <small id="validMsg">
301
305
  <span aria-live="assertive">All good !</span>
302
306
  </small>
303
- </doc-code>
307
+ </doc-demo>
308
+
309
+ <div class="code-group">
310
+ <div role="tablist">
311
+ <button role="tab" aria-controls="html">HTML</button>
312
+ <button role="tab" aria-controls="scss">SCSS</button>
313
+ </div>
314
+ <doc-code id="html" data-type="html" role="tabpanel">
315
+ <label for="invalidInput">Invalid input</label>
316
+ <input id="invalidInput" type="text" aria-describedby="invalidMsg" aria-invalid="true" value="My bad value">
317
+ <small id="invalidMsg">
318
+ <span aria-live="assertive">Incorrect value !</span>
319
+ </small>
320
+ <label for="validInput">Valid input</label>
321
+ <input id="validInput" type="text" aria-describedby="validMsg" aria-invalid="false" value="My good value">
322
+ <small id="validMsg">
323
+ <span aria-live="assertive">All good !</span>
324
+ </small>
325
+ </doc-code>
326
+ <doc-code id="scss" data-type="css" role="tabpanel">
327
+ form {
328
+ div:has([aria-invalid=true]) {
329
+
330
+ --form-border-color: var(--color-error);
331
+
332
+ > label,
333
+ > small {
334
+ color: var(--color-error);
335
+ }
336
+
337
+ }
338
+ }
339
+ </doc-code>
340
+ </div>
341
+
342
+
304
343
 
305
344
  <h2>Variants</h2>
306
345
  <h3>Group</h3>
@@ -88,13 +88,16 @@
88
88
  <doc-code id="css" data-type="css" role="tabpanel">
89
89
  --list-transition
90
90
  --list-decoration
91
- --list-focus-size
92
- --list-focus-style
93
- --list-focus-color
94
- --list-focus-offset
91
+ --list-outline-size
92
+ --list-outline-style
93
+ --list-outline-color
94
+ --list-outline-offset
95
95
  --list-hover-color
96
96
  --list-hover-background
97
97
  --list-hover-border-color
98
+ --list-focus-color
99
+ --list-focus-background
100
+ --list-focus-border-color
98
101
  --list-active-color
99
102
  --list-active-background
100
103
  --list-active-border-color
@@ -198,7 +198,25 @@
198
198
 
199
199
  <h2>Javascript</h2>
200
200
  <p>To enable this component you need to import the javascript file and create a new Tree object.</p>
201
-
201
+ <h3>Methods</h3>
202
+ <table>
203
+ <thead>
204
+ <tr>
205
+ <th>Method</th>
206
+ <th>Description</th>
207
+ </tr>
208
+ </thead>
209
+ <tbody>
210
+ <tr>
211
+ <td data-label="Method">
212
+ <p>resetEvents()</p>
213
+ </td>
214
+ <td data-label="Description">
215
+ <p>This method will reset the items and events</p>
216
+ </td>
217
+ </tr>
218
+ </tbody>
219
+ </table>
202
220
  <h3>Events</h3>
203
221
  <table>
204
222
  <thead>
@@ -55,11 +55,11 @@
55
55
  --border-style: solid;
56
56
  --border-radius: .25rem;
57
57
 
58
- // Focus (outline)
59
- --focus-size: 3px;
60
- --focus-style: solid;
61
- --focus-offset: 0;
62
- --focus-opacity: 50%;
58
+ // Outline (:focus)
59
+ --outline-size: 3px;
60
+ --outline-style: solid;
61
+ --outline-offset: 0;
62
+ --outline-opacity: 50%;
63
63
 
64
64
  // Hover (color-mix)
65
65
  --hover-color: black;
@@ -84,12 +84,26 @@
84
84
  --color-warning-contrast: black;
85
85
 
86
86
  // Icons
87
- --icon-date: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="black" viewBox="0 0 16 16"><path d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z" /></svg>');
88
- --icon-time: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71V3.5z" /><path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0z" /></svg>');
89
- --icon-file: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z" /><path d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708l3-3z" /></svg>');
90
- --icon-select: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z" /></svg>');
91
- --icon-radio: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" viewBox="0 0 16 16"><circle cx="8" cy="8" r="8" /></svg>');
92
- --icon-check: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" viewBox="0 0 16 16"><path d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.267.267 0 0 1 .02-.022z" /></svg>');
87
+ --icon-date: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="black" viewBox="0 0 16 16">
88
+ <path d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z" />
89
+ </svg>');
90
+ --icon-time: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
91
+ <path d="M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71V3.5z" />
92
+ <path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0z" />
93
+ </svg>');
94
+ --icon-file: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
95
+ <path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z" />
96
+ <path d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708l3-3z" />
97
+ </svg>');
98
+ --icon-select: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
99
+ <path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z" />
100
+ </svg>');
101
+ --icon-radio: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" viewBox="0 0 16 16">
102
+ <circle cx="8" cy="8" r="8" />
103
+ </svg>');
104
+ --icon-check: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" viewBox="0 0 16 16">
105
+ <path d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.267.267 0 0 1 .02-.022z" />
106
+ </svg>');
93
107
  --icon-switch: var(--icon-radio);
94
108
 
95
109
  }
@@ -102,20 +116,20 @@
102
116
  // This is the light theme (or if there is none)
103
117
  html[data-theme=light],
104
118
  html:not([data-theme]) {
105
- --color-body: white;
106
- --color-font: black;
119
+ --color-body: white;
120
+ --color-font: black;
107
121
  }
108
122
 
109
123
  // This is for the dark theme
110
124
  html[data-theme=dark] {
111
- --color-body: black;
112
- --color-font: white;
125
+ --color-body: black;
126
+ --color-font: white;
113
127
  }
114
128
 
115
129
  // This is for the dark theme
116
130
  html[data-theme=my-custom-theme] {
117
- --color-body: white;
118
- --color-font: orange;
131
+ --color-body: white;
132
+ --color-font: orange;
119
133
  }
120
134
  </doc-code>
121
135
 
@@ -18,7 +18,7 @@ class DocLayout extends HTMLElement {
18
18
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pin-angle" viewBox="0 0 16 16">
19
19
  <path d="M9.828.722a.5.5 0 0 1 .354.146l4.95 4.95a.5.5 0 0 1 0 .707c-.48.48-1.072.588-1.503.588-.177 0-.335-.018-.46-.039l-3.134 3.134a6 6 0 0 1 .16 1.013c.046.702-.032 1.687-.72 2.375a.5.5 0 0 1-.707 0l-2.829-2.828-3.182 3.182c-.195.195-1.219.902-1.414.707s.512-1.22.707-1.414l3.182-3.182-2.828-2.829a.5.5 0 0 1 0-.707c.688-.688 1.673-.767 2.375-.72a6 6 0 0 1 1.013.16l3.134-3.133a3 3 0 0 1-.04-.461c0-.43.108-1.022.589-1.503a.5.5 0 0 1 .353-.146m.122 2.112v-.002zm0-.002v.002a.5.5 0 0 1-.122.51L6.293 6.878a.5.5 0 0 1-.511.12H5.78l-.014-.004a5 5 0 0 0-.288-.076 5 5 0 0 0-.765-.116c-.422-.028-.836.008-1.175.15l5.51 5.509c.141-.34.177-.753.149-1.175a5 5 0 0 0-.192-1.054l-.004-.013v-.001a.5.5 0 0 1 .12-.512l3.536-3.535a.5.5 0 0 1 .532-.115l.096.022c.087.017.208.034.344.034q.172.002.343-.04L9.927 2.028q-.042.172-.04.343a1.8 1.8 0 0 0 .062.46z"/>
20
20
  </svg>
21
- 0.1.17
21
+ 0.1.19
22
22
  </span>
23
23
  </li>
24
24
  <li>
@@ -19,6 +19,10 @@ doc-layout {
19
19
  overflow: hidden;
20
20
  transition: all .5s ease-in-out;
21
21
 
22
+ :focus {
23
+ outline: 3px dashed pink;
24
+ }
25
+
22
26
  > header {
23
27
  grid-area: header;
24
28
  }
package/js/_dialog.js CHANGED
@@ -36,6 +36,8 @@ export default class Dialog extends BaseComponent {
36
36
 
37
37
  this._form = this._element.querySelector('form') ?? {}
38
38
 
39
+ this._previousFocus = null
40
+
39
41
  // Init the event listener
40
42
  this.#init()
41
43
 
@@ -49,7 +51,7 @@ export default class Dialog extends BaseComponent {
49
51
  #init() {
50
52
 
51
53
  // OPEN
52
- this._buttons.open.forEach((button) => button.addEventListener('click', () => this.open()))
54
+ this._buttons.open.forEach((button) => button.addEventListener('click', (e) => this.open(e)))
53
55
 
54
56
  // CLOSE
55
57
  this._buttons.close.forEach((button) => button.addEventListener('click', () => this.close()))
@@ -79,7 +81,7 @@ export default class Dialog extends BaseComponent {
79
81
  * Open the dialog
80
82
  *
81
83
  */
82
- open() {
84
+ open(e) {
83
85
 
84
86
  // Run event before opening, and stop if e.preventDefault()
85
87
  const callback = this.emmitEvent('opening')
@@ -88,6 +90,11 @@ export default class Dialog extends BaseComponent {
88
90
  // Open the dialog (the methode changed if it's a modal)
89
91
  this._isModal ? this._element.showModal() : this._element.show()
90
92
 
93
+ // If open via an HTML element, keep it as previous to reset the focus
94
+ if (e && e.target instanceof HTMLElement) {
95
+ this._previousFocus = e.target
96
+ }
97
+
91
98
  // Toggle the [inert] attribute on the body
92
99
  this.#toggleBodyAttribute()
93
100
 
@@ -112,6 +119,9 @@ export default class Dialog extends BaseComponent {
112
119
  // Toggle the [inert] attribute on the body
113
120
  this.#toggleBodyAttribute()
114
121
 
122
+ // Reset the focus on the previous element
123
+ if (this._previousFocus) this._previousFocus.focus()
124
+
115
125
  // Run event after closed
116
126
  this.emmitEvent('closed')
117
127
 
@@ -148,6 +158,9 @@ export default class Dialog extends BaseComponent {
148
158
  // Toggle the [inert] attribute on the body
149
159
  this.#toggleBodyAttribute()
150
160
 
161
+ // Reset the focus on the previous element
162
+ if (this._previousFocus) this._previousFocus.focus()
163
+
151
164
  // Run event after submited
152
165
  this.emmitEvent('submited', { form: this._form })
153
166
 
package/js/_drawer.js CHANGED
@@ -45,7 +45,7 @@ export default class Drawer extends BaseComponent {
45
45
 
46
46
  this._isOpen = !this._element.hidden
47
47
 
48
- this._focus = this._element.querySelector('[tabindex="0"]') ?? this._element.querySelector('a,button,input') ?? null
48
+ this._focus = this._element.getAttribute('tabindex') === '0' ? this._element : this._element.querySelector('[tabindex="0"]') ?? this._element.querySelector('button, a, input')
49
49
 
50
50
  this._cookie = this._options.cookie ? new Cookie(this._options.cookie) : null
51
51
 
package/js/_dropdown.js CHANGED
@@ -37,7 +37,7 @@ export default class Dropdown extends BaseComponent {
37
37
  // Define the properties
38
38
  this._button = this._element.querySelector('[aria-controls]')
39
39
  this._content = document.getElementById(this._button.getAttribute('aria-controls'))
40
- this._focus = this._element.querySelector('[tabindex="0"]') ?? this._element.querySelector('a,button,input') ?? null
40
+ this._focus = this._content.getAttribute('tabindex') === '0' ? this._content : this._content.querySelector('[tabindex="0"]') ?? this._content.querySelector('button, a, input')
41
41
 
42
42
  // Init the event listener
43
43
  this.#init()
package/js/_sortable.js CHANGED
@@ -29,7 +29,8 @@ export default class Sortable extends BaseComponent {
29
29
  this._withHandle = this._element.querySelector('[data-handle=sortable]') ? true : false
30
30
  this._current = null
31
31
 
32
- // Lier une seule fois les méthodes à `this`
32
+ // Bind this only one time foreach methods
33
+ // * Because I add and remove events
33
34
  this.handleMouseDown = this.handleMouseDown.bind(this)
34
35
  this.handleMouseUp = this.handleMouseUp.bind(this)
35
36
  this.drag = this.drag.bind(this)
@@ -74,9 +75,6 @@ export default class Sortable extends BaseComponent {
74
75
  */
75
76
  #initEvents() {
76
77
 
77
- // Get the items
78
- //this._items = this._element.querySelectorAll('[draggable]')
79
-
80
78
  // Add the events
81
79
  this._items.forEach(item => {
82
80
  item.addEventListener('mousedown', this.handleMouseDown)
package/js/_toggle.js CHANGED
@@ -103,7 +103,10 @@ export default class Toggle extends BaseComponent {
103
103
  // Toggle the [hidden] attribute
104
104
  toggable.hidden = this.value !== toggleValue && groupValue !== toggleValue
105
105
 
106
- if (!toggable.hidden && toggable.hasAttribute('tabindex')) toggable.focus()
106
+ if (!toggable.hidden) {
107
+ const focus = toggable.getAttribute('tabindex') === '0' ? toggable : toggable.querySelector('[tabindex="0"]') ?? toggable.querySelector('button, a, input')
108
+ if (focus) focus.focus()
109
+ }
107
110
 
108
111
  })
109
112
 
package/js/_tree.js CHANGED
@@ -29,7 +29,9 @@ export default class Tree extends BaseComponent {
29
29
 
30
30
  this._withHandle = this._type == 'grid' && this._element.querySelector('[data-handle=tree]') ? true : false
31
31
 
32
- this._items = this._element.querySelectorAll('[aria-expanded]')
32
+ // Bind this only one time foreach methods
33
+ // * Because I add and remove events
34
+ this.toggle = this.toggle.bind(this)
33
35
 
34
36
  // Init the event listener
35
37
  this.#init()
@@ -43,12 +45,45 @@ export default class Tree extends BaseComponent {
43
45
  */
44
46
  #init() {
45
47
 
46
- this._items.forEach(item => item.addEventListener('click', (e) => {
47
- if (!this._withHandle || (e.target.hasAttribute('data-handle') && e.target.getAttribute('data-handle') === 'tree')) {
48
- e.stopPropagation()
49
- this.toggle(item)
50
- }
51
- }))
48
+ // Init the events on the items
49
+ this.#initEvents()
50
+
51
+ }
52
+
53
+ /**
54
+ * Get the list of the items
55
+ *
56
+ * @return {object}
57
+ */
58
+ get _items() {
59
+ return this._element.querySelectorAll('[aria-expanded]')
60
+ }
61
+
62
+ /**
63
+ * Init the items and the event listeners
64
+ *
65
+ * @private
66
+ */
67
+ #initEvents() {
68
+ this._items.forEach(item => {
69
+ item.addEventListener('click', this.toggle)
70
+ })
71
+ }
72
+
73
+ /**
74
+ * Reset the event listeners
75
+ *
76
+ * @private
77
+ */
78
+ resetEvents() {
79
+
80
+ // Remove the listeners
81
+ this._items.forEach((item) => {
82
+ item.removeEventListener('click', this.toggle)
83
+ })
84
+
85
+ // Re-initialise the items
86
+ this.#initEvents()
52
87
 
53
88
  }
54
89
 
@@ -57,7 +92,21 @@ export default class Tree extends BaseComponent {
57
92
  *
58
93
  * @param {HTMLElement} item - The current item
59
94
  */
60
- toggle(item) {
95
+ toggle(e) {
96
+
97
+ let item
98
+
99
+ if (e instanceof Event) {
100
+ // If it's an event verify about the handle + if target is an HTML element
101
+ if (!(e.target instanceof Element)) return
102
+ if (this._withHandle && (!e.target.hasAttribute('data-handle') || e.target.getAttribute('data-handle') !== 'tree')) return
103
+ e.stopPropagation()
104
+ item = e.target.closest('[aria-expanded]')
105
+ } else {
106
+ // If it's NOT an event verify that it's an HTML element
107
+ if (!(e instanceof Element)) return
108
+ item = e
109
+ }
61
110
 
62
111
  // Check for errors
63
112
  if (!(item instanceof HTMLElement)) throw new Error(ErrorMessage.instanceOf('item', 'HTMLElement'))
@@ -74,8 +123,36 @@ export default class Tree extends BaseComponent {
74
123
 
75
124
  // Set the focus on the required child
76
125
  if (!isExpanded) {
77
- const childToFocus = children.find(el => el.hasAttribute('tabindex')) ?? null
78
- if (childToFocus) childToFocus.focus()
126
+
127
+ let childToFocus = null
128
+
129
+ const child = children.find(el => {
130
+
131
+ // Check if the child have a tabindex
132
+ if (el.getAttribute('tabindex') === '0') {
133
+ childToFocus = el
134
+ return el
135
+ }
136
+
137
+ // Otherwise if an element inside the child have the tabindex
138
+ const childWithTabIndex = el.querySelector('[tabindex="0"]')
139
+ if (childWithTabIndex) {
140
+ childToFocus = childWithTabIndex
141
+ return childWithTabIndex
142
+ }
143
+
144
+ // Otherwise get the first focusable element
145
+ childToFocus = el.querySelector('button, a, input')
146
+ return el
147
+
148
+ })
149
+
150
+ // Add the focus if open
151
+ // * Need to wait the transition before make it focused
152
+ if (child && childToFocus) {
153
+ childToFocus.focus()
154
+ }
155
+
79
156
  }
80
157
 
81
158
  // If type grid, collapse the subchildren
@@ -26,7 +26,7 @@ beforeAll(() => {
26
26
  document.body.innerHTML =
27
27
  '<div id="backdrop"></div>' +
28
28
  '<button aria-expanded="false" aria-pressed="false" aria-controls="drawer"></button>' +
29
- '<aside id="drawer" tabindex="0" hidden><button id="focus"></button></aside>'
29
+ '<aside id="drawer" hidden><button id="focus"></button></aside>'
30
30
 
31
31
  Object.defineProperty(window, 'innerWidth', { configurable: true, value: 1200 })
32
32
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@natachah/vanilla-frontend",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "A vanilla frontend framework",
5
5
  "keywords": [
6
6
  "html5",
@@ -35,6 +35,9 @@
35
35
  $default-padding-inline: var(--#{$name}-padding-inline, map.get($properties, padding-inline));
36
36
  $default-padding-block: var(--#{$name}-padding-block, map.get($properties, padding-block));
37
37
 
38
+ $default-hover-background: color-mix(in srgb, $default-background, var(--hover-color) var(--hover-percent));
39
+ $default-active-background: color-mix(in srgb, $default-background, var(--active-color) var(--active-percent));
40
+
38
41
  // Define the CSS
39
42
  color: $default-color;
40
43
  background-color: $default-background;
@@ -59,23 +62,28 @@
59
62
  // Define the interactive states
60
63
  @if($states !=()) {
61
64
 
62
- // State: focus
63
- @if(list.index($states, 'focus')) {
64
- &:focus {
65
- outline: var(--#{$name}-focus-size, var(--focus-size)) var(--#{$name}-focus-style, var(--focus-style)) var(--#{$name}-focus-color, color-mix(in srgb, var(--#{$name}-border-color, var(--#{$name}-background, $default-color)), transparent var(--focus-opacity)));
66
- outline-offset: var(--#{$name}-focus-offset, var(--focus-offset));
67
- }
68
- }
69
-
70
65
  // State: hover
71
66
  @if(list.index($states, 'hover')) {
72
67
  &:hover {
73
68
  color: var(--#{$name}-hover-color, $default-color);
74
- background-color: var(--#{$name}-hover-background, color-mix(in srgb, $default-background, var(--hover-color) var(--hover-percent)));
69
+ background-color: var(--#{$name}-hover-background, $default-hover-background);
75
70
  border-color: var(--#{$name}-hover-border-color, $default-border-color);
76
71
  }
77
72
  }
78
73
 
74
+ // State: focus
75
+ @if(list.index($states, 'focus')) {
76
+
77
+ &:focus,
78
+ &:focus-visible {
79
+ color: var(--#{$name}-focus-color, var(--#{$name}-hover-color, $default-color));
80
+ background-color: var(--#{$name}-focus-background, var(--#{$name}-hover-background, $default-hover-background));
81
+ border-color: var(--#{$name}-focus-border-color, var(--#{$name}-hover-border-color, $default-border-color));
82
+ outline: var(--#{$name}-outline-size, var(--outline-size)) var(--#{$name}-outline-style, var(--outline-style)) var(--#{$name}-outline-color, color-mix(in srgb, currentColor, transparent var(--outline-opacity)));
83
+ outline-offset: var(--#{$name}-outline-offset, var(--outline-offset));
84
+ }
85
+ }
86
+
79
87
  // State: active
80
88
  @if(list.index($states, 'active')) {
81
89
 
@@ -84,7 +92,7 @@
84
92
  &[aria-selected=true],
85
93
  &[aria-pressed=true] {
86
94
  color: var(--#{$name}-active-color, $default-color);
87
- background-color: var(--#{$name}-active-background, color-mix(in srgb, $default-background, var(--active-color) var(--active-percent)));
95
+ background-color: var(--#{$name}-active-background, $default-active-background);
88
96
  border-color: var(--#{$name}-active-border-color, $default-border-color);
89
97
  }
90
98
  }
@@ -30,9 +30,11 @@ button[role=link] {
30
30
  color: var(--anchor-hover-color, var(--anchor-color, currentColor));
31
31
  }
32
32
 
33
+ &:focus,
33
34
  &:focus-visible {
34
- outline: var(--anchor-focus-size, var(--focus-size)) var(--anchor-focus-style, var(--focus-style)) var(--anchor-focus-color, color-mix(in srgb, var(--anchor-color, var(--color-font)), transparent var(--focus-opacity)));
35
- outline-offset: var(--anchor-focus-offset, var(--focus-offset));
35
+ color: var(--anchor-focus-color, var(--anchor-hover-color, var(--anchor-color, currentColor)));
36
+ outline: var(--anchor-outline-size, var(--outline-size)) var(--anchor-outline-style, var(--outline-style)) var(--anchor-outline-color, color-mix(in srgb, currentColor, transparent var(--outline-opacity)));
37
+ outline-offset: var(--anchor-outline-offset, var(--outline-offset));
36
38
  }
37
39
 
38
40
  &:active,
@@ -38,11 +38,11 @@
38
38
  --border-style: solid;
39
39
  --border-radius: .25rem;
40
40
 
41
- // Focus (outline)
42
- --focus-size: 3px;
43
- --focus-style: solid;
44
- --focus-offset: 0;
45
- --focus-opacity: 50%;
41
+ // Outline (:focus)
42
+ --outline-size: 3px;
43
+ --outline-style: solid;
44
+ --outline-offset: 0;
45
+ --outline-opacity: 50%;
46
46
 
47
47
  // Hover (color-mix)
48
48
  --hover-color: black;