desy-html 4.3.0 → 5.1.1

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.
Files changed (28) hide show
  1. package/docs/_include.template-header.njk +4 -0
  2. package/docs/_macro.example-render.njk +4 -0
  3. package/docs/componentes.html +3 -0
  4. package/docs/ds/_ds.example.menu-vertical.njk +3 -3
  5. package/docs/examples-tree.html +5 -0
  6. package/docs/index.html +16 -0
  7. package/package.json +1 -1
  8. package/src/css/styles.css +1 -0
  9. package/src/js/aria/tree.js +245 -0
  10. package/src/js/aria/treeitem.js +260 -0
  11. package/src/js/desy-html.js +28 -0
  12. package/src/js/index.js +3 -1
  13. package/src/templates/components/description-list/_examples.description-list.njk +63 -0
  14. package/src/templates/components/dropdown/params.dropdown.yaml +6 -6
  15. package/src/templates/components/header/_examples.header.njk +33 -1
  16. package/src/templates/components/header/_template.header.header__dropdown.njk +7 -2
  17. package/src/templates/components/header/_template.header.header__offcanvas.njk +1 -1
  18. package/src/templates/components/header/_template.header.header__subnav.njk +17 -13
  19. package/src/templates/components/header/_template.header.njk +42 -30
  20. package/src/templates/components/header/params.header.yaml +45 -5
  21. package/src/templates/components/menubar/_examples.menubar.njk +169 -0
  22. package/src/templates/components/pagination/_examples.pagination.njk +32 -0
  23. package/src/templates/components/pagination/_template.pagination.njk +1 -0
  24. package/src/templates/components/tree/_examples.tree.njk +987 -0
  25. package/src/templates/components/tree/_macro.tree.njk +3 -0
  26. package/src/templates/components/tree/_styles.tree.css +45 -0
  27. package/src/templates/components/tree/_template.tree.njk +175 -0
  28. package/src/templates/components/tree/params.tree.yaml +159 -0
@@ -253,6 +253,10 @@
253
253
  {
254
254
  href: "examples-tooltip.html",
255
255
  text: "Tooltip"
256
+ },
257
+ {
258
+ href: "/examples-tree.html",
259
+ text: "Tree"
256
260
  }],
257
261
  attributes: {
258
262
  "aria-label": "Navegación ejemplos"
@@ -91,6 +91,8 @@ import componentTabs %}
91
91
  import componentTextarea %}
92
92
  {% from "components/tooltip/_macro.tooltip.njk"
93
93
  import componentTooltip %}
94
+ {% from "components/tree/_macro.tree.njk"
95
+ import componentTree %}
94
96
 
95
97
  {% macro exampleRender(data) %}
96
98
  {% set exampleComponent = data.data.exampleComponent %}
@@ -206,6 +208,8 @@ import componentTooltip %}
206
208
  {{ componentTextarea(example.data) }}
207
209
  {% elseif exampleComponent == "tooltip" %}
208
210
  {{ componentTooltip(example.data) }}
211
+ {% elseif exampleComponent == "tree" %}
212
+ {{ componentTree(example.data) }}
209
213
  {% endif %}
210
214
  <div class="mt-xl py-base">
211
215
  <span x-text="textShow" @click="isOpenCode = !isOpenCode; isOpenCode ? textShow = 'Hide code' : textShow = 'Show code'" class="text-primary-base hover:text-primary-dark underline cursor-pointer">Show code</span>
@@ -148,6 +148,9 @@
148
148
  <div>
149
149
  <p class="c-paragraph-base"><a href="examples-tooltip.html" class="c-link">Tooltip</a></p>
150
150
  </div>
151
+ <div>
152
+ <p class="c-paragraph-base"><a href="examples-tree.html" class="c-link">Tree</a></p>
153
+ </div>
151
154
  </div>
152
155
  <div class="pb-2xl"></div>
153
156
  {% endblock %}
@@ -28,7 +28,7 @@
28
28
  }
29
29
  ],
30
30
  "attributes": {
31
- "aria-label": "Menu vertical"
31
+ "aria-label": "Árbol"
32
32
  }
33
33
  }) }}
34
34
  </div>
@@ -58,7 +58,7 @@
58
58
  }
59
59
  ],
