mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-12 13:18:22 +00:00
prepare basecoat 1.0 migration
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@import "./basecoat-base.css";
|
||||
@@ -0,0 +1,2 @@
|
||||
@import "./base/base.css";
|
||||
@import "./basecoat-components.css";
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Components */
|
||||
@import "./components/alert.css";
|
||||
@import "./components/badge.css";
|
||||
@import "./components/button.css";
|
||||
@import "./components/button-group.css";
|
||||
@import "./components/card.css";
|
||||
@import "./components/collapsible.css";
|
||||
@import "./components/command.css";
|
||||
@import "./components/combobox.css";
|
||||
@import "./components/dialog.css";
|
||||
@import "./components/dropdown-menu.css";
|
||||
@import "./components/empty.css";
|
||||
@import "./components/field.css";
|
||||
@import "./components/checkbox.css";
|
||||
@import "./components/input.css";
|
||||
@import "./components/input-group.css";
|
||||
@import "./components/item.css";
|
||||
@import "./components/kbd.css";
|
||||
@import "./components/label.css";
|
||||
@import "./components/native-select.css";
|
||||
@import "./components/popover.css";
|
||||
@import "./components/radio.css";
|
||||
@import "./components/range.css";
|
||||
@import "./components/select.css";
|
||||
@import "./components/sidebar.css";
|
||||
@import "./components/skeleton.css";
|
||||
@import "./components/switch.css";
|
||||
@import "./components/table.css";
|
||||
@import "./components/tabs.css";
|
||||
@import "./components/textarea.css";
|
||||
@import "./components/toast.css";
|
||||
@import "./components/tooltip.css";
|
||||
@@ -1,34 +1,2 @@
|
||||
@import "./base/base.css";
|
||||
|
||||
/* Components */
|
||||
@import "./components/alert.css";
|
||||
@import "./components/badge.css";
|
||||
@import "./components/button.css";
|
||||
@import "./components/button-group.css";
|
||||
@import "./components/card.css";
|
||||
@import "./components/collapsible.css";
|
||||
@import "./components/command.css";
|
||||
@import "./components/combobox.css";
|
||||
@import "./components/dialog.css";
|
||||
@import "./components/dropdown-menu.css";
|
||||
@import "./components/empty.css";
|
||||
@import "./components/field.css";
|
||||
@import "./components/checkbox.css";
|
||||
@import "./components/input.css";
|
||||
@import "./components/input-group.css";
|
||||
@import "./components/item.css";
|
||||
@import "./components/kbd.css";
|
||||
@import "./components/label.css";
|
||||
@import "./components/native-select.css";
|
||||
@import "./components/popover.css";
|
||||
@import "./components/radio.css";
|
||||
@import "./components/range.css";
|
||||
@import "./components/select.css";
|
||||
@import "./components/sidebar.css";
|
||||
@import "./components/switch.css";
|
||||
@import "./components/table.css";
|
||||
@import "./components/tabs.css";
|
||||
@import "./components/textarea.css";
|
||||
@import "./components/toast.css";
|
||||
@import "./components/tooltip.css";
|
||||
@import "./basecoat-base.css";
|
||||
@import "./styles/style-luma.css";
|
||||
|
||||
@@ -1,34 +1,2 @@
|
||||
@import "./base/base.css";
|
||||
|
||||
/* Components */
|
||||
@import "./components/alert.css";
|
||||
@import "./components/badge.css";
|
||||
@import "./components/button.css";
|
||||
@import "./components/button-group.css";
|
||||
@import "./components/card.css";
|
||||
@import "./components/collapsible.css";
|
||||
@import "./components/command.css";
|
||||
@import "./components/combobox.css";
|
||||
@import "./components/dialog.css";
|
||||
@import "./components/dropdown-menu.css";
|
||||
@import "./components/empty.css";
|
||||
@import "./components/field.css";
|
||||
@import "./components/checkbox.css";
|
||||
@import "./components/input.css";
|
||||
@import "./components/input-group.css";
|
||||
@import "./components/item.css";
|
||||
@import "./components/kbd.css";
|
||||
@import "./components/label.css";
|
||||
@import "./components/native-select.css";
|
||||
@import "./components/popover.css";
|
||||
@import "./components/radio.css";
|
||||
@import "./components/range.css";
|
||||
@import "./components/select.css";
|
||||
@import "./components/sidebar.css";
|
||||
@import "./components/switch.css";
|
||||
@import "./components/table.css";
|
||||
@import "./components/tabs.css";
|
||||
@import "./components/textarea.css";
|
||||
@import "./components/toast.css";
|
||||
@import "./components/tooltip.css";
|
||||
@import "./basecoat-base.css";
|
||||
@import "./styles/style-lyra.css";
|
||||
|
||||
@@ -1,34 +1,2 @@
|
||||
@import "./base/base.css";
|
||||
|
||||
/* Components */
|
||||
@import "./components/alert.css";
|
||||
@import "./components/badge.css";
|
||||
@import "./components/button.css";
|
||||
@import "./components/button-group.css";
|
||||
@import "./components/card.css";
|
||||
@import "./components/collapsible.css";
|
||||
@import "./components/command.css";
|
||||
@import "./components/combobox.css";
|
||||
@import "./components/dialog.css";
|
||||
@import "./components/dropdown-menu.css";
|
||||
@import "./components/empty.css";
|
||||
@import "./components/field.css";
|
||||
@import "./components/checkbox.css";
|
||||
@import "./components/input.css";
|
||||
@import "./components/input-group.css";
|
||||
@import "./components/item.css";
|
||||
@import "./components/kbd.css";
|
||||
@import "./components/label.css";
|
||||
@import "./components/native-select.css";
|
||||
@import "./components/popover.css";
|
||||
@import "./components/radio.css";
|
||||
@import "./components/range.css";
|
||||
@import "./components/select.css";
|
||||
@import "./components/sidebar.css";
|
||||
@import "./components/switch.css";
|
||||
@import "./components/table.css";
|
||||
@import "./components/tabs.css";
|
||||
@import "./components/textarea.css";
|
||||
@import "./components/toast.css";
|
||||
@import "./components/tooltip.css";
|
||||
@import "./basecoat-base.css";
|
||||
@import "./styles/style-maia.css";
|
||||
|
||||
@@ -1,34 +1,2 @@
|
||||
@import "./base/base.css";
|
||||
|
||||
/* Components */
|
||||
@import "./components/alert.css";
|
||||
@import "./components/badge.css";
|
||||
@import "./components/button.css";
|
||||
@import "./components/button-group.css";
|
||||
@import "./components/card.css";
|
||||
@import "./components/collapsible.css";
|
||||
@import "./components/command.css";
|
||||
@import "./components/combobox.css";
|
||||
@import "./components/dialog.css";
|
||||
@import "./components/dropdown-menu.css";
|
||||
@import "./components/empty.css";
|
||||
@import "./components/field.css";
|
||||
@import "./components/checkbox.css";
|
||||
@import "./components/input.css";
|
||||
@import "./components/input-group.css";
|
||||
@import "./components/item.css";
|
||||
@import "./components/kbd.css";
|
||||
@import "./components/label.css";
|
||||
@import "./components/native-select.css";
|
||||
@import "./components/popover.css";
|
||||
@import "./components/radio.css";
|
||||
@import "./components/range.css";
|
||||
@import "./components/select.css";
|
||||
@import "./components/sidebar.css";
|
||||
@import "./components/switch.css";
|
||||
@import "./components/table.css";
|
||||
@import "./components/tabs.css";
|
||||
@import "./components/textarea.css";
|
||||
@import "./components/toast.css";
|
||||
@import "./components/tooltip.css";
|
||||
@import "./basecoat-base.css";
|
||||
@import "./styles/style-mira.css";
|
||||
|
||||
@@ -1,34 +1,2 @@
|
||||
@import "./base/base.css";
|
||||
|
||||
/* Components */
|
||||
@import "./components/alert.css";
|
||||
@import "./components/badge.css";
|
||||
@import "./components/button.css";
|
||||
@import "./components/button-group.css";
|
||||
@import "./components/card.css";
|
||||
@import "./components/collapsible.css";
|
||||
@import "./components/command.css";
|
||||
@import "./components/combobox.css";
|
||||
@import "./components/dialog.css";
|
||||
@import "./components/dropdown-menu.css";
|
||||
@import "./components/empty.css";
|
||||
@import "./components/field.css";
|
||||
@import "./components/checkbox.css";
|
||||
@import "./components/input.css";
|
||||
@import "./components/input-group.css";
|
||||
@import "./components/item.css";
|
||||
@import "./components/kbd.css";
|
||||
@import "./components/label.css";
|
||||
@import "./components/native-select.css";
|
||||
@import "./components/popover.css";
|
||||
@import "./components/radio.css";
|
||||
@import "./components/range.css";
|
||||
@import "./components/select.css";
|
||||
@import "./components/sidebar.css";
|
||||
@import "./components/switch.css";
|
||||
@import "./components/table.css";
|
||||
@import "./components/tabs.css";
|
||||
@import "./components/textarea.css";
|
||||
@import "./components/toast.css";
|
||||
@import "./components/tooltip.css";
|
||||
@import "./basecoat-base.css";
|
||||
@import "./styles/style-nova.css";
|
||||
|
||||
@@ -1,34 +1,2 @@
|
||||
@import "./base/base.css";
|
||||
|
||||
/* Components */
|
||||
@import "./components/alert.css";
|
||||
@import "./components/badge.css";
|
||||
@import "./components/button.css";
|
||||
@import "./components/button-group.css";
|
||||
@import "./components/card.css";
|
||||
@import "./components/collapsible.css";
|
||||
@import "./components/command.css";
|
||||
@import "./components/combobox.css";
|
||||
@import "./components/dialog.css";
|
||||
@import "./components/dropdown-menu.css";
|
||||
@import "./components/empty.css";
|
||||
@import "./components/field.css";
|
||||
@import "./components/checkbox.css";
|
||||
@import "./components/input.css";
|
||||
@import "./components/input-group.css";
|
||||
@import "./components/item.css";
|
||||
@import "./components/kbd.css";
|
||||
@import "./components/label.css";
|
||||
@import "./components/native-select.css";
|
||||
@import "./components/popover.css";
|
||||
@import "./components/radio.css";
|
||||
@import "./components/range.css";
|
||||
@import "./components/select.css";
|
||||
@import "./components/sidebar.css";
|
||||
@import "./components/switch.css";
|
||||
@import "./components/table.css";
|
||||
@import "./components/tabs.css";
|
||||
@import "./components/textarea.css";
|
||||
@import "./components/toast.css";
|
||||
@import "./components/tooltip.css";
|
||||
@import "./basecoat-base.css";
|
||||
@import "./styles/style-rhea.css";
|
||||
|
||||
@@ -1,34 +1,2 @@
|
||||
@import "./base/base.css";
|
||||
|
||||
/* Components */
|
||||
@import "./components/alert.css";
|
||||
@import "./components/badge.css";
|
||||
@import "./components/button.css";
|
||||
@import "./components/button-group.css";
|
||||
@import "./components/card.css";
|
||||
@import "./components/collapsible.css";
|
||||
@import "./components/command.css";
|
||||
@import "./components/combobox.css";
|
||||
@import "./components/dialog.css";
|
||||
@import "./components/dropdown-menu.css";
|
||||
@import "./components/empty.css";
|
||||
@import "./components/field.css";
|
||||
@import "./components/checkbox.css";
|
||||
@import "./components/input.css";
|
||||
@import "./components/input-group.css";
|
||||
@import "./components/item.css";
|
||||
@import "./components/kbd.css";
|
||||
@import "./components/label.css";
|
||||
@import "./components/native-select.css";
|
||||
@import "./components/popover.css";
|
||||
@import "./components/radio.css";
|
||||
@import "./components/range.css";
|
||||
@import "./components/select.css";
|
||||
@import "./components/sidebar.css";
|
||||
@import "./components/switch.css";
|
||||
@import "./components/table.css";
|
||||
@import "./components/tabs.css";
|
||||
@import "./components/textarea.css";
|
||||
@import "./components/toast.css";
|
||||
@import "./components/tooltip.css";
|
||||
@import "./basecoat-base.css";
|
||||
@import "./styles/style-sera.css";
|
||||
|
||||
@@ -1,34 +1,2 @@
|
||||
@import "./base/base.css";
|
||||
|
||||
/* Components */
|
||||
@import "./components/alert.css";
|
||||
@import "./components/badge.css";
|
||||
@import "./components/button.css";
|
||||
@import "./components/button-group.css";
|
||||
@import "./components/card.css";
|
||||
@import "./components/collapsible.css";
|
||||
@import "./components/command.css";
|
||||
@import "./components/combobox.css";
|
||||
@import "./components/dialog.css";
|
||||
@import "./components/dropdown-menu.css";
|
||||
@import "./components/empty.css";
|
||||
@import "./components/field.css";
|
||||
@import "./components/checkbox.css";
|
||||
@import "./components/input.css";
|
||||
@import "./components/input-group.css";
|
||||
@import "./components/item.css";
|
||||
@import "./components/kbd.css";
|
||||
@import "./components/label.css";
|
||||
@import "./components/native-select.css";
|
||||
@import "./components/popover.css";
|
||||
@import "./components/radio.css";
|
||||
@import "./components/range.css";
|
||||
@import "./components/select.css";
|
||||
@import "./components/sidebar.css";
|
||||
@import "./components/switch.css";
|
||||
@import "./components/table.css";
|
||||
@import "./components/tabs.css";
|
||||
@import "./components/textarea.css";
|
||||
@import "./components/toast.css";
|
||||
@import "./components/tooltip.css";
|
||||
@import "./basecoat-base.css";
|
||||
@import "./styles/style-vega.css";
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@import "./basecoat.css";
|
||||
@import "./basecoat.css";
|
||||
|
||||
+1
-34
@@ -1,34 +1 @@
|
||||
@import "./base/base.css";
|
||||
|
||||
/* Components */
|
||||
@import "./components/alert.css";
|
||||
@import "./components/badge.css";
|
||||
@import "./components/button.css";
|
||||
@import "./components/button-group.css";
|
||||
@import "./components/card.css";
|
||||
@import "./components/collapsible.css";
|
||||
@import "./components/command.css";
|
||||
@import "./components/combobox.css";
|
||||
@import "./components/dialog.css";
|
||||
@import "./components/dropdown-menu.css";
|
||||
@import "./components/empty.css";
|
||||
@import "./components/field.css";
|
||||
@import "./components/checkbox.css";
|
||||
@import "./components/input.css";
|
||||
@import "./components/input-group.css";
|
||||
@import "./components/item.css";
|
||||
@import "./components/kbd.css";
|
||||
@import "./components/label.css";
|
||||
@import "./components/native-select.css";
|
||||
@import "./components/popover.css";
|
||||
@import "./components/radio.css";
|
||||
@import "./components/range.css";
|
||||
@import "./components/select.css";
|
||||
@import "./components/sidebar.css";
|
||||
@import "./components/switch.css";
|
||||
@import "./components/table.css";
|
||||
@import "./components/tabs.css";
|
||||
@import "./components/textarea.css";
|
||||
@import "./components/toast.css";
|
||||
@import "./components/tooltip.css";
|
||||
@import "./styles/style-vega.css";
|
||||
@import "./basecoat-vega.css";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
/* Command */
|
||||
@layer components {
|
||||
.command-dialog {
|
||||
@apply inset-y-0 opacity-0 transition-all transition-discrete;
|
||||
@apply inset-y-0 bg-transparent p-0 text-inherit opacity-0 transition-all transition-discrete;
|
||||
max-width: none;
|
||||
max-height: none;
|
||||
|
||||
&:is([open], :popover-open) {
|
||||
@apply opacity-100;
|
||||
@@ -32,7 +34,7 @@
|
||||
}
|
||||
|
||||
> * {
|
||||
@apply fixed top-[50%] left-[50%] z-50 flex max-h-[min(640px,calc(100%_-_2rem))] w-full max-w-[calc(100%_-_2rem)] -translate-x-1/2 -translate-y-1/2 scale-95 flex-col overflow-hidden transition-all sm:max-w-lg;
|
||||
@apply fixed top-1/3 left-1/2 z-50 flex max-h-[min(640px,calc(100%_-_2rem))] w-full max-w-[calc(100%_-_2rem)] -translate-x-1/2 translate-y-0 scale-95 flex-col overflow-hidden transition-all sm:max-w-lg;
|
||||
}
|
||||
|
||||
> * > :is(button, form[method='dialog']) {
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
}
|
||||
|
||||
> * {
|
||||
@apply fixed top-1/2 left-1/2 z-50 flex max-h-[calc(100%_-_2rem)] w-full -translate-x-1/2 -translate-y-1/2 scale-95 flex-col overflow-hidden transition-all outline-hidden;
|
||||
@apply fixed top-1/2 left-1/2 z-50 flex max-h-[calc(100%_-_2rem)] w-full -translate-x-1/2 -translate-y-1/2 scale-95 flex-col transition-all outline-hidden;
|
||||
}
|
||||
|
||||
> * > header {
|
||||
@@ -30,7 +30,7 @@
|
||||
}
|
||||
|
||||
> * > section {
|
||||
@apply flex-1 overflow-y-auto;
|
||||
@apply flex-1;
|
||||
}
|
||||
|
||||
> * > footer {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@layer components {
|
||||
.field > input[type='radio'],
|
||||
.input[type='radio'] {
|
||||
@apply relative shrink-0 appearance-none outline-none disabled:cursor-not-allowed disabled:opacity-50;
|
||||
@apply relative shrink-0 appearance-none border outline-none disabled:cursor-not-allowed disabled:opacity-50;
|
||||
|
||||
&:checked:before {
|
||||
@apply absolute top-1/2 left-1/2 content-[''] -translate-x-1/2 -translate-y-1/2 rounded-full;
|
||||
|
||||
@@ -483,7 +483,7 @@
|
||||
}
|
||||
|
||||
.command [role='menu']:not(:has([role='menuitem']:not([aria-hidden='true'])))::before {
|
||||
@apply px-3 py-2 text-center text-sm;
|
||||
@apply py-6 text-center text-sm;
|
||||
}
|
||||
|
||||
.command [role='group'] {
|
||||
@@ -594,7 +594,7 @@
|
||||
}
|
||||
|
||||
*:not(select).select [role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@apply px-3 py-2 text-center text-sm;
|
||||
@apply py-6 text-center text-sm;
|
||||
}
|
||||
|
||||
*:not(select).select [role='heading'] {
|
||||
@@ -620,8 +620,9 @@
|
||||
|
||||
|
||||
/* Dialog */
|
||||
.dialog::backdrop {
|
||||
@apply bg-black/30 backdrop-blur-sm;
|
||||
.dialog::backdrop,
|
||||
.command-dialog::backdrop {
|
||||
@apply bg-black/30 backdrop-blur-sm duration-100;
|
||||
}
|
||||
|
||||
.dialog > * {
|
||||
|
||||
@@ -475,7 +475,7 @@
|
||||
}
|
||||
|
||||
.command [role='menu']:not(:has([role='menuitem']:not([aria-hidden='true'])))::before {
|
||||
@apply px-3 py-2 text-center text-xs;
|
||||
@apply py-6 text-center text-xs;
|
||||
}
|
||||
|
||||
.command [role='group'] {
|
||||
@@ -582,7 +582,7 @@
|
||||
}
|
||||
|
||||
*:not(select).select [role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@apply px-3 py-2 text-center text-xs;
|
||||
@apply py-6 text-center text-xs;
|
||||
}
|
||||
|
||||
*:not(select).select [role='heading'] {
|
||||
@@ -608,8 +608,9 @@
|
||||
|
||||
|
||||
/* Dialog */
|
||||
.dialog::backdrop {
|
||||
@apply bg-black/10 backdrop-blur-xs;
|
||||
.dialog::backdrop,
|
||||
.command-dialog::backdrop {
|
||||
@apply bg-black/10 backdrop-blur-xs duration-100;
|
||||
}
|
||||
|
||||
.dialog > * {
|
||||
|
||||
@@ -475,7 +475,7 @@
|
||||
}
|
||||
|
||||
.command [role='menu']:not(:has([role='menuitem']:not([aria-hidden='true'])))::before {
|
||||
@apply px-3 py-2 text-center text-sm;
|
||||
@apply py-6 text-center text-sm;
|
||||
}
|
||||
|
||||
.command [role='group'] {
|
||||
@@ -586,7 +586,7 @@
|
||||
}
|
||||
|
||||
*:not(select).select [role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@apply px-3 py-2 text-center text-sm;
|
||||
@apply py-6 text-center text-sm;
|
||||
}
|
||||
|
||||
*:not(select).select [role='heading'] {
|
||||
@@ -612,8 +612,9 @@
|
||||
|
||||
|
||||
/* Dialog */
|
||||
.dialog::backdrop {
|
||||
@apply bg-black/80 backdrop-blur-xs;
|
||||
.dialog::backdrop,
|
||||
.command-dialog::backdrop {
|
||||
@apply bg-black/80 backdrop-blur-xs duration-100;
|
||||
}
|
||||
|
||||
.dialog > * {
|
||||
|
||||
@@ -475,7 +475,7 @@
|
||||
}
|
||||
|
||||
.command [role='menu']:not(:has([role='menuitem']:not([aria-hidden='true'])))::before {
|
||||
@apply px-3 py-2 text-center text-xs/relaxed;
|
||||
@apply py-6 text-center text-xs/relaxed;
|
||||
}
|
||||
|
||||
.command [role='group'] {
|
||||
@@ -616,8 +616,9 @@
|
||||
|
||||
|
||||
/* Dialog */
|
||||
.dialog::backdrop {
|
||||
@apply bg-black/80 backdrop-blur-xs;
|
||||
.dialog::backdrop,
|
||||
.command-dialog::backdrop {
|
||||
@apply bg-black/80 backdrop-blur-xs duration-100;
|
||||
}
|
||||
|
||||
.dialog > * {
|
||||
|
||||
@@ -475,7 +475,7 @@
|
||||
}
|
||||
|
||||
.command [role='menu']:not(:has([role='menuitem']:not([aria-hidden='true'])))::before {
|
||||
@apply px-3 py-2 text-center text-sm;
|
||||
@apply py-6 text-center text-sm;
|
||||
}
|
||||
|
||||
.command [role='group'] {
|
||||
@@ -586,7 +586,7 @@
|
||||
}
|
||||
|
||||
*:not(select).select [role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@apply px-3 py-2 text-center text-sm;
|
||||
@apply py-6 text-center text-sm;
|
||||
}
|
||||
|
||||
*:not(select).select [role='heading'] {
|
||||
@@ -612,8 +612,9 @@
|
||||
|
||||
|
||||
/* Dialog */
|
||||
.dialog::backdrop {
|
||||
@apply bg-black/10 backdrop-blur-xs;
|
||||
.dialog::backdrop,
|
||||
.command-dialog::backdrop {
|
||||
@apply bg-black/10 backdrop-blur-xs duration-100;
|
||||
}
|
||||
|
||||
.dialog > * {
|
||||
|
||||
@@ -483,7 +483,7 @@
|
||||
}
|
||||
|
||||
.command [role='menu']:not(:has([role='menuitem']:not([aria-hidden='true'])))::before {
|
||||
@apply px-3 py-2 text-center text-sm;
|
||||
@apply py-6 text-center text-sm;
|
||||
}
|
||||
|
||||
.command [role='group'] {
|
||||
@@ -594,7 +594,7 @@
|
||||
}
|
||||
|
||||
*:not(select).select [role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@apply px-3 py-2 text-center text-sm;
|
||||
@apply py-6 text-center text-sm;
|
||||
}
|
||||
|
||||
*:not(select).select [role='heading'] {
|
||||
@@ -620,8 +620,9 @@
|
||||
|
||||
|
||||
/* Dialog */
|
||||
.dialog::backdrop {
|
||||
@apply bg-black/30 backdrop-blur-sm;
|
||||
.dialog::backdrop,
|
||||
.command-dialog::backdrop {
|
||||
@apply bg-black/30 backdrop-blur-sm duration-100;
|
||||
}
|
||||
|
||||
.dialog > * {
|
||||
|
||||
@@ -491,7 +491,7 @@
|
||||
}
|
||||
|
||||
.command [role='menu']:not(:has([role='menuitem']:not([aria-hidden='true'])))::before {
|
||||
@apply px-3 py-2 text-center text-sm;
|
||||
@apply py-6 text-center text-sm;
|
||||
}
|
||||
|
||||
.command [role='group'] {
|
||||
@@ -602,7 +602,7 @@
|
||||
}
|
||||
|
||||
*:not(select).select [role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@apply px-3 py-2 text-center text-sm;
|
||||
@apply py-6 text-center text-sm;
|
||||
}
|
||||
|
||||
*:not(select).select [role='heading'] {
|
||||
@@ -628,8 +628,9 @@
|
||||
|
||||
|
||||
/* Dialog */
|
||||
.dialog::backdrop {
|
||||
@apply bg-black/20 backdrop-blur-sm;
|
||||
.dialog::backdrop,
|
||||
.command-dialog::backdrop {
|
||||
@apply bg-black/20 backdrop-blur-sm duration-100;
|
||||
}
|
||||
|
||||
.dialog > * {
|
||||
|
||||
@@ -475,7 +475,7 @@
|
||||
}
|
||||
|
||||
.command [role='menu']:not(:has([role='menuitem']:not([aria-hidden='true'])))::before {
|
||||
@apply px-3 py-2 text-center text-sm;
|
||||
@apply py-6 text-center text-sm;
|
||||
}
|
||||
|
||||
.command [role='group'] {
|
||||
@@ -586,7 +586,7 @@
|
||||
}
|
||||
|
||||
*:not(select).select [role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@apply px-3 py-2 text-center text-sm;
|
||||
@apply py-6 text-center text-sm;
|
||||
}
|
||||
|
||||
*:not(select).select [role='heading'] {
|
||||
@@ -612,8 +612,9 @@
|
||||
|
||||
|
||||
/* Dialog */
|
||||
.dialog::backdrop {
|
||||
@apply bg-black/10 backdrop-blur-xs;
|
||||
.dialog::backdrop,
|
||||
.command-dialog::backdrop {
|
||||
@apply bg-black/10 backdrop-blur-xs duration-100;
|
||||
}
|
||||
|
||||
.dialog > * {
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
import { collectMenuSlugs, docUrl, fallbackLabelFromSlug } from './menu.js';
|
||||
|
||||
export const registerLlmExports = (eleventyConfig, options) => {
|
||||
const collectFiles = async (dir, predicate) => {
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
||||
const out = [];
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) out.push(...(await collectFiles(fullPath, predicate)));
|
||||
else if (entry.isFile() && predicate(fullPath)) out.push(fullPath);
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
const writeTextFile = async (outputDir, relativePath, content) => {
|
||||
const outPath = path.join(outputDir, relativePath);
|
||||
await fs.mkdir(path.dirname(outPath), { recursive: true });
|
||||
await fs.writeFile(outPath, content, 'utf8');
|
||||
};
|
||||
|
||||
const stripFrontMatter = (source) => {
|
||||
if (!source.startsWith('---')) return source;
|
||||
const match = source.match(/^---\s*\n[\s\S]*?\n---\s*\n?/);
|
||||
return match ? source.slice(match[0].length) : source;
|
||||
};
|
||||
|
||||
const readFrontMatterField = (source, key) => {
|
||||
if (!source.startsWith('---')) return null;
|
||||
const match = source.match(/^---\s*\n([\s\S]*?)\n---\s*\n?/);
|
||||
if (!match) return null;
|
||||
const line = match[1]
|
||||
.split('\n')
|
||||
.map((l) => l.trim())
|
||||
.find((l) => l.startsWith(`${key}:`));
|
||||
if (!line) return null;
|
||||
let value = line.slice(`${key}:`.length).trim();
|
||||
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
||||
value = value.slice(1, -1);
|
||||
}
|
||||
return value || null;
|
||||
};
|
||||
|
||||
eleventyConfig.on('afterBuild', async (eventsArg) => {
|
||||
const outputDir = eventsArg?.directories?.output || eventsArg?.dir?.output;
|
||||
const inputDir = eventsArg?.directories?.input || eventsArg?.dir?.input;
|
||||
if (!outputDir || !inputDir) return;
|
||||
|
||||
const docsRoot = path.resolve(inputDir);
|
||||
const siteUrl = options.getSiteUrl();
|
||||
const siteTitle = options.getSiteTitle();
|
||||
|
||||
let docsConfig = null;
|
||||
for (const docsConfigPath of [path.join(docsRoot, '_data', 'docs.json'), path.join(docsRoot, 'docs.json')]) {
|
||||
try {
|
||||
docsConfig = JSON.parse(await fs.readFile(docsConfigPath, 'utf8'));
|
||||
break;
|
||||
} catch (_) {}
|
||||
}
|
||||
const menu = docsConfig?.menu || [];
|
||||
const menuSlugs = collectMenuSlugs(menu);
|
||||
|
||||
const sourceFiles = await collectFiles(docsRoot, (filePath) => filePath.endsWith('.md'));
|
||||
const docs = (
|
||||
await Promise.all(
|
||||
sourceFiles.map(async (filePath) => {
|
||||
const source = await fs.readFile(filePath, 'utf8');
|
||||
const relative = path.relative(docsRoot, filePath).replaceAll(path.sep, '/');
|
||||
const slug = relative.replace(/\.md$/, '') === 'index' ? 'index' : relative.replace(/\.md$/, '');
|
||||
const url = docUrl(slug);
|
||||
const title = readFrontMatterField(source, 'title') || fallbackLabelFromSlug(slug);
|
||||
const description = (readFrontMatterField(source, 'description') || '').trim();
|
||||
const content = stripFrontMatter(source).trim();
|
||||
return { slug, url, title: title.trim(), description, content };
|
||||
}),
|
||||
)
|
||||
).sort((a, b) => a.url.localeCompare(b.url));
|
||||
|
||||
const docsBySlug = new Map(docs.map((d) => [d.slug, d]));
|
||||
const docsInMenu = menuSlugs.map((slug) => docsBySlug.get(slug)).filter(Boolean);
|
||||
const docsNotInMenu = docs.filter((doc) => !docsInMenu.includes(doc));
|
||||
const orderedDocs = [...docsInMenu, ...docsNotInMenu];
|
||||
|
||||
const markdownExportPathFromSlug = (slug) => {
|
||||
if (slug === 'index') return 'index.md';
|
||||
if (slug.endsWith('/index')) return `${slug.slice(0, -'/index'.length)}.md`;
|
||||
return `${slug}.md`;
|
||||
};
|
||||
|
||||
await Promise.all(
|
||||
docs.map(async (doc) => {
|
||||
const parts = [`# ${doc.title}`];
|
||||
if (doc.description) parts.push(doc.description);
|
||||
if (doc.content) parts.push(doc.content);
|
||||
await writeTextFile(outputDir, markdownExportPathFromSlug(doc.slug), parts.join('\n\n') + '\n');
|
||||
}),
|
||||
);
|
||||
|
||||
const llmsLines = [`# ${siteTitle}`, '', '## Docs', ''];
|
||||
menu.forEach((group) => {
|
||||
if (group?.type !== 'group') return;
|
||||
llmsLines.push(`### ${group.label || 'Docs'}`, '');
|
||||
const addSlug = (slug) => {
|
||||
const doc = docsBySlug.get(slug);
|
||||
if (!doc) return;
|
||||
const fullUrl = `${siteUrl}${doc.url}`;
|
||||
llmsLines.push(doc.description ? `- [${doc.title}](${fullUrl}): ${doc.description}` : `- [${doc.title}](${fullUrl})`);
|
||||
};
|
||||
(group.items || []).forEach((item) => {
|
||||
if (typeof item === 'string') addSlug(item);
|
||||
else if (item?.type === 'submenu' && Array.isArray(item.items)) item.items.forEach(addSlug);
|
||||
});
|
||||
llmsLines.push('');
|
||||
});
|
||||
await writeTextFile(outputDir, 'llms.txt', llmsLines.join('\n'));
|
||||
|
||||
const fullLines = [];
|
||||
orderedDocs.forEach((doc) => {
|
||||
fullLines.push(`# ${doc.title}`, `Source: ${siteUrl}${doc.url}`, '');
|
||||
if (doc.description) fullLines.push(doc.description, '');
|
||||
fullLines.push(doc.content, '', '---', '');
|
||||
});
|
||||
await writeTextFile(outputDir, 'llms-full.txt', fullLines.join('\n'));
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
export const docUrl = (slug) => {
|
||||
if (slug === 'index') return '/';
|
||||
if (slug.endsWith('/index')) return '/' + slug.replace(/\/index$/, '/');
|
||||
return '/' + slug + '/';
|
||||
};
|
||||
|
||||
export const flattenMenuSlugs = (menu) => {
|
||||
const out = [];
|
||||
|
||||
const walk = (items) => {
|
||||
(items || []).forEach((item) => {
|
||||
if (typeof item === 'string') {
|
||||
out.push(item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item?.type === 'submenu' && Array.isArray(item.items)) {
|
||||
walk(item.items);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
(menu || []).forEach((group) => {
|
||||
if (group?.type !== 'group') return;
|
||||
walk(group.items);
|
||||
});
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
export const collectMenuSlugs = (menu) => {
|
||||
return [...new Set(flattenMenuSlugs(menu))];
|
||||
};
|
||||
|
||||
export const fallbackLabelFromSlug = (slug) => {
|
||||
if (slug === 'index') return 'Introduction';
|
||||
const parts = String(slug).split('/');
|
||||
const last = parts[parts.length - 1];
|
||||
const base = last === 'index' && parts.length > 1 ? parts[parts.length - 2] : last;
|
||||
return base.replace(/-/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase());
|
||||
};
|
||||
@@ -0,0 +1,151 @@
|
||||
import { docUrl, fallbackLabelFromSlug, flattenMenuSlugs } from './menu.js';
|
||||
|
||||
export const registerNavigationFilters = (eleventyConfig, lucideIcons) => {
|
||||
const brandIcons = {
|
||||
github:
|
||||
'<svg aria-hidden="true" class="lucide lucide-github" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>',
|
||||
discord:
|
||||
'<svg aria-hidden="true" class="lucide lucide-discord" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/></svg>',
|
||||
};
|
||||
|
||||
const lucideSvg = (iconName) => {
|
||||
if (!iconName) return null;
|
||||
const pascalName = iconName
|
||||
.split('-')
|
||||
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
||||
.join('');
|
||||
const iconComponent = lucideIcons?.[pascalName];
|
||||
if (!iconComponent) return null;
|
||||
return iconComponent;
|
||||
};
|
||||
|
||||
const resolveIcon = (icon) => {
|
||||
if (!icon) return null;
|
||||
if (typeof icon !== 'string') return icon;
|
||||
const trimmed = icon.trim();
|
||||
if (!trimmed) return null;
|
||||
if (trimmed.startsWith('<svg')) return trimmed;
|
||||
if (brandIcons[trimmed]) return brandIcons[trimmed];
|
||||
return lucideSvg(trimmed) || null;
|
||||
};
|
||||
|
||||
const docsForCollections = (collections) => collections?.all || collections?.docs || [];
|
||||
|
||||
const buildMenus = (menu, collections, currentUrl = '') => {
|
||||
const docs = docsForCollections(collections);
|
||||
const docsByUrl = new Map(docs.map((doc) => [doc.url, doc]));
|
||||
|
||||
const processSlug = (slug) => {
|
||||
const url = docUrl(slug);
|
||||
const doc = docsByUrl.get(url);
|
||||
const title = doc?.data?.title ? String(doc.data.title) : fallbackLabelFromSlug(slug);
|
||||
const description = doc?.data?.description ? String(doc.data.description) : '';
|
||||
const iconSvg = resolveIcon(doc?.data?.icon) || null;
|
||||
|
||||
const sidebarItem = {
|
||||
icon: iconSvg,
|
||||
url,
|
||||
label: title,
|
||||
description,
|
||||
current: url.replace(/\/$/, '') === String(currentUrl).replace(/\/$/, ''),
|
||||
};
|
||||
|
||||
const keywords = [title, description].filter(Boolean).join(' ');
|
||||
const iconHtml = iconSvg || '';
|
||||
const commandItem = {
|
||||
type: 'item',
|
||||
label: `${iconHtml}<span>${title}</span>`,
|
||||
keywords,
|
||||
attrs: {
|
||||
onclick: `window.location.href='${url}'; this.closest('dialog')?.close();`,
|
||||
},
|
||||
};
|
||||
|
||||
return { sidebar: sidebarItem, command: commandItem };
|
||||
};
|
||||
|
||||
const processItem = (item) => {
|
||||
if (typeof item === 'string') return processSlug(item);
|
||||
|
||||
if (item?.type === 'submenu') {
|
||||
const submenuIcon = resolveIcon(item.icon) || null;
|
||||
const processed = (item.items || []).map((sub) => processItem(sub));
|
||||
return {
|
||||
sidebar: {
|
||||
...item,
|
||||
icon: submenuIcon,
|
||||
open: item.open ?? processed.some((entry) => entry.sidebar?.current),
|
||||
items: processed.map((entry) => entry.sidebar).filter(Boolean),
|
||||
},
|
||||
command: {
|
||||
type: 'group',
|
||||
label: item.label,
|
||||
items: processed.map((entry) => entry.command).filter(Boolean),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (item?.url) {
|
||||
const iconSvg = resolveIcon(item.icon) || null;
|
||||
return {
|
||||
sidebar: { ...item, icon: iconSvg, current: item.url === currentUrl },
|
||||
command: item.external
|
||||
? null
|
||||
: {
|
||||
type: 'item',
|
||||
label: `${iconSvg || ''}<span>${item.label}</span>`,
|
||||
attrs: { onclick: `window.location.href='${item.url}'; this.closest('dialog')?.close();` },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return { sidebar: null, command: null };
|
||||
};
|
||||
|
||||
const processedGroups = (menu || []).map((group) => {
|
||||
if (group?.type !== 'group') return { sidebar: group, command: group };
|
||||
|
||||
const processed = (group.items || []).map((item) => processItem(item));
|
||||
return {
|
||||
sidebar: { ...group, items: processed.map((entry) => entry.sidebar).filter(Boolean) },
|
||||
command: { type: 'group', label: group.label, items: processed.map((entry) => entry.command).filter(Boolean) },
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
sidebar: processedGroups.map((p) => p.sidebar),
|
||||
command: processedGroups.map((p) => p.command),
|
||||
};
|
||||
};
|
||||
|
||||
eleventyConfig.addFilter('getNavigation', function getNavigation(menu, collections) {
|
||||
const flattened = flattenMenuSlugs(menu);
|
||||
const currentUrl = this.page.url;
|
||||
const index = flattened.findIndex((slug) => docUrl(slug) === currentUrl);
|
||||
if (index === -1) return { prev: null, next: null };
|
||||
|
||||
const docs = docsForCollections(collections);
|
||||
const labelFor = (slug) => {
|
||||
const url = docUrl(slug);
|
||||
const doc = docs.find((d) => d.url === url);
|
||||
if (doc?.data?.title) return String(doc.data.title);
|
||||
return fallbackLabelFromSlug(slug);
|
||||
};
|
||||
|
||||
return {
|
||||
prev: index > 0 ? { url: docUrl(flattened[index - 1]), label: labelFor(flattened[index - 1]) } : null,
|
||||
next:
|
||||
index < flattened.length - 1
|
||||
? { url: docUrl(flattened[index + 1]), label: labelFor(flattened[index + 1]) }
|
||||
: null,
|
||||
};
|
||||
});
|
||||
|
||||
eleventyConfig.addFilter('sidebarMenu', function sidebarMenu(menu, collections) {
|
||||
return buildMenus(menu, collections, this.page.url).sidebar;
|
||||
});
|
||||
|
||||
eleventyConfig.addFilter('commandMenu', function commandMenu(menu, collections) {
|
||||
return buildMenus(menu, collections, this.page.url).command;
|
||||
});
|
||||
};
|
||||
@@ -132,9 +132,6 @@
|
||||
{{ caller() if caller }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<button type="button" class="btn-sm-icon-ghost" aria-label="Close dialog" onclick="this.closest('dialog').close()">
|
||||
<svg 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-x-icon lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</dialog>
|
||||
{% endmacro %}
|
||||
|
||||
+12
-1
@@ -28,6 +28,17 @@
|
||||
const getValue = option => option.dataset.value ?? option.textContent.trim();
|
||||
const getLabel = option => option.dataset.label || option.textContent.trim();
|
||||
|
||||
const scrollOptionIntoListbox = (option) => {
|
||||
const optionRect = option.getBoundingClientRect();
|
||||
const listboxRect = listbox.getBoundingClientRect();
|
||||
|
||||
if (optionRect.top < listboxRect.top) {
|
||||
listbox.scrollTop -= listboxRect.top - optionRect.top;
|
||||
} else if (optionRect.bottom > listboxRect.bottom) {
|
||||
listbox.scrollTop += optionRect.bottom - listboxRect.bottom;
|
||||
}
|
||||
};
|
||||
|
||||
const setActiveOption = (index) => {
|
||||
if (activeIndex > -1 && options[activeIndex]) {
|
||||
options[activeIndex].classList.remove('active');
|
||||
@@ -238,7 +249,7 @@
|
||||
|
||||
const nextOption = visibleOptions[nextVisibleIndex];
|
||||
setActiveOption(options.indexOf(nextOption));
|
||||
nextOption.scrollIntoView({ block: 'nearest' });
|
||||
scrollOptionIntoListbox(nextOption);
|
||||
};
|
||||
|
||||
input.addEventListener('focus', openPopover);
|
||||
|
||||
+14
-3
@@ -19,6 +19,17 @@
|
||||
let visibleMenuItems = [...menuItems];
|
||||
let activeIndex = -1;
|
||||
|
||||
const scrollItemIntoMenu = (item) => {
|
||||
const itemRect = item.getBoundingClientRect();
|
||||
const menuRect = menu.getBoundingClientRect();
|
||||
|
||||
if (itemRect.top < menuRect.top) {
|
||||
menu.scrollTop -= menuRect.top - itemRect.top;
|
||||
} else if (itemRect.bottom > menuRect.bottom) {
|
||||
menu.scrollTop += itemRect.bottom - menuRect.bottom;
|
||||
}
|
||||
};
|
||||
|
||||
const setActiveItem = (index) => {
|
||||
if (activeIndex > -1 && menuItems[activeIndex]) {
|
||||
menuItems[activeIndex].classList.remove('active');
|
||||
@@ -69,7 +80,7 @@
|
||||
|
||||
if (visibleMenuItems.length > 0) {
|
||||
setActiveItem(menuItems.indexOf(visibleMenuItems[0]));
|
||||
visibleMenuItems[0].scrollIntoView({ block: 'nearest' });
|
||||
scrollItemIntoMenu(visibleMenuItems[0]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -119,7 +130,7 @@
|
||||
if (nextVisibleIndex !== currentVisibleIndex) {
|
||||
const newActiveItem = visibleMenuItems[nextVisibleIndex];
|
||||
setActiveItem(menuItems.indexOf(newActiveItem));
|
||||
newActiveItem.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
||||
scrollItemIntoMenu(newActiveItem);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -147,7 +158,7 @@
|
||||
|
||||
if (visibleMenuItems.length > 0) {
|
||||
setActiveItem(menuItems.indexOf(visibleMenuItems[0]));
|
||||
visibleMenuItems[0].scrollIntoView({ block: 'nearest' });
|
||||
scrollItemIntoMenu(visibleMenuItems[0]);
|
||||
}
|
||||
|
||||
container.dataset.commandInitialized = true;
|
||||
|
||||
+13
-2
@@ -27,6 +27,17 @@
|
||||
|
||||
const getValue = (opt) => opt.dataset.value ?? opt.textContent.trim();
|
||||
|
||||
const scrollOptionIntoListbox = (option) => {
|
||||
const optionRect = option.getBoundingClientRect();
|
||||
const listboxRect = listbox.getBoundingClientRect();
|
||||
|
||||
if (optionRect.top < listboxRect.top) {
|
||||
listbox.scrollTop -= listboxRect.top - optionRect.top;
|
||||
} else if (optionRect.bottom > listboxRect.bottom) {
|
||||
listbox.scrollTop += optionRect.bottom - listboxRect.bottom;
|
||||
}
|
||||
};
|
||||
|
||||
const setActiveOption = (index) => {
|
||||
if (activeIndex > -1 && options[activeIndex]) {
|
||||
options[activeIndex].classList.remove('active');
|
||||
@@ -238,7 +249,7 @@
|
||||
if (nextVisibleIndex !== currentVisibleIndex) {
|
||||
const newActiveOption = visibleOptions[nextVisibleIndex];
|
||||
setActiveOption(options.indexOf(newActiveOption));
|
||||
newActiveOption.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
||||
scrollOptionIntoListbox(newActiveOption);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -273,7 +284,7 @@
|
||||
const selectedOption = listbox.querySelector('[role="option"][aria-selected="true"]');
|
||||
if (selectedOption) {
|
||||
setActiveOption(options.indexOf(selectedOption));
|
||||
selectedOption.scrollIntoView({ block: 'nearest' });
|
||||
scrollOptionIntoListbox(selectedOption);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -132,9 +132,6 @@
|
||||
{{ caller() if caller }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<button type="button" class="btn-sm-icon-ghost" aria-label="Close dialog" onclick="this.closest('dialog').close()">
|
||||
<svg 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-x-icon lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</dialog>
|
||||
{% endmacro %}
|
||||
|
||||
+7
-60
@@ -2,9 +2,8 @@
|
||||
Renders a tabs component.
|
||||
|
||||
@param id {string} - Unique identifier for the tabs component.
|
||||
@param tabsets {array} - An array of objects, each representing a tab and its panel.
|
||||
@param tabsets {array} - An array of objects, each representing a tab and panel.
|
||||
Each object should have:
|
||||
- id {string}: Unique identifier prefix for the tab and panel.
|
||||
- tab {string}: HTML content for the tab button.
|
||||
- panel {string} [optional]: HTML content for the tab panel.
|
||||
- tab_attrs {object} [optional]: Additional HTML attributes for the tab button.
|
||||
@@ -14,67 +13,15 @@
|
||||
Set aria-orientation="vertical" for vertical tabs or data-variant="line" for line tabs.
|
||||
@param default_tab_index {number} [optional] [default=1] - The 1-based index of the tab to be active initially.
|
||||
#}
|
||||
{% macro tabs(
|
||||
{%- macro tabs(
|
||||
id=None,
|
||||
tabsets=[],
|
||||
main_attrs={},
|
||||
tablist_attrs={},
|
||||
default_tab_index=1
|
||||
)
|
||||
%}
|
||||
{% set id = id or ("tabs-" + (range(100000, 999999) | random | string)) %}
|
||||
{% set orientation = tablist_attrs['aria-orientation'] or 'horizontal' %}
|
||||
<div
|
||||
class="tabs {{ main_attrs.class }}"
|
||||
id="{{ id }}"
|
||||
{% for key, value in main_attrs %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
<nav
|
||||
role="tablist"
|
||||
aria-orientation="{{ orientation }}"
|
||||
{% for key, value in tablist_attrs %}
|
||||
{% if key != 'aria-orientation' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{% for tabset in tabsets %}
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
id="{{ id }}-tab-{{ loop.index }}"
|
||||
aria-controls="{{ id }}-panel-{{ loop.index }}"
|
||||
aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}"
|
||||
tabindex="{{ '0' if loop.index == default_tab_index and not tabset.tab_attrs.disabled and tabset.tab_attrs['aria-disabled'] != 'true' else '-1' }}"
|
||||
{% if tabset.tab_attrs %}
|
||||
{% for key, value in tabset.tab_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
{{ tabset.tab | safe }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
|
||||
{% for tabset in tabsets %}
|
||||
{% if tabset.panel %}
|
||||
<div
|
||||
role="tabpanel"
|
||||
id="{{ id }}-panel-{{ loop.index }}"
|
||||
aria-labelledby="{{ id }}-tab-{{ loop.index }}"
|
||||
tabindex="-1"
|
||||
aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}"
|
||||
{% if loop.index != default_tab_index %}hidden{% endif %}
|
||||
{% if tabset.panel_attrs %}
|
||||
{% for key, value in tabset.panel_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
{{ tabset.panel | safe }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
-%}
|
||||
{%- set id = id or ("tabs-" + (range(100000, 999999) | random | string)) -%}
|
||||
{%- set orientation = tablist_attrs['aria-orientation'] or 'horizontal' -%}
|
||||
<div class="tabs {{ main_attrs.class }}" id="{{ id }}"{% for key, value in main_attrs %}{% if key != 'class' %} {{ key }}="{{ value }}"{% endif %}{% endfor %}><nav role="tablist" aria-orientation="{{ orientation }}"{% for key, value in tablist_attrs %}{% if key != 'aria-orientation' %} {{ key }}="{{ value }}"{% endif %}{% endfor %}>{% for tabset in tabsets %}<button type="button" role="tab" id="{{ id }}-tab-{{ loop.index }}" aria-controls="{{ id }}-panel-{{ loop.index }}" aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}" tabindex="{{ '0' if loop.index == default_tab_index and not tabset.tab_attrs.disabled and tabset.tab_attrs['aria-disabled'] != 'true' else '-1' }}"{% if tabset.tab_attrs %}{% for key, value in tabset.tab_attrs %} {{ key }}="{{ value }}"{% endfor %}{% endif %}>{{ tabset.tab | safe }}</button>{% endfor %}</nav>{% for tabset in tabsets %}{% if tabset.panel %}<div role="tabpanel" id="{{ id }}-panel-{{ loop.index }}" aria-labelledby="{{ id }}-tab-{{ loop.index }}" tabindex="-1" aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}"{% if loop.index != default_tab_index %} hidden{% endif %}{% if tabset.panel_attrs %}{% for key, value in tabset.panel_attrs %} {{ key }}="{{ value }}"{% endfor %}{% endif %}>{{ tabset.panel | safe }}</div>{% endif %}{% endfor %}</div>
|
||||
{%- endmacro -%}
|
||||
|
||||
Reference in New Issue
Block a user