basecoat-cli 0.2.0-beta.1 → 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.
@@ -1,13 +1,35 @@
1
1
  (() => {
2
2
  const initDropdownMenu = (dropdownMenuComponent) => {
3
- const trigger = dropdownMenuComponent.querySelector('[popovertarget]');
4
- const popover = dropdownMenuComponent.querySelector('[popover]');
5
- const menu = popover?.querySelector('[role="menu"]');
6
- if (!trigger || !popover || !menu) return;
3
+ const trigger = dropdownMenuComponent.querySelector(':scope > button');
4
+ const popover = dropdownMenuComponent.querySelector(':scope > [data-popover]');
5
+ const menu = popover.querySelector('[role="menu"]');
6
+
7
+ if (!trigger || !menu || !popover) {
8
+ console.error('Dropdown menu component is missing a trigger, a menu, or a popover content element.', dropdownMenuComponent);
9
+ return;
10
+ }
7
11
 
8
12
  let menuItems = [];
9
13
  let activeIndex = -1;
10
14
 
15
+ const closeMenu = () => {
16
+ if (trigger.getAttribute('aria-expanded') === 'false') return;
17
+ trigger.setAttribute('aria-expanded', 'false');
18
+ trigger.removeAttribute('aria-activedescendant');
19
+ popover.setAttribute('aria-hidden', 'true');
20
+ trigger.focus();
21
+ activeIndex = -1;
22
+ };
23
+
24
+ const openMenu = () => {
25
+ trigger.setAttribute('aria-expanded', 'true');
26
+ popover.setAttribute('aria-hidden', 'false');
27
+ menuItems = Array.from(menu.querySelectorAll('[role^="menuitem"]:not([disabled])'));
28
+ if (menuItems.length > 0) {
29
+ setActiveItem(0);
30
+ }
31
+ };
32
+
11
33
  const setActiveItem = (index) => {
12
34
  if (activeIndex > -1 && menuItems[activeIndex]) {
13
35
  menuItems[activeIndex].classList.remove('active');
@@ -17,36 +39,48 @@
17
39
  const activeItem = menuItems[activeIndex];
18
40
  activeItem.classList.add('active');
19
41
  trigger.setAttribute('aria-activedescendant', activeItem.id);
20
- activeItem.scrollIntoView({ block: 'nearest' });
21
42
  } else {
22
43
  trigger.removeAttribute('aria-activedescendant');
23
44
  }
24
45
  };
25
46
 
26
- const handleKeyDown = (e) => {
27
- if (!popover.matches(':popover-open')) {
47
+ trigger.addEventListener('click', () => {
48
+ const isExpanded = trigger.getAttribute('aria-expanded') === 'true';
49
+ if (isExpanded) {
50
+ closeMenu();
51
+ } else {
52
+ openMenu();
53
+ }
54
+ });
55
+
56
+ dropdownMenuComponent.addEventListener('keydown', (e) => {
57
+ const isExpanded = trigger.getAttribute('aria-expanded') === 'true';
58
+
59
+ if (e.key === 'Escape') {
60
+ if (isExpanded) closeMenu();
61
+ return;
62
+ }
63
+
64
+ if (!isExpanded) {
28
65
  if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(e.key)) {
29
66
  e.preventDefault();
30
- trigger.click();
67
+ openMenu();
31
68
  }
32
69
  return;
33
70
  }
34
71
 
35
- let nextIndex = activeIndex;
36
72
  if (menuItems.length === 0) return;
37
73
 
74
+ let nextIndex = activeIndex;
75
+
38
76
  switch (e.key) {
39
77
  case 'ArrowDown':
40
78
  e.preventDefault();
41
- if (activeIndex < menuItems.length - 1) {
42
- nextIndex = activeIndex + 1;
43
- }
79
+ nextIndex = activeIndex < menuItems.length - 1 ? activeIndex + 1 : 0;
44
80
  break;
45
81
  case 'ArrowUp':
46
82
  e.preventDefault();
47
- if (activeIndex > 0) {
48
- nextIndex = activeIndex - 1;
49
- }
83
+ nextIndex = activeIndex > 0 ? activeIndex - 1 : menuItems.length - 1;
50
84
  break;
51
85
  case 'Home':
52
86
  e.preventDefault();
@@ -60,42 +94,24 @@
60
94
  case ' ':
61
95
  e.preventDefault();
62
96
  menuItems[activeIndex]?.click();
63
- break;
97
+ closeMenu();
98
+ return;
64
99
  }
65
100
 
66
101
  if (nextIndex !== activeIndex) {
67
102
  setActiveItem(nextIndex);
68
103
  }
69
- };
70
-
71
- trigger.addEventListener('keydown', handleKeyDown);
72
-
73
- popover.addEventListener('toggle', (e) => {
74
- trigger.setAttribute('aria-expanded', e.newState === 'open');
75
- if (e.newState === 'open') {
76
- menuItems = Array.from(menu.querySelectorAll('[role^="menuitem"]:not([disabled])'));
77
- menuItems.forEach((item, index) => {
78
- if (!item.id) item.id = `${menu.id}-item-${index}`;
79
- });
80
- setActiveItem(0);
81
- } else {
82
- setActiveItem(-1);
83
- }
84
104
  });
85
105
 
86
106
  menu.addEventListener('click', (e) => {
87
107
  if (e.target.closest('[role^="menuitem"]')) {
88
- popover.hidePopover();
108
+ closeMenu();
89
109
  }
90
110
  });
91
111
 
92
- menu.addEventListener('mouseover', (e) => {
93
- const item = e.target.closest('[role^="menuitem"]:not([disabled])');
94
- if (item) {
95
- const index = menuItems.indexOf(item);
96
- if (index > -1 && index !== activeIndex) {
97
- setActiveItem(index);
98
- }
112
+ document.addEventListener('click', (e) => {
113
+ if (!dropdownMenuComponent.contains(e.target)) {
114
+ closeMenu();
99
115
  }
100
116
  });
101
117
 
@@ -107,15 +123,14 @@
107
123
  const observer = new MutationObserver((mutations) => {
108
124
  mutations.forEach((mutation) => {
109
125
  mutation.addedNodes.forEach((node) => {
110
- if (node.nodeType === Node.ELEMENT_NODE) {
111
- if (node.matches('.dropdown-menu:not([data-dropdown-menu-initialized])')) {
112
- initDropdownMenu(node);
113
- }
114
- node.querySelectorAll('.dropdown-menu:not([data-dropdown-menu-initialized])').forEach(initDropdownMenu);
126
+ if (node.nodeType !== Node.ELEMENT_NODE) return;
127
+ if (node.matches('.dropdown-menu:not([data-dropdown-menu-initialized])')) {
128
+ initDropdownMenu(node);
115
129
  }
130
+ node.querySelectorAll('.dropdown-menu:not([data-dropdown-menu-initialized])').forEach(initDropdownMenu);
116
131
  });
117
132
  });
118
133
  });
119
-
134
+
120
135
  observer.observe(document.body, { childList: true, subtree: true });
121
136
  })();
@@ -1 +1 @@
1
- (()=>{const e=e=>{const t=e.querySelector("[popovertarget]"),r=e.querySelector("[popover]"),o=r?.querySelector('[role="menu"]');if(!t||!r||!o)return;let n=[],a=-1;const d=e=>{if(a>-1&&n[a]&&n[a].classList.remove("active"),a=e,a>-1&&n[a]){const e=n[a];e.classList.add("active"),t.setAttribute("aria-activedescendant",e.id),e.scrollIntoView({block:"nearest"})}else t.removeAttribute("aria-activedescendant")};t.addEventListener("keydown",(e=>{if(!r.matches(":popover-open"))return void(["ArrowDown","ArrowUp","Enter"," "].includes(e.key)&&(e.preventDefault(),t.click()));let o=a;if(0!==n.length){switch(e.key){case"ArrowDown":e.preventDefault(),a<n.length-1&&(o=a+1);break;case"ArrowUp":e.preventDefault(),a>0&&(o=a-1);break;case"Home":e.preventDefault(),o=0;break;case"End":e.preventDefault(),o=n.length-1;break;case"Enter":case" ":e.preventDefault(),n[a]?.click()}o!==a&&d(o)}})),r.addEventListener("toggle",(e=>{t.setAttribute("aria-expanded","open"===e.newState),"open"===e.newState?(n=Array.from(o.querySelectorAll('[role^="menuitem"]:not([disabled])')),n.forEach(((e,t)=>{e.id||(e.id=`${o.id}-item-${t}`)})),d(0)):d(-1)})),o.addEventListener("click",(e=>{e.target.closest('[role^="menuitem"]')&&r.hidePopover()})),o.addEventListener("mouseover",(e=>{const t=e.target.closest('[role^="menuitem"]:not([disabled])');if(t){const e=n.indexOf(t);e>-1&&e!==a&&d(e)}})),e.dataset.dropdownMenuInitialized=!0};document.querySelectorAll(".dropdown-menu:not([data-dropdown-menu-initialized])").forEach(e);new MutationObserver((t=>{t.forEach((t=>{t.addedNodes.forEach((t=>{t.nodeType===Node.ELEMENT_NODE&&(t.matches(".dropdown-menu:not([data-dropdown-menu-initialized])")&&e(t),t.querySelectorAll(".dropdown-menu:not([data-dropdown-menu-initialized])").forEach(e))}))}))})).observe(document.body,{childList:!0,subtree:!0})})();
1
+ (()=>{const e=e=>{const t=e.querySelector(":scope > button"),r=e.querySelector(":scope > [data-popover]"),n=r.querySelector('[role="menu"]');if(!t||!n||!r)return void console.error("Dropdown menu component is missing a trigger, a menu, or a popover content element.",e);let a=[],o=-1;const d=()=>{"false"!==t.getAttribute("aria-expanded")&&(t.setAttribute("aria-expanded","false"),t.removeAttribute("aria-activedescendant"),r.setAttribute("aria-hidden","true"),t.focus(),o=-1)},i=()=>{t.setAttribute("aria-expanded","true"),r.setAttribute("aria-hidden","false"),a=Array.from(n.querySelectorAll('[role^="menuitem"]:not([disabled])')),a.length>0&&c(0)},c=e=>{if(o>-1&&a[o]&&a[o].classList.remove("active"),o=e,o>-1&&a[o]){const e=a[o];e.classList.add("active"),t.setAttribute("aria-activedescendant",e.id)}else t.removeAttribute("aria-activedescendant")};t.addEventListener("click",(()=>{"true"===t.getAttribute("aria-expanded")?d():i()})),e.addEventListener("keydown",(e=>{const r="true"===t.getAttribute("aria-expanded");if("Escape"===e.key)return void(r&&d());if(!r)return void(["ArrowDown","ArrowUp","Enter"," "].includes(e.key)&&(e.preventDefault(),i()));if(0===a.length)return;let n=o;switch(e.key){case"ArrowDown":e.preventDefault(),n=o<a.length-1?o+1:0;break;case"ArrowUp":e.preventDefault(),n=o>0?o-1:a.length-1;break;case"Home":e.preventDefault(),n=0;break;case"End":e.preventDefault(),n=a.length-1;break;case"Enter":case" ":return e.preventDefault(),a[o]?.click(),void d()}n!==o&&c(n)})),n.addEventListener("click",(e=>{e.target.closest('[role^="menuitem"]')&&d()})),document.addEventListener("click",(t=>{e.contains(t.target)||d()})),e.dataset.dropdownMenuInitialized=!0};document.querySelectorAll(".dropdown-menu:not([data-dropdown-menu-initialized])").forEach(e);new MutationObserver((t=>{t.forEach((t=>{t.addedNodes.forEach((t=>{t.nodeType===Node.ELEMENT_NODE&&(t.matches(".dropdown-menu:not([data-dropdown-menu-initialized])")&&e(t),t.querySelectorAll(".dropdown-menu:not([data-dropdown-menu-initialized])").forEach(e))}))}))})).observe(document.body,{childList:!0,subtree:!0})})();
@@ -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})})();
@@ -1,8 +1,8 @@
1
1
  (() => {
2
2
  const initSelect = (selectComponent) => {
3
- const trigger = selectComponent.querySelector(':scope > [popovertarget]');
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
- updateValue(option);
28
-
29
- trigger.removeAttribute('aria-activedescendant');
30
- options.forEach(opt => opt.classList.remove('active'));
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
- if (!['ArrowDown', 'ArrowUp', 'Enter', 'Home', 'End'].includes(e.key)) {
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 (!popover.matches(':popover-open')) {
70
- if (e.currentTarget === trigger && e.key !== 'Enter') {
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(options[activeIndex]);
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
- popover.addEventListener('toggle', (e) => {
141
- trigger.setAttribute('aria-expanded', e.newState === 'open');
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 > [popovertarget]"),r=t.querySelector(":scope > span"),a=e.querySelector(":scope > [popover]"),i=a.querySelector('[role="listbox"]'),o=e.querySelector(':scope > input[type="hidden"]'),n=e.querySelector('header input[type="text"]');if(!(t&&a&&i&&o))return;const s=Array.from(i.querySelectorAll('[role="option"]'));let c=[...s],l=-1;const d=e=>{e&&(r.innerHTML=e.dataset.label||e.innerHTML,o.value=e.dataset.value,i.querySelector('[role="option"][aria-selected="true"]')?.removeAttribute("aria-selected"),e.setAttribute("aria-selected","true"))},u=e=>{e&&(d(e),t.removeAttribute("aria-activedescendant"),s.forEach((e=>e.classList.remove("active"))),l=-1,a.hidePopover())};if(n){const e=()=>{const e=n.value.trim().toLowerCase();l>-1&&(s[l].classList.remove("active"),t.removeAttribute("aria-activedescendant"),l=-1),c=[],s.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 v=s.find((e=>o.value&&e.dataset.value===o.value));!v&&s.length>0&&(v=s[0]),d(v);const p=e=>{if(!["ArrowDown","ArrowUp","Enter","Home","End"].includes(e.key))return;if(!a.matches(":popover-open"))return void(e.currentTarget===t&&"Enter"!==e.key&&(e.preventDefault(),t.click()));if(e.preventDefault(),"Enter"===e.key)return void(l>-1&&u(s[l]));if(0===c.length)return;const r=l>-1?c.indexOf(s[l]):-1;let i=r;switch(e.key){case"ArrowDown":r<c.length-1&&(i=r+1);break;case"ArrowUp":r>0?i=r-1:-1===r&&(i=0);break;case"Home":i=0;break;case"End":i=c.length-1}if(i!==r){r>-1&&c[r].classList.remove("active");const e=c[i];e.classList.add("active"),l=s.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),i.addEventListener("click",(e=>{const t=e.target.closest('[role="option"]');t&&u(t)})),a.addEventListener("toggle",(e=>{if(t.setAttribute("aria-expanded","open"===e.newState),"open"===e.newState){n&&n.focus();const e=i.querySelector('[role="option"][aria-selected="true"]');let r=null;e&&c.includes(e)?r=e:c.length>0&&(r=c[0]),l>-1&&s[l]?.classList.remove("active"),r?(l=s.indexOf(r),r.classList.add("active"),r.id&&t.setAttribute("aria-activedescendant",r.id),r.scrollIntoView({block:"nearest"})):l=-1}else"closed"===e.newState&&(n&&(n.value="",c=[...s],s.forEach((e=>e.setAttribute("aria-hidden","false")))),t.removeAttribute("aria-activedescendant"),l>-1&&s[l]?.classList.remove("active"),l=-1)})),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})})();
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 content_attrs {object} [optional] - Additional HTML attributes for the dialog content article.
12
- @param content_header_attrs {object} [optional] - Additional HTML attributes for the dialog header.
13
- @param content_body_attrs {object} [optional] - Additional HTML attributes for the dialog body section.
14
- @param content_footer_attrs {object} [optional] - Additional HTML attributes for the dialog footer.
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.
@@ -6,23 +6,28 @@
6
6
  @param menu {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 content_attrs {object} [optional] - Additional HTML attributes for the dropdown content div.
9
+ @param popover_attrs {object} [optional] - Additional HTML attributes for the dropdown content div.
10
10
  #}
11
11
  {% macro dropdown_menu(
12
12
  id=None,
13
13
  trigger,
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 class="dropdown-menu">
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
- {% if key != "class" %}{{ key }}="{{ value }}"{% endif %}
45
+ {{ key }}="{{ value }}"
41
46
  {% endfor %}
42
47
  >
43
- <nav
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
- </nav>
61
+ </div>
57
62
  </div>
58
63
  </div>
59
64
  {% endmacro %}
@@ -77,7 +82,7 @@
77
82
  {{ key }}="{{ value }}"
78
83
  {% endfor %}
79
84
  >
80
- <div role="heading" id="{{ group_label_id }}">{{ item.label }}</div>
85
+ <div role="presentation" id="{{ group_label_id }}">{{ item.label }}</div>
81
86
  {{ render_dropdown_items(item.items, item_id) if item.items }}
82
87
  </div>
83
88
  {% elif item.type == "separator" %}
@@ -85,6 +90,7 @@
85
90
  {% elif item.type == "item" or not item.type %}
86
91
  {% if item.url %}
87
92
  <a
93
+ id="{{ item_id }}"
88
94
  role="menuitem"
89
95
  href="{{ item.url }}"
90
96
  {% for key, value in item.attrs %}
@@ -94,15 +100,15 @@
94
100
  {{ item.label | safe }}
95
101
  </a>
96
102
  {% else %}
97
- <button
103
+ <div
104
+ id="{{ item_id }}"
98
105
  role="menuitem"
99
- type="button"
100
106
  {% for key, value in item.attrs %}
101
107
  {{ key }}="{{ value }}"
102
108
  {% endfor %}
103
109
  >
104
110
  {{ item.label | safe }}
105
- </button>
111
+ </div>
106
112
  {% endif %}
107
113
  {% endif %}
108
114
  {% 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 content_attrs {object} [optional] - Additional HTML attributes for the popover content div.
8
+ @param popover_attrs {object} [optional] - Additional HTML attributes for the popover content div.
9
9
  #}
10
10
  {% macro popover(
11
11
  id=None,
12
12
  trigger,
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
- <button
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
- id="{{ id }}"
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
- {{ caller() if caller }}
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 %}