mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-12 21:28:18 +00:00
155 lines
3.9 KiB
Plaintext
155 lines
3.9 KiB
Plaintext
{% macro toast(
|
|
id='toasts',
|
|
messages=None,
|
|
main_attrs=None
|
|
) %}
|
|
{% if registerComponent('toast') %}
|
|
<script>
|
|
document.addEventListener('alpine:init', () => {
|
|
Alpine.data('toast', () => ({
|
|
open: false,
|
|
focusedIndex: -1,
|
|
menuItems: [],
|
|
|
|
init() {
|
|
this.$nextTick(() => {
|
|
const menuElement = this.$el.querySelector('[role="menu"]');
|
|
this.menuItems = Array.from(menuElement.querySelectorAll('[role=menuitem]'));
|
|
});
|
|
},
|
|
|
|
$trigger: {
|
|
'@click'() { this.handleClick() },
|
|
'@keydown.down.prevent'() { this.open && this.nextItem() },
|
|
':aria-expanded'() { return this.open },
|
|
':aria-pressed'() { return this.open }
|
|
},
|
|
|
|
$menu: {
|
|
'@click'(e) { this.handleMenuItemClick(e) },
|
|
'@keydown.escape.window'() { this.handleEscape() },
|
|
'@keydown.down.prevent'() { this.nextItem() },
|
|
'@keydown.up.prevent'() { this.prevItem() },
|
|
'@keydown.home.prevent'() { this.focusItem(0) },
|
|
'@keydown.end.prevent'() { this.focusItem(this.menuItems.length - 1) },
|
|
':aria-hidden'() { return !this.open },
|
|
'x-cloak': ''
|
|
},
|
|
|
|
focusItem(index) {
|
|
if (index >= 0 && index < this.menuItems.length) {
|
|
this.focusedIndex = index;
|
|
this.menuItems[index].focus();
|
|
}
|
|
},
|
|
|
|
nextItem() {
|
|
this.focusItem((this.focusedIndex + 1) % this.menuItems.length);
|
|
},
|
|
|
|
prevItem() {
|
|
this.focusItem(this.focusedIndex <= 0 ? this.menuItems.length - 1 : this.focusedIndex - 1);
|
|
},
|
|
|
|
handleClick() {
|
|
this.open = !this.open;
|
|
this.$nextTick(() => {
|
|
if(this.open && this.menuItems.length) this.focusItem(0);
|
|
});
|
|
},
|
|
|
|
handleEscape() {
|
|
if (this.open) this.open = false;
|
|
},
|
|
|
|
handleMenuItemClick(event) {
|
|
const menuItem = event.target.closest('[role=menuitem]');
|
|
if (menuItem && !menuItem.disabled) {
|
|
menuItem.blur();
|
|
this.open = false;
|
|
}
|
|
},
|
|
}));
|
|
});
|
|
</script>
|
|
{% endif %}
|
|
|
|
<div
|
|
id="{{ id }}"
|
|
class="toasts"
|
|
x-data="toast"
|
|
{% for key, value in main_attrs %}
|
|
{{ key }}="{{ value }}"
|
|
{% endfor %}
|
|
>
|
|
{% for category, message in toasts %}
|
|
<div
|
|
x-data="{
|
|
active: true,
|
|
init() {
|
|
if ('{{ category }}' !== 'error') setTimeout(() => this.active = false, 3000)
|
|
}
|
|
}"
|
|
role="{{ 'alert' if category == 'error' else 'status' }}"
|
|
aria-atomic="true"
|
|
aria-hidden="true"
|
|
:aria-hidden="!active"
|
|
:inert="!active"
|
|
>
|
|
<button
|
|
x-on:click="active = !active"
|
|
aria-label="{{ _('Dismiss') }}"
|
|
>
|
|
{% include "icons/x.svg" %}
|
|
</button>
|
|
<section class="w-full text-left flex-1">
|
|
{% if message is string %}
|
|
<p class="font-medium w-full text-left flex-1">{{ message }}</p>
|
|
{% else %}
|
|
<h4 class="font-medium mb-1">{{ message.title }}</h4>
|
|
<p class="text-muted-foreground">{{ message.description }}</p>
|
|
{% if message.action %}
|
|
<a
|
|
href="{{ message.action.url }}"
|
|
class="btn-outline h-8 px-2.5 mt-4"
|
|
>{{ message.action.label }}</a>
|
|
{% endif %}
|
|
{% endif %}
|
|
</section>
|
|
{% if category in ('error', 'success') %}
|
|
{% if category == 'error' %}
|
|
<div class="text-danger py-0.5">
|
|
{% include "icons/circle-x.svg" %}
|
|
</div>
|
|
{% elif category == 'success' %}
|
|
<div class="text-success py-0.5">
|
|
{% include "icons/circle-check.svg" %}
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endmacro %}
|
|
|
|
|
|
|
|
{% macro toasts(
|
|
id=None,
|
|
messages=None,
|
|
flash=True
|
|
) %}
|
|
{% if flash %}
|
|
{% set toasts = (messages or []) + get_flashed_messages(with_categories=true) %}
|
|
{% else %}
|
|
{% set toasts = messages or [] %}
|
|
{% endif %}
|
|
<div
|
|
class="toasts"
|
|
id="{{ id or 'toasts' }}"
|
|
{}
|
|
|
|
>
|
|
|
|
</div>
|
|
{% endmacro %} |