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.
@@ -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 %}
@@ -4,14 +4,13 @@
4
4
  @param id {string} [optional] - Unique identifier for the select component.
5
5
  @param selected {string} [optional] - The initially selected value.
6
6
  @param name {string} [optional] - The name attribute for the hidden input storing the selected value.
7
- @param items {array} [optional] - An array of items (objects with type, label, value, attrs, and items) to render.
8
7
  @param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
9
8
  @param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
10
- @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.
11
10
  @param listbox_attrs {object} [optional] - Additional HTML attributes for the listbox div.
12
11
  @param input_attrs {object} [optional] - Additional HTML attributes for the hidden input.
13
12
  @param search_placeholder {string} [optional] [default="Search entries..."] - Placeholder text for the search input (combobox only).
14
- @param is_combobox {boolean} [optional] [default=False] - Renders a combobox with search functionality if True.
13
+ @param is_combobox {boolean} [optional] [default=false] - Renders a combobox with search functionality if true.
15
14
  #}
16
15
  {% macro select(
17
16
  id=None,
@@ -20,37 +19,57 @@
20
19
  items=None,
21
20
  main_attrs={},
22
21
  trigger_attrs={},
23
- content_attrs={},
22
+ popover_attrs={},
24
23
  listbox_attrs={},
25
24
  input_attrs={},
26
25
  search_placeholder="Search entries...",
27
- is_combobox=False
26
+ is_combobox=false
28
27
  ) %}
28
+ {% set id = id or ("select-" + (range(100000, 999999) | random | string)) %}
29
+
30
+ {% set first_option = namespace(item=None) %}
31
+ {% set selected_option = namespace(item=None) %}
32
+
33
+ {% for item in items %}
34
+ {% if item.type == "group" %}
35
+ {% for sub_item in item.items %}
36
+ {% if not first_option.item %}
37
+ {% set first_option.item = sub_item %}
38
+ {% endif %}
39
+ {% if selected and sub_item.value == selected and not selected_option.item %}
40
+ {% set selected_option.item = sub_item %}
41
+ {% endif %}
42
+ {% endfor %}
43
+ {% else %}
44
+ {% if not first_option.item %}
45
+ {% set first_option.item = item %}
46
+ {% endif %}
47
+ {% if selected and item.value == selected and not selected_option.item %}
48
+ {% set selected_option.item = item %}
49
+ {% endif %}
50
+ {% endif %}
51
+ {% endfor %}
52
+
53
+ {% set default_option = selected_option.item or first_option.item or None %}
54
+
29
55
  <div
30
- class="popover {{ main_attrs.class }}"
31
- x-data="select('{{ name }}', '{{ selected or '' }}')"
32
- @click.away="open = false"
33
- {% if id %}id="{{ id }}"{% endif %}
56
+ class="select {{ main_attrs.class }}"
34
57
  {% for key, value in main_attrs.items() %}