60
60
  "attributes": {
61
- "aria-label": "Menu vertical"
61
+ "aria-label": "Árbol"
62
62
  }
63
63
  }) }}
64
64
  </div>
@@ -123,7 +123,7 @@
123
123
  }
124
124
  ],
125
125
  "attributes": {
126
- "aria-label": "Menu vertical"
126
+ "aria-label": "Árbol"
127
127
  }
128
128
  }) }}
129
129
  </div>
@@ -0,0 +1,5 @@
1
+ {% set title = "Tree. Componente, parámetros y ejemplos. Documentación de desy-html. Gobierno de Aragón" %}
2
+ {% extends "_template.examples.njk" %}
3
+ {% block contentBlock %}
4
+ {% import "components/tree/_examples.tree.njk" as exampleData %}
5
+ {% endblock %}
package/docs/index.html CHANGED
@@ -38,6 +38,22 @@
38
38
 
39
39
  <h2>Changelog (English)</h2>
40
40
  <p>What's new in the latest version of desy-html</p>
41
+ <h3>v.5.1.1</h3>
42
+ <ul class="text-sm">
43
+ <li>Fixed pagination from having too much listbox height.</li>
44
+ </ul>
45
+ <h3>v.5.1.0</h3>
46
+ <ul class="text-sm">
47
+ <li>Added tree component.</li>
48
+ </ul>
49
+ <h3>v.5.0.1</h3>
50
+ <ul class="text-sm">
51
+ <li>Fixed accesibility bad semantics in subnav. Added dropdown contentHtml rendering and example.</li>
52
+ </ul>
53
+ <h3>v.5.0.0</h3>
54
+ <ul class="text-sm">
55
+ <li>Header now uses correct dropdown params.</li>
56
+ </ul>
41
57
  <h3>v.4.3.0</h3>
42
58
  <ul class="text-sm">
43
59
  <li>Added disabled and active param in menubar and fixed accesibility issues.</li>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "desy-html",
3
- "version": "4.3.0",
3
+ "version": "5.1.1",
4
4
  "description": "desy-html contains the code you need to start building a user interface for Gobierno de Aragón government webapps.",
