basecoat-cli 0.2.0 → 0.2.2

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.
@@ -8,103 +8,87 @@
8
8
  @param footer {string} [optional] - HTML content for the dialog footer.
9
9
  @param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
10
10
  @param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
11
- @param content_attrs {object} [optional] - Additional HTML attributes for the dialog content article.
12
- @param content_header_attrs {object} [optional] - Additional HTML attributes for the dialog header.
13
- @param content_body_attrs {object} [optional] - Additional HTML attributes for the dialog body section.
14
- @param content_footer_attrs {object} [optional] - Additional HTML attributes for the dialog footer.
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.
11
+ @param dialog_attrs {object} [optional] - Additional HTML attributes for the dialog content article.
12
+ @param header_attrs {object} [optional] - Additional HTML attributes for the dialog header.
13
+ @param body_attrs {object} [optional] - Additional HTML attributes for the dialog body section.
14
+ @param footer_attrs {object} [optional] - Additional HTML attributes for the dialog footer.
15
+ @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
18
  #}
19
19
  {% macro dialog(
20
- id,
20
+ id=None,
21
21
  trigger=None,
22
22
  title=None,
23
23
  description=None,
24
24
  footer=None,
25
- main_attrs={},
25
+ dialog_attrs={},
26
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
27
+ header_attrs={},
28
+ body_attrs={},
29
+ footer_attrs={},
30
+ open=false,
31
+ close_button=true,
32
+ close_on_overlay_click=true
34
33
  ) %}
35
- <div
34
+ {% set id = id or ("dialog-" + (range(100000, 999999) | random | string)) %}
35
+ {% if trigger %}
36
+ <button
37
+ type="button"
38
+ onclick="document.getElementById('{{ id }}').showModal()"
39
+ {% for key, value in trigger_attrs.items() %}
40
+ {{ key }}="{{ value }}"
41
+ {% endfor %}
42
+ >
43
+ {{ trigger }}
44
+ </button>
45
+ {% endif %}
46
+ <dialog
36
47
  id="{{ id }}"
37
- x-data="dialog({{ 'true' if open else 'false' }}, {{ 'true' if close_on_overlay_click else 'false' }})"
38
- x-bind="$main"
39
48
  class="dialog"
40
- {% for key, value in main_attrs.items() %}
49
+ aria-labelledby="{{ id }}-title"
50
+ {% if description %}aria-describedby="{{ id }}-description"{% endif %}
51
+ {% if close_on_overlay_click %}onclick="this.close()"{% endif %}
52
+ {% for key, value in dialog_attrs.items() %}
41
53
  {{ key }}="{{ value }}"
42
54
  {% endfor %}
43
55
  >
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
- {% if description %}<p>{{ description | safe }}</p>{% endif %}
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
- >
56
+ <article {% if close_on_overlay_click %}onclick="event.stopPropagation()"{% endif %}>
57
+ {% if title or description %}
58
+ <header
59
+ {% for key, value in header_attrs %}
60
+ {{ key }}="{{ value }}"
61
+ {% endfor %}
62
+ >
63
+ <h2 id="{{ id }}-title">{{ title | safe }}</h2>
64
+ {% if description %}<p id="{{ id }}-description">{{ description | safe }}</p>{% endif %}
65
+ </header>
66
+ {% endif %}
67
+ {% if caller %}
68
+ <section
69
+ {% for key, value in body_attrs.items() %}
70
+ {{ key }}="{{ value }}"
71
+ {% endfor %}
72
+ >
73
+ {{ caller() }}
74
+ </section>
75
+ {% endif %}
76
+ {% if footer %}
77
+ <footer
78
+ {% for key, value in footer_attrs.items() %}
79
+ {{ key }}="{{ value }}"
80
+ {% endfor %}
81
+ >
82
+ {{ footer | safe }}
83
+ </footer>
84
+ {% endif %}
85
+ {% if close_button %}
86
+ <form method="dialog">
87
+ <button aria-label="Close dialog">
104
88
  <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>
89
+ </button>
90
+ </form>
91
+ {% endif %}
92
+ </article>
93
+ </dialog>
110
94
  {% endmacro %}
@@ -3,61 +3,62 @@
3
3
 
4
4
  @param id {string} [optional] - Unique identifier for the dropdown component.
5
5
  @param trigger {string} [optional] - HTML content for the button that triggers the dropdown.
6
- @param menu {array} [optional] - Array of menu items for the dropdown.
6
+ @param items {array} [optional] - Array of menu items for the dropdown.
7
7
  @param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
8
8
  @param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
9
- @param content_attrs {object} [optional] - Additional HTML attributes for the dropdown content div.
9
+ @param popover_attrs {object} [optional] - Additional HTML attributes for the dropdown content div.
10
10
  #}
11
11
  {% macro dropdown_menu(
12
+ trigger,
12
13
  id=None,
13
- trigger=None,
14
- menu=None,
14
+ items=None,
15
15
  main_attrs={},
16
16
  trigger_attrs={},
17
- content_attrs={}
17
+ popover_attrs={},
18
+ menu_attrs={}
18
19
  ) %}
20
+ {% set id = id or ("dropdown-menu-" + (range(100000, 999999) | random | string)) %}
21
+
19
22
  <div
20
- class="popover {{ main_attrs.class }}"
21
- x-data="dropdownMenu"
22
- @click.away="open = false"
23
- {% if id %}id="{{ id }}"{% endif %}
23
+ class="dropdown-menu {{ main_attrs.class }}"
24
24
  {% for key, value in main_attrs.items() %}
