basecoat-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/jinja/dialog.html.jinja +110 -0
- package/dist/assets/jinja/dropdown-menu.html.jinja +113 -0
- package/dist/assets/jinja/popover.html.jinja +55 -0
- package/dist/assets/jinja/select.html.jinja +151 -0
- package/dist/assets/jinja/sidebar.html.jinja +138 -0
- package/dist/assets/jinja/tabs.html.jinja +75 -0
- package/dist/assets/jinja/toast.html.jinja +198 -0
- package/dist/assets/js/dialog.js +54 -0
- package/dist/assets/js/dropdown-menu.js +91 -0
- package/dist/assets/js/popover.js +26 -0
- package/dist/assets/js/select.js +150 -0
- package/dist/assets/js/sidebar.js +25 -0
- package/dist/assets/js/tabs.js +64 -0
- package/dist/assets/js/toast.js +75 -0
- package/dist/assets/nunjucks/dialog.njk +110 -0
- package/dist/assets/nunjucks/dropdown-menu.njk +113 -0
- package/dist/assets/nunjucks/popover.njk +55 -0
- package/dist/assets/nunjucks/select.njk +151 -0
- package/dist/assets/nunjucks/sidebar.njk +138 -0
- package/dist/assets/nunjucks/tabs.njk +75 -0
- package/dist/assets/nunjucks/toast.njk +198 -0
- package/dist/index.js +193 -0
- package/package.json +45 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
{#
|
|
2
|
+
Renders a dialog component with optional trigger, header, body, and footer.
|
|
3
|
+
|
|
4
|
+
@param id {string} - Unique identifier for the dialog component.
|
|
5
|
+
@param trigger {string} [optional] - Text or HTML for the button that triggers the dialog.
|
|
6
|
+
@param title {string} [optional] - Title text displayed in the dialog header.
|
|
7
|
+
@param description {string} [optional] - Description text displayed below the title.
|
|
8
|
+
@param footer {string} [optional] - HTML content for the dialog footer.
|
|
9
|
+
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
|
10
|
+
@param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
|
|
11
|
+
@param content_attrs {object} [optional] - Additional HTML attributes for the dialog content article.
|
|
12
|
+
@param content_header_attrs {object} [optional] - Additional HTML attributes for the dialog header.
|
|
13
|
+
@param content_body_attrs {object} [optional] - Additional HTML attributes for the dialog body section.
|
|
14
|
+
@param content_footer_attrs {object} [optional] - Additional HTML attributes for the dialog footer.
|
|
15
|
+
@param open {boolean} [optional] [default=False] - Whether the dialog should be open initially.
|
|
16
|
+
@param close_button {boolean} [optional] [default=True] - Whether to include a close button.
|
|
17
|
+
@param close_on_overlay_click {boolean} [optional] [default=True] - Whether clicking the overlay closes the dialog.
|
|
18
|
+
#}
|
|
19
|
+
{% macro dialog(
|
|
20
|
+
id,
|
|
21
|
+
trigger=None,
|
|
22
|
+
title=None,
|
|
23
|
+
description=None,
|
|
24
|
+
footer=None,
|
|
25
|
+
main_attrs={},
|
|
26
|
+
trigger_attrs={},
|
|
27
|
+
content_attrs={},
|
|
28
|
+
content_header_attrs={},
|
|
29
|
+
content_body_attrs={},
|
|
30
|
+
content_footer_attrs={},
|
|
31
|
+
open=False,
|
|
32
|
+
close_button=True,
|
|
33
|
+
close_on_overlay_click=True
|
|
34
|
+
) %}
|
|
35
|
+
<div
|
|
36
|
+
id="{{ id }}"
|
|
37
|
+
x-data="dialog({{ 'true' if open else 'false' }}, {{ 'true' if close_on_overlay_click else 'false' }})"
|
|
38
|
+
x-bind="$main"
|
|
39
|
+
class="dialog"
|
|
40
|
+
{% for key, value in main_attrs.items() %}
|
|
41
|
+
{{ key }}="{{ value }}"
|
|
42
|
+
{% endfor %}
|
|
43
|
+
>
|
|
44
|
+
{% if trigger %}
|
|
45
|
+
<button
|
|
46
|
+
type="button"
|
|
47
|
+
aria-expanded="false"
|
|
48
|
+
aria-controls="{{ id }}-dialog"
|
|
49
|
+
x-bind="$trigger"
|
|
50
|
+
{% for key, value in trigger_attrs.items() %}
|
|
51
|
+
{{ key }}="{{ value }}"
|
|
52
|
+
{% endfor %}
|
|
53
|
+
>
|
|
54
|
+
{{ trigger }}
|
|
55
|
+
</button>
|
|
56
|
+
{% endif %}
|
|
57
|
+
<div
|
|
58
|
+
role="dialog"
|
|
59
|
+
id="{{ id }}-dialog"
|
|
60
|
+
tabindex="-1"
|
|
61
|
+
aria-modal="true"
|
|
62
|
+
aria-labelledby="{{ id }}-title"
|
|
63
|
+
inert
|
|
64
|
+
x-bind="$content"
|
|
65
|
+
>
|
|
66
|
+
<article
|
|
67
|
+
{% for key, value in content_attrs.items() %}
|
|
68
|
+
{{ key }}="{{ value }}"
|
|
69
|
+
{% endfor %}
|
|
70
|
+
>
|
|
71
|
+
{% if title or description %}
|
|
72
|
+
<header
|
|
73
|
+
{% for key, value in content_header_attrs.items() %}
|
|
74
|
+
{{ key }}="{{ value }}"
|
|
75
|
+
{% endfor %}
|
|
76
|
+
>
|
|
77
|
+
<h2 id="{{ id }}-title">{{ title | safe }}</h2>
|
|
78
|
+
<p>{{ description | safe }}</p>
|
|
79
|
+
</header>
|
|
80
|
+
{% endif %}
|
|
81
|
+
{% if caller %}
|
|
82
|
+
<section
|
|
83
|
+
{% for key, value in content_body_attrs.items() %}
|
|
84
|
+
{{ key }}="{{ value }}"
|
|
85
|
+
{% endfor %}
|
|
86
|
+
>
|
|
87
|
+
{{ caller() }}
|
|
88
|
+
</section>
|
|
89
|
+
{% endif %}
|
|
90
|
+
{% if footer %}
|
|
91
|
+
<footer
|
|
92
|
+
{% for key, value in content_footer_attrs.items() %}
|
|
93
|
+
{{ key }}="{{ value }}"
|
|
94
|
+
{% endfor %}
|
|
95
|
+
>
|
|
96
|
+
{{ footer | safe }}
|
|
97
|
+
</footer>
|
|
98
|
+
{% endif %}
|
|
99
|
+
{% if close_button %}
|
|
100
|
+
<button
|
|
101
|
+
@click="hide()"
|
|
102
|
+
aria-label="Close dialog"
|
|
103
|
+
>
|
|
104
|
+
<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>
|
|
105
|
+
</button>
|
|
106
|
+
{% endif %}
|
|
107
|
+
</article>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
{% endmacro %}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
{#
|
|
2
|
+
Renders a dropdown menu component.
|
|
3
|
+
|
|
4
|
+
@param id {string} [optional] - Unique identifier for the dropdown component.
|
|
5
|
+
@param trigger {string} [optional] - HTML content for the button that triggers the dropdown.
|
|
6
|
+
@param menu {array} [optional] - Array of menu items for the dropdown.
|
|
7
|
+
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
|
8
|
+
@param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
|
|
9
|
+
@param content_attrs {object} [optional] - Additional HTML attributes for the dropdown content div.
|
|
10
|
+
#}
|
|
11
|
+
{% macro dropdown_menu(
|
|
12
|
+
id=None,
|
|
13
|
+
trigger=None,
|
|
14
|
+
menu=None,
|
|
15
|
+
main_attrs={},
|
|
16
|
+
trigger_attrs={},
|
|
17
|
+
content_attrs={}
|
|
18
|
+
) %}
|
|
19
|
+
<div
|
|
20
|
+
class="popover {{ main_attrs.class }}"
|
|
21
|
+
x-data="dropdownMenu"
|
|
22
|
+
@click.away="open = false"
|
|
23
|
+
{% if id %}id="{{ id }}"{% endif %}
|
|
24
|
+
{% for key, value in main_attrs %}
|
|
25
|
+
{% if key != "class" %}{{ key }}="{{ value }}"{% endif %}
|
|
26
|
+
{% endfor %}
|
|
27
|
+
>
|
|
28
|
+
{% if trigger %}
|
|
29
|
+
<button
|
|
30
|
+
type="button"
|
|
31
|
+
aria-haspopup="menu"
|
|
32
|
+
aria-expanded="false"
|
|
33
|
+
x-bind="$trigger"
|
|
34
|
+
{% if id %}
|
|
35
|
+
id="{{ id }}-trigger"
|
|
36
|
+
aria-controls="{{ id }}-menu"
|
|
37
|
+
{% endif %}
|
|
38
|
+
{% for key, value in trigger_attrs %}
|
|
39
|
+
{{ key }}="{{ value }}"
|
|
40
|
+
{% endfor %}
|
|
41
|
+
>
|
|
42
|
+
{{ trigger | safe }}
|
|
43
|
+
</button>
|
|
44
|
+
{% endif %}
|
|
45
|
+
<div
|
|
46
|
+
data-popover
|
|
47
|
+
aria-hidden="true"
|
|
48
|
+
x-bind="$content"
|
|
49
|
+
{% if id %}id="{{ id }}-menu"{% endif %}
|
|
50
|
+
{% for key, value in content_attrs %}
|
|
51
|
+
{{ key }}="{{ value }}"
|
|
52
|
+
{% endfor %}
|
|
53
|
+
>
|
|
54
|
+
<nav role="menu">
|
|
55
|
+
{% if menu %}
|
|
56
|
+
{{ render_dropdown_items(menu, id ~ "-items" if id else "items") }}
|
|
57
|
+
{% else %}
|
|
58
|
+
{{ caller() if caller }}
|
|
59
|
+
{% endif %}
|
|
60
|
+
</nav>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
{% endmacro %}
|
|
64
|
+
|
|
65
|
+
{#
|
|
66
|
+
Renders dropdown menu items recursively.
|
|
67
|
+
|
|
68
|
+
@param items {array} - The array of items to render.
|
|
69
|
+
@param parent_id_prefix {string} [optional] - Prefix for generating element IDs.
|
|
70
|
+
#}
|
|
71
|
+
{% macro render_dropdown_items(items, parent_id_prefix="items") %}
|
|
72
|
+
{% for item in items.items() %}
|
|
73
|
+
{% set item_id = parent_id_prefix ~ "-" ~ loop.index %}
|
|
74
|
+
|
|
75
|
+
{% if item.type == "group" %}
|
|
76
|
+
{% set group_label_id = item.id if item.id else "group-label-" + item_id %}
|
|
77
|
+
<div
|
|
78
|
+
role="group"
|
|
79
|
+
aria-labelledby="{{ group_label_id }}"
|
|
80
|
+
{% for key, value in item.attrs.items() %}
|
|
81
|
+
{{ key }}="{{ value }}"
|
|
82
|
+
{% endfor %}
|
|
83
|
+
>
|
|
84
|
+
<div role="heading" id="{{ group_label_id }}">{{ item.label }}</div>
|
|
85
|
+
{{ render_dropdown_items(item.items, item_id) if item.items }}
|
|
86
|
+
</div>
|
|
87
|
+
{% elif item.type == "separator" %}
|
|
88
|
+
<hr role="separator" />
|
|
89
|
+
{% elif item.type == "item" or not item.type %}
|
|
90
|
+
{% if item.url %}
|
|
91
|
+
<a
|
|
92
|
+
role="menuitem"
|
|
93
|
+
href="{{ item.url }}"
|
|
94
|
+
{% for key, value in item.attrs.items() %}
|
|
95
|
+
{% if key != "url" %} {{ key }}="{{ value }}" {% endif %}
|
|
96
|
+
{% endfor %}
|
|
97
|
+
>
|
|
98
|
+
{{ item.label | safe }}
|
|
99
|
+
</a>
|
|
100
|
+
{% else %}
|
|
101
|
+
<button
|
|
102
|
+
role="menuitem"
|
|
103
|
+
type="button"
|
|
104
|
+
{% for key, value in item.attrs.items() %}
|
|
105
|
+
{{ key }}="{{ value }}"
|
|
106
|
+
{% endfor %}
|
|
107
|
+
>
|
|
108
|
+
{{ item.label | safe }}
|
|
109
|
+
</button>
|
|
110
|
+
{% endif %}
|
|
111
|
+
{% endif %}
|
|
112
|
+
{% endfor %}
|
|
113
|
+
{% endmacro %}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{#
|
|
2
|
+
Renders a generic popover component, often used internally by other components like dropdowns or selects.
|
|
3
|
+
|
|
4
|
+
@param id {string} [optional] - Unique identifier for the popover component.
|
|
5
|
+
@param trigger {string} [optional] - HTML content for the element that triggers the popover.
|
|
6
|
+
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
|
7
|
+
@param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger element.
|
|
8
|
+
@param content_attrs {object} [optional] - Additional HTML attributes for the popover content div.
|
|
9
|
+
#}
|
|
10
|
+
{% macro popover(
|
|
11
|
+
id=None,
|
|
12
|
+
trigger=None,
|
|
13
|
+
main_attrs={},
|
|
14
|
+
trigger_attrs={},
|
|
15
|
+
content_attrs={}
|
|
16
|
+
) %}
|
|
17
|
+
<div
|
|
18
|
+
class="popover {{ main_attrs.class }}"
|
|
19
|
+
x-data="popover"
|
|
20
|
+
@click.away="open = false"
|
|
21
|
+
{% if id %}id="{{ id }}"{% endif %}
|
|
22
|
+
{% for key, value in main_attrs.items() %}
|
|
23
|
+
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
24
|
+
{% endfor %}
|
|
25
|
+
>
|
|
26
|
+
{% if trigger %}
|
|
27
|
+
<button
|
|
28
|
+
type="button"
|
|
29
|
+
aria-haspopup="menu"
|
|
30
|
+
aria-expanded="false"
|
|
31
|
+
x-bind="$trigger"
|
|
32
|
+
{% if id %}
|
|
33
|
+
id="{{ id }}-trigger"
|
|
34
|
+
aria-controls="{{ id }}-menu"
|
|
35
|
+
{% endif %}
|
|
36
|
+
{% for key, value in trigger_attrs.items() %}
|
|
37
|
+
{{ key }}="{{ value }}"
|
|
38
|
+
{% endfor %}
|
|
39
|
+
>
|
|
40
|
+
{{ trigger | safe }}
|
|
41
|
+
</button>
|
|
42
|
+
{% endif %}
|
|
43
|
+
<div
|
|
44
|
+
data-popover
|
|
45
|
+
aria-hidden="true"
|
|
46
|
+
x-bind="$content"
|
|
47
|
+
{% if id %}id="{{ id }}-menu"{% endif %}
|
|
48
|
+
{% for key, value in content_attrs.items() %}
|
|
49
|
+
{{ key }}="{{ value }}"
|
|
50
|
+
{% endfor %}
|
|
51
|
+
>
|
|
52
|
+
{{ caller() if caller }}
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
{% endmacro %}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
{#
|
|
2
|
+
Renders a select or combobox component.
|
|
3
|
+
|
|
4
|
+
@param id {string} [optional] - Unique identifier for the select component.
|
|
5
|
+
@param selected {string} [optional] - The initially selected value.
|
|
6
|
+
@param name {string} [optional] - The name attribute for the hidden input storing the selected value.
|
|
7
|
+
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
|
8
|
+
@param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
|
|
9
|
+
@param content_attrs {object} [optional] - Additional HTML attributes for the popover content div.
|
|
10
|
+
@param listbox_attrs {object} [optional] - Additional HTML attributes for the listbox div.
|
|
11
|
+
@param input_attrs {object} [optional] - Additional HTML attributes for the hidden input.
|
|
12
|
+
@param search_placeholder {string} [optional] [default="Search entries..."] - Placeholder text for the search input (combobox only).
|
|
13
|
+
@param is_combobox {boolean} [optional] [default=False] - Renders a combobox with search functionality if True.
|
|
14
|
+
#}
|
|
15
|
+
{% macro select(
|
|
16
|
+
id=None,
|
|
17
|
+
selected=None,
|
|
18
|
+
name=None,
|
|
19
|
+
items=None,
|
|
20
|
+
main_attrs={},
|
|
21
|
+
trigger_attrs={},
|
|
22
|
+
content_attrs={},
|
|
23
|
+
listbox_attrs={},
|
|
24
|
+
input_attrs={},
|
|
25
|
+
search_placeholder="Search entries...",
|
|
26
|
+
is_combobox=False
|
|
27
|
+
) %}
|
|
28
|
+
<div
|
|
29
|
+
class="popover {{ main_attrs.class }}"
|
|
30
|
+
x-data="select('{{ name }}', '{{ selected or '' }}')"
|
|
31
|
+
@click.away="open = false"
|
|
32
|
+
{% if id %}id="{{ id }}"{% endif %}
|
|
33
|
+
{% for key, value in main_attrs.items() %}
|
|
34
|
+
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
35
|
+
{% endfor %}
|
|
36
|
+
>
|
|
37
|
+
<button
|
|
38
|
+
type="button"
|
|
39
|
+
aria-haspopup="listbox"
|
|
40
|
+
aria-expanded="false"
|
|
41
|
+
x-bind="$trigger"
|
|
42
|
+
{% if id %}
|
|
43
|
+
id="{{ id }}-trigger"
|
|
44
|
+
aria-controls="{{ id }}-content"
|
|
45
|
+
{% endif %}
|
|
46
|
+
class="btn-outline justify-between font-normal {{ trigger_attrs.class }}"
|
|
47
|
+
{% for key, value in trigger_attrs.items() %}
|
|
48
|
+
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
49
|
+
{% endfor %}
|
|
50
|
+
>
|
|
51
|
+
<div x-html="selectedLabel" class="flex items-center gap-x-2"
|
|
52
|
+
></div>
|
|
53
|
+
{% if is_combobox %}
|
|
54
|
+
<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-chevrons-up-down-icon lucide-chevrons-up-down text-muted-foreground opacity-50 shrink-0"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>
|
|
55
|
+
{% else %}
|
|
56
|
+
<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-chevron-down-icon lucide-chevron-down text-muted-foreground opacity-50 shrink-0"><path d="m6 9 6 6 6-6"/></svg>
|
|
57
|
+
{% endif %}
|
|
58
|
+
</button>
|
|
59
|
+
<div
|
|
60
|
+
data-popover
|
|
61
|
+
aria-hidden="true"
|
|
62
|
+
x-bind="$content"
|
|
63
|
+
{% if id %}id="{{ id }}-content"{% endif %}
|
|
64
|
+
{% for key, value in content_attrs.items() %}
|
|
65
|
+
{{ key }}="{{ value }}"
|
|
66
|
+
{% endfor %}
|
|
67
|
+
>
|
|
68
|
+
{% if is_combobox %}
|
|
69
|
+
<header>
|
|
70
|
+
<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>
|
|
71
|
+
<input
|
|
72
|
+
type="text"
|
|
73
|
+
value=""
|
|
74
|
+
placeholder="{{ search_placeholder }}"
|
|
75
|
+
autocomplete="off"
|
|
76
|
+
autocorrect="off"
|
|
77
|
+
spellcheck="false"
|
|
78
|
+
aria-autocomplete="list"
|
|
79
|
+
role="combobox"
|
|
80
|
+
aria-expanded="true"
|
|
81
|
+
aria-controls="{{ id }}-content"
|
|
82
|
+
aria-labelledby="{{ id }}-trigger"
|
|
83
|
+
x-model="query"
|
|
84
|
+
x-bind="$filter"
|
|
85
|
+
>
|
|
86
|
+
</header>
|
|
87
|
+
{% endif %}
|
|
88
|
+
<div
|
|
89
|
+
role="listbox"
|
|
90
|
+
aria-orientation="vertical"
|
|
91
|
+
{% for key, value in listbox_attrs.items() %}
|
|
92
|
+
{{ key }}="{{ value }}"
|
|
93
|
+
{% endfor %}
|
|
94
|
+
>
|
|
95
|
+
{% if items %}
|
|
96
|
+
{{ render_select_items(items, id ~ "-items" if id else "items") }}
|
|
97
|
+
{% else %}
|
|
98
|
+
{{ caller() if caller }}
|
|
99
|
+
{% endif %}
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
{% if name is defined %}
|
|
103
|
+
<input
|
|
104
|
+
type="hidden"
|
|
105
|
+
name="{{ name }}"
|
|
106
|
+
x-model="selectedValue"
|
|
107
|
+
{% for key, value in input_attrs.items() %}
|
|
108
|
+
{% if key != 'name' %}{{ key }}="{{ value }}"{% endif %}
|
|
109
|
+
{% endfor %}
|
|
110
|
+
>
|
|
111
|
+
{% endif %}
|
|
112
|
+
</div>
|
|
113
|
+
{% endmacro %}
|
|
114
|
+
|
|
115
|
+
{#
|
|
116
|
+
Renders a list of items for the select component.
|
|
117
|
+
|
|
118
|
+
@param items {array} - The array of items to render.
|
|
119
|
+
@param parent_id_prefix {string} [optional] - The prefix for the item id.
|
|
120
|
+
#}
|
|
121
|
+
{% macro render_select_items(items, parent_id_prefix="items") %}
|
|
122
|
+
{% for item in items.items() %}
|
|
123
|
+
{% set item_id = parent_id_prefix ~ "-" ~ loop.index %}
|
|
124
|
+
|
|
125
|
+
{% if item.type == "group" %}
|
|
126
|
+
{% set group_label_id = item.id if item.id else "group-label-" + item_id %}
|
|
127
|
+
<div
|
|
128
|
+
role="group"
|
|
129
|
+
aria-labelledby="{{ group_label_id }}"
|
|
130
|
+
{% for key, value in item.attrs.items() %}
|
|
131
|
+
{{ key }}="{{ value }}"
|
|
132
|
+
{% endfor %}
|
|
133
|
+
>
|
|
134
|
+
<div role="heading" id="{{ group_label_id }}">{{ item.label }}</div>
|
|
135
|
+
{{ render_select_items(item.items, item_id) if item.items }}
|
|
136
|
+
</div>
|
|
137
|
+
{% elif item.type == "separator" %}
|
|
138
|
+
<hr role="separator" />
|
|
139
|
+
{% elif item.type == "item" or not item.type %}
|
|
140
|
+
<div
|
|
141
|
+
role="option"
|
|
142
|
+
data-value="{{ item.value }}"
|
|
143
|
+
{% for key, value in item.attrs.items() %}
|
|
144
|
+
{{ key }}="{{ value }}"
|
|
145
|
+
{% endfor %}
|
|
146
|
+
>
|
|
147
|
+
{{ item.label | safe }}
|
|
148
|
+
</div>
|
|
149
|
+
{% endif %}
|
|
150
|
+
{% endfor %}
|
|
151
|
+
{% endmacro %}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
{#
|
|
2
|
+
Renders a sidebar component.
|
|
3
|
+
|
|
4
|
+
@param id {string} [optional] - Unique identifier for the sidebar component.
|
|
5
|
+
@param label {string} [optional] [default="Sidebar navigation"] - Label for the sidebar navigation.
|
|
6
|
+
@param open {boolean} [optional] [default=True] - Whether the sidebar is open.
|
|
7
|
+
@param side {string} [optional] - Side of the sidebar to display.
|
|
8
|
+
@param header {string} [optional] - Header content for the sidebar.
|
|
9
|
+
@param footer {string} [optional] - Footer content for the sidebar.
|
|
10
|
+
@param menu {array} [optional] - Array of menu items for the sidebar.
|
|
11
|
+
#}
|
|
12
|
+
{% macro sidebar(
|
|
13
|
+
id=None,
|
|
14
|
+
label="Sidebar navigation",
|
|
15
|
+
open=True,
|
|
16
|
+
side=None,
|
|
17
|
+
header=None,
|
|
18
|
+
footer=None,
|
|
19
|
+
menu=None,
|
|
20
|
+
main_attrs={},
|
|
21
|
+
header_attrs={},
|
|
22
|
+
content_attrs={},
|
|
23
|
+
footer_attrs={},
|
|
24
|
+
content_wrapper_attrs=None
|
|
25
|
+
) %}
|
|
26
|
+
<div
|
|
27
|
+
{% if id %}id="{{ id }}"{% endif %}
|
|
28
|
+
class="sidebar {{ main_attrs.class }}"
|
|
29
|
+
data-uninitialized
|
|
30
|
+
data-side="{{ side if side else "left" }}"
|
|
31
|
+
aria-hidden="{{ "true" if not open else "false" }}"
|
|
32
|
+
{{ "inert" if not open }}
|
|
33
|
+
{% for key, value in main_attrs.items() %}
|
|
34
|
+
{% if key != "class" %}{{ key }}="{{ value }}"{% endif %}
|
|
35
|
+
{% endfor %}
|
|
36
|
+
x-data="sidebar({{ "true" if open else "false" }})"
|
|
37
|
+
x-bind="$main"
|
|
38
|
+
>
|
|
39
|
+
<nav
|
|
40
|
+
aria-label="{{ label }}"
|
|
41
|
+
>
|
|
42
|
+
{% if header %}
|
|
43
|
+
<header
|
|
44
|
+
{% for key, value in header_attrs.items() %}
|
|
45
|
+
{{ key }}="{{ value }}"
|
|
46
|
+
{% endfor %}
|
|
47
|
+
>
|
|
48
|
+
{{ header | safe }}
|
|
49
|
+
</header>
|
|
50
|
+
{% endif %}
|
|
51
|
+
|
|
52
|
+
<section
|
|
53
|
+
{% for key, value in content_attrs.items() %}
|
|
54
|
+
{{ key }}="{{ value }}"
|
|
55
|
+
{% endfor %}
|
|
56
|
+
>
|
|
57
|
+
{% if menu %}
|
|
58
|
+
{{ render_sidebar_content(menu, id ~ "-content" if id else "content") }}
|
|
59
|
+
{% else %}
|
|
60
|
+
{{ caller() if caller }}
|
|
61
|
+
{% endif %}
|
|
62
|
+
</section>
|
|
63
|
+
|
|
64
|
+
{% if footer %}
|
|
65
|
+
<footer
|
|
66
|
+
{% for key, value in footer_attrs.items() %}
|
|
67
|
+
{{ key }}="{{ value }}"
|
|
68
|
+
{% endfor %}
|
|
69
|
+
>
|
|
70
|
+
{{ footer | safe }}
|
|
71
|
+
</footer>
|
|
72
|
+
{% endif %}
|
|
73
|
+
</nav>
|
|
74
|
+
</div>
|
|
75
|
+
{% endmacro %}
|
|
76
|
+
|
|
77
|
+
{#
|
|
78
|
+
Renders sidebar content recursively (groups, items, submenus, separators).
|
|
79
|
+
|
|
80
|
+
@param items {array} - The array of items to render.
|
|
81
|
+
@param parent_id_prefix {string} [optional] - Prefix for generating element IDs.
|
|
82
|
+
#}
|
|
83
|
+
{% macro render_sidebar_content(items, parent_id_prefix="content") %}
|
|
84
|
+
{% for item in items.items() %}
|
|
85
|
+
{% set item_id = parent_id_prefix ~ "-" ~ loop.index %}
|
|
86
|
+
|
|
87
|
+
{% if item.type == "group" %}
|
|
88
|
+
{% set group_label_id = item.id if item.id else "group-label-" + item_id %}
|
|
89
|
+
<div
|
|
90
|
+
role="group"
|
|
91
|
+
{% if item.label %}aria-labelledby="{{ group_label_id }}"{% endif %}
|
|
92
|
+
{% for key, value in item.attrs.items() %}
|
|
93
|
+
{{ key }}="{{ value }}"
|
|
94
|
+
{% endfor %}
|
|
95
|
+
>
|
|
96
|
+
{% if item.label %}
|
|
97
|
+
<h3 id="{{ group_label_id }}">{{ item.label }}</h3>
|
|
98
|
+
{% endif %}
|
|
99
|
+
<ul>
|
|
100
|
+
{{ render_sidebar_content(item.items, item_id) if item.items }}
|
|
101
|
+
</ul>
|
|
102
|
+
</div>
|
|
103
|
+
{% elif item.type == "separator" %}
|
|
104
|
+
<hr role="separator"/>
|
|
105
|
+
{% elif item.type == "submenu" %}
|
|
106
|
+
<li>
|
|
107
|
+
<details
|
|
108
|
+
id="submenu-{{ item_id }}"
|
|
109
|
+
{{ "open" if item.open }}
|
|
110
|
+
{% for key, value in item.attrs.items() %}
|
|
111
|
+
{% if key != "open" %}{{ key }}="{{ value }}"{% endif %}
|
|
112
|
+
{% endfor %}
|
|
113
|
+
>
|
|
114
|
+
<summary aria-controls="submenu-{{ item_id }}-content">
|
|
115
|
+
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
|
116
|
+
{{ item.label }}
|
|
117
|
+
</summary>
|
|
118
|
+
<ul id="submenu-{{ item_id }}-content">
|
|
119
|
+
{{ render_sidebar_content(item.items, item_id) if item.items }}
|
|
120
|
+
</ul>
|
|
121
|
+
</details>
|
|
122
|
+
</li>
|
|
123
|
+
{% elif item.type == "item" or not item.type %}
|
|
124
|
+
<li>
|
|
125
|
+
<a
|
|
126
|
+
href="{{ item.url }}"
|
|
127
|
+
{{ 'aria-current="page"' if item.current }}
|
|
128
|
+
{% for key, value in item.attrs.items() %}
|
|
129
|
+
{% if key != "href" and key != "aria-current" %}{{ key }}="{{ value }}"{% endif %}
|
|
130
|
+
{% endfor %}
|
|
131
|
+
>
|
|
132
|
+
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
|
133
|
+
<span>{{ item.label }}</span>
|
|
134
|
+
</a>
|
|
135
|
+
</li>
|
|
136
|
+
{% endif %}
|
|
137
|
+
{% endfor %}
|
|
138
|
+
{% endmacro %}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{#
|
|
2
|
+
Renders a tabs component.
|
|
3
|
+
|
|
4
|
+
@param id {string} - Unique identifier for the tabs component.
|
|
5
|
+
@param tabsets {array} - An array of objects, each representing a tab and its panel.
|
|
6
|
+
Each object should have:
|
|
7
|
+
- id {string}: Unique identifier prefix for the tab and panel.
|
|
8
|
+
- tab {string}: HTML content for the tab button.
|
|
9
|
+
- panel {string} [optional]: HTML content for the tab panel.
|
|
10
|
+
- tab_attrs {object} [optional]: Additional HTML attributes for the tab button.
|
|
11
|
+
- panel_attrs {object} [optional]: Additional HTML attributes for the tab panel div.
|
|
12
|
+
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
|
13
|
+
@param tablist_attrs {object} [optional] - Additional HTML attributes for the tablist nav element.
|
|
14
|
+
@param default_tab_index {number} [optional] [default=1] - The 1-based index of the tab to be active initially.
|
|
15
|
+
#}
|
|
16
|
+
{% macro tabs(
|
|
17
|
+
id,
|
|
18
|
+
tabsets=[],
|
|
19
|
+
main_attrs=None,
|
|
20
|
+
tablist_attrs=None,
|
|
21
|
+
default_tab_index=1
|
|
22
|
+
)
|
|
23
|
+
%}
|
|
24
|
+
<div
|
|
25
|
+
class="tabs {{ main_attrs.class }}"
|
|
26
|
+
x-data="tabs({{ default_tab_index - 1 }})"
|
|
27
|
+
{% if id %}id="{{ id }}"{% endif %}
|
|
28
|
+
{% for key, value in main_attrs.items() %}
|
|
29
|
+
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
|
30
|
+
{% endfor %}
|
|
31
|
+
>
|
|
32
|
+
<nav
|
|
33
|
+
role="tablist"
|
|
34
|
+
aria-orientation="horizontal"
|
|
35
|
+
x-bind="$tablist"
|
|
36
|
+
{% for key, value in tablist_attrs.items() %}
|
|
37
|
+
{{ key }}="{{ value }}"
|
|
38
|
+
{% endfor %}
|
|
39
|
+
>
|
|
40
|
+
{% for tabset in tabsets.items() %}
|
|
41
|
+
<button
|
|
42
|
+
type="button"
|
|
43
|
+
role="tab"
|
|
44
|
+
id="{{ tabset.id }}-tab-{{ loop.index }}"
|
|
45
|
+
aria-controls="{{ tabset.id }}-panel-{{ loop.index }}"
|
|
46
|
+
aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}"
|
|
47
|
+
tabindex="0"
|
|
48
|
+
{% for key, value in tabset.tab_attrs.items() %}
|
|
49
|
+
{{ key }}="{{ value }}"
|
|
50
|
+
{% endfor %}
|
|
51
|
+
>
|
|
52
|
+
{{ tabset.tab | safe }}
|
|
53
|
+
</button>
|
|
54
|
+
{% endfor %}
|
|
55
|
+
</nav>
|
|
56
|
+
|
|
57
|
+
{% for tabset in tabsets.items() %}
|
|
58
|
+
{% if tabset.panel %}
|
|
59
|
+
<div
|
|
60
|
+
role="tabpanel"
|
|
61
|
+
id="{{ tabset.id }}-panel-{{ loop.index }}"
|
|
62
|
+
aria-labelledby="{{ tabset.id }}-tab-{{ loop.index }}"
|
|
63
|
+
tabindex="-1"
|
|
64
|
+
aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}"
|
|
65
|
+
{% if loop.index != default_tab_index %}hidden{% endif %}
|
|
66
|
+
{% for key, value in tabset.panel_attrs.items() %}
|
|
67
|
+
{{ key }}="{{ value }}"
|
|
68
|
+
{% endfor %}
|
|
69
|
+
>
|
|
70
|
+
{{ tabset.panel | safe }}
|
|
71
|
+
</div>
|
|
72
|
+
{% endif %}
|
|
73
|
+
{% endfor %}
|
|
74
|
+
</div>
|
|
75
|
+
{% endmacro %}
|