basecoat-cli 0.3.2 → 0.3.4
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 +5 -5
- package/dist/assets/jinja/toast.html.jinja +12 -3
- package/dist/assets/js/all.js +154 -4
- 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 +5 -4
- package/dist/assets/js/select.min.js +1 -1
- package/dist/assets/nunjucks/command.njk +187 -0
- package/dist/assets/nunjucks/dialog.njk +4 -4
- package/dist/assets/nunjucks/toast.njk +12 -3
- 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,10 +53,10 @@
|
|
|
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
|
-
{% for key, value in header_attrs %}
|
|
59
|
+
{% for key, value in header_attrs.items() %}
|
|
60
60
|
{{ key }}="{{ value }}"
|
|
61
61
|
{% endfor %}
|
|
62
62
|
>
|
|
@@ -89,6 +89,6 @@
|
|
|
89
89
|
</button>
|
|
90
90
|
</form>
|
|
91
91
|
{% endif %}
|
|
92
|
-
</
|
|
92
|
+
</div>
|
|
93
93
|
</dialog>
|
|
94
94
|
{% endmacro %}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
@param id {string} [optional] [default="toaster"] - Unique identifier for the toaster container.
|
|
6
6
|
@param toasts {array} [optional] - An array of toast objects to render initially. See the <code>toast()</code> macro for more details.
|
|
7
7
|
@param attrs {object} [optional] - Additional HTML attributes for the main toaster container div.
|
|
8
|
+
|
|
9
|
+
Supports caller() for custom toast markup.
|
|
8
10
|
#}
|
|
9
11
|
{% macro toaster(
|
|
10
12
|
id="toaster",
|
|
@@ -24,9 +26,11 @@
|
|
|
24
26
|
title=item.title,
|
|
25
27
|
description=item.description,
|
|
26
28
|
action=item.action,
|
|
27
|
-
cancel=item.cancel
|
|
29
|
+
cancel=item.cancel,
|
|
30
|
+
attrs=item.attrs
|
|
28
31
|
) }}
|
|
29
32
|
{% endfor %}
|
|
33
|
+
{{ caller() if caller }}
|
|
30
34
|
</div>
|
|
31
35
|
{% endmacro %}
|
|
32
36
|
|
|
@@ -44,20 +48,25 @@
|
|
|
44
48
|
- label {string}: Button text.
|
|
45
49
|
- onclick {string}: JavaScript code to execute on click.
|
|
46
50
|
- url {string}: URL for an anchor link button.
|
|
51
|
+
@param attrs {object} [optional] - Additional HTML attributes for the toast div.
|
|
47
52
|
#}
|
|
48
53
|
{% macro toast(
|
|
49
54
|
category="success",
|
|
50
55
|
title="",
|
|
51
56
|
description="",
|
|
52
57
|
action=None,
|
|
53
|
-
cancel=None
|
|
58
|
+
cancel=None,
|
|
59
|
+
attrs={}
|
|
54
60
|
) %}
|
|
55
61
|
<div
|
|
56
|
-
class="toast"
|
|
62
|
+
class="toast {{ attrs.class }}"
|
|
57
63
|
role="{{ 'alert' if category == 'error' else 'status' }}"
|
|
58
64
|
aria-atomic="true"
|
|
59
65
|
aria-hidden="false"
|
|
60
66
|
{% if category %}data-category="{{ category }}"{% endif %}
|
|
67
|
+
{% for key, value in attrs.items() %}
|
|
68
|
+
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
69
|
+
{% endfor %}
|
|
61
70
|
>
|
|
62
71
|
<div class="toast-content">
|
|
63
72
|
{% if category in ["error", "success", "info", "warning"] %}
|
package/dist/assets/js/all.js
CHANGED
|
@@ -97,6 +97,155 @@
|
|
|
97
97
|
startObserver();
|
|
98
98
|
});
|
|
99
99
|
})();
|
|
100
|
+
(() => {
|
|
101
|
+
const initCommand = (container) => {
|
|
102
|
+
const input = container.querySelector('header input');
|
|
103
|
+
const menu = container.querySelector('[role="menu"]');
|
|
104
|
+
|
|
105
|
+
if (!input || !menu) {
|
|
106
|
+
const missing = [];
|
|
107
|
+
if (!input) missing.push('input');
|
|
108
|
+
if (!menu) missing.push('menu');
|
|
109
|
+
console.error(`Command component initialization failed. Missing element(s): ${missing.join(', ')}`, container);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const allMenuItems = Array.from(menu.querySelectorAll('[role="menuitem"]'));
|
|
114
|
+
const menuItems = allMenuItems.filter(item =>
|
|
115
|
+
!item.hasAttribute('disabled') &&
|
|
116
|
+
item.getAttribute('aria-disabled') !== 'true'
|
|
117
|
+
);
|
|
118
|
+
let visibleMenuItems = [...menuItems];
|
|
119
|
+
let activeIndex = -1;
|
|
120
|
+
|
|
121
|
+
const setActiveItem = (index) => {
|
|
122
|
+
if (activeIndex > -1 && menuItems[activeIndex]) {
|
|
123
|
+
menuItems[activeIndex].classList.remove('active');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
activeIndex = index;
|
|
127
|
+
|
|
128
|
+
if (activeIndex > -1) {
|
|
129
|
+
const activeItem = menuItems[activeIndex];
|
|
130
|
+
activeItem.classList.add('active');
|
|
131
|
+
if (activeItem.id) {
|
|
132
|
+
input.setAttribute('aria-activedescendant', activeItem.id);
|
|
133
|
+
} else {
|
|
134
|
+
input.removeAttribute('aria-activedescendant');
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
input.removeAttribute('aria-activedescendant');
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const filterMenuItems = () => {
|
|
142
|
+
const searchTerm = input.value.trim().toLowerCase();
|
|
143
|
+
|
|
144
|
+
setActiveItem(-1);
|
|
145
|
+
|
|
146
|
+
visibleMenuItems = [];
|
|
147
|
+
allMenuItems.forEach(item => {
|
|
148
|
+
const itemText = (item.dataset.label || item.textContent).trim().toLowerCase();
|
|
149
|
+
const keywords = (item.dataset.keywords || '').toLowerCase();
|
|
150
|
+
const matches = itemText.includes(searchTerm) || keywords.includes(searchTerm);
|
|
151
|
+
item.setAttribute('aria-hidden', String(!matches));
|
|
152
|
+
if (matches && menuItems.includes(item)) {
|
|
153
|
+
visibleMenuItems.push(item);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
if (visibleMenuItems.length > 0) {
|
|
158
|
+
setActiveItem(menuItems.indexOf(visibleMenuItems[0]));
|
|
159
|
+
visibleMenuItems[0].scrollIntoView({ block: 'nearest' });
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
input.addEventListener('input', filterMenuItems);
|
|
164
|
+
|
|
165
|
+
const handleKeyNavigation = (event) => {
|
|
166
|
+
if (!['ArrowDown', 'ArrowUp', 'Enter', 'Home', 'End'].includes(event.key)) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (event.key === 'Enter') {
|
|
171
|
+
event.preventDefault();
|
|
172
|
+
if (activeIndex > -1) {
|
|
173
|
+
menuItems[activeIndex]?.click();
|
|
174
|
+
}
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (visibleMenuItems.length === 0) return;
|
|
179
|
+
|
|
180
|
+
event.preventDefault();
|
|
181
|
+
|
|
182
|
+
const currentVisibleIndex = activeIndex > -1 ? visibleMenuItems.indexOf(menuItems[activeIndex]) : -1;
|
|
183
|
+
let nextVisibleIndex = currentVisibleIndex;
|
|
184
|
+
|
|
185
|
+
switch (event.key) {
|
|
186
|
+
case 'ArrowDown':
|
|
187
|
+
if (currentVisibleIndex < visibleMenuItems.length - 1) {
|
|
188
|
+
nextVisibleIndex = currentVisibleIndex + 1;
|
|
189
|
+
}
|
|
190
|
+
break;
|
|
191
|
+
case 'ArrowUp':
|
|
192
|
+
if (currentVisibleIndex > 0) {
|
|
193
|
+
nextVisibleIndex = currentVisibleIndex - 1;
|
|
194
|
+
} else if (currentVisibleIndex === -1) {
|
|
195
|
+
nextVisibleIndex = 0;
|
|
196
|
+
}
|
|
197
|
+
break;
|
|
198
|
+
case 'Home':
|
|
199
|
+
nextVisibleIndex = 0;
|
|
200
|
+
break;
|
|
201
|
+
case 'End':
|
|
202
|
+
nextVisibleIndex = visibleMenuItems.length - 1;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (nextVisibleIndex !== currentVisibleIndex) {
|
|
207
|
+
const newActiveItem = visibleMenuItems[nextVisibleIndex];
|
|
208
|
+
setActiveItem(menuItems.indexOf(newActiveItem));
|
|
209
|
+
newActiveItem.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
menu.addEventListener('mousemove', (event) => {
|
|
214
|
+
const menuItem = event.target.closest('[role="menuitem"]');
|
|
215
|
+
if (menuItem && visibleMenuItems.includes(menuItem)) {
|
|
216
|
+
const index = menuItems.indexOf(menuItem);
|
|
217
|
+
if (index !== activeIndex) {
|
|
218
|
+
setActiveItem(index);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
menu.addEventListener('click', (event) => {
|
|
224
|
+
const clickedItem = event.target.closest('[role="menuitem"]');
|
|
225
|
+
if (clickedItem && visibleMenuItems.includes(clickedItem)) {
|
|
226
|
+
const dialog = container.closest('dialog.command-dialog');
|
|
227
|
+
if (dialog && !clickedItem.hasAttribute('data-keep-command-open')) {
|
|
228
|
+
dialog.close();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
input.addEventListener('keydown', handleKeyNavigation);
|
|
234
|
+
|
|
235
|
+
if (visibleMenuItems.length > 0) {
|
|
236
|
+
setActiveItem(menuItems.indexOf(visibleMenuItems[0]));
|
|
237
|
+
visibleMenuItems[0].scrollIntoView({ block: 'nearest' });
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
container.dataset.commandInitialized = true;
|
|
241
|
+
container.dispatchEvent(new CustomEvent('basecoat:initialized'));
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
if (window.basecoat) {
|
|
245
|
+
window.basecoat.register('command', '.command:not([data-command-initialized])', initCommand);
|
|
246
|
+
}
|
|
247
|
+
})();
|
|
248
|
+
|
|
100
249
|
(() => {
|
|
101
250
|
const initDropdownMenu = (dropdownMenuComponent) => {
|
|
102
251
|
const trigger = dropdownMenuComponent.querySelector(':scope > button');
|
|
@@ -360,7 +509,8 @@
|
|
|
360
509
|
return;
|
|
361
510
|
}
|
|
362
511
|
|
|
363
|
-
const
|
|
512
|
+
const allOptions = Array.from(listbox.querySelectorAll('[role="option"]'));
|
|
513
|
+
const options = allOptions.filter(opt => opt.getAttribute('aria-disabled') !== 'true');
|
|
364
514
|
let visibleOptions = [...options];
|
|
365
515
|
let activeIndex = -1;
|
|
366
516
|
|
|
@@ -413,7 +563,7 @@
|
|
|
413
563
|
const resetFilter = () => {
|
|
414
564
|
filter.value = '';
|
|
415
565
|
visibleOptions = [...options];
|
|
416
|
-
|
|
566
|
+
allOptions.forEach(opt => opt.setAttribute('aria-hidden', 'false'));
|
|
417
567
|
};
|
|
418
568
|
|
|
419
569
|
if (hasTransition()) {
|
|
@@ -454,11 +604,11 @@
|
|
|
454
604
|
setActiveOption(-1);
|
|
455
605
|
|
|
456
606
|
visibleOptions = [];
|
|
457
|
-
|
|
607
|
+
allOptions.forEach(option => {
|
|
458
608
|
const optionText = (option.dataset.label || option.textContent).trim().toLowerCase();
|
|
459
609
|
const matches = optionText.includes(searchTerm);
|
|
460
610
|
option.setAttribute('aria-hidden', String(!matches));
|
|
461
|
-
if (matches) {
|
|
611
|
+
if (matches && options.includes(option)) {
|
|
462
612
|
visibleOptions.push(option);
|
|
463
613
|
}
|
|
464
614
|
});
|
|
@@ -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)}},b=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 h=s.find((e=>e.dataset.value===o.value));h||(h=s.find((e=>void 0!==e.dataset.value))??s[0]),v(h,!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&&b(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&&b(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));b(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))})();
|
|
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("header input"),n=e.querySelector('[role="menu"]');if(!t||!n){const a=[];return t||a.push("input"),n||a.push("menu"),void console.error(`Command component initialization failed. Missing element(s): ${a.join(", ")}`,e)}const a=Array.from(n.querySelectorAll('[role="menuitem"]')),i=a.filter((e=>!e.hasAttribute("disabled")&&"true"!==e.getAttribute("aria-disabled")));let o=[...i],r=-1;const s=e=>{if(r>-1&&i[r]&&i[r].classList.remove("active"),r=e,r>-1){const e=i[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),o=[],a.forEach((t=>{const n=(t.dataset.label||t.textContent).trim().toLowerCase(),a=(t.dataset.keywords||"").toLowerCase(),r=n.includes(e)||a.includes(e);t.setAttribute("aria-hidden",String(!r)),r&&i.includes(t)&&o.push(t)})),o.length>0&&(s(i.indexOf(o[0])),o[0].scrollIntoView({block:"nearest"}))}));n.addEventListener("mousemove",(e=>{const t=e.target.closest('[role="menuitem"]');if(t&&o.includes(t)){const e=i.indexOf(t);e!==r&&s(e)}})),n.addEventListener("click",(t=>{const n=t.target.closest('[role="menuitem"]');if(n&&o.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&&i[r]?.click());if(0===o.length)return;e.preventDefault();const t=r>-1?o.indexOf(i[r]):-1;let n=t;switch(e.key){case"ArrowDown":t<o.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=o.length-1}if(n!==t){const e=o[n];s(i.indexOf(e)),e.scrollIntoView({block:"nearest",behavior:"smooth"})}})),o.length>0&&(s(i.indexOf(o[0])),o[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)})(),(()=>{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)}},m=(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)}},b=e=>{if(!e)return;const t=o.value,n=e.dataset.value;null!=n&&n!==t&&p(e),m()};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 h=d.find((e=>e.dataset.value===o.value));h||(h=d.find((e=>void 0!==e.dataset.value))??d[0]),p(h,!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 m();if("Enter"===e.key)return void(l>-1&&b(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")?m():(()=>{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&&b(t)})),document.addEventListener("click",(t=>{e.contains(t.target)||m(!1)})),document.addEventListener("basecoat:popover",(t=>{t.detail.source!==e&&m(!1)})),a.setAttribute("aria-hidden","true"),e.selectByValue=e=>{const t=d.find((t=>t.dataset.value===e));b(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>`:"",m=`\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=m.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))})();
|
|
@@ -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
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{const e=e=>{const t=e.querySelector(":scope > button"),a=t.querySelector(":scope > span"),
|
|
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)})();
|
|
@@ -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 %}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
@param id {string} [optional] [default="toaster"] - Unique identifier for the toaster container.
|
|
6
6
|
@param toasts {array} [optional] - An array of toast objects to render initially. See the <code>toast()</code> macro for more details.
|
|
7
7
|
@param attrs {object} [optional] - Additional HTML attributes for the main toaster container div.
|
|
8
|
+
|
|
9
|
+
Supports caller() for custom toast markup.
|
|
8
10
|
#}
|
|
9
11
|
{% macro toaster(
|
|
10
12
|
id="toaster",
|
|
@@ -24,9 +26,11 @@
|
|
|
24
26
|
title=item.title,
|
|
25
27
|
description=item.description,
|
|
26
28
|
action=item.action,
|
|
27
|
-
cancel=item.cancel
|
|
29
|
+
cancel=item.cancel,
|
|
30
|
+
attrs=item.attrs
|
|
28
31
|
) }}
|
|
29
32
|
{% endfor %}
|
|
33
|
+
{{ caller() if caller }}
|
|
30
34
|
</div>
|
|
31
35
|
{% endmacro %}
|
|
32
36
|
|
|
@@ -44,20 +48,25 @@
|
|
|
44
48
|
- label {string}: Button text.
|
|
45
49
|
- onclick {string}: JavaScript code to execute on click.
|
|
46
50
|
- url {string}: URL for an anchor link button.
|
|
51
|
+
@param attrs {object} [optional] - Additional HTML attributes for the toast div.
|
|
47
52
|
#}
|
|
48
53
|
{% macro toast(
|
|
49
54
|
category="success",
|
|
50
55
|
title="",
|
|
51
56
|
description="",
|
|
52
57
|
action=None,
|
|
53
|
-
cancel=None
|
|
58
|
+
cancel=None,
|
|
59
|
+
attrs={}
|
|
54
60
|
) %}
|
|
55
61
|
<div
|
|
56
|
-
class="toast"
|
|
62
|
+
class="toast {{ attrs.class }}"
|
|
57
63
|
role="{{ 'alert' if category == 'error' else 'status' }}"
|
|
58
64
|
aria-atomic="true"
|
|
59
65
|
aria-hidden="false"
|
|
60
66
|
{% if category %}data-category="{{ category }}"{% endif %}
|
|
67
|
+
{% for key, value in attrs %}
|
|
68
|
+
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
69
|
+
{% endfor %}
|
|
61
70
|
>
|
|
62
71
|
<div class="toast-content">
|
|
63
72
|
{% if category in ["error", "success", "info", "warning"] %}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "basecoat-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
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",
|