@a4ui/core 0.11.1 → 0.13.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 +80 -37
- package/dist/elements.css +79 -9
- package/dist/full.css +3112 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.js +3392 -2537
- package/dist/lib/flyToCart.d.ts +25 -0
- package/dist/ui/Curtain.d.ts +44 -0
- package/dist/ui/Expandable.d.ts +34 -0
- package/dist/ui/FillText.d.ts +18 -0
- package/dist/ui/HoldToConfirm.d.ts +24 -0
- package/dist/ui/LoadingDots.d.ts +20 -0
- package/dist/ui/MultiStateBadge.d.ts +23 -0
- package/dist/ui/NotificationStack.d.ts +32 -0
- package/dist/ui/NowPlaying.d.ts +21 -0
- package/dist/ui/Parallax.d.ts +21 -0
- package/dist/ui/ScrambleText.d.ts +22 -0
- package/dist/ui/TextReveal.d.ts +23 -0
- package/package.json +8 -1
- package/src/charts/BarChart.tsx +55 -0
- package/src/charts/DonutChart.tsx +127 -0
- package/src/charts/Sparkline.tsx +101 -0
- package/src/charts/index.ts +5 -0
- package/src/commerce/CartLine.tsx +84 -0
- package/src/commerce/CartSummary.tsx +70 -0
- package/src/commerce/FilterGroup.tsx +70 -0
- package/src/commerce/PriceTag.tsx +62 -0
- package/src/commerce/ProductCard.tsx +121 -0
- package/src/commerce/ProductGrid.tsx +27 -0
- package/src/commerce/QuantityStepper.tsx +59 -0
- package/src/commerce/index.ts +10 -0
- package/src/elements.tsx +167 -0
- package/src/index.ts +186 -0
- package/src/layout/AppShell.tsx +102 -0
- package/src/layout/ChristmasBackground.tsx +177 -0
- package/src/layout/EffectsToggle.tsx +41 -0
- package/src/layout/NavGroup.tsx +45 -0
- package/src/layout/SnowScenery.tsx +76 -0
- package/src/layout/SpaceBackground.tsx +459 -0
- package/src/layout/ThemeToggle.tsx +40 -0
- package/src/layout/ThemedScenery.tsx +179 -0
- package/src/layout/sceneEffects.ts +49 -0
- package/src/lib/cn.ts +17 -0
- package/src/lib/effects.ts +71 -0
- package/src/lib/flyToCart.ts +103 -0
- package/src/lib/media.ts +27 -0
- package/src/lib/motion.ts +191 -0
- package/src/lib/theme.ts +113 -0
- package/src/lib/virtual.ts +33 -0
- package/src/styles/space.css +510 -0
- package/src/styles/tokens.css +213 -0
- package/src/themes/index.ts +102 -0
- package/src/themes/palettes.ts +368 -0
- package/src/ui/Accordion.tsx +65 -0
- package/src/ui/Affix.tsx +72 -0
- package/src/ui/Alert.tsx +55 -0
- package/src/ui/AlertDialog.tsx +58 -0
- package/src/ui/Anchor.tsx +100 -0
- package/src/ui/Avatar.tsx +34 -0
- package/src/ui/AvatarGroup.tsx +55 -0
- package/src/ui/BackToTop.tsx +51 -0
- package/src/ui/Badge.tsx +46 -0
- package/src/ui/BottomNavigation.tsx +65 -0
- package/src/ui/Breadcrumb.tsx +64 -0
- package/src/ui/Button.tsx +49 -0
- package/src/ui/Calendar.tsx +40 -0
- package/src/ui/CalendarHeatmap.tsx +180 -0
- package/src/ui/Card.tsx +84 -0
- package/src/ui/Carousel.tsx +121 -0
- package/src/ui/Cascader.tsx +158 -0
- package/src/ui/Checkbox.tsx +35 -0
- package/src/ui/Clock.tsx +221 -0
- package/src/ui/Collapse.tsx +48 -0
- package/src/ui/ColorPicker.tsx +89 -0
- package/src/ui/Combobox.tsx +67 -0
- package/src/ui/Command.tsx +158 -0
- package/src/ui/Comment.tsx +91 -0
- package/src/ui/ContextMenu.tsx +58 -0
- package/src/ui/Countdown.tsx +123 -0
- package/src/ui/Curtain.tsx +348 -0
- package/src/ui/DataGrid.tsx +177 -0
- package/src/ui/DateField.tsx +177 -0
- package/src/ui/DateRangePicker.tsx +204 -0
- package/src/ui/DateTimeField.tsx +68 -0
- package/src/ui/Descriptions.tsx +56 -0
- package/src/ui/Drawer.tsx +72 -0
- package/src/ui/Dropdown.tsx +71 -0
- package/src/ui/Dropzone.tsx +88 -0
- package/src/ui/Empty.tsx +53 -0
- package/src/ui/Expandable.tsx +229 -0
- package/src/ui/FileUpload.tsx +198 -0
- package/src/ui/FillText.tsx +63 -0
- package/src/ui/FloatingActionButton.tsx +48 -0
- package/src/ui/Form.tsx +108 -0
- package/src/ui/Highlight.tsx +53 -0
- package/src/ui/HoldToConfirm.tsx +141 -0
- package/src/ui/HoverCard.tsx +42 -0
- package/src/ui/Image.tsx +66 -0
- package/src/ui/Input.tsx +38 -0
- package/src/ui/Kbd.tsx +25 -0
- package/src/ui/List.tsx +71 -0
- package/src/ui/LoadingDots.tsx +75 -0
- package/src/ui/Marquee.tsx +48 -0
- package/src/ui/Mentions.tsx +170 -0
- package/src/ui/Meter.tsx +57 -0
- package/src/ui/Modal.tsx +106 -0
- package/src/ui/MultiSelect.tsx +236 -0
- package/src/ui/MultiStateBadge.tsx +93 -0
- package/src/ui/NotificationCenter.tsx +103 -0
- package/src/ui/NotificationStack.tsx +104 -0
- package/src/ui/NowPlaying.tsx +100 -0
- package/src/ui/NumberInput.tsx +48 -0
- package/src/ui/PageHeader.tsx +60 -0
- package/src/ui/Pagination.tsx +62 -0
- package/src/ui/Parallax.tsx +51 -0
- package/src/ui/Popover.tsx +42 -0
- package/src/ui/Portal.tsx +34 -0
- package/src/ui/Progress.tsx +55 -0
- package/src/ui/RadioGroup.tsx +65 -0
- package/src/ui/Rating.tsx +98 -0
- package/src/ui/Result.tsx +75 -0
- package/src/ui/RingProgress.tsx +75 -0
- package/src/ui/ScrambleText.tsx +95 -0
- package/src/ui/SegmentedControl.tsx +61 -0
- package/src/ui/Select.tsx +55 -0
- package/src/ui/Separator.tsx +39 -0
- package/src/ui/Skeleton.tsx +23 -0
- package/src/ui/Slider.tsx +57 -0
- package/src/ui/Sortable.tsx +174 -0
- package/src/ui/SpeedDial.tsx +85 -0
- package/src/ui/Spinner.tsx +33 -0
- package/src/ui/Splitter.tsx +115 -0
- package/src/ui/Stat.tsx +74 -0
- package/src/ui/Stepper.tsx +134 -0
- package/src/ui/Switch.tsx +44 -0
- package/src/ui/Table.tsx +128 -0
- package/src/ui/Tabs.tsx +72 -0
- package/src/ui/TagInput.tsx +85 -0
- package/src/ui/TextReveal.tsx +93 -0
- package/src/ui/Textarea.tsx +38 -0
- package/src/ui/TimeField.tsx +298 -0
- package/src/ui/Timeline.tsx +89 -0
- package/src/ui/Toast.tsx +68 -0
- package/src/ui/Toggle.tsx +41 -0
- package/src/ui/ToggleGroup.tsx +59 -0
- package/src/ui/Tooltip.tsx +42 -0
- package/src/ui/Tour.tsx +197 -0
- package/src/ui/Transfer.tsx +112 -0
- package/src/ui/Tree.tsx +100 -0
- package/src/ui/TreeSelect.tsx +151 -0
- package/src/ui/VirtualList.tsx +81 -0
- package/src/ui/internal/CalendarCore.tsx +276 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface FlyToCartOptions {
|
|
2
|
+
/** Image URL to fly; if omitted, the source element is cloned. */
|
|
3
|
+
image?: string;
|
|
4
|
+
/** Flight duration in seconds. @default 0.6 */
|
|
5
|
+
duration?: number;
|
|
6
|
+
/** Called when the flight finishes (e.g. to increment the cart count). */
|
|
7
|
+
onArrive?: () => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Animate a small ghost from `source` to `target` (e.g. product → cart icon),
|
|
11
|
+
* then a bump on the target. No-op under reduced motion (still calls onArrive).
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* function onAddToCart(e: MouseEvent) {
|
|
16
|
+
* const button = e.currentTarget as HTMLElement
|
|
17
|
+
* const cartIcon = document.getElementById('cart-icon')!
|
|
18
|
+
* flyToCart(button, cartIcon, {
|
|
19
|
+
* image: product.thumbnailUrl,
|
|
20
|
+
* onArrive: () => setCartCount((n) => n + 1),
|
|
21
|
+
* })
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare function flyToCart(source: Element, target: Element, opts?: FlyToCartOptions): void;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export type CurtainVariant = 'fade' | 'doors' | 'blinds' | 'iris' | 'shutter' | 'clip' | 'pixels';
|
|
3
|
+
export interface CurtainProps {
|
|
4
|
+
/** true = cover the screen; false = uncover (reveal content). */
|
|
5
|
+
show: boolean;
|
|
6
|
+
/** Which wipe style. @default 'fade' */
|
|
7
|
+
variant?: CurtainVariant;
|
|
8
|
+
/** Overlay color (CSS color / var). @default 'hsl(var(--background))' */
|
|
9
|
+
color?: string;
|
|
10
|
+
/** Seconds for one direction. @default 0.6 */
|
|
11
|
+
duration?: number;
|
|
12
|
+
/** Called once the screen is fully covered (show went true→covered). */
|
|
13
|
+
onCovered?: () => void;
|
|
14
|
+
/** Called once fully uncovered (show went false→revealed). */
|
|
15
|
+
onRevealed?: () => void;
|
|
16
|
+
class?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A controlled full-screen curtain for page/route transitions. Set `show` to
|
|
20
|
+
* `true` to cover the viewport, `false` to uncover it, and swap your route
|
|
21
|
+
* content while covered. `variant` picks the wipe style — fade, sliding
|
|
22
|
+
* doors, blinds, shutter (vertical blinds), iris/circle wipe, a left-to-right
|
|
23
|
+
* clip wipe, or a pixel-grid cascade —
|
|
24
|
+
* `onCovered` / `onRevealed` fire once each transition finishes so you can
|
|
25
|
+
* swap content at exactly the right moment. Skips the animation (and still
|
|
26
|
+
* fires the callback on the next microtask) under reduced motion.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```tsx
|
|
30
|
+
* const [covered, setCovered] = createSignal(false)
|
|
31
|
+
* const goTo = (path: string) => setCovered(true)
|
|
32
|
+
* return (
|
|
33
|
+
* <>
|
|
34
|
+
* <Curtain
|
|
35
|
+
* show={covered()}
|
|
36
|
+
* variant="iris"
|
|
37
|
+
* onCovered={() => { loadRoute(); setCovered(false) }}
|
|
38
|
+
* />
|
|
39
|
+
* <Router />
|
|
40
|
+
* </>
|
|
41
|
+
* )
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function Curtain(props: CurtainProps): JSX.Element;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface ExpandableProps {
|
|
3
|
+
/** Collapsed content — the card users click to expand. */
|
|
4
|
+
trigger: JSX.Element;
|
|
5
|
+
/** Expanded panel content. */
|
|
6
|
+
children: JSX.Element;
|
|
7
|
+
/** `dialog` = centered panel capped at `maxWidth`; `full` = near-fullscreen. @default 'dialog' */
|
|
8
|
+
size?: 'dialog' | 'full';
|
|
9
|
+
/** Max width (px) of the expanded panel when `size='dialog'`. @default 640 */
|
|
10
|
+
maxWidth?: number;
|
|
11
|
+
/** Notified when the panel opens/closes. */
|
|
12
|
+
onOpenChange?: (open: boolean) => void;
|
|
13
|
+
/** Class for the inline trigger wrapper. */
|
|
14
|
+
class?: string;
|
|
15
|
+
/** Class for the expanded panel surface. */
|
|
16
|
+
panelClass?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Expands a card into an overlay panel with a shared-element (FLIP) transition —
|
|
20
|
+
* the card appears to grow into the dialog and shrink back on close. Animates
|
|
21
|
+
* position + size (never scale) so content stays crisp. Falls back to a plain
|
|
22
|
+
* show/hide under reduced motion. Close with the ✕, a backdrop click, or Escape.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* <Expandable
|
|
27
|
+
* trigger={<Card class="cursor-pointer">Tap to expand</Card>}
|
|
28
|
+
* size="dialog"
|
|
29
|
+
* >
|
|
30
|
+
* <div class="p-6">Full details here…</div>
|
|
31
|
+
* </Expandable>
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function Expandable(props: ExpandableProps): JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface FillTextProps {
|
|
3
|
+
text: string;
|
|
4
|
+
/** Seconds per sweep. @default 1.6 */
|
|
5
|
+
duration?: number;
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Renders `text` with a bright gradient band that sweeps across the glyphs
|
|
10
|
+
* repeatedly, useful as a lightweight loading indicator. Respects
|
|
11
|
+
* `prefers-reduced-motion` by rendering plain muted text instead of animating.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* <FillText text="Loading results…" />
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare function FillText(props: FillTextProps): JSX.Element;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface HoldToConfirmProps {
|
|
3
|
+
onConfirm: () => void;
|
|
4
|
+
/** Button label. @default 'Hold to confirm' */
|
|
5
|
+
label?: string;
|
|
6
|
+
/** How long to hold, in ms. @default 1200 */
|
|
7
|
+
holdMs?: number;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Press-and-hold button for destructive/important actions: `onConfirm` only
|
|
13
|
+
* fires once the pointer (or Space/Enter) has been held down for `holdMs`.
|
|
14
|
+
* A fill layer sweeps left-to-right as visual progress; releasing early stops
|
|
15
|
+
* and resets it. Under reduced motion the hold is still required (gated by a
|
|
16
|
+
* plain timer instead of the tracked animation), but the fill jumps instead
|
|
17
|
+
* of sweeping and the success flourish is skipped.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* <HoldToConfirm label="Hold to delete" holdMs={1500} onConfirm={() => deleteItem(id)} />
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function HoldToConfirm(props: HoldToConfirmProps): JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface LoadingDotsProps {
|
|
3
|
+
/** Dot diameter in px. @default 8 */
|
|
4
|
+
size?: number;
|
|
5
|
+
/** Optional accessible label. @default 'Loading' */
|
|
6
|
+
label?: string;
|
|
7
|
+
class?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Three dots bouncing in a staggered wave — an inline, indeterminate loading
|
|
11
|
+
* indicator. Dots use `bg-current`, so color/size follow the ancestor's text
|
|
12
|
+
* color; diameter is set via `size`. Falls back to a gentle opacity pulse
|
|
13
|
+
* under reduced motion.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* <LoadingDots label="Loading results" class="text-primary" />
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function LoadingDots(props: LoadingDotsProps): JSX.Element;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
/** The lifecycle state a {@link MultiStateBadge} can be in. */
|
|
3
|
+
export type BadgeState = 'idle' | 'loading' | 'success' | 'error';
|
|
4
|
+
export interface MultiStateBadgeProps {
|
|
5
|
+
/** Current lifecycle state; drives color, icon, and label. */
|
|
6
|
+
state: BadgeState;
|
|
7
|
+
/** Override the per-state text. Defaults: idle→'Ready', loading→'Working…', success→'Done', error→'Failed'. */
|
|
8
|
+
labels?: Partial<Record<BadgeState, string>>;
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Status badge that animates between `idle` → `loading` → `success` → `error`,
|
|
13
|
+
* morphing its color and swapping its icon/label on each transition. Colors
|
|
14
|
+
* tween via a CSS `transition-colors` class; the pill also gets a spring
|
|
15
|
+
* "pop" and the icon/label cross-fade via Motion's `animate`, both skipped
|
|
16
|
+
* when {@link motionReduced} is true.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* <MultiStateBadge state={saveState()} labels={{ error: 'Save failed' }} />
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function MultiStateBadge(props: MultiStateBadgeProps): JSX.Element;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface StackNotification {
|
|
3
|
+
id: string | number;
|
|
4
|
+
title: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
icon?: JSX.Element;
|
|
7
|
+
}
|
|
8
|
+
export interface NotificationStackProps {
|
|
9
|
+
/** Newest first. The parent owns this array. */
|
|
10
|
+
items: StackNotification[];
|
|
11
|
+
/** How many cards are visible before the rest collapse behind. @default 3 */
|
|
12
|
+
max?: number;
|
|
13
|
+
/** Fired when a card is dismissed; the parent should drop it from `items`. */
|
|
14
|
+
onDismiss?: (id: string | number) => void;
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Stacked notifications. Cards behind the front one are offset down and scaled,
|
|
19
|
+
* so they peek from the bottom edge; removing the front card promotes the rest
|
|
20
|
+
* with a smooth transition (skipped under reduced motion). New cards fade/slide
|
|
21
|
+
* in via `animateIn`.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* const [items, setItems] = createStore<StackNotification[]>([])
|
|
26
|
+
* <NotificationStack
|
|
27
|
+
* items={items}
|
|
28
|
+
* onDismiss={(id) => setItems((xs) => xs.filter((x) => x.id !== id))}
|
|
29
|
+
* />
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function NotificationStack(props: NotificationStackProps): JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface NowPlayingProps {
|
|
3
|
+
title: string;
|
|
4
|
+
artist?: string;
|
|
5
|
+
/** Cover art URL. */
|
|
6
|
+
cover?: string;
|
|
7
|
+
/** Whether the equalizer bars are animating. @default true */
|
|
8
|
+
playing?: boolean;
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Compact "now playing" music widget with an animated equalizer. Bars bounce
|
|
13
|
+
* via CSS keyframes while `playing` is true; they freeze to a low static
|
|
14
|
+
* height when paused or under reduced motion.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* <NowPlaying title="Random Access Memories" artist="Daft Punk" cover="/covers/ram.jpg" playing />
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function NowPlaying(props: NowPlayingProps): JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface ParallaxProps {
|
|
3
|
+
children: JSX.Element;
|
|
4
|
+
/** Parallax strength; positive moves slower/opposite. Pixels of travel across the scroll range. @default 80 */
|
|
5
|
+
amount?: number;
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Wraps `children` in an element that drifts vertically as the page scrolls,
|
|
10
|
+
* creating a depth effect. Tracks scroll progress of the element itself
|
|
11
|
+
* against the viewport via Motion's `scroll`; respects `prefers-reduced-motion`
|
|
12
|
+
* (renders static).
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* <Parallax amount={120}>
|
|
17
|
+
* <img src="/hero.png" alt="" />
|
|
18
|
+
* </Parallax>
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function Parallax(props: ParallaxProps): JSX.Element;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface ScrambleTextProps {
|
|
3
|
+
text: string;
|
|
4
|
+
/** When to run: on mount, or on hover. @default 'mount' */
|
|
5
|
+
trigger?: 'mount' | 'hover';
|
|
6
|
+
/** Total scramble duration in ms. @default 800 */
|
|
7
|
+
duration?: number;
|
|
8
|
+
class?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Text that scrambles through random glyphs before "decoding" into the final
|
|
12
|
+
* string, left to right, over `duration` ms. Runs once on mount by default;
|
|
13
|
+
* pass `trigger="hover"` to (re)run it — cleanly restarting if it's still
|
|
14
|
+
* mid-scramble — on pointer hover instead. Renders the final text as-is, with
|
|
15
|
+
* no scramble, when the user prefers reduced motion.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <ScrambleText text="ACCESS GRANTED" trigger="hover" duration={600} />
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function ScrambleText(props: ScrambleTextProps): JSX.Element;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface TextRevealProps {
|
|
3
|
+
text: string;
|
|
4
|
+
/** Split unit. @default 'word' */
|
|
5
|
+
by?: 'word' | 'char';
|
|
6
|
+
/** Reveal when scrolled into view instead of on mount. @default false */
|
|
7
|
+
onView?: boolean;
|
|
8
|
+
class?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Reveals `text`, split into words or characters, with a staggered
|
|
12
|
+
* fade + slide-up (via Motion's `animate` + `stagger`). Runs once on mount by
|
|
13
|
+
* default; pass `onView` to instead reveal the first time the component
|
|
14
|
+
* scrolls into view. Whitespace between words renders as plain text, not an
|
|
15
|
+
* animated unit. Under reduced motion, everything stays visible and no
|
|
16
|
+
* animation runs.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* <TextReveal text="Ship it." by="char" onView />
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function TextReveal(props: TextRevealProps): JSX.Element;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@a4ui/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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",
|
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
],
|
|
30
30
|
"files": [
|
|
31
31
|
"dist",
|
|
32
|
+
"src",
|
|
33
|
+
"!src/**/*.test.ts",
|
|
34
|
+
"!src/**/*.test.tsx",
|
|
32
35
|
"preset.js"
|
|
33
36
|
],
|
|
34
37
|
"main": "./dist/index.js",
|
|
@@ -37,16 +40,19 @@
|
|
|
37
40
|
"exports": {
|
|
38
41
|
".": {
|
|
39
42
|
"types": "./dist/index.d.ts",
|
|
43
|
+
"solid": "./src/index.ts",
|
|
40
44
|
"import": "./dist/index.js",
|
|
41
45
|
"default": "./dist/index.js"
|
|
42
46
|
},
|
|
43
47
|
"./commerce": {
|
|
44
48
|
"types": "./dist/commerce/index.d.ts",
|
|
49
|
+
"solid": "./src/commerce/index.ts",
|
|
45
50
|
"import": "./dist/commerce.js",
|
|
46
51
|
"default": "./dist/commerce.js"
|
|
47
52
|
},
|
|
48
53
|
"./charts": {
|
|
49
54
|
"types": "./dist/charts/index.d.ts",
|
|
55
|
+
"solid": "./src/charts/index.ts",
|
|
50
56
|
"import": "./dist/charts.js",
|
|
51
57
|
"default": "./dist/charts.js"
|
|
52
58
|
},
|
|
@@ -55,6 +61,7 @@
|
|
|
55
61
|
"default": "./preset.js"
|
|
56
62
|
},
|
|
57
63
|
"./styles.css": "./dist/styles.css",
|
|
64
|
+
"./full.css": "./dist/full.css",
|
|
58
65
|
"./elements": {
|
|
59
66
|
"import": "./dist/elements.js",
|
|
60
67
|
"default": "./dist/elements.js"
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// BarChart — a simple vertical bar chart built from plain flex `div`s (no
|
|
2
|
+
// SVG, no charting library). Bar heights are percentages of the tallest
|
|
3
|
+
// value so the whole chart reflows with its container, and height changes
|
|
4
|
+
// animate via `transition-all`. Colors come from theme tokens only.
|
|
5
|
+
import { For, type JSX } from 'solid-js'
|
|
6
|
+
|
|
7
|
+
import { cn } from '../lib/cn'
|
|
8
|
+
|
|
9
|
+
export interface BarDatum {
|
|
10
|
+
label: string
|
|
11
|
+
value: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface BarChartProps {
|
|
15
|
+
data: BarDatum[]
|
|
16
|
+
/** Height of the plot area in px. Default 160. */
|
|
17
|
+
height?: number
|
|
18
|
+
/** Fill color token. Default 'primary'. */
|
|
19
|
+
tone?: 'primary' | 'accent'
|
|
20
|
+
class?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Vertical bar chart rendered with flexbox: each bar's height is `value /
|
|
25
|
+
* max` of the plot area, filled with a theme token, with its label truncated
|
|
26
|
+
* beneath it and its raw value available via a native tooltip.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```tsx
|
|
30
|
+
* <BarChart data={[{ label: 'Mon', value: 12 }, { label: 'Tue', value: 30 }]} tone="accent" />
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function BarChart(props: BarChartProps): JSX.Element {
|
|
34
|
+
const height = () => props.height ?? 160
|
|
35
|
+
const tone = () => props.tone ?? 'primary'
|
|
36
|
+
const max = () => Math.max(...props.data.map((d) => d.value), 0) || 1
|
|
37
|
+
const barClass = () => (tone() === 'accent' ? 'bg-accent' : 'bg-primary')
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div class={cn('flex w-full items-end gap-2', props.class)} style={{ height: `${height()}px` }}>
|
|
41
|
+
<For each={props.data}>
|
|
42
|
+
{(datum) => (
|
|
43
|
+
<div class="flex h-full min-w-0 flex-1 flex-col items-center justify-end gap-1">
|
|
44
|
+
<div
|
|
45
|
+
title={`${datum.label}: ${datum.value}`}
|
|
46
|
+
class={cn('w-full rounded-t transition-all', barClass())}
|
|
47
|
+
style={{ height: `${Math.max((datum.value / max()) * 100, 0)}%` }}
|
|
48
|
+
/>
|
|
49
|
+
<span class="w-full truncate text-center text-xs text-muted-foreground">{datum.label}</span>
|
|
50
|
+
</div>
|
|
51
|
+
)}
|
|
52
|
+
</For>
|
|
53
|
+
</div>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// DonutChart — a native SVG donut/ring chart (no charting library). Each
|
|
2
|
+
// segment is a `<circle>` stroked with `stroke-dasharray`/`stroke-dashoffset`
|
|
3
|
+
// to draw an arc proportional to `value / total`; the whole group is rotated
|
|
4
|
+
// so the first segment starts at 12 o'clock and segments run clockwise in
|
|
5
|
+
// order. Colors come from theme tokens, either per-segment `tone` or, when
|
|
6
|
+
// omitted, cycled across the `--data-*` series tokens by index.
|
|
7
|
+
import { For, Show, type JSX } from 'solid-js'
|
|
8
|
+
|
|
9
|
+
import { cn } from '../lib/cn'
|
|
10
|
+
|
|
11
|
+
export type DonutTone = 'primary' | 'accent' | 'destructive' | 'emit' | 'received' | 'net'
|
|
12
|
+
|
|
13
|
+
export interface DonutSegment {
|
|
14
|
+
label: string
|
|
15
|
+
value: number
|
|
16
|
+
tone?: DonutTone
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface DonutChartProps {
|
|
20
|
+
data: DonutSegment[]
|
|
21
|
+
/** Overall SVG width/height in px. Default 160. */
|
|
22
|
+
size?: number
|
|
23
|
+
/** Ring stroke width in px. Default 20. */
|
|
24
|
+
thickness?: number
|
|
25
|
+
/** Show the summed total in the center. Default true. */
|
|
26
|
+
showTotal?: boolean
|
|
27
|
+
class?: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Explicit tone -> CSS custom property name.
|
|
31
|
+
const TONE_TOKENS: Record<DonutTone, string> = {
|
|
32
|
+
primary: '--primary',
|
|
33
|
+
accent: '--accent',
|
|
34
|
+
destructive: '--destructive',
|
|
35
|
+
emit: '--data-emit',
|
|
36
|
+
received: '--data-received',
|
|
37
|
+
net: '--data-net',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Fallback rotation used when a segment has no explicit `tone`.
|
|
41
|
+
const CYCLE_TOKENS = ['--data-emit', '--data-received', '--data-net'] as const
|
|
42
|
+
|
|
43
|
+
/** Resolve the CSS custom property a segment should stroke with. */
|
|
44
|
+
function tokenFor(segment: DonutSegment, index: number): string {
|
|
45
|
+
if (segment.tone) return TONE_TOKENS[segment.tone]
|
|
46
|
+
return CYCLE_TOKENS[index % CYCLE_TOKENS.length]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* SVG donut chart: one arc per segment, sized by `value / total` of the
|
|
51
|
+
* circle's circumference, starting at 12 o'clock and running clockwise.
|
|
52
|
+
* Segment colors come from each datum's `tone`, or cycle through the
|
|
53
|
+
* `--data-*` series tokens by index when omitted.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```tsx
|
|
57
|
+
* <DonutChart
|
|
58
|
+
* data={[
|
|
59
|
+
* { label: 'Sent', value: 62, tone: 'emit' },
|
|
60
|
+
* { label: 'Received', value: 38, tone: 'received' },
|
|
61
|
+
* ]}
|
|
62
|
+
* />
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export function DonutChart(props: DonutChartProps): JSX.Element {
|
|
66
|
+
const size = () => props.size ?? 160
|
|
67
|
+
const thickness = () => props.thickness ?? 20
|
|
68
|
+
const showTotal = () => props.showTotal ?? true
|
|
69
|
+
const center = () => size() / 2
|
|
70
|
+
const radius = () => Math.max(size() / 2 - thickness() / 2, 0)
|
|
71
|
+
const circumference = () => 2 * Math.PI * radius()
|
|
72
|
+
const total = () => props.data.reduce((sum, d) => sum + d.value, 0)
|
|
73
|
+
|
|
74
|
+
const arcs = () => {
|
|
75
|
+
const grandTotal = total() || 1
|
|
76
|
+
let cumulative = 0
|
|
77
|
+
return props.data.map((segment, index) => {
|
|
78
|
+
const fraction = segment.value / grandTotal
|
|
79
|
+
const arcLength = fraction * circumference()
|
|
80
|
+
const offset = cumulative
|
|
81
|
+
cumulative += arcLength
|
|
82
|
+
return { segment, index, arcLength, offset }
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<div
|
|
88
|
+
class={cn('relative inline-flex items-center justify-center', props.class)}
|
|
89
|
+
style={{ width: `${size()}px`, height: `${size()}px` }}
|
|
90
|
+
>
|
|
91
|
+
<svg width={size()} height={size()} viewBox={`0 0 ${size()} ${size()}`} role="img">
|
|
92
|
+
<g transform={`rotate(-90 ${center()} ${center()})`}>
|
|
93
|
+
<circle
|
|
94
|
+
cx={center()}
|
|
95
|
+
cy={center()}
|
|
96
|
+
r={radius()}
|
|
97
|
+
fill="none"
|
|
98
|
+
stroke="hsl(var(--muted))"
|
|
99
|
+
stroke-width={thickness()}
|
|
100
|
+
/>
|
|
101
|
+
<For each={arcs()}>
|
|
102
|
+
{(arc) => (
|
|
103
|
+
<circle
|
|
104
|
+
cx={center()}
|
|
105
|
+
cy={center()}
|
|
106
|
+
r={radius()}
|
|
107
|
+
fill="none"
|
|
108
|
+
stroke={`hsl(var(${tokenFor(arc.segment, arc.index)}))`}
|
|
109
|
+
stroke-width={thickness()}
|
|
110
|
+
stroke-linecap="butt"
|
|
111
|
+
stroke-dasharray={`${arc.arcLength} ${Math.max(circumference() - arc.arcLength, 0)}`}
|
|
112
|
+
stroke-dashoffset={-arc.offset}
|
|
113
|
+
>
|
|
114
|
+
<title>{`${arc.segment.label}: ${arc.segment.value}`}</title>
|
|
115
|
+
</circle>
|
|
116
|
+
)}
|
|
117
|
+
</For>
|
|
118
|
+
</g>
|
|
119
|
+
</svg>
|
|
120
|
+
<Show when={showTotal()}>
|
|
121
|
+
<div class="absolute inset-0 flex items-center justify-center text-lg font-semibold tabular-nums text-foreground">
|
|
122
|
+
{total()}
|
|
123
|
+
</div>
|
|
124
|
+
</Show>
|
|
125
|
+
</div>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Sparkline — a tiny inline trend line rendered as a native SVG polyline (no
|
|
2
|
+
// charting library). Values are scaled to fill the viewBox (min -> bottom,
|
|
3
|
+
// max -> top, with a couple px of padding so the line never clips), stroked
|
|
4
|
+
// with a theme token so it recolors with the active palette. An optional
|
|
5
|
+
// faint area fill traces the same points down to the baseline.
|
|
6
|
+
import { Show, type JSX } from 'solid-js'
|
|
7
|
+
|
|
8
|
+
import { cn } from '../lib/cn'
|
|
9
|
+
|
|
10
|
+
export interface SparklineProps {
|
|
11
|
+
data: number[]
|
|
12
|
+
/** SVG viewBox width in px. Default 120. */
|
|
13
|
+
width?: number
|
|
14
|
+
/** SVG viewBox height in px. Default 32. */
|
|
15
|
+
height?: number
|
|
16
|
+
/** Stroke color token. Default 'primary'. */
|
|
17
|
+
tone?: 'primary' | 'accent' | 'destructive'
|
|
18
|
+
/** Fill the area under the line faintly. Default false. */
|
|
19
|
+
area?: boolean
|
|
20
|
+
class?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const PAD = 2
|
|
24
|
+
|
|
25
|
+
/** Build the `x,y` point list for `data` scaled into `w`×`h` (with padding). */
|
|
26
|
+
function pointsFor(data: number[], w: number, h: number): { x: number; y: number }[] {
|
|
27
|
+
if (data.length === 0) return []
|
|
28
|
+
if (data.length === 1) {
|
|
29
|
+
return [{ x: w / 2, y: h / 2 }]
|
|
30
|
+
}
|
|
31
|
+
const min = Math.min(...data)
|
|
32
|
+
const max = Math.max(...data)
|
|
33
|
+
const span = max - min || 1
|
|
34
|
+
const innerH = Math.max(h - PAD * 2, 0)
|
|
35
|
+
const stepX = w / (data.length - 1)
|
|
36
|
+
return data.map((value, i) => ({
|
|
37
|
+
x: i * stepX,
|
|
38
|
+
y: PAD + innerH - ((value - min) / span) * innerH,
|
|
39
|
+
}))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Minimal inline trend line: an SVG polyline scaled to fit `width`×`height`,
|
|
44
|
+
* stroked with a theme token so it recolors with the active palette. Pass
|
|
45
|
+
* `area` for a faint fill under the line.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```tsx
|
|
49
|
+
* <Sparkline data={[3, 5, 4, 8, 6, 9, 7]} tone="accent" area />
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export function Sparkline(props: SparklineProps): JSX.Element {
|
|
53
|
+
const width = () => props.width ?? 120
|
|
54
|
+
const height = () => props.height ?? 32
|
|
55
|
+
const tone = () => props.tone ?? 'primary'
|
|
56
|
+
const points = () => pointsFor(props.data, width(), height())
|
|
57
|
+
|
|
58
|
+
const polylinePoints = () =>
|
|
59
|
+
points()
|
|
60
|
+
.map((p) => `${p.x},${p.y}`)
|
|
61
|
+
.join(' ')
|
|
62
|
+
|
|
63
|
+
const areaPath = () => {
|
|
64
|
+
const pts = points()
|
|
65
|
+
if (pts.length === 0) return ''
|
|
66
|
+
const line = pts.map((p) => `${p.x},${p.y}`).join(' L ')
|
|
67
|
+
const first = pts[0]
|
|
68
|
+
const last = pts[pts.length - 1]
|
|
69
|
+
return `M ${first.x},${height()} L ${line} L ${last.x},${height()} Z`
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<svg
|
|
74
|
+
class={cn('overflow-visible', props.class)}
|
|
75
|
+
width={width()}
|
|
76
|
+
height={height()}
|
|
77
|
+
viewBox={`0 0 ${width()} ${height()}`}
|
|
78
|
+
preserveAspectRatio="none"
|
|
79
|
+
role="img"
|
|
80
|
+
>
|
|
81
|
+
<Show when={points().length > 0}>
|
|
82
|
+
<Show when={props.area}>
|
|
83
|
+
<path d={areaPath()} fill={`hsl(var(--${tone()}) / 0.15)`} stroke="none" />
|
|
84
|
+
</Show>
|
|
85
|
+
<Show
|
|
86
|
+
when={points().length > 1}
|
|
87
|
+
fallback={<circle cx={points()[0]?.x} cy={points()[0]?.y} r={2} fill={`hsl(var(--${tone()}))`} />}
|
|
88
|
+
>
|
|
89
|
+
<polyline
|
|
90
|
+
points={polylinePoints()}
|
|
91
|
+
fill="none"
|
|
92
|
+
stroke={`hsl(var(--${tone()}))`}
|
|
93
|
+
stroke-width={2}
|
|
94
|
+
stroke-linecap="round"
|
|
95
|
+
stroke-linejoin="round"
|
|
96
|
+
/>
|
|
97
|
+
</Show>
|
|
98
|
+
</Show>
|
|
99
|
+
</svg>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Barrel for the native-SVG chart set: theme-tokened, no external chart
|
|
2
|
+
// library. Re-exports each component alongside its prop types.
|
|
3
|
+
export { Sparkline, type SparklineProps } from './Sparkline'
|
|
4
|
+
export { BarChart, type BarDatum, type BarChartProps } from './BarChart'
|
|
5
|
+
export { DonutChart, type DonutSegment, type DonutChartProps } from './DonutChart'
|