basecoat-cli 0.2.0-beta.1 → 0.2.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.
- package/dist/assets/jinja/dialog.html.jinja +69 -85
- package/dist/assets/jinja/dropdown-menu.html.jinja +46 -38
- package/dist/assets/jinja/popover.html.jinja +11 -19
- package/dist/assets/jinja/select.html.jinja +67 -46
- package/dist/assets/jinja/sidebar.html.jinja +28 -22
- package/dist/assets/jinja/tabs.html.jinja +21 -18
- package/dist/assets/jinja/toast.html.jinja +46 -135
- package/dist/assets/js/all.js +188 -93
- package/dist/assets/js/all.min.js +1 -1
- package/dist/assets/js/dropdown-menu.js +60 -45
- package/dist/assets/js/dropdown-menu.min.js +1 -1
- package/dist/assets/js/popover.js +69 -0
- package/dist/assets/js/popover.min.js +1 -0
- package/dist/assets/js/select.js +59 -49
- package/dist/assets/js/select.min.js +1 -1
- package/dist/assets/nunjucks/dialog.njk +4 -4
- package/dist/assets/nunjucks/dropdown-menu.njk +35 -23
- package/dist/assets/nunjucks/popover.njk +28 -20
- package/dist/assets/nunjucks/select.njk +44 -32
- package/dist/assets/nunjucks/sidebar.njk +21 -12
- package/dist/assets/nunjucks/tabs.njk +12 -8
- package/dist/assets/nunjucks/toast.njk +7 -7
- package/package.json +1 -1
- package/dist/assets/js/css-anchor-positioning.js +0 -6340
- package/dist/assets/js/css-anchor-positioning.min.js +0 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
const initPopover = (popoverComponent) => {
|
|
3
|
+
const trigger = popoverComponent.querySelector(':scope > button');
|
|
4
|
+
const content = popoverComponent.querySelector(':scope > [data-popover]');
|
|
5
|
+
|
|
6
|
+
if (!trigger || !content) {
|
|
7
|
+
console.error('Popover component is missing a trigger button or a content element.', popoverComponent);
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const closePopover = () => {
|
|
12
|
+
if (trigger.getAttribute('aria-expanded') === 'false') return;
|
|
13
|
+
trigger.setAttribute('aria-expanded', 'false');
|
|
14
|
+
content.setAttribute('aria-hidden', 'true');
|
|
15
|
+
trigger.focus();
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const openPopover = () => {
|
|
19
|
+
const elementToFocus = content.querySelector('[autofocus]');
|
|
20
|
+
if (elementToFocus) {
|
|
21
|
+
content.addEventListener('transitionend', () => {
|
|
22
|
+
elementToFocus.focus();
|
|
23
|
+
}, { once: true });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
trigger.setAttribute('aria-expanded', 'true');
|
|
27
|
+
content.setAttribute('aria-hidden', 'false');
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
trigger.addEventListener('click', () => {
|
|
31
|
+
const isExpanded = trigger.getAttribute('aria-expanded') === 'true';
|
|
32
|
+
if (isExpanded) {
|
|
33
|
+
closePopover();
|
|
34
|
+
} else {
|
|
35
|
+
openPopover();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
popoverComponent.addEventListener('keydown', (e) => {
|
|
40
|
+
if (e.key === 'Escape') {
|
|
41
|
+
closePopover();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
document.addEventListener('click', (e) => {
|
|
46
|
+
if (!popoverComponent.contains(e.target)) {
|
|
47
|
+
closePopover();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
popoverComponent.dataset.popoverInitialized = true;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
document.querySelectorAll('.popover:not([data-popover-initialized])').forEach(initPopover);
|
|
55
|
+
|
|
56
|
+
const observer = new MutationObserver((mutations) => {
|
|
57
|
+
mutations.forEach((mutation) => {
|
|
58
|
+
mutation.addedNodes.forEach((node) => {
|
|
59
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
60
|
+
if (node.matches('.popover:not([data-popover-initialized])')) {
|
|
61
|
+
initPopover(node);
|
|
62
|
+
}
|
|
63
|
+
node.querySelectorAll('.popover:not([data-popover-initialized])').forEach(initPopover);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
69
|
+
})();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(()=>{const e=e=>{const t=e.querySelector(":scope > button"),o=e.querySelector(":scope > [data-popover]");if(!t||!o)return void console.error("Popover component is missing a trigger button or a content element.",e);const r=()=>{"false"!==t.getAttribute("aria-expanded")&&(t.setAttribute("aria-expanded","false"),o.setAttribute("aria-hidden","true"),t.focus())};t.addEventListener("click",(()=>{"true"===t.getAttribute("aria-expanded")?r():(()=>{const e=o.querySelector("[autofocus]");e&&o.addEventListener("transitionend",(()=>{e.focus()}),{once:!0}),t.setAttribute("aria-expanded","true"),o.setAttribute("aria-hidden","false")})()})),e.addEventListener("keydown",(e=>{"Escape"===e.key&&r()})),document.addEventListener("click",(t=>{e.contains(t.target)||r()})),e.dataset.popoverInitialized=!0};document.querySelectorAll(".popover:not([data-popover-initialized])").forEach(e);new MutationObserver((t=>{t.forEach((t=>{t.addedNodes.forEach((t=>{t.nodeType===Node.ELEMENT_NODE&&(t.matches(".popover:not([data-popover-initialized])")&&e(t),t.querySelectorAll(".popover:not([data-popover-initialized])").forEach(e))}))}))})).observe(document.body,{childList:!0,subtree:!0})})();
|
package/dist/assets/js/select.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
const initSelect = (selectComponent) => {
|
|
3
|
-
const trigger = selectComponent.querySelector(':scope >
|
|
3
|
+
const trigger = selectComponent.querySelector(':scope > button');
|
|
4
4
|
const selectedValue = trigger.querySelector(':scope > span');
|
|
5
|
-
const popover = selectComponent.querySelector(':scope > [popover]');
|
|
5
|
+
const popover = selectComponent.querySelector(':scope > [data-popover]');
|
|
6
6
|
const listbox = popover.querySelector('[role="listbox"]');
|
|
7
7
|
const input = selectComponent.querySelector(':scope > input[type="hidden"]');
|
|
8
8
|
const filter = selectComponent.querySelector('header input[type="text"]');
|
|
@@ -21,15 +21,26 @@
|
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
const closePopover = () => {
|
|
25
|
+
popover.setAttribute('aria-hidden', 'true');
|
|
26
|
+
trigger.setAttribute('aria-expanded', 'false');
|
|
27
|
+
if (filter) {
|
|
28
|
+
filter.value = '';
|
|
29
|
+
visibleOptions = [...options];
|
|
30
|
+
options.forEach(opt => opt.setAttribute('aria-hidden', 'false'));
|
|
31
|
+
}
|
|
32
|
+
trigger.removeAttribute('aria-activedescendant');
|
|
33
|
+
if (activeIndex > -1) options[activeIndex]?.classList.remove('active');
|
|
34
|
+
activeIndex = -1;
|
|
35
|
+
}
|
|
36
|
+
|
|
24
37
|
const selectOption = (option) => {
|
|
25
38
|
if (!option) return;
|
|
26
39
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
activeIndex = -1;
|
|
32
|
-
popover.hidePopover();
|
|
40
|
+
if (option.dataset.value) {
|
|
41
|
+
updateValue(option);
|
|
42
|
+
}
|
|
43
|
+
closePopover();
|
|
33
44
|
};
|
|
34
45
|
|
|
35
46
|
if (filter) {
|
|
@@ -62,12 +73,14 @@
|
|
|
62
73
|
updateValue(initialOption);
|
|
63
74
|
|
|
64
75
|
const handleKeyNavigation = (e) => {
|
|
65
|
-
|
|
76
|
+
const isPopoverOpen = popover.getAttribute('aria-hidden') === 'false';
|
|
77
|
+
|
|
78
|
+
if (!['ArrowDown', 'ArrowUp', 'Enter', 'Home', 'End', 'Escape'].includes(e.key)) {
|
|
66
79
|
return;
|
|
67
80
|
}
|
|
68
81
|
|
|
69
|
-
if (!
|
|
70
|
-
if (e.
|
|
82
|
+
if (!isPopoverOpen) {
|
|
83
|
+
if (e.key !== 'Enter' && e.key !== 'Escape') {
|
|
71
84
|
e.preventDefault();
|
|
72
85
|
trigger.click();
|
|
73
86
|
}
|
|
@@ -76,9 +89,14 @@
|
|
|
76
89
|
|
|
77
90
|
e.preventDefault();
|
|
78
91
|
|
|
92
|
+
if (e.key === 'Escape') {
|
|
93
|
+
closePopover();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
79
97
|
if (e.key === 'Enter') {
|
|
80
98
|
if (activeIndex > -1) {
|
|
81
|
-
selectOption(
|
|
99
|
+
selectOption(visibleOptions[activeIndex]);
|
|
82
100
|
}
|
|
83
101
|
return;
|
|
84
102
|
}
|
|
@@ -130,6 +148,31 @@
|
|
|
130
148
|
filter.addEventListener('keydown', handleKeyNavigation);
|
|
131
149
|
}
|
|
132
150
|
|
|
151
|
+
trigger.addEventListener('click', () => {
|
|
152
|
+
const isExpanded = trigger.getAttribute('aria-expanded') === 'true';
|
|
153
|
+
|
|
154
|
+
if (isExpanded) {
|
|
155
|
+
closePopover();
|
|
156
|
+
} else {
|
|
157
|
+
popover.setAttribute('aria-hidden', 'false');
|
|
158
|
+
trigger.setAttribute('aria-expanded', 'true');
|
|
159
|
+
if (filter) filter.focus();
|
|
160
|
+
|
|
161
|
+
const selectedOption = listbox.querySelector('[role="option"][aria-selected="true"]');
|
|
162
|
+
if (selectedOption) {
|
|
163
|
+
if (activeIndex > -1) {
|
|
164
|
+
options[activeIndex]?.classList.remove('active');
|
|
165
|
+
}
|
|
166
|
+
activeIndex = options.indexOf(selectedOption);
|
|
167
|
+
selectedOption.classList.add('active');
|
|
168
|
+
if (selectedOption.id) {
|
|
169
|
+
trigger.setAttribute('aria-activedescendant', selectedOption.id);
|
|
170
|
+
}
|
|
171
|
+
selectedOption.scrollIntoView({ block: 'nearest' });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
133
176
|
listbox.addEventListener('click', (e) => {
|
|
134
177
|
const clickedOption = e.target.closest('[role="option"]');
|
|
135
178
|
if (clickedOption) {
|
|
@@ -137,46 +180,13 @@
|
|
|
137
180
|
}
|
|
138
181
|
});
|
|
139
182
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (e.newState === 'open') {
|
|
144
|
-
if (filter) filter.focus();
|
|
145
|
-
|
|
146
|
-
const selectedOption = listbox.querySelector('[role="option"][aria-selected="true"]');
|
|
147
|
-
let startingOption = null;
|
|
148
|
-
|
|
149
|
-
if (selectedOption && visibleOptions.includes(selectedOption)) {
|
|
150
|
-
startingOption = selectedOption;
|
|
151
|
-
} else if (visibleOptions.length > 0) {
|
|
152
|
-
startingOption = visibleOptions[0];
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (activeIndex > -1) options[activeIndex]?.classList.remove('active');
|
|
156
|
-
|
|
157
|
-
if (startingOption) {
|
|
158
|
-
activeIndex = options.indexOf(startingOption);
|
|
159
|
-
startingOption.classList.add('active');
|
|
160
|
-
if (startingOption.id) {
|
|
161
|
-
trigger.setAttribute('aria-activedescendant', startingOption.id);
|
|
162
|
-
}
|
|
163
|
-
startingOption.scrollIntoView({ block: 'nearest' });
|
|
164
|
-
} else {
|
|
165
|
-
activeIndex = -1;
|
|
166
|
-
}
|
|
167
|
-
} else if (e.newState === 'closed') {
|
|
168
|
-
if (filter) {
|
|
169
|
-
filter.value = '';
|
|
170
|
-
visibleOptions = [...options];
|
|
171
|
-
options.forEach(opt => opt.setAttribute('aria-hidden', 'false'));
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
trigger.removeAttribute('aria-activedescendant');
|
|
175
|
-
if (activeIndex > -1) options[activeIndex]?.classList.remove('active');
|
|
176
|
-
activeIndex = -1;
|
|
183
|
+
document.addEventListener('click', (e) => {
|
|
184
|
+
if (!selectComponent.contains(e.target)) {
|
|
185
|
+
closePopover();
|
|
177
186
|
}
|
|
178
187
|
});
|
|
179
188
|
|
|
189
|
+
popover.setAttribute('aria-hidden', 'true');
|
|
180
190
|
selectComponent.dataset.selectInitialized = true;
|
|
181
191
|
};
|
|
182
192
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{const e=e=>{const t=e.querySelector(":scope >
|
|
1
|
+
(()=>{const e=e=>{const t=e.querySelector(":scope > button"),r=t.querySelector(":scope > span"),a=e.querySelector(":scope > [data-popover]"),i=a.querySelector('[role="listbox"]'),s=e.querySelector(':scope > input[type="hidden"]'),n=e.querySelector('header input[type="text"]');if(!(t&&a&&i&&s))return;const o=Array.from(i.querySelectorAll('[role="option"]'));let c=[...o],d=-1;const l=e=>{e&&(r.innerHTML=e.dataset.label||e.innerHTML,s.value=e.dataset.value,i.querySelector('[role="option"][aria-selected="true"]')?.removeAttribute("aria-selected"),e.setAttribute("aria-selected","true"))},u=()=>{a.setAttribute("aria-hidden","true"),t.setAttribute("aria-expanded","false"),n&&(n.value="",c=[...o],o.forEach((e=>e.setAttribute("aria-hidden","false")))),t.removeAttribute("aria-activedescendant"),d>-1&&o[d]?.classList.remove("active"),d=-1},v=e=>{e&&(e.dataset.value&&l(e),u())};if(n){const e=()=>{const e=n.value.trim().toLowerCase();d>-1&&(o[d].classList.remove("active"),t.removeAttribute("aria-activedescendant"),d=-1),c=[],o.forEach((t=>{const r=(t.dataset.label||t.textContent).trim().toLowerCase().includes(e);t.setAttribute("aria-hidden",String(!r)),r&&c.push(t)}))};n.addEventListener("input",e)}let b=o.find((e=>s.value&&e.dataset.value===s.value));!b&&o.length>0&&(b=o[0]),l(b);const p=e=>{const r="false"===a.getAttribute("aria-hidden");if(!["ArrowDown","ArrowUp","Enter","Home","End","Escape"].includes(e.key))return;if(!r)return void("Enter"!==e.key&&"Escape"!==e.key&&(e.preventDefault(),t.click()));if(e.preventDefault(),"Escape"===e.key)return void u();if("Enter"===e.key)return void(d>-1&&v(c[d]));if(0===c.length)return;const i=d>-1?c.indexOf(o[d]):-1;let s=i;switch(e.key){case"ArrowDown":i<c.length-1&&(s=i+1);break;case"ArrowUp":i>0?s=i-1:-1===i&&(s=0);break;case"Home":s=0;break;case"End":s=c.length-1}if(s!==i){i>-1&&c[i].classList.remove("active");const e=c[s];e.classList.add("active"),d=o.indexOf(e),e.id&&t.setAttribute("aria-activedescendant",e.id),e.scrollIntoView({block:"nearest",behavior:"smooth"})}};t.addEventListener("keydown",p),n&&n.addEventListener("keydown",p),t.addEventListener("click",(()=>{if("true"===t.getAttribute("aria-expanded"))u();else{a.setAttribute("aria-hidden","false"),t.setAttribute("aria-expanded","true"),n&&n.focus();const e=i.querySelector('[role="option"][aria-selected="true"]');e&&(d>-1&&o[d]?.classList.remove("active"),d=o.indexOf(e),e.classList.add("active"),e.id&&t.setAttribute("aria-activedescendant",e.id),e.scrollIntoView({block:"nearest"}))}})),i.addEventListener("click",(e=>{const t=e.target.closest('[role="option"]');t&&v(t)})),document.addEventListener("click",(t=>{e.contains(t.target)||u()})),a.setAttribute("aria-hidden","true"),e.dataset.selectInitialized=!0};document.querySelectorAll("div.select:not([data-select-initialized])").forEach(e);new MutationObserver((t=>{t.forEach((t=>{t.addedNodes.forEach((t=>{t.nodeType===Node.ELEMENT_NODE&&(t.matches("div.select:not([data-select-initialized])")&&e(t),t.querySelectorAll("div.select:not([data-select-initialized])").forEach(e))}))}))})).observe(document.body,{childList:!0,subtree:!0})})();
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
@param footer {string} [optional] - HTML content for the dialog footer.
|
|
9
9
|
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
|
10
10
|
@param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
|
|
11
|
-
@param
|
|
12
|
-
@param
|
|
13
|
-
@param
|
|
14
|
-
@param
|
|
11
|
+
@param dialog_attrs {object} [optional] - Additional HTML attributes for the dialog content article.
|
|
12
|
+
@param header_attrs {object} [optional] - Additional HTML attributes for the dialog header.
|
|
13
|
+
@param body_attrs {object} [optional] - Additional HTML attributes for the dialog body section.
|
|
14
|
+
@param footer_attrs {object} [optional] - Additional HTML attributes for the dialog footer.
|
|
15
15
|
@param open {boolean} [optional] [default=false] - Whether the dialog should be open initially.
|
|
16
16
|
@param close_button {boolean} [optional] [default=true] - Whether to include a close button.
|
|
17
17
|
@param close_on_overlay_click {boolean} [optional] [default=true] - Whether clicking the overlay closes the dialog.
|
|
@@ -3,26 +3,31 @@
|
|
|
3
3
|
|
|
4
4
|
@param id {string} [optional] - Unique identifier for the dropdown component.
|
|
5
5
|
@param trigger {string} [optional] - HTML content for the button that triggers the dropdown.
|
|
6
|
-
@param
|
|
6
|
+
@param items {array} [optional] - Array of menu items for the dropdown.
|
|
7
7
|
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
|
8
8
|
@param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
|
|
9
|
-
@param
|
|
9
|
+
@param popover_attrs {object} [optional] - Additional HTML attributes for the dropdown content div.
|
|
10
10
|
#}
|
|
11
11
|
{% macro dropdown_menu(
|
|
12
|
-
id=None,
|
|
13
12
|
trigger,
|
|
13
|
+
id=None,
|
|
14
14
|
items=None,
|
|
15
|
+
main_attrs={},
|
|
15
16
|
trigger_attrs={},
|
|
16
17
|
popover_attrs={},
|
|
17
18
|
menu_attrs={}
|
|
18
19
|
) %}
|
|
19
20
|
{% set id = id or ("dropdown-menu-" + (range(100000, 999999) | random | string)) %}
|
|
20
21
|
|
|
21
|
-
<div
|
|
22
|
+
<div
|
|
23
|
+
class="dropdown-menu {{ main_attrs.class }}"
|
|
24
|
+
{% for key, value in main_attrs %}
|
|
25
|
+
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
26
|
+
{% endfor %}
|
|
27
|
+
>
|
|
22
28
|
<button
|
|
23
29
|
type="button"
|
|
24
30
|
id="{{ id }}-trigger"
|
|
25
|
-
popovertarget="{{ id }}"
|
|
26
31
|
aria-haspopup="menu"
|
|
27
32
|
aria-controls="{{ id }}-menu"
|
|
28
33
|
aria-expanded="false"
|
|
@@ -33,14 +38,14 @@
|
|
|
33
38
|
{{ trigger | safe }}
|
|
34
39
|
</button>
|
|
35
40
|
<div
|
|
36
|
-
popover
|
|
37
|
-
class="popover p-1 {% if popover_attrs.class %} {{ popover_attrs.class }}{% endif %}"
|
|
38
41
|
id="{{ id }}"
|
|
42
|
+
data-popover
|
|
43
|
+
aria-hidden="true"
|
|
39
44
|
{% for key, value in popover_attrs %}
|
|
40
|
-
{
|
|
45
|
+
{{ key }}="{{ value }}"
|
|
41
46
|
{% endfor %}
|
|
42
47
|
>
|
|
43
|
-
<
|
|
48
|
+
<div
|
|
44
49
|
role="menu"
|
|
45
50
|
id="{{ id }}-menu"
|
|
46
51
|
aria-labelledby="{{ id }}-trigger"
|
|
@@ -53,7 +58,7 @@
|
|
|
53
58
|
{% else %}
|
|
54
59
|
{{ caller() if caller }}
|
|
55
60
|
{% endif %}
|
|
56
|
-
</
|
|
61
|
+
</div>
|
|
57
62
|
</div>
|
|
58
63
|
</div>
|
|
59
64
|
{% endmacro %}
|
|
@@ -73,11 +78,13 @@
|
|
|
73
78
|
<div
|
|
74
79
|
role="group"
|
|
75
80
|
aria-labelledby="{{ group_label_id }}"
|
|
76
|
-
{%
|
|
77
|
-
{
|
|
78
|
-
|
|
81
|
+
{% if item.attrs %}
|
|
82
|
+
{% for key, value in item.attrs %}
|
|
83
|
+
{{ key }}="{{ value }}"
|
|
84
|
+
{% endfor %}
|
|
85
|
+
{% endif %}
|
|
79
86
|
>
|
|
80
|
-
<div role="
|
|
87
|
+
<div role="presentation" id="{{ group_label_id }}">{{ item.label }}</div>
|
|
81
88
|
{{ render_dropdown_items(item.items, item_id) if item.items }}
|
|
82
89
|
</div>
|
|
83
90
|
{% elif item.type == "separator" %}
|
|
@@ -85,24 +92,29 @@
|
|
|
85
92
|
{% elif item.type == "item" or not item.type %}
|
|
86
93
|
{% if item.url %}
|
|
87
94
|
<a
|
|
95
|
+
id="{{ item_id }}"
|
|
88
96
|
role="menuitem"
|
|
89
97
|
href="{{ item.url }}"
|
|
90
|
-
{%
|
|
91
|
-
{%
|
|
92
|
-
|
|
98
|
+
{% if item.attrs %}
|
|
99
|
+
{% for key, value in item.attrs %}
|
|
100
|
+
{% if key != "url" %} {{ key }}="{{ value }}" {% endif %}
|
|
101
|
+
{% endfor %}
|
|
102
|
+
{% endif %}
|
|
93
103
|
>
|
|
94
104
|
{{ item.label | safe }}
|
|
95
105
|
</a>
|
|
96
106
|
{% else %}
|
|
97
|
-
<
|
|
107
|
+
<div
|
|
108
|
+
id="{{ item_id }}"
|
|
98
109
|
role="menuitem"
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
110
|
+
{% if item.attrs %}
|
|
111
|
+
{% for key, value in item.attrs %}
|
|
112
|
+
{{ key }}="{{ value }}"
|
|
113
|
+
{% endfor %}
|
|
114
|
+
{% endif %}
|
|
103
115
|
>
|
|
104
116
|
{{ item.label | safe }}
|
|
105
|
-
</
|
|
117
|
+
</div>
|
|
106
118
|
{% endif %}
|
|
107
119
|
{% endif %}
|
|
108
120
|
{% endfor %}
|
|
@@ -5,35 +5,43 @@
|
|
|
5
5
|
@param trigger {string} [optional] - HTML content for the element that triggers the popover.
|
|
6
6
|
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
|
7
7
|
@param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger element.
|
|
8
|
-
@param
|
|
8
|
+
@param popover_attrs {object} [optional] - Additional HTML attributes for the popover content div.
|
|
9
9
|
#}
|
|
10
10
|
{% macro popover(
|
|
11
|
-
id=None,
|
|
12
11
|
trigger,
|
|
12
|
+
id=None,
|
|
13
|
+
main_attrs={},
|
|
13
14
|
trigger_attrs={},
|
|
14
15
|
popover_attrs={}
|
|
15
16
|
) %}
|
|
16
17
|
{% set id = id or ("popover-" + (range(100000, 999999) | random | string)) %}
|
|
17
|
-
|
|
18
|
-
type="button"
|
|
19
|
-
popovertarget="{{ id }}"
|
|
20
|
-
id="{{ id }}-trigger"
|
|
21
|
-
onclick="this.setAttribute('aria-expanded', this.getAttribute('aria-expanded') === 'false' ? 'true' : 'false')"
|
|
22
|
-
aria-expanded="false"
|
|
23
|
-
{% for key, value in trigger_attrs %}
|
|
24
|
-
{{ key }}="{{ value }}"
|
|
25
|
-
{% endfor %}
|
|
26
|
-
>
|
|
27
|
-
{{ trigger | safe }}
|
|
28
|
-
</button>
|
|
18
|
+
|
|
29
19
|
<div
|
|
30
|
-
popover
|
|
31
|
-
|
|
32
|
-
class="popover {{ popover_attrs.class }}"
|
|
33
|
-
{% for key, value in popover_attrs %}
|
|
20
|
+
class="popover {{ main_attrs.class }}"
|
|
21
|
+
{% for key, value in main_attrs %}
|
|
34
22
|
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
35
23
|
{% endfor %}
|
|
36
|
-
>
|
|
37
|
-
|
|
24
|
+
>
|
|
25
|
+
<button
|
|
26
|
+
id="{{ id }}-trigger"
|
|
27
|
+
type="button"
|
|
28
|
+
aria-expanded="false"
|
|
29
|
+
aria-controls="{{ id }}"
|
|
30
|
+
{% for key, value in trigger_attrs %}
|
|
31
|
+
{{ key }}="{{ value }}"
|
|
32
|
+
{% endfor %}
|
|
33
|
+
>
|
|
34
|
+
{{ trigger | safe }}
|
|
35
|
+
</button>
|
|
36
|
+
<div
|
|
37
|
+
id="{{ id }}"
|
|
38
|
+
data-popover
|
|
39
|
+
aria-hidden="true"
|
|
40
|
+
{% for key, value in popover_attrs %}
|
|
41
|
+
{{ key }}="{{ value }}"
|
|
42
|
+
{% endfor %}
|
|
43
|
+
>
|
|
44
|
+
{{ caller() if caller }}
|
|
45
|
+
</div>
|
|
38
46
|
</div>
|
|
39
47
|
{% endmacro %}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
@param name {string} [optional] - The name attribute for the hidden input storing the selected value.
|
|
7
7
|
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
|
8
8
|
@param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
|
|
9
|
-
@param
|
|
9
|
+
@param popover_attrs {object} [optional] - Additional HTML attributes for the popover content div.
|
|
10
10
|
@param listbox_attrs {object} [optional] - Additional HTML attributes for the listbox div.
|
|
11
11
|
@param input_attrs {object} [optional] - Additional HTML attributes for the hidden input.
|
|
12
12
|
@param search_placeholder {string} [optional] [default="Search entries..."] - Placeholder text for the search input (combobox only).
|
|
@@ -17,9 +17,11 @@
|
|
|
17
17
|
selected=None,
|
|
18
18
|
name=None,
|
|
19
19
|
items=None,
|
|
20
|
+
main_attrs={},
|
|
20
21
|
trigger_attrs={},
|
|
21
22
|
popover_attrs={},
|
|
22
23
|
listbox_attrs={},
|
|
24
|
+
input_attrs={},
|
|
23
25
|
search_placeholder="Search entries...",
|
|
24
26
|
is_combobox=false
|
|
25
27
|
) %}
|
|
@@ -28,34 +30,40 @@
|
|
|
28
30
|
{% set first_option = [] %}
|
|
29
31
|
{% set selected_option = [] %}
|
|
30
32
|
|
|
31
|
-
{%
|
|
32
|
-
{%
|
|
33
|
-
{%
|
|
33
|
+
{% if items %}
|
|
34
|
+
{% for item in items %}
|
|
35
|
+
{% if item.type == "group" %}
|
|
36
|
+
{% for sub_item in item.items %}
|
|
37
|
+
{% if not first_option[0] %}
|
|
38
|
+
{% set first_option = (first_option.push(sub_item), first_option) %}
|
|
39
|
+
{% endif %}
|
|
40
|
+
{% if selected and sub_item.value == selected and not selected_option[0] %}
|
|
41
|
+
{% set selected_option = (selected_option.push(sub_item), selected_option) %}
|
|
42
|
+
{% endif %}
|
|
43
|
+
{% endfor %}
|
|
44
|
+
{% else %}
|
|
34
45
|
{% if not first_option[0] %}
|
|
35
|
-
{% set first_option = (first_option.push(
|
|
46
|
+
{% set first_option = (first_option.push(item), first_option) %}
|
|
36
47
|
{% endif %}
|
|
37
|
-
{% if selected and
|
|
38
|
-
{% set selected_option = (selected_option.push(
|
|
48
|
+
{% if selected and item.value == selected and not selected_option[0] %}
|
|
49
|
+
{% set selected_option = (selected_option.push(item), selected_option) %}
|
|
39
50
|
{% endif %}
|
|
40
|
-
{% endfor %}
|
|
41
|
-
{% else %}
|
|
42
|
-
{% if not first_option[0] %}
|
|
43
|
-
{% set first_option = (first_option.push(item), first_option) %}
|
|
44
51
|
{% endif %}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
{% endif %}
|
|
48
|
-
{% endif %}
|
|
49
|
-
{% endfor %}
|
|
52
|
+
{% endfor %}
|
|
53
|
+
{% endif %}
|
|
50
54
|
|
|
51
55
|
{% set default_option = selected_option[0] or first_option[0] or None %}
|
|
52
56
|
|
|
53
|
-
<div
|
|
57
|
+
<div
|
|
58
|
+
class="select {{ main_attrs.class }}"
|
|
59
|
+
{% for key, value in main_attrs %}
|
|
60
|
+
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
61
|
+
{% endfor %}
|
|
62
|
+
>
|
|
54
63
|
<button
|
|
55
64
|
type="button"
|
|
56
65
|
class="btn-outline justify-between font-normal {{ trigger_attrs.class }}"
|
|
57
66
|
id="{{ id }}-trigger"
|
|
58
|
-
popovertarget="{{ id }}"
|
|
59
67
|
aria-haspopup="listbox"
|
|
60
68
|
aria-expanded="false"
|
|
61
69
|
aria-controls="{{ id }}-listbox"
|
|
@@ -71,11 +79,11 @@
|
|
|
71
79
|
{% endif %}
|
|
72
80
|
</button>
|
|
73
81
|
<div
|
|
74
|
-
popover
|
|
75
82
|
id="{{ id }}"
|
|
76
|
-
|
|
83
|
+
data-popover
|
|
84
|
+
aria-hidden="true"
|
|
77
85
|
{% for key, value in popover_attrs %}
|
|
78
|
-
{
|
|
86
|
+
{{ key }}="{{ value }}"
|
|
79
87
|
{% endfor %}
|
|
80
88
|
>
|
|
81
89
|
{% if is_combobox %}
|
|
@@ -90,8 +98,8 @@
|
|
|
90
98
|
spellcheck="false"
|
|
91
99
|
aria-autocomplete="list"
|
|
92
100
|
role="combobox"
|
|
93
|
-
aria-expanded="
|
|
94
|
-
aria-controls="{{ id }}-
|
|
101
|
+
aria-expanded="false"
|
|
102
|
+
aria-controls="{{ id }}-listbox"
|
|
95
103
|
aria-labelledby="{{ id }}-trigger"
|
|
96
104
|
>
|
|
97
105
|
</header>
|
|
@@ -105,7 +113,7 @@
|
|
|
105
113
|
{{ key }}="{{ value }}"
|
|
106
114
|
{% endfor %}
|
|
107
115
|
>
|
|
108
|
-
{% if items.length > 0 %}
|
|
116
|
+
{% if items and items.length > 0 %}
|
|
109
117
|
{{ render_select_items(items, default_option.value, id ~ "-items" if id else "items") }}
|
|
110
118
|
{% else %}
|
|
111
119
|
{{ caller() if caller }}
|
|
@@ -115,7 +123,7 @@
|
|
|
115
123
|
<input
|
|
116
124
|
type="hidden"
|
|
117
125
|
name="{{ name or id ~ '-value' }}"
|
|
118
|
-
value="{{
|
|
126
|
+
value="{{ (default_option.value if default_option) or '' }}"
|
|
119
127
|
{% for key, value in input_attrs %}
|
|
120
128
|
{% if key != 'name' and key != 'value' %}{{ key }}="{{ value }}"{% endif %}
|
|
121
129
|
{% endfor %}
|
|
@@ -137,11 +145,13 @@
|
|
|
137
145
|
<div
|
|
138
146
|
role="group"
|
|
139
147
|
aria-labelledby="{{ group_label_id }}"
|
|
140
|
-
{%
|
|
141
|
-
{
|
|
142
|
-
|
|
148
|
+
{% if item.attrs %}
|
|
149
|
+
{% for key, value in item.attrs %}
|
|
150
|
+
{{ key }}="{{ value }}"
|
|
151
|
+
{% endfor %}
|
|
152
|
+
{% endif %}
|
|
143
153
|
>
|
|
144
|
-
<div role="
|
|
154
|
+
<div role="presentation" id="{{ group_label_id }}">{{ item.label }}</div>
|
|
145
155
|
{{ render_select_items(item.items, selected, item_id) if item.items }}
|
|
146
156
|
</div>
|
|
147
157
|
{% elif item.type == "separator" %}
|
|
@@ -152,9 +162,11 @@
|
|
|
152
162
|
role="option"
|
|
153
163
|
data-value="{{ item.value }}"
|
|
154
164
|
{% if selected == item.value %}aria-selected="true"{% endif %}
|
|
155
|
-
{%
|
|
156
|
-
|
|
157
|
-
|
|
165
|
+
{% if item.attrs %}
|
|
166
|
+
{% for key, value in item.attrs %}
|
|
167
|
+
{{ key }}="{{ value }}"
|
|
168
|
+
{% endfor %}
|
|
169
|
+
{% endif %}
|
|
158
170
|
>
|
|
159
171
|
{{ item.label | safe }}
|
|
160
172
|
</div>
|