35
58
  {% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
36
59
  {% endfor %}
37
60
  >
38
61
  <button
39
62
  type="button"
63
+ class="btn-outline justify-between font-normal {{ trigger_attrs.class }}"
64
+ id="{{ id }}-trigger"
40
65
  aria-haspopup="listbox"
41
66
  aria-expanded="false"
42
- x-bind="$trigger"
43
- {% if id %}
44
- id="{{ id }}-trigger"
45
- aria-controls="{{ id }}-content"
46
- {% endif %}
47
- class="btn-outline justify-between font-normal {{ trigger_attrs.class }}"
67
+ aria-controls="{{ id }}-listbox"
48
68
  {% for key, value in trigger_attrs.items() %}
49
69
  {% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
50
70
  {% endfor %}
51
71
  >
52
- <div x-html="selectedLabel" class="flex items-center gap-x-2"
53
- ></div>
72
+ <span class="truncate">{{ default_option.label }}</span>
54
73
  {% if is_combobox %}
55
74
  <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>
56
75
  {% else %}
@@ -58,11 +77,10 @@
58
77
  {% endif %}
59
78
  </button>
60
79
  <div
80
+ id="{{ id }}"
61
81
  data-popover
62
82
  aria-hidden="true"
63
- x-bind="$content"
64
- {% if id %}id="{{ id }}-content"{% endif %}
65
- {% for key, value in content_attrs.items() %}
83
+ {% for key, value in popover_attrs.items() %}
66
84
  {{ key }}="{{ value }}"
67
85
  {% endfor %}
68
86
  >
@@ -78,38 +96,36 @@
78
96
  spellcheck="false"
79
97
  aria-autocomplete="list"
80
98
  role="combobox"
81
- aria-expanded="true"
82
- aria-controls="{{ id }}-content"
99
+ aria-expanded="false"
100
+ aria-controls="{{ id }}-listbox"
83
101
  aria-labelledby="{{ id }}-trigger"
84
- x-model="query"
85
- x-bind="$filter"
86
102
  >
87
103
  </header>
88
104
  {% endif %}
89
105
  <div
90
106
  role="listbox"
107
+ id="{{ id }}-listbox"
91
108
  aria-orientation="vertical"
109
+ aria-labelledby="{{ id }}-trigger"
92
110
  {% for key, value in listbox_attrs.items() %}
93
111
  {{ key }}="{{ value }}"
94
112
  {% endfor %}
95
113
  >
96
114
  {% if items %}
97
- {{ render_select_items(items, id ~ "-items" if id else "items") }}
115
+ {{ render_select_items(items, default_option.value, id ~ "-items" if id else "items") }}
98
116
  {% else %}
99
117
  {{ caller() if caller }}
100
118
  {% endif %}
101
119
  </div>
102
120
  </div>
103
- {% if name is defined %}
104
- <input
105
- type="hidden"
106
- name="{{ name }}"
107
- x-model="selectedValue"
108
- {% for key, value in input_attrs.items() %}
109
- {% if key != 'name' %}{{ key }}="{{ value }}"{% endif %}
110
- {% endfor %}
111
- >
112
- {% endif %}
121
+ <input
122
+ type="hidden"
123
+ name="{{ name or id ~ '-value' }}"
124
+ value="{{ (default_option.value if default_option) or '' }}"
125
+ {% for key, value in input_attrs.items() %}
126
+ {% if key != 'name' and key != 'value' %}{{ key }}="{{ value }}"{% endif %}
127
+ {% endfor %}
128
+ >
113
129
  </div>
114
130
  {% endmacro %}
115
131
 
@@ -119,31 +135,36 @@
119
135
  @param items {array} - The array of items to render.
120
136
  @param parent_id_prefix {string} [optional] - The prefix for the item id.
121
137
  #}
122
- {% macro render_select_items(items, parent_id_prefix="items") %}
123
- {% for item in items.items() %}
138
+ {% macro render_select_items(items, selected, parent_id_prefix="items") %}
139
+ {% for item in items %}
124
140
  {% set item_id = parent_id_prefix ~ "-" ~ loop.index %}
125
-
126
141
  {% if item.type == "group" %}
127
142
  {% set group_label_id = item.id if item.id else "group-label-" + item_id %}
128
143
  <div
129
144
  role="group"
130
145
  aria-labelledby="{{ group_label_id }}"
131
- {% for key, value in item.attrs.items() %}
132
- {{ key }}="{{ value }}"
133
- {% endfor %}
146
+ {% if item.attrs %}
147
+ {% for key, value in item.attrs.items() %}
148
+ {{ key }}="{{ value }}"
149
+ {% endfor %}
150
+ {% endif %}
134
151
  >
135
- <div role="heading" id="{{ group_label_id }}">{{ item.label }}</div>
136
- {{ render_select_items(item.items, item_id) if item.items }}
152
+ <div role="presentation" id="{{ group_label_id }}">{{ item.label }}</div>
153
+ {{ render_select_items(item.items, selected, item_id) if item.items }}
137
154
  </div>
138
155
  {% elif item.type == "separator" %}
139
156
  <hr role="separator" />
140
157
  {% elif item.type == "item" or not item.type %}
141
158
  <div
159
+ id="{{ item_id }}"
142
160
  role="option"
143
161
  data-value="{{ item.value }}"
144
- {% for key, value in item.attrs.items() %}
145
- {{ key }}="{{ value }}"
146
- {% endfor %}
162
+ {% if selected == item.value %}aria-selected="true"{% endif %}
163
+ {% if item.attrs %}
164
+ {% for key, value in item.attrs.items() %}
165
+ {{ key }}="{{ value }}"
166
+ {% endfor %}
167
+ {% endif %}
147
168
  >
148
169
  {{ item.label | safe }}
149
170
  </div>