diff --git a/docs/src/index.njk b/docs/src/index.njk index 09babd3..81f8c8f 100644 --- a/docs/src/index.njk +++ b/docs/src/index.njk @@ -192,7 +192,7 @@ layout: layouts/layout.njk
- +
diff --git a/package.json b/package.json index a30855d..9ce9ce6 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,10 @@ "description": "shadcn/ui for the rest of us", "type": "module", "files": [ - "css", - "js", - "nunjucks", - "jinja", + "src/css", + "src/js", + "src/nunjucks", + "src/jinja", "cli" ], "bin": { diff --git a/src/jinja/dropdown-menu.html.jinja b/src/jinja/dropdown-menu.html.jinja index cdaa0bf..fc16e12 100644 --- a/src/jinja/dropdown-menu.html.jinja +++ b/src/jinja/dropdown-menu.html.jinja @@ -3,6 +3,7 @@ @param id {string} [optional] - Unique identifier for the dropdown component. @param trigger {string} [optional] - HTML content for the button that triggers the dropdown. + @param menu {array} [optional] - Array of menu items for the dropdown. @param main_attrs {object} [optional] - Additional HTML attributes for the main container div. @param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button. @param content_attrs {object} [optional] - Additional HTML attributes for the dropdown content div. @@ -10,6 +11,7 @@ {% macro dropdown_menu( id=None, trigger=None, + menu=None, main_attrs={}, trigger_attrs={}, content_attrs={} @@ -19,8 +21,8 @@ x-data="dropdownMenu" @click.away="open = false" {% if id %}id="{{ id }}"{% endif %} - {% for key, value in main_attrs.items() %} - {% if key != 'class' %}{{ key }}="{{ value }}"{% endif %} + {% for key, value in main_attrs %} + {% if key != "class" %}{{ key }}="{{ value }}"{% endif %} {% endfor %} > {% if trigger %} @@ -33,7 +35,7 @@ id="{{ id }}-trigger" aria-controls="{{ id }}-menu" {% endif %} - {% for key, value in trigger_attrs.items() %} + {% for key, value in trigger_attrs %} {{ key }}="{{ value }}" {% endfor %} > @@ -45,13 +47,67 @@ aria-hidden="true" x-bind="$content" {% if id %}id="{{ id }}-menu"{% endif %} - {% for key, value in content_attrs.items() %} + {% for key, value in content_attrs %} {{ key }}="{{ value }}" {% endfor %} > +{% endmacro %} + +{# + Renders dropdown menu items recursively. + + @param items {array} - The array of items to render. + @param parent_id_prefix {string} [optional] - Prefix for generating element IDs. +#} +{% macro render_dropdown_items(items, parent_id_prefix="items") %} + {% for item in items.items() %} + {% set item_id = parent_id_prefix ~ "-" ~ loop.index %} + + {% if item.type == "group" %} + {% set group_label_id = item.id if item.id else "group-label-" + item_id %} +
+
{{ item.label }}
+ {{ render_dropdown_items(item.items, item_id) if item.items }} +
+ {% elif item.type == "separator" %} +
+ {% elif item.type == "item" or not item.type %} + {% if item.url %} + + {{ item.label | safe }} + + {% else %} + + {% endif %} + {% endif %} + {% endfor %} {% endmacro %} \ No newline at end of file diff --git a/src/jinja/select.html.jinja b/src/jinja/select.html.jinja index 72b2e5e..e58aa47 100644 --- a/src/jinja/select.html.jinja +++ b/src/jinja/select.html.jinja @@ -10,12 +10,13 @@ @param listbox_attrs {object} [optional] - Additional HTML attributes for the listbox div. @param input_attrs {object} [optional] - Additional HTML attributes for the hidden input. @param search_placeholder {string} [optional] [default="Search entries..."] - Placeholder text for the search input (combobox only). - @param is_combobox {boolean} [optional] [default=False] - Renders a combobox with search functionality if true. + @param is_combobox {boolean} [optional] [default=False] - Renders a combobox with search functionality if True. #} {% macro select( id=None, selected=None, name=None, + items=None, main_attrs={}, trigger_attrs={}, content_attrs={}, @@ -91,7 +92,11 @@ {{ key }}="{{ value }}" {% endfor %} > - {{ caller() if caller }} + {% if items %} + {{ render_select_items(items, id ~ "-items" if id else "items") }} + {% else %} + {{ caller() if caller }} + {% endif %} {% if name is defined %} @@ -105,4 +110,42 @@ > {% endif %} +{% endmacro %} + +{# + Renders a list of items for the select component. + + @param items {array} - The array of items to render. + @param parent_id_prefix {string} [optional] - The prefix for the item id. +#} +{% macro render_select_items(items, parent_id_prefix="items") %} + {% for item in items.items() %} + {% set item_id = parent_id_prefix ~ "-" ~ loop.index %} + + {% if item.type == "group" %} + {% set group_label_id = item.id if item.id else "group-label-" + item_id %} +
+
{{ item.label }}
+ {{ render_select_items(item.items, item_id) if item.items }} +
+ {% elif item.type == "separator" %} +
+ {% elif item.type == "item" or not item.type %} +
+ {{ item.label | safe }} +
+ {% endif %} + {% endfor %} {% endmacro %} \ No newline at end of file diff --git a/src/jinja/sidebar.html.jinja b/src/jinja/sidebar.html.jinja new file mode 100644 index 0000000..f6f12bd --- /dev/null +++ b/src/jinja/sidebar.html.jinja @@ -0,0 +1,138 @@ +{# + Renders a sidebar component. + + @param id {string} [optional] - Unique identifier for the sidebar component. + @param label {string} [optional] [default="Sidebar navigation"] - Label for the sidebar navigation. + @param open {boolean} [optional] [default=True] - Whether the sidebar is open. + @param side {string} [optional] - Side of the sidebar to display. + @param header {string} [optional] - Header content for the sidebar. + @param footer {string} [optional] - Footer content for the sidebar. + @param menu {array} [optional] - Array of menu items for the sidebar. +#} +{% macro sidebar( + id=None, + label="Sidebar navigation", + open=True, + side=None, + header=None, + footer=None, + menu=None, + main_attrs={}, + header_attrs={}, + content_attrs={}, + footer_attrs={}, + content_wrapper_attrs=None +) %} + +{% endmacro %} + +{# + Renders sidebar content recursively (groups, items, submenus, separators). + + @param items {array} - The array of items to render. + @param parent_id_prefix {string} [optional] - Prefix for generating element IDs. +#} +{% macro render_sidebar_content(items, parent_id_prefix="content") %} + {% for item in items.items() %} + {% set item_id = parent_id_prefix ~ "-" ~ loop.index %} + + {% if item.type == "group" %} + {% set group_label_id = item.id if item.id else "group-label-" + item_id %} +
+ {% if item.label %} +

{{ item.label }}

+ {% endif %} + +
+ {% elif item.type == "separator" %} +
+ {% elif item.type == "submenu" %} +
  • + +
  • + {% elif item.type == "item" or not item.type %} +
  • + + {% if item.icon %}{{ item.icon | safe }}{% endif %} + {{ item.label }} + +
  • + {% endif %} + {% endfor %} +{% endmacro %} \ No newline at end of file diff --git a/src/jinja/tabs.html.jinja b/src/jinja/tabs.html.jinja index e9cddb6..4b1537e 100644 --- a/src/jinja/tabs.html.jinja +++ b/src/jinja/tabs.html.jinja @@ -37,7 +37,7 @@ {{ key }}="{{ value }}" {% endfor %} > - {% for tabset in tabsets %} + {% for tabset in tabsets.items() %} - {% elif toast.action.url %} - {{ toast.action.label }} - {% endif %} - {% endif %} - {% if toast.cancel %} - {% if toast.cancel.click %} - - {% elif toast.cancel.url %} - {{ toast.cancel.label }} - {% endif %} - {% endif %} - - {% endif %} - - - +{% for item in toasts.items() %} + {{ toast( + category=item.category, + title=item.title, + description=item.description, + action=item.action, + cancel=item.cancel + ) }} {% endfor %} @@ -113,16 +46,16 @@
    @@ -153,7 +86,7 @@ @@ -165,9 +98,101 @@ > + {% endif %} -{% endmacro %} \ No newline at end of file +{% endmacro %} + +{# + Renders a single toast message. + + @param category {string} [optional] [default="success"] - Type of toast ('success', 'error', 'info', 'warning'). Determines icon and ARIA role. + @param title {string} [optional] - The main title text of the toast. + @param description {string} [optional] - The secondary description text. + @param action {object} [optional] - Defines an action button. + - label {string}: Button text. + - click {string}: JavaScript code to execute on click (e.g., '$dispatch(\'custom-event\')'). + - url {string}: URL for an anchor link button. + @param cancel {object} [optional] - Defines a cancel/dismiss button (similar structure to action). + - label {string}: Button text. + - click {string}: JavaScript code to execute on click (e.g., '$dispatch(\'custom-event\')'). + - url {string}: URL for an anchor link button. +#} +{% macro toast( + category="success", + title="", + description="", + action=None, + cancel=None +) %} +
    +
    +
    + {% if category in ["error", "success", "info", "warning"] %} + {{ toast_icons[category] | safe }} + {% endif %} +
    + {% if title %} +

    {{ title }}

    + {% endif %} + {% if description %} +

    {{ description }}

    + {% endif %} +
    + {% if action or cancel %} +
    + {% if action %} + {% if action.click %} + + {% elif action.url %} + {{ action.label }} + {% endif %} + {% endif %} + {% if cancel %} + {% if cancel.click %} + + {% elif cancel.url %} + {{ toast.cancel.label }} + {% endif %} + {% endif %} +
    + {% endif %} +
    +
    +
    +{% endmacro %} + +{% set toast_icons = { + 'success': '', + 'error': '', + 'info': '', + 'warning': '' +} %} \ No newline at end of file