mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-14 14:08:18 +00:00
Migrate empty and item docs to Basecoat APIs
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@import "./basecoat-luma.css";
|
||||
@@ -0,0 +1,34 @@
|
||||
@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-luma.css";
|
||||
@@ -0,0 +1,2 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@import "./basecoat-lyra.css";
|
||||
@@ -0,0 +1,34 @@
|
||||
@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-lyra.css";
|
||||
@@ -0,0 +1,2 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@import "./basecoat-maia.css";
|
||||
@@ -0,0 +1,34 @@
|
||||
@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-maia.css";
|
||||
@@ -0,0 +1,2 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@import "./basecoat-mira.css";
|
||||
@@ -0,0 +1,34 @@
|
||||
@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-mira.css";
|
||||
@@ -0,0 +1,2 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@import "./basecoat-nova.css";
|
||||
@@ -0,0 +1,34 @@
|
||||
@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-nova.css";
|
||||
@@ -0,0 +1,2 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@import "./basecoat-rhea.css";
|
||||
@@ -0,0 +1,34 @@
|
||||
@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-rhea.css";
|
||||
@@ -0,0 +1,2 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@import "./basecoat-sera.css";
|
||||
@@ -0,0 +1,34 @@
|
||||
@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-sera.css";
|
||||
@@ -0,0 +1,2 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@import "./basecoat-vega.css";
|
||||
@@ -0,0 +1,34 @@
|
||||
@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";
|
||||
@@ -8,11 +8,15 @@
|
||||
@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";
|
||||
|
||||
@@ -2,34 +2,20 @@
|
||||
@layer components {
|
||||
.alert,
|
||||
.alert-destructive {
|
||||
@apply relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current;
|
||||
|
||||
> h2,
|
||||
> h3,
|
||||
> h4,
|
||||
> h5,
|
||||
> h6,
|
||||
> strong,
|
||||
> [data-title] {
|
||||
@apply col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight;
|
||||
}
|
||||
> section {
|
||||
@apply text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed;
|
||||
|
||||
ul {
|
||||
@apply list-inside list-disc text-sm;
|
||||
}
|
||||
}
|
||||
@apply relative w-full;
|
||||
}
|
||||
.alert {
|
||||
@apply bg-card text-card-foreground;
|
||||
}
|
||||
.alert-destructive {
|
||||
@apply text-destructive bg-card [&>svg]:text-current;
|
||||
|
||||
> section {
|
||||
@apply text-destructive;
|
||||
.alert > :is(h2, h3, h4, h5, h6, strong, [data-title]),
|
||||
.alert-destructive > :is(h2, h3, h4, h5, h6, strong, [data-title]) {
|
||||
@apply min-h-4;
|
||||
}
|
||||
|
||||
.alert > section,
|
||||
.alert-destructive > section {
|
||||
@apply grid justify-items-start gap-1;
|
||||
|
||||
ul {
|
||||
@apply list-inside list-disc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,21 +4,9 @@
|
||||
.badge-primary,
|
||||
.badge-secondary,
|
||||
.badge-destructive,
|
||||
.badge-outline {
|
||||
@apply inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden;
|
||||
}
|
||||
.badge,
|
||||
.badge-primary {
|
||||
@apply border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90;
|
||||
}
|
||||
.badge-secondary {
|
||||
@apply border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90;
|
||||
}
|
||||
.badge-destructive {
|
||||
@apply border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60;
|
||||
}
|
||||
.badge-outline {
|
||||
@apply text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground;
|
||||
.badge-outline,
|
||||
.badge-ghost,
|
||||
.badge-link {
|
||||
@apply inline-flex w-fit shrink-0 items-center justify-center overflow-hidden whitespace-nowrap focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,33 +1,36 @@
|
||||
/* Button Group */
|
||||
@layer components {
|
||||
.button-group {
|
||||
@apply inline-flex w-fit items-stretch;
|
||||
/* isolate */
|
||||
@apply isolate inline-flex w-fit items-stretch;
|
||||
|
||||
> .input {
|
||||
@apply flex-1;
|
||||
}
|
||||
|
||||
> *:focus-visible,
|
||||
> :is(.dropdown-menu, .popover, .select) > button:focus-visible {
|
||||
@apply relative z-10;
|
||||
}
|
||||
|
||||
> hr[role='separator'] {
|
||||
@apply w-0 h-auto self-stretch border border-input shrink-0 m-0;
|
||||
> :is(hr[role='separator'], .button-group-separator) {
|
||||
@apply relative w-px h-auto self-stretch shrink-0 border-0 my-px mx-0;
|
||||
}
|
||||
|
||||
&:not([data-orientation='vertical']) {
|
||||
> *:not(:first-child),
|
||||
> :is(.dropdown-menu, .popover, .select):not(:first-child) > button {
|
||||
@apply rounded-l-none border-l-0;
|
||||
@apply rounded-s-none border-s-0;
|
||||
}
|
||||
> *:not(:last-child),
|
||||
> :is(.dropdown-menu, .popover, .select):not(:last-child) > button {
|
||||
@apply rounded-r-none;
|
||||
@apply rounded-e-none;
|
||||
}
|
||||
}
|
||||
&[data-orientation='vertical'] {
|
||||
@apply flex-col;
|
||||
|
||||
> hr[role='separator'] {
|
||||
@apply w-auto h-px;
|
||||
> :is(hr[role='separator'], .button-group-separator) {
|
||||
@apply w-auto h-px mx-px my-0;
|
||||
}
|
||||
|
||||
> *:not(:first-child),
|
||||
@@ -40,5 +43,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button-group-text {
|
||||
@apply flex items-center [&_svg]:pointer-events-none;
|
||||
}
|
||||
|
||||
.button-group-separator {
|
||||
@apply relative self-stretch shrink-0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,6 @@
|
||||
.btn-lg-icon-ghost,
|
||||
.btn-lg-icon-link,
|
||||
.btn-lg-icon-destructive {
|
||||
@apply inline-flex shrink-0 items-center justify-center whitespace-nowrap transition-all outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 cursor-pointer;
|
||||
@apply inline-flex shrink-0 items-center justify-center whitespace-nowrap transition-all outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-13
@@ -1,25 +1,22 @@
|
||||
/* Card */
|
||||
@layer components {
|
||||
.card {
|
||||
@apply bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm;
|
||||
@apply flex flex-col;
|
||||
|
||||
> header {
|
||||
@apply @container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6;
|
||||
/* TODO: CLEAN has-data-[slot=card-action] */
|
||||
@apply @container/card-header grid auto-rows-min items-start has-[>[data-slot=card-action],>.card-action]:grid-cols-[1fr_auto] has-[>p]:grid-rows-[auto_auto];
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply leading-none font-semibold;
|
||||
}
|
||||
p {
|
||||
@apply text-muted-foreground text-sm;
|
||||
}
|
||||
> header > :is(h2, h3, [data-title], .card-title) {
|
||||
@apply leading-none;
|
||||
}
|
||||
> section {
|
||||
@apply px-6;
|
||||
|
||||
> header > :is([data-slot='card-action'], .card-action) {
|
||||
@apply col-start-2 row-span-2 row-start-1 self-start justify-self-end;
|
||||
}
|
||||
|
||||
> footer {
|
||||
@apply flex items-center px-6 [.border-t]:pt-6;
|
||||
@apply flex items-center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/* Checkbox */
|
||||
@layer components {
|
||||
:is(.form, .field) input[type='checkbox']:not([role='switch']),
|
||||
.field > input[type='checkbox']:not([role='switch']),
|
||||
.input[type='checkbox']:not([role='switch']) {
|
||||
@apply appearance-none border-input dark:bg-input/30 checked:bg-primary dark:checked:bg-primary checked:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50;
|
||||
@apply relative shrink-0 appearance-none outline-none disabled:cursor-not-allowed disabled:opacity-50;
|
||||
|
||||
&:checked:after {
|
||||
@apply content-[''] block size-3.5 bg-primary-foreground;
|
||||
@apply content-[''] block bg-current;
|
||||
@apply mask-[image:var(--check-icon)] mask-size-[0.875rem] mask-no-repeat mask-center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/* Combobox */
|
||||
@layer components {
|
||||
.combobox {
|
||||
@apply relative inline-flex w-fit flex-col;
|
||||
}
|
||||
|
||||
.combobox > input[role='combobox'] {
|
||||
@apply w-full min-w-0 outline-hidden placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50;
|
||||
}
|
||||
|
||||
.combobox > svg,
|
||||
.combobox-trigger-icon {
|
||||
@apply pointer-events-none absolute shrink-0;
|
||||
}
|
||||
|
||||
.combobox [data-popover] {
|
||||
@apply isolate overflow-hidden;
|
||||
min-width: max(100%, 9rem);
|
||||
}
|
||||
|
||||
.combobox [role='listbox'] {
|
||||
@apply outline-hidden overflow-y-auto;
|
||||
}
|
||||
|
||||
.combobox [role='listbox'] [role='group']:not(:has([role='option']:not([aria-hidden='true']))) {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
.combobox [role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@apply flex items-center justify-center truncate;
|
||||
}
|
||||
|
||||
.combobox [role='listbox'][data-empty]:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@apply content-[attr(data-empty)];
|
||||
}
|
||||
|
||||
.combobox [role='listbox']:not([data-empty]):not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@apply content-['No_results_found'];
|
||||
}
|
||||
|
||||
.combobox [role='option'] {
|
||||
@apply aria-hidden:hidden relative flex w-full cursor-default items-center truncate outline-hidden select-none aria-disabled:pointer-events-none aria-disabled:opacity-50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0;
|
||||
}
|
||||
|
||||
.combobox [role='option'][aria-selected='true'] {
|
||||
@apply bg-[image:var(--check-icon)] bg-no-repeat bg-position-[center_right_0.5rem] bg-size-[0.875rem];
|
||||
}
|
||||
|
||||
.combobox [role='option'][aria-selected='true']:dir(rtl) {
|
||||
@apply bg-position-[center_left_0.5rem];
|
||||
}
|
||||
|
||||
.combobox [role='separator'] {
|
||||
@apply border-0;
|
||||
}
|
||||
|
||||
.combobox [role='option'].active,
|
||||
.combobox [role='option']:focus-visible,
|
||||
.combobox:not([data-combobox-initialized]) [role='option']:not([aria-disabled='true']):not(:disabled):hover {
|
||||
@apply bg-accent text-accent-foreground;
|
||||
}
|
||||
|
||||
.combobox-chips {
|
||||
@apply flex w-full min-w-0 flex-wrap items-center;
|
||||
}
|
||||
|
||||
.combobox-chips > input[role='combobox'] {
|
||||
@apply min-w-16 flex-1 bg-transparent outline-hidden placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50;
|
||||
}
|
||||
|
||||
.combobox-chip {
|
||||
@apply inline-flex w-fit shrink-0 items-center whitespace-nowrap;
|
||||
}
|
||||
|
||||
.combobox-chip-remove {
|
||||
@apply inline-flex items-center justify-center;
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,13 @@
|
||||
.command-dialog {
|
||||
@apply inset-y-0 opacity-0 transition-all transition-discrete;
|
||||
|
||||
&:is([open],:popover-open) {
|
||||
&:is([open], :popover-open) {
|
||||
@apply opacity-100;
|
||||
|
||||
&::backdrop {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
> * {
|
||||
@apply scale-100;
|
||||
}
|
||||
@@ -19,91 +20,112 @@
|
||||
&::backdrop {
|
||||
@apply opacity-0;
|
||||
}
|
||||
|
||||
> * {
|
||||
@apply scale-95;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&::backdrop {
|
||||
@apply bg-black/50 opacity-0 transition-all transition-discrete;
|
||||
@apply opacity-0 transition-all transition-discrete;
|
||||
}
|
||||
|
||||
> * {
|
||||
@apply bg-background fixed top-[50%] left-[50%] z-50 flex flex-col w-full max-w-[calc(100%_-_2rem)] -translate-x-1/2 -translate-y-1/2 overflow-hidden rounded-lg border shadow-lg sm:max-w-lg max-h-[min(640px,calc(100%_-_2rem))];
|
||||
@apply transition-all scale-95;
|
||||
|
||||
> button,
|
||||
> form[method='dialog'] {
|
||||
@apply absolute top-4 right-4;
|
||||
}
|
||||
> button,
|
||||
> form[method='dialog'] > button {
|
||||
@apply ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4;
|
||||
}
|
||||
@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;
|
||||
}
|
||||
|
||||
.command {
|
||||
> header input {
|
||||
@apply h-12;
|
||||
}
|
||||
[role='menu'] {
|
||||
@apply px-2;
|
||||
> * > :is(button, form[method='dialog']) {
|
||||
@apply absolute;
|
||||
}
|
||||
|
||||
[role='menuitem'] {
|
||||
@apply py-3 px-2 [&_svg]:size-5;
|
||||
}
|
||||
[role='heading'] {
|
||||
@apply px-2 py-1.5 font-medium text-muted-foreground;
|
||||
}
|
||||
}
|
||||
> * > :is(button, form[method='dialog'] > button) {
|
||||
@apply inline-flex items-center justify-center opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0;
|
||||
}
|
||||
|
||||
> * > :is(button, form[method='dialog'] > button) > svg:not([class*='size-']) {
|
||||
@apply size-4;
|
||||
}
|
||||
}
|
||||
|
||||
.command {
|
||||
> header {
|
||||
@apply flex items-center border-b px-3 gap-2;
|
||||
@apply flex w-full flex-col overflow-hidden;
|
||||
}
|
||||
|
||||
svg {
|
||||
@apply size-4 shrink-0 opacity-50;
|
||||
}
|
||||
input {
|
||||
@apply placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50;
|
||||
}
|
||||
}
|
||||
[role='menu'] {
|
||||
@apply max-h-[300px] overflow-y-auto overflow-x-hidden p-1;
|
||||
.command > header {
|
||||
@apply flex items-center;
|
||||
}
|
||||
|
||||
[role='menuitem'] {
|
||||
@apply aria-hidden:hidden relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none aria-disabled:opacity-50 aria-disabled:pointer-events-none disabled:opacity-50 disabled:pointer-events-none w-full truncate [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4;
|
||||
.command > header svg {
|
||||
@apply shrink-0;
|
||||
}
|
||||
|
||||
&.active {
|
||||
@apply bg-accent text-accent-foreground;
|
||||
}
|
||||
}
|
||||
[role='heading'] {
|
||||
@apply text-muted-foreground flex px-2 py-1.5 text-xs font-medium;
|
||||
}
|
||||
[role='group']:not(:has([role='menuitem']:not([aria-hidden='true']))) {
|
||||
@apply hidden;
|
||||
}
|
||||
[role='separator'] {
|
||||
@apply border-border -mx-1 my-1;
|
||||
}
|
||||
&:not(:has([role='menuitem']:not([aria-hidden='true'])))::before {
|
||||
@apply flex items-center justify-center py-6 px-3 text-sm truncate -m-1;
|
||||
}
|
||||
&[data-empty]:not(:has([role='menuitem']:not([aria-hidden='true'])))::before {
|
||||
@apply content-[attr(data-empty)];
|
||||
}
|
||||
&:not([data-empty]):not(:has([role='menuitem']:not([aria-hidden='true'])))::before {
|
||||
@apply content-['No_results_found'];
|
||||
}
|
||||
}
|
||||
&:not([data-command-initialized]) [role='menuitem'] {
|
||||
@apply hover:bg-accent hover:text-accent-foreground;
|
||||
}
|
||||
&:has(> header input:not(:placeholder-shown)) [role='separator'] {
|
||||
@apply hidden;
|
||||
}
|
||||
.command > header input {
|
||||
@apply w-full bg-transparent outline-hidden disabled:cursor-not-allowed disabled:opacity-50;
|
||||
}
|
||||
|
||||
.command [role='menu'] {
|
||||
@apply overflow-x-hidden overflow-y-auto outline-hidden;
|
||||
}
|
||||
|
||||
.command [role='menuitem'] {
|
||||
@apply aria-hidden:hidden relative flex w-full cursor-default items-center truncate outline-hidden select-none disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0;
|
||||
}
|
||||
|
||||
.command [role='menuitem'].active {
|
||||
@apply bg-accent text-accent-foreground;
|
||||
}
|
||||
|
||||
.command [role='group'] {
|
||||
@apply overflow-hidden;
|
||||
}
|
||||
|
||||
.command [role='group']:not(:has([role='menuitem']:not([aria-hidden='true']))) {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
.command [role='heading'] {
|
||||
@apply flex;
|
||||
}
|
||||
|
||||
.command [role='separator'] {
|
||||
@apply border-0;
|
||||
}
|
||||
|
||||
.command [data-shortcut] {
|
||||
@apply ms-auto;
|
||||
}
|
||||
|
||||
.command [data-indicator] {
|
||||
@apply ms-auto opacity-0;
|
||||
}
|
||||
|
||||
.command [role='menuitem'][data-checked='true'] [data-indicator],
|
||||
.command [role='menuitem'][aria-selected='true'] [data-indicator] {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
.command [role='menuitem']:has([data-shortcut]) [data-indicator] {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
.command [role='menu']:not(:has([role='menuitem']:not([aria-hidden='true'])))::before {
|
||||
@apply flex items-center justify-center truncate;
|
||||
}
|
||||
|
||||
.command [role='menu'][data-empty]:not(:has([role='menuitem']:not([aria-hidden='true'])))::before {
|
||||
@apply content-[attr(data-empty)];
|
||||
}
|
||||
|
||||
.command [role='menu']:not([data-empty]):not(:has([role='menuitem']:not([aria-hidden='true'])))::before {
|
||||
@apply content-['No_results_found'];
|
||||
}
|
||||
|
||||
.command:not([data-command-initialized]) [role='menuitem']:not([aria-disabled='true']):not(:disabled):hover {
|
||||
@apply bg-accent text-accent-foreground;
|
||||
}
|
||||
|
||||
.command:has(> header input:not(:placeholder-shown)) [role='separator'] {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,61 +1,52 @@
|
||||
/* Dialog */
|
||||
@layer components {
|
||||
.dialog {
|
||||
@apply inset-y-0 opacity-0 transition-all transition-discrete;
|
||||
@apply bg-transparent p-0 text-inherit opacity-0 transition-all transition-discrete backdrop:opacity-0 backdrop:transition-all backdrop:transition-discrete;
|
||||
max-width: none;
|
||||
max-height: none;
|
||||
|
||||
&:is([open],:popover-open) {
|
||||
@apply opacity-100;
|
||||
&:is([open], :popover-open) {
|
||||
@apply opacity-100 backdrop:opacity-100;
|
||||
|
||||
&::backdrop {
|
||||
@apply opacity-100;
|
||||
}
|
||||
> * {
|
||||
@apply scale-100;
|
||||
}
|
||||
|
||||
@starting-style {
|
||||
@apply opacity-0;
|
||||
@apply opacity-0 backdrop:opacity-0;
|
||||
|
||||
&::backdrop {
|
||||
@apply opacity-0;
|
||||
}
|
||||
> * {
|
||||
@apply scale-95;
|
||||
}
|
||||
}
|
||||
}
|
||||
&::backdrop {
|
||||
@apply bg-black/50 opacity-0 transition-all transition-discrete;
|
||||
}
|
||||
|
||||
> * {
|
||||
@apply bg-background fixed top-[50%] left-[50%] z-50 flex flex-col w-full max-w-[calc(100%_-_2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-lg border p-6 shadow-lg sm:max-w-lg max-h-[calc(100%_-_2rem)];
|
||||
@apply transition-all scale-95;
|
||||
@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;
|
||||
}
|
||||
|
||||
> header {
|
||||
@apply flex flex-col gap-2 text-center sm:text-left;
|
||||
> * > header {
|
||||
@apply flex flex-col;
|
||||
}
|
||||
|
||||
> h2 {
|
||||
@apply text-lg leading-none font-semibold;
|
||||
}
|
||||
> p {
|
||||
@apply text-muted-foreground text-sm;
|
||||
}
|
||||
}
|
||||
> section {
|
||||
@apply flex-1 -mx-6 px-6;
|
||||
}
|
||||
> footer {
|
||||
@apply flex flex-col-reverse gap-2 sm:flex-row sm:justify-end;
|
||||
}
|
||||
> button,
|
||||
> form[method='dialog'] {
|
||||
@apply absolute top-4 right-4;
|
||||
}
|
||||
> button,
|
||||
> form[method='dialog'] > button {
|
||||
@apply ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4;
|
||||
}
|
||||
> * > section {
|
||||
@apply flex-1 overflow-y-auto;
|
||||
}
|
||||
|
||||
> * > footer {
|
||||
@apply flex flex-col-reverse sm:flex-row sm:justify-end;
|
||||
}
|
||||
|
||||
> * > :is(button, form[method='dialog']) {
|
||||
@apply absolute;
|
||||
}
|
||||
|
||||
> * > :is(button, form[method='dialog'] > button) {
|
||||
@apply inline-flex items-center justify-center opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0;
|
||||
}
|
||||
|
||||
> * > :is(button, form[method='dialog'] > button) > svg:not([class*='size-']) {
|
||||
@apply size-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,38 +2,47 @@
|
||||
@layer components {
|
||||
.dropdown-menu {
|
||||
@apply relative inline-flex;
|
||||
}
|
||||
|
||||
[data-popover] {
|
||||
@apply p-1;
|
||||
min-width: anchor-size(width);
|
||||
.dropdown-menu > [data-popover] {
|
||||
min-width: anchor-size(width);
|
||||
}
|
||||
|
||||
[role='menuitem'],
|
||||
[role='menuitemcheckbox'],
|
||||
[role='menuitemradio'] {
|
||||
@apply aria-hidden:hidden [&_svg]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none [&_svg]:shrink-0 [&_svg]:size-4 aria-disabled:opacity-50 aria-disabled:pointer-events-none disabled:opacity-50 disabled:pointer-events-none w-full truncate;
|
||||
.dropdown-menu [role='menu'] {
|
||||
@apply outline-hidden;
|
||||
}
|
||||
|
||||
&:not([aria-disabled='true']) {
|
||||
@apply focus-visible:bg-accent focus-visible:text-accent-foreground;
|
||||
}
|
||||
.dropdown-menu :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']) {
|
||||
@apply aria-hidden:hidden relative flex w-full cursor-default items-center truncate outline-hidden select-none disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0;
|
||||
}
|
||||
|
||||
&.active {
|
||||
@apply bg-accent text-accent-foreground;
|
||||
}
|
||||
}
|
||||
[role='menu'] [role='heading'] {
|
||||
@apply flex px-2 py-1.5 text-sm font-medium;
|
||||
}
|
||||
[role='separator'] {
|
||||
@apply border-border -mx-1 my-1;
|
||||
}
|
||||
}
|
||||
&:not([data-dropdown-menu-initialized]) [data-popover] {
|
||||
[role='menuitem'],
|
||||
[role='menuitemcheckbox'],
|
||||
[role='menuitemradio'] {
|
||||
@apply hover:bg-accent hover:text-accent-foreground;
|
||||
}
|
||||
.dropdown-menu [role='menu'] [role='heading'] {
|
||||
@apply flex;
|
||||
}
|
||||
|
||||
.dropdown-menu [role='separator'] {
|
||||
@apply border-0;
|
||||
}
|
||||
|
||||
.dropdown-menu [data-shortcut] {
|
||||
@apply ms-auto;
|
||||
}
|
||||
|
||||
.dropdown-menu [data-indicator] {
|
||||
@apply pointer-events-none invisible;
|
||||
}
|
||||
|
||||
.dropdown-menu :is([role='menuitemcheckbox'], [role='menuitemradio'])[aria-checked='true'] [data-indicator] {
|
||||
@apply visible;
|
||||
}
|
||||
|
||||
.dropdown-menu [data-rtl-flip] {
|
||||
@apply rtl:rotate-180;
|
||||
}
|
||||
|
||||
.dropdown-menu:not([data-dropdown-menu-initialized]) :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']) {
|
||||
&:not([aria-disabled='true']):not(:disabled):hover {
|
||||
@apply bg-accent text-accent-foreground;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/* Empty */
|
||||
@layer components {
|
||||
.empty {
|
||||
@apply flex w-full min-w-0 flex-1 flex-col items-center justify-center text-center text-balance;
|
||||
}
|
||||
|
||||
.empty > header {
|
||||
@apply flex max-w-sm flex-col items-center;
|
||||
}
|
||||
|
||||
.empty figure {
|
||||
@apply flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0;
|
||||
}
|
||||
|
||||
.empty figure:has(img) {
|
||||
@apply overflow-hidden [&_img]:size-full [&_img]:object-cover;
|
||||
}
|
||||
|
||||
.empty > :is(section, footer) {
|
||||
@apply flex w-full max-w-sm min-w-0 flex-col items-center text-balance;
|
||||
}
|
||||
|
||||
.empty p {
|
||||
@apply [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary;
|
||||
}
|
||||
}
|
||||
@@ -1,44 +1,86 @@
|
||||
/* Field */
|
||||
@layer components {
|
||||
.fieldset {
|
||||
@apply flex flex-col gap-6;
|
||||
|
||||
> legend {
|
||||
@apply text-base font-medium mb-3;
|
||||
}
|
||||
@apply flex flex-col;
|
||||
}
|
||||
.field {
|
||||
@apply flex flex-col w-full gap-3 data-[invalid=true]:text-destructive [&>*]:w-full [&>.sr-only]:w-auto;
|
||||
|
||||
h2,
|
||||
h3 {
|
||||
@apply flex w-fit items-center gap-2 text-sm leading-snug font-medium;
|
||||
}
|
||||
[role="alert"] {
|
||||
@apply text-sm text-destructive font-normal;
|
||||
|
||||
ul {
|
||||
@apply ml-4 flex list-disc flex-col gap-1;
|
||||
}
|
||||
}
|
||||
section {
|
||||
@apply flex flex-1 flex-col gap-1.5 leading-snug;
|
||||
}
|
||||
.fieldset > :is([role='group'], [role='radiogroup']):not(.field),
|
||||
.field > :is(fieldset, [role='group'], [role='radiogroup']):not(.field) {
|
||||
@apply @container/field-group flex w-full flex-col;
|
||||
}
|
||||
|
||||
.field {
|
||||
@apply flex w-full flex-col data-[invalid=true]:text-destructive [&>*]:w-full [&>.sr-only]:w-auto;
|
||||
|
||||
&[data-orientation='horizontal'] {
|
||||
@apply flex-row items-center [&>label]:flex-auto has-[>section]:items-start has-[>section]:[&>[type=checkbox],[type=radio]]:mt-px [&_p]:text-balance;
|
||||
}
|
||||
|
||||
&[data-orientation='responsive'] {
|
||||
@apply flex-col @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto @md/field-group:has-[>section]:items-start @md/field-group:[&>label]:flex-auto @md/field-group:has-[>section]:[&>[type=checkbox],[type=radio]]:mt-px;
|
||||
}
|
||||
|
||||
section,
|
||||
.field-content {
|
||||
@apply flex flex-1 flex-col leading-snug;
|
||||
}
|
||||
|
||||
h2,
|
||||
h3,
|
||||
.field-title {
|
||||
@apply flex w-fit items-center;
|
||||
}
|
||||
|
||||
[role='alert'],
|
||||
.field-error {
|
||||
@apply font-normal;
|
||||
|
||||
ul {
|
||||
@apply ms-4 flex list-disc flex-col gap-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.field > label,
|
||||
label:has(> .field) {
|
||||
@apply w-fit;
|
||||
}
|
||||
|
||||
.field > label:has(> .field),
|
||||
.field > label:has(input[type='radio']),
|
||||
.field > label:has(input[type='checkbox']),
|
||||
.field > label:has(input[role='switch']),
|
||||
label:has(> .field) {
|
||||
@apply w-full;
|
||||
}
|
||||
|
||||
.field > label:has(> .field),
|
||||
label:has(> .field) {
|
||||
@apply flex-col;
|
||||
}
|
||||
|
||||
.field-description,
|
||||
.fieldset legend + p,
|
||||
.field > p,
|
||||
.field section > p {
|
||||
@apply text-muted-foreground text-sm leading-normal font-normal [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4;
|
||||
@apply leading-normal font-normal [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4;
|
||||
}
|
||||
.fieldset legend + p {
|
||||
@apply -mt-1.5;
|
||||
}
|
||||
.field > p {
|
||||
|
||||
.field > p,
|
||||
.field section > p,
|
||||
.field-description {
|
||||
@apply last:mt-0 nth-last-2:-mt-1;
|
||||
}
|
||||
}
|
||||
|
||||
.field-separator {
|
||||
@apply relative;
|
||||
}
|
||||
|
||||
.field-separator > hr[role='separator'] {
|
||||
@apply absolute inset-x-0 top-1/2;
|
||||
}
|
||||
|
||||
.field-separator > span {
|
||||
@apply relative mx-auto block w-fit bg-background;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/* Input Group */
|
||||
@layer components {
|
||||
.input-group {
|
||||
@apply relative isolate flex w-full min-w-0 items-center overflow-hidden outline-none;
|
||||
|
||||
&[data-orientation='vertical'] {
|
||||
@apply h-auto flex-col items-stretch;
|
||||
}
|
||||
}
|
||||
|
||||
.input-group > :is(input, textarea, select, [data-control]) {
|
||||
@apply text-foreground placeholder:text-muted-foreground block min-w-0 flex-1 appearance-none rounded-none border-0 bg-transparent shadow-none outline-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 disabled:bg-transparent;
|
||||
}
|
||||
|
||||
.input-group > :is(input, select, [data-control]) {
|
||||
@apply h-full;
|
||||
}
|
||||
|
||||
.input-group > [data-control] {
|
||||
@apply w-full;
|
||||
}
|
||||
|
||||
.input-group[data-orientation='vertical'] > :is(input, select, [data-control]) {
|
||||
@apply h-auto flex-none;
|
||||
}
|
||||
|
||||
.input-group > textarea {
|
||||
@apply field-sizing-content min-h-20 w-full resize-none;
|
||||
}
|
||||
|
||||
.input-group > :is(span, kbd, svg, [role='group'], .popover, .dropdown-menu, button, footer, header, hr[role='separator']) {
|
||||
@apply shrink-0;
|
||||
}
|
||||
|
||||
.input-group > :is(span, [role='group'], footer, header) {
|
||||
@apply inline-flex items-center;
|
||||
}
|
||||
|
||||
.input-group > svg {
|
||||
@apply pointer-events-none;
|
||||
}
|
||||
|
||||
.input-group > [data-align='start'] {
|
||||
@apply order-first;
|
||||
}
|
||||
|
||||
.input-group > [data-align='end'] {
|
||||
@apply order-last;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
/* Input */
|
||||
@layer components {
|
||||
:is(.form, .field) input[type='text']:not(:is(.select [data-popover] > header input[role='combobox'], .command > header input, .dialog > * > header input)),
|
||||
:is(.form, .field) input[type='email'],
|
||||
:is(.form, .field) input[type='password'],
|
||||
:is(.form, .field) input[type='number'],
|
||||
:is(.form, .field) input[type='file'],
|
||||
:is(.form, .field) input[type='tel'],
|
||||
:is(.form, .field) input[type='url'],
|
||||
:is(.form, .field) input[type='search']:not(:is(.select [data-popover] > header input[role='combobox'], .command > header input, .dialog > * > header input)),
|
||||
:is(.form, .field) input[type='date'],
|
||||
:is(.form, .field) input[type='datetime-local'],
|
||||
:is(.form, .field) input[type='month'],
|
||||
:is(.form, .field) input[type='week'],
|
||||
:is(.form, .field) input[type='time'],
|
||||
.field > input[type='text']:not(:is(.command > header input, .dialog > * > header input)),
|
||||
.field > input[type='email'],
|
||||
.field > input[type='password'],
|
||||
.field > input[type='number'],
|
||||
.field > input[type='file'],
|
||||
.field > input[type='tel'],
|
||||
.field > input[type='url'],
|
||||
.field > input[type='search']:not(:is(.command > header input, .dialog > * > header input)),
|
||||
.field > input[type='date'],
|
||||
.field > input[type='datetime-local'],
|
||||
.field > input[type='month'],
|
||||
.field > input[type='week'],
|
||||
.field > input[type='time'],
|
||||
.input[type='text'],
|
||||
.input[type='email'],
|
||||
.input[type='password'],
|
||||
@@ -25,10 +25,8 @@
|
||||
.input[type='datetime-local'],
|
||||
.input[type='month'],
|
||||
.input[type='week'],
|
||||
.input[type='time'] {
|
||||
@apply appearance-none file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm;
|
||||
@apply focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px];
|
||||
@apply aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive;
|
||||
.input[type='time'],
|
||||
.input:not([type]) {
|
||||
@apply w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/* Item */
|
||||
@layer components {
|
||||
.item-group {
|
||||
@apply flex w-full flex-col;
|
||||
}
|
||||
|
||||
.item-group > hr {
|
||||
@apply w-full shrink-0 border-0 border-t;
|
||||
}
|
||||
|
||||
.item {
|
||||
@apply flex w-full flex-wrap items-center border transition-colors duration-100 outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50;
|
||||
}
|
||||
|
||||
a.item {
|
||||
@apply hover:bg-muted;
|
||||
}
|
||||
|
||||
.item > figure {
|
||||
@apply flex shrink-0 items-center justify-center [&_svg]:pointer-events-none;
|
||||
}
|
||||
|
||||
.item > figure:has(img) {
|
||||
@apply overflow-hidden [&_img]:size-full [&_img]:object-cover;
|
||||
}
|
||||
|
||||
.item > section {
|
||||
@apply flex flex-1 flex-col;
|
||||
}
|
||||
|
||||
.item > section + section {
|
||||
@apply flex-none;
|
||||
}
|
||||
|
||||
.item > aside {
|
||||
@apply flex items-center;
|
||||
}
|
||||
|
||||
.item > :is(header, footer) {
|
||||
@apply flex basis-full items-center justify-between;
|
||||
}
|
||||
|
||||
.item :is(h2, h3, h4) {
|
||||
@apply line-clamp-1 flex w-fit items-center;
|
||||
}
|
||||
|
||||
.item p {
|
||||
@apply text-muted-foreground line-clamp-2 text-start font-normal [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
/* Kbd */
|
||||
@layer components {
|
||||
.kbd {
|
||||
@apply bg-muted text-muted-foreground pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm px-1 font-sans text-xs font-medium select-none [&_svg:not([class*='size-'])]:size-3;
|
||||
@apply pointer-events-none inline-flex items-center justify-center select-none;
|
||||
}
|
||||
|
||||
.kbd-group {
|
||||
@apply inline-flex items-center;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/* Label */
|
||||
@layer components {
|
||||
:is(.form, .field) label,
|
||||
.field > label,
|
||||
.label {
|
||||
@apply flex items-center gap-2 text-sm leading-none font-medium select-none peer-disabled:pointer-events-none peer-disabled:opacity-50;
|
||||
@apply flex items-center select-none peer-disabled:pointer-events-none;
|
||||
|
||||
&:has(>*:disabled),
|
||||
&:has(+*:disabled) {
|
||||
@apply opacity-50 pointer-events-none;
|
||||
}
|
||||
&:has(>*:disabled),
|
||||
&:has(+*:disabled) {
|
||||
@apply pointer-events-none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/* Native Select */
|
||||
@layer components {
|
||||
:is(.form, .field) select,
|
||||
.field > select,
|
||||
select.select {
|
||||
@apply appearance-none border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent pl-3 pr-9 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 h-9;
|
||||
@apply bg-[image:var(--chevron-down-icon-50)] bg-no-repeat bg-position-[center_right_0.75rem] bg-size-[1rem];
|
||||
@apply min-w-0 appearance-none outline-none select-none disabled:cursor-not-allowed disabled:opacity-50;
|
||||
|
||||
option,
|
||||
optgroup {
|
||||
@apply bg-popover text-popover-foreground;
|
||||
@apply bg-[Canvas] text-[CanvasText];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +1,81 @@
|
||||
/* Popover */
|
||||
@layer components {
|
||||
[data-popover] {
|
||||
@apply absolute bg-popover text-popover-foreground overflow-x-hidden overflow-y-auto rounded-md border shadow-md z-50 visible opacity-100 scale-100 min-w-full w-max transition-all;
|
||||
@apply absolute z-50 min-w-full w-max overflow-x-hidden overflow-y-auto visible opacity-100 scale-100 transition-all outline-hidden;
|
||||
|
||||
&[aria-hidden='true'] {
|
||||
@apply invisible opacity-0 scale-95;
|
||||
|
||||
|
||||
&:not([data-side]),
|
||||
&[data-side='bottom'] {
|
||||
@apply -translate-y-2;
|
||||
}
|
||||
|
||||
&[data-side='top'] {
|
||||
@apply translate-y-2;
|
||||
}
|
||||
|
||||
&[data-side='left'] {
|
||||
@apply translate-x-2;
|
||||
}
|
||||
|
||||
&[data-side='right'] {
|
||||
@apply -translate-x-2;
|
||||
}
|
||||
}
|
||||
|
||||
&:not([data-side]),
|
||||
&[data-side='bottom'] {
|
||||
@apply mt-1 top-full;
|
||||
}
|
||||
|
||||
&[data-side='top'] {
|
||||
@apply mb-1 bottom-full;
|
||||
}
|
||||
|
||||
&[data-side='left'] {
|
||||
@apply mr-1 right-full;
|
||||
@apply me-1 end-full;
|
||||
}
|
||||
|
||||
&[data-side='right'] {
|
||||
@apply ml-1 left-full;
|
||||
@apply ms-1 start-full;
|
||||
}
|
||||
|
||||
&:not([data-side]),
|
||||
&[data-side='bottom'],
|
||||
&[data-side='top'] {
|
||||
&:not([data-align]),
|
||||
&[data-align='start'] {
|
||||
@apply left-0;
|
||||
@apply start-0;
|
||||
}
|
||||
|
||||
&[data-align='end'] {
|
||||
@apply right-0;
|
||||
@apply end-0;
|
||||
}
|
||||
|
||||
&[data-align='center'] {
|
||||
@apply left-1/2 -translate-x-1/2;
|
||||
@apply start-1/2 -translate-x-1/2;
|
||||
}
|
||||
}
|
||||
|
||||
&[data-side='left'],
|
||||
&[data-side='right'] {
|
||||
&:not([data-align]),
|
||||
&[data-align='start'] {
|
||||
@apply top-0;
|
||||
}
|
||||
|
||||
&[data-align='end'] {
|
||||
@apply bottom-0;
|
||||
}
|
||||
|
||||
&[data-align='center'] {
|
||||
@apply top-1/2 -translate-y-1/2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.popover {
|
||||
@apply relative inline-flex;
|
||||
|
||||
[data-popover] {
|
||||
@apply p-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
/* Radio */
|
||||
@layer components {
|
||||
:is(.form, .field) input[type='radio'],
|
||||
.field > input[type='radio'],
|
||||
.input[type='radio'] {
|
||||
@apply appearance-none border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 relative;
|
||||
@apply relative shrink-0 appearance-none outline-none disabled:cursor-not-allowed disabled:opacity-50;
|
||||
|
||||
&:checked:before {
|
||||
@apply absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 content-[''] rounded-full size-2 bg-primary;
|
||||
@apply absolute top-1/2 left-1/2 content-[''] -translate-x-1/2 -translate-y-1/2 rounded-full;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,49 +1,50 @@
|
||||
/* Range */
|
||||
@layer components {
|
||||
:is(.form, .field) input[type='range'],
|
||||
.field > input[type='range'],
|
||||
.input[type='range'] {
|
||||
@apply appearance-none flex items-center p-0 outline-none;
|
||||
@apply flex appearance-none items-center p-0 outline-none disabled:pointer-events-none disabled:opacity-50;
|
||||
--slider-value: 20%;
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
&::-webkit-slider-thumb {
|
||||
@apply ring-4;
|
||||
}
|
||||
&::-moz-range-thumb {
|
||||
@apply ring-4;
|
||||
}
|
||||
&::-ms-thumb {
|
||||
@apply ring-4;
|
||||
}
|
||||
&::-webkit-slider-thumb {
|
||||
@apply block shrink-0 appearance-none;
|
||||
}
|
||||
|
||||
&::-webkit-slider-thumb {
|
||||
@apply appearance-none border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm -mt-1.25;
|
||||
}
|
||||
&::-webkit-slider-runnable-track {
|
||||
@apply appearance-none rounded-full h-1.5 w-full;
|
||||
@apply w-full appearance-none;
|
||||
background: linear-gradient(to right, var(--primary) var(--slider-value), var(--muted) var(--slider-value));
|
||||
}
|
||||
}
|
||||
|
||||
&:dir(rtl)::-webkit-slider-runnable-track {
|
||||
background: linear-gradient(to left, var(--primary) var(--slider-value), var(--muted) var(--slider-value));
|
||||
}
|
||||
|
||||
&::-moz-range-thumb {
|
||||
@apply appearance-none border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm -mt-1.25;
|
||||
@apply block shrink-0 appearance-none;
|
||||
}
|
||||
|
||||
&::-moz-range-track {
|
||||
@apply appearance-none rounded-full h-1.5 w-full;
|
||||
@apply w-full appearance-none;
|
||||
background: linear-gradient(to right, var(--primary) var(--slider-value), var(--muted) var(--slider-value));
|
||||
}
|
||||
|
||||
&:dir(rtl)::-moz-range-track {
|
||||
background: linear-gradient(to left, var(--primary) var(--slider-value), var(--muted) var(--slider-value));
|
||||
}
|
||||
|
||||
&::-ms-thumb {
|
||||
@apply appearance-none border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm -mt-1.25;
|
||||
@apply block shrink-0 appearance-none;
|
||||
}
|
||||
|
||||
&::-ms-track {
|
||||
@apply appearance-none rounded-full h-1.5 w-full;
|
||||
@apply w-full appearance-none;
|
||||
}
|
||||
|
||||
&::-ms-fill-lower {
|
||||
@apply bg-primary rounded-full;
|
||||
@apply bg-primary;
|
||||
}
|
||||
|
||||
&::-ms-fill-upper {
|
||||
@apply bg-muted rounded-full;
|
||||
@apply bg-muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,63 +2,63 @@
|
||||
@layer components {
|
||||
*:not(select).select {
|
||||
@apply relative inline-flex;
|
||||
|
||||
& > button {
|
||||
@apply justify-between font-normal;
|
||||
}
|
||||
|
||||
&[aria-invalid='true'] {
|
||||
@apply ring-destructive/20 dark:ring-destructive/40 border-destructive;
|
||||
}
|
||||
}
|
||||
*:not(select).select > button {
|
||||
@apply flex w-fit items-center justify-between whitespace-nowrap font-normal outline-hidden disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0;
|
||||
}
|
||||
|
||||
[data-popover] {
|
||||
@apply p-1;
|
||||
*:not(select).select > button > span {
|
||||
@apply flex flex-1 truncate text-start;
|
||||
}
|
||||
|
||||
[role='option'] {
|
||||
@apply aria-hidden:hidden [&_svg]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm pl-2 py-1.5 pr-7.5 text-sm outline-hidden select-none [&_svg]:shrink-0 [&_svg]:size-4 aria-disabled:opacity-50 aria-disabled:pointer-events-none disabled:opacity-50 disabled:pointer-events-none w-full truncate;
|
||||
|
||||
&[aria-selected='true'] {
|
||||
@apply bg-[image:var(--check-icon)] bg-no-repeat bg-position-[center_right_0.5rem] bg-size-[0.875rem];
|
||||
}
|
||||
&.active,
|
||||
&:focus-visible {
|
||||
@apply bg-accent text-accent-foreground;
|
||||
}
|
||||
}
|
||||
[role='listbox'] [role='heading'] {
|
||||
@apply flex text-muted-foreground px-2 py-1.5 text-xs;
|
||||
}
|
||||
[role='listbox'] [role='group']:not(:has([role='option']:not([aria-hidden='true']))) {
|
||||
@apply hidden;
|
||||
}
|
||||
[role='separator'] {
|
||||
@apply border-border -mx-1 my-1;
|
||||
}
|
||||
> header {
|
||||
@apply flex h-9 items-center gap-2 border-b px-3 -mx-1 -mt-1 mb-1;
|
||||
*:not(select).select > button > svg {
|
||||
@apply shrink-0;
|
||||
}
|
||||
|
||||
svg {
|
||||
@apply size-4 shrink-0 opacity-50;
|
||||
}
|
||||
input[role='combobox'] {
|
||||
@apply placeholder:text-muted-foreground flex h-10 flex-1 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50 min-w-0;
|
||||
}
|
||||
}
|
||||
[role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@apply flex items-center justify-center p-6 text-sm truncate;
|
||||
}
|
||||
[role='listbox'][data-empty]:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@apply content-[attr(data-empty)];
|
||||
}
|
||||
[role='listbox']:not([data-empty]):not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@apply content-['No_results_found'];
|
||||
}
|
||||
}
|
||||
[data-popover]:has(> header input:not(:placeholder-shown)) [role='separator'] {
|
||||
@apply hidden;
|
||||
}
|
||||
&:not([data-select-initialized]) [data-popover] [role='option'] {
|
||||
@apply hover:bg-accent hover:text-accent-foreground;
|
||||
}
|
||||
*:not(select).select [data-popover] {
|
||||
@apply isolate overflow-x-hidden;
|
||||
min-width: max(100%, 9rem);
|
||||
}
|
||||
|
||||
*:not(select).select [role='listbox'] {
|
||||
@apply outline-hidden;
|
||||
}
|
||||
|
||||
*:not(select).select [role='listbox'] [role='group']:not(:has([role='option']:not([aria-hidden='true']))) {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
*:not(select).select [role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@apply flex items-center justify-center truncate;
|
||||
}
|
||||
|
||||
*:not(select).select [role='listbox'][data-empty]:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@apply content-[attr(data-empty)];
|
||||
}
|
||||
|
||||
*:not(select).select [role='listbox']:not([data-empty]):not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@apply content-['No_results_found'];
|
||||
}
|
||||
|
||||
*:not(select).select [role='option'] {
|
||||
@apply aria-hidden:hidden relative flex w-full cursor-default items-center truncate outline-hidden select-none aria-disabled:pointer-events-none aria-disabled:opacity-50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0;
|
||||
}
|
||||
|
||||
*:not(select).select [role='option'][aria-selected='true'] {
|
||||
@apply bg-[image:var(--check-icon)] bg-no-repeat bg-position-[center_right_0.5rem] bg-size-[0.875rem];
|
||||
}
|
||||
|
||||
*:not(select).select [role='option'][aria-selected='true']:dir(rtl) {
|
||||
@apply bg-position-[center_left_0.5rem];
|
||||
}
|
||||
|
||||
*:not(select).select [role='separator'] {
|
||||
@apply border-0;
|
||||
}
|
||||
|
||||
*:not(select).select:not([data-select-initialized]) [role='option']:not([aria-disabled='true']):not(:disabled):hover,
|
||||
*:not(select).select [role='option'].active {
|
||||
@apply bg-accent text-accent-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
@apply max-md:bg-black/50 max-md:fixed max-md:inset-0 max-md:z-40;
|
||||
}
|
||||
nav {
|
||||
@apply bg-sidebar text-sidebar-foreground flex flex-col w-(--sidebar-mobile-width) md:w-(--sidebar-width) fixed inset-y-0 z-50 transition-transform ease-in-out duration-300;
|
||||
@apply fixed inset-y-0 z-50 flex w-(--sidebar-mobile-width) flex-col transition-transform duration-300 ease-in-out md:w-(--sidebar-width);
|
||||
}
|
||||
& + * {
|
||||
@apply transition-[margin] ease-in-out duration-300;
|
||||
@@ -50,73 +50,52 @@
|
||||
nav {
|
||||
> header,
|
||||
> footer {
|
||||
@apply flex flex-col gap-2 p-2;
|
||||
@apply flex flex-col;
|
||||
}
|
||||
[role=separator] {
|
||||
@apply border-sidebar-border mx-2 w-auto;
|
||||
@apply w-auto;
|
||||
}
|
||||
> section {
|
||||
@apply flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto;
|
||||
@apply flex min-h-0 flex-1 flex-col overflow-y-auto;
|
||||
|
||||
> [role=group] {
|
||||
@apply relative flex w-full min-w-0 flex-col p-2;
|
||||
@apply relative flex w-full min-w-0 flex-col;
|
||||
}
|
||||
h3 {
|
||||
@apply text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0;
|
||||
@apply flex shrink-0 items-center outline-hidden transition-[margin,opacity] duration-200 ease-linear [&>svg]:shrink-0;
|
||||
}
|
||||
ul {
|
||||
@apply flex w-full min-w-0 flex-col gap-1;
|
||||
@apply flex w-full min-w-0 flex-col;
|
||||
|
||||
li {
|
||||
@apply relative;
|
||||
|
||||
> a,
|
||||
> button,
|
||||
> details > summary {
|
||||
@apply flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&[aria-current=page]]:bg-sidebar-accent [&[aria-current=page]]:font-medium [&[aria-current=page]]:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0;
|
||||
|
||||
&:not([data-variant]),
|
||||
&[data-variant=default] {
|
||||
@apply hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
|
||||
}
|
||||
&[data-variant=outline] {
|
||||
@apply bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))];
|
||||
}
|
||||
&:not([data-size]),
|
||||
&[data-size=default] {
|
||||
@apply h-8 text-sm;
|
||||
}
|
||||
&[data-size=sm] {
|
||||
@apply h-7 text-xs;
|
||||
}
|
||||
&[data-size=lg] {
|
||||
@apply h-12 text-sm group-data-[collapsible=icon]:p-0!;
|
||||
}
|
||||
@apply flex w-full items-center overflow-hidden text-left outline-hidden transition-[width,height,padding] disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:shrink-0;
|
||||
}
|
||||
> details {
|
||||
&:not([open]) {
|
||||
> summary {
|
||||
&::after {
|
||||
@apply -rotate-90;
|
||||
@apply -rotate-90 rtl:rotate-90;
|
||||
}
|
||||
}
|
||||
}
|
||||
> summary {
|
||||
&::after {
|
||||
@apply content-[''] block size-3.5 bg-primary ml-auto transition-transform ease-linear;
|
||||
@apply ms-auto block size-3.5 bg-current content-[''] transition-transform ease-linear;
|
||||
@apply mask-[image:var(--chevron-down-icon)] mask-size-[1rem] mask-no-repeat mask-center;
|
||||
}
|
||||
}
|
||||
&::details-content {
|
||||
@apply px-3.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
ul {
|
||||
@apply border-sidebar-border flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5 w-full;
|
||||
@apply flex min-w-0 flex-col;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
/* Skeleton */
|
||||
@layer components {
|
||||
.skeleton {
|
||||
@apply animate-pulse;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,8 @@
|
||||
/* Switch */
|
||||
@layer components {
|
||||
:is(.form, .field) input[type='checkbox'][role='switch'],
|
||||
.field > input[type='checkbox'][role='switch'],
|
||||
.input[type='checkbox'][role='switch'] {
|
||||
@apply appearance-none focus-visible:border-ring focus-visible:ring-ring/50 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50;
|
||||
@apply bg-input dark:bg-input/80 checked:bg-primary dark:checked:bg-primary;
|
||||
@apply before:content-[''] before:pointer-events-none before:block before:size-4 before:rounded-full before:ring-0 before:transition-all;
|
||||
@apply before:bg-background dark:before:bg-foreground;
|
||||
@apply dark:checked:before:bg-primary-foreground checked:before:ms-3.5;
|
||||
@apply inline-flex shrink-0 appearance-none items-center transition-all outline-none disabled:cursor-not-allowed disabled:opacity-50;
|
||||
@apply before:pointer-events-none before:block before:content-[''] before:ring-0 before:transition-transform;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,28 +1,34 @@
|
||||
/* Tables */
|
||||
@layer components {
|
||||
.table-container {
|
||||
@apply relative w-full overflow-x-auto;
|
||||
}
|
||||
|
||||
.table {
|
||||
@apply w-full caption-bottom text-sm;
|
||||
@apply w-full caption-bottom;
|
||||
|
||||
thead {
|
||||
@apply [&_tr]:border-b;
|
||||
}
|
||||
|
||||
tbody {
|
||||
@apply [&_tr:last-child]:border-0;
|
||||
}
|
||||
|
||||
tfoot {
|
||||
@apply bg-muted/50 border-t font-medium [&>tr]:last:border-b-0;
|
||||
@apply border-t font-medium [&>tr]:last:border-b-0;
|
||||
}
|
||||
|
||||
tr {
|
||||
@apply hover:bg-muted/50 border-b transition-colors;
|
||||
@apply border-b transition-colors data-[state=selected]:bg-muted;
|
||||
}
|
||||
|
||||
th {
|
||||
@apply text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px];
|
||||
@apply align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pe-0 [&>[role=checkbox]]:translate-y-[2px];
|
||||
}
|
||||
|
||||
td {
|
||||
@apply p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px];
|
||||
}
|
||||
caption {
|
||||
@apply text-muted-foreground mt-4 text-sm;
|
||||
@apply align-middle whitespace-nowrap [&:has([role=checkbox])]:pe-0 [&>[role=checkbox]]:translate-y-[2px];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
-11
@@ -1,22 +1,31 @@
|
||||
/* Tabs */
|
||||
@layer components {
|
||||
.tabs {
|
||||
@apply flex flex-col gap-2;
|
||||
|
||||
[role='tablist'] {
|
||||
@apply bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px];
|
||||
@apply flex;
|
||||
|
||||
[role='tab'] {
|
||||
@apply focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring text-foreground dark:text-muted-foreground inline-flex h-[calc(100%_-_1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4;
|
||||
&:has(> [role='tablist'][aria-orientation='horizontal']),
|
||||
&:not(:has(> [role='tablist'][aria-orientation='vertical'])) {
|
||||
@apply flex-col;
|
||||
}
|
||||
|
||||
&[aria-selected='true'] {
|
||||
@apply bg-background dark:text-foreground dark:border-input dark:bg-input/30 shadow-sm;
|
||||
}
|
||||
&:has(> [role='tablist'][aria-orientation='vertical']) {
|
||||
@apply flex-row;
|
||||
}
|
||||
|
||||
> [role='tablist'] {
|
||||
@apply text-muted-foreground inline-flex w-fit items-center justify-center;
|
||||
|
||||
&[aria-orientation='vertical'] {
|
||||
@apply h-fit flex-col;
|
||||
}
|
||||
|
||||
> [role='tab'] {
|
||||
@apply relative inline-flex flex-1 items-center justify-center whitespace-nowrap transition-all outline-none disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0;
|
||||
}
|
||||
}
|
||||
[role='tabpanel'] {
|
||||
|
||||
> [role='tabpanel'] {
|
||||
@apply flex-1 outline-none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/* Textarea */
|
||||
@layer components {
|
||||
:is(.form, .field) textarea,
|
||||
.field > textarea,
|
||||
.textarea {
|
||||
@apply border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm;
|
||||
@apply flex field-sizing-content min-h-16 w-full outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* Toasts */
|
||||
@layer components {
|
||||
.toaster {
|
||||
@apply fixed bottom-0 p-4 pointer-events-none z-50 w-full sm:max-w-90 flex flex-col-reverse;
|
||||
@apply pointer-events-none fixed bottom-0 z-50 flex w-full flex-col-reverse p-4 sm:max-w-90;
|
||||
|
||||
&:not([data-align]),
|
||||
&[data-align='end'] {
|
||||
@@ -14,10 +14,10 @@
|
||||
@apply left-1/2 -translate-x-1/2;
|
||||
}
|
||||
.toast {
|
||||
@apply pointer-events-auto w-full mt-4 animate-[toast-up_0.3s_ease-in-out] grid grid-rows-[1fr] transition-[grid-template-rows,opacity,margin] duration-300 ease-in-out;
|
||||
@apply pointer-events-auto mt-4 grid w-full grid-rows-[1fr] animate-[toast-up_0.3s_ease-in-out] transition-[grid-template-rows,opacity,margin] duration-300 ease-in-out;
|
||||
|
||||
.toast-content {
|
||||
@apply text-popover-foreground text-[13px] bg-popover border shadow-lg rounded-lg overflow-hidden flex gap-2.5 p-3 items-center;
|
||||
@apply flex items-center gap-2.5 overflow-hidden p-3 text-[13px];
|
||||
|
||||
svg {
|
||||
@apply size-4 shrink-0;
|
||||
@@ -31,7 +31,7 @@
|
||||
}
|
||||
}
|
||||
footer {
|
||||
@apply ml-auto flex flex-col gap-2;
|
||||
@apply ms-auto flex flex-col gap-2;
|
||||
|
||||
[data-toast-action],
|
||||
[data-toast-cancel] {
|
||||
@@ -40,7 +40,7 @@
|
||||
}
|
||||
}
|
||||
&[aria-hidden='true'] {
|
||||
@apply grid-rows-[0fr] opacity-0 m-0 border-0 p-0 overflow-hidden;
|
||||
@apply m-0 grid-rows-[0fr] overflow-hidden border-0 p-0 opacity-0;
|
||||
|
||||
.toast-content {
|
||||
@apply border-0;
|
||||
@@ -55,4 +55,3 @@
|
||||
transform: translateY(100%);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,21 +4,26 @@
|
||||
@apply relative;
|
||||
|
||||
&:before {
|
||||
@apply absolute content-[attr(data-tooltip)] bg-foreground text-background z-[60] truncate max-w-xs w-fit rounded-md px-3 py-1.5 text-xs invisible opacity-0 scale-95 transition-all pointer-events-none;
|
||||
@apply absolute z-[60] w-fit max-w-xs origin-(--transform-origin) scale-95 truncate invisible opacity-0 transition-all pointer-events-none content-[attr(data-tooltip)];
|
||||
}
|
||||
|
||||
&:hover:before {
|
||||
@apply visible opacity-100 scale-100;
|
||||
}
|
||||
|
||||
&:focus-visible:not(:hover):before {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
&:not([data-side]),
|
||||
&[data-side='top'] {
|
||||
@apply before:bottom-full before:mb-1.5 before:translate-y-2 hover:before:translate-y-0 ;
|
||||
@apply before:bottom-full before:mb-1.5 before:translate-y-2 hover:before:translate-y-0;
|
||||
}
|
||||
|
||||
&[data-side='bottom'] {
|
||||
@apply before:top-full before:mt-1.5 before:-translate-y-2 hover:before:translate-y-0 ;
|
||||
@apply before:top-full before:mt-1.5 before:-translate-y-2 hover:before:translate-y-0;
|
||||
}
|
||||
|
||||
&:not([data-side]),
|
||||
&[data-side='top'],
|
||||
&[data-side='bottom'] {
|
||||
@@ -30,17 +35,38 @@
|
||||
}
|
||||
&:not([data-align]),
|
||||
&[data-align='center'] {
|
||||
@apply before:left-1/2 before:-translate-x-1/2;
|
||||
@apply before:left-1/2 before:-translate-x-1/2;
|
||||
}
|
||||
}
|
||||
|
||||
&[data-side='left'] {
|
||||
@apply before:right-full before:mr-1.5 before:translate-x-2 hover:before:translate-x-0;
|
||||
}
|
||||
|
||||
&[data-side='right'] {
|
||||
@apply before:left-full before:ml-1.5 before:-translate-x-2 hover:before:translate-x-0;
|
||||
}
|
||||
|
||||
&[data-side='inline-start'] {
|
||||
@apply before:right-full before:mr-1.5 before:translate-x-2 hover:before:translate-x-0;
|
||||
}
|
||||
|
||||
&[data-side='inline-start']:dir(rtl) {
|
||||
@apply before:right-auto before:left-full before:mr-0 before:ml-1.5 before:-translate-x-2 hover:before:translate-x-0;
|
||||
}
|
||||
|
||||
&[data-side='inline-end'] {
|
||||
@apply before:left-full before:ml-1.5 before:-translate-x-2 hover:before:translate-x-0;
|
||||
}
|
||||
|
||||
&[data-side='inline-end']:dir(rtl) {
|
||||
@apply before:left-auto before:right-full before:ml-0 before:mr-1.5 before:translate-x-2 hover:before:translate-x-0;
|
||||
}
|
||||
|
||||
&[data-side='left'],
|
||||
&[data-side='right'] {
|
||||
&[data-side='right'],
|
||||
&[data-side='inline-start'],
|
||||
&[data-side='inline-end'] {
|
||||
&[data-align='start'] {
|
||||
@apply before:top-0;
|
||||
}
|
||||
|
||||
+1066
-16
File diff suppressed because it is too large
Load Diff
+1047
-16
File diff suppressed because it is too large
Load Diff
+1054
-16
File diff suppressed because it is too large
Load Diff
+1055
-16
File diff suppressed because it is too large
Load Diff
+1051
-16
File diff suppressed because it is too large
Load Diff
+1063
-16
File diff suppressed because it is too large
Load Diff
+1074
-16
File diff suppressed because it is too large
Load Diff
+1051
-16
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,102 @@
|
||||
{#
|
||||
Renders an input-first combobox component.
|
||||
#}
|
||||
{% macro combobox(
|
||||
id=None,
|
||||
selected=None,
|
||||
name=None,
|
||||
multiple=false,
|
||||
placeholder=None,
|
||||
close_on_select=false,
|
||||
auto_highlight=false,
|
||||
main_attrs={},
|
||||
input_attrs={},
|
||||
popover_attrs={},
|
||||
listbox_attrs={},
|
||||
hidden_input_attrs={}
|
||||
) %}
|
||||
{% set id = id or ("combobox-" + (range(100000, 999999) | random | string)) %}
|
||||
{% if selected is defined and selected is iterable and selected is not string %}
|
||||
{% set selected_values = selected %}
|
||||
{% elif selected is defined and selected %}
|
||||
{% set selected_values = [selected] %}
|
||||
{% else %}
|
||||
{% set selected_values = [] %}
|
||||
{% endif %}
|
||||
{% set hidden_value = (selected_values | tojson if multiple else (selected_values[0] if selected_values else '')) %}
|
||||
|
||||
<div
|
||||
id="{{ id }}"
|
||||
class="combobox {{ main_attrs.class }}"
|
||||
{% if multiple %}data-multiple="true"{% endif %}
|
||||
{% if multiple and close_on_select %}data-close-on-select="true"{% endif %}
|
||||
{% if auto_highlight %}data-auto-highlight="true"{% endif %}
|
||||
{% for key, value in main_attrs.items() %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{% if multiple %}
|
||||
<div class="combobox-chips {{ input_attrs.class }}">
|
||||
<input
|
||||
type="text"
|
||||
role="combobox"
|
||||
placeholder="{{ placeholder or '' }}"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
aria-autocomplete="list"
|
||||
aria-expanded="false"
|
||||
aria-controls="{{ id }}-listbox"
|
||||
{% for key, value in input_attrs.items() %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
</div>
|
||||
{% else %}
|
||||
<input
|
||||
type="text"
|
||||
role="combobox"
|
||||
placeholder="{{ placeholder or '' }}"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
aria-autocomplete="list"
|
||||
aria-expanded="false"
|
||||
aria-controls="{{ id }}-listbox"
|
||||
class="{{ input_attrs.class }}"
|
||||
{% for key, value in input_attrs.items() %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
<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="combobox-trigger-icon"><path d="m6 9 6 6 6-6"/></svg>
|
||||
{% endif %}
|
||||
<div
|
||||
id="{{ id }}-popover"
|
||||
data-popover
|
||||
aria-hidden="true"
|
||||
{% for key, value in popover_attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
<div
|
||||
role="listbox"
|
||||
id="{{ id }}-listbox"
|
||||
aria-orientation="vertical"
|
||||
{% if multiple %}aria-multiselectable="true"{% endif %}
|
||||
{% for key, value in listbox_attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ caller() if caller else '' }}
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
type="hidden"
|
||||
{% if name %}name="{{ name }}"{% endif %}
|
||||
value='{{ hidden_value }}'
|
||||
{% for key, value in hidden_input_attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
@@ -89,7 +89,7 @@
|
||||
{% set id = id or ("command-dialog-" ~ range(100000, 999999) | random | string) %}
|
||||
<dialog
|
||||
id="{{ id }}"
|
||||
class="dialog command-dialog {{ dialog_attrs.class }}"
|
||||
class="command-dialog {{ dialog_attrs.class }}"
|
||||
aria-label="Command menu"
|
||||
{% if open %}open{% endif %}
|
||||
onclick="if (event.target === this) this.close()"
|
||||
@@ -132,7 +132,7 @@
|
||||
{{ caller() if caller }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<button type="button" aria-label="Close dialog" onclick="this.closest('dialog').close()">
|
||||
<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>
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
</footer>
|
||||
{% endif %}
|
||||
{% if close_button %}
|
||||
<button type="button" aria-label="Close dialog" onclick="this.closest('dialog').close()">
|
||||
<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>
|
||||
{% endif %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{#
|
||||
Renders a select or combobox component.
|
||||
Renders a select component.
|
||||
|
||||
@param id {string} [optional] - Unique identifier for the select component.
|
||||
@param selected {string|array} [optional] - The initially selected value(s).
|
||||
@@ -12,8 +12,6 @@
|
||||
@param popover_attrs {object} [optional] - Additional HTML attributes for the popover content div.
|
||||
@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.
|
||||
#}
|
||||
{% macro select(
|
||||
id=None,
|
||||
@@ -27,9 +25,7 @@
|
||||
trigger_attrs={},
|
||||
popover_attrs={},
|
||||
listbox_attrs={},
|
||||
input_attrs={},
|
||||
search_placeholder="Search entries...",
|
||||
is_combobox=false
|
||||
input_attrs={}
|
||||
) %}
|
||||
{% set id = id or ("select-" + (range(100000, 999999) | random | string)) %}
|
||||
|
||||
@@ -85,7 +81,7 @@
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-outline {{ trigger_attrs.class }}"
|
||||
class="{{ trigger_attrs.class }}"
|
||||
id="{{ id }}-trigger"
|
||||
aria-haspopup="listbox"
|
||||
aria-expanded="false"
|
||||
@@ -95,11 +91,7 @@
|
||||
{% endfor %}
|
||||
>
|
||||
<span class="truncate">{{ default_label }}</span>
|
||||
{% if is_combobox %}
|
||||
<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-chevrons-up-down-icon lucide-chevrons-up-down text-muted-foreground opacity-50 shrink-0"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>
|
||||
{% else %}
|
||||
<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-chevron-down-icon lucide-chevron-down text-muted-foreground opacity-50 shrink-0"><path d="m6 9 6 6 6-6"/></svg>
|
||||
{% endif %}
|
||||
</button>
|
||||
<div
|
||||
id="{{ id }}-popover"
|
||||
@@ -109,24 +101,6 @@
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{% if is_combobox %}
|
||||
<header>
|
||||
<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-search-icon lucide-search"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
||||
<input
|
||||
type="text"
|
||||
value=""
|
||||
placeholder="{{ search_placeholder }}"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
aria-autocomplete="list"
|
||||
role="combobox"
|
||||
aria-expanded="false"
|
||||
aria-controls="{{ id }}-listbox"
|
||||
aria-labelledby="{{ id }}-trigger"
|
||||
>
|
||||
</header>
|
||||
{% endif %}
|
||||
<div
|
||||
role="listbox"
|
||||
id="{{ id }}-listbox"
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
>
|
||||
<summary aria-controls="submenu-{{ item_id }}-content">
|
||||
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
{{ item.label | safe }}
|
||||
<span>{{ item.label | safe }}</span>
|
||||
</summary>
|
||||
<ul id="submenu-{{ item_id }}-content">
|
||||
{{ render_sidebar_content(item["items"], item_id) if item["items"] }}
|
||||
@@ -141,4 +141,4 @@
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
- panel_attrs {object} [optional]: Additional HTML attributes for the tab panel div.
|
||||
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
||||
@param tablist_attrs {object} [optional] - Additional HTML attributes for the tablist nav element.
|
||||
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(
|
||||
@@ -22,6 +23,7 @@
|
||||
)
|
||||
%}
|
||||
{% 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 }}"
|
||||
@@ -31,9 +33,9 @@
|
||||
>
|
||||
<nav
|
||||
role="tablist"
|
||||
aria-orientation="horizontal"
|
||||
aria-orientation="{{ orientation }}"
|
||||
{% for key, value in tablist_attrs.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% if key != 'aria-orientation' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{% for tabset in tabsets %}
|
||||
@@ -43,7 +45,7 @@
|
||||
id="{{ id }}-tab-{{ loop.index }}"
|
||||
aria-controls="{{ id }}-panel-{{ loop.index }}"
|
||||
aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}"
|
||||
tabindex="0"
|
||||
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.items() %}
|
||||
{{ key }}="{{ value }}"
|
||||
@@ -75,4 +77,4 @@
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
||||
@@ -0,0 +1,323 @@
|
||||
(() => {
|
||||
const initCombobox = (combobox) => {
|
||||
const input = combobox.querySelector(':scope > input[role="combobox"], :scope > .combobox-chips input[role="combobox"]');
|
||||
const chips = combobox.querySelector(':scope > .combobox-chips');
|
||||
const popover = combobox.querySelector(':scope > [data-popover]');
|
||||
const listbox = popover ? popover.querySelector('[role="listbox"]') : null;
|
||||
const hiddenInput = combobox.querySelector(':scope > input[type="hidden"]');
|
||||
|
||||
if (!input || !popover || !listbox || !hiddenInput) {
|
||||
const missing = [];
|
||||
if (!input) missing.push('input');
|
||||
if (!popover) missing.push('popover');
|
||||
if (!listbox) missing.push('listbox');
|
||||
if (!hiddenInput) missing.push('hidden input');
|
||||
console.error(`Combobox initialisation failed. Missing element(s): ${missing.join(', ')}`, combobox);
|
||||
return;
|
||||
}
|
||||
|
||||
const allOptions = Array.from(listbox.querySelectorAll('[role="option"]'));
|
||||
const options = allOptions.filter(option => option.getAttribute('aria-disabled') !== 'true');
|
||||
const isMultiple = listbox.getAttribute('aria-multiselectable') === 'true' || combobox.dataset.multiple === 'true';
|
||||
const closeOnSelect = combobox.dataset.closeOnSelect === 'true';
|
||||
const autoHighlight = combobox.dataset.autoHighlight === 'true';
|
||||
const selectedOptions = isMultiple ? new Set() : null;
|
||||
let visibleOptions = [...options];
|
||||
let activeIndex = -1;
|
||||
|
||||
const getValue = option => option.dataset.value ?? option.textContent.trim();
|
||||
const getLabel = option => option.dataset.label || option.textContent.trim();
|
||||
|
||||
const setActiveOption = (index) => {
|
||||
if (activeIndex > -1 && options[activeIndex]) {
|
||||
options[activeIndex].classList.remove('active');
|
||||
}
|
||||
|
||||
activeIndex = index;
|
||||
|
||||
if (activeIndex > -1) {
|
||||
const activeOption = options[activeIndex];
|
||||
activeOption.classList.add('active');
|
||||
if (!activeOption.id) activeOption.id = `${listbox.id || combobox.id || 'combobox'}-option-${activeIndex}`;
|
||||
input.setAttribute('aria-activedescendant', activeOption.id);
|
||||
} else {
|
||||
input.removeAttribute('aria-activedescendant');
|
||||
}
|
||||
};
|
||||
|
||||
const syncEmptyState = () => {
|
||||
popover.dataset.empty = String(visibleOptions.length === 0);
|
||||
};
|
||||
|
||||
const filterOptions = ({ preserveActive = false } = {}) => {
|
||||
const previousActive = activeIndex > -1 ? options[activeIndex] : null;
|
||||
const search = input.value.trim().toLowerCase();
|
||||
visibleOptions = [];
|
||||
|
||||
allOptions.forEach(option => {
|
||||
if (option.hasAttribute('data-force')) {
|
||||
option.setAttribute('aria-hidden', 'false');
|
||||
if (options.includes(option)) visibleOptions.push(option);
|
||||
return;
|
||||
}
|
||||
|
||||
const optionText = (option.dataset.filter || option.dataset.label || option.textContent).trim().toLowerCase();
|
||||
const keywords = (option.dataset.keywords || '').toLowerCase().split(/[\s,]+/).filter(Boolean);
|
||||
const matches = !search || optionText.includes(search) || keywords.some(keyword => keyword.includes(search));
|
||||
option.setAttribute('aria-hidden', String(!matches));
|
||||
if (matches && options.includes(option)) visibleOptions.push(option);
|
||||
});
|
||||
|
||||
if (preserveActive && previousActive && visibleOptions.includes(previousActive)) {
|
||||
setActiveOption(options.indexOf(previousActive));
|
||||
} else {
|
||||
setActiveOption(autoHighlight && visibleOptions.length > 0 ? options.indexOf(visibleOptions[0]) : -1);
|
||||
}
|
||||
syncEmptyState();
|
||||
};
|
||||
|
||||
const openPopover = () => {
|
||||
if (popover.getAttribute('aria-hidden') === 'false') return;
|
||||
|
||||
document.dispatchEvent(new CustomEvent('basecoat:popover', {
|
||||
detail: { source: combobox }
|
||||
}));
|
||||
|
||||
filterOptions();
|
||||
popover.setAttribute('aria-hidden', 'false');
|
||||
input.setAttribute('aria-expanded', 'true');
|
||||
};
|
||||
|
||||
const closePopover = (focusInput = false) => {
|
||||
if (popover.getAttribute('aria-hidden') === 'true') return;
|
||||
popover.setAttribute('aria-hidden', 'true');
|
||||
input.setAttribute('aria-expanded', 'false');
|
||||
setActiveOption(-1);
|
||||
if (focusInput) input.focus();
|
||||
};
|
||||
|
||||
const renderChips = () => {
|
||||
if (!chips) return;
|
||||
chips.querySelectorAll('.combobox-chip').forEach(chip => chip.remove());
|
||||
const selected = options.filter(option => selectedOptions.has(option));
|
||||
|
||||
selected.forEach(option => {
|
||||
const chip = document.createElement('span');
|
||||
chip.className = 'combobox-chip';
|
||||
chip.dataset.value = getValue(option);
|
||||
chip.textContent = getLabel(option);
|
||||
|
||||
const remove = document.createElement('button');
|
||||
remove.type = 'button';
|
||||
remove.className = 'combobox-chip-remove';
|
||||
remove.setAttribute('aria-label', `Remove ${getLabel(option)}`);
|
||||
remove.innerHTML = '<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"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>';
|
||||
remove.addEventListener('click', () => deselect(getValue(option)));
|
||||
|
||||
chip.appendChild(remove);
|
||||
chips.insertBefore(chip, input);
|
||||
});
|
||||
};
|
||||
|
||||
const updateValue = (optionOrOptions, triggerEvent = true) => {
|
||||
let value;
|
||||
|
||||
if (isMultiple) {
|
||||
const selected = Array.isArray(optionOrOptions) ? optionOrOptions : [];
|
||||
selectedOptions.clear();
|
||||
selected.forEach(option => selectedOptions.add(option));
|
||||
options.forEach(option => {
|
||||
if (selectedOptions.has(option)) {
|
||||
option.setAttribute('aria-selected', 'true');
|
||||
} else {
|
||||
option.removeAttribute('aria-selected');
|
||||
}
|
||||
});
|
||||
value = options.filter(option => selectedOptions.has(option)).map(getValue);
|
||||
hiddenInput.value = JSON.stringify(value);
|
||||
input.value = '';
|
||||
renderChips();
|
||||
filterOptions({ preserveActive: true });
|
||||
} else {
|
||||
const option = optionOrOptions;
|
||||
if (!option) return;
|
||||
options.forEach(opt => {
|
||||
if (opt === option) {
|
||||
opt.setAttribute('aria-selected', 'true');
|
||||
} else {
|
||||
opt.removeAttribute('aria-selected');
|
||||
}
|
||||
});
|
||||
value = getValue(option);
|
||||
hiddenInput.value = value;
|
||||
input.value = getLabel(option);
|
||||
}
|
||||
|
||||
if (triggerEvent) {
|
||||
combobox.dispatchEvent(new CustomEvent('change', {
|
||||
detail: { value },
|
||||
bubbles: true
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const select = (value) => {
|
||||
const option = options.find(opt => getValue(opt) === value);
|
||||
if (!option) return;
|
||||
|
||||
if (isMultiple) {
|
||||
const selected = new Set(selectedOptions);
|
||||
selected.add(option);
|
||||
updateValue(options.filter(opt => selected.has(opt)));
|
||||
if (closeOnSelect) closePopover(true);
|
||||
} else {
|
||||
updateValue(option);
|
||||
closePopover(true);
|
||||
}
|
||||
};
|
||||
|
||||
const deselect = (value) => {
|
||||
if (!isMultiple) return;
|
||||
const option = options.find(opt => getValue(opt) === value);
|
||||
if (!option || !selectedOptions.has(option)) return;
|
||||
const selected = new Set(selectedOptions);
|
||||
selected.delete(option);
|
||||
updateValue(options.filter(opt => selected.has(opt)));
|
||||
};
|
||||
|
||||
const toggle = (value) => {
|
||||
if (!isMultiple) return;
|
||||
const option = options.find(opt => getValue(opt) === value);
|
||||
if (!option) return;
|
||||
selectedOptions.has(option) ? deselect(value) : select(value);
|
||||
};
|
||||
|
||||
const handleKeydown = (event) => {
|
||||
if (!['ArrowDown', 'ArrowUp', 'Enter', 'Home', 'End', 'Escape', 'Backspace'].includes(event.key)) return;
|
||||
|
||||
const isOpen = popover.getAttribute('aria-hidden') === 'false';
|
||||
|
||||
if (event.key === 'Backspace' && isMultiple && input.value === '') {
|
||||
const selected = options.filter(option => selectedOptions.has(option));
|
||||
const last = selected[selected.length - 1];
|
||||
if (last) deselect(getValue(last));
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === 'Escape') {
|
||||
closePopover(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isOpen && ['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) {
|
||||
event.preventDefault();
|
||||
openPopover();
|
||||
}
|
||||
|
||||
if (popover.getAttribute('aria-hidden') === 'true') return;
|
||||
|
||||
if (event.key === 'Enter') {
|
||||
if (activeIndex > -1) {
|
||||
event.preventDefault();
|
||||
const option = options[activeIndex];
|
||||
isMultiple ? toggle(getValue(option)) : select(getValue(option));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key) || visibleOptions.length === 0) return;
|
||||
event.preventDefault();
|
||||
|
||||
const currentVisibleIndex = activeIndex > -1 ? visibleOptions.indexOf(options[activeIndex]) : -1;
|
||||
let nextVisibleIndex = currentVisibleIndex;
|
||||
|
||||
if (event.key === 'ArrowDown') nextVisibleIndex = Math.min(currentVisibleIndex + 1, visibleOptions.length - 1);
|
||||
if (event.key === 'ArrowUp') nextVisibleIndex = currentVisibleIndex <= 0 ? 0 : currentVisibleIndex - 1;
|
||||
if (event.key === 'Home') nextVisibleIndex = 0;
|
||||
if (event.key === 'End') nextVisibleIndex = visibleOptions.length - 1;
|
||||
|
||||
const nextOption = visibleOptions[nextVisibleIndex];
|
||||
setActiveOption(options.indexOf(nextOption));
|
||||
nextOption.scrollIntoView({ block: 'nearest' });
|
||||
};
|
||||
|
||||
input.addEventListener('focus', openPopover);
|
||||
input.addEventListener('click', openPopover);
|
||||
input.addEventListener('input', () => {
|
||||
openPopover();
|
||||
filterOptions();
|
||||
if (!isMultiple) {
|
||||
hiddenInput.value = '';
|
||||
options.forEach(option => option.removeAttribute('aria-selected'));
|
||||
}
|
||||
});
|
||||
input.addEventListener('keydown', handleKeydown);
|
||||
|
||||
listbox.addEventListener('mousemove', (event) => {
|
||||
const option = event.target.closest('[role="option"]');
|
||||
if (option && visibleOptions.includes(option)) setActiveOption(options.indexOf(option));
|
||||
});
|
||||
|
||||
listbox.addEventListener('click', (event) => {
|
||||
const option = event.target.closest('[role="option"]');
|
||||
if (!option || !options.includes(option)) return;
|
||||
isMultiple ? toggle(getValue(option)) : select(getValue(option));
|
||||
if (isMultiple && !closeOnSelect) input.focus();
|
||||
});
|
||||
|
||||
document.addEventListener('click', (event) => {
|
||||
if (!combobox.contains(event.target)) closePopover(false);
|
||||
});
|
||||
|
||||
document.addEventListener('basecoat:popover', (event) => {
|
||||
if (event.detail.source !== combobox) closePopover(false);
|
||||
});
|
||||
|
||||
if (isMultiple) {
|
||||
let initialValues = [];
|
||||
try {
|
||||
initialValues = JSON.parse(hiddenInput.value || '[]');
|
||||
} catch (_) {
|
||||
initialValues = [];
|
||||
}
|
||||
if (!Array.isArray(initialValues)) initialValues = [];
|
||||
const ariaSelected = options.filter(option => option.getAttribute('aria-selected') === 'true').map(getValue);
|
||||
const values = initialValues.length ? initialValues : ariaSelected;
|
||||
updateValue(options.filter(option => values.includes(getValue(option))), false);
|
||||
} else {
|
||||
const initialOption = options.find(option => getValue(option) === hiddenInput.value || option.getAttribute('aria-selected') === 'true');
|
||||
if (initialOption) updateValue(initialOption, false);
|
||||
}
|
||||
|
||||
popover.setAttribute('aria-hidden', 'true');
|
||||
input.setAttribute('aria-expanded', 'false');
|
||||
|
||||
Object.defineProperty(combobox, 'value', {
|
||||
get: () => isMultiple ? options.filter(option => selectedOptions.has(option)).map(getValue) : hiddenInput.value,
|
||||
set: (value) => {
|
||||
if (isMultiple) {
|
||||
const values = Array.isArray(value) ? value : (value == null ? [] : [value]);
|
||||
updateValue(options.filter(option => values.includes(getValue(option))));
|
||||
} else {
|
||||
select(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
combobox.select = select;
|
||||
combobox.selectByValue = select;
|
||||
if (isMultiple) {
|
||||
combobox.deselect = deselect;
|
||||
combobox.toggle = toggle;
|
||||
combobox.selectAll = () => updateValue(options);
|
||||
combobox.selectNone = () => updateValue([]);
|
||||
}
|
||||
|
||||
combobox.dataset.comboboxInitialized = true;
|
||||
combobox.dispatchEvent(new CustomEvent('basecoat:initialized'));
|
||||
};
|
||||
|
||||
if (window.basecoat) {
|
||||
window.basecoat.register('combobox', '.combobox:not([data-combobox-initialized])', initCombobox);
|
||||
}
|
||||
})();
|
||||
+11
-8
@@ -16,6 +16,9 @@
|
||||
let menuItems = [];
|
||||
let activeIndex = -1;
|
||||
|
||||
const isDisabled = (item) =>
|
||||
item.hasAttribute('disabled') || item.getAttribute('aria-disabled') === 'true';
|
||||
|
||||
const closePopover = (focusOnTrigger = true) => {
|
||||
if (trigger.getAttribute('aria-expanded') === 'false') return;
|
||||
trigger.setAttribute('aria-expanded', 'false');
|
||||
@@ -36,10 +39,7 @@
|
||||
|
||||
trigger.setAttribute('aria-expanded', 'true');
|
||||
popover.setAttribute('aria-hidden', 'false');
|
||||
menuItems = Array.from(menu.querySelectorAll('[role^="menuitem"]')).filter(item =>
|
||||
!item.hasAttribute('disabled') &&
|
||||
item.getAttribute('aria-disabled') !== 'true'
|
||||
);
|
||||
menuItems = Array.from(menu.querySelectorAll('[role^="menuitem"]')).filter(item => !isDisabled(item));
|
||||
|
||||
if (menuItems.length > 0 && initialSelection) {
|
||||
if (initialSelection === 'first') {
|
||||
@@ -58,7 +58,9 @@
|
||||
if (activeIndex > -1 && menuItems[activeIndex]) {
|
||||
const activeItem = menuItems[activeIndex];
|
||||
activeItem.classList.add('active');
|
||||
trigger.setAttribute('aria-activedescendant', activeItem.id);
|
||||
if (activeItem.id) {
|
||||
trigger.setAttribute('aria-activedescendant', activeItem.id);
|
||||
}
|
||||
} else {
|
||||
trigger.removeAttribute('aria-activedescendant');
|
||||
}
|
||||
@@ -131,7 +133,7 @@
|
||||
|
||||
menu.addEventListener('mousemove', (event) => {
|
||||
const menuItem = event.target.closest('[role^="menuitem"]');
|
||||
if (menuItem && menuItems.includes(menuItem)) {
|
||||
if (menuItem && !isDisabled(menuItem) && menuItems.includes(menuItem)) {
|
||||
const index = menuItems.indexOf(menuItem);
|
||||
if (index !== activeIndex) {
|
||||
setActiveItem(index);
|
||||
@@ -144,7 +146,8 @@
|
||||
});
|
||||
|
||||
menu.addEventListener('click', (event) => {
|
||||
if (event.target.closest('[role^="menuitem"]')) {
|
||||
const menuItem = event.target.closest('[role^="menuitem"]');
|
||||
if (menuItem && !isDisabled(menuItem)) {
|
||||
closePopover();
|
||||
}
|
||||
});
|
||||
@@ -168,4 +171,4 @@
|
||||
if (window.basecoat) {
|
||||
window.basecoat.register('dropdown-menu', '.dropdown-menu:not([data-dropdown-menu-initialized])', initDropdownMenu);
|
||||
}
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
(() => {
|
||||
function updateRange(element) {
|
||||
const min = parseFloat(element.min || '0');
|
||||
const max = parseFloat(element.max || '100');
|
||||
const value = parseFloat(element.value || '0');
|
||||
const percent = max === min ? 0 : ((value - min) / (max - min)) * 100;
|
||||
element.style.setProperty('--slider-value', `${percent}%`);
|
||||
}
|
||||
|
||||
function initRange(element) {
|
||||
if (element.dataset.rangeInitialized) return;
|
||||
|
||||
updateRange(element);
|
||||
element.addEventListener('input', () => updateRange(element));
|
||||
element.dataset.rangeInitialized = 'true';
|
||||
}
|
||||
|
||||
if (window.basecoat) {
|
||||
window.basecoat.register('range', 'input[type="range"]:not([data-range-initialized])', initRange);
|
||||
}
|
||||
})();
|
||||
+1
-72
@@ -5,7 +5,6 @@
|
||||
const popover = selectComponent.querySelector(':scope > [data-popover]');
|
||||
const listbox = popover ? popover.querySelector('[role="listbox"]') : null;
|
||||
const input = selectComponent.querySelector(':scope > input[type="hidden"]');
|
||||
const filter = selectComponent.querySelector('header input[type="text"]');
|
||||
|
||||
if (!trigger || !popover || !listbox || !input) {
|
||||
const missing = [];
|
||||
@@ -48,11 +47,6 @@
|
||||
}
|
||||
};
|
||||
|
||||
const hasTransition = () => {
|
||||
const style = getComputedStyle(popover);
|
||||
return parseFloat(style.transitionDuration) > 0 || parseFloat(style.transitionDelay) > 0;
|
||||
};
|
||||
|
||||
const updateValue = (optionOrOptions, triggerEvent = true) => {
|
||||
let value;
|
||||
|
||||
@@ -101,20 +95,6 @@
|
||||
const closePopover = (focusOnTrigger = true) => {
|
||||
if (popover.getAttribute('aria-hidden') === 'true') return;
|
||||
|
||||
if (filter) {
|
||||
const resetFilter = () => {
|
||||
filter.value = '';
|
||||
visibleOptions = [...options];
|
||||
allOptions.forEach(opt => opt.setAttribute('aria-hidden', 'false'));
|
||||
};
|
||||
|
||||
if (hasTransition()) {
|
||||
popover.addEventListener('transitionend', resetFilter, { once: true });
|
||||
} else {
|
||||
resetFilter();
|
||||
}
|
||||
}
|
||||
|
||||
if (focusOnTrigger) trigger.focus();
|
||||
popover.setAttribute('aria-hidden', 'true');
|
||||
trigger.setAttribute('aria-expanded', 'false');
|
||||
@@ -161,39 +141,6 @@
|
||||
toggleMultipleValue(option);
|
||||
};
|
||||
|
||||
if (filter) {
|
||||
const filterOptions = () => {
|
||||
const searchTerm = filter.value.trim().toLowerCase();
|
||||
|
||||
setActiveOption(-1);
|
||||
|
||||
visibleOptions = [];
|
||||
allOptions.forEach(option => {
|
||||
if (option.hasAttribute('data-force')) {
|
||||
option.setAttribute('aria-hidden', 'false');
|
||||
if (options.includes(option)) {
|
||||
visibleOptions.push(option);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const optionText = (option.dataset.filter || option.textContent).trim().toLowerCase();
|
||||
const keywordList = (option.dataset.keywords || '')
|
||||
.toLowerCase()
|
||||
.split(/[\s,]+/)
|
||||
.filter(Boolean);
|
||||
const matchesKeyword = keywordList.some(keyword => keyword.includes(searchTerm));
|
||||
const matches = optionText.includes(searchTerm) || matchesKeyword;
|
||||
option.setAttribute('aria-hidden', String(!matches));
|
||||
if (matches && options.includes(option)) {
|
||||
visibleOptions.push(option);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
filter.addEventListener('input', filterOptions);
|
||||
}
|
||||
|
||||
// Initialization
|
||||
if (isMultiple) {
|
||||
const ariaSelected = options.filter(opt => opt.getAttribute('aria-selected') === 'true');
|
||||
@@ -315,25 +262,11 @@
|
||||
});
|
||||
|
||||
trigger.addEventListener('keydown', handleKeyNavigation);
|
||||
if (filter) {
|
||||
filter.addEventListener('keydown', handleKeyNavigation);
|
||||
}
|
||||
|
||||
const openPopover = () => {
|
||||
document.dispatchEvent(new CustomEvent('basecoat:popover', {
|
||||
detail: { source: selectComponent }
|
||||
}));
|
||||
|
||||
if (filter) {
|
||||
if (hasTransition()) {
|
||||
popover.addEventListener('transitionend', () => {
|
||||
filter.focus();
|
||||
}, { once: true });
|
||||
} else {
|
||||
filter.focus();
|
||||
}
|
||||
}
|
||||
|
||||
popover.setAttribute('aria-hidden', 'false');
|
||||
trigger.setAttribute('aria-expanded', 'true');
|
||||
|
||||
@@ -366,11 +299,7 @@
|
||||
closePopover();
|
||||
} else {
|
||||
setActiveOption(options.indexOf(option));
|
||||
if (filter) {
|
||||
filter.focus();
|
||||
} else {
|
||||
trigger.focus();
|
||||
}
|
||||
trigger.focus();
|
||||
}
|
||||
} else {
|
||||
if (input.value !== getValue(option)) {
|
||||
|
||||
+24
-6
@@ -6,7 +6,12 @@
|
||||
const tabs = Array.from(tablist.querySelectorAll('[role="tab"]'));
|
||||
const panels = tabs.map(tab => document.getElementById(tab.getAttribute('aria-controls'))).filter(Boolean);
|
||||
|
||||
const isDisabled = (tab) => tab.disabled || tab.getAttribute('aria-disabled') === 'true';
|
||||
const getEnabledTabs = () => tabs.filter(tab => !isDisabled(tab));
|
||||
|
||||
const selectTab = (tabToSelect) => {
|
||||
if (isDisabled(tabToSelect)) return;
|
||||
|
||||
tabs.forEach((tab, index) => {
|
||||
tab.setAttribute('aria-selected', 'false');
|
||||
tab.setAttribute('tabindex', '-1');
|
||||
@@ -29,20 +34,33 @@
|
||||
if (!tabs.includes(currentTab)) return;
|
||||
|
||||
let nextTab;
|
||||
const currentIndex = tabs.indexOf(currentTab);
|
||||
const enabledTabs = getEnabledTabs();
|
||||
const currentIndex = enabledTabs.indexOf(currentTab);
|
||||
const orientation = tablist.getAttribute('aria-orientation') || 'horizontal';
|
||||
if (currentIndex === -1) return;
|
||||
|
||||
switch (event.key) {
|
||||
case 'ArrowRight':
|
||||
nextTab = tabs[(currentIndex + 1) % tabs.length];
|
||||
if (orientation !== 'horizontal') return;
|
||||
nextTab = enabledTabs[(currentIndex + 1) % enabledTabs.length];
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
nextTab = tabs[(currentIndex - 1 + tabs.length) % tabs.length];
|
||||
if (orientation !== 'horizontal') return;
|
||||
nextTab = enabledTabs[(currentIndex - 1 + enabledTabs.length) % enabledTabs.length];
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
if (orientation !== 'vertical') return;
|
||||
nextTab = enabledTabs[(currentIndex + 1) % enabledTabs.length];
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
if (orientation !== 'vertical') return;
|
||||
nextTab = enabledTabs[(currentIndex - 1 + enabledTabs.length) % enabledTabs.length];
|
||||
break;
|
||||
case 'Home':
|
||||
nextTab = tabs[0];
|
||||
nextTab = enabledTabs[0];
|
||||
break;
|
||||
case 'End':
|
||||
nextTab = tabs[tabs.length - 1];
|
||||
nextTab = enabledTabs[enabledTabs.length - 1];
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@@ -60,4 +78,4 @@
|
||||
if (window.basecoat) {
|
||||
window.basecoat.register('tabs', '.tabs:not([data-tabs-initialized])', initTabs);
|
||||
}
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
{#
|
||||
Renders an input-first combobox component.
|
||||
|
||||
@param id {string} [optional] - Unique identifier for the combobox component.
|
||||
@param selected {string|array} [optional] - The initially selected value(s).
|
||||
@param name {string} [optional] - The name attribute for the hidden input storing the selected value.
|
||||
@param multiple {boolean} [optional] [default=false] - Enables multiple selection mode with chips.
|
||||
@param placeholder {string} [optional] - Placeholder text for the visible input.
|
||||
@param close_on_select {boolean} [optional] [default=false] - Closes the popover when selecting an option in multiple mode.
|
||||
@param auto_highlight {boolean} [optional] [default=false] - Highlights the first matching option while filtering.
|
||||
@param main_attrs {object} [optional] - Additional HTML attributes for the root div.
|
||||
@param input_attrs {object} [optional] - Additional HTML attributes for the visible input.
|
||||
@param popover_attrs {object} [optional] - Additional HTML attributes for the popover div.
|
||||
@param listbox_attrs {object} [optional] - Additional HTML attributes for the listbox div.
|
||||
@param hidden_input_attrs {object} [optional] - Additional HTML attributes for the hidden input.
|
||||
#}
|
||||
{% macro combobox(
|
||||
id=None,
|
||||
selected=None,
|
||||
name=None,
|
||||
multiple=false,
|
||||
placeholder=None,
|
||||
close_on_select=false,
|
||||
auto_highlight=false,
|
||||
main_attrs={},
|
||||
input_attrs={},
|
||||
popover_attrs={},
|
||||
listbox_attrs={},
|
||||
hidden_input_attrs={}
|
||||
) %}
|
||||
{% set id = id or ("combobox-" + (range(100000, 999999) | random | string)) %}
|
||||
{% set selected_values = [] %}
|
||||
{% if selected is defined and selected is iterable and selected is not string %}
|
||||
{% set selected_values = selected %}
|
||||
{% elif selected is defined and selected %}
|
||||
{% set selected_values = [selected] %}
|
||||
{% endif %}
|
||||
{% set hidden_value = (selected_values | dump if multiple else (selected_values[0] if selected_values[0] else '')) %}
|
||||
|
||||
<div
|
||||
id="{{ id }}"
|
||||
class="combobox {{ main_attrs.class }}"
|
||||
{% if multiple %}data-multiple="true"{% endif %}
|
||||
{% if multiple and close_on_select %}data-close-on-select="true"{% endif %}
|
||||
{% if auto_highlight %}data-auto-highlight="true"{% endif %}
|
||||
{% for key, value in main_attrs %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{% if multiple %}
|
||||
<div class="combobox-chips {{ input_attrs.class }}">
|
||||
<input
|
||||
type="text"
|
||||
role="combobox"
|
||||
placeholder="{{ placeholder or '' }}"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
aria-autocomplete="list"
|
||||
aria-expanded="false"
|
||||
aria-controls="{{ id }}-listbox"
|
||||
{% for key, value in input_attrs %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
</div>
|
||||
{% else %}
|
||||
<input
|
||||
type="text"
|
||||
role="combobox"
|
||||
placeholder="{{ placeholder or '' }}"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
aria-autocomplete="list"
|
||||
aria-expanded="false"
|
||||
aria-controls="{{ id }}-listbox"
|
||||
class="{{ input_attrs.class }}"
|
||||
{% for key, value in input_attrs %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
<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="combobox-trigger-icon"><path d="m6 9 6 6 6-6"/></svg>
|
||||
{% endif %}
|
||||
<div
|
||||
id="{{ id }}-popover"
|
||||
data-popover
|
||||
aria-hidden="true"
|
||||
{% for key, value in popover_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
<div
|
||||
role="listbox"
|
||||
id="{{ id }}-listbox"
|
||||
aria-orientation="vertical"
|
||||
{% if multiple %}aria-multiselectable="true"{% endif %}
|
||||
{% for key, value in listbox_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ caller() if caller else '' }}
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
type="hidden"
|
||||
{% if name %}name="{{ name }}"{% endif %}
|
||||
value='{{ hidden_value }}'
|
||||
{% for key, value in hidden_input_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
@@ -132,7 +132,7 @@
|
||||
{{ caller() if caller }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<button type="button" aria-label="Close dialog" onclick="this.closest('dialog').close()">
|
||||
<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>
|
||||
|
||||
@@ -83,10 +83,10 @@
|
||||
</footer>
|
||||
{% endif %}
|
||||
{% if close_button %}
|
||||
<button type="button" aria-label="Close dialog" onclick="this.closest('dialog').close()">
|
||||
<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>
|
||||
{% endif %}
|
||||
</div>
|
||||
</dialog>
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
||||
+3
-29
@@ -1,5 +1,5 @@
|
||||
{#
|
||||
Renders a select or combobox component.
|
||||
Renders a select component.
|
||||
|
||||
@param id {string} [optional] - Unique identifier for the select component.
|
||||
@param selected {string|array} [optional] - The initially selected value(s).
|
||||
@@ -12,8 +12,6 @@
|
||||
@param popover_attrs {object} [optional] - Additional HTML attributes for the popover content div.
|
||||
@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.
|
||||
#}
|
||||
{% macro select(
|
||||
id=None,
|
||||
@@ -27,9 +25,7 @@
|
||||
trigger_attrs={},
|
||||
popover_attrs={},
|
||||
listbox_attrs={},
|
||||
input_attrs={},
|
||||
search_placeholder="Search entries...",
|
||||
is_combobox=false
|
||||
input_attrs={}
|
||||
) %}
|
||||
{% set id = id or ("select-" + (range(100000, 999999) | random | string)) %}
|
||||
|
||||
@@ -85,7 +81,7 @@
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-outline {{ trigger_attrs.class }}"
|
||||
class="{{ trigger_attrs.class }}"
|
||||
id="{{ id }}-trigger"
|
||||
aria-haspopup="listbox"
|
||||
aria-expanded="false"
|
||||
@@ -95,11 +91,7 @@
|
||||
{% endfor %}
|
||||
>
|
||||
<span class="truncate">{{ default_label }}</span>
|
||||
{% if is_combobox %}
|
||||
<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-chevrons-up-down-icon lucide-chevrons-up-down text-muted-foreground opacity-50 shrink-0"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>
|
||||
{% else %}
|
||||
<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-chevron-down-icon lucide-chevron-down text-muted-foreground opacity-50 shrink-0"><path d="m6 9 6 6 6-6"/></svg>
|
||||
{% endif %}
|
||||
</button>
|
||||
<div
|
||||
id="{{ id }}-popover"
|
||||
@@ -109,24 +101,6 @@
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{% if is_combobox %}
|
||||
<header>
|
||||
<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-search-icon lucide-search"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
||||
<input
|
||||
type="text"
|
||||
value=""
|
||||
placeholder="{{ search_placeholder }}"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
aria-autocomplete="list"
|
||||
role="combobox"
|
||||
aria-expanded="false"
|
||||
aria-controls="{{ id }}-listbox"
|
||||
aria-labelledby="{{ id }}-trigger"
|
||||
>
|
||||
</header>
|
||||
{% endif %}
|
||||
<div
|
||||
role="listbox"
|
||||
id="{{ id }}-listbox"
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
>
|
||||
<summary aria-controls="submenu-{{ item_id }}-content">
|
||||
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
{{ item.label | safe }}
|
||||
<span>{{ item.label | safe }}</span>
|
||||
</summary>
|
||||
<ul id="submenu-{{ item_id }}-content">
|
||||
{{ render_sidebar_content(item.items, item_id) if item.items }}
|
||||
@@ -141,4 +141,4 @@
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
- panel_attrs {object} [optional]: Additional HTML attributes for the tab panel div.
|
||||
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
|
||||
@param tablist_attrs {object} [optional] - Additional HTML attributes for the tablist nav element.
|
||||
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(
|
||||
@@ -22,6 +23,7 @@
|
||||
)
|
||||
%}
|
||||
{% 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 }}"
|
||||
@@ -31,9 +33,9 @@
|
||||
>
|
||||
<nav
|
||||
role="tablist"
|
||||
aria-orientation="horizontal"
|
||||
aria-orientation="{{ orientation }}"
|
||||
{% for key, value in tablist_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% if key != 'aria-orientation' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{% for tabset in tabsets %}
|
||||
@@ -43,7 +45,7 @@
|
||||
id="{{ id }}-tab-{{ loop.index }}"
|
||||
aria-controls="{{ id }}-panel-{{ loop.index }}"
|
||||
aria-selected="{{ 'true' if loop.index == default_tab_index else 'false' }}"
|
||||
tabindex="0"
|
||||
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 }}"
|
||||
@@ -75,4 +77,4 @@
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
||||
Reference in New Issue
Block a user