25
- {% if key != "class" %}{{ key }}="{{ value }}"{% endif %}
25
+ {% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
26
26
  {% endfor %}
27
27
  >
28
- {% if trigger %}
29
28
  <button
30
29
  type="button"
31
- aria-haspopup="menu"
32
- aria-expanded="false"
33
- x-bind="$trigger"
34
- {% if id %}
35
30
  id="{{ id }}-trigger"
31
+ aria-haspopup="menu"
36
32
  aria-controls="{{ id }}-menu"
37
- {% endif %}
33
+ aria-expanded="false"
38
34
  {% for key, value in trigger_attrs.items() %}
39
35
  {{ key }}="{{ value }}"
40
36
  {% endfor %}
41
37
  >
42
38
  {{ trigger | safe }}
43
39
  </button>
44
- {% endif %}
45
40
  <div
41
+ id="{{ id }}"
46
42
  data-popover
47
43
  aria-hidden="true"
48
- x-bind="$content"
49
- {% if id %}id="{{ id }}-menu"{% endif %}
50
- {% for key, value in content_attrs.items() %}
44
+ {% for key, value in popover_attrs.items() %}
51
45
  {{ key }}="{{ value }}"
52
46
  {% endfor %}
53
47
  >
54
- <nav role="menu">
55
- {% if menu %}
56
- {{ render_dropdown_items(menu, id ~ "-items" if id else "items") }}
48
+ <div
49
+ role="menu"
50
+ id="{{ id }}-menu"
51
+ aria-labelledby="{{ id }}-trigger"
52
+ {% for key, value in menu_attrs.items() %}
53
+ {{ key }}="{{ value }}"
54
+ {% endfor %}
55
+ >
56
+ {% if items %}
57
+ {{ render_dropdown_items(items, id ~ "-items" if id else "items") }}
57
58
  {% else %}
58
59
  {{ caller() if caller }}
59
60
  {% endif %}
60
- </nav>
61
+ </div>
61
62
  </div>
62
63
  </div>
63
64
  {% endmacro %}
@@ -69,7 +70,7 @@
69
70
  @param parent_id_prefix {string} [optional] - Prefix for generating element IDs.
70
71
  #}
71
72
  {% macro render_dropdown_items(items, parent_id_prefix="items") %}
72
- {% for item in items.items() %}
73
+ {% for item in items %}
73
74
  {% set item_id = parent_id_prefix ~ "-" ~ loop.index %}
74
75
 
75
76
  {% if item.type == "group" %}
@@ -77,11 +78,13 @@
77
78
  <div
78
79
  role="group"
79
80
  aria-labelledby="{{ group_label_id }}"
80
- {% for key, value in item.attrs.items() %}
81
- {{ key }}="{{ value }}"
82
- {% endfor %}
81
+ {% if item.attrs %}
82
+ {% for key, value in item.attrs.items() %}
83
+ {{ key }}="{{ value }}"
84
+ {% endfor %}
85
+ {% endif %}
83
86
  >
84
- <div role="heading" id="{{ group_label_id }}">{{ item.label }}</div>
87
+ <div role="presentation" id="{{ group_label_id }}">{{ item.label }}</div>
85
88
  {{ render_dropdown_items(item.items, item_id) if item.items }}
86
89
  </div>
87
90
  {% elif item.type == "separator" %}
@@ -89,24 +92,29 @@
89
92
  {% elif item.type == "item" or not item.type %}
90
93
  {% if item.url %}
91
94
  <a
95
+ id="{{ item_id }}"
92
96
  role="menuitem"
93
97
  href="{{ item.url }}"
94
- {% for key, value in item.attrs.items() %}
95
- {% if key != "url" %} {{ key }}="{{ value }}" {% endif %}
96
- {% endfor %}
98
+ {% if item.attrs %}
99
+ {% for key, value in item.attrs.items() %}
100
+ {% if key != "url" %} {{ key }}="{{ value }}" {% endif %}
101
+ {% endfor %}
102
+ {% endif %}
97
103
  >
98
104
  {{ item.label | safe }}
99
105
  </a>
100
106
  {% else %}
101
- <button
107
+ <div
108
+ id="{{ item_id }}"
102
109
  role="menuitem"
103
- type="button"
104
- {% for key, value in item.attrs.items() %}
105
- {{ key }}="{{ value }}"
106
- {% endfor %}
110
+ {% if item.attrs %}
111
+ {% for key, value in item.attrs.items() %}
112
+ {{ key }}="{{ value }}"
113
+ {% endfor %}
114
+ {% endif %}
107
115
  >
108
116
  {{ item.label | safe }}
109
- </button>
117
+ </div>
110
118
  {% endif %}
111
119
  {% endif %}
112
120
  {% endfor %}
@@ -5,51 +5,43 @@
5
5
  @param trigger {string} [optional] - HTML content for the element that triggers the popover.
6
6
  @param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
7
7
  @param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger element.
8
- @param content_attrs {object} [optional] - Additional HTML attributes for the popover content div.
8
+ @param popover_attrs {object} [optional] - Additional HTML attributes for the popover content div.
9
9
  #}
10
10
  {% macro popover(
11
+ trigger,
11
12
  id=None,
12
- trigger=None,
13
13
  main_attrs={},
14
14
  trigger_attrs={},
15
- content_attrs={}
15
+ popover_attrs={}
16
16
  ) %}
17
+ {% set id = id or ("popover-" + (range(100000, 999999) | random | string)) %}
18
+
17
19
  <div
18
20
  class="popover {{ main_attrs.class }}"
19
- x-data="popover"
20
- @click.away="open = false"
21
- {% if id %}id="{{ id }}"{% endif %}
22
21
  {% for key, value in main_attrs.items() %}
23
22
  {% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
24
23
  {% endfor %}
25
24
  >
26
- {% if trigger %}
27
25
  <button
26
+ id="{{ id }}-trigger"
28
27
  type="button"
29
- aria-haspopup="menu"
30
28
  aria-expanded="false"
31
- x-bind="$trigger"
32
- {% if id %}
33
- id="{{ id }}-trigger"
34
- aria-controls="{{ id }}-menu"
35
- {% endif %}
29
+ aria-controls="{{ id }}"
36
30
  {% for key, value in trigger_attrs.items() %}
37
31
  {{ key }}="{{ value }}"
38
32
  {% endfor %}
39
33
  >
40
34
  {{ trigger | safe }}
41
35
  </button>
42
- {% endif %}
43
36
  <div
37
+ id="{{ id }}"
44
38
  data-popover
45
39
  aria-hidden="true"
46
- x-bind="$content"
47
- {% if id %}id="{{ id }}-menu"{% endif %}
48
- {% for key, value in content_attrs.items() %}
40
+ {% for key, value in popover_attrs.items() %}
49
41
  {{ key }}="{{ value }}"
50
42
  {% endfor %}
51
- >
43
+ >
52
44
  {{ caller() if caller }}
53
45
  </div>
54
46
  </div>
55
- {% endmacro %}
47
+ {% endmacro %}
@@ -6,7 +6,7 @@
6
6
  @param name {string} [optional] - The name attribute for the hidden input storing the selected value.
7
7
  @param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
8
8
  @param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
9
- @param content_attrs {object} [optional] - Additional HTML attributes for the popover content div.
9
+ @param popover_attrs {object} [optional] - Additional HTML attributes for the popover content div.
10
10
  @param listbox_attrs {object} [optional] - Additional HTML attributes for the listbox div.
11
11
  @param input_attrs {object} [optional] - Additional HTML attributes for the hidden input.
12
12
  @param search_placeholder {string} [optional] [default="Search entries..."] - Placeholder text for the search input (combobox only).
@@ -17,49 +17,55 @@
17
17
  selected=None,
18
18
  name=None,
19
19
  items=None,
20
+ main_attrs={},
20
21
  trigger_attrs={},
21
22
  popover_attrs={},
22
23
  listbox_attrs={},
24
+ input_attrs={},
23
25
  search_placeholder="Search entries...",
24
26
  is_combobox=false
25
27
  ) %}