5
5
  "author": {
6
6
  "name": "Desy (SDA Servicios Digitales de Aragón)",
@@ -35,6 +35,7 @@
35
35
  @import "../templates/components/skip-link/_styles.skip-link.css";
36
36
  @import "../templates/components/table-advanced/_styles.table-advanced.css";
37
37
  @import "../templates/components/tabs/_styles.tabs.css";
38
+ @import "../templates/components/tree/_styles.tree.css";
38
39
  @import "./component.form-group.css";
39
40
  @import "./component.tippy-box.css";
40
41
 
@@ -0,0 +1,245 @@
1
+ export function Tree(aria) {
2
+ /*
3
+ * This content is licensed according to the W3C Software License at
4
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
5
+ *
6
+ * File: Tree.js
7
+ *
8
+ * Desc: Tree widget that implements ARIA Authoring Practices
9
+ * for a tree being used as a file viewer
10
+ */
11
+
12
+ /*
13
+ * @constructor
14
+ *
15
+ * @desc
16
+ * Tree item object for representing the state and user interactions for a
17
+ * tree widget
18
+ *
19
+ * @param node
20
+ * An element with the role=tree attribute
21
+ */
22
+ aria.Tree = function (node) {
23
+ // Check whether node is a DOM element
24
+ if (typeof node !== 'object') {
25
+ return;
26
+ }
27
+
28
+ this.domNode = node;
29
+
30
+ this.treeitems = [];
31
+ this.firstChars = [];
32
+
33
+ this.firstTreeitem = null;
34
+ this.lastTreeitem = null;
35
+
36
+ };
37
+
38
+ aria.Tree.prototype.init = function () {
39
+
40
+ function findTreeitems (node, tree, group) {
41
+
42
+ var elem = node.firstElementChild;
43
+ var ti = group;
44
+
45
+ while (elem) {
46
+
47
+ if (elem.tagName.toLowerCase() === 'li') {
48
+ ti = new aria.Treeitem(elem, tree, group);
49
+ ti.init();
50
+ tree.treeitems.push(ti);
51
+ tree.firstChars.push(ti.label.substring(0, 1).toLowerCase());
52
+ }
53
+
54
+ if (elem.firstElementChild) {
55
+ findTreeitems(elem, tree, ti);
56
+ }
57
+
58
+ elem = elem.nextElementSibling;
59
+ }
60
+ }
61
+
62
+ // initialize pop up menus
63
+ if (!this.domNode.getAttribute('role')) {
64
+ this.domNode.setAttribute('role', 'tree');
65
+ }
66
+
67
+ findTreeitems(this.domNode, this, false);
68
+
69
+ this.updateVisibleTreeitems();
70
+
71
+ this.firstTreeitem.domNode.tabIndex = 0;
72
+
73
+ };
74
+
75
+ aria.Tree.prototype.setFocusToItem = function (treeitem) {
76
+
77
+ for (var i = 0; i < this.treeitems.length; i++) {
78
+ var ti = this.treeitems[i];
79
+
80
+ if (ti === treeitem) {
81
+ ti.domNode.tabIndex = 0;
82
+ ti.domNode.focus();
83
+ }
84
+ else {
85
+ ti.domNode.tabIndex = -1;
86
+ }
87
+ }
88
+
89
+ };
90
+
91
+ aria.Tree.prototype.setFocusToNextItem = function (currentItem) {
92
+
93
+ var nextItem = false;
94
+
95
+ for (var i = (this.treeitems.length - 1); i >= 0; i--) {
96
+ var ti = this.treeitems[i];
97
+ if (ti === currentItem) {
98
+ break;
99
+ }
100
+ if (ti.isVisible) {
101
+ nextItem = ti;
102
+ }
103
+ }
104
+
105
+ if (nextItem) {
106
+ this.setFocusToItem(nextItem);
107
+ }
108
+
109
+ };
110
+
111
+ aria.Tree.prototype.setFocusToPreviousItem = function (currentItem) {
112
+
113
+ var prevItem = false;
114
+
115
+ for (var i = 0; i < this.treeitems.length; i++) {
116
+ var ti = this.treeitems[i];
117
+ if (ti === currentItem) {
118
+ break;
119
+ }
120
+ if (ti.isVisible) {
121
+ prevItem = ti;
122
+ }
123
+ }
124
+
125
+ if (prevItem) {
126
+ this.setFocusToItem(prevItem);
127
+ }
128
+ };
129
+
130
+ aria.Tree.prototype.setFocusToParentItem = function (currentItem) {
131
+
132
+ if (currentItem.groupTreeitem) {
133
+ this.setFocusToItem(currentItem.groupTreeitem);
134
+ }
135
+ };
136
+
137
+ aria.Tree.prototype.setFocusToFirstItem = function () {
138
+ this.setFocusToItem(this.firstTreeitem);
139
+ };
140
+
141
+ aria.Tree.prototype.setFocusToLastItem = function () {
142
+ this.setFocusToItem(this.lastTreeitem);
143
+ };
144
+
145
+ aria.Tree.prototype.expandTreeitem = function (currentItem) {
146
+
147
+ if (currentItem.isExpandable) {
148
+ currentItem.domNode.setAttribute('aria-expanded', true);
149
+ this.updateVisibleTreeitems();
150
+ }
151
+
152
+ };
153
+
154
+ aria.Tree.prototype.expandAllSiblingItems = function (currentItem) {
155
+ for (var i = 0; i < this.treeitems.length; i++) {
156
+ var ti = this.treeitems[i];
157
+
158
+ if ((ti.groupTreeitem === currentItem.groupTreeitem) && ti.isExpandable) {
159
+ this.expandTreeitem(ti);
160
+ }
161
+ }
162
+
163
+ };
164
+
165
+ aria.Tree.prototype.collapseTreeitem = function (currentItem) {
166
+
167
+ var groupTreeitem = false;
168
+
169
+ if (currentItem.isExpanded()) {
170
+ groupTreeitem = currentItem;
171
+ }
172
+ else {
173
+ groupTreeitem = currentItem.groupTreeitem;
174
+ }
175
+
176
+ if (groupTreeitem) {
177
+ groupTreeitem.domNode.setAttribute('aria-expanded', false);
178
+ this.updateVisibleTreeitems();
179
+ this.setFocusToItem(groupTreeitem);
180
+ }
181
+
182
+ };
183
+
184
+ aria.Tree.prototype.updateVisibleTreeitems = function () {
185
+
186
+ this.firstTreeitem = this.treeitems[0];
187
+
188
+ for (var i = 0; i < this.treeitems.length; i++) {
189
+ var ti = this.treeitems[i];
190
+
191
+ var parent = ti.domNode.parentNode;
192
+
193
+ ti.isVisible = true;
194
+
195
+ while (parent && (parent !== this.domNode)) {
196
+
197
+ if (parent.getAttribute('aria-expanded') == 'false') {
198
+ ti.isVisible = false;
199
+ }
200
+ parent = parent.parentNode;
201
+ }
202
+
203
+ if (ti.isVisible) {
204
+ this.lastTreeitem = ti;
205
+ }
206
+ }
207
+
208
+ };
209
+
210
+ aria.Tree.prototype.setFocusByFirstCharacter = function (currentItem, char) {
211
+ var start, index, char = char.toLowerCase();
212
+
213
+ // Get start index for search based on position of currentItem
214
+ start = this.treeitems.indexOf(currentItem) + 1;
215
+ if (start === this.treeitems.length) {
216
+ start = 0;
217
+ }
218
+
219
+ // Check remaining slots in the menu
220
+ index = this.getIndexFirstChars(start, char);
221
+
222
+ // If not found in remaining slots, check from beginning
223
+ if (index === -1) {
224
+ index = this.getIndexFirstChars(0, char);
225
+ }
226
+
227
+ // If match was found...
228
+ if (index > -1) {
229
+ this.setFocusToItem(this.treeitems[index]);
230
+ }
231
+ };
232
+
233
+ aria.Tree.prototype.getIndexFirstChars = function (startIndex, char) {
234
+ for (var i = startIndex; i < this.firstChars.length; i++) {
235
+ if (this.treeitems[i].isVisible) {
236
+ if (char === this.firstChars[i]) {
237
+ return i;
238
+ }
239
+ }
240
+ }
241
+ return -1;
242
+ };
243
+
244
+ return aria;
245
+ }
@@ -0,0 +1,260 @@
1
+ export function Treeitem(aria) {
2
+ /*
3
+ * This content is licensed according to the W3C Software License at
4
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
5
+ *
6
+ * File: Treeitem.js
7
+ *
8
+ * Desc: Treeitem widget that implements ARIA Authoring Practices
9
+ * for a tree being used as a file viewer
10
+ */
11
+
12
+ /*
13
+ * @constructor
14
+ *
15
+ * @desc
16
+ * Treeitem object for representing the state and user interactions for a
17
+ * treeItem widget
18
+ *
19
+ * @param node
20
+ * An element with the role=tree attribute
21
+ */
22
+
23
+ aria.Treeitem = function (node, treeObj, group) {
24
+
25
+ // Check whether node is a DOM element
26
+ if (typeof node !== 'object') {
27
+ return;
28
+ }
29
+
30
+ node.tabIndex = -1;
31
+ this.tree = treeObj;
32
+ this.groupTreeitem = group;
33
+ this.domNode = node;
34
+ this.label = node.textContent.trim();
35
+
36
+ if (node.getAttribute('aria-label')) {
37
+ this.label = node.getAttribute('aria-label').trim();
38
+ }
39
+
40
+ this.isExpandable = false;
41
+ this.isVisible = false;
42
+ this.inGroup = false;
43
+
44
+ if (group) {
45
+ this.inGroup = true;
46
+ }
47
+
48
+ var elem = node.firstElementChild;
49
+
50
+ while (elem) {
51
+
52
+ if (elem.tagName.toLowerCase() == 'ul') {
53
+ elem.setAttribute('role', 'group');
54
+ this.isExpandable = true;
55
+ break;
56
+ }
57
+
58
+ elem = elem.nextElementSibling;
59
+ }
60
+
61
+ this.keyCode = Object.freeze({
62
+ RETURN: 13,
63
+ SPACE: 32,
64
+ PAGEUP: 33,
65
+ PAGEDOWN: 34,
66
+ END: 35,
67
+ HOME: 36,
68
+ LEFT: 37,
69
+ UP: 38,
70
+ RIGHT: 39,
71
+ DOWN: 40
72
+ });
73
+ };
74
+
75
+ aria.Treeitem.prototype.init = function () {
76
+ this.domNode.tabIndex = -1;
77
+
78
+ if (!this.domNode.getAttribute('role')) {
79
+ this.domNode.setAttribute('role', 'treeitem');
80
+ }
81
+
82
+ this.domNode.addEventListener('keydown', this.handleKeydown.bind(this));
83
+ this.domNode.addEventListener('click', this.handleClick.bind(this));
84
+ this.domNode.addEventListener('focus', this.handleFocus.bind(this));
85
+ this.domNode.addEventListener('blur', this.handleBlur.bind(this));
86
+
87
+ if (!this.isExpandable) {
88
+ this.domNode.addEventListener('mouseover', this.handleMouseOver.bind(this));
89
+ this.domNode.addEventListener('mouseout', this.handleMouseOut.bind(this));
90
+ }
91
+ };
92
+
93
+ aria.Treeitem.prototype.isExpanded = function () {
94
+
95
+ if (this.isExpandable) {
96
+ return this.domNode.getAttribute('aria-expanded') === 'true';
97
+ }
98
+
99
+ return false;
100
+
101
+ };
102
+
103
+ /* EVENT HANDLERS */
104
+
105
+ aria.Treeitem.prototype.handleKeydown = function (event) {
106
+
107
+ var tgt = event.currentTarget,
108
+ flag = false,
109
+ char = event.key,
110
+ clickEvent;
111
+
112
+ function isPrintableCharacter (str) {
113
+ return str.length === 1 && str.match(/\S/);
114
+ }
115
+
116
+ function printableCharacter (item) {
117
+ if (char == '*') {
118
+ item.tree.expandAllSiblingItems(item);
119
+ flag = true;
120
+ }
121
+ else {
122
+ if (isPrintableCharacter(char)) {
123
+ item.tree.setFocusByFirstCharacter(item, char);
124
+ flag = true;
125
+ }
126
+ }
127
+ }
128
+
129
+ if (event.altKey || event.ctrlKey || event.metaKey) {
130
+ return;
131
+ }
132
+
133
+ if (event.shift) {
134
+ if (isPrintableCharacter(char)) {
135
+ printableCharacter(this);
136
+ }
137
+ }
138
+ else {
139
+ switch (event.keyCode) {
140
+ case this.keyCode.SPACE:
141
+ case this.keyCode.RETURN:
142
+ // Create simulated mouse event to mimic the behavior of ATs
143
+ // and let the event handler handleClick do the housekeeping.
144
+ try {
145
+ clickEvent = new MouseEvent('click', {
146
+ 'view': window,
147
+ 'bubbles': true,
148
+ 'cancelable': true
149
+ });
150
+ }
151
+ catch (err) {
152
+ if (document.createEvent) {
153
+ // DOM Level 3 for IE 9+
154
+ clickEvent = document.createEvent('MouseEvents');
155
+ clickEvent.initEvent('click', true, true);
156
+ }
157
+ }
158
+ tgt.dispatchEvent(clickEvent);
159
+ flag = true;
160
+ break;
161
+
162
+ case this.keyCode.UP:
163
+ this.tree.setFocusToPreviousItem(this);
164
+ flag = true;
165
+ break;
166
+
167
+ case this.keyCode.DOWN:
168
+ this.tree.setFocusToNextItem(this);
169
+ flag = true;
170
+ break;
171
+
172
+ case this.keyCode.RIGHT:
173
+ if (this.isExpandable) {
174
+ if (this.isExpanded()) {
175
+ this.tree.setFocusToNextItem(this);
176
+ }
177
+ else {
178
+ this.tree.expandTreeitem(this);
179
+ }
180
+ }
181
+ flag = true;
182
+ break;
183
+
184
+ case this.keyCode.LEFT:
185
+ if (this.isExpandable && this.isExpanded()) {
186
+ this.tree.collapseTreeitem(this);
187
+ flag = true;
188
+ }
189
+ else {
190
+ if (this.inGroup) {
191
+ this.tree.setFocusToParentItem(this);
192
+ flag = true;
193
+ }
194
+ }
195
+ break;
196
+
197
+ case this.keyCode.HOME:
198
+ this.tree.setFocusToFirstItem();
199
+ flag = true;
200
+ break;
201
+
202
+ case this.keyCode.END:
203
+ this.tree.setFocusToLastItem();
204
+ flag = true;
205
+ break;
206
+
207
+ default:
208
+ if (isPrintableCharacter(char)) {
209
+ printableCharacter(this);
210
+ }
211
+ break;
212
+ }
213
+
214
+ }
215
+
216
+ if (flag) {
217
+ event.stopPropagation();
218
+ event.preventDefault();
219
+ }
220
+ };
221
+
222
+ aria.Treeitem.prototype.handleClick = function (event) {
223
+ if (this.isExpandable) {
224
+ if (this.isExpanded()) {
225
+ this.tree.collapseTreeitem(this);
226
+ }
227
+ else {
228
+ this.tree.expandTreeitem(this);
229
+ }
230
+ event.stopPropagation();
231
+ }
232
+ else {
233
+ this.tree.setFocusToItem(this);
234
+ }
235
+ };
236
+
237
+ aria.Treeitem.prototype.handleFocus = function (event) {
238
+ var node = this.domNode;
239
+ if (this.isExpandable) {
240
+ node = node.firstElementChild;
241
+ }
242
+ node.classList.add('c-tree__item--focus');
243
+ };
244
+
245
+ aria.Treeitem.prototype.handleBlur = function (event) {
246
+ var node = this.domNode;
247
+ if (this.isExpandable) {
248
+ node = node.firstElementChild;
249
+ }
250
+ node.classList.remove('c-tree__item--focus');
251
+ };
252
+
253
+ aria.Treeitem.prototype.handleMouseOver = function (event) {
254
+ event.currentTarget.classList.add('c-tree__item--hover');
255
+ };
256
+
257
+ aria.Treeitem.prototype.handleMouseOut = function (event) {
258
+ event.currentTarget.classList.remove('c-tree__item--hover');
259
+ };
260
+ }
@@ -9,6 +9,8 @@ import { MenubarItemAction } from './aria/MenubarItemAction.js';
9
9
  import { MenubarAction } from './aria/MenubarAction.js';
10
10
  import { listbox } from './aria/listbox.js';
11
11
  import { alert } from './aria/alert.js';
12
+ import { Treeitem } from './aria/treeitem.js';
13
+ import { Tree } from './aria/tree.js';
12
14
 
13
15
 
14
16
  export function accordionComponent(aria) {
@@ -311,3 +313,29 @@ export function tooltipComponent(aria) {
311
313
  }
312
314
  }
313
315
 
316
+ export function treeComponent(aria) {
317
+ Treeitem(aria);
318
+ Tree(aria);
319
+ const modules = document.querySelectorAll('[data-module]');
320
+ for (const item in modules) if (modules.hasOwnProperty(item)) {
321
+ const moduleValue = modules[item].getAttribute('data-module');
322
+
323
+ if (moduleValue == 'c-tree'){
324
+ const tree = new aria.Tree(modules[item]);
325
+ tree.init();
326
+ }
327
+
328
+ if (moduleValue == 'c-tree__item'){
329
+ modules[item].addEventListener('click', function (event) {
330
+ const current = event.target;
331
+ if ((current.tagName == 'INPUT')||(current.tagName == 'LABEL')) {
332
+ event.stopPropagation();
333
+ } else {
334
+ event.stopPropagation();
335
+ event.preventDefault();
336
+ }
337
+ });
338
+ }
339
+ }
340
+ }
341
+
package/src/js/index.js CHANGED
@@ -12,7 +12,8 @@ import {
12
12
  menubarComponent,
13
13
  tableAdvancedComponent,
14
14
  tabsComponent,
15
- tooltipComponent
15
+ tooltipComponent,
16
+ treeComponent
16
17
  } from './desy-html.js';
17
18
 
18
19
  var aria = aria || {};
@@ -26,3 +27,4 @@ menubarComponent(aria);
26
27
  tableAdvancedComponent(aria);
27
28
  tabsComponent(aria);
28
29
  tooltipComponent(aria);
30
+ treeComponent(aria);