basecoat-cli 0.3.1 → 0.3.3
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/command.html.jinja +187 -0
- package/dist/assets/jinja/dialog.html.jinja +4 -4
- package/dist/assets/js/all.js +10 -6
- package/dist/assets/js/all.min.js +1 -1
- package/dist/assets/js/command.js +148 -0
- package/dist/assets/js/command.min.js +1 -0
- package/dist/assets/js/select.js +9 -5
- package/dist/assets/js/select.min.js +1 -1
- package/dist/assets/js/toast.js +1 -1
- package/dist/assets/js/toast.min.js +1 -1
- package/dist/assets/nunjucks/command.njk +187 -0
- package/dist/assets/nunjucks/dialog.njk +4 -4
- package/package.json +2 -3
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
{#
|
|
2
|
+
Renders a standalone command menu component with search and keyboard navigation.
|
|
3
|
+
|
|
4
|
+
@param id {string} [optional] - Unique identifier for the command component.
|
|
5
|
+
@param items {array} [optional] - Array of items to render (alternative to using caller).
|
|
6
|
+
@param placeholder {string} [optional] [default="Type a command or search..."] - Placeholder text for the search input.
|
|
7
|
+
@param empty_text {string} [optional] [default="No results found."] - Text displayed when no results match the search.
|
|
8
|
+
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
|
9
|
+
@param input_attrs {object} [optional] - Additional HTML attributes for the search input.
|
|
10
|
+
@param menu_attrs {object} [optional] - Additional HTML attributes for the menu div.
|
|
11
|
+
#}
|
|
12
|
+
{% macro command(
|
|
13
|
+
id=None,
|
|
14
|
+
items=None,
|
|
15
|
+
placeholder="Type a command or search...",
|
|
16
|
+
empty_text="No results found.",
|
|
17
|
+
main_attrs={},
|
|
18
|
+
input_attrs={},
|
|
19
|
+
menu_attrs={}
|
|
20
|
+
) %}
|
|
21
|
+
{% set id = id or ("command-" ~ range(100000, 999999) | random | string) %}
|
|
22
|
+
<div
|
|
23
|
+
id="{{ id }}"
|
|
24
|
+
class="command {{ main_attrs.class }}"
|
|
25
|
+
aria-label="Command menu"
|
|
26
|
+
{% for key, value in main_attrs.items() %}
|
|
27
|
+
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
28
|
+
{% endfor %}
|
|
29
|
+
>
|
|
30
|
+
<header>
|
|
31
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-icon lucide-search"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
|
32
|
+
<input
|
|
33
|
+
type="text"
|
|
34
|
+
id="{{ id }}-input"
|
|
35
|
+
placeholder="{{ placeholder }}"
|
|
36
|
+
autocomplete="off"
|
|
37
|
+
autocorrect="off"
|
|
38
|
+
spellcheck="false"
|
|
39
|
+
aria-autocomplete="list"
|
|
40
|
+
role="combobox"
|
|
41
|
+
aria-expanded="true"
|
|
42
|
+
aria-controls="{{ id }}-menu"
|
|
43
|
+
{% for key, value in input_attrs.items() %}
|
|
44
|
+
{{ key }}="{{ value }}"
|
|
45
|
+
{% endfor %}
|
|
46
|
+
>
|
|
47
|
+
</header>
|
|
48
|
+
<div
|
|
49
|
+
role="menu"
|
|
50
|
+
id="{{ id }}-menu"
|
|
51
|
+
aria-orientation="vertical"
|
|
52
|
+
data-empty="{{ empty_text }}"
|
|
53
|
+
class="scrollbar {{ menu_attrs.class }}"
|
|
54
|
+
{% for key, value in menu_attrs.items() %}
|
|
55
|
+
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
56
|
+
{% endfor %}
|
|
57
|
+
>
|
|
58
|
+
{% if items and items|length > 0 %}
|
|
59
|
+
{{ render_command_items(items, id ~ "-items" if id else "items") }}
|
|
60
|
+
{% else %}
|
|
61
|
+
{{ caller() if caller }}
|
|
62
|
+
{% endif %}
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
{% endmacro %}
|
|
66
|
+
|
|
67
|
+
{#
|
|
68
|
+
Renders a command dialog (modal command palette).
|
|
69
|
+
|
|
70
|
+
@param id {string} [optional] - Unique identifier for the command dialog.
|
|
71
|
+
@param items {array} [optional] - Array of items to render (alternative to using caller).
|
|
72
|
+
@param placeholder {string} [optional] [default="Type a command or search..."] - Placeholder text for the search input.
|
|
73
|
+
@param empty_text {string} [optional] [default="No results found."] - Text displayed when no results match the search.
|
|
74
|
+
@param dialog_attrs {object} [optional] - Additional HTML attributes for the dialog element.
|
|
75
|
+
@param input_attrs {object} [optional] - Additional HTML attributes for the search input.
|
|
76
|
+
@param menu_attrs {object} [optional] - Additional HTML attributes for the menu div.
|
|
77
|
+
@param open {boolean} [optional] [default=False] - Whether the command dialog should be open initially.
|
|
78
|
+
#}
|
|
79
|
+
{% macro command_dialog(
|
|
80
|
+
id=None,
|
|
81
|
+
items=None,
|
|
82
|
+
placeholder="Type a command or search...",
|
|
83
|
+
empty_text="No results found.",
|
|
84
|
+
dialog_attrs={},
|
|
85
|
+
input_attrs={},
|
|
86
|
+
menu_attrs={},
|
|
87
|
+
open=False
|
|
88
|
+
) %}
|
|
89
|
+
{% set id = id or ("command-dialog-" ~ range(100000, 999999) | random | string) %}
|
|
90
|
+
<dialog
|
|
91
|
+
id="{{ id }}"
|
|
92
|
+
class="dialog command-dialog {{ dialog_attrs.class }}"
|
|
93
|
+
aria-label="Command menu"
|
|
94
|
+
{% if open %}open{% endif %}
|
|
95
|
+
onclick="if (event.target === this) this.close()"
|
|
96
|
+
{% for key, value in dialog_attrs.items() %}
|
|
97
|
+
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
98
|
+
{% endfor %}
|
|
99
|
+
>
|
|
100
|
+
<div class="command">
|
|
101
|
+
<header>
|
|
102
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-icon lucide-search"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
|
103
|
+
<input
|
|
104
|
+
type="text"
|
|
105
|
+
id="{{ id }}-input"
|
|
106
|
+
placeholder="{{ placeholder }}"
|
|
107
|
+
autocomplete="off"
|
|
108
|
+
autocorrect="off"
|
|
109
|
+
spellcheck="false"
|
|
110
|
+
aria-autocomplete="list"
|
|
111
|
+
role="combobox"
|
|
112
|
+
aria-expanded="true"
|
|
113
|
+
aria-controls="{{ id }}-menu"
|
|
114
|
+
{% for key, value in input_attrs.items() %}
|
|
115
|
+
{{ key }}="{{ value }}"
|
|
116
|
+
{% endfor %}
|
|
117
|
+
>
|
|
118
|
+
</header>
|
|
119
|
+
<div
|
|
120
|
+
role="menu"
|
|
121
|
+
id="{{ id }}-menu"
|
|
122
|
+
aria-orientation="vertical"
|
|
123
|
+
data-empty="{{ empty_text }}"
|
|
124
|
+
class="scrollbar {{ menu_attrs.class }}"
|
|
125
|
+
{% for key, value in menu_attrs.items() %}
|
|
126
|
+
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
127
|
+
{% endfor %}
|
|
128
|
+
>
|
|
129
|
+
{% if items and items|length > 0 %}
|
|
130
|
+
{{ render_command_items(items, id ~ "-items" if id else "items") }}
|
|
131
|
+
{% else %}
|
|
132
|
+
{{ caller() if caller }}
|
|
133
|
+
{% endif %}
|
|
134
|
+
</div>
|
|
135
|
+
<button type="button" aria-label="Close dialog" onclick="this.closest('dialog').close()">
|
|
136
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
|
137
|
+
</button>
|
|
138
|
+
</div>
|
|
139
|
+
</dialog>
|
|
140
|
+
{% endmacro %}
|
|
141
|
+
|
|
142
|
+
{#
|
|
143
|
+
Renders a list of items for the command component.
|
|
144
|
+
|
|
145
|
+
@param items {array} - Array of items to render. Each item can be:
|
|
146
|
+
- { type: "item", label: "Text", content: "HTML", keywords: "search terms", disabled: true, attrs: {} }
|
|
147
|
+
- { type: "group", label: "Group Title", items: [...] }
|
|
148
|
+
- { type: "separator" }
|
|
149
|
+
Note: Use attrs to add data-label for alternative search text if needed.
|
|
150
|
+
@param parent_id_prefix {string} [optional] - The prefix for the item id.
|
|
151
|
+
#}
|
|
152
|
+
{% macro render_command_items(items, parent_id_prefix="items") %}
|
|
153
|
+
{% for item in items %}
|
|
154
|
+
{% set item_id = parent_id_prefix ~ "-" ~ loop.index %}
|
|
155
|
+
{% if item.type == "group" %}
|
|
156
|
+
{% set group_label_id = item.id if item.id else "group-label-" + item_id %}
|
|
157
|
+
<div
|
|
158
|
+
role="group"
|
|
159
|
+
aria-labelledby="{{ group_label_id }}"
|
|
160
|
+
{% if item.attrs %}
|
|
161
|
+
{% for key, value in item.attrs.items() %}
|
|
162
|
+
{{ key }}="{{ value }}"
|
|
163
|
+
{% endfor %}
|
|
164
|
+
{% endif %}
|
|
165
|
+
>
|
|
166
|
+
<span role="heading" id="{{ group_label_id }}">{{ item.label }}</span>
|
|
167
|
+
{{ render_command_items(item.items, item_id) if item.items }}
|
|
168
|
+
</div>
|
|
169
|
+
{% elif item.type == "separator" %}
|
|
170
|
+
<hr role="separator" />
|
|
171
|
+
{% elif item.type == "item" or not item.type %}
|
|
172
|
+
<div
|
|
173
|
+
id="{{ item_id }}"
|
|
174
|
+
role="menuitem"
|
|
175
|
+
{% if item.keywords %}data-keywords="{{ item.keywords }}"{% endif %}
|
|
176
|
+
{% if item.disabled %}aria-disabled="true"{% endif %}
|
|
177
|
+
{% if item.attrs %}
|
|
178
|
+
{% for key, value in item.attrs.items() %}
|
|
179
|
+
{{ key }}="{{ value }}"
|
|
180
|
+
{% endfor %}
|
|
181
|
+
{% endif %}
|
|
182
|
+
>
|
|
183
|
+
{{ item.content | safe if item.content else item.label }}
|
|
184
|
+
</div>
|
|
185
|
+
{% endif %}
|
|
186
|
+
{% endfor %}
|
|
187
|
+
{% endmacro %}
|
|
@@ -8,7 +8,7 @@
|
|
|
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 dialog_attrs {object} [optional] - Additional HTML attributes for the dialog content
|
|
11
|
+
@param dialog_attrs {object} [optional] - Additional HTML attributes for the dialog content container.
|
|
12
12
|
@param header_attrs {object} [optional] - Additional HTML attributes for the dialog header.
|
|
13
13
|
@param body_attrs {object} [optional] - Additional HTML attributes for the dialog body section.
|
|
14
14
|
@param footer_attrs {object} [optional] - Additional HTML attributes for the dialog footer.
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
{{ key }}="{{ value }}"
|
|
41
41
|
{% endfor %}
|
|
42
42
|
>
|
|
43
|
-
{{ trigger }}
|
|
43
|
+
{{ trigger | safe }}
|
|
44
44
|
</button>
|
|
45
45
|
{% endif %}
|
|
46
46
|
<dialog
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
54
54
|
{% endfor %}
|
|
55
55
|
>
|
|
56
|
-
<
|
|
56
|
+
<div>
|
|
57
57
|
{% if title or description %}
|
|
58
58
|
<header
|
|
59
59
|
{% for key, value in header_attrs %}
|
|
@@ -89,6 +89,6 @@
|
|
|
89
89
|
</button>
|
|
90
90
|
</form>
|
|
91
91
|
{% endif %}
|
|
92
|
-
</
|
|
92
|
+
</div>
|
|
93
93
|
</dialog>
|
|
94
94
|
{% endmacro %}
|
package/dist/assets/js/all.js
CHANGED
|
@@ -360,7 +360,8 @@
|
|
|
360
360
|
return;
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
-
const
|
|
363
|
+
const allOptions = Array.from(listbox.querySelectorAll('[role="option"]'));
|
|
364
|
+
const options = allOptions.filter(opt => opt.getAttribute('aria-disabled') !== 'true');
|
|
364
365
|
let visibleOptions = [...options];
|
|
365
366
|
let activeIndex = -1;
|
|
366
367
|
|
|
@@ -413,7 +414,7 @@
|
|
|
413
414
|
const resetFilter = () => {
|
|
414
415
|
filter.value = '';
|
|
415
416
|
visibleOptions = [...options];
|
|
416
|
-
|
|
417
|
+
allOptions.forEach(opt => opt.setAttribute('aria-hidden', 'false'));
|
|
417
418
|
};
|
|
418
419
|
|
|
419
420
|
if (hasTransition()) {
|
|
@@ -454,11 +455,11 @@
|
|
|
454
455
|
setActiveOption(-1);
|
|
455
456
|
|
|
456
457
|
visibleOptions = [];
|
|
457
|
-
|
|
458
|
+
allOptions.forEach(option => {
|
|
458
459
|
const optionText = (option.dataset.label || option.textContent).trim().toLowerCase();
|
|
459
460
|
const matches = optionText.includes(searchTerm);
|
|
460
461
|
option.setAttribute('aria-hidden', String(!matches));
|
|
461
|
-
if (matches) {
|
|
462
|
+
if (matches && options.includes(option)) {
|
|
462
463
|
visibleOptions.push(option);
|
|
463
464
|
}
|
|
464
465
|
});
|
|
@@ -468,7 +469,10 @@
|
|
|
468
469
|
}
|
|
469
470
|
|
|
470
471
|
let initialOption = options.find(opt => opt.dataset.value === input.value);
|
|
471
|
-
|
|
472
|
+
|
|
473
|
+
if (!initialOption) {
|
|
474
|
+
initialOption = options.find(opt => opt.dataset.value !== undefined) ?? options[0];
|
|
475
|
+
}
|
|
472
476
|
|
|
473
477
|
updateValue(initialOption, false);
|
|
474
478
|
|
|
@@ -890,7 +894,7 @@
|
|
|
890
894
|
clearTimeout(state.timeoutId);
|
|
891
895
|
toasts.delete(element);
|
|
892
896
|
|
|
893
|
-
if (document.activeElement) document.activeElement.blur();
|
|
897
|
+
if (element.contains(document.activeElement)) document.activeElement.blur();
|
|
894
898
|
element.setAttribute('aria-hidden', 'true');
|
|
895
899
|
element.addEventListener('transitionend', () => element.remove(), { once: true });
|
|
896
900
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{const e={};let t=null;const n=()=>{Object.entries(e).forEach((([e,{selector:t,init:n}])=>{document.querySelectorAll(t).forEach(n)}))},a=t=>{t.nodeType===Node.ELEMENT_NODE&&Object.entries(e).forEach((([e,{selector:n,init:a}])=>{t.matches(n)&&a(t),t.querySelectorAll(n).forEach(a)}))},i=()=>{t||(t=new MutationObserver((e=>{e.forEach((e=>{e.addedNodes.forEach(a)}))})),t.observe(document.body,{childList:!0,subtree:!0}))};window.basecoat={register:(t,n,a)=>{e[t]={selector:n,init:a}},init:t=>{const n=e[t];if(!n)return void console.warn(`Component '${t}' not found in registry`);const a=`data-${t}-initialized`;document.querySelectorAll(`[${a}]`).forEach((e=>{e.removeAttribute(a)})),document.querySelectorAll(n.selector).forEach(n.init)},initAll:()=>{Object.entries(e).forEach((([e,{selector:t}])=>{const n=`data-${e}-initialized`;document.querySelectorAll(`[${n}]`).forEach((e=>{e.removeAttribute(n)}))})),n()},start:i,stop:()=>{t&&(t.disconnect(),t=null)}},document.addEventListener("DOMContentLoaded",(()=>{n(),i()}))})(),(()=>{const e=e=>{const t=e.querySelector(":scope > button"),n=e.querySelector(":scope > [data-popover]"),a=n.querySelector('[role="menu"]');if(!t||!a||!n){const i=[];return t||i.push("trigger"),a||i.push("menu"),n||i.push("popover"),void console.error(`Dropdown menu initialisation failed. Missing element(s): ${i.join(", ")}`,e)}let i=[],o=-1;const r=(e=!0)=>{"false"!==t.getAttribute("aria-expanded")&&(t.setAttribute("aria-expanded","false"),t.removeAttribute("aria-activedescendant"),n.setAttribute("aria-hidden","true"),e&&t.focus(),d(-1))},s=(o=!1)=>{document.dispatchEvent(new CustomEvent("basecoat:popover",{detail:{source:e}})),t.setAttribute("aria-expanded","true"),n.setAttribute("aria-hidden","false"),i=Array.from(a.querySelectorAll('[role^="menuitem"]')).filter((e=>!e.hasAttribute("disabled")&&"true"!==e.getAttribute("aria-disabled"))),i.length>0&&o&&("first"===o?d(0):"last"===o&&d(i.length-1))},d=e=>{if(o>-1&&i[o]&&i[o].classList.remove("active"),o=e,o>-1&&i[o]){const e=i[o];e.classList.add("active"),t.setAttribute("aria-activedescendant",e.id)}else t.removeAttribute("aria-activedescendant")};t.addEventListener("click",(()=>{"true"===t.getAttribute("aria-expanded")?r():s(!1)})),e.addEventListener("keydown",(e=>{const n="true"===t.getAttribute("aria-expanded");if("Escape"===e.key)return void(n&&r());if(!n)return void(["Enter"," "].includes(e.key)?(e.preventDefault(),s(!1)):"ArrowDown"===e.key?(e.preventDefault(),s("first")):"ArrowUp"===e.key&&(e.preventDefault(),s("last")));if(0===i.length)return;let a=o;switch(e.key){case"ArrowDown":e.preventDefault(),a=-1===o?0:Math.min(o+1,i.length-1);break;case"ArrowUp":e.preventDefault(),a=-1===o?i.length-1:Math.max(o-1,0);break;case"Home":e.preventDefault(),a=0;break;case"End":e.preventDefault(),a=i.length-1;break;case"Enter":case" ":return e.preventDefault(),i[o]?.click(),void r()}a!==o&&d(a)})),a.addEventListener("mousemove",(e=>{const t=e.target.closest('[role^="menuitem"]');if(t&&i.includes(t)){const e=i.indexOf(t);e!==o&&d(e)}})),a.addEventListener("mouseleave",(()=>{d(-1)})),a.addEventListener("click",(e=>{e.target.closest('[role^="menuitem"]')&&r()})),document.addEventListener("click",(t=>{e.contains(t.target)||r()})),document.addEventListener("basecoat:popover",(t=>{t.detail.source!==e&&r(!1)})),e.dataset.dropdownMenuInitialized=!0,e.dispatchEvent(new CustomEvent("basecoat:initialized"))};window.basecoat&&window.basecoat.register("dropdown-menu",".dropdown-menu:not([data-dropdown-menu-initialized])",e)})(),(()=>{const e=e=>{const t=e.querySelector(":scope > button"),n=e.querySelector(":scope > [data-popover]");if(!t||!n){const a=[];return t||a.push("trigger"),n||a.push("content"),void console.error(`Popover initialisation failed. Missing element(s): ${a.join(", ")}`,e)}const a=(e=!0)=>{"false"!==t.getAttribute("aria-expanded")&&(t.setAttribute("aria-expanded","false"),n.setAttribute("aria-hidden","true"),e&&t.focus())};t.addEventListener("click",(()=>{"true"===t.getAttribute("aria-expanded")?a():(()=>{document.dispatchEvent(new CustomEvent("basecoat:popover",{detail:{source:e}}));const a=n.querySelector("[autofocus]");a&&n.addEventListener("transitionend",(()=>{a.focus()}),{once:!0}),t.setAttribute("aria-expanded","true"),n.setAttribute("aria-hidden","false")})()})),e.addEventListener("keydown",(e=>{"Escape"===e.key&&a()})),document.addEventListener("click",(t=>{e.contains(t.target)||a()})),document.addEventListener("basecoat:popover",(t=>{t.detail.source!==e&&a(!1)})),e.dataset.popoverInitialized=!0,e.dispatchEvent(new CustomEvent("basecoat:initialized"))};window.basecoat&&window.basecoat.register("popover",".popover:not([data-popover-initialized])",e)})(),(()=>{const e=e=>{const t=e.querySelector(":scope > button"),n=t.querySelector(":scope > span"),a=e.querySelector(":scope > [data-popover]"),i=a.querySelector('[role="listbox"]'),o=e.querySelector(':scope > input[type="hidden"]'),r=e.querySelector('header input[type="text"]');if(!(t&&a&&i&&o)){const n=[];return t||n.push("trigger"),a||n.push("popover"),i||n.push("listbox"),o||n.push("input"),void console.error(`Select component initialisation failed. Missing element(s): ${n.join(", ")}`,e)}const s=Array.from(i.querySelectorAll('[role="option"]'));let d=[...s],c=-1;const l=e=>{if(c>-1&&s[c]&&s[c].classList.remove("active"),c=e,c>-1){const e=s[c];e.classList.add("active"),e.id?t.setAttribute("aria-activedescendant",e.id):t.removeAttribute("aria-activedescendant")}else t.removeAttribute("aria-activedescendant")},u=()=>{const e=getComputedStyle(a);return parseFloat(e.transitionDuration)>0||parseFloat(e.transitionDelay)>0},v=(t,a=!0)=>{if(t&&(n.innerHTML=t.dataset.label||t.innerHTML,o.value=t.dataset.value,i.querySelector('[role="option"][aria-selected="true"]')?.removeAttribute("aria-selected"),t.setAttribute("aria-selected","true"),a)){const n=new CustomEvent("change",{detail:{value:t.dataset.value},bubbles:!0});e.dispatchEvent(n)}},p=(e=!0)=>{if("true"!==a.getAttribute("aria-hidden")){if(r){const e=()=>{r.value="",d=[...s],s.forEach((e=>e.setAttribute("aria-hidden","false")))};u()?a.addEventListener("transitionend",e,{once:!0}):e()}e&&t.focus(),a.setAttribute("aria-hidden","true"),t.setAttribute("aria-expanded","false"),l(-1)}},h=e=>{if(!e)return;const t=o.value,n=e.dataset.value;null!=n&&n!==t&&v(e),p()};if(r){const e=()=>{const e=r.value.trim().toLowerCase();l(-1),d=[],s.forEach((t=>{const n=(t.dataset.label||t.textContent).trim().toLowerCase().includes(e);t.setAttribute("aria-hidden",String(!n)),n&&d.push(t)}))};r.addEventListener("input",e)}let b=s.find((e=>e.dataset.value===o.value));!b&&s.length>0&&(b=s[0]),v(b,!1);const m=e=>{const n="false"===a.getAttribute("aria-hidden");if(!["ArrowDown","ArrowUp","Enter","Home","End","Escape"].includes(e.key))return;if(!n)return void("Enter"!==e.key&&"Escape"!==e.key&&(e.preventDefault(),t.click()));if(e.preventDefault(),"Escape"===e.key)return void p();if("Enter"===e.key)return void(c>-1&&h(s[c]));if(0===d.length)return;const i=c>-1?d.indexOf(s[c]):-1;let o=i;switch(e.key){case"ArrowDown":i<d.length-1&&(o=i+1);break;case"ArrowUp":i>0?o=i-1:-1===i&&(o=0);break;case"Home":o=0;break;case"End":o=d.length-1}if(o!==i){const e=d[o];l(s.indexOf(e)),e.scrollIntoView({block:"nearest",behavior:"smooth"})}};i.addEventListener("mousemove",(e=>{const t=e.target.closest('[role="option"]');if(t&&d.includes(t)){const e=s.indexOf(t);e!==c&&l(e)}})),i.addEventListener("mouseleave",(()=>{const e=i.querySelector('[role="option"][aria-selected="true"]');l(e?s.indexOf(e):-1)})),t.addEventListener("keydown",m),r&&r.addEventListener("keydown",m);t.addEventListener("click",(()=>{"true"===t.getAttribute("aria-expanded")?p():(()=>{document.dispatchEvent(new CustomEvent("basecoat:popover",{detail:{source:e}})),r&&(u()?a.addEventListener("transitionend",(()=>{r.focus()}),{once:!0}):r.focus()),a.setAttribute("aria-hidden","false"),t.setAttribute("aria-expanded","true");const n=i.querySelector('[role="option"][aria-selected="true"]');n&&(l(s.indexOf(n)),n.scrollIntoView({block:"nearest"}))})()})),i.addEventListener("click",(e=>{const t=e.target.closest('[role="option"]');t&&h(t)})),document.addEventListener("click",(t=>{e.contains(t.target)||p(!1)})),document.addEventListener("basecoat:popover",(t=>{t.detail.source!==e&&p(!1)})),a.setAttribute("aria-hidden","true"),e.selectByValue=e=>{const t=s.find((t=>t.dataset.value===e));h(t)},e.dataset.selectInitialized=!0,e.dispatchEvent(new CustomEvent("basecoat:initialized"))};window.basecoat&&window.basecoat.register("select","div.select:not([data-select-initialized])",e)})(),(()=>{if(!window.history.__basecoatPatched){const e=window.history.pushState;window.history.pushState=function(...t){e.apply(this,t),window.dispatchEvent(new Event("basecoat:locationchange"))};const t=window.history.replaceState;window.history.replaceState=function(...e){t.apply(this,e),window.dispatchEvent(new Event("basecoat:locationchange"))},window.history.__basecoatPatched=!0}const e=e=>{const t="false"!==e.dataset.initialOpen,n="true"===e.dataset.initialMobileOpen,a=parseInt(e.dataset.breakpoint)||768;let i=a>0?window.innerWidth>=a?t:n:t;const o=()=>{const t=window.location.pathname.replace(/\/$/,"");e.querySelectorAll("a").forEach((e=>{if(e.hasAttribute("data-ignore-current"))return;new URL(e.href).pathname.replace(/\/$/,"")===t?e.setAttribute("aria-current","page"):e.removeAttribute("aria-current")}))},r=()=>{e.setAttribute("aria-hidden",!i),i?e.removeAttribute("inert"):e.setAttribute("inert","")},s=e=>{i=e,r()},d=e.id;document.addEventListener("basecoat:sidebar",(e=>{if(!e.detail?.id||e.detail.id===d)switch(e.detail?.action){case"open":s(!0);break;case"close":s(!1);break;default:s(!i)}})),e.addEventListener("click",(t=>{const n=t.target,i=e.querySelector("nav");if(window.innerWidth<a&&n.closest("a, button")&&!n.closest("[data-keep-mobile-sidebar-open]"))return document.activeElement&&document.activeElement.blur(),void s(!1);(n===e||i&&!i.contains(n))&&(document.activeElement&&document.activeElement.blur(),s(!1))})),window.addEventListener("popstate",o),window.addEventListener("basecoat:locationchange",o),r(),o(),e.dataset.sidebarInitialized=!0,e.dispatchEvent(new CustomEvent("basecoat:initialized"))};window.basecoat&&window.basecoat.register("sidebar",".sidebar:not([data-sidebar-initialized])",e)})(),(()=>{const e=e=>{const t=e.querySelector('[role="tablist"]');if(!t)return;const n=Array.from(t.querySelectorAll('[role="tab"]')),a=n.map((e=>document.getElementById(e.getAttribute("aria-controls")))).filter(Boolean),i=e=>{n.forEach(((e,t)=>{e.setAttribute("aria-selected","false"),e.setAttribute("tabindex","-1"),a[t]&&(a[t].hidden=!0)})),e.setAttribute("aria-selected","true"),e.setAttribute("tabindex","0");const t=document.getElementById(e.getAttribute("aria-controls"));t&&(t.hidden=!1)};t.addEventListener("click",(e=>{const t=e.target.closest('[role="tab"]');t&&i(t)})),t.addEventListener("keydown",(e=>{const t=e.target;if(!n.includes(t))return;let a;const o=n.indexOf(t);switch(e.key){case"ArrowRight":a=n[(o+1)%n.length];break;case"ArrowLeft":a=n[(o-1+n.length)%n.length];break;case"Home":a=n[0];break;case"End":a=n[n.length-1];break;default:return}e.preventDefault(),i(a),a.focus()})),e.dataset.tabsInitialized=!0,e.dispatchEvent(new CustomEvent("basecoat:initialized"))};window.basecoat&&window.basecoat.register("tabs",".tabs:not([data-tabs-initialized])",e)})(),(()=>{let e;const t=new WeakMap;let n=!1;const a={success:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>',error:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>',info:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>',warning:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>'};function i(e){if(e.dataset.toastInitialized)return;const a=parseInt(e.dataset.duration),i=-1!==a?a||("error"===e.dataset.category?5e3:3e3):-1,o={remainingTime:i,timeoutId:null,startTime:null};-1!==i&&(n?o.timeoutId=null:(o.startTime=Date.now(),o.timeoutId=setTimeout((()=>s(e)),i))),t.set(e,o),e.dataset.toastInitialized="true"}function o(){n||(n=!0,e.querySelectorAll('.toast:not([aria-hidden="true"])').forEach((e=>{if(!t.has(e))return;const n=t.get(e);n.timeoutId&&(clearTimeout(n.timeoutId),n.timeoutId=null,n.remainingTime-=Date.now()-n.startTime)})))}function r(){n&&(n=!1,e.querySelectorAll('.toast:not([aria-hidden="true"])').forEach((e=>{if(!t.has(e))return;const n=t.get(e);-1===n.remainingTime||n.timeoutId||(n.remainingTime>0?(n.startTime=Date.now(),n.timeoutId=setTimeout((()=>s(e)),n.remainingTime)):s(e))})))}function s(e){if(!t.has(e))return;const n=t.get(e);clearTimeout(n.timeoutId),t.delete(e),document.activeElement&&document.activeElement.blur(),e.setAttribute("aria-hidden","true"),e.addEventListener("transitionend",(()=>e.remove()),{once:!0})}document.addEventListener("basecoat:toast",(t=>{if(!e)return void console.error("Cannot create toast: toaster container not found on page.");const n=function(e){const{category:t="info",title:n,description:i,action:o,cancel:r,duration:s,icon:d}=e,c=d||t&&a[t]||"",l=n?`<h2>${n}</h2>`:"",u=i?`<p>${i}</p>`:"",v=o?.href?`<a href="${o.href}" class="btn" data-toast-action>${o.label}</a>`:o?.onclick?`<button type="button" class="btn" data-toast-action onclick="${o.onclick}">${o.label}</button>`:"",p=r?`<button type="button" class="btn-outline h-6 text-xs px-2.5 rounded-sm" data-toast-cancel onclick="${r?.onclick}">${r.label}</button>`:"",h=`\n <div\n class="toast"\n role="${"error"===t?"alert":"status"}"\n aria-atomic="true"\n ${t?`data-category="${t}"`:""}\n ${void 0!==s?`data-duration="${s}"`:""}\n >\n <div class="toast-content">\n ${c}\n <section>\n ${l}\n ${u}\n </section>\n ${v||p?`<footer>${v}${p}</footer>`:""}\n </div>\n </div>\n </div>\n `,b=document.createElement("template");return b.innerHTML=h.trim(),b.content.firstChild}(t.detail?.config||{});e.appendChild(n)})),window.basecoat&&(window.basecoat.register("toaster","#toaster:not([data-toaster-initialized])",(function(t){t.dataset.toasterInitialized||(e=t,e.addEventListener("mouseenter",o),e.addEventListener("mouseleave",r),e.addEventListener("click",(e=>{const t=e.target.closest(".toast footer a"),n=e.target.closest(".toast footer button");(t||n)&&s(e.target.closest(".toast"))})),e.querySelectorAll(".toast:not([data-toast-initialized])").forEach(i),e.dataset.toasterInitialized="true",e.dispatchEvent(new CustomEvent("basecoat:initialized")))})),window.basecoat.register("toast",".toast:not([data-toast-initialized])",i))})();
|
|
1
|
+
(()=>{const e={};let t=null;const n=()=>{Object.entries(e).forEach((([e,{selector:t,init:n}])=>{document.querySelectorAll(t).forEach(n)}))},a=t=>{t.nodeType===Node.ELEMENT_NODE&&Object.entries(e).forEach((([e,{selector:n,init:a}])=>{t.matches(n)&&a(t),t.querySelectorAll(n).forEach(a)}))},i=()=>{t||(t=new MutationObserver((e=>{e.forEach((e=>{e.addedNodes.forEach(a)}))})),t.observe(document.body,{childList:!0,subtree:!0}))};window.basecoat={register:(t,n,a)=>{e[t]={selector:n,init:a}},init:t=>{const n=e[t];if(!n)return void console.warn(`Component '${t}' not found in registry`);const a=`data-${t}-initialized`;document.querySelectorAll(`[${a}]`).forEach((e=>{e.removeAttribute(a)})),document.querySelectorAll(n.selector).forEach(n.init)},initAll:()=>{Object.entries(e).forEach((([e,{selector:t}])=>{const n=`data-${e}-initialized`;document.querySelectorAll(`[${n}]`).forEach((e=>{e.removeAttribute(n)}))})),n()},start:i,stop:()=>{t&&(t.disconnect(),t=null)}},document.addEventListener("DOMContentLoaded",(()=>{n(),i()}))})(),(()=>{const e=e=>{const t=e.querySelector(":scope > button"),n=e.querySelector(":scope > [data-popover]"),a=n.querySelector('[role="menu"]');if(!t||!a||!n){const i=[];return t||i.push("trigger"),a||i.push("menu"),n||i.push("popover"),void console.error(`Dropdown menu initialisation failed. Missing element(s): ${i.join(", ")}`,e)}let i=[],o=-1;const r=(e=!0)=>{"false"!==t.getAttribute("aria-expanded")&&(t.setAttribute("aria-expanded","false"),t.removeAttribute("aria-activedescendant"),n.setAttribute("aria-hidden","true"),e&&t.focus(),d(-1))},s=(o=!1)=>{document.dispatchEvent(new CustomEvent("basecoat:popover",{detail:{source:e}})),t.setAttribute("aria-expanded","true"),n.setAttribute("aria-hidden","false"),i=Array.from(a.querySelectorAll('[role^="menuitem"]')).filter((e=>!e.hasAttribute("disabled")&&"true"!==e.getAttribute("aria-disabled"))),i.length>0&&o&&("first"===o?d(0):"last"===o&&d(i.length-1))},d=e=>{if(o>-1&&i[o]&&i[o].classList.remove("active"),o=e,o>-1&&i[o]){const e=i[o];e.classList.add("active"),t.setAttribute("aria-activedescendant",e.id)}else t.removeAttribute("aria-activedescendant")};t.addEventListener("click",(()=>{"true"===t.getAttribute("aria-expanded")?r():s(!1)})),e.addEventListener("keydown",(e=>{const n="true"===t.getAttribute("aria-expanded");if("Escape"===e.key)return void(n&&r());if(!n)return void(["Enter"," "].includes(e.key)?(e.preventDefault(),s(!1)):"ArrowDown"===e.key?(e.preventDefault(),s("first")):"ArrowUp"===e.key&&(e.preventDefault(),s("last")));if(0===i.length)return;let a=o;switch(e.key){case"ArrowDown":e.preventDefault(),a=-1===o?0:Math.min(o+1,i.length-1);break;case"ArrowUp":e.preventDefault(),a=-1===o?i.length-1:Math.max(o-1,0);break;case"Home":e.preventDefault(),a=0;break;case"End":e.preventDefault(),a=i.length-1;break;case"Enter":case" ":return e.preventDefault(),i[o]?.click(),void r()}a!==o&&d(a)})),a.addEventListener("mousemove",(e=>{const t=e.target.closest('[role^="menuitem"]');if(t&&i.includes(t)){const e=i.indexOf(t);e!==o&&d(e)}})),a.addEventListener("mouseleave",(()=>{d(-1)})),a.addEventListener("click",(e=>{e.target.closest('[role^="menuitem"]')&&r()})),document.addEventListener("click",(t=>{e.contains(t.target)||r()})),document.addEventListener("basecoat:popover",(t=>{t.detail.source!==e&&r(!1)})),e.dataset.dropdownMenuInitialized=!0,e.dispatchEvent(new CustomEvent("basecoat:initialized"))};window.basecoat&&window.basecoat.register("dropdown-menu",".dropdown-menu:not([data-dropdown-menu-initialized])",e)})(),(()=>{const e=e=>{const t=e.querySelector(":scope > button"),n=e.querySelector(":scope > [data-popover]");if(!t||!n){const a=[];return t||a.push("trigger"),n||a.push("content"),void console.error(`Popover initialisation failed. Missing element(s): ${a.join(", ")}`,e)}const a=(e=!0)=>{"false"!==t.getAttribute("aria-expanded")&&(t.setAttribute("aria-expanded","false"),n.setAttribute("aria-hidden","true"),e&&t.focus())};t.addEventListener("click",(()=>{"true"===t.getAttribute("aria-expanded")?a():(()=>{document.dispatchEvent(new CustomEvent("basecoat:popover",{detail:{source:e}}));const a=n.querySelector("[autofocus]");a&&n.addEventListener("transitionend",(()=>{a.focus()}),{once:!0}),t.setAttribute("aria-expanded","true"),n.setAttribute("aria-hidden","false")})()})),e.addEventListener("keydown",(e=>{"Escape"===e.key&&a()})),document.addEventListener("click",(t=>{e.contains(t.target)||a()})),document.addEventListener("basecoat:popover",(t=>{t.detail.source!==e&&a(!1)})),e.dataset.popoverInitialized=!0,e.dispatchEvent(new CustomEvent("basecoat:initialized"))};window.basecoat&&window.basecoat.register("popover",".popover:not([data-popover-initialized])",e)})(),(()=>{const e=e=>{const t=e.querySelector(":scope > button"),n=t.querySelector(":scope > span"),a=e.querySelector(":scope > [data-popover]"),i=a.querySelector('[role="listbox"]'),o=e.querySelector(':scope > input[type="hidden"]'),r=e.querySelector('header input[type="text"]');if(!(t&&a&&i&&o)){const n=[];return t||n.push("trigger"),a||n.push("popover"),i||n.push("listbox"),o||n.push("input"),void console.error(`Select component initialisation failed. Missing element(s): ${n.join(", ")}`,e)}const s=Array.from(i.querySelectorAll('[role="option"]')),d=s.filter((e=>"true"!==e.getAttribute("aria-disabled")));let c=[...d],l=-1;const u=e=>{if(l>-1&&d[l]&&d[l].classList.remove("active"),l=e,l>-1){const e=d[l];e.classList.add("active"),e.id?t.setAttribute("aria-activedescendant",e.id):t.removeAttribute("aria-activedescendant")}else t.removeAttribute("aria-activedescendant")},v=()=>{const e=getComputedStyle(a);return parseFloat(e.transitionDuration)>0||parseFloat(e.transitionDelay)>0},p=(t,a=!0)=>{if(t&&(n.innerHTML=t.dataset.label||t.innerHTML,o.value=t.dataset.value,i.querySelector('[role="option"][aria-selected="true"]')?.removeAttribute("aria-selected"),t.setAttribute("aria-selected","true"),a)){const n=new CustomEvent("change",{detail:{value:t.dataset.value},bubbles:!0});e.dispatchEvent(n)}},b=(e=!0)=>{if("true"!==a.getAttribute("aria-hidden")){if(r){const e=()=>{r.value="",c=[...d],s.forEach((e=>e.setAttribute("aria-hidden","false")))};v()?a.addEventListener("transitionend",e,{once:!0}):e()}e&&t.focus(),a.setAttribute("aria-hidden","true"),t.setAttribute("aria-expanded","false"),u(-1)}},h=e=>{if(!e)return;const t=o.value,n=e.dataset.value;null!=n&&n!==t&&p(e),b()};if(r){const e=()=>{const e=r.value.trim().toLowerCase();u(-1),c=[],s.forEach((t=>{const n=(t.dataset.label||t.textContent).trim().toLowerCase().includes(e);t.setAttribute("aria-hidden",String(!n)),n&&d.includes(t)&&c.push(t)}))};r.addEventListener("input",e)}let m=d.find((e=>e.dataset.value===o.value));m||(m=d.find((e=>void 0!==e.dataset.value))??d[0]),p(m,!1);const w=e=>{const n="false"===a.getAttribute("aria-hidden");if(!["ArrowDown","ArrowUp","Enter","Home","End","Escape"].includes(e.key))return;if(!n)return void("Enter"!==e.key&&"Escape"!==e.key&&(e.preventDefault(),t.click()));if(e.preventDefault(),"Escape"===e.key)return void b();if("Enter"===e.key)return void(l>-1&&h(d[l]));if(0===c.length)return;const i=l>-1?c.indexOf(d[l]):-1;let o=i;switch(e.key){case"ArrowDown":i<c.length-1&&(o=i+1);break;case"ArrowUp":i>0?o=i-1:-1===i&&(o=0);break;case"Home":o=0;break;case"End":o=c.length-1}if(o!==i){const e=c[o];u(d.indexOf(e)),e.scrollIntoView({block:"nearest",behavior:"smooth"})}};i.addEventListener("mousemove",(e=>{const t=e.target.closest('[role="option"]');if(t&&c.includes(t)){const e=d.indexOf(t);e!==l&&u(e)}})),i.addEventListener("mouseleave",(()=>{const e=i.querySelector('[role="option"][aria-selected="true"]');u(e?d.indexOf(e):-1)})),t.addEventListener("keydown",w),r&&r.addEventListener("keydown",w);t.addEventListener("click",(()=>{"true"===t.getAttribute("aria-expanded")?b():(()=>{document.dispatchEvent(new CustomEvent("basecoat:popover",{detail:{source:e}})),r&&(v()?a.addEventListener("transitionend",(()=>{r.focus()}),{once:!0}):r.focus()),a.setAttribute("aria-hidden","false"),t.setAttribute("aria-expanded","true");const n=i.querySelector('[role="option"][aria-selected="true"]');n&&(u(d.indexOf(n)),n.scrollIntoView({block:"nearest"}))})()})),i.addEventListener("click",(e=>{const t=e.target.closest('[role="option"]');t&&h(t)})),document.addEventListener("click",(t=>{e.contains(t.target)||b(!1)})),document.addEventListener("basecoat:popover",(t=>{t.detail.source!==e&&b(!1)})),a.setAttribute("aria-hidden","true"),e.selectByValue=e=>{const t=d.find((t=>t.dataset.value===e));h(t)},e.dataset.selectInitialized=!0,e.dispatchEvent(new CustomEvent("basecoat:initialized"))};window.basecoat&&window.basecoat.register("select","div.select:not([data-select-initialized])",e)})(),(()=>{if(!window.history.__basecoatPatched){const e=window.history.pushState;window.history.pushState=function(...t){e.apply(this,t),window.dispatchEvent(new Event("basecoat:locationchange"))};const t=window.history.replaceState;window.history.replaceState=function(...e){t.apply(this,e),window.dispatchEvent(new Event("basecoat:locationchange"))},window.history.__basecoatPatched=!0}const e=e=>{const t="false"!==e.dataset.initialOpen,n="true"===e.dataset.initialMobileOpen,a=parseInt(e.dataset.breakpoint)||768;let i=a>0?window.innerWidth>=a?t:n:t;const o=()=>{const t=window.location.pathname.replace(/\/$/,"");e.querySelectorAll("a").forEach((e=>{if(e.hasAttribute("data-ignore-current"))return;new URL(e.href).pathname.replace(/\/$/,"")===t?e.setAttribute("aria-current","page"):e.removeAttribute("aria-current")}))},r=()=>{e.setAttribute("aria-hidden",!i),i?e.removeAttribute("inert"):e.setAttribute("inert","")},s=e=>{i=e,r()},d=e.id;document.addEventListener("basecoat:sidebar",(e=>{if(!e.detail?.id||e.detail.id===d)switch(e.detail?.action){case"open":s(!0);break;case"close":s(!1);break;default:s(!i)}})),e.addEventListener("click",(t=>{const n=t.target,i=e.querySelector("nav");if(window.innerWidth<a&&n.closest("a, button")&&!n.closest("[data-keep-mobile-sidebar-open]"))return document.activeElement&&document.activeElement.blur(),void s(!1);(n===e||i&&!i.contains(n))&&(document.activeElement&&document.activeElement.blur(),s(!1))})),window.addEventListener("popstate",o),window.addEventListener("basecoat:locationchange",o),r(),o(),e.dataset.sidebarInitialized=!0,e.dispatchEvent(new CustomEvent("basecoat:initialized"))};window.basecoat&&window.basecoat.register("sidebar",".sidebar:not([data-sidebar-initialized])",e)})(),(()=>{const e=e=>{const t=e.querySelector('[role="tablist"]');if(!t)return;const n=Array.from(t.querySelectorAll('[role="tab"]')),a=n.map((e=>document.getElementById(e.getAttribute("aria-controls")))).filter(Boolean),i=e=>{n.forEach(((e,t)=>{e.setAttribute("aria-selected","false"),e.setAttribute("tabindex","-1"),a[t]&&(a[t].hidden=!0)})),e.setAttribute("aria-selected","true"),e.setAttribute("tabindex","0");const t=document.getElementById(e.getAttribute("aria-controls"));t&&(t.hidden=!1)};t.addEventListener("click",(e=>{const t=e.target.closest('[role="tab"]');t&&i(t)})),t.addEventListener("keydown",(e=>{const t=e.target;if(!n.includes(t))return;let a;const o=n.indexOf(t);switch(e.key){case"ArrowRight":a=n[(o+1)%n.length];break;case"ArrowLeft":a=n[(o-1+n.length)%n.length];break;case"Home":a=n[0];break;case"End":a=n[n.length-1];break;default:return}e.preventDefault(),i(a),a.focus()})),e.dataset.tabsInitialized=!0,e.dispatchEvent(new CustomEvent("basecoat:initialized"))};window.basecoat&&window.basecoat.register("tabs",".tabs:not([data-tabs-initialized])",e)})(),(()=>{let e;const t=new WeakMap;let n=!1;const a={success:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>',error:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>',info:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>',warning:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>'};function i(e){if(e.dataset.toastInitialized)return;const a=parseInt(e.dataset.duration),i=-1!==a?a||("error"===e.dataset.category?5e3:3e3):-1,o={remainingTime:i,timeoutId:null,startTime:null};-1!==i&&(n?o.timeoutId=null:(o.startTime=Date.now(),o.timeoutId=setTimeout((()=>s(e)),i))),t.set(e,o),e.dataset.toastInitialized="true"}function o(){n||(n=!0,e.querySelectorAll('.toast:not([aria-hidden="true"])').forEach((e=>{if(!t.has(e))return;const n=t.get(e);n.timeoutId&&(clearTimeout(n.timeoutId),n.timeoutId=null,n.remainingTime-=Date.now()-n.startTime)})))}function r(){n&&(n=!1,e.querySelectorAll('.toast:not([aria-hidden="true"])').forEach((e=>{if(!t.has(e))return;const n=t.get(e);-1===n.remainingTime||n.timeoutId||(n.remainingTime>0?(n.startTime=Date.now(),n.timeoutId=setTimeout((()=>s(e)),n.remainingTime)):s(e))})))}function s(e){if(!t.has(e))return;const n=t.get(e);clearTimeout(n.timeoutId),t.delete(e),e.contains(document.activeElement)&&document.activeElement.blur(),e.setAttribute("aria-hidden","true"),e.addEventListener("transitionend",(()=>e.remove()),{once:!0})}document.addEventListener("basecoat:toast",(t=>{if(!e)return void console.error("Cannot create toast: toaster container not found on page.");const n=function(e){const{category:t="info",title:n,description:i,action:o,cancel:r,duration:s,icon:d}=e,c=d||t&&a[t]||"",l=n?`<h2>${n}</h2>`:"",u=i?`<p>${i}</p>`:"",v=o?.href?`<a href="${o.href}" class="btn" data-toast-action>${o.label}</a>`:o?.onclick?`<button type="button" class="btn" data-toast-action onclick="${o.onclick}">${o.label}</button>`:"",p=r?`<button type="button" class="btn-outline h-6 text-xs px-2.5 rounded-sm" data-toast-cancel onclick="${r?.onclick}">${r.label}</button>`:"",b=`\n <div\n class="toast"\n role="${"error"===t?"alert":"status"}"\n aria-atomic="true"\n ${t?`data-category="${t}"`:""}\n ${void 0!==s?`data-duration="${s}"`:""}\n >\n <div class="toast-content">\n ${c}\n <section>\n ${l}\n ${u}\n </section>\n ${v||p?`<footer>${v}${p}</footer>`:""}\n </div>\n </div>\n </div>\n `,h=document.createElement("template");return h.innerHTML=b.trim(),h.content.firstChild}(t.detail?.config||{});e.appendChild(n)})),window.basecoat&&(window.basecoat.register("toaster","#toaster:not([data-toaster-initialized])",(function(t){t.dataset.toasterInitialized||(e=t,e.addEventListener("mouseenter",o),e.addEventListener("mouseleave",r),e.addEventListener("click",(e=>{const t=e.target.closest(".toast footer a"),n=e.target.closest(".toast footer button");(t||n)&&s(e.target.closest(".toast"))})),e.querySelectorAll(".toast:not([data-toast-initialized])").forEach(i),e.dataset.toasterInitialized="true",e.dispatchEvent(new CustomEvent("basecoat:initialized")))})),window.basecoat.register("toast",".toast:not([data-toast-initialized])",i))})();
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
const initCommand = (container) => {
|
|
3
|
+
const input = container.querySelector('header input');
|
|
4
|
+
const menu = container.querySelector('[role="menu"]');
|
|
5
|
+
|
|
6
|
+
if (!input || !menu) {
|
|
7
|
+
const missing = [];
|
|
8
|
+
if (!input) missing.push('input');
|
|
9
|
+
if (!menu) missing.push('menu');
|
|
10
|
+
console.error(`Command component initialization failed. Missing element(s): ${missing.join(', ')}`, container);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const allMenuItems = Array.from(menu.querySelectorAll('[role="menuitem"]'));
|
|
15
|
+
const menuItems = allMenuItems.filter(item =>
|
|
16
|
+
!item.hasAttribute('disabled') &&
|
|
17
|
+
item.getAttribute('aria-disabled') !== 'true'
|
|
18
|
+
);
|
|
19
|
+
let visibleMenuItems = [...menuItems];
|
|
20
|
+
let activeIndex = -1;
|
|
21
|
+
|
|
22
|
+
const setActiveItem = (index) => {
|
|
23
|
+
if (activeIndex > -1 && menuItems[activeIndex]) {
|
|
24
|
+
menuItems[activeIndex].classList.remove('active');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
activeIndex = index;
|
|
28
|
+
|
|
29
|
+
if (activeIndex > -1) {
|
|
30
|
+
const activeItem = menuItems[activeIndex];
|
|
31
|
+
activeItem.classList.add('active');
|
|
32
|
+
if (activeItem.id) {
|
|
33
|
+
input.setAttribute('aria-activedescendant', activeItem.id);
|
|
34
|
+
} else {
|
|
35
|
+
input.removeAttribute('aria-activedescendant');
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
input.removeAttribute('aria-activedescendant');
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const filterMenuItems = () => {
|
|
43
|
+
const searchTerm = input.value.trim().toLowerCase();
|
|
44
|
+
|
|
45
|
+
setActiveItem(-1);
|
|
46
|
+
|
|
47
|
+
visibleMenuItems = [];
|
|
48
|
+
allMenuItems.forEach(item => {
|
|
49
|
+
const itemText = (item.dataset.label || item.textContent).trim().toLowerCase();
|
|
50
|
+
const keywords = (item.dataset.keywords || '').toLowerCase();
|
|
51
|
+
const matches = itemText.includes(searchTerm) || keywords.includes(searchTerm);
|
|
52
|
+
item.setAttribute('aria-hidden', String(!matches));
|
|
53
|
+
if (matches && menuItems.includes(item)) {
|
|
54
|
+
visibleMenuItems.push(item);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (visibleMenuItems.length > 0) {
|
|
59
|
+
setActiveItem(menuItems.indexOf(visibleMenuItems[0]));
|
|
60
|
+
visibleMenuItems[0].scrollIntoView({ block: 'nearest' });
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
input.addEventListener('input', filterMenuItems);
|
|
65
|
+
|
|
66
|
+
const handleKeyNavigation = (event) => {
|
|
67
|
+
if (!['ArrowDown', 'ArrowUp', 'Enter', 'Home', 'End'].includes(event.key)) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (event.key === 'Enter') {
|
|
72
|
+
event.preventDefault();
|
|
73
|
+
if (activeIndex > -1) {
|
|
74
|
+
menuItems[activeIndex]?.click();
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (visibleMenuItems.length === 0) return;
|
|
80
|
+
|
|
81
|
+
event.preventDefault();
|
|
82
|
+
|
|
83
|
+
const currentVisibleIndex = activeIndex > -1 ? visibleMenuItems.indexOf(menuItems[activeIndex]) : -1;
|
|
84
|
+
let nextVisibleIndex = currentVisibleIndex;
|
|
85
|
+
|
|
86
|
+
switch (event.key) {
|
|
87
|
+
case 'ArrowDown':
|
|
88
|
+
if (currentVisibleIndex < visibleMenuItems.length - 1) {
|
|
89
|
+
nextVisibleIndex = currentVisibleIndex + 1;
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
case 'ArrowUp':
|
|
93
|
+
if (currentVisibleIndex > 0) {
|
|
94
|
+
nextVisibleIndex = currentVisibleIndex - 1;
|
|
95
|
+
} else if (currentVisibleIndex === -1) {
|
|
96
|
+
nextVisibleIndex = 0;
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
case 'Home':
|
|
100
|
+
nextVisibleIndex = 0;
|
|
101
|
+
break;
|
|
102
|
+
case 'End':
|
|
103
|
+
nextVisibleIndex = visibleMenuItems.length - 1;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (nextVisibleIndex !== currentVisibleIndex) {
|
|
108
|
+
const newActiveItem = visibleMenuItems[nextVisibleIndex];
|
|
109
|
+
setActiveItem(menuItems.indexOf(newActiveItem));
|
|
110
|
+
newActiveItem.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
menu.addEventListener('mousemove', (event) => {
|
|
115
|
+
const menuItem = event.target.closest('[role="menuitem"]');
|
|
116
|
+
if (menuItem && visibleMenuItems.includes(menuItem)) {
|
|
117
|
+
const index = menuItems.indexOf(menuItem);
|
|
118
|
+
if (index !== activeIndex) {
|
|
119
|
+
setActiveItem(index);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
menu.addEventListener('click', (event) => {
|
|
125
|
+
const clickedItem = event.target.closest('[role="menuitem"]');
|
|
126
|
+
if (clickedItem && visibleMenuItems.includes(clickedItem)) {
|
|
127
|
+
const dialog = container.closest('dialog.command-dialog');
|
|
128
|
+
if (dialog && !clickedItem.hasAttribute('data-keep-command-open')) {
|
|
129
|
+
dialog.close();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
input.addEventListener('keydown', handleKeyNavigation);
|
|
135
|
+
|
|
136
|
+
if (visibleMenuItems.length > 0) {
|
|
137
|
+
setActiveItem(menuItems.indexOf(visibleMenuItems[0]));
|
|
138
|
+
visibleMenuItems[0].scrollIntoView({ block: 'nearest' });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
container.dataset.commandInitialized = true;
|
|
142
|
+
container.dispatchEvent(new CustomEvent('basecoat:initialized'));
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
if (window.basecoat) {
|
|
146
|
+
window.basecoat.register('command', '.command:not([data-command-initialized])', initCommand);
|
|
147
|
+
}
|
|
148
|
+
})();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(()=>{const e=e=>{const t=e.querySelector("header input"),n=e.querySelector('[role="menu"]');if(!t||!n){const i=[];return t||i.push("input"),n||i.push("menu"),void console.error(`Command component initialization failed. Missing element(s): ${i.join(", ")}`,e)}const i=Array.from(n.querySelectorAll('[role="menuitem"]')),o=i.filter((e=>!e.hasAttribute("disabled")&&"true"!==e.getAttribute("aria-disabled")));let a=[...o],r=-1;const s=e=>{if(r>-1&&o[r]&&o[r].classList.remove("active"),r=e,r>-1){const e=o[r];e.classList.add("active"),e.id?t.setAttribute("aria-activedescendant",e.id):t.removeAttribute("aria-activedescendant")}else t.removeAttribute("aria-activedescendant")};t.addEventListener("input",(()=>{const e=t.value.trim().toLowerCase();s(-1),a=[],i.forEach((t=>{const n=(t.dataset.label||t.textContent).trim().toLowerCase(),i=(t.dataset.keywords||"").toLowerCase(),r=n.includes(e)||i.includes(e);t.setAttribute("aria-hidden",String(!r)),r&&o.includes(t)&&a.push(t)})),a.length>0&&(s(o.indexOf(a[0])),a[0].scrollIntoView({block:"nearest"}))}));n.addEventListener("mousemove",(e=>{const t=e.target.closest('[role="menuitem"]');if(t&&a.includes(t)){const e=o.indexOf(t);e!==r&&s(e)}})),n.addEventListener("click",(t=>{const n=t.target.closest('[role="menuitem"]');if(n&&a.includes(n)){const t=e.closest("dialog.command-dialog");t&&!n.hasAttribute("data-keep-command-open")&&t.close()}})),t.addEventListener("keydown",(e=>{if(!["ArrowDown","ArrowUp","Enter","Home","End"].includes(e.key))return;if("Enter"===e.key)return e.preventDefault(),void(r>-1&&o[r]?.click());if(0===a.length)return;e.preventDefault();const t=r>-1?a.indexOf(o[r]):-1;let n=t;switch(e.key){case"ArrowDown":t<a.length-1&&(n=t+1);break;case"ArrowUp":t>0?n=t-1:-1===t&&(n=0);break;case"Home":n=0;break;case"End":n=a.length-1}if(n!==t){const e=a[n];s(o.indexOf(e)),e.scrollIntoView({block:"nearest",behavior:"smooth"})}})),a.length>0&&(s(o.indexOf(a[0])),a[0].scrollIntoView({block:"nearest"})),e.dataset.commandInitialized=!0,e.dispatchEvent(new CustomEvent("basecoat:initialized"))};window.basecoat&&window.basecoat.register("command",".command:not([data-command-initialized])",e)})();
|
package/dist/assets/js/select.js
CHANGED
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const
|
|
19
|
+
const allOptions = Array.from(listbox.querySelectorAll('[role="option"]'));
|
|
20
|
+
const options = allOptions.filter(opt => opt.getAttribute('aria-disabled') !== 'true');
|
|
20
21
|
let visibleOptions = [...options];
|
|
21
22
|
let activeIndex = -1;
|
|
22
23
|
|
|
@@ -69,7 +70,7 @@
|
|
|
69
70
|
const resetFilter = () => {
|
|
70
71
|
filter.value = '';
|
|
71
72
|
visibleOptions = [...options];
|
|
72
|
-
|
|
73
|
+
allOptions.forEach(opt => opt.setAttribute('aria-hidden', 'false'));
|
|
73
74
|
};
|
|
74
75
|
|
|
75
76
|
if (hasTransition()) {
|
|
@@ -110,11 +111,11 @@
|
|
|
110
111
|
setActiveOption(-1);
|
|
111
112
|
|
|
112
113
|
visibleOptions = [];
|
|
113
|
-
|
|
114
|
+
allOptions.forEach(option => {
|
|
114
115
|
const optionText = (option.dataset.label || option.textContent).trim().toLowerCase();
|
|
115
116
|
const matches = optionText.includes(searchTerm);
|
|
116
117
|
option.setAttribute('aria-hidden', String(!matches));
|
|
117
|
-
if (matches) {
|
|
118
|
+
if (matches && options.includes(option)) {
|
|
118
119
|
visibleOptions.push(option);
|
|
119
120
|
}
|
|
120
121
|
});
|
|
@@ -124,7 +125,10 @@
|
|
|
124
125
|
}
|
|
125
126
|
|
|
126
127
|
let initialOption = options.find(opt => opt.dataset.value === input.value);
|
|
127
|
-
|
|
128
|
+
|
|
129
|
+
if (!initialOption) {
|
|
130
|
+
initialOption = options.find(opt => opt.dataset.value !== undefined) ?? options[0];
|
|
131
|
+
}
|
|
128
132
|
|
|
129
133
|
updateValue(initialOption, false);
|
|
130
134
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{const e=e=>{const t=e.querySelector(":scope > button"),
|
|
1
|
+
(()=>{const e=e=>{const t=e.querySelector(":scope > button"),a=t.querySelector(":scope > span"),i=e.querySelector(":scope > [data-popover]"),n=i.querySelector('[role="listbox"]'),r=e.querySelector(':scope > input[type="hidden"]'),o=e.querySelector('header input[type="text"]');if(!(t&&i&&n&&r)){const a=[];return t||a.push("trigger"),i||a.push("popover"),n||a.push("listbox"),r||a.push("input"),void console.error(`Select component initialisation failed. Missing element(s): ${a.join(", ")}`,e)}const s=Array.from(n.querySelectorAll('[role="option"]')),d=s.filter((e=>"true"!==e.getAttribute("aria-disabled")));let c=[...d],l=-1;const u=e=>{if(l>-1&&d[l]&&d[l].classList.remove("active"),l=e,l>-1){const e=d[l];e.classList.add("active"),e.id?t.setAttribute("aria-activedescendant",e.id):t.removeAttribute("aria-activedescendant")}else t.removeAttribute("aria-activedescendant")},v=()=>{const e=getComputedStyle(i);return parseFloat(e.transitionDuration)>0||parseFloat(e.transitionDelay)>0},p=(t,i=!0)=>{if(t&&(a.innerHTML=t.dataset.label||t.innerHTML,r.value=t.dataset.value,n.querySelector('[role="option"][aria-selected="true"]')?.removeAttribute("aria-selected"),t.setAttribute("aria-selected","true"),i)){const a=new CustomEvent("change",{detail:{value:t.dataset.value},bubbles:!0});e.dispatchEvent(a)}},b=(e=!0)=>{if("true"!==i.getAttribute("aria-hidden")){if(o){const e=()=>{o.value="",c=[...d],s.forEach((e=>e.setAttribute("aria-hidden","false")))};v()?i.addEventListener("transitionend",e,{once:!0}):e()}e&&t.focus(),i.setAttribute("aria-hidden","true"),t.setAttribute("aria-expanded","false"),u(-1)}},f=e=>{if(!e)return;const t=r.value,a=e.dataset.value;null!=a&&a!==t&&p(e),b()};if(o){const e=()=>{const e=o.value.trim().toLowerCase();u(-1),c=[],s.forEach((t=>{const a=(t.dataset.label||t.textContent).trim().toLowerCase().includes(e);t.setAttribute("aria-hidden",String(!a)),a&&d.includes(t)&&c.push(t)}))};o.addEventListener("input",e)}let E=d.find((e=>e.dataset.value===r.value));E||(E=d.find((e=>void 0!==e.dataset.value))??d[0]),p(E,!1);const h=e=>{const a="false"===i.getAttribute("aria-hidden");if(!["ArrowDown","ArrowUp","Enter","Home","End","Escape"].includes(e.key))return;if(!a)return void("Enter"!==e.key&&"Escape"!==e.key&&(e.preventDefault(),t.click()));if(e.preventDefault(),"Escape"===e.key)return void b();if("Enter"===e.key)return void(l>-1&&f(d[l]));if(0===c.length)return;const n=l>-1?c.indexOf(d[l]):-1;let r=n;switch(e.key){case"ArrowDown":n<c.length-1&&(r=n+1);break;case"ArrowUp":n>0?r=n-1:-1===n&&(r=0);break;case"Home":r=0;break;case"End":r=c.length-1}if(r!==n){const e=c[r];u(d.indexOf(e)),e.scrollIntoView({block:"nearest",behavior:"smooth"})}};n.addEventListener("mousemove",(e=>{const t=e.target.closest('[role="option"]');if(t&&c.includes(t)){const e=d.indexOf(t);e!==l&&u(e)}})),n.addEventListener("mouseleave",(()=>{const e=n.querySelector('[role="option"][aria-selected="true"]');u(e?d.indexOf(e):-1)})),t.addEventListener("keydown",h),o&&o.addEventListener("keydown",h);t.addEventListener("click",(()=>{"true"===t.getAttribute("aria-expanded")?b():(()=>{document.dispatchEvent(new CustomEvent("basecoat:popover",{detail:{source:e}})),o&&(v()?i.addEventListener("transitionend",(()=>{o.focus()}),{once:!0}):o.focus()),i.setAttribute("aria-hidden","false"),t.setAttribute("aria-expanded","true");const a=n.querySelector('[role="option"][aria-selected="true"]');a&&(u(d.indexOf(a)),a.scrollIntoView({block:"nearest"}))})()})),n.addEventListener("click",(e=>{const t=e.target.closest('[role="option"]');t&&f(t)})),document.addEventListener("click",(t=>{e.contains(t.target)||b(!1)})),document.addEventListener("basecoat:popover",(t=>{t.detail.source!==e&&b(!1)})),i.setAttribute("aria-hidden","true"),e.selectByValue=e=>{const t=d.find((t=>t.dataset.value===e));f(t)},e.dataset.selectInitialized=!0,e.dispatchEvent(new CustomEvent("basecoat:initialized"))};window.basecoat&&window.basecoat.register("select","div.select:not([data-select-initialized])",e)})();
|
package/dist/assets/js/toast.js
CHANGED
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
clearTimeout(state.timeoutId);
|
|
100
100
|
toasts.delete(element);
|
|
101
101
|
|
|
102
|
-
if (document.activeElement) document.activeElement.blur();
|
|
102
|
+
if (element.contains(document.activeElement)) document.activeElement.blur();
|
|
103
103
|
element.setAttribute('aria-hidden', 'true');
|
|
104
104
|
element.addEventListener('transitionend', () => element.remove(), { once: true });
|
|
105
105
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{let t;const e=new WeakMap;let n=!1;const o={success:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>',error:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>',info:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>',warning:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>'};function i(t){if(t.dataset.toastInitialized)return;const o=parseInt(t.dataset.duration),i=-1!==o?o||("error"===t.dataset.category?5e3:3e3):-1,a={remainingTime:i,timeoutId:null,startTime:null};-1!==i&&(n?a.timeoutId=null:(a.startTime=Date.now(),a.timeoutId=setTimeout((()=>s(t)),i))),e.set(t,a),t.dataset.toastInitialized="true"}function a(){n||(n=!0,t.querySelectorAll('.toast:not([aria-hidden="true"])').forEach((t=>{if(!e.has(t))return;const n=e.get(t);n.timeoutId&&(clearTimeout(n.timeoutId),n.timeoutId=null,n.remainingTime-=Date.now()-n.startTime)})))}function r(){n&&(n=!1,t.querySelectorAll('.toast:not([aria-hidden="true"])').forEach((t=>{if(!e.has(t))return;const n=e.get(t);-1===n.remainingTime||n.timeoutId||(n.remainingTime>0?(n.startTime=Date.now(),n.timeoutId=setTimeout((()=>s(t)),n.remainingTime)):s(t))})))}function s(t){if(!e.has(t))return;const n=e.get(t);clearTimeout(n.timeoutId),e.delete(t),document.activeElement&&document.activeElement.blur(),t.setAttribute("aria-hidden","true"),t.addEventListener("transitionend",(()=>t.remove()),{once:!0})}document.addEventListener("basecoat:toast",(e=>{if(!t)return void console.error("Cannot create toast: toaster container not found on page.");const n=function(t){const{category:e="info",title:n,description:i,action:a,cancel:r,duration:s,icon:d}=t,c=d||e&&o[e]||"",l=n?`<h2>${n}</h2>`:"",u=i?`<p>${i}</p>`:"",h=a?.href?`<a href="${a.href}" class="btn" data-toast-action>${a.label}</a>`:a?.onclick?`<button type="button" class="btn" data-toast-action onclick="${a.onclick}">${a.label}</button>`:"",m=r?`<button type="button" class="btn-outline h-6 text-xs px-2.5 rounded-sm" data-toast-cancel onclick="${r?.onclick}">${r.label}</button>`:"",w=`\n <div\n class="toast"\n role="${"error"===e?"alert":"status"}"\n aria-atomic="true"\n ${e?`data-category="${e}"`:""}\n ${void 0!==s?`data-duration="${s}"`:""}\n >\n <div class="toast-content">\n ${c}\n <section>\n ${l}\n ${u}\n </section>\n ${h||m?`<footer>${h}${m}</footer>`:""}\n </div>\n </div>\n </div>\n `,g=document.createElement("template");return g.innerHTML=w.trim(),g.content.firstChild}(e.detail?.config||{});t.appendChild(n)})),window.basecoat&&(window.basecoat.register("toaster","#toaster:not([data-toaster-initialized])",(function(e){e.dataset.toasterInitialized||(t=e,t.addEventListener("mouseenter",a),t.addEventListener("mouseleave",r),t.addEventListener("click",(t=>{const e=t.target.closest(".toast footer a"),n=t.target.closest(".toast footer button");(e||n)&&s(t.target.closest(".toast"))})),t.querySelectorAll(".toast:not([data-toast-initialized])").forEach(i),t.dataset.toasterInitialized="true",t.dispatchEvent(new CustomEvent("basecoat:initialized")))})),window.basecoat.register("toast",".toast:not([data-toast-initialized])",i))})();
|
|
1
|
+
(()=>{let t;const e=new WeakMap;let n=!1;const o={success:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>',error:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>',info:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>',warning:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>'};function i(t){if(t.dataset.toastInitialized)return;const o=parseInt(t.dataset.duration),i=-1!==o?o||("error"===t.dataset.category?5e3:3e3):-1,a={remainingTime:i,timeoutId:null,startTime:null};-1!==i&&(n?a.timeoutId=null:(a.startTime=Date.now(),a.timeoutId=setTimeout((()=>s(t)),i))),e.set(t,a),t.dataset.toastInitialized="true"}function a(){n||(n=!0,t.querySelectorAll('.toast:not([aria-hidden="true"])').forEach((t=>{if(!e.has(t))return;const n=e.get(t);n.timeoutId&&(clearTimeout(n.timeoutId),n.timeoutId=null,n.remainingTime-=Date.now()-n.startTime)})))}function r(){n&&(n=!1,t.querySelectorAll('.toast:not([aria-hidden="true"])').forEach((t=>{if(!e.has(t))return;const n=e.get(t);-1===n.remainingTime||n.timeoutId||(n.remainingTime>0?(n.startTime=Date.now(),n.timeoutId=setTimeout((()=>s(t)),n.remainingTime)):s(t))})))}function s(t){if(!e.has(t))return;const n=e.get(t);clearTimeout(n.timeoutId),e.delete(t),t.contains(document.activeElement)&&document.activeElement.blur(),t.setAttribute("aria-hidden","true"),t.addEventListener("transitionend",(()=>t.remove()),{once:!0})}document.addEventListener("basecoat:toast",(e=>{if(!t)return void console.error("Cannot create toast: toaster container not found on page.");const n=function(t){const{category:e="info",title:n,description:i,action:a,cancel:r,duration:s,icon:d}=t,c=d||e&&o[e]||"",l=n?`<h2>${n}</h2>`:"",u=i?`<p>${i}</p>`:"",h=a?.href?`<a href="${a.href}" class="btn" data-toast-action>${a.label}</a>`:a?.onclick?`<button type="button" class="btn" data-toast-action onclick="${a.onclick}">${a.label}</button>`:"",m=r?`<button type="button" class="btn-outline h-6 text-xs px-2.5 rounded-sm" data-toast-cancel onclick="${r?.onclick}">${r.label}</button>`:"",w=`\n <div\n class="toast"\n role="${"error"===e?"alert":"status"}"\n aria-atomic="true"\n ${e?`data-category="${e}"`:""}\n ${void 0!==s?`data-duration="${s}"`:""}\n >\n <div class="toast-content">\n ${c}\n <section>\n ${l}\n ${u}\n </section>\n ${h||m?`<footer>${h}${m}</footer>`:""}\n </div>\n </div>\n </div>\n `,g=document.createElement("template");return g.innerHTML=w.trim(),g.content.firstChild}(e.detail?.config||{});t.appendChild(n)})),window.basecoat&&(window.basecoat.register("toaster","#toaster:not([data-toaster-initialized])",(function(e){e.dataset.toasterInitialized||(t=e,t.addEventListener("mouseenter",a),t.addEventListener("mouseleave",r),t.addEventListener("click",(t=>{const e=t.target.closest(".toast footer a"),n=t.target.closest(".toast footer button");(e||n)&&s(t.target.closest(".toast"))})),t.querySelectorAll(".toast:not([data-toast-initialized])").forEach(i),t.dataset.toasterInitialized="true",t.dispatchEvent(new CustomEvent("basecoat:initialized")))})),window.basecoat.register("toast",".toast:not([data-toast-initialized])",i))})();
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
{#
|
|
2
|
+
Renders a standalone command menu component with search and keyboard navigation.
|
|
3
|
+
|
|
4
|
+
@param id {string} [optional] - Unique identifier for the command component.
|
|
5
|
+
@param items {array} [optional] - Array of items to render (alternative to using caller).
|
|
6
|
+
@param placeholder {string} [optional] [default="Type a command or search..."] - Placeholder text for the search input.
|
|
7
|
+
@param empty_text {string} [optional] [default="No results found."] - Text displayed when no results match the search.
|
|
8
|
+
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
|
9
|
+
@param input_attrs {object} [optional] - Additional HTML attributes for the search input.
|
|
10
|
+
@param menu_attrs {object} [optional] - Additional HTML attributes for the menu div.
|
|
11
|
+
#}
|
|
12
|
+
{% macro command(
|
|
13
|
+
id=None,
|
|
14
|
+
items=None,
|
|
15
|
+
placeholder="Type a command or search...",
|
|
16
|
+
empty_text="No results found.",
|
|
17
|
+
main_attrs={},
|
|
18
|
+
input_attrs={},
|
|
19
|
+
menu_attrs={}
|
|
20
|
+
) %}
|
|
21
|
+
{% set id = id or ("command-" + (range(100000, 999999) | random | string)) %}
|
|
22
|
+
<div
|
|
23
|
+
id="{{ id }}"
|
|
24
|
+
class="command {{ main_attrs.class }}"
|
|
25
|
+
aria-label="Command menu"
|
|
26
|
+
{% for key, value in main_attrs %}
|
|
27
|
+
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
28
|
+
{% endfor %}
|
|
29
|
+
>
|
|
30
|
+
<header>
|
|
31
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-icon lucide-search"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
|
32
|
+
<input
|
|
33
|
+
type="text"
|
|
34
|
+
id="{{ id }}-input"
|
|
35
|
+
placeholder="{{ placeholder }}"
|
|
36
|
+
autocomplete="off"
|
|
37
|
+
autocorrect="off"
|
|
38
|
+
spellcheck="false"
|
|
39
|
+
aria-autocomplete="list"
|
|
40
|
+
role="combobox"
|
|
41
|
+
aria-expanded="true"
|
|
42
|
+
aria-controls="{{ id }}-menu"
|
|
43
|
+
{% for key, value in input_attrs %}
|
|
44
|
+
{{ key }}="{{ value }}"
|
|
45
|
+
{% endfor %}
|
|
46
|
+
>
|
|
47
|
+
</header>
|
|
48
|
+
<div
|
|
49
|
+
role="menu"
|
|
50
|
+
id="{{ id }}-menu"
|
|
51
|
+
aria-orientation="vertical"
|
|
52
|
+
data-empty="{{ empty_text }}"
|
|
53
|
+
class="scrollbar {{ menu_attrs.class }}"
|
|
54
|
+
{% for key, value in menu_attrs %}
|
|
55
|
+
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
56
|
+
{% endfor %}
|
|
57
|
+
>
|
|
58
|
+
{% if items and items.length > 0 %}
|
|
59
|
+
{{ render_command_items(items, id ~ "-items" if id else "items") }}
|
|
60
|
+
{% else %}
|
|
61
|
+
{{ caller() if caller }}
|
|
62
|
+
{% endif %}
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
{% endmacro %}
|
|
66
|
+
|
|
67
|
+
{#
|
|
68
|
+
Renders a command dialog (modal command palette).
|
|
69
|
+
|
|
70
|
+
@param id {string} [optional] - Unique identifier for the command dialog.
|
|
71
|
+
@param items {array} [optional] - Array of items to render (alternative to using caller).
|
|
72
|
+
@param placeholder {string} [optional] [default="Type a command or search..."] - Placeholder text for the search input.
|
|
73
|
+
@param empty_text {string} [optional] [default="No results found."] - Text displayed when no results match the search.
|
|
74
|
+
@param dialog_attrs {object} [optional] - Additional HTML attributes for the dialog element.
|
|
75
|
+
@param input_attrs {object} [optional] - Additional HTML attributes for the search input.
|
|
76
|
+
@param menu_attrs {object} [optional] - Additional HTML attributes for the menu div.
|
|
77
|
+
@param open {boolean} [optional] [default=false] - Whether the command dialog should be open initially.
|
|
78
|
+
#}
|
|
79
|
+
{% macro command_dialog(
|
|
80
|
+
id=None,
|
|
81
|
+
items=None,
|
|
82
|
+
placeholder="Type a command or search...",
|
|
83
|
+
empty_text="No results found.",
|
|
84
|
+
dialog_attrs={},
|
|
85
|
+
input_attrs={},
|
|
86
|
+
menu_attrs={},
|
|
87
|
+
open=false
|
|
88
|
+
) %}
|
|
89
|
+
{% set id = id or ("command-dialog-" + (range(100000, 999999) | random | string)) %}
|
|
90
|
+
<dialog
|
|
91
|
+
id="{{ id }}"
|
|
92
|
+
class="command-dialog {{ dialog_attrs.class }}"
|
|
93
|
+
aria-label="Command menu"
|
|
94
|
+
{% if open %}open{% endif %}
|
|
95
|
+
onclick="if (event.target === this) this.close()"
|
|
96
|
+
{% for key, value in dialog_attrs %}
|
|
97
|
+
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
98
|
+
{% endfor %}
|
|
99
|
+
>
|
|
100
|
+
<div class="command">
|
|
101
|
+
<header>
|
|
102
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-icon lucide-search"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
|
103
|
+
<input
|
|
104
|
+
type="text"
|
|
105
|
+
id="{{ id }}-input"
|
|
106
|
+
placeholder="{{ placeholder }}"
|
|
107
|
+
autocomplete="off"
|
|
108
|
+
autocorrect="off"
|
|
109
|
+
spellcheck="false"
|
|
110
|
+
aria-autocomplete="list"
|
|
111
|
+
role="combobox"
|
|
112
|
+
aria-expanded="true"
|
|
113
|
+
aria-controls="{{ id }}-menu"
|
|
114
|
+
{% for key, value in input_attrs %}
|
|
115
|
+
{{ key }}="{{ value }}"
|
|
116
|
+
{% endfor %}
|
|
117
|
+
>
|
|
118
|
+
</header>
|
|
119
|
+
<div
|
|
120
|
+
role="menu"
|
|
121
|
+
id="{{ id }}-menu"
|
|
122
|
+
aria-orientation="vertical"
|
|
123
|
+
data-empty="{{ empty_text }}"
|
|
124
|
+
class="scrollbar {{ menu_attrs.class }}"
|
|
125
|
+
{% for key, value in menu_attrs %}
|
|
126
|
+
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
127
|
+
{% endfor %}
|
|
128
|
+
>
|
|
129
|
+
{% if items and items.length > 0 %}
|
|
130
|
+
{{ render_command_items(items, id ~ "-items" if id else "items") }}
|
|
131
|
+
{% else %}
|
|
132
|
+
{{ caller() if caller }}
|
|
133
|
+
{% endif %}
|
|
134
|
+
</div>
|
|
135
|
+
<button type="button" aria-label="Close dialog" onclick="this.closest('dialog').close()">
|
|
136
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
|
137
|
+
</button>
|
|
138
|
+
</div>
|
|
139
|
+
</dialog>
|
|
140
|
+
{% endmacro %}
|
|
141
|
+
|
|
142
|
+
{#
|
|
143
|
+
Renders a list of items for the command component.
|
|
144
|
+
|
|
145
|
+
@param items {array} - Array of items to render. Each item can be:
|
|
146
|
+
- { type: "item", label: "Text", content: "HTML", keywords: "search terms", disabled: true, attrs: {} }
|
|
147
|
+
- { type: "group", label: "Group Title", items: [...] }
|
|
148
|
+
- { type: "separator" }
|
|
149
|
+
Note: Use attrs to add data-label for alternative search text if needed.
|
|
150
|
+
@param parent_id_prefix {string} [optional] - The prefix for the item id.
|
|
151
|
+
#}
|
|
152
|
+
{% macro render_command_items(items, parent_id_prefix="items") %}
|
|
153
|
+
{% for item in items %}
|
|
154
|
+
{% set item_id = parent_id_prefix ~ "-" ~ loop.index %}
|
|
155
|
+
{% if item.type == "group" %}
|
|
156
|
+
{% set group_label_id = item.id if item.id else "group-label-" + item_id %}
|
|
157
|
+
<div
|
|
158
|
+
role="group"
|
|
159
|
+
aria-labelledby="{{ group_label_id }}"
|
|
160
|
+
{% if item.attrs %}
|
|
161
|
+
{% for key, value in item.attrs %}
|
|
162
|
+
{{ key }}="{{ value }}"
|
|
163
|
+
{% endfor %}
|
|
164
|
+
{% endif %}
|
|
165
|
+
>
|
|
166
|
+
<span role="heading" id="{{ group_label_id }}">{{ item.label }}</span>
|
|
167
|
+
{{ render_command_items(item.items, item_id) if item.items }}
|
|
168
|
+
</div>
|
|
169
|
+
{% elif item.type == "separator" %}
|
|
170
|
+
<hr role="separator" />
|
|
171
|
+
{% elif item.type == "item" or not item.type %}
|
|
172
|
+
<div
|
|
173
|
+
id="{{ item_id }}"
|
|
174
|
+
role="menuitem"
|
|
175
|
+
{% if item.keywords %}data-keywords="{{ item.keywords }}"{% endif %}
|
|
176
|
+
{% if item.disabled %}aria-disabled="true"{% endif %}
|
|
177
|
+
{% if item.attrs %}
|
|
178
|
+
{% for key, value in item.attrs %}
|
|
179
|
+
{{ key }}="{{ value }}"
|
|
180
|
+
{% endfor %}
|
|
181
|
+
{% endif %}
|
|
182
|
+
>
|
|
183
|
+
{{ item.content | safe if item.content else item.label }}
|
|
184
|
+
</div>
|
|
185
|
+
{% endif %}
|
|
186
|
+
{% endfor %}
|
|
187
|
+
{% endmacro %}
|
|
@@ -8,7 +8,7 @@
|
|
|
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 dialog_attrs {object} [optional] - Additional HTML attributes for the dialog content
|
|
11
|
+
@param dialog_attrs {object} [optional] - Additional HTML attributes for the dialog content container.
|
|
12
12
|
@param header_attrs {object} [optional] - Additional HTML attributes for the dialog header.
|
|
13
13
|
@param body_attrs {object} [optional] - Additional HTML attributes for the dialog body section.
|
|
14
14
|
@param footer_attrs {object} [optional] - Additional HTML attributes for the dialog footer.
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
{{ key }}="{{ value }}"
|
|
41
41
|
{% endfor %}
|
|
42
42
|
>
|
|
43
|
-
{{ trigger }}
|
|
43
|
+
{{ trigger | safe }}
|
|
44
44
|
</button>
|
|
45
45
|
{% endif %}
|
|
46
46
|
<dialog
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
54
54
|
{% endfor %}
|
|
55
55
|
>
|
|
56
|
-
<
|
|
56
|
+
<div>
|
|
57
57
|
{% if title or description %}
|
|
58
58
|
<header
|
|
59
59
|
{% for key, value in header_attrs %}
|
|
@@ -87,6 +87,6 @@
|
|
|
87
87
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
|
88
88
|
</button>
|
|
89
89
|
{% endif %}
|
|
90
|
-
</
|
|
90
|
+
</div>
|
|
91
91
|
</dialog>
|
|
92
92
|
{% endmacro %}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "basecoat-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Add Basecoat components to your project",
|
|
5
5
|
"author": "hunvreus",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,8 +30,7 @@
|
|
|
30
30
|
"css",
|
|
31
31
|
"html",
|
|
32
32
|
"jinja",
|
|
33
|
-
"nunjucks"
|
|
34
|
-
"alpinejs"
|
|
33
|
+
"nunjucks"
|
|
35
34
|
],
|
|
36
35
|
"repository": {
|
|
37
36
|
"type": "git",
|