Documentation finished

This commit is contained in:
Ronan Berder
2025-05-07 14:57:30 +08:00
parent b7c772f9a2
commit 1a4d292437
23 changed files with 536 additions and 296 deletions
-11
View File
@@ -150,17 +150,6 @@
[x-cloak] {
display: none !important;
}
/* HTMX */
.htmx-indicator {
@apply invisible opacity-0 transition-discrete transition-all;
}
.htmx-indicator:not(.htmx-request) {
@apply hidden;
}
.htmx-request,
.htmx-request.htmx-indicator {
@apply visible opacity-100;
}
}
/* Alert */
+6 -1
View File
@@ -36,6 +36,11 @@ window.basecoat.registerToast = function(Alpine) {
this.open = false;
this.$el.blur();
},
executeAction(actionString) {
if (actionString) {
Alpine.evaluate(this.$el, actionString);
}
},
$toastBindings: {
['@mouseenter']() { this.$store.toaster.isPaused = true },
@@ -50,7 +55,7 @@ window.basecoat.registerToast = function(Alpine) {
const template = document.getElementById('toast-template');
if (!toaster) {
console.error(`Toaster container with id #${targetId} not found.`);
console.error(`Toaster container with id #${toasterId} not found.`);
return;
}
if (!template) {
+108 -83
View File
@@ -3,17 +3,7 @@
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.
#}
@@ -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 %}
{{ 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>'
} %}