@a4ui/core 0.38.0 → 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 +8 -4
- 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 +9 -5
- 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 +29 -8
- 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 +9 -5
- 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 +9 -5
- 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
|
@@ -85,19 +85,19 @@ export function PresenceAvatars(props: PresenceAvatarsProps): JSX.Element {
|
|
|
85
85
|
<For each={shown()}>
|
|
86
86
|
{(user, index) => (
|
|
87
87
|
<div
|
|
88
|
-
class={cn('relative rounded-full ring-2 ring-background', index() > 0 && '-
|
|
88
|
+
class={cn('relative rounded-full ring-2 ring-background', index() > 0 && '-ms-2')}
|
|
89
89
|
title={user.name}
|
|
90
90
|
>
|
|
91
91
|
<Avatar src={user.avatar} alt={user.name} fallback={initials(user.name)} />
|
|
92
92
|
<span
|
|
93
93
|
aria-hidden="true"
|
|
94
|
-
class="absolute bottom-0
|
|
94
|
+
class="absolute bottom-0 end-0 h-2.5 w-2.5 rounded-full bg-emerald-500 ring-2 ring-background"
|
|
95
95
|
/>
|
|
96
96
|
</div>
|
|
97
97
|
)}
|
|
98
98
|
</For>
|
|
99
99
|
<Show when={overflow() > 0}>
|
|
100
|
-
<div class="-
|
|
100
|
+
<div class="-ms-2 rounded-full ring-2 ring-background">
|
|
101
101
|
<span class="grid h-9 w-9 shrink-0 place-items-center rounded-full bg-muted text-xs font-medium text-muted-foreground">
|
|
102
102
|
+{overflow()}
|
|
103
103
|
</span>
|
|
@@ -125,7 +125,7 @@ export function PresenceAvatars(props: PresenceAvatarsProps): JSX.Element {
|
|
|
125
125
|
/>
|
|
126
126
|
</svg>
|
|
127
127
|
<span
|
|
128
|
-
class="
|
|
128
|
+
class="ms-1.5 whitespace-nowrap rounded-full px-2 py-0.5 text-xs font-medium text-white shadow"
|
|
129
129
|
style={{ 'background-color': color() }}
|
|
130
130
|
>
|
|
131
131
|
{user()!.name}
|
package/src/ui/PricingTable.tsx
CHANGED
|
@@ -131,7 +131,7 @@ export function PricingTable(props: PricingTableProps): JSX.Element {
|
|
|
131
131
|
class={cn('relative flex flex-col', tier.highlighted && 'ring-2 ring-primary')}
|
|
132
132
|
>
|
|
133
133
|
<Show when={tier.highlighted}>
|
|
134
|
-
<Badge tone="info" class="absolute -top-3
|
|
134
|
+
<Badge tone="info" class="absolute -top-3 end-6">
|
|
135
135
|
Popular
|
|
136
136
|
</Badge>
|
|
137
137
|
</Show>
|
package/src/ui/Prose.tsx
CHANGED
|
@@ -29,14 +29,14 @@ const PROSE =
|
|
|
29
29
|
'[&_h4]:mb-2 [&_h4]:mt-4 [&_h4]:font-semibold ' +
|
|
30
30
|
'[&_p]:my-4 [&_.lead]:text-muted-foreground ' +
|
|
31
31
|
'[&_a]:font-medium [&_a]:text-primary [&_a]:underline [&_a]:underline-offset-4 [&_a:hover]:opacity-80 ' +
|
|
32
|
-
'[&_blockquote]:my-6 [&_blockquote]:border-
|
|
33
|
-
'[&_ul]:my-4 [&_ul]:list-disc [&_ul]:
|
|
32
|
+
'[&_blockquote]:my-6 [&_blockquote]:border-s-2 [&_blockquote]:border-border [&_blockquote]:ps-4 [&_blockquote]:italic [&_blockquote]:text-muted-foreground ' +
|
|
33
|
+
'[&_ul]:my-4 [&_ul]:list-disc [&_ul]:ps-6 [&_ol]:my-4 [&_ol]:list-decimal [&_ol]:ps-6 [&_li]:my-1 ' +
|
|
34
34
|
'[&_code]:rounded [&_code]:bg-muted [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-[0.875em] ' +
|
|
35
35
|
'[&_pre]:my-4 [&_pre]:overflow-x-auto [&_pre]:rounded-lg [&_pre]:border [&_pre]:border-border [&_pre]:bg-muted [&_pre]:p-4 ' +
|
|
36
36
|
'[&_pre_code]:bg-transparent [&_pre_code]:p-0 ' +
|
|
37
37
|
'[&_hr]:my-8 [&_hr]:border-border ' +
|
|
38
38
|
'[&_table]:my-6 [&_table]:w-full [&_table]:border-collapse [&_table]:text-sm ' +
|
|
39
|
-
'[&_th]:border-b [&_th]:border-border [&_th]:px-3 [&_th]:py-2 [&_th]:text-
|
|
39
|
+
'[&_th]:border-b [&_th]:border-border [&_th]:px-3 [&_th]:py-2 [&_th]:text-start [&_th]:font-semibold ' +
|
|
40
40
|
'[&_td]:border-b [&_td]:border-border [&_td]:px-3 [&_td]:py-2'
|
|
41
41
|
|
|
42
42
|
/**
|
package/src/ui/QueryBuilder.tsx
CHANGED
|
@@ -143,7 +143,7 @@ function GroupView(props: GroupViewProps): JSX.Element {
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
return (
|
|
146
|
-
<div class={cn('flex flex-col gap-2 rounded-md p-2', props.depth > 0 && 'border-
|
|
146
|
+
<div class={cn('flex flex-col gap-2 rounded-md p-2', props.depth > 0 && 'border-s-2 border-border ps-3')}>
|
|
147
147
|
<div class="flex flex-wrap items-center gap-2">
|
|
148
148
|
<div
|
|
149
149
|
class="inline-flex overflow-hidden rounded-md border border-input"
|
|
@@ -169,18 +169,18 @@ function GroupView(props: GroupViewProps): JSX.Element {
|
|
|
169
169
|
</For>
|
|
170
170
|
</div>
|
|
171
171
|
<Button variant="outline" onClick={addRule}>
|
|
172
|
-
<Plus class="
|
|
172
|
+
<Plus class="me-1 h-3.5 w-3.5" aria-hidden="true" />
|
|
173
173
|
Rule
|
|
174
174
|
</Button>
|
|
175
175
|
<Button variant="outline" onClick={addGroup}>
|
|
176
|
-
<Plus class="
|
|
176
|
+
<Plus class="me-1 h-3.5 w-3.5" aria-hidden="true" />
|
|
177
177
|
Group
|
|
178
178
|
</Button>
|
|
179
179
|
<Show when={props.onRemove}>
|
|
180
180
|
{(remove) => (
|
|
181
181
|
<button
|
|
182
182
|
type="button"
|
|
183
|
-
class="
|
|
183
|
+
class="ms-auto rounded-md p-1.5 text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
184
184
|
aria-label="Remove group"
|
|
185
185
|
onClick={() => remove()()}
|
|
186
186
|
>
|
package/src/ui/Rating.tsx
CHANGED
|
@@ -6,6 +6,8 @@ import type { JSX } from 'solid-js'
|
|
|
6
6
|
import { createSignal, For, Show } from 'solid-js'
|
|
7
7
|
|
|
8
8
|
import { cn } from '../lib/cn'
|
|
9
|
+
import { readingKey } from '../lib/direction'
|
|
10
|
+
import { useDirection } from './DirectionProvider'
|
|
9
11
|
|
|
10
12
|
export interface RatingProps {
|
|
11
13
|
/** Currently selected rating, from 0 to `max`. */
|
|
@@ -30,6 +32,7 @@ export interface RatingProps {
|
|
|
30
32
|
* ```
|
|
31
33
|
*/
|
|
32
34
|
export function Rating(props: RatingProps): JSX.Element {
|
|
35
|
+
const dir = useDirection()
|
|
33
36
|
const [hover, setHover] = createSignal(0)
|
|
34
37
|
const max = () => props.max ?? 5
|
|
35
38
|
const stars = () => Array.from({ length: max() }, (_, i) => i + 1)
|
|
@@ -79,10 +82,11 @@ export function Rating(props: RatingProps): JSX.Element {
|
|
|
79
82
|
onBlur={() => setHover(0)}
|
|
80
83
|
onClick={() => set(n)}
|
|
81
84
|
onKeyDown={(e) => {
|
|
82
|
-
|
|
85
|
+
const key = readingKey(e.key, dir())
|
|
86
|
+
if (key === 'ArrowRight' || key === 'ArrowUp') {
|
|
83
87
|
e.preventDefault()
|
|
84
88
|
set(Math.min(props.value + 1, max()))
|
|
85
|
-
} else if (
|
|
89
|
+
} else if (key === 'ArrowLeft' || key === 'ArrowDown') {
|
|
86
90
|
e.preventDefault()
|
|
87
91
|
set(Math.max(props.value - 1, 0))
|
|
88
92
|
}
|
|
@@ -46,7 +46,7 @@ export function ReasoningTrace(props: ReasoningTraceProps): JSX.Element {
|
|
|
46
46
|
class="flex w-full items-center gap-2 rounded-xl px-3 py-2 text-sm font-medium text-muted-foreground hover:bg-muted"
|
|
47
47
|
>
|
|
48
48
|
<Brain class="h-4 w-4 shrink-0" aria-hidden="true" />
|
|
49
|
-
<span class="flex-1 text-
|
|
49
|
+
<span class="flex-1 text-start">{props.label ?? 'Reasoning'}</span>
|
|
50
50
|
<Show when={props.streaming}>
|
|
51
51
|
<span class="relative flex h-1.5 w-1.5 shrink-0" aria-hidden="true">
|
|
52
52
|
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-current opacity-75 motion-reduce:animate-none" />
|
package/src/ui/ScrollArea.tsx
CHANGED
|
@@ -59,7 +59,7 @@ export function ScrollArea(props: ScrollAreaProps): JSX.Element {
|
|
|
59
59
|
'[scrollbar-color:hsl(var(--muted-foreground)/0.35)_transparent] [scrollbar-width:thin]',
|
|
60
60
|
'[&::-webkit-scrollbar]:h-2 [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-track]:bg-transparent',
|
|
61
61
|
'[&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-muted-foreground/35',
|
|
62
|
-
'
|
|
62
|
+
'[&::-webkit-scrollbar-thumb:hover]:bg-muted-foreground/55',
|
|
63
63
|
AXIS[local.orientation ?? 'vertical'],
|
|
64
64
|
local.class,
|
|
65
65
|
)}
|
|
@@ -44,12 +44,12 @@ export function ScrollProgress(props: ScrollProgressProps): JSX.Element {
|
|
|
44
44
|
return (
|
|
45
45
|
<div
|
|
46
46
|
aria-hidden="true"
|
|
47
|
-
class={cn('fixed top-0
|
|
47
|
+
class={cn('fixed top-0 start-0 z-[9999] w-full', props.class)}
|
|
48
48
|
style={{ height: `${props.height ?? 3}px` }}
|
|
49
49
|
>
|
|
50
50
|
<div
|
|
51
51
|
ref={bar}
|
|
52
|
-
class="origin-left will-change-transform"
|
|
52
|
+
class="origin-left rtl:origin-right will-change-transform"
|
|
53
53
|
style={{
|
|
54
54
|
height: '100%',
|
|
55
55
|
width: '100%',
|
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
|
)
|