@a4ui/core 0.38.1 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -9
- package/dist/{Collapse-Cr5cDqAl.js → Collapse-CrKiFD8K.js} +173 -124
- package/dist/charts/Sparkline.d.ts +2 -0
- package/dist/charts.js +8 -5
- package/dist/commerce.js +59 -59
- package/dist/elements.css +248 -151
- package/dist/elements.iife.js +1 -1
- package/dist/elements.js +1025 -1015
- package/dist/full.css +248 -151
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4723 -4656
- package/dist/lib/anchored.d.ts +7 -0
- package/dist/lib/direction.d.ts +18 -0
- package/dist/styles.css +27 -11
- package/dist/theme.css +1559 -0
- package/dist/ui/DirectionProvider.d.ts +47 -0
- package/dist/ui/Drawer.d.ts +6 -2
- package/dist/ui/Sidebar.d.ts +8 -2
- package/dist/ui/Toast.d.ts +5 -1
- package/package.json +5 -3
- package/preset.d.ts +2 -1
- package/preset.js +16 -5
- package/src/charts/BarList.tsx +1 -1
- package/src/charts/Sparkline.tsx +5 -1
- package/src/commerce/CartLine.tsx +1 -1
- package/src/commerce/FacetSidebar.tsx +1 -1
- package/src/commerce/ProductCard.tsx +1 -1
- package/src/index.ts +7 -1
- package/src/layout/NavGroup.tsx +2 -2
- package/src/lib/anchored.ts +13 -0
- package/src/lib/direction.ts +35 -0
- package/src/ui/Accordion.tsx +2 -2
- package/src/ui/ActivityFeed.tsx +2 -2
- package/src/ui/AlertDialog.tsx +2 -1
- package/src/ui/Anchor.tsx +1 -1
- package/src/ui/AnnouncementBar.tsx +3 -3
- package/src/ui/ArtifactPanel.tsx +1 -1
- package/src/ui/AvailabilityPicker.tsx +2 -2
- package/src/ui/AvatarGroup.tsx +2 -2
- package/src/ui/BackToTop.tsx +1 -1
- package/src/ui/Badge.tsx +1 -1
- package/src/ui/BlockEditor.tsx +1 -1
- package/src/ui/ButtonGroup.tsx +1 -1
- package/src/ui/Callout.tsx +1 -1
- package/src/ui/Carousel.tsx +17 -9
- package/src/ui/Cascader.tsx +2 -2
- package/src/ui/CategoryStrip.tsx +4 -1
- package/src/ui/CodeEditor.tsx +5 -5
- package/src/ui/CodeTabs.tsx +1 -1
- package/src/ui/Combobox.tsx +2 -1
- package/src/ui/Command.tsx +3 -2
- package/src/ui/Comment.tsx +1 -1
- package/src/ui/Configurator.tsx +2 -2
- package/src/ui/ContextMenu.tsx +2 -1
- package/src/ui/DateField.tsx +4 -4
- package/src/ui/DateRangePicker.tsx +4 -4
- package/src/ui/DiffViewer.tsx +4 -3
- package/src/ui/DirectionProvider.tsx +82 -0
- package/src/ui/Drawer.tsx +23 -5
- package/src/ui/Dropdown.tsx +2 -1
- package/src/ui/EmojiPicker.tsx +5 -2
- package/src/ui/Expandable.tsx +2 -2
- package/src/ui/Faq.tsx +1 -1
- package/src/ui/Field.tsx +1 -1
- package/src/ui/FloatingActionButton.tsx +2 -2
- package/src/ui/FollowingPointer.tsx +2 -2
- package/src/ui/Hero.tsx +2 -2
- package/src/ui/HoldToConfirm.tsx +2 -2
- package/src/ui/HoverCard.tsx +2 -1
- package/src/ui/Image.tsx +3 -2
- package/src/ui/InlineSelect.tsx +1 -1
- package/src/ui/InputGroup.tsx +2 -2
- package/src/ui/Item.tsx +1 -1
- package/src/ui/JsonViewer.tsx +2 -2
- package/src/ui/Kanban.tsx +1 -1
- package/src/ui/KpiBlock.tsx +1 -1
- package/src/ui/Lightbox.tsx +9 -6
- package/src/ui/MasterDetail.tsx +1 -1
- package/src/ui/Mentions.tsx +1 -1
- package/src/ui/Menubar.tsx +9 -8
- package/src/ui/Modal.tsx +3 -2
- package/src/ui/ModelPicker.tsx +2 -1
- package/src/ui/MultiSelect.tsx +5 -5
- package/src/ui/NativeSelect.tsx +2 -2
- package/src/ui/NavigationMenu.tsx +5 -4
- package/src/ui/NotificationCenter.tsx +1 -1
- package/src/ui/NotificationStack.tsx +1 -1
- package/src/ui/NumberInput.tsx +2 -2
- package/src/ui/OnboardingChecklist.tsx +2 -2
- package/src/ui/Pagination.tsx +2 -2
- package/src/ui/PillSearch.tsx +3 -3
- package/src/ui/PivotTable.tsx +0 -0
- package/src/ui/Popover.tsx +2 -1
- package/src/ui/Portal.tsx +3 -1
- package/src/ui/PresenceAvatars.tsx +4 -4
- package/src/ui/PricingTable.tsx +1 -1
- package/src/ui/Prose.tsx +3 -3
- package/src/ui/QueryBuilder.tsx +4 -4
- package/src/ui/Rating.tsx +6 -2
- package/src/ui/ReasoningTrace.tsx +1 -1
- package/src/ui/ScrollArea.tsx +1 -1
- package/src/ui/ScrollProgress.tsx +2 -2
- package/src/ui/SideRail.tsx +3 -3
- package/src/ui/Sidebar.tsx +43 -23
- package/src/ui/SlashMenu.tsx +1 -1
- package/src/ui/Snippet.tsx +6 -3
- package/src/ui/Sortable.tsx +1 -1
- package/src/ui/SpecSheet.tsx +1 -1
- package/src/ui/SpeedDial.tsx +1 -1
- package/src/ui/Splitter.tsx +8 -3
- package/src/ui/SpreadsheetGrid.tsx +4 -4
- package/src/ui/Stepper.tsx +1 -1
- package/src/ui/SuggestionChips.tsx +1 -1
- package/src/ui/Switch.tsx +1 -1
- package/src/ui/Table.tsx +5 -2
- package/src/ui/TimeField.tsx +8 -8
- package/src/ui/Timeline.tsx +2 -2
- package/src/ui/Toast.tsx +9 -3
- package/src/ui/ToolCallTimeline.tsx +2 -2
- package/src/ui/Tooltip.tsx +2 -1
- package/src/ui/Tour.tsx +1 -1
- package/src/ui/TransactionFeed.tsx +1 -1
- package/src/ui/Transfer.tsx +2 -2
- package/src/ui/Tree.tsx +2 -2
- package/src/ui/TreeSelect.tsx +2 -2
- package/src/ui/TreeTable.tsx +4 -4
- package/src/ui/UsageMeter.tsx +1 -1
- package/src/ui/VideoPlayerShell.tsx +2 -2
- package/src/ui/internal/CalendarCore.tsx +6 -6
package/src/ui/SideRail.tsx
CHANGED
|
@@ -60,7 +60,7 @@ export function SideRail(props: SideRailProps): JSX.Element {
|
|
|
60
60
|
<div
|
|
61
61
|
role="tablist"
|
|
62
62
|
aria-orientation="vertical"
|
|
63
|
-
class={cn('flex w-20 flex-col items-stretch gap-1 border-
|
|
63
|
+
class={cn('flex w-20 flex-col items-stretch gap-1 border-e border-border bg-glass py-2', props.class)}
|
|
64
64
|
>
|
|
65
65
|
<For each={props.items}>
|
|
66
66
|
{(item, index) => {
|
|
@@ -92,14 +92,14 @@ export function SideRail(props: SideRailProps): JSX.Element {
|
|
|
92
92
|
<span
|
|
93
93
|
aria-hidden="true"
|
|
94
94
|
class={cn(
|
|
95
|
-
'absolute inset-y-1
|
|
95
|
+
'absolute inset-y-1 start-0 w-0.5 rounded-full bg-primary transition-opacity',
|
|
96
96
|
active() ? 'opacity-100' : 'opacity-0',
|
|
97
97
|
)}
|
|
98
98
|
/>
|
|
99
99
|
<span class="relative inline-flex">
|
|
100
100
|
{item.icon}
|
|
101
101
|
<Show when={item.badge}>
|
|
102
|
-
<span class="absolute -
|
|
102
|
+
<span class="absolute -end-2 -top-2">{item.badge}</span>
|
|
103
103
|
</Show>
|
|
104
104
|
</span>
|
|
105
105
|
<Show when={showLabels()}>
|
package/src/ui/Sidebar.tsx
CHANGED
|
@@ -23,11 +23,20 @@ import {
|
|
|
23
23
|
} from 'solid-js'
|
|
24
24
|
|
|
25
25
|
import { cn } from '../lib/cn'
|
|
26
|
+
import { portalDir } from '../lib/direction'
|
|
27
|
+
import { useDirection } from './DirectionProvider'
|
|
26
28
|
import { useMediaQuery } from '../lib/media'
|
|
27
29
|
import { Drawer } from './Drawer'
|
|
28
30
|
import { ScrollArea } from './ScrollArea'
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Which edge the sidebar sits on. The layout is a flex row in DOM order, so the
|
|
34
|
+
* edge follows the reading direction: `start` (alias `left`) is the leading
|
|
35
|
+
* edge, `end` (alias `right`) the trailing one. Under `dir="rtl"` `start` is the
|
|
36
|
+
* right-hand side.
|
|
37
|
+
*/
|
|
38
|
+
export type SidebarSide = 'start' | 'end' | 'left' | 'right'
|
|
39
|
+
const isStart = (side: SidebarSide): boolean => side === 'start' || side === 'left'
|
|
31
40
|
export type SidebarVariant = 'sidebar' | 'floating' | 'inset'
|
|
32
41
|
export type SidebarCollapsible = 'offcanvas' | 'icon' | 'none'
|
|
33
42
|
|
|
@@ -95,7 +104,7 @@ export function SidebarProvider(props: SidebarProviderProps): JSX.Element {
|
|
|
95
104
|
const [internal, setInternal] = createSignal(props.defaultOpen ?? true)
|
|
96
105
|
const [openMobile, setOpenMobile] = createSignal(false)
|
|
97
106
|
const [config, setConfig] = createSignal<ReturnType<SidebarContextValue['config']>>({
|
|
98
|
-
side: '
|
|
107
|
+
side: 'start',
|
|
99
108
|
variant: 'sidebar',
|
|
100
109
|
collapsible: 'offcanvas',
|
|
101
110
|
})
|
|
@@ -144,7 +153,7 @@ export function SidebarProvider(props: SidebarProviderProps): JSX.Element {
|
|
|
144
153
|
}
|
|
145
154
|
|
|
146
155
|
interface SidebarProps extends ParentProps {
|
|
147
|
-
/** Which edge it sits on. @default '
|
|
156
|
+
/** Which edge it sits on; follows `dir` (see {@link SidebarSide}). @default 'start' */
|
|
148
157
|
side?: SidebarSide
|
|
149
158
|
/** `sidebar` is flush, `floating` is a detached rounded panel, `inset` has no surface of its own (pair with `SidebarInset`). @default 'sidebar' */
|
|
150
159
|
variant?: SidebarVariant
|
|
@@ -169,7 +178,7 @@ interface SidebarProps extends ParentProps {
|
|
|
169
178
|
*/
|
|
170
179
|
export function Sidebar(props: SidebarProps): JSX.Element {
|
|
171
180
|
const ctx = useSidebarInternal()
|
|
172
|
-
const side = () => props.side ?? '
|
|
181
|
+
const side = () => props.side ?? 'start'
|
|
173
182
|
const variant = () => props.variant ?? 'sidebar'
|
|
174
183
|
const collapsible = () => props.collapsible ?? 'offcanvas'
|
|
175
184
|
createEffect(() => ctx.setConfig({ side: side(), variant: variant(), collapsible: collapsible() }))
|
|
@@ -185,7 +194,7 @@ export function Sidebar(props: SidebarProps): JSX.Element {
|
|
|
185
194
|
class={cn(
|
|
186
195
|
'relative flex h-full w-full min-w-0 flex-col',
|
|
187
196
|
variant() === 'sidebar' &&
|
|
188
|
-
cn('bg-glass', side()
|
|
197
|
+
cn('bg-glass', isStart(side()) ? 'border-e border-border' : 'border-s border-border'),
|
|
189
198
|
variant() === 'floating' && 'rounded-xl border border-border bg-glass shadow-lg',
|
|
190
199
|
props.class,
|
|
191
200
|
)}
|
|
@@ -202,7 +211,7 @@ export function Sidebar(props: SidebarProps): JSX.Element {
|
|
|
202
211
|
<Drawer
|
|
203
212
|
open={ctx.openMobile()}
|
|
204
213
|
onOpenChange={ctx.setOpenMobile}
|
|
205
|
-
side={side()}
|
|
214
|
+
side={isStart(side()) ? 'start' : 'end'}
|
|
206
215
|
title={label()}
|
|
207
216
|
class="max-w-[var(--sidebar-w)] p-0"
|
|
208
217
|
>
|
|
@@ -233,10 +242,16 @@ export function Sidebar(props: SidebarProps): JSX.Element {
|
|
|
233
242
|
<div
|
|
234
243
|
class={cn(
|
|
235
244
|
'absolute inset-y-0 transition-[transform,visibility] duration-200 ease-out',
|
|
236
|
-
side()
|
|
245
|
+
isStart(side()) ? 'start-0' : 'end-0',
|
|
237
246
|
variant() !== 'sidebar' && 'p-2',
|
|
238
247
|
collapsible() === 'icon' ? 'w-full' : 'w-[var(--sidebar-w)]',
|
|
239
|
-
offscreen() &&
|
|
248
|
+
offscreen() &&
|
|
249
|
+
cn(
|
|
250
|
+
'invisible',
|
|
251
|
+
isStart(side())
|
|
252
|
+
? '-translate-x-full rtl:translate-x-full'
|
|
253
|
+
: 'translate-x-full rtl:-translate-x-full',
|
|
254
|
+
),
|
|
240
255
|
)}
|
|
241
256
|
>
|
|
242
257
|
{surface()}
|
|
@@ -275,7 +290,7 @@ export function SidebarTrigger(props: SidebarTriggerProps): JSX.Element {
|
|
|
275
290
|
props.class,
|
|
276
291
|
)}
|
|
277
292
|
>
|
|
278
|
-
{props.children ?? <PanelLeft class="h-4 w-4" aria-hidden="true" />}
|
|
293
|
+
{props.children ?? <PanelLeft class="h-4 w-4 rtl:-scale-x-100" aria-hidden="true" />}
|
|
279
294
|
</button>
|
|
280
295
|
)
|
|
281
296
|
}
|
|
@@ -287,7 +302,7 @@ export function SidebarTrigger(props: SidebarTriggerProps): JSX.Element {
|
|
|
287
302
|
*/
|
|
288
303
|
export function SidebarRail(props: { class?: string }): JSX.Element {
|
|
289
304
|
const ctx = useSidebarInternal()
|
|
290
|
-
const left = () => ctx.config().side
|
|
305
|
+
const left = () => isStart(ctx.config().side)
|
|
291
306
|
return (
|
|
292
307
|
<button
|
|
293
308
|
type="button"
|
|
@@ -297,8 +312,10 @@ export function SidebarRail(props: { class?: string }): JSX.Element {
|
|
|
297
312
|
title="Toggle sidebar"
|
|
298
313
|
onClick={() => ctx.toggle()}
|
|
299
314
|
class={cn(
|
|
300
|
-
'absolute inset-y-0 z-10 hidden w-3 after:absolute after:inset-y-0 after:
|
|
301
|
-
left()
|
|
315
|
+
'absolute inset-y-0 z-10 hidden w-3 after:absolute after:inset-y-0 after:start-1/2 after:w-px after:bg-transparent after:transition-colors hover:after:bg-border md:block',
|
|
316
|
+
left()
|
|
317
|
+
? '-end-1.5 cursor-w-resize rtl:cursor-e-resize'
|
|
318
|
+
: '-start-1.5 cursor-e-resize rtl:cursor-w-resize',
|
|
302
319
|
props.class,
|
|
303
320
|
)}
|
|
304
321
|
/>
|
|
@@ -325,7 +342,7 @@ export function SidebarInset(props: ParentProps<{ class?: string }>): JSX.Elemen
|
|
|
325
342
|
inset() &&
|
|
326
343
|
cn(
|
|
327
344
|
'm-2 rounded-xl border border-border bg-glass shadow-sm',
|
|
328
|
-
ctx.config().side
|
|
345
|
+
isStart(ctx.config().side) ? 'ms-0' : 'me-0',
|
|
329
346
|
),
|
|
330
347
|
props.class,
|
|
331
348
|
)}
|
|
@@ -416,7 +433,7 @@ export function SidebarGroupAction(props: SidebarGroupActionProps): JSX.Element
|
|
|
416
433
|
aria-label={props.label}
|
|
417
434
|
onClick={(e) => props.onClick?.(e)}
|
|
418
435
|
class={cn(
|
|
419
|
-
'absolute
|
|
436
|
+
'absolute end-1 top-0 grid h-6 w-6 place-items-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring group-data-[icon-collapsed=true]/sidebar:hidden [&>svg]:h-4 [&>svg]:w-4',
|
|
420
437
|
props.class,
|
|
421
438
|
)}
|
|
422
439
|
>
|
|
@@ -476,17 +493,20 @@ const SIZE: Record<MenuButtonSize, string> = {
|
|
|
476
493
|
// child is visually hidden (sr-only) so the accessible name survives, and the
|
|
477
494
|
// icon is centred.
|
|
478
495
|
const ITEM_BASE =
|
|
479
|
-
'flex w-full min-w-0 items-center gap-2.5 rounded-lg px-2.5 text-
|
|
496
|
+
'flex w-full min-w-0 items-center gap-2.5 rounded-lg px-2.5 text-start font-medium text-foreground/80 outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring aria-[current=page]:bg-muted aria-[current=page]:text-foreground group-has-[[data-sidebar=menu-action]]/item:pe-9 [&>svg]:h-[18px] [&>svg]:w-[18px] [&>svg]:shrink-0 [&>span]:truncate group-data-[icon-collapsed=true]/sidebar:justify-center group-data-[icon-collapsed=true]/sidebar:px-0 group-data-[icon-collapsed=true]/sidebar:[&>span]:sr-only'
|
|
480
497
|
|
|
481
498
|
function MenuTooltip(
|
|
482
499
|
props: ParentProps<{ tooltip?: JSX.Element; as: 'a' | 'button'; [key: string]: unknown }>,
|
|
483
500
|
) {
|
|
484
501
|
const iconCollapsed = useContext(IconCollapsed)
|
|
485
502
|
const ctx = useSidebarInternal()
|
|
503
|
+
const dir = useDirection()
|
|
486
504
|
const [local, rest] = splitProps(props, ['tooltip', 'as', 'children'])
|
|
505
|
+
// Kobalte placements are physical: the tooltip opens toward the content side.
|
|
506
|
+
const towardContent = () => (isStart(ctx.config().side) === (dir() === 'ltr') ? 'right' : 'left')
|
|
487
507
|
return (
|
|
488
508
|
<KTooltip
|
|
489
|
-
placement={
|
|
509
|
+
placement={towardContent()}
|
|
490
510
|
gutter={10}
|
|
491
511
|
openDelay={150}
|
|
492
512
|
closeDelay={0}
|
|
@@ -495,7 +515,7 @@ function MenuTooltip(
|
|
|
495
515
|
<KTooltip.Trigger as={local.as} {...rest}>
|
|
496
516
|
{local.children}
|
|
497
517
|
</KTooltip.Trigger>
|
|
498
|
-
<KTooltip.Portal>
|
|
518
|
+
<KTooltip.Portal ref={portalDir}>
|
|
499
519
|
<KTooltip.Content class="z-50 rounded-md border border-border bg-card px-2.5 py-1.5 text-xs text-card-foreground shadow-md">
|
|
500
520
|
{local.tooltip}
|
|
501
521
|
</KTooltip.Content>
|
|
@@ -565,7 +585,7 @@ export function SidebarMenuAction(
|
|
|
565
585
|
aria-label={props.label}
|
|
566
586
|
onClick={(e) => props.onClick?.(e)}
|
|
567
587
|
class={cn(
|
|
568
|
-
'absolute
|
|
588
|
+
'absolute end-1 top-1/2 grid h-6 w-6 -translate-y-1/2 place-items-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring group-data-[icon-collapsed=true]/sidebar:hidden [&>svg]:h-4 [&>svg]:w-4',
|
|
569
589
|
props.showOnHover &&
|
|
570
590
|
'opacity-0 focus-visible:opacity-100 group-focus-within/item:opacity-100 group-hover/item:opacity-100',
|
|
571
591
|
props.class,
|
|
@@ -582,7 +602,7 @@ export function SidebarMenuBadge(props: ParentProps<{ class?: string }>): JSX.El
|
|
|
582
602
|
<span
|
|
583
603
|
data-sidebar="menu-badge"
|
|
584
604
|
class={cn(
|
|
585
|
-
'pointer-events-none absolute
|
|
605
|
+
'pointer-events-none absolute end-2 top-1/2 -translate-y-1/2 select-none rounded-md bg-primary/15 px-1.5 text-[11px] font-semibold tabular-nums text-foreground group-data-[icon-collapsed=true]/sidebar:hidden',
|
|
586
606
|
props.class,
|
|
587
607
|
)}
|
|
588
608
|
>
|
|
@@ -651,8 +671,8 @@ export function SidebarMenuSub(props: SidebarMenuSubProps): JSX.Element {
|
|
|
651
671
|
<ChevronRight
|
|
652
672
|
aria-hidden="true"
|
|
653
673
|
class={cn(
|
|
654
|
-
'
|
|
655
|
-
shown()
|
|
674
|
+
'ms-auto h-4 w-4 shrink-0 transition-transform duration-200 group-data-[icon-collapsed=true]/sidebar:hidden',
|
|
675
|
+
shown() ? 'rotate-90' : 'rtl:-scale-x-100',
|
|
656
676
|
)}
|
|
657
677
|
/>
|
|
658
678
|
</MenuTooltip>
|
|
@@ -664,7 +684,7 @@ export function SidebarMenuSub(props: SidebarMenuSubProps): JSX.Element {
|
|
|
664
684
|
)}
|
|
665
685
|
>
|
|
666
686
|
<div class="min-h-0 overflow-hidden">
|
|
667
|
-
<ul class="
|
|
687
|
+
<ul class="ms-4 mt-1 flex list-none flex-col gap-0.5 border-s border-border py-0.5 ps-2.5">
|
|
668
688
|
{props.children}
|
|
669
689
|
</ul>
|
|
670
690
|
</div>
|
|
@@ -688,7 +708,7 @@ export function SidebarMenuSubButton(
|
|
|
688
708
|
): JSX.Element {
|
|
689
709
|
const cls = () =>
|
|
690
710
|
cn(
|
|
691
|
-
'flex h-8 w-full min-w-0 items-center gap-2 rounded-md px-2 text-
|
|
711
|
+
'flex h-8 w-full min-w-0 items-center gap-2 rounded-md px-2 text-start text-[13px] font-medium text-foreground/75 outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring aria-[current=page]:bg-muted aria-[current=page]:text-foreground [&>span]:truncate',
|
|
692
712
|
props.class,
|
|
693
713
|
)
|
|
694
714
|
return (
|
package/src/ui/SlashMenu.tsx
CHANGED
|
@@ -116,7 +116,7 @@ export function SlashMenu(props: SlashMenuProps): JSX.Element {
|
|
|
116
116
|
onMouseEnter={() => setActive(i())}
|
|
117
117
|
onClick={() => pick(item)}
|
|
118
118
|
class={cn(
|
|
119
|
-
'flex cursor-pointer items-center gap-2 rounded-md px-3 py-2 text-
|
|
119
|
+
'flex cursor-pointer items-center gap-2 rounded-md px-3 py-2 text-start transition-colors',
|
|
120
120
|
active() === i() ? 'bg-muted' : undefined,
|
|
121
121
|
)}
|
|
122
122
|
>
|
package/src/ui/Snippet.tsx
CHANGED
|
@@ -42,21 +42,24 @@ export function Snippet(props: SnippetProps): JSX.Element {
|
|
|
42
42
|
return (
|
|
43
43
|
<div class={cn('relative rounded-lg border border-border bg-muted px-3 py-2', props.class)}>
|
|
44
44
|
<Show when={props.language}>
|
|
45
|
-
<span class="absolute
|
|
45
|
+
<span class="absolute end-9 top-2 rounded bg-card px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">
|
|
46
46
|
{props.language}
|
|
47
47
|
</span>
|
|
48
48
|
</Show>
|
|
49
49
|
<button
|
|
50
50
|
type="button"
|
|
51
51
|
aria-label={copied() ? 'Copied' : 'Copy to clipboard'}
|
|
52
|
-
class="absolute
|
|
52
|
+
class="absolute end-2 top-2 inline-flex h-5 w-5 items-center justify-center rounded text-muted-foreground hover:text-foreground"
|
|
53
53
|
onClick={copy}
|
|
54
54
|
>
|
|
55
55
|
<Show when={copied()} fallback={<Copy class="h-3.5 w-3.5" />}>
|
|
56
56
|
<Check class="h-3.5 w-3.5" />
|
|
57
57
|
</Show>
|
|
58
58
|
</button>
|
|
59
|
-
<pre
|
|
59
|
+
<pre
|
|
60
|
+
dir="ltr"
|
|
61
|
+
class="overflow-x-auto whitespace-pre pr-8 rtl:pl-8 rtl:pr-0 font-mono text-sm text-foreground"
|
|
62
|
+
>
|
|
60
63
|
<code>{props.code}</code>
|
|
61
64
|
</pre>
|
|
62
65
|
</div>
|
package/src/ui/Sortable.tsx
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// a floating clone follows the pointer through a Portal, and the grabbed
|
|
4
4
|
// row's slot collapses into a dashed placeholder until it's dropped.
|
|
5
5
|
import { createEffect, createSignal, For, Show, type JSX } from 'solid-js'
|
|
6
|
-
import { Portal } from '
|
|
6
|
+
import { Portal } from './Portal'
|
|
7
7
|
import { GripVertical } from 'lucide-solid'
|
|
8
8
|
|
|
9
9
|
import { cn } from '../lib/cn'
|
package/src/ui/SpecSheet.tsx
CHANGED
|
@@ -76,7 +76,7 @@ export function SpecSheet(props: SpecSheetProps): JSX.Element {
|
|
|
76
76
|
)}
|
|
77
77
|
>
|
|
78
78
|
<dt class="text-muted-foreground">{row.label}</dt>
|
|
79
|
-
<dd class="text-
|
|
79
|
+
<dd class="text-end font-medium text-foreground">{row.value}</dd>
|
|
80
80
|
</div>
|
|
81
81
|
)}
|
|
82
82
|
</For>
|
package/src/ui/SpeedDial.tsx
CHANGED
|
@@ -40,7 +40,7 @@ export function SpeedDial(props: SpeedDialProps): JSX.Element {
|
|
|
40
40
|
const [open, setOpen] = createSignal(false)
|
|
41
41
|
|
|
42
42
|
return (
|
|
43
|
-
<div class={cn('fixed bottom-6
|
|
43
|
+
<div class={cn('fixed bottom-6 end-6 z-40 flex flex-col items-center gap-3', props.class)}>
|
|
44
44
|
<Show when={open()}>
|
|
45
45
|
<div class="flex flex-col items-center gap-3">
|
|
46
46
|
<For each={props.actions}>
|
package/src/ui/Splitter.tsx
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import type { JSX } from 'solid-js'
|
|
3
3
|
import { createSignal } from 'solid-js'
|
|
4
4
|
import { cn } from '../lib/cn'
|
|
5
|
+
import { readingKey } from '../lib/direction'
|
|
6
|
+
import { useDirection } from './DirectionProvider'
|
|
5
7
|
|
|
6
8
|
export interface SplitterProps {
|
|
7
9
|
/** Content of the first pane (left when horizontal, top when vertical). */
|
|
@@ -35,6 +37,7 @@ export interface SplitterProps {
|
|
|
35
37
|
* ```
|
|
36
38
|
*/
|
|
37
39
|
export function Splitter(props: SplitterProps): JSX.Element {
|
|
40
|
+
const dir = useDirection()
|
|
38
41
|
const orientation = () => props.orientation ?? 'horizontal'
|
|
39
42
|
const min = () => props.min ?? 15
|
|
40
43
|
const max = () => props.max ?? 85
|
|
@@ -50,7 +53,7 @@ export function Splitter(props: SplitterProps): JSX.Element {
|
|
|
50
53
|
const rect = container.getBoundingClientRect()
|
|
51
54
|
const pct =
|
|
52
55
|
orientation() === 'horizontal'
|
|
53
|
-
? ((event.clientX - rect.left) / rect.width) * 100
|
|
56
|
+
? ((dir() === 'rtl' ? rect.right - event.clientX : event.clientX - rect.left) / rect.width) * 100
|
|
54
57
|
: ((event.clientY - rect.top) / rect.height) * 100
|
|
55
58
|
setSize(clamp(pct))
|
|
56
59
|
}
|
|
@@ -72,8 +75,10 @@ export function Splitter(props: SplitterProps): JSX.Element {
|
|
|
72
75
|
const onKeyDown = (event: KeyboardEvent) => {
|
|
73
76
|
const step = 3
|
|
74
77
|
let delta = 0
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
// The first pane sits on the right under rtl, so the horizontal keys swap.
|
|
79
|
+
const key = readingKey(event.key, dir())
|
|
80
|
+
if (key === 'ArrowLeft' || key === 'ArrowUp') delta = -step
|
|
81
|
+
else if (key === 'ArrowRight' || key === 'ArrowDown') delta = step
|
|
77
82
|
else if (event.key === 'Home') return setSize(min())
|
|
78
83
|
else if (event.key === 'End') return setSize(max())
|
|
79
84
|
else return
|
|
@@ -246,12 +246,12 @@ export function SpreadsheetGrid(props: SpreadsheetGridProps): JSX.Element {
|
|
|
246
246
|
>
|
|
247
247
|
<div class="grid" style={{ 'grid-template-columns': `48px repeat(${props.cols}, minmax(80px, 1fr))` }}>
|
|
248
248
|
<div role="row" class="contents">
|
|
249
|
-
<div aria-hidden="true" class="border-b border-
|
|
249
|
+
<div aria-hidden="true" class="border-b border-e border-border bg-muted" />
|
|
250
250
|
<For each={headers()}>
|
|
251
251
|
{(h) => (
|
|
252
252
|
<div
|
|
253
253
|
role="columnheader"
|
|
254
|
-
class="border-b border-
|
|
254
|
+
class="border-b border-e border-border bg-muted px-2 py-1 text-center text-xs font-semibold text-muted-foreground"
|
|
255
255
|
>
|
|
256
256
|
{h}
|
|
257
257
|
</div>
|
|
@@ -263,7 +263,7 @@ export function SpreadsheetGrid(props: SpreadsheetGridProps): JSX.Element {
|
|
|
263
263
|
<div role="row" class="contents">
|
|
264
264
|
<div
|
|
265
265
|
role="rowheader"
|
|
266
|
-
class="border-b border-
|
|
266
|
+
class="border-b border-e border-border bg-muted px-2 py-1 text-center text-xs text-muted-foreground"
|
|
267
267
|
>
|
|
268
268
|
{r + 1}
|
|
269
269
|
</div>
|
|
@@ -273,7 +273,7 @@ export function SpreadsheetGrid(props: SpreadsheetGridProps): JSX.Element {
|
|
|
273
273
|
role="gridcell"
|
|
274
274
|
aria-selected={isActive(r, c)}
|
|
275
275
|
class={cn(
|
|
276
|
-
'relative h-8 border-b border-
|
|
276
|
+
'relative h-8 border-b border-e border-border px-2 py-1 text-sm cursor-cell',
|
|
277
277
|
inSelection(r, c) && 'bg-primary/10',
|
|
278
278
|
isActive(r, c) && 'ring-2 ring-inset ring-primary',
|
|
279
279
|
isAnchor(r, c) && !isActive(r, c) && 'border-primary',
|
package/src/ui/Stepper.tsx
CHANGED
|
@@ -90,7 +90,7 @@ export function Stepper(props: StepperProps): JSX.Element {
|
|
|
90
90
|
aria-hidden="true"
|
|
91
91
|
class={cn(
|
|
92
92
|
connectorFilled() ? 'bg-primary' : 'bg-border',
|
|
93
|
-
isVertical() ? '
|
|
93
|
+
isVertical() ? 'ms-4 -mt-1 mb-1 w-0.5 flex-1' : 'mx-2 h-0.5 flex-1',
|
|
94
94
|
)}
|
|
95
95
|
/>
|
|
96
96
|
</Show>
|
|
@@ -36,7 +36,7 @@ export function SuggestionChips(props: SuggestionChipsProps): JSX.Element {
|
|
|
36
36
|
<div class={cn('flex flex-wrap gap-2', props.class)}>
|
|
37
37
|
<For each={props.suggestions}>
|
|
38
38
|
{(suggestion) => (
|
|
39
|
-
<span class="inline-flex items-center gap-1 rounded-full border border-border
|
|
39
|
+
<span class="inline-flex items-center gap-1 rounded-full border border-border ps-3 pe-1.5 py-1.5 text-sm text-foreground transition-colors hover:bg-muted">
|
|
40
40
|
<button
|
|
41
41
|
type="button"
|
|
42
42
|
onClick={() => props.onSelect?.(suggestion)}
|
package/src/ui/Switch.tsx
CHANGED
|
@@ -34,7 +34,7 @@ export function Switch(props: SwitchProps): JSX.Element {
|
|
|
34
34
|
>
|
|
35
35
|
<KSwitch.Input />
|
|
36
36
|
<KSwitch.Control class="inline-flex h-5 w-9 shrink-0 items-center rounded-full bg-input px-0.5 transition-colors data-[checked]:bg-primary data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50">
|
|
37
|
-
<KSwitch.Thumb class="h-4 w-4 rounded-full bg-white shadow-sm transition-transform data-[checked]:translate-x-4" />
|
|
37
|
+
<KSwitch.Thumb class="h-4 w-4 rounded-full bg-white shadow-sm transition-transform data-[checked]:translate-x-4 rtl:data-[checked]:-translate-x-4" />
|
|
38
38
|
</KSwitch.Control>
|
|
39
39
|
<Show when={props.label}>
|
|
40
40
|
<KSwitch.Label class="text-sm text-foreground">{props.label}</KSwitch.Label>
|
package/src/ui/Table.tsx
CHANGED
|
@@ -61,7 +61,7 @@ export function Table(props: TableProps): JSX.Element {
|
|
|
61
61
|
export function TableHead(props: TableProps): JSX.Element {
|
|
62
62
|
const [local, rest] = splitProps(props, ['class', 'children'])
|
|
63
63
|
return (
|
|
64
|
-
<thead class={cn('border-b border-border text-
|
|
64
|
+
<thead class={cn('border-b border-border text-muted-foreground', local.class)} {...rest}>
|
|
65
65
|
{local.children}
|
|
66
66
|
</thead>
|
|
67
67
|
)
|
|
@@ -112,7 +112,10 @@ export function TableRow(props: TableProps): JSX.Element {
|
|
|
112
112
|
export function TableHeadCell(props: TableProps): JSX.Element {
|
|
113
113
|
const [local, rest] = splitProps(props, ['class', 'children'])
|
|
114
114
|
return (
|
|
115
|
-
<th
|
|
115
|
+
<th
|
|
116
|
+
class={cn('px-3 py-2 text-start text-xs font-semibold uppercase tracking-wide', local.class)}
|
|
117
|
+
{...rest}
|
|
118
|
+
>
|
|
116
119
|
{local.children}
|
|
117
120
|
</th>
|
|
118
121
|
)
|
package/src/ui/TimeField.tsx
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
import { Clock } from 'lucide-solid'
|
|
7
7
|
import type { JSX } from 'solid-js'
|
|
8
8
|
import { For, Show, createEffect, createMemo, createSignal, onCleanup } from 'solid-js'
|
|
9
|
-
import { Portal } from '
|
|
9
|
+
import { Portal } from './Portal'
|
|
10
10
|
|
|
11
|
-
import { createAnchorReflow } from '../lib/anchored'
|
|
11
|
+
import { anchoredLeft, createAnchorReflow } from '../lib/anchored'
|
|
12
12
|
import { cn } from '../lib/cn'
|
|
13
13
|
|
|
14
14
|
type AmPm = 'AM' | 'PM'
|
|
@@ -108,7 +108,7 @@ export function TimeField(props: TimeFieldProps): JSX.Element {
|
|
|
108
108
|
if (!btnRef) return
|
|
109
109
|
const r = btnRef.getBoundingClientRect()
|
|
110
110
|
const w = popWidth()
|
|
111
|
-
const left =
|
|
111
|
+
const left = anchoredLeft(btnRef, w)
|
|
112
112
|
setPos({ top: r.bottom + 4, left })
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -200,7 +200,7 @@ export function TimeField(props: TimeFieldProps): JSX.Element {
|
|
|
200
200
|
|
|
201
201
|
const optionClass = (selected: boolean) =>
|
|
202
202
|
cn(
|
|
203
|
-
'rounded-md px-2 py-1 text-
|
|
203
|
+
'rounded-md px-2 py-1 text-start text-sm transition-colors',
|
|
204
204
|
selected ? 'bg-primary text-primary-foreground' : 'text-foreground hover:bg-muted',
|
|
205
205
|
)
|
|
206
206
|
|
|
@@ -211,7 +211,7 @@ export function TimeField(props: TimeFieldProps): JSX.Element {
|
|
|
211
211
|
type="button"
|
|
212
212
|
disabled={props.disabled}
|
|
213
213
|
onClick={() => (open() ? close() : openPopover())}
|
|
214
|
-
class="flex w-full items-center justify-between gap-2 rounded-md border border-input bg-background px-3 py-2 text-
|
|
214
|
+
class="flex w-full items-center justify-between gap-2 rounded-md border border-input bg-background px-3 py-2 text-start text-sm text-foreground outline-none transition-colors a4-field disabled:cursor-not-allowed disabled:opacity-50"
|
|
215
215
|
>
|
|
216
216
|
<span class={displayLabel(props.value, props.hour12 ?? false) ? '' : 'text-muted-foreground'}>
|
|
217
217
|
{displayLabel(props.value, props.hour12 ?? false) ?? props.label ?? 'Select time'}
|
|
@@ -226,12 +226,12 @@ export function TimeField(props: TimeFieldProps): JSX.Element {
|
|
|
226
226
|
data-a4-floating
|
|
227
227
|
style={{ position: 'fixed', top: `${pos().top}px`, left: `${pos().left}px` }}
|
|
228
228
|
class={cn(
|
|
229
|
-
'pointer-events-auto z-50 flex gap-1 divide-x divide-border rounded-lg border border-border bg-card p-2 text-card-foreground shadow-lg',
|
|
229
|
+
'pointer-events-auto z-50 flex gap-1 divide-x rtl:divide-x-reverse divide-border rounded-lg border border-border bg-card p-2 text-card-foreground shadow-lg',
|
|
230
230
|
props.hour12 ? 'w-56' : 'w-44',
|
|
231
231
|
)}
|
|
232
232
|
>
|
|
233
233
|
<div
|
|
234
|
-
class="flex max-h-56 flex-1 flex-col gap-0.5 overflow-y-auto
|
|
234
|
+
class="flex max-h-56 flex-1 flex-col gap-0.5 overflow-y-auto pe-1"
|
|
235
235
|
role="listbox"
|
|
236
236
|
aria-label="Hour"
|
|
237
237
|
>
|
|
@@ -278,7 +278,7 @@ export function TimeField(props: TimeFieldProps): JSX.Element {
|
|
|
278
278
|
|
|
279
279
|
<Show when={props.hour12}>
|
|
280
280
|
<div
|
|
281
|
-
class="flex max-h-56 flex-1 flex-col gap-0.5 overflow-y-auto
|
|
281
|
+
class="flex max-h-56 flex-1 flex-col gap-0.5 overflow-y-auto ps-1"
|
|
282
282
|
role="listbox"
|
|
283
283
|
aria-label="AM or PM"
|
|
284
284
|
>
|
package/src/ui/Timeline.tsx
CHANGED
|
@@ -55,12 +55,12 @@ export function Timeline(props: TimelineProps): JSX.Element {
|
|
|
55
55
|
<ol class={cn('relative flex flex-col gap-6', props.class)}>
|
|
56
56
|
<For each={props.items}>
|
|
57
57
|
{(item, index) => (
|
|
58
|
-
<li class="relative flex gap-4
|
|
58
|
+
<li class="relative flex gap-4 ps-2">
|
|
59
59
|
{/* Line segment: hidden on the last item so it doesn't extend past the dot. */}
|
|
60
60
|
<Show when={index() < props.items.length - 1}>
|
|
61
61
|
<span
|
|
62
62
|
aria-hidden="true"
|
|
63
|
-
class="absolute
|
|
63
|
+
class="absolute start-[calc(0.5rem+0.375rem)] top-3 -bottom-6 w-px -translate-x-1/2 bg-border"
|
|
64
64
|
/>
|
|
65
65
|
</Show>
|
|
66
66
|
<span
|
package/src/ui/Toast.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import type { JSX } from 'solid-js'
|
|
|
6
6
|
import { Show } from 'solid-js'
|
|
7
7
|
|
|
8
8
|
import { cn } from '../lib/cn'
|
|
9
|
+
import { useDirection } from './DirectionProvider'
|
|
9
10
|
|
|
10
11
|
/** Visual/semantic tone of a toast — controls its border/background accent color. */
|
|
11
12
|
export type ToastTone = 'success' | 'error' | 'info'
|
|
@@ -58,11 +59,16 @@ export const toast = {
|
|
|
58
59
|
info: (title: string, description?: string) => show('info', title, description),
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
/**
|
|
62
|
+
/**
|
|
63
|
+
* Mount once near the app root — hosts the toast viewport. Mount it inside a
|
|
64
|
+
* `DirectionProvider` for RTL: the viewport sits in the end corner (the left
|
|
65
|
+
* one under RTL) and swipe-to-dismiss follows the direction.
|
|
66
|
+
*/
|
|
62
67
|
export function Toaster(): JSX.Element {
|
|
68
|
+
const dir = useDirection()
|
|
63
69
|
return (
|
|
64
|
-
<KToast.Region duration={5000} swipeDirection=
|
|
65
|
-
<KToast.List class="fixed bottom-4
|
|
70
|
+
<KToast.Region duration={5000} swipeDirection={dir() === 'rtl' ? 'left' : 'right'}>
|
|
71
|
+
<KToast.List class="fixed bottom-4 end-4 z-50 flex w-80 flex-col gap-2 outline-none" />
|
|
66
72
|
</KToast.Region>
|
|
67
73
|
)
|
|
68
74
|
}
|
|
@@ -46,11 +46,11 @@ export function ToolCallTimeline(props: ToolCallTimelineProps): JSX.Element {
|
|
|
46
46
|
<ol class={cn('relative flex flex-col gap-4', props.class)}>
|
|
47
47
|
<For each={props.calls}>
|
|
48
48
|
{(call, index) => (
|
|
49
|
-
<li class="relative flex gap-3
|
|
49
|
+
<li class="relative flex gap-3 ps-1">
|
|
50
50
|
<Show when={index() < props.calls.length - 1}>
|
|
51
51
|
<span
|
|
52
52
|
aria-hidden="true"
|
|
53
|
-
class="absolute
|
|
53
|
+
class="absolute start-[calc(0.5rem+2px)] top-6 -bottom-4 w-px -translate-x-1/2 bg-border"
|
|
54
54
|
/>
|
|
55
55
|
</Show>
|
|
56
56
|
<StatusNode status={call.status} />
|
package/src/ui/Tooltip.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import { Tooltip as KTooltip } from '@kobalte/core/tooltip'
|
|
|
3
3
|
import type { JSX, ParentProps } from 'solid-js'
|
|
4
4
|
|
|
5
5
|
import { cn } from '../lib/cn'
|
|
6
|
+
import { portalDir } from '../lib/direction'
|
|
6
7
|
|
|
7
8
|
interface TooltipProps extends ParentProps {
|
|
8
9
|
/** Content shown inside the tooltip popover. */
|
|
@@ -26,7 +27,7 @@ export function Tooltip(props: TooltipProps): JSX.Element {
|
|
|
26
27
|
return (
|
|
27
28
|
<KTooltip>
|
|
28
29
|
<KTooltip.Trigger class="inline-flex">{props.children}</KTooltip.Trigger>
|
|
29
|
-
<KTooltip.Portal>
|
|
30
|
+
<KTooltip.Portal ref={portalDir}>
|
|
30
31
|
<KTooltip.Content
|
|
31
32
|
class={cn(
|
|
32
33
|
'z-50 rounded-md border border-border bg-card px-2.5 py-1.5 text-xs text-card-foreground shadow-md',
|
package/src/ui/Tour.tsx
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// highlight.
|
|
9
9
|
import type { JSX } from 'solid-js'
|
|
10
10
|
import { createEffect, createSignal, onCleanup, onMount, Show } from 'solid-js'
|
|
11
|
-
import { Portal } from '
|
|
11
|
+
import { Portal } from './Portal'
|
|
12
12
|
|
|
13
13
|
import { cn } from '../lib/cn'
|
|
14
14
|
import { Button } from './Button'
|
|
@@ -86,7 +86,7 @@ export function TransactionFeed(props: TransactionFeedProps): JSX.Element {
|
|
|
86
86
|
<p class="truncate text-xs text-muted-foreground">{tx.subtitle}</p>
|
|
87
87
|
</Show>
|
|
88
88
|
</div>
|
|
89
|
-
<div class="shrink-0 text-
|
|
89
|
+
<div class="shrink-0 text-end">
|
|
90
90
|
<p
|
|
91
91
|
class={cn(
|
|
92
92
|
'text-sm font-medium tabular-nums',
|
package/src/ui/Transfer.tsx
CHANGED
|
@@ -100,10 +100,10 @@ export function Transfer(props: TransferProps): JSX.Element {
|
|
|
100
100
|
{pane(titles()[0], available)}
|
|
101
101
|
<div class="flex flex-col items-center justify-center gap-2">
|
|
102
102
|
<Button variant="outline" class="px-2" onClick={moveRight} aria-label="Move to selected">
|
|
103
|
-
<ChevronRight class="h-4 w-4" />
|
|
103
|
+
<ChevronRight class="h-4 w-4 rtl:-scale-x-100" />
|
|
104
104
|
</Button>
|
|
105
105
|
<Button variant="outline" class="px-2" onClick={moveLeft} aria-label="Move to available">
|
|
106
|
-
<ChevronLeft class="h-4 w-4" />
|
|
106
|
+
<ChevronLeft class="h-4 w-4 rtl:-scale-x-100" />
|
|
107
107
|
</Button>
|
|
108
108
|
</div>
|
|
109
109
|
{pane(titles()[1], chosen)}
|
package/src/ui/Tree.tsx
CHANGED
|
@@ -64,14 +64,14 @@ export function Tree(props: TreeProps): JSX.Element {
|
|
|
64
64
|
<li role="treeitem" aria-expanded={hasChildren() ? isExpanded() : undefined}>
|
|
65
65
|
<div
|
|
66
66
|
class="flex items-center gap-1.5 rounded-md px-2 py-1 text-sm text-foreground hover:bg-muted cursor-pointer"
|
|
67
|
-
style={{ 'padding-
|
|
67
|
+
style={{ 'padding-inline-start': `${nodeProps.depth * 1 + 0.5}rem` }}
|
|
68
68
|
onClick={() => hasChildren() && toggle(nodeProps.node.id)}
|
|
69
69
|
>
|
|
70
70
|
<Show when={hasChildren()} fallback={<span class="h-3.5 w-3.5 shrink-0" aria-hidden="true" />}>
|
|
71
71
|
<ChevronRight
|
|
72
72
|
class={cn(
|
|
73
73
|
'h-3.5 w-3.5 shrink-0 transition-transform duration-200',
|
|
74
|
-
isExpanded()
|
|
74
|
+
isExpanded() ? 'rotate-90' : 'rtl:-scale-x-100',
|
|
75
75
|
)}
|
|
76
76
|
aria-hidden="true"
|
|
77
77
|
/>
|