26
28
  {% set id = id or ("select-" + (range(100000, 999999) | random | string)) %}
27
29
 
28
- {% set first_option = [] %}
29
- {% set selected_option = [] %}
30
+ {% set first_option = namespace(item=None) %}
31
+ {% set selected_option = namespace(item=None) %}
30
32
 
31
- {% for item in items.items() %}
33
+ {% for item in items %}
32
34
  {% if item.type == "group" %}
33
- {% for sub_item in item.items.items() %}
34
- {% if not first_option[0] %}
35
- {% set first_option = (first_option.push(sub_item), first_option) %}
35
+ {% for sub_item in item.items %}
36
+ {% if not first_option.item %}
37
+ {% set first_option.item = sub_item %}
36
38
  {% endif %}
37
- {% if selected and sub_item.value == selected and not selected_option[0] %}
38
- {% set selected_option = (selected_option.push(sub_item), selected_option) %}
39
+ {% if selected and sub_item.value == selected and not selected_option.item %}
40
+ {% set selected_option.item = sub_item %}
39
41
  {% endif %}
40
42
  {% endfor %}
41
43
  {% else %}
42
- {% if not first_option[0] %}
43
- {% set first_option = (first_option.push(item), first_option) %}
44
+ {% if not first_option.item %}
45
+ {% set first_option.item = item %}
44
46
  {% endif %}
45
- {% if selected and item.value == selected and not selected_option[0] %}
46
- {% set selected_option = (selected_option.push(item), selected_option) %}
47
+ {% if selected and item.value == selected and not selected_option.item %}
48
+ {% set selected_option.item = item %}
47
49
  {% endif %}
48
50
  {% endif %}
49
51
  {% endfor %}
50
52
 
51
- {% set default_option = selected_option[0] or first_option[0] or None %}
53
+ {% set default_option = selected_option.item or first_option.item or None %}
52
54
 
