mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-12 13:18:22 +00:00
More components and refactoring select
This commit is contained in:
+15
-16
@@ -1,20 +1,19 @@
|
||||
{% macro dialog(
|
||||
id,
|
||||
trigger=None,
|
||||
title=None,
|
||||
description=None,
|
||||
footer=None,
|
||||
main_attrs={},
|
||||
trigger_attrs={},
|
||||
content_attrs={},
|
||||
content_header_attrs={},
|
||||
content_body_attrs={},
|
||||
content_footer_attrs={},
|
||||
open=false,
|
||||
close_button=true,
|
||||
close_on_overlay_click=true
|
||||
)
|
||||
%}
|
||||
id,
|
||||
trigger=None,
|
||||
title=None,
|
||||
description=None,
|
||||
footer=None,
|
||||
main_attrs={},
|
||||
trigger_attrs={},
|
||||
content_attrs={},
|
||||
content_header_attrs={},
|
||||
content_body_attrs={},
|
||||
content_footer_attrs={},
|
||||
open=false,
|
||||
close_button=true,
|
||||
close_on_overlay_click=true
|
||||
) %}
|
||||
{% if registerComponent('dialog') %}
|
||||
<script>
|
||||
document.addEventListener('alpine:init', () => {
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
{% macro popover(
|
||||
id=None,
|
||||
trigger=None,
|
||||
main_attrs=None,
|
||||
trigger_attrs=None,
|
||||
content_attrs=None
|
||||
) %}
|
||||
{% if registerComponent('popover') %}
|
||||
<script>
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.data('popover', () => ({
|
||||
open: false,
|
||||
focusedIndex: null,
|
||||
menuItems: [],
|
||||
|
||||
init() {
|
||||
this.$nextTick(() => {
|
||||
this.menuItems = Array.from(this.$el.querySelectorAll('[role=menuitem]'));
|
||||
});
|
||||
},
|
||||
|
||||
handleTriggerClick() {
|
||||
this.open = !this.open;
|
||||
},
|
||||
|
||||
handleEscape() {
|
||||
if (this.open) this.open = false;
|
||||
},
|
||||
|
||||
focusNavItem() {
|
||||
if (this.menuItems.length === 0) return;
|
||||
if (this.focusedIndex >= this.menuItems.length) {
|
||||
this.focusedIndex = this.menuItems.length - 1;
|
||||
}
|
||||
if (this.focusedIndex < 0) {
|
||||
this.focusedIndex = 0;
|
||||
}
|
||||
this.menuItems[this.focusedIndex].focus();
|
||||
},
|
||||
|
||||
nextNavItem() {
|
||||
if (this.menuItems.length === 0) return;
|
||||
if (this.focusedIndex === null) {
|
||||
this.focusedIndex = 0;
|
||||
} else {
|
||||
this.focusedIndex++;
|
||||
}
|
||||
this.focusNavItem();
|
||||
},
|
||||
|
||||
prevNavItem() {
|
||||
if (this.menuItems.length === 0) return;
|
||||
if (this.focusedIndex === null) {
|
||||
this.focusedIndex = 0;
|
||||
} else {
|
||||
this.focusedIndex--;
|
||||
}
|
||||
this.focusNavItem();
|
||||
},
|
||||
|
||||
handleNavItemClick(event) {
|
||||
if (this.menuItems.length === 0) return;
|
||||
const menuItem = event.target.closest('[role=menuitem]');
|
||||
if (menuItem && !menuItem.disabled) {
|
||||
menuItem.blur();
|
||||
this.open = false;
|
||||
}
|
||||
},
|
||||
|
||||
$trigger: {
|
||||
'@click'() { this.handleTriggerClick() },
|
||||
'@keydown.down.prevent'() { this.open && this.nextNavItem() },
|
||||
':aria-expanded'() { return this.open },
|
||||
},
|
||||
|
||||
$content: {
|
||||
'@click'(e) { this.handleNavItemClick(e) },
|
||||
'@keydown.escape.window'() { this.handleEscape() },
|
||||
'@keydown.down.prevent'() { this.nextNavItem() },
|
||||
'@keydown.up.prevent'() { this.prevNavItem() },
|
||||
'@keydown.home.prevent'() { this.focusNavItem(0) },
|
||||
'@keydown.end.prevent'() { this.focusNavItem(this.menuItems.length - 1) },
|
||||
':aria-hidden'() { return !this.open },
|
||||
'x-cloak': ''
|
||||
},
|
||||
}));
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
<div
|
||||
class="popover {% if main_attrs %}{{ main_attrs.pop('class', '') }}{% endif %}"
|
||||
x-data="popover"
|
||||
@click.away="open = false"
|
||||
{% if id %}id="{{ id }}"{% endif %}
|
||||
{% for key, value in main_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{% if trigger %}
|
||||
<button
|
||||
type="button"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded="false"
|
||||
x-bind="$trigger"
|
||||
{% if id %}
|
||||
id="{{ id }}-trigger"
|
||||
aria-controls="{{ id }}-menu"
|
||||
{% endif %}
|
||||
{% for key, value in trigger_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ trigger | safe }}
|
||||
</button>
|
||||
{% endif %}
|
||||
<div
|
||||
data-popover
|
||||
aria-hidden="true"
|
||||
x-bind="$content"
|
||||
{% if id %}id="{{ id }}-menu"{% endif %}
|
||||
{% for key, value in content_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ caller() }}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
+177
-97
@@ -1,144 +1,224 @@
|
||||
{% macro select(
|
||||
id=None,
|
||||
main_attrs=None,
|
||||
trigger_attrs=None,
|
||||
menu_attrs=None,
|
||||
value=None,
|
||||
input_name=None
|
||||
)
|
||||
%}
|
||||
id=None,
|
||||
name=None,
|
||||
selected=None,
|
||||
main_attrs=None,
|
||||
trigger_attrs=None,
|
||||
content_attrs=None,
|
||||
listbox_attrs=None,
|
||||
is_combobox=False
|
||||
) %}
|
||||
{% if registerComponent('select') %}
|
||||
<script>
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.data('select', (initialValue = null) => ({
|
||||
Alpine.data('select', (name = null, initialValue = null) => ({
|
||||
open: false,
|
||||
focusedIndex: -1,
|
||||
name: null,
|
||||
options: [],
|
||||
selectedLabel: initialValue,
|
||||
selectedValue: initialValue,
|
||||
|
||||
disabledOptions: [],
|
||||
focusedIndex: null,
|
||||
selectedLabel: null,
|
||||
selectedValue: null,
|
||||
query: '',
|
||||
|
||||
init() {
|
||||
this.$nextTick(() => {
|
||||
const listboxElement = this.$el.querySelector('[role="listbox"]');
|
||||
this.options = Array.from(listboxElement.querySelectorAll('[role=option]'));
|
||||
|
||||
this.selectOptionByValue(initialValue);
|
||||
if (name) this.name = name;
|
||||
this.options = Array.from(this.$el.querySelectorAll('[role=option]:not([aria-disabled])'));
|
||||
this.disabledOptions = Array.from(this.$el.querySelectorAll('[role=option][aria-disabled=true]'));
|
||||
if (this.options.length === 0) return;
|
||||
if (initialValue) {
|
||||
const option = this.options.find(opt => opt.getAttribute('data-value') === initialValue);
|
||||
this.selectOption(option);
|
||||
this.focusedIndex = this.options.indexOf(option);
|
||||
this.focusOption();
|
||||
} else {
|
||||
this.selectOption(this.options[0]);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
$trigger: {
|
||||
'@click'() { this.handleClick() },
|
||||
'@keydown.down.prevent'() { this.open && this.nextOption() },
|
||||
':aria-expanded'() { return this.open },
|
||||
':aria-pressed'() { return this.open }
|
||||
focusOption() {
|
||||
if (this.options.length === 0) return;
|
||||
|
||||
if (this.focusedIndex >= this.options.length) {
|
||||
this.focusedIndex = this.options.length - 1;
|
||||
} else if (this.focusedIndex < 0 || this.focusedIndex === null) {
|
||||
this.focusedIndex = 0;
|
||||
}
|
||||
this.options.forEach(opt => opt.removeAttribute('data-focus'));
|
||||
this.options[this.focusedIndex].setAttribute('data-focus', '');
|
||||
},
|
||||
|
||||
$listbox: {
|
||||
'@click'(e) { this.handleOptionClick(e) },
|
||||
'@keydown.escape.window'() { this.handleEscape() },
|
||||
'@keydown.down.prevent'() { this.nextOption() },
|
||||
'@keydown.up.prevent'() { this.prevOption() },
|
||||
focusOnSelectedOption() {
|
||||
if (this.options.length === 0 || this.selectedValue === null) return;
|
||||
const option = this.options.find(opt => opt.getAttribute('data-value') === this.selectedValue);
|
||||
if (!option) return;
|
||||
this.focusedIndex = this.options.indexOf(option);
|
||||
this.focusOption();
|
||||
},
|
||||
moveOptionFocus(delta) {
|
||||
if (this.options.length === 0) return;
|
||||
|
||||
if (!this.open) {
|
||||
this.open = true;
|
||||
this.focusOnSelectedOption();
|
||||
} else {
|
||||
this.focusedIndex = this.focusedIndex === null
|
||||
? 0
|
||||
: this.focusedIndex + delta;
|
||||
}
|
||||
this.focusOption();
|
||||
},
|
||||
handleOptionClick(event) {
|
||||
const option = event.target.closest('[role=option]');
|
||||
if (option && option.getAttribute('aria-disabled') !== 'true') {
|
||||
this.selectOption(option);
|
||||
this.open = false;
|
||||
this.$nextTick(() => this.$refs.trigger.focus());
|
||||
}
|
||||
},
|
||||
handleOptionMousemove(event) {
|
||||
console.log("here");
|
||||
const option = event.target.closest('[role=option]');
|
||||
if (option && option.getAttribute('aria-disabled') !== 'true') {
|
||||
this.focusedIndex = this.options.indexOf(option);
|
||||
this.focusOption();
|
||||
}
|
||||
},
|
||||
handleOptionEnter(event) {
|
||||
this.selectOption(this.options[this.focusedIndex]);
|
||||
this.open = !this.open;
|
||||
},
|
||||
selectOption(option) {
|
||||
if (this.options.length === 0 || !option || option.disabled || !option.getAttribute('data-value')
|
||||
) return;
|
||||
|
||||
this.options.forEach(opt => {
|
||||
opt.setAttribute('aria-selected', opt === option);
|
||||
});
|
||||
this.selectedLabel = option.innerHTML;
|
||||
this.selectedValue = option.getAttribute('data-value');
|
||||
this.$dispatch('select:change', {
|
||||
value: this.selectedValue,
|
||||
label: this.selectedLabel
|
||||
});
|
||||
},
|
||||
filterOptions(query) {
|
||||
if (query.length > 0) {
|
||||
this.disabledOptions.forEach(opt => { opt.setAttribute('aria-hidden', 'true'); });
|
||||
} else {
|
||||
this.disabledOptions.forEach(opt => { opt.removeAttribute('aria-hidden'); });
|
||||
}
|
||||
this.options.forEach(opt => {
|
||||
opt.removeAttribute('aria-hidden');
|
||||
if (opt.getAttribute('data-value') != null && !opt.innerHTML.toLowerCase().includes(query.toLowerCase())) {
|
||||
opt.setAttribute('aria-hidden', 'true');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
$trigger: {
|
||||
'@click'() { this.open = !this.open; this.focusOnSelectedOption(); },
|
||||
'@keydown.escape.window'() {
|
||||
this.open = false;
|
||||
this.$refs.trigger.focus();
|
||||
},
|
||||
'@keydown.down.prevent'() { this.moveOptionFocus(+1); },
|
||||
'@keydown.up.prevent'() { this.moveOptionFocus(-1); },
|
||||
'@keydown.home.prevent'() { this.focusOption(0) },
|
||||
'@keydown.end.prevent'() { this.focusOption(this.options.length - 1) },
|
||||
'@keydown.enter.prevent'() { this.handleOptionEnter() },
|
||||
':aria-expanded'() { return this.open },
|
||||
'x-ref': 'trigger'
|
||||
},
|
||||
|
||||
$content: {
|
||||
'@click'(e) { this.handleOptionClick(e) },
|
||||
'@mousemove'(e) { this.handleOptionMousemove(e) },
|
||||
':aria-hidden'() { return !this.open },
|
||||
'x-cloak': ''
|
||||
},
|
||||
|
||||
selectOption(option) {
|
||||
if (option && !option.disabled) {
|
||||
this.options.forEach(opt => {
|
||||
opt.setAttribute('aria-selected', opt === option);
|
||||
});
|
||||
this.selectedLabel = option.innerHTML;
|
||||
this.selectedValue = option.getAttribute('data-value');
|
||||
option.blur();
|
||||
this.open = false;
|
||||
} else if (this.options.length > 0) {
|
||||
this.selectOption(this.options[0]);
|
||||
}
|
||||
},
|
||||
|
||||
selectOptionByValue(value) {
|
||||
const option = this.options.find(opt => opt.getAttribute('data-value') === value);
|
||||
this.selectOption(option);
|
||||
},
|
||||
|
||||
focusOption(index) {
|
||||
if (index >= 0 && index < this.options.length) {
|
||||
this.focusedIndex = index;
|
||||
this.options[index].focus();
|
||||
}
|
||||
},
|
||||
|
||||
nextOption() {
|
||||
this.focusOption((this.focusedIndex + 1) % this.options.length);
|
||||
},
|
||||
|
||||
prevOption() {
|
||||
this.focusOption(this.focusedIndex <= 0 ? this.options.length - 1 : this.focusedIndex - 1);
|
||||
},
|
||||
|
||||
handleClick() {
|
||||
this.open = !this.open;
|
||||
this.$nextTick(() => {
|
||||
if(this.open && this.options.length) this.focusOption(0);
|
||||
});
|
||||
},
|
||||
|
||||
handleEscape() {
|
||||
if (this.open) this.open = false;
|
||||
},
|
||||
|
||||
handleOptionClick(event) {
|
||||
const option = event.target.closest('[role=option]');
|
||||
this.selectOption(option);
|
||||
},
|
||||
$filter: {
|
||||
'@input'(e) { this.filterOptions(e.target.value) },
|
||||
'@keydown.down.prevent'() { this.moveOptionFocus(+1); },
|
||||
'@keydown.up.prevent'() { this.moveOptionFocus(-1); },
|
||||
'@keydown.home.prevent'() { this.focusOption(0) },
|
||||
'@keydown.end.prevent'() { this.focusOption(this.options.length - 1) },
|
||||
'@keydown.enter.prevent'() { this.handleOptionEnter() },
|
||||
}
|
||||
}));
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
<div
|
||||
class="dropdown {% if main_attrs %}{{ main_attrs.pop('class', '') }}{% endif %}"
|
||||
x-data="select('{{ value if value else '' }}')"
|
||||
class="popover {{ main_attrs.class }}"
|
||||
x-data="select('{{ name }}', '{{ selected }}')"
|
||||
@click.away="open = false"
|
||||
{% if id %}id="{{ id }}"{% endif %}
|
||||
{% for key, value in main_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% for key, value in trigger_attrs %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
aria-haspopup="menu"
|
||||
aria-haspopup="listbox"
|
||||
aria-expanded="false"
|
||||
x-bind="$trigger"
|
||||
class="btn-outline justify-between font-normal"
|
||||
{% if id %}
|
||||
id="{{ id }}-trigger"
|
||||
aria-controls="{{ id }}-menu"
|
||||
aria-controls="{{ id }}-content"
|
||||
{% endif %}
|
||||
class="btn-outline justify-between font-normal {{ trigger_attrs.class }}"
|
||||
{% for key, value in trigger_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
<span x-html="selectedLabel" class="flex items-center gap-x-2"></span>
|
||||
<span class="text-muted-foreground opacity-50">{% lucide "chevron-down" %}</span>
|
||||
<div x-html="selectedLabel" class="flex items-center gap-x-2"
|
||||
></div>
|
||||
{% lucide "chevron-down", class="text-muted-foreground opacity-50 shrink-0" %}
|
||||
</button>
|
||||
<div
|
||||
class="min-w-full w-fit"
|
||||
role="listbox"
|
||||
aria-orientation="vertical"
|
||||
data-popover
|
||||
aria-hidden="true"
|
||||
x-bind="$listbox"
|
||||
{% if id %}id="{{ id }}-menu"{% endif %}
|
||||
{% for key, value in menu_attrs %}
|
||||
x-bind="$content"
|
||||
{% if id %}id="{{ id }}-content"{% endif %}
|
||||
{% for key, value in content_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ caller() }}
|
||||
{% if is_combobox %}
|
||||
<header>
|
||||
{% lucide "search" %}
|
||||
<input
|
||||
type="text"
|
||||
value=""
|
||||
placeholder="Search framework..."
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
aria-autocomplete="list"
|
||||
role="combobox"
|
||||
aria-expanded="true"
|
||||
aria-controls="{{ id }}-content"
|
||||
aria-labelledby="{{ id }}-trigger"
|
||||
x-model="query"
|
||||
x-bind="$filter"
|
||||
>
|
||||
</header>
|
||||
{% endif %}
|
||||
<div
|
||||
role="listbox"
|
||||
aria-orientation="vertical"
|
||||
{% for key, value in listbox_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ caller() }}
|
||||
</div>
|
||||
</div>
|
||||
{% if input_name %}
|
||||
<input type="hidden" name="{{ input_name }}" x-model="selectedValue">
|
||||
{% if name %}
|
||||
<input type="hidden" name="{{ name }}" x-model="selectedValue">
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
@@ -1,15 +1,14 @@
|
||||
{% macro dropdown(
|
||||
id=None,
|
||||
trigger=None,
|
||||
{% macro tabs(
|
||||
tabs,
|
||||
main_attrs=None,
|
||||
trigger_attrs=None,
|
||||
menu_attrs=None
|
||||
content_attrs=None
|
||||
)
|
||||
%}
|
||||
{% if registerComponent('dropdown') %}
|
||||
{% if registerComponent('toast') %}
|
||||
<script>
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.data('dropdown', () => ({
|
||||
Alpine.data('toast', () => ({
|
||||
open: false,
|
||||
focusedIndex: -1,
|
||||
menuItems: [],
|
||||
@@ -78,8 +77,8 @@
|
||||
{% endif %}
|
||||
|
||||
<div
|
||||
class="dropdown {% if main_attrs %}{{ main_attrs.pop('class', '') }}{% endif %}"
|
||||
x-data="dropdown"
|
||||
class="toast {% if main_attrs %}{{ main_attrs.pop('class', '') }}{% endif %}"
|
||||
x-data="toast"
|
||||
@click.away="open = false"
|
||||
{% if id %}id="{{ id }}"{% endif %}
|
||||
{% for key, value in main_attrs %}
|
||||
+76
-42
@@ -1,15 +1,12 @@
|
||||
{% macro dropdown(
|
||||
id=None,
|
||||
trigger=None,
|
||||
main_attrs=None,
|
||||
trigger_attrs=None,
|
||||
menu_attrs=None
|
||||
)
|
||||
%}
|
||||
{% if registerComponent('dropdown') %}
|
||||
{% macro toast(
|
||||
id='toasts',
|
||||
messages=None,
|
||||
main_attrs=None
|
||||
) %}
|
||||
{% if registerComponent('toast') %}
|
||||
<script>
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.data('dropdown', () => ({
|
||||
Alpine.data('toast', () => ({
|
||||
open: false,
|
||||
focusedIndex: -1,
|
||||
menuItems: [],
|
||||
@@ -78,44 +75,81 @@
|
||||
{% endif %}
|
||||
|
||||
<div
|
||||
class="dropdown {% if main_attrs %}{{ main_attrs.pop('class', '') }}{% endif %}"
|
||||
x-data="dropdown"
|
||||
@click.away="open = false"
|
||||
{% if id %}id="{{ id }}"{% endif %}
|
||||
id="{{ id }}"
|
||||
class="toasts"
|
||||
x-data="toast"
|
||||
{% for key, value in main_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{% if trigger %}
|
||||
<button
|
||||
type="button"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded="false"
|
||||
aria-pressed="false"
|
||||
x-bind="$trigger"
|
||||
{% if id %}
|
||||
id="{{ id }}-trigger"
|
||||
aria-controls="{{ id }}-menu"
|
||||
{% endif %}
|
||||
{% for key, value in trigger_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ trigger | safe }}
|
||||
</button>
|
||||
{% endif %}
|
||||
<nav
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
{% 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"
|
||||
x-bind="$menu"
|
||||
{% if id %}id="{{ id }}-menu"{% endif %}
|
||||
{% for key, value in menu_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
:aria-hidden="!active"
|
||||
:inert="!active"
|
||||
>
|
||||
{{ caller() }}
|
||||
</nav>
|
||||
<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 %}
|
||||
Reference in New Issue
Block a user