basecoat-cli 0.2.0-beta.1 → 0.2.1

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.
@@ -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 %}