mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-11 04:38:18 +00:00
Update Jinja macros
This commit is contained in:
+1
-1
@@ -192,7 +192,7 @@ layout: layouts/layout.njk
|
||||
</div>
|
||||
<div class="grid gap-2 flex-1">
|
||||
<label for="payment-method-cvv">CVV</label>
|
||||
<input type="text" id="payment-method-cvv" placeholder="123" maxlength="3" class="w-full" />
|
||||
<input type="text" id="payment-method-cvv" placeholder="123" maxlength="4" class="w-full" />
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn w-full">Continue</button>
|
||||
|
||||
+4
-4
@@ -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": {
|
||||
|
||||
@@ -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 %}
|
||||
>
|
||||
<nav role="menu">
|
||||
{{ caller() if caller }}
|
||||
{% if menu %}
|
||||
{{ render_dropdown_items(menu, id ~ "-items" if id else "items") }}
|
||||
{% else %}
|
||||
{{ caller() if caller }}
|
||||
{% endif %}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
{% 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 %}
|
||||
<div
|
||||
role="group"
|
||||
aria-labelledby="{{ group_label_id }}"
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
<div role="heading" id="{{ group_label_id }}">{{ item.label }}</div>
|
||||
{{ render_dropdown_items(item.items, item_id) if item.items }}
|
||||
</div>
|
||||
{% elif item.type == "separator" %}
|
||||
<hr role="separator" />
|
||||
{% elif item.type == "item" or not item.type %}
|
||||
{% if item.url %}
|
||||
<a
|
||||
role="menuitem"
|
||||
href="{{ item.url }}"
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{% if key != "url" %} {{ key }}="{{ value }}" {% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{{ item.label | safe }}
|
||||
</a>
|
||||
{% else %}
|
||||
<button
|
||||
role="menuitem"
|
||||
type="button"
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ item.label | safe }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
@@ -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 %}
|
||||
</div>
|
||||
</div>
|
||||
{% if name is defined %}
|
||||
@@ -105,4 +110,42 @@
|
||||
>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% 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 %}
|
||||
<div
|
||||
role="group"
|
||||
aria-labelledby="{{ group_label_id }}"
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
<div role="heading" id="{{ group_label_id }}">{{ item.label }}</div>
|
||||
{{ render_select_items(item.items, item_id) if item.items }}
|
||||
</div>
|
||||
{% elif item.type == "separator" %}
|
||||
<hr role="separator" />
|
||||
{% elif item.type == "item" or not item.type %}
|
||||
<div
|
||||
role="option"
|
||||
data-value="{{ item.value }}"
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ item.label | safe }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
@@ -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
|
||||
) %}
|
||||
<div
|
||||
{% if id %}id="{{ id }}"{% endif %}
|
||||
class="sidebar {{ main_attrs.class }}"
|
||||
data-uninitialized
|
||||
data-side="{{ side if side else "left" }}"
|
||||
aria-hidden="{{ "true" if not open else "false" }}"
|
||||
{{ "inert" if not open }}
|
||||
{% for key, value in main_attrs.items() %}
|
||||
{% if key != "class" %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
x-data="sidebar({{ "true" if open else "false" }})"
|
||||
x-bind="$main"
|
||||
>
|
||||
<nav
|
||||
aria-label="{{ label }}"
|
||||
>
|
||||
{% if header %}
|
||||
<header
|
||||
{% for key, value in header_attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ header | safe }}
|
||||
</header>
|
||||
{% endif %}
|
||||
|
||||
<section
|
||||
{% for key, value in content_attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{% if menu %}
|
||||
{{ render_sidebar_content(menu, id ~ "-content" if id else "content") }}
|
||||
{% else %}
|
||||
{{ caller() if caller }}
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
{% if footer %}
|
||||
<footer
|
||||
{% for key, value in footer_attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ footer | safe }}
|
||||
</footer>
|
||||
{% endif %}
|
||||
</nav>
|
||||
</div>
|
||||
{% 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 %}
|
||||
<div
|
||||
role="group"
|
||||
{% if item.label %}aria-labelledby="{{ group_label_id }}"{% endif %}
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{% if item.label %}
|
||||
<h3 id="{{ group_label_id }}">{{ item.label }}</h3>
|
||||
{% endif %}
|
||||
<ul>
|
||||
{{ render_sidebar_content(item.items, item_id) if item.items }}
|
||||
</ul>
|
||||
</div>
|
||||
{% elif item.type == "separator" %}
|
||||
<hr role="separator"/>
|
||||
{% elif item.type == "submenu" %}
|
||||
<li>
|
||||
<details
|
||||
id="submenu-{{ item_id }}"
|
||||
{{ "open" if item.open }}
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{% if key != "open" %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
<summary aria-controls="submenu-{{ item_id }}-content">
|
||||
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
{{ item.label }}
|
||||
</summary>
|
||||
<ul id="submenu-{{ item_id }}-content">
|
||||
{{ render_sidebar_content(item.items, item_id) if item.items }}
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
{% elif item.type == "item" or not item.type %}
|
||||
<li>
|
||||
<a
|
||||
href="{{ item.url }}"
|
||||
{{ 'aria-current="page"' if item.current }}
|
||||
{% for key, value in item.attrs.items() %}
|
||||
{% if key != "href" and key != "aria-current" %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
<span>{{ item.label }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
@@ -37,7 +37,7 @@
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{% for tabset in tabsets %}
|
||||
{% for tabset in tabsets.items() %}
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
@@ -54,7 +54,7 @@
|
||||
{% endfor %}
|
||||
</nav>
|
||||
|
||||
{% for tabset in tabsets %}
|
||||
{% for tabset in tabsets.items() %}
|
||||
{% if tabset.panel %}
|
||||
<div
|
||||
role="tabpanel"
|
||||
|
||||
+109
-84
@@ -3,19 +3,9 @@
|
||||
Can render the initial container or be used with htmx OOB swaps to add toasts dynamically.
|
||||
|
||||
@param id {string} [optional] [default="toaster"] - Unique identifier for the toaster container.
|
||||
@param toasts {array} [optional] - An array of toast objects to render initially.
|
||||
Each toast object can have:
|
||||
- category {string} [optional]: Type of toast ('success', 'error', 'info', 'warning'). Determines icon and ARIA role.
|
||||
- title {string} [optional]: The main title text of the toast.
|
||||
- description {string} [optional]: The secondary description text.
|
||||
- duration {number} [optional] [default=5000]: Duration in milliseconds before the toast auto-dismisses.
|
||||
- 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.
|
||||
- cancel {object} [optional]: Defines a cancel/dismiss button (similar structure to action).
|
||||
@param toasts {array} [optional] - An array of toast objects to render initially. See the <code>toast()</code> macro for more details.
|
||||
@param main_attrs {object} [optional] - Additional HTML attributes for the main toaster container div.
|
||||
@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.
|
||||
@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.
|
||||
#}
|
||||
{% macro toaster(
|
||||
id="toaster",
|
||||
@@ -23,13 +13,6 @@
|
||||
main_attrs=None,
|
||||
is_fragment=False
|
||||
) %}
|
||||
{% set icons = {
|
||||
'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>',
|
||||
'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>',
|
||||
'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>',
|
||||
'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>'
|
||||
} %}
|
||||
|
||||
<div
|
||||
id="{{ id }}"
|
||||
class="toaster"
|
||||
@@ -38,64 +21,14 @@
|
||||
{% endfor %}
|
||||
{% if is_fragment %}hx-swap-oob="beforeend"{% endif %}
|
||||
>
|
||||
{% for toast in toasts %}
|
||||
<div
|
||||
class="toast"
|
||||
role="{{ 'alert' if toast.category == 'error' else 'status' }}"
|
||||
aria-atomic="true"
|
||||
aria-hidden="false"
|
||||
{% if toast.category %}data-category="{{ toast.category }}"{% endif %}
|
||||
x-data="toast('{{ toast.category }}', {{ toast.duration }})"
|
||||
x-bind="$toastBindings"
|
||||
>
|
||||
<div class="toast-content">
|
||||
<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">
|
||||
{% if toast.category in ["error", "success", "info", "warning"] %}
|
||||
{{ icons[toast.category] | safe }}
|
||||
{% endif %}
|
||||
<section class="flex-1 flex flex-col gap-0.5 items-start">
|
||||
{% if toast.title %}
|
||||
<h2 class="font-medium">{{ toast.title }}</h2>
|
||||
{% endif %}
|
||||
{% if toast.description %}
|
||||
<p class="text-muted-foreground">{{ toast.description }}</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% if toast.action or toast.cancel %}
|
||||
<footer class="flex flex-col gap-1 self-start">
|
||||
{% if toast.action %}
|
||||
{% if toast.action.click %}
|
||||
<button
|
||||
type="button"
|
||||
class="btn h-6 text-xs px-2.5 rounded-sm"
|
||||
@click="{{ toast.action.click }}"
|
||||
>{{ toast.action.label }}</button>
|
||||
{% elif toast.action.url %}
|
||||
<a
|
||||
href="{{ toast.action.url }}"
|
||||
class="btn h-6 text-xs px-2.5 rounded-sm"
|
||||
>{{ toast.action.label }}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if toast.cancel %}
|
||||
{% if toast.cancel.click %}
|
||||
<button
|
||||
type="button"
|
||||
class="btn-outline h-6 text-xs px-2.5 rounded-sm"
|
||||
@click="{{ toast.cancel.click }}"
|
||||
>{{ toast.cancel.label }}</button>
|
||||
{% elif toast.cancel.url %}
|
||||
<a
|
||||
href="{{ toast.cancel.url }}"
|
||||
class="btn-outline h-6 text-xs px-2.5 rounded-sm"
|
||||
>{{ toast.cancel.label }}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</footer>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% for item in toasts.items() %}
|
||||
{{ toast(
|
||||
category=item.category,
|
||||
title=item.title,
|
||||
description=item.description,
|
||||
action=item.action,
|
||||
cancel=item.cancel
|
||||
) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
@@ -113,16 +46,16 @@
|
||||
<span aria-hidden="true" role="img" x-html="config.icon"></span>
|
||||
</template>
|
||||
<template x-if="!config.icon && config.category === 'success'">
|
||||
{{ icons.success | safe }}
|
||||
{{ toast_icons.success | safe }}
|
||||
</template>
|
||||
<template x-if="!config.icon && config.category === 'error'">
|
||||
{{ icons.error | safe }}
|
||||
{{ toast_icons.error | safe }}
|
||||
</template>
|
||||
<template x-if="!config.icon && config.category === 'info'">
|
||||
{{ icons.info | safe }}
|
||||
{{ toast_icons.info | safe }}
|
||||
</template>
|
||||
<template x-if="!config.icon && config.category === 'warning'">
|
||||
{{ icons.warning | safe }}
|
||||
{{ toast_icons.warning | safe }}
|
||||
</template>
|
||||
<section class="flex-1 flex flex-col gap-0.5 items-start">
|
||||
<template x-if="config.title">
|
||||
@@ -138,7 +71,7 @@
|
||||
<button
|
||||
type="button"
|
||||
class="btn h-6 text-xs px-2.5 rounded-sm"
|
||||
@click="$evaluate(config.action.click)"
|
||||
@click="executeAction(config.action.click)"
|
||||
x-text="config.action.label"
|
||||
></button>
|
||||
</template>
|
||||
@@ -153,7 +86,7 @@
|
||||
<button
|
||||
type="button"
|
||||
class="btn-outline h-6 text-xs px-2.5 rounded-sm"
|
||||
@click="$evaluate(config.cancel.click)"
|
||||
@click="executeAction(config.cancel.click)"
|
||||
x-text="config.cancel.label"
|
||||
></button>
|
||||
</template>
|
||||
@@ -165,9 +98,101 @@
|
||||
></a>
|
||||
</template>
|
||||
</footer>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
{% 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
|
||||
) %}
|
||||
<div
|
||||
class="toast"
|
||||
role="{{ 'alert' if category == 'error' else 'status' }}"
|
||||
aria-atomic="true"
|
||||
aria-hidden="false"
|
||||
{% if category %}data-category="{{ category }}"{% endif %}
|
||||
x-data="toast({
|
||||
category: '{{ category }}',
|
||||
duration: {{ duration or 'null' }}
|
||||
})"
|
||||
x-bind="$toastBindings"
|
||||
>
|
||||
<div class="toast-content">
|
||||
<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">
|
||||
{% if category in ["error", "success", "info", "warning"] %}
|
||||
{{ toast_icons[category] | safe }}
|
||||
{% endif %}
|
||||
<section class="flex-1 flex flex-col gap-0.5 items-start">
|
||||
{% if title %}
|
||||
<h2 class="font-medium">{{ title }}</h2>
|
||||
{% endif %}
|
||||
{% if description %}
|
||||
<p class="text-muted-foreground">{{ description }}</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% if action or cancel %}
|
||||
<footer class="flex flex-col gap-1 self-start">
|
||||
{% if action %}
|
||||
{% if action.click %}
|
||||
<button
|
||||
type="button"
|
||||
class="btn h-6 text-xs px-2.5 rounded-sm"
|
||||
@click="{{ action.click }}"
|
||||
>{{ action.label }}</button>
|
||||
{% elif action.url %}
|
||||
<a
|
||||
href="{{ action.url }}"
|
||||
class="btn h-6 text-xs px-2.5 rounded-sm"
|
||||
>{{ action.label }}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if cancel %}
|
||||
{% if cancel.click %}
|
||||
<button
|
||||
type="button"
|
||||
class="btn-outline h-6 text-xs px-2.5 rounded-sm"
|
||||
@click="{{ cancel.click }}"
|
||||
>{{ cancel.label }}</button>
|
||||
{% elif cancel.url %}
|
||||
<a
|
||||
href="{{ cancel.url }}"
|
||||
class="btn-outline h-6 text-xs px-2.5 rounded-sm"
|
||||
>{{ toast.cancel.label }}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</footer>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% set toast_icons = {
|
||||
'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>',
|
||||
'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>',
|
||||
'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>',
|
||||
'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>'
|
||||
} %}
|
||||
Reference in New Issue
Block a user