@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
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Accessor, JSX } from 'solid-js';
|
|
2
|
+
import { Direction } from '../lib/direction';
|
|
3
|
+
export type { Direction };
|
|
4
|
+
/** Props for {@link DirectionProvider}. */
|
|
5
|
+
export interface DirectionProviderProps {
|
|
6
|
+
/**
|
|
7
|
+
* Text direction. When omitted it is derived from `locale`; when both are
|
|
8
|
+
* omitted it is `'ltr'`.
|
|
9
|
+
*/
|
|
10
|
+
dir?: Direction;
|
|
11
|
+
/**
|
|
12
|
+
* BCP 47 locale (for example `'ar'`, `'he-IL'`). Forwarded to Kobalte for
|
|
13
|
+
* number/date formatting and collation. Its reading direction must agree
|
|
14
|
+
* with `dir`; a mismatch throws.
|
|
15
|
+
*/
|
|
16
|
+
locale?: string;
|
|
17
|
+
/** Extra classes for the wrapper element. */
|
|
18
|
+
class?: string;
|
|
19
|
+
children?: JSX.Element;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Sets the reading direction for everything inside it.
|
|
23
|
+
*
|
|
24
|
+
* - Renders a `display: contents` wrapper carrying the `dir` attribute, so
|
|
25
|
+
* Tailwind logical utilities (`ms-*`, `start-*`, `text-start`) and the
|
|
26
|
+
* browser's bidi handling follow it.
|
|
27
|
+
* - Wraps Kobalte's `I18nProvider`, so arrow keys in Tabs, Menubar, Slider,
|
|
28
|
+
* menus and the like reverse under `rtl`.
|
|
29
|
+
* - Overlays that portal out to `<body>` (Modal, Drawer, Popover, Dropdown,
|
|
30
|
+
* Toast, ...) copy the direction onto their own container.
|
|
31
|
+
*
|
|
32
|
+
* Without a provider nothing changes: components inherit `dir` from the page
|
|
33
|
+
* (set `<html dir="rtl">` yourself in that case).
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```tsx
|
|
37
|
+
* <DirectionProvider dir="rtl" locale="ar">
|
|
38
|
+
* <App />
|
|
39
|
+
* </DirectionProvider>
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare function DirectionProvider(props: DirectionProviderProps): JSX.Element;
|
|
43
|
+
/**
|
|
44
|
+
* Accessor of the current direction: the nearest {@link DirectionProvider}'s,
|
|
45
|
+
* or outside one the `dir` attribute of `<html>` (`'ltr'` when unset).
|
|
46
|
+
*/
|
|
47
|
+
export declare function useDirection(): Accessor<Direction>;
|
package/dist/ui/Drawer.d.ts
CHANGED
|
@@ -12,10 +12,14 @@ interface DrawerProps extends ParentProps {
|
|
|
12
12
|
/** Edge the panel is anchored to and slides in from. @default 'right' */
|
|
13
13
|
side?: DrawerSide;
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* `left` / `right` are physical edges. `start` / `end` follow the reading
|
|
17
|
+
* direction (`start` is the right edge under `dir="rtl"`).
|
|
18
|
+
*/
|
|
19
|
+
export type DrawerSide = 'right' | 'left' | 'start' | 'end' | 'top' | 'bottom';
|
|
16
20
|
export type { DrawerProps };
|
|
17
21
|
/**
|
|
18
|
-
* Edge-anchored slide-over panel (right by default, or `side="left" | "top" | "bottom"`) built on Kobalte's `Dialog` (focus trap, portal,
|
|
22
|
+
* Edge-anchored slide-over panel (right by default, or `side="left" | "start" | "end" | "top" | "bottom"`) built on Kobalte's `Dialog` (focus trap, portal,
|
|
19
23
|
* and open/close presence handled for you). Use for create/edit forms or detail
|
|
20
24
|
* panels that need more room than a centered `Modal`.
|
|
21
25
|
*
|
package/dist/ui/Sidebar.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Accessor, JSX, ParentProps } from 'solid-js';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Which edge the sidebar sits on. The layout is a flex row in DOM order, so the
|
|
4
|
+
* edge follows the reading direction: `start` (alias `left`) is the leading
|
|
5
|
+
* edge, `end` (alias `right`) the trailing one. Under `dir="rtl"` `start` is the
|
|
6
|
+
* right-hand side.
|
|
7
|
+
*/
|
|
8
|
+
export type SidebarSide = 'start' | 'end' | 'left' | 'right';
|
|
3
9
|
export type SidebarVariant = 'sidebar' | 'floating' | 'inset';
|
|
4
10
|
export type SidebarCollapsible = 'offcanvas' | 'icon' | 'none';
|
|
5
11
|
interface SidebarContextValue {
|
|
@@ -53,7 +59,7 @@ interface SidebarProviderProps extends ParentProps {
|
|
|
53
59
|
*/
|
|
54
60
|
export declare function SidebarProvider(props: SidebarProviderProps): JSX.Element;
|
|
55
61
|
interface SidebarProps extends ParentProps {
|
|
56
|
-
/** Which edge it sits on. @default '
|
|
62
|
+
/** Which edge it sits on; follows `dir` (see {@link SidebarSide}). @default 'start' */
|
|
57
63
|
side?: SidebarSide;
|
|
58
64
|
/** `sidebar` is flush, `floating` is a detached rounded panel, `inset` has no surface of its own (pair with `SidebarInset`). @default 'sidebar' */
|
|
59
65
|
variant?: SidebarVariant;
|
package/dist/ui/Toast.d.ts
CHANGED
|
@@ -17,5 +17,9 @@ export declare const toast: {
|
|
|
17
17
|
error: (title: string, description?: string) => void;
|
|
18
18
|
info: (title: string, description?: string) => void;
|
|
19
19
|
};
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Mount once near the app root — hosts the toast viewport. Mount it inside a
|
|
22
|
+
* `DirectionProvider` for RTL: the viewport sits in the end corner (the left
|
|
23
|
+
* one under RTL) and swipe-to-dismiss follows the direction.
|
|
24
|
+
*/
|
|
21
25
|
export declare function Toaster(): JSX.Element;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@a4ui/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.0",
|
|
4
4
|
"description": "A4ui — Spatial Glass design system & component library for SolidJS (Kobalte behavior + Tailwind glass tokens + motion).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
},
|
|
71
71
|
"./styles.css": "./dist/styles.css",
|
|
72
72
|
"./full.css": "./dist/full.css",
|
|
73
|
+
"./theme.css": "./dist/theme.css",
|
|
73
74
|
"./elements": {
|
|
74
75
|
"import": "./dist/elements.js",
|
|
75
76
|
"default": "./dist/elements.js"
|
|
@@ -86,8 +87,9 @@
|
|
|
86
87
|
},
|
|
87
88
|
"scripts": {
|
|
88
89
|
"prebuild": "node scripts/sync-version.mjs",
|
|
89
|
-
"build": "vite build && npm run build:elements",
|
|
90
|
+
"build": "vite build && npm run build:elements && npm run build:theme",
|
|
90
91
|
"build:elements": "vite build --config vite.elements.config.ts && node scripts/build-elements-css.mjs",
|
|
92
|
+
"build:theme": "node scripts/build-theme-css.mjs",
|
|
91
93
|
"dev": "vite build --watch",
|
|
92
94
|
"preview": "node scripts/gen-llms.mjs && vite --config vite.preview.config.ts",
|
|
93
95
|
"preview:build": "node scripts/gen-llms.mjs && vite build --config vite.preview.config.ts",
|
|
@@ -119,7 +121,7 @@
|
|
|
119
121
|
},
|
|
120
122
|
"peerDependencies": {
|
|
121
123
|
"solid-js": "^1.9.16",
|
|
122
|
-
"tailwindcss": "^3.4.0"
|
|
124
|
+
"tailwindcss": "^3.4.0 || ^4.0.0"
|
|
123
125
|
},
|
|
124
126
|
"dependencies": {
|
|
125
127
|
"@kobalte/core": "^0.13.14",
|
package/preset.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Types for `@a4ui/core/preset` — the Spatial Glass Tailwind preset.
|
|
2
|
-
// Requires Tailwind CSS v3 (`tailwindcss@^3.4.0`): v4 removed `presets:`.
|
|
2
|
+
// Requires Tailwind CSS v3 (`tailwindcss@^3.4.0`): v4 removed `presets:`. On v4, use
|
|
3
|
+
// `@import '@a4ui/core/theme.css'` instead; loading this preset there throws.
|
|
3
4
|
import type { Config } from 'tailwindcss'
|
|
4
5
|
|
|
5
6
|
/**
|
package/preset.js
CHANGED
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
// presets: [a4ui],
|
|
14
14
|
// content: ['./src/**/*.{ts,tsx}', './node_modules/@a4ui/core/dist/**/*.js'],
|
|
15
15
|
// }
|
|
16
|
+
//
|
|
17
|
+
// Tailwind 3 only. Tailwind 4 consumers use `@a4ui/core/theme.css`, which
|
|
18
|
+
// scripts/build-theme-css.mjs generates from this file.
|
|
16
19
|
|
|
17
20
|
// Explicit .js extension so the preset resolves under strict Node ESM (used by
|
|
18
21
|
// our elements-css build), not just bundlers.
|
|
@@ -21,7 +24,15 @@ import plugin from 'tailwindcss/plugin.js'
|
|
|
21
24
|
// Frosted "space glass" surfaces. addComponents so they tree-shake like any
|
|
22
25
|
// utility (emitted only when the class is found in scanned content — a4ui's own
|
|
23
26
|
// components use them, so scanning ./node_modules/@a4ui/core/dist covers it).
|
|
24
|
-
const glass = plugin(({ addComponents, addVariant }) => {
|
|
27
|
+
const glass = plugin(({ addComponents, addVariant, e }) => {
|
|
28
|
+
// Tailwind 4 reaches this only through `@config`. It would otherwise stop on a
|
|
29
|
+
// message about `addUtilities` that says nothing about the actual problem, and the
|
|
30
|
+
// components need more than this preset there anyway. `e` is a Tailwind 3 plugin API.
|
|
31
|
+
if (typeof e !== 'function') {
|
|
32
|
+
throw new Error(
|
|
33
|
+
"@a4ui/core/preset requires Tailwind 3. On Tailwind 4, remove the preset and add `@import '@a4ui/core/theme.css';` after `@import 'tailwindcss';` in your CSS.",
|
|
34
|
+
)
|
|
35
|
+
}
|
|
25
36
|
// `light:` applies when an ancestor is in the light theme (`[data-theme='light']`),
|
|
26
37
|
// mirroring how dark is the base and light is the override throughout A4ui. Used
|
|
27
38
|
// for theme-aware tints (e.g. Badge tones) that need a lighter foreground on dark
|
|
@@ -56,8 +67,8 @@ const glass = plugin(({ addComponents, addVariant }) => {
|
|
|
56
67
|
position: 'relative',
|
|
57
68
|
overflow: 'hidden',
|
|
58
69
|
background: 'hsl(var(--card) / 0.5)',
|
|
59
|
-
backdropFilter: 'blur(14px) saturate(180%)',
|
|
60
70
|
WebkitBackdropFilter: 'blur(14px) saturate(180%)',
|
|
71
|
+
backdropFilter: 'blur(14px) saturate(180%)',
|
|
61
72
|
border: '1px solid hsl(var(--foreground) / 0.14)',
|
|
62
73
|
borderRadius: 'var(--radius-xl, 1rem)',
|
|
63
74
|
boxShadow:
|
|
@@ -76,8 +87,8 @@ const glass = plugin(({ addComponents, addVariant }) => {
|
|
|
76
87
|
'.card': {
|
|
77
88
|
position: 'relative',
|
|
78
89
|
background: 'hsl(var(--card) / 0.55)',
|
|
79
|
-
backdropFilter: 'blur(8px) saturate(150%)',
|
|
80
90
|
WebkitBackdropFilter: 'blur(8px) saturate(150%)',
|
|
91
|
+
backdropFilter: 'blur(8px) saturate(150%)',
|
|
81
92
|
border: '1px solid hsl(var(--foreground) / 0.16)',
|
|
82
93
|
borderRadius: 'var(--radius-xl, 1rem)',
|
|
83
94
|
boxShadow: '0 1px 2px hsl(var(--shadow) / 0.05), 0 4px 12px hsl(var(--shadow) / 0.1)',
|
|
@@ -145,8 +156,8 @@ const glass = plugin(({ addComponents, addVariant }) => {
|
|
|
145
156
|
// ---- Floating overlay glass (menus, modals, drawers, toasts) ----
|
|
146
157
|
'.bg-glass': {
|
|
147
158
|
background: 'hsl(var(--card) / 0.72)',
|
|
148
|
-
backdropFilter: 'blur(14px) saturate(160%)',
|
|
149
159
|
WebkitBackdropFilter: 'blur(14px) saturate(160%)',
|
|
160
|
+
backdropFilter: 'blur(14px) saturate(160%)',
|
|
150
161
|
border: '1px solid hsl(var(--foreground) / 0.14)',
|
|
151
162
|
},
|
|
152
163
|
"[data-theme='light'] .bg-glass": {
|
|
@@ -174,8 +185,8 @@ const glass = plugin(({ addComponents, addVariant }) => {
|
|
|
174
185
|
},
|
|
175
186
|
'html.calm .card, html.calm .bg-glass': {
|
|
176
187
|
background: 'hsl(var(--card))',
|
|
177
|
-
backdropFilter: 'none',
|
|
178
188
|
WebkitBackdropFilter: 'none',
|
|
189
|
+
backdropFilter: 'none',
|
|
179
190
|
},
|
|
180
191
|
'html.calm .tile-glass': {
|
|
181
192
|
background: 'hsl(var(--muted))',
|
package/src/charts/BarList.tsx
CHANGED
|
@@ -54,7 +54,7 @@ export function BarList(props: BarListProps): JSX.Element {
|
|
|
54
54
|
{(datum) => (
|
|
55
55
|
<div class="relative isolate flex h-8 w-full min-w-0 items-center overflow-hidden rounded-md bg-muted/40">
|
|
56
56
|
<div
|
|
57
|
-
class={cn('absolute inset-y-0
|
|
57
|
+
class={cn('absolute inset-y-0 start-0 rounded-md transition-all', fillClass())}
|
|
58
58
|
style={{ width: `${Math.max((datum.value / max()) * 100, 0)}%` }}
|
|
59
59
|
/>
|
|
60
60
|
<Show
|
package/src/charts/Sparkline.tsx
CHANGED
|
@@ -17,6 +17,8 @@ export interface SparklineProps {
|
|
|
17
17
|
tone?: 'primary' | 'accent' | 'destructive'
|
|
18
18
|
/** Fill the area under the line faintly. Default false. */
|
|
19
19
|
area?: boolean
|
|
20
|
+
/** Accessible description, e.g. 'Revenue, rising'. Without it the chart is hidden from assistive tech as decoration. */
|
|
21
|
+
label?: string
|
|
20
22
|
class?: string
|
|
21
23
|
}
|
|
22
24
|
|
|
@@ -76,7 +78,9 @@ export function Sparkline(props: SparklineProps): JSX.Element {
|
|
|
76
78
|
height={height()}
|
|
77
79
|
viewBox={`0 0 ${width()} ${height()}`}
|
|
78
80
|
preserveAspectRatio="none"
|
|
79
|
-
role=
|
|
81
|
+
role={props.label ? 'img' : undefined}
|
|
82
|
+
aria-label={props.label}
|
|
83
|
+
aria-hidden={props.label ? undefined : 'true'}
|
|
80
84
|
>
|
|
81
85
|
<Show when={points().length > 0}>
|
|
82
86
|
<Show when={props.area}>
|
|
@@ -66,7 +66,7 @@ export function CartLine(props: CartLineProps): JSX.Element {
|
|
|
66
66
|
onChange={(quantity) => props.onQuantityChange?.(quantity)}
|
|
67
67
|
min={1}
|
|
68
68
|
/>
|
|
69
|
-
<span class="min-w-[4rem] text-
|
|
69
|
+
<span class="min-w-[4rem] text-end font-medium tabular-nums text-foreground">
|
|
70
70
|
{format(lineTotal())}
|
|
71
71
|
</span>
|
|
72
72
|
<Show when={props.onRemove}>
|
|
@@ -152,7 +152,7 @@ export function FacetSidebar(props: FacetSidebarProps): JSX.Element {
|
|
|
152
152
|
<Show when={props.onClearAll}>
|
|
153
153
|
<Button
|
|
154
154
|
variant="ghost"
|
|
155
|
-
class="
|
|
155
|
+
class="ms-auto h-auto px-2 py-1 text-xs"
|
|
156
156
|
onClick={() => props.onClearAll?.()}
|
|
157
157
|
>
|
|
158
158
|
Clear all
|
|
@@ -95,7 +95,7 @@ export function ProductCard(props: ProductCardProps): JSX.Element {
|
|
|
95
95
|
{/* Diagonal corner ribbon (clipped by the overflow-hidden image box). */}
|
|
96
96
|
<span
|
|
97
97
|
class={cn(
|
|
98
|
-
'absolute -
|
|
98
|
+
'absolute -end-9 top-3.5 w-32 rotate-45 py-1 text-center text-[11px] font-semibold uppercase tracking-wide shadow-md',
|
|
99
99
|
RIBBON_TONE[inferTone(props.badge, props.badgeTone)],
|
|
100
100
|
)}
|
|
101
101
|
>
|
package/src/index.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// import '@a4ui/core/styles.css'
|
|
9
9
|
// import { Button, Card, Modal } from '@a4ui/core'
|
|
10
10
|
|
|
11
|
-
export const A4UI_VERSION = '0.
|
|
11
|
+
export const A4UI_VERSION = '0.39.0'
|
|
12
12
|
|
|
13
13
|
// Helpers (src/lib) — generic, framework-level utilities.
|
|
14
14
|
export { cn } from './lib/cn'
|
|
@@ -277,6 +277,12 @@ export { ColorPicker, type ColorPickerProps } from './ui/ColorPicker'
|
|
|
277
277
|
export { Comment, type CommentData, type CommentProps } from './ui/Comment'
|
|
278
278
|
export { CalendarHeatmap, type HeatmapValue, type CalendarHeatmapProps } from './ui/CalendarHeatmap'
|
|
279
279
|
export { Portal, type PortalProps } from './ui/Portal'
|
|
280
|
+
export {
|
|
281
|
+
DirectionProvider,
|
|
282
|
+
useDirection,
|
|
283
|
+
type Direction,
|
|
284
|
+
type DirectionProviderProps,
|
|
285
|
+
} from './ui/DirectionProvider'
|
|
280
286
|
export { Sortable, type SortableProps } from './ui/Sortable'
|
|
281
287
|
export { Clock, type ClockProps } from './ui/Clock'
|
|
282
288
|
|
package/src/layout/NavGroup.tsx
CHANGED
|
@@ -28,7 +28,7 @@ export function NavGroup(props: NavGroupProps): JSX.Element {
|
|
|
28
28
|
<summary class="mt-3 flex cursor-pointer list-none select-none items-center justify-between rounded-md px-3 py-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground transition-colors hover:bg-muted hover:text-foreground [&::-webkit-details-marker]:hidden">
|
|
29
29
|
<span>{props.title}</span>
|
|
30
30
|
<svg
|
|
31
|
-
class="h-3.5 w-3.5 shrink-0 transition-transform duration-200 group-[[open]]:rotate-90"
|
|
31
|
+
class="h-3.5 w-3.5 shrink-0 transition-transform duration-200 group-[[open]]:rotate-90 rtl:-scale-x-100 rtl:group-[[open]]:scale-x-100"
|
|
32
32
|
viewBox="0 0 20 20"
|
|
33
33
|
fill="none"
|
|
34
34
|
stroke="currentColor"
|
|
@@ -39,7 +39,7 @@ export function NavGroup(props: NavGroupProps): JSX.Element {
|
|
|
39
39
|
<path d="M7 5l6 5-6 5" />
|
|
40
40
|
</svg>
|
|
41
41
|
</summary>
|
|
42
|
-
<div class="
|
|
42
|
+
<div class="ms-4 mt-1 space-y-0.5 border-s border-border ps-2">{props.children}</div>
|
|
43
43
|
</details>
|
|
44
44
|
)
|
|
45
45
|
}
|
package/src/lib/anchored.ts
CHANGED
|
@@ -55,3 +55,16 @@ export function createAnchorReflow(options: AnchorReflowOptions): () => void {
|
|
|
55
55
|
else options.place()
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Viewport `left` for a fixed panel aligned with its trigger's inline-start edge,
|
|
61
|
+
* kept 8px inside the viewport. Under `dir="rtl"` that is the trigger's right
|
|
62
|
+
* edge, so the panel extends leftwards. The direction is read from the trigger,
|
|
63
|
+
* which also works for a `dir` set by a provider or an ancestor element.
|
|
64
|
+
*/
|
|
65
|
+
export function anchoredLeft(anchor: HTMLElement, panelWidth: number): number {
|
|
66
|
+
const rect = anchor.getBoundingClientRect()
|
|
67
|
+
const rtl = getComputedStyle(anchor).direction === 'rtl'
|
|
68
|
+
const start = rtl ? rect.right - panelWidth : rect.left
|
|
69
|
+
return Math.max(8, Math.min(start, window.innerWidth - panelWidth - 8))
|
|
70
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Shared direction context. `DirectionProvider` (src/ui/DirectionProvider.tsx)
|
|
2
|
+
// provides it; overlays that render through a portal read it to stamp `dir`
|
|
3
|
+
// on their container, because a portal mounts outside the provider's wrapper
|
|
4
|
+
// element and so does not inherit its `dir` attribute.
|
|
5
|
+
import { type Accessor, createContext, createEffect, useContext } from 'solid-js'
|
|
6
|
+
|
|
7
|
+
/** Text direction supported by {@link DirectionProvider}. */
|
|
8
|
+
export type Direction = 'ltr' | 'rtl'
|
|
9
|
+
|
|
10
|
+
/** Accessor of the nearest provider's direction, or `undefined` outside one. */
|
|
11
|
+
export const DirectionContext = createContext<Accessor<Direction>>()
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Maps a physical horizontal arrow key to the key that means the same thing in
|
|
15
|
+
* reading order: under `rtl`, ArrowLeft moves forward and ArrowRight back, so
|
|
16
|
+
* handlers written for LTR can run on the swapped key. Other keys pass through.
|
|
17
|
+
*/
|
|
18
|
+
export function readingKey(key: string, dir: Direction): string {
|
|
19
|
+
if (dir !== 'rtl') return key
|
|
20
|
+
if (key === 'ArrowLeft') return 'ArrowRight'
|
|
21
|
+
if (key === 'ArrowRight') return 'ArrowLeft'
|
|
22
|
+
return key
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* `ref` for a portal container (Solid `Portal` or any Kobalte `*.Portal`).
|
|
27
|
+
* Copies the nearest `DirectionProvider`'s direction onto the container so the
|
|
28
|
+
* overlay renders in the same direction as the tree that opened it. Outside a
|
|
29
|
+
* provider it does nothing, so the overlay inherits from `<body>` as before.
|
|
30
|
+
*/
|
|
31
|
+
export function portalDir(el: Element): void {
|
|
32
|
+
const dir = useContext(DirectionContext)
|
|
33
|
+
if (!dir) return
|
|
34
|
+
createEffect(() => el.setAttribute('dir', dir()))
|
|
35
|
+
}
|
package/src/ui/Accordion.tsx
CHANGED
|
@@ -41,10 +41,10 @@ export function Accordion(props: AccordionProps): JSX.Element {
|
|
|
41
41
|
{(item) => (
|
|
42
42
|
<KAccordion.Item value={item.value} class="border-b border-border">
|
|
43
43
|
<KAccordion.Header>
|
|
44
|
-
<KAccordion.Trigger class="flex w-full items-center justify-between py-3 text-sm font-medium text-foreground [&[data-expanded]>svg]:rotate-90">
|
|
44
|
+
<KAccordion.Trigger class="flex w-full items-center justify-between py-3 text-sm font-medium text-foreground [&[data-expanded]>svg]:rotate-90 [&[data-expanded]>svg]:rtl:scale-x-100">
|
|
45
45
|
{item.title}
|
|
46
46
|
<svg
|
|
47
|
-
class="h-3.5 w-3.5 shrink-0 transition-transform duration-200"
|
|
47
|
+
class="h-3.5 w-3.5 shrink-0 transition-transform duration-200 rtl:-scale-x-100"
|
|
48
48
|
viewBox="0 0 20 20"
|
|
49
49
|
fill="none"
|
|
50
50
|
stroke="currentColor"
|
package/src/ui/ActivityFeed.tsx
CHANGED
|
@@ -136,11 +136,11 @@ export function ActivityFeed(props: ActivityFeedProps): JSX.Element {
|
|
|
136
136
|
<ol class="relative flex flex-col gap-5">
|
|
137
137
|
<For each={group.items}>
|
|
138
138
|
{(item, index) => (
|
|
139
|
-
<li class="relative flex gap-3
|
|
139
|
+
<li class="relative flex gap-3 ps-1">
|
|
140
140
|
<Show when={index() < group.items.length - 1}>
|
|
141
141
|
<span
|
|
142
142
|
aria-hidden="true"
|
|
143
|
-
class="absolute
|
|
143
|
+
class="absolute start-[1.375rem] top-9 -bottom-5 w-px -translate-x-1/2 bg-border"
|
|
144
144
|
/>
|
|
145
145
|
</Show>
|
|
146
146
|
<Show
|
package/src/ui/AlertDialog.tsx
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
// Confirmation dialog on Kobalte's AlertDialog primitive — centered modal,
|
|
2
2
|
// reuses the modal-overlay/modal-content keyframes from styles.css.
|
|
3
|
-
import
|
|
3
|
+
// Namespace import on purpose: Kobalte's `AlertDialog` is built with Object.assign on the
|
|
4
|
+
// same `DialogRoot` function as `Dialog`, so `AlertDialog.Content` depends on module load order (#118).
|
|
5
|
+
// The named exports (`Root`, `Content`, ...) are not affected.
|
|
6
|
+
import * as KAlertDialog from '@kobalte/core/alert-dialog'
|
|
4
7
|
import type { JSX, ParentProps } from 'solid-js'
|
|
5
8
|
import { Show } from 'solid-js'
|
|
6
9
|
|
|
7
10
|
import { cn } from '../lib/cn'
|
|
11
|
+
import { portalDir } from '../lib/direction'
|
|
8
12
|
|
|
9
13
|
interface AlertDialogProps extends ParentProps {
|
|
10
14
|
/** Whether the dialog is currently visible. */
|
|
@@ -30,8 +34,8 @@ interface AlertDialogProps extends ParentProps {
|
|
|
30
34
|
*/
|
|
31
35
|
export function AlertDialog(props: AlertDialogProps): JSX.Element {
|
|
32
36
|
return (
|
|
33
|
-
<KAlertDialog open={props.open} onOpenChange={props.onOpenChange}>
|
|
34
|
-
<KAlertDialog.Portal>
|
|
37
|
+
<KAlertDialog.Root open={props.open} onOpenChange={props.onOpenChange}>
|
|
38
|
+
<KAlertDialog.Portal ref={portalDir}>
|
|
35
39
|
<KAlertDialog.Overlay class="modal-overlay fixed inset-0 z-40 bg-background/80 backdrop-blur-sm" />
|
|
36
40
|
<div class="fixed inset-0 z-50 flex items-center justify-center p-4">
|
|
37
41
|
<KAlertDialog.Content
|
|
@@ -53,6 +57,6 @@ export function AlertDialog(props: AlertDialogProps): JSX.Element {
|
|
|
53
57
|
</KAlertDialog.Content>
|
|
54
58
|
</div>
|
|
55
59
|
</KAlertDialog.Portal>
|
|
56
|
-
</KAlertDialog>
|
|
60
|
+
</KAlertDialog.Root>
|
|
57
61
|
)
|
|
58
62
|
}
|
package/src/ui/Anchor.tsx
CHANGED
|
@@ -87,7 +87,7 @@ export function Anchor(props: AnchorProps): JSX.Element {
|
|
|
87
87
|
class={cn(
|
|
88
88
|
'block rounded px-3 py-1.5 text-sm transition-colors',
|
|
89
89
|
activeId() === item.id
|
|
90
|
-
? 'border-
|
|
90
|
+
? 'border-s-2 border-primary font-medium text-primary'
|
|
91
91
|
: 'text-muted-foreground hover:text-foreground',
|
|
92
92
|
)}
|
|
93
93
|
>
|
|
@@ -76,7 +76,7 @@ export function AnnouncementBar(props: AnnouncementBarProps): JSX.Element {
|
|
|
76
76
|
class={cn(
|
|
77
77
|
'relative w-full rounded-none px-4 py-2 text-center text-sm',
|
|
78
78
|
TONE[props.tone ?? 'primary'],
|
|
79
|
-
props.dismissible && '
|
|
79
|
+
props.dismissible && 'pe-10',
|
|
80
80
|
props.class,
|
|
81
81
|
)}
|
|
82
82
|
>
|
|
@@ -91,7 +91,7 @@ export function AnnouncementBar(props: AnnouncementBarProps): JSX.Element {
|
|
|
91
91
|
|
|
92
92
|
<Show when={props.couponCode}>
|
|
93
93
|
{(code) => (
|
|
94
|
-
<span class="
|
|
94
|
+
<span class="ms-2 inline-flex items-center gap-1 rounded-md bg-black/10 px-2 py-0.5 font-mono">
|
|
95
95
|
{code()}
|
|
96
96
|
<button
|
|
97
97
|
type="button"
|
|
@@ -113,7 +113,7 @@ export function AnnouncementBar(props: AnnouncementBarProps): JSX.Element {
|
|
|
113
113
|
type="button"
|
|
114
114
|
aria-label="Dismiss announcement"
|
|
115
115
|
onClick={dismiss}
|
|
116
|
-
class="absolute
|
|
116
|
+
class="absolute end-2 top-1/2 -translate-y-1/2 rounded-sm p-1 transition-opacity hover:opacity-80 motion-reduce:transition-none"
|
|
117
117
|
>
|
|
118
118
|
<X class="h-4 w-4" />
|
|
119
119
|
</button>
|
package/src/ui/ArtifactPanel.tsx
CHANGED
|
@@ -51,7 +51,7 @@ export function ArtifactPanel(props: ArtifactPanelProps): JSX.Element {
|
|
|
51
51
|
aria-hidden={!props.open}
|
|
52
52
|
class={cn(
|
|
53
53
|
'card h-full shrink-0 overflow-hidden border-border bg-glass',
|
|
54
|
-
props.open ? 'border-
|
|
54
|
+
props.open ? 'border-s' : 'border-0',
|
|
55
55
|
reduced() ? undefined : 'transition-[width] duration-300 ease-in-out',
|
|
56
56
|
props.class,
|
|
57
57
|
)}
|
|
@@ -90,7 +90,7 @@ export function AvailabilityPicker(props: AvailabilityPickerProps): JSX.Element
|
|
|
90
90
|
return (
|
|
91
91
|
<div class={cn('flex overflow-hidden rounded-xl border border-border bg-card', props.class)}>
|
|
92
92
|
{/* Date list */}
|
|
93
|
-
<div class="flex w-32 shrink-0 flex-col gap-1 overflow-y-auto border-
|
|
93
|
+
<div class="flex w-32 shrink-0 flex-col gap-1 overflow-y-auto border-e border-border p-2">
|
|
94
94
|
<For each={dates()} fallback={<p class="p-2 text-xs text-muted-foreground">No dates</p>}>
|
|
95
95
|
{(dateKey) => {
|
|
96
96
|
const d = parseDateKey(dateKey)
|
|
@@ -103,7 +103,7 @@ export function AvailabilityPicker(props: AvailabilityPickerProps): JSX.Element
|
|
|
103
103
|
disabled={count() === 0}
|
|
104
104
|
onClick={() => setViewingDate(dateKey)}
|
|
105
105
|
class={cn(
|
|
106
|
-
'flex flex-col items-start rounded-md px-2 py-1.5 text-
|
|
106
|
+
'flex flex-col items-start rounded-md px-2 py-1.5 text-start transition-colors',
|
|
107
107
|
isActive() ? 'bg-primary text-primary-foreground' : 'text-foreground hover:bg-muted',
|
|
108
108
|
count() === 0 && !isActive() && 'text-muted-foreground/50',
|
|
109
109
|
)}
|
package/src/ui/AvatarGroup.tsx
CHANGED
|
@@ -38,13 +38,13 @@ export function AvatarGroup(props: AvatarGroupProps): JSX.Element {
|
|
|
38
38
|
<div class={cn('flex items-center', props.class)}>
|
|
39
39
|
<For each={shown()}>
|
|
40
40
|
{(avatar, index) => (
|
|
41
|
-
<div class={cn('ring-2 ring-background rounded-full', index() > 0 && '-
|
|
41
|
+
<div class={cn('ring-2 ring-background rounded-full', index() > 0 && '-ms-2')}>
|
|
42
42
|
<Avatar src={avatar.src} alt={avatar.alt} fallback={avatar.fallback} />
|
|
43
43
|
</div>
|
|
44
44
|
)}
|
|
45
45
|
</For>
|
|
46
46
|
{overflow() > 0 && (
|
|
47
|
-
<div class="-
|
|
47
|
+
<div class="-ms-2 ring-2 ring-background rounded-full">
|
|
48
48
|
<span class="grid h-9 w-9 shrink-0 place-items-center rounded-full bg-muted text-xs font-medium text-muted-foreground">
|
|
49
49
|
+{overflow()}
|
|
50
50
|
</span>
|
package/src/ui/BackToTop.tsx
CHANGED
|
@@ -40,7 +40,7 @@ export function BackToTop(props: BackToTopProps): JSX.Element {
|
|
|
40
40
|
aria-label="Back to top"
|
|
41
41
|
onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
|
|
42
42
|
class={cn(
|
|
43
|
-
'fixed bottom-6
|
|
43
|
+
'fixed bottom-6 end-6 z-40 grid h-11 w-11 place-items-center rounded-full bg-primary text-primary-foreground shadow-lg',
|
|
44
44
|
props.class,
|
|
45
45
|
)}
|
|
46
46
|
>
|
package/src/ui/Badge.tsx
CHANGED
|
@@ -15,7 +15,7 @@ export type BadgeTone = 'neutral' | 'success' | 'warning' | 'danger' | 'info'
|
|
|
15
15
|
const TONE_CLASSES: Record<BadgeTone, string> = {
|
|
16
16
|
neutral: 'bg-muted text-muted-foreground ring-border',
|
|
17
17
|
success: 'bg-emerald-500/15 text-emerald-300 ring-emerald-500/30 light:text-emerald-700',
|
|
18
|
-
warning: 'bg-amber-500/15 text-amber-300 ring-amber-500/30 light:text-amber-
|
|
18
|
+
warning: 'bg-amber-500/15 text-amber-300 ring-amber-500/30 light:text-amber-800',
|
|
19
19
|
danger: 'bg-rose-500/15 text-rose-300 ring-rose-500/30 light:text-rose-700',
|
|
20
20
|
info: 'bg-sky-500/15 text-sky-300 ring-sky-500/30 light:text-sky-700',
|
|
21
21
|
}
|
package/src/ui/BlockEditor.tsx
CHANGED
|
@@ -115,7 +115,7 @@ export function BlockEditor(props: BlockEditorProps): JSX.Element {
|
|
|
115
115
|
onClick={() => setMenuOpen((open) => !open)}
|
|
116
116
|
aria-expanded={menuOpen()}
|
|
117
117
|
>
|
|
118
|
-
<Plus class="
|
|
118
|
+
<Plus class="me-1 h-4 w-4" />
|
|
119
119
|
Add block
|
|
120
120
|
</Button>
|
|
121
121
|
|
package/src/ui/ButtonGroup.tsx
CHANGED
|
@@ -17,7 +17,7 @@ interface ButtonGroupProps extends ParentProps {
|
|
|
17
17
|
export type { ButtonGroupProps }
|
|
18
18
|
|
|
19
19
|
const HORIZONTAL =
|
|
20
|
-
'flex-row [&>*:not(:first-child)]:-
|
|
20
|
+
'flex-row [&>*:not(:first-child)]:-ms-px [&>*:not(:first-child)]:rounded-s-none [&>*:not(:last-child)]:rounded-e-none'
|
|
21
21
|
const VERTICAL =
|
|
22
22
|
'flex-col [&>*]:w-full [&>*:not(:first-child)]:-mt-px [&>*:not(:first-child)]:rounded-t-none [&>*:not(:last-child)]:rounded-b-none'
|
|
23
23
|
|
package/src/ui/Callout.tsx
CHANGED
|
@@ -48,7 +48,7 @@ export interface CalloutProps {
|
|
|
48
48
|
export function Callout(props: CalloutProps): JSX.Element {
|
|
49
49
|
const tone = () => TONE[props.tone ?? 'info']
|
|
50
50
|
return (
|
|
51
|
-
<div class={cn('flex gap-3 rounded-lg border-
|
|
51
|
+
<div class={cn('flex gap-3 rounded-lg border-s-4 p-4 text-sm text-foreground', tone().wrap, props.class)}>
|
|
52
52
|
<Show
|
|
53
53
|
when={props.icon}
|
|
54
54
|
fallback={<Dynamic component={tone().Icon} class={cn('mt-0.5 h-4 w-4 shrink-0', tone().icon)} />}
|