53
- <div class="select">
55
+ <div
56
+ class="select {{ main_attrs.class }}"
57
+ {% for key, value in main_attrs.items() %}
58
+ {% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
59
+ {% endfor %}
60
+ >
54
61
  <button
55
62
  type="button"
56
63
  class="btn-outline justify-between font-normal {{ trigger_attrs.class }}"
57
64
  id="{{ id }}-trigger"
58
- popovertarget="{{ id }}"
59
65
  aria-haspopup="listbox"
60
66
  aria-expanded="false"
61
67
  aria-controls="{{ id }}-listbox"
62
- {% for key, value in trigger_attrs %}
68
+ {% for key, value in trigger_attrs.items() %}
63
69
  {% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
64
70
  {% endfor %}
65
71
  >
@@ -71,11 +77,11 @@
71
77
  {% endif %}
72
78
  </button>
73
79
  <div
74
- popover
75
80
  id="{{ id }}"
76
- class="popover {{ popover_attrs.class }}"
81
+ data-popover
82
+ aria-hidden="true"
77
83
  {% for key, value in popover_attrs.items() %}
78
- {% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
84
+ {{ key }}="{{ value }}"
79
85
  {% endfor %}
80
86
  >
81
87
  {% if is_combobox %}
@@ -90,8 +96,8 @@
90
96
  spellcheck="false"
91
97
  aria-autocomplete="list"
92
98
  role="combobox"
93
- aria-expanded="true"
94
- aria-controls="{{ id }}-content"
99
+ aria-expanded="false"
100
+ aria-controls="{{ id }}-listbox"
95
101
  aria-labelledby="{{ id }}-trigger"
96
102
  >
97
103
  </header>
@@ -101,11 +107,11 @@
101
107
  id="{{ id }}-listbox"
102
108
  aria-orientation="vertical"
103
109
  aria-labelledby="{{ id }}-trigger"
104
- {% for key, value in listbox_attrs %}
110
+ {% for key, value in listbox_attrs.items() %}
105
111
  {{ key }}="{{ value }}"
106
112
  {% endfor %}
107
113
  >
108
- {% if items.length > 0 %}
114
+ {% if items %}
109
115
  {{ render_select_items(items, default_option.value, id ~ "-items" if id else "items") }}
110
116
  {% else %}
111
117
  {{ caller() if caller }}
@@ -115,7 +121,7 @@
115
121
  <input
116
122
  type="hidden"
117
123
  name="{{ name or id ~ '-value' }}"
118
- value="{{ selected or '' }}"
124
+ value="{{ (default_option.value if default_option) or '' }}"
119
125
  {% for key, value in input_attrs.items() %}
120
126
  {% if key != 'name' and key != 'value' %}{{ key }}="{{ value }}"{% endif %}
121
127
  {% endfor %}
@@ -130,18 +136,20 @@
130
136
  @param parent_id_prefix {string} [optional] - The prefix for the item id.
131
137
  #}
132
138
  {% macro render_select_items(items, selected, parent_id_prefix="items") %}
133
- {% for item in items.items() %}
139
+ {% for item in items %}
134
140
  {% set item_id = parent_id_prefix ~ "-" ~ loop.index %}
135
141
  {% if item.type == "group" %}
136
142
  {% set group_label_id = item.id if item.id else "group-label-" + item_id %}
137
143
  <div
138
144
  role="group"
139
145
  aria-labelledby="{{ group_label_id }}"
140
- {% for key, value in item.attrs %}
141
- {{ key }}="{{ value }}"
142
- {% endfor %}
146
+ {% if item.attrs %}
147
+ {% for key, value in item.attrs.items() %}
148
+ {{ key }}="{{ value }}"
149
+ {% endfor %}
150
+ {% endif %}
143
151
  >
144
- <div role="heading" id="{{ group_label_id }}">{{ item.label }}</div>
152
+ <div role="presentation" id="{{ group_label_id }}">{{ item.label }}</div>
145
153
  {{ render_select_items(item.items, selected, item_id) if item.items }}
146
154
  </div>
147
155
  {% elif item.type == "separator" %}
@@ -152,9 +160,11 @@
152
160
  role="option"
153
161
  data-value="{{ item.value }}"
154
162
  {% if selected == item.value %}aria-selected="true"{% endif %}
155
- {% for key, value in item.attrs %}
156
- {{ key }}="{{ value }}"
157
- {% endfor %}
163
+ {% if item.attrs %}
164
+ {% for key, value in item.attrs.items() %}
165
+ {{ key }}="{{ value }}"
166
+ {% endfor %}
167
+ {% endif %}
158
168
  >
159
169
  {{ item.label | safe }}
160
170
  </div>
@@ -2,17 +2,21 @@
2
2
  Renders a sidebar component.
3
3
 
4
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.
5
+ @param label {string} [optional] - Label for the sidebar navigation.
6
+ @param open {boolean} [optional] - Whether the sidebar is open.
7
7
  @param side {string} [optional] - Side of the sidebar to display.
8
8
  @param header {string} [optional] - Header content for the sidebar.
9
9
  @param footer {string} [optional] - Footer content for the sidebar.
10
10
  @param menu {array} [optional] - Array of menu items for the sidebar.
11
+ @param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
12
+ @param header_attrs {object} [optional] - Additional HTML attributes for the header div.
13
+ @param content_attrs {object} [optional] - Additional HTML attributes for the content div.
14
+ @param footer_attrs {object} [optional] - Additional HTML attributes for the footer div.
11
15
  #}
12
16
  {% macro sidebar(
13
17
  id=None,
14
18
  label="Sidebar navigation",
15
- open=True,
19
+ open=true,
16
20
  side=None,
17
21
  header=None,
18
22
  footer=None,
@@ -20,21 +24,17 @@
20
24
  main_attrs={},
21
25
  header_attrs={},
22
26
  content_attrs={},
23
- footer_attrs={},
24
- content_wrapper_attrs=None
27
+ footer_attrs={}
25
28
  ) %}
26
- <div
29
+ <aside
27
30
  {% if id %}id="{{ id }}"{% endif %}
28
31
  class="sidebar {{ main_attrs.class }}"
29
- data-uninitialized
30
32
  data-side="{{ side if side else "left" }}"
31
33
  aria-hidden="{{ "true" if not open else "false" }}"
32
34
  {{ "inert" if not open }}
33
35
  {% for key, value in main_attrs.items() %}
34
36
  {% if key != "class" %}{{ key }}="{{ value }}"{% endif %}
35
37
  {% endfor %}
36
- x-data="sidebar({{ "true" if open else "false" }})"
37
- x-bind="$main"
38
38
  >
39
39
  <nav
40
40
  aria-label="{{ label }}"
@@ -71,7 +71,7 @@
71
71
  </footer>
72
72
  {% endif %}
73
73
  </nav>
74
- </div>
74
+ </aside>
75
75
  {% endmacro %}
76
76
 
77
77
  {#
@@ -81,7 +81,7 @@
81
81
  @param parent_id_prefix {string} [optional] - Prefix for generating element IDs.
82
82
  #}
83
83
  {% macro render_sidebar_content(items, parent_id_prefix="content") %}
84
- {% for item in items.items() %}
84
+ {% for item in items %}
85
85
  {% set item_id = parent_id_prefix ~ "-" ~ loop.index %}
86
86
 
87
87
  {% if item.type == "group" %}
@@ -89,15 +89,17 @@
89
89
  <div
90
90
  role="group"
91
91
  {% if item.label %}aria-labelledby="{{ group_label_id }}"{% endif %}
92
- {% for key, value in item.attrs.items() %}
93
- {{ key }}="{{ value }}"
94
- {% endfor %}
92
+ {% if item.attrs %}
93
+ {% for key, value in item.attrs.items() %}
94
+ {{ key }}="{{ value }}"
95
+ {% endfor %}
96
+ {% endif %}
95
97
  >
96
98
  {% if item.label %}
97
99
  <h3 id="{{ group_label_id }}">{{ item.label }}</h3>
98
100
  {% endif %}
99
101
  <ul>
100
- {{ render_sidebar_content(item.items, item_id) if item.items }}
102
+ {{ render_sidebar_content(item["items"], item_id) if item["items"] }}
101
103
  </ul>
102
104
  </div>
103
105
  {% elif item.type == "separator" %}
@@ -107,16 +109,18 @@
107
109
  <details
108
110
  id="submenu-{{ item_id }}"
109
111
  {{ "open" if item.open }}
110
- {% for key, value in item.attrs.items() %}
111
- {% if key != "open" %}{{ key }}="{{ value }}"{% endif %}
112
- {% endfor %}
112
+ {% if item.attrs %}
113
+ {% for key, value in item.attrs.items() %}
114
+ {% if key != "open" %}{{ key }}="{{ value }}"{% endif %}
115
+ {% endfor %}
116
+ {% endif %}
113
117
  >
114
118
  <summary aria-controls="submenu-{{ item_id }}-content">
115
119
  {% if item.icon %}{{ item.icon | safe }}{% endif %}
116
120
  {{ item.label }}
117
121
  </summary>
118
122
  <ul id="submenu-{{ item_id }}-content">
119
- {{ render_sidebar_content(item.items, item_id) if item.items }}
123
+ {{ render_sidebar_content(item["items"], item_id) if item["items"] }}
120
124
  </ul>
121
125
  </details>
122
126
  </li>
@@ -125,9 +129,11 @@
125
129
  <a
126
130
  href="{{ item.url }}"
127
131
  {{ '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 %}
132
+ {% if item.attrs %}
133
+ {% for key, value in item.attrs.items() %}
134
+ {% if key != "href" and key != "aria-current" %}{{ key }}="{{ value }}"{% endif %}
135
+ {% endfor %}
136
+ {% endif %}
131
137
  >
132
138
  {% if item.icon %}{{ item.icon | safe }}{% endif %}
133
139
  <span>{{ item.label }}</span>
@@ -14,17 +14,17 @@
14
14
  @param default_tab_index {number} [optional] [default=1] - The 1-based index of the tab to be active initially.
15
15
  #}
16
16
  {% macro tabs(
17
- id,
17
+ id=None,
18
18
  tabsets=[],
19
- main_attrs=None,
20
- tablist_attrs=None,
19
+ main_attrs={},
20
+ tablist_attrs={},
21
21
  default_tab_index=1
22
22
  )
23
23
  %}
24
+ {% set id = id or ("tabs-" + (range(100000, 999999) | random | string)) %}
24
25
  <div
25
26
  class="tabs {{ main_attrs.class }}"
26
- x-data="tabs({{ default_tab_index - 1 }})"
27
- {% if id %}id="{{ id }}"{% endif %}
27
+ id="{{ id }}"
28
28
  {% for key, value in main_attrs.items() %}
29
29
  {% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
30
30
  {% endfor %}
@@ -32,40 +32,43 @@
32
32
  <nav
33
33
  role="tablist"
34
34
  aria-orientation="horizontal"
35
- x-bind="$tablist"
36
35
  {% for key, value in tablist_attrs.items() %}
37
36
  {{ key }}="{{ value }}"
38
37
  {% endfor %}
39
38
  >
40
- {% for tabset in tabsets.items() %}
39
+ {% for tabset in tabsets %}
41
40
  <button
42
41
  type="button"
43
42
  role="tab"
44
- id="{{ tabset.id }}-tab-{{ loop.index }}"
45
- aria-controls="{{ tabset.id }}-panel-{{ loop.index }}"
43
+ id="{{ id }}-tab-{{ loop.index }}"
44
+ aria-controls="{{ id }}-panel-{{ loop.index }}"
46
45
  aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}"
47
46
  tabindex="0"
48
- {% for key, value in tabset.tab_attrs.items() %}
49
- {{ key }}="{{ value }}"
50
- {% endfor %}
47
+ {% if tabset.tab_attrs %}
48
+ {% for key, value in tabset.tab_attrs.items() %}
49
+ {{ key }}="{{ value }}"
50
+ {% endfor %}
51
+ {% endif %}
51
52
  >
52
53
  {{ tabset.tab | safe }}
53
54
  </button>
54
55
  {% endfor %}
55
56
  </nav>
56
57
 
57
- {% for tabset in tabsets.items() %}
58
+ {% for tabset in tabsets %}
58
59
  {% if tabset.panel %}
59
60
  <div
60
61
  role="tabpanel"
61
- id="{{ tabset.id }}-panel-{{ loop.index }}"
62
- aria-labelledby="{{ tabset.id }}-tab-{{ loop.index }}"
62
+ id="{{ id }}-panel-{{ loop.index }}"
63
+ aria-labelledby="{{ id }}-tab-{{ loop.index }}"
63
64
  tabindex="-1"
64
65
  aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}"
65
66
  {% if loop.index != default_tab_index %}hidden{% endif %}
66
- {% for key, value in tabset.panel_attrs.items() %}
67
- {{ key }}="{{ value }}"
68
- {% endfor %}
67
+ {% if tabset.panel_attrs %}
68
+ {% for key, value in tabset.panel_attrs.items() %}
69
+ {{ key }}="{{ value }}"
70
+ {% endfor %}
71
+ {% endif %}
69
72
  >
70
73
  {{ tabset.panel | safe }}
71
74
  </div>
@@ -5,21 +5,19 @@
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 main_attrs {object} [optional] - Additional HTML attributes for the main toaster container div.
8
- @param is_fragment {boolean} [optional] [default=False] - If True, renders only the toast elements with hx-swap-oob="beforeend", suitable for htmx responses. Skips script and template inclusion.
8
+ @param is_fragment {boolean} [optional] [default=false] - If true, renders only the toast elements with hx-swap-oob="beforeend", suitable for htmx responses. Skips script and template inclusion.
9
9
  #}
10
10
  {% macro toaster(
11
11
  id="toaster",
12
12
  toasts=[],
13
- main_attrs={},
14
- is_fragment=False
13
+ attrs={}
15
14
  ) %}
16
15
  <div
17
16
  id="{{ id }}"
18
- class="toaster"
19
- {% for key, value in main_attrs.items() %}
20
- {{ key }}="{{ value }}"
17
+ class="toaster {{ attrs.class }}"
18
+ {% for key, value in attrs.items() %}
19
+ {% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
21
20
  {% endfor %}
22
- {% if is_fragment %}hx-swap-oob="beforeend"{% endif %}
23
21
  >
24
22
  {% for item in toasts %}
25
23
  {{ toast(
@@ -31,79 +29,6 @@
31
29
  ) }}
32
30
  {% endfor %}
33
31
  </div>
34
-
35
- {% if not is_fragment %}
36
- <template id="toast-template">
37
- <div
38
- class="toast"
39
- role="status"
40
- aria-atomic="true"
41
- x-bind="$toastBindings"
42
- >
43
- <div class="toast-content">
44
- <div class="flex items-center justify-between gap-x-3 p-4 [&>svg]:size-4 [&>svg]:shrink-0 [&>[role=img]]:size-4 [&>[role=img]]:shrink-0 [&>[role=img]>svg]:size-4">
45
- <template x-if="config.icon">
46
- <span aria-hidden="true" role="img" x-html="config.icon"></span>
47
- </template>
48
- <template x-if="!config.icon && config.category === 'success'">
49
- {{ toast_icons.success | safe }}
50
- </template>
51
- <template x-if="!config.icon && config.category === 'error'">
52
- {{ toast_icons.error | safe }}
53
- </template>
54
- <template x-if="!config.icon && config.category === 'info'">
55
- {{ toast_icons.info | safe }}
56
- </template>
57
- <template x-if="!config.icon && config.category === 'warning'">
58
- {{ toast_icons.warning | safe }}
59
- </template>
60
- <section class="flex-1 flex flex-col gap-0.5 items-start">
61
- <template x-if="config.title">
62
- <h2 class="font-medium" x-text="config.title"></h2>
63
- </template>
64
- <template x-if="config.description">
65
- <p class="text-muted-foreground" x-text="config.description"></p>
66
- </template>
67
- </section>
68
- <template x-if="config.action || config.cancel">
69
- <footer class="flex flex-col gap-1 self-start">
70
- <template x-if="config.action?.click">
71
- <button
72
- type="button"
73
- class="btn h-6 text-xs px-2.5 rounded-sm"
74
- @click="executeAction(config.action.click)"
75
- x-text="config.action.label"
76
- ></button>
77
- </template>
78
- <template x-if="config.action?.url">
79
- <a
80
- :href="config.action.url"
81
- class="btn h-6 text-xs px-2.5 rounded-sm"
82
- x-text="config.action.label"
83
- ></a>
84
- </template>
85
- <template x-if="config.cancel?.click">
86
- <button
87
- type="button"
88
- class="btn-outline h-6 text-xs px-2.5 rounded-sm"
89
- @click="executeAction(config.cancel.click)"
90
- x-text="config.cancel.label"
91
- ></button>
92
- </template>
93
- <template x-if="config.cancel?.url">
94
- <a
95
- :href="config.cancel.url"
96
- class="btn-outline h-6 text-xs px-2.5 rounded-sm"
97
- x-text="config.cancel.label"
98
- ></a>
99
- </template>
100
- </footer>
101
- </template>
102
- </div>
103
- </div>
104
- </div>
105
- </template>
106
- {% endif %}
107
32
  {% endmacro %}
108
33
 
109
34
  {#
@@ -134,65 +59,51 @@
134
59
  aria-atomic="true"
135
60
  aria-hidden="false"
136
61
  {% if category %}data-category="{{ category }}"{% endif %}
137
- x-data="toast({
138
- category: '{{ category }}',
139
- duration: {{ duration or 'null' }}
140
- })"
141
- x-bind="$toastBindings"
142
62
  >
143
63
  <div class="toast-content">
144
- <div class="flex items-center justify-between gap-x-3 p-4 [&>svg]:size-4 [&>svg]:shrink-0 [&>[role=img]]:size-4 [&>[role=img]]:shrink-0 [&>[role=img]>svg]:size-4">
145
- {% if category in ["error", "success", "info", "warning"] %}
146
- {{ toast_icons[category] | safe }}
64
+ {% if category in ["error", "success", "info", "warning"] %}
65
+ {% if category == "success" %}
66
+ <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>
67
+ {% elif category == "error" %}
68
+ <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>
69
+ {% elif category == "info" %}
70
+ <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>
71
+ {% elif category == "warning" %}
72
+ <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>
147
73
  {% endif %}
148
- <section class="flex-1 flex flex-col gap-0.5 items-start">
149
- {% if title %}
150
- <h2 class="font-medium">{{ title }}</h2>
74
+ {% endif %}
75
+ <section>
76
+ {% if title %}<h2>{{ title }}</h2>{% endif %}
77
+ {% if description %}<p>{{ description }}</p>{% endif %}
78
+ </section>
79
+ {% if action or cancel %}
80
+ <footer>
81
+ {% if action %}
82
+ {% if action.href %}
83
+ <a
84
+ href="{{ action.href }}"
85
+ class="btn-sm"
86
+ data-toast-action
87
+ >{{ action.label }}</a>
88
+ {% else %}
89
+ <button
90
+ type="button"
91
+ class="btn"
92
+ data-toast-action
93
+ {% if action.onclick %}onclick="{{ action.onclick }}"{% endif %}
94
+ >{{ action.label }}</button>
95
+ {% endif %}
151
96
  {% endif %}
152
- {% if description %}
153
- <p class="text-muted-foreground">{{ description }}</p>
97
+ {% if cancel %}
98
+ <button
99
+ type="button"
100
+ class="btn-sm-outline"
101
+ data-toast-cancel
102
+ {% if cancel.onclick %}onclick="{{ cancel.onclick }}"{% endif %}
103
+ >{{ cancel.label }}</button>
154
104
  {% endif %}
155
- </section>
156
- {% if action or cancel %}
157
- <footer class="flex flex-col gap-1 self-start">
158
- {% if action %}
159
- {% if action.click %}
160
- <button
161
- type="button"
162
- class="btn h-6 text-xs px-2.5 rounded-sm"
163
- @click="{{ action.click }}"
164
- >{{ action.label }}</button>
165
- {% elif action.url %}
166
- <a
167
- href="{{ action.url }}"
168
- class="btn h-6 text-xs px-2.5 rounded-sm"
169
- >{{ action.label }}</a>
170
- {% endif %}
171
- {% endif %}
172
- {% if cancel %}
173
- {% if cancel.click %}
174
- <button
175
- type="button"
176
- class="btn-outline h-6 text-xs px-2.5 rounded-sm"
177
- @click="{{ cancel.click }}"
178
- >{{ cancel.label }}</button>
179
- {% elif cancel.url %}
180
- <a
181
- href="{{ cancel.url }}"
182
- class="btn-outline h-6 text-xs px-2.5 rounded-sm"
183
- >{{ toast.cancel.label }}</a>
184
- {% endif %}
185
- {% endif %}
186
- </footer>
187
- {% endif %}
188
- </div>
105
+ </footer>
106
+ {% endif %}
189
107
  </div>
190
108
  </div>
191
- {% endmacro %}
192
-
193
- {% set toast_icons = {
194
- '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" class="lucide lucide-circle-check-icon lucide-circle-check"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>',
195
- '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" class="lucide lucide-circle-x-icon lucide-circle-x"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>',
196
- '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" class="lucide lucide-info-icon lucide-info"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>',
197
- '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" class="lucide lucide-triangle-alert-icon lucide-triangle-alert"><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>'
198
- } %}
109
+ {% endmacro %}
@@ -3,14 +3,14 @@
3
3
 
4
4
  @param id {string} [optional] - Unique identifier for the dropdown component.
5
5
  @param trigger {string} [optional] - HTML content for the button that triggers the dropdown.
6
- @param menu {array} [optional] - Array of menu items for the dropdown.
6
+ @param items {array} [optional] - Array of menu items for the dropdown.
7
7
  @param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
8
8
  @param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
9
9
  @param popover_attrs {object} [optional] - Additional HTML attributes for the dropdown content div.
10
10
  #}
11
11
  {% macro dropdown_menu(
12
- id=None,
13
12
  trigger,
13
+ id=None,
14
14
  items=None,
15
15
  main_attrs={},
16
16
  trigger_attrs={},
@@ -78,9 +78,11 @@
78
78
  <div
79
79
  role="group"
80
80
  aria-labelledby="{{ group_label_id }}"
81
- {% for key, value in item.attrs %}
82
- {{ key }}="{{ value }}"
83
- {% endfor %}
81
+ {% if item.attrs %}
82
+ {% for key, value in item.attrs %}
83
+ {{ key }}="{{ value }}"
84
+ {% endfor %}
85
+ {% endif %}
84
86
  >
85
87
  <div role="presentation" id="{{ group_label_id }}">{{ item.label }}</div>
86
88
  {{ render_dropdown_items(item.items, item_id) if item.items }}
@@ -93,9 +95,11 @@
93
95
  id="{{ item_id }}"
94
96
  role="menuitem"
95
97
  href="{{ item.url }}"
96
- {% for key, value in item.attrs %}
97
- {% if key != "url" %} {{ key }}="{{ value }}" {% endif %}
98
- {% endfor %}
98
+ {% if item.attrs %}
99
+ {% for key, value in item.attrs %}
100
+ {% if key != "url" %} {{ key }}="{{ value }}" {% endif %}
101
+ {% endfor %}
102
+ {% endif %}
99
103
  >
100
104
  {{ item.label | safe }}
101
105
  </a>
@@ -103,9 +107,11 @@
103
107
  <div
104
108
  id="{{ item_id }}"
105
109
  role="menuitem"
106
- {% for key, value in item.attrs %}
107
- {{ key }}="{{ value }}"
108
- {% endfor %}
110
+ {% if item.attrs %}
111
+ {% for key, value in item.attrs %}
112
+ {{ key }}="{{ value }}"
113
+ {% endfor %}
114
+ {% endif %}
109
115
  >
110
116
  {{ item.label | safe }}
111
117
  </div>
@@ -8,8 +8,8 @@
8
8
  @param popover_attrs {object} [optional] - Additional HTML attributes for the popover content div.
9
9
  #}
10
10
  {% macro popover(
11
- id=None,
12
11
  trigger,
12
+ id=None,
13
13
  main_attrs={},
14
14
  trigger_attrs={},
15
15
  popover_attrs={}
@@ -145,9 +145,11 @@
145
145
  <div
146
146
  role="group"
147
147
  aria-labelledby="{{ group_label_id }}"
148
- {% for key, value in item.attrs %}
149
- {{ key }}="{{ value }}"
150
- {% endfor %}
148
+ {% if item.attrs %}
149
+ {% for key, value in item.attrs %}
150
+ {{ key }}="{{ value }}"
151
+ {% endfor %}
152
+ {% endif %}
151
153
  >
152
154
  <div role="presentation" id="{{ group_label_id }}">{{ item.label }}</div>
153
155
  {{ render_select_items(item.items, selected, item_id) if item.items }}
@@ -160,9 +162,11 @@
160
162
  role="option"
161
163
  data-value="{{ item.value }}"
162
164
  {% if selected == item.value %}aria-selected="true"{% endif %}
163
- {% for key, value in item.attrs %}
164
- {{ key }}="{{ value }}"
165
- {% endfor %}
165
+ {% if item.attrs %}
166
+ {% for key, value in item.attrs %}
167
+ {{ key }}="{{ value }}"
168
+ {% endfor %}
169
+ {% endif %}
166
170
  >
167
171
  {{ item.label | safe }}
168
172
  </div>
@@ -7,7 +7,11 @@
7
7
  @param side {string} [optional] - Side of the sidebar to display.
8
8
  @param header {string} [optional] - Header content for the sidebar.
9
9
  @param footer {string} [optional] - Footer content for the sidebar.
10
- @param menu {array} [optional] - Array of menu items for the sidebar.
10
+ @param items {array} [optional] - Array of menu items for the sidebar.
11
+ @param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
12
+ @param header_attrs {object} [optional] - Additional HTML attributes for the header element.
13
+ @param content_attrs {object} [optional] - Additional HTML attributes for the content section.
14
+ @param footer_attrs {object} [optional] - Additional HTML attributes for the footer element.
11
15
  #}
12
16
  {% macro sidebar(
13
17
  id=None,
@@ -20,8 +24,7 @@
20
24
  main_attrs={},
21
25
  header_attrs={},
22
26
  content_attrs={},
23
- footer_attrs={},
24
- content_wrapper_attrs=None
27
+ footer_attrs={}
25
28
  ) %}
26
29
  <aside
27
30
  {% if id %}id="{{ id }}"{% endif %}
@@ -86,9 +89,11 @@
86
89
  <div
87
90
  role="group"
88
91
  {% if item.label %}aria-labelledby="{{ group_label_id }}"{% endif %}
89
- {% for key, value in item.attrs %}
90
- {{ key }}="{{ value }}"
91
- {% endfor %}
92
+ {% if item.attrs %}
93
+ {% for key, value in item.attrs %}
94
+ {{ key }}="{{ value }}"
95
+ {% endfor %}
96
+ {% endif %}
92
97
  >
93
98
  {% if item.label %}
94
99
  <h3 id="{{ group_label_id }}">{{ item.label }}</h3>
@@ -104,9 +109,11 @@
104
109
  <details
105
110
  id="submenu-{{ item_id }}"
106
111
  {{ "open" if item.open }}
107
- {% for key, value in item.attrs %}
108
- {% if key != "open" %}{{ key }}="{{ value }}"{% endif %}
109
- {% endfor %}
112
+ {% if item.attrs %}
113
+ {% for key, value in item.attrs %}
114
+ {% if key != "open" %}{{ key }}="{{ value }}"{% endif %}
115
+ {% endfor %}
116
+ {% endif %}
110
117
  >
111
118
  <summary aria-controls="submenu-{{ item_id }}-content">
112
119
  {% if item.icon %}{{ item.icon | safe }}{% endif %}
@@ -122,9 +129,11 @@
122
129
  <a
123
130
  href="{{ item.url }}"
124
131
  {{ 'aria-current="page"' if item.current }}
125
- {% for key, value in item.attrs %}
126
- {% if key != "href" and key != "aria-current" %}{{ key }}="{{ value }}"{% endif %}
127
- {% endfor %}
132
+ {% if item.attrs %}
133
+ {% for key, value in item.attrs %}
134
+ {% if key != "href" and key != "aria-current" %}{{ key }}="{{ value }}"{% endif %}
135
+ {% endfor %}
136
+ {% endif %}
128
137
  >
129
138
  {% if item.icon %}{{ item.icon | safe }}{% endif %}
130
139
  <span>{{ item.label }}</span>
@@ -16,8 +16,8 @@
16
16
  {% macro tabs(
17
17
  id=None,
18
18
  tabsets=[],
19
- main_attrs=None,
20
- tablist_attrs=None,
19
+ main_attrs={},
20
+ tablist_attrs={},
21
21
  default_tab_index=1
22
22
  )
23
23
  %}
@@ -44,9 +44,11 @@
44
44
  aria-controls="{{ id }}-panel-{{ loop.index }}"
45
45
  aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}"
46
46
  tabindex="0"
47
- {% for key, value in tabset.tab_attrs %}
48
- {{ key }}="{{ value }}"
49
- {% endfor %}
47
+ {% if tabset.tab_attrs %}
48
+ {% for key, value in tabset.tab_attrs %}
49
+ {{ key }}="{{ value }}"
50
+ {% endfor %}
51
+ {% endif %}
50
52
  >
51
53
  {{ tabset.tab | safe }}
52
54
  </button>
@@ -62,9 +64,11 @@
62
64
  tabindex="-1"
63
65
  aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}"
64
66
  {% if loop.index != default_tab_index %}hidden{% endif %}
65
- {% for key, value in tabset.panel_attrs %}
66
- {{ key }}="{{ value }}"
67
- {% endfor %}
67
+ {% if tabset.panel_attrs %}
68
+ {% for key, value in tabset.panel_attrs %}
69
+ {{ key }}="{{ value }}"
70
+ {% endfor %}
71
+ {% endif %}
68
72
  >
69
73
  {{ tabset.panel | safe }}
70
74
  </div>
@@ -10,7 +10,7 @@
10
10
  {% macro toaster(
11
11
  id="toaster",
12
12
  toasts=[],
13
- attrs=None
13
+ attrs={}
14
14
  ) %}
15
15
  <div
16
16
  id="{{ id }}"
@@ -63,13 +63,13 @@
63
63
  <div class="toast-content">
64
64
  {% if category in ["error", "success", "info", "warning"] %}
65
65
  {% if category == "success" %}
66
- {% lucide "circle-check" %}
66
+ <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>
67
67
  {% elif category == "error" %}
68
- {% lucide "circle-x" %}
68
+ <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>
69
69
  {% elif category == "info" %}
70
- {% lucide "info" %}
70
+ <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>
71
71
  {% elif category == "warning" %}
72
- {% lucide "triangle-alert" %}
72
+ <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>
73
73
  {% endif %}
74
74
  {% endif %}
75
75
  <section>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "basecoat-cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Add Basecoat components to your project",
5
5
  "author": "hunvreus",
6
6
  "license": "MIT",