@atmos.build/ui 0.1.6 → 0.1.8
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.
|
@@ -7,7 +7,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigge
|
|
|
7
7
|
import { MiddleTruncate } from './middle-truncate.js';
|
|
8
8
|
import { StickyHeaderFade } from './sticky-header-fade.js';
|
|
9
9
|
export function DetailTopBar({ title, trail, meta, actions, className, contentClassName, leftClassName, actionsClassName, chromeFont, headingFont, dataTestId, fade = true, }) {
|
|
10
|
-
return (_jsxs("div", { className: cn('bg-background/95 sticky top-0 z-50 flex h-10 items-center justify-between gap-3 px-
|
|
10
|
+
return (_jsxs("div", { className: cn('bg-background/95 sticky top-0 z-50 flex h-10 items-center justify-between gap-3 px-4 backdrop-blur', chromeFont, className), "data-slot": "detail-top-bar", "data-testid": dataTestId, children: [_jsxs("div", { className: cn('flex min-w-0 flex-1 items-center gap-3 overflow-hidden', contentClassName), children: [_jsx("div", { className: cn('flex min-w-0 flex-1 items-center overflow-hidden', leftClassName), children: trail ?? (_jsx("h1", { className: cn('text-foreground min-w-0 truncate text-[13px] leading-5 font-medium', headingFont), children: title })) }), meta ? (_jsx("div", { className: "text-muted-foreground min-w-0 shrink truncate font-mono text-[11.5px]", children: meta })) : null] }), actions ? (_jsx("div", { className: cn('flex shrink-0 items-center gap-1', actionsClassName), children: actions })) : null, fade ? _jsx(StickyHeaderFade, { "data-slot": "detail-top-bar-fade" }) : null] }));
|
|
11
11
|
}
|
|
12
12
|
export function DetailBreadcrumb({ items, ariaLabel, className, separator = '/', collapseMiddle = false, tailItems = 2, collapsedAriaLabel, dataTestId, }) {
|
|
13
13
|
const collapsedItems = collapseMiddle ? collapsedMiddleItems(items, tailItems) : [];
|
|
@@ -6,6 +6,7 @@ export interface NautAppearance {
|
|
|
6
6
|
shape: NautShape;
|
|
7
7
|
color: NautColor;
|
|
8
8
|
}
|
|
9
|
+
export type NautAnimationRate = 'efficient' | 'smooth';
|
|
9
10
|
export interface NautAvatarProps extends Omit<React.SVGProps<SVGSVGElement>, 'ref'> {
|
|
10
11
|
shape?: NautShape;
|
|
11
12
|
color?: NautColor;
|
|
@@ -19,6 +20,7 @@ export interface NautAvatarProps extends Omit<React.SVGProps<SVGSVGElement>, 're
|
|
|
19
20
|
} | null;
|
|
20
21
|
/** Glance at a mouse that comes near the face. Ignored once `gaze` is given. */
|
|
21
22
|
followPointer?: boolean;
|
|
23
|
+
animationRate?: NautAnimationRate;
|
|
22
24
|
/** Any stable id — keeps a list of Nauts from blinking in unison. */
|
|
23
25
|
seed?: string | number;
|
|
24
26
|
}
|
|
@@ -10,6 +10,7 @@ export { NAUT_COLORS, NAUT_COLOR_KEYS, NAUT_EXPRESSIONS, NAUT_GESTURES, NAUT_SHA
|
|
|
10
10
|
const RECT_MAX_AGE_MS = 500;
|
|
11
11
|
const IDLE_FRAME_MS = 1000 / 3;
|
|
12
12
|
const RESPONSIVE_FRAME_MS = 1000 / 20;
|
|
13
|
+
const SMOOTH_FRAME_MS = 1000 / 60;
|
|
13
14
|
const POINTER_SETTLE_MS = 350;
|
|
14
15
|
/** Where the mouse is, shared by every face so one listener serves the page. */
|
|
15
16
|
const pointer = {
|
|
@@ -75,7 +76,11 @@ const ticker = {
|
|
|
75
76
|
if (!entry.visible)
|
|
76
77
|
continue;
|
|
77
78
|
const responsive = (entry.follow && pointerResponsive) || entry.engine.needsResponsiveFrameRate;
|
|
78
|
-
const frameMs =
|
|
79
|
+
const frameMs = entry.smooth
|
|
80
|
+
? SMOOTH_FRAME_MS
|
|
81
|
+
: responsive
|
|
82
|
+
? RESPONSIVE_FRAME_MS
|
|
83
|
+
: IDLE_FRAME_MS;
|
|
79
84
|
if (now - entry.lastUpdate < frameMs)
|
|
80
85
|
continue;
|
|
81
86
|
const dt = Math.min((now - entry.lastUpdate) / 1000, 0.1);
|
|
@@ -111,7 +116,7 @@ const seedNumber = (seed) => typeof seed === 'number' ? seed : hashSeed(seed ??
|
|
|
111
116
|
* look around. The eyes are holes, so set `--naut-avatar-eye` to the surface the
|
|
112
117
|
* avatar sits on (defaults to `--background`).
|
|
113
118
|
*/
|
|
114
|
-
export const NautAvatar = React.forwardRef(function NautAvatar({ shape = 'circle', color = 'ink', expression = 'neutral', idle = true, gaze = null, followPointer = true, seed, className, style, ...props }, forwardedRef) {
|
|
119
|
+
export const NautAvatar = React.forwardRef(function NautAvatar({ shape = 'circle', color = 'ink', expression = 'neutral', idle = true, gaze = null, followPointer = true, animationRate = 'efficient', seed, className, style, ...props }, forwardedRef) {
|
|
115
120
|
const reducedMotion = useReducedMotion();
|
|
116
121
|
const animated = idle && !reducedMotion;
|
|
117
122
|
const svgRef = React.useRef(null);
|
|
@@ -126,6 +131,7 @@ export const NautAvatar = React.forwardRef(function NautAvatar({ shape = 'circle
|
|
|
126
131
|
svg: null,
|
|
127
132
|
visible: true,
|
|
128
133
|
follow: false,
|
|
134
|
+
smooth: false,
|
|
129
135
|
rect: null,
|
|
130
136
|
rectAt: -1,
|
|
131
137
|
rectTime: 0,
|
|
@@ -141,7 +147,8 @@ export const NautAvatar = React.forwardRef(function NautAvatar({ shape = 'circle
|
|
|
141
147
|
engine.idle = animated;
|
|
142
148
|
engine.gazeTarget = gaze;
|
|
143
149
|
entry.follow = followPointer && !gaze && animated;
|
|
144
|
-
|
|
150
|
+
entry.smooth = animationRate === 'smooth';
|
|
151
|
+
}, [engine, entry, shape, color, expression, animated, gaze, followPointer, animationRate]);
|
|
145
152
|
React.useLayoutEffect(() => {
|
|
146
153
|
if (!rootRef.current ||
|
|
147
154
|
!bodyRef.current ||
|
|
@@ -191,5 +198,5 @@ export const NautAvatar = React.forwardRef(function NautAvatar({ shape = 'circle
|
|
|
191
198
|
engine.settle();
|
|
192
199
|
engine.update(0);
|
|
193
200
|
}, [animated, engine, shape, color, expression]);
|
|
194
|
-
return (_jsx("svg", { ref: svgRef, viewBox: NAUT_VIEWBOX, "data-slot": "naut-avatar", "data-shape": shape, "data-color": color, "data-expression": expression, "data-atmos-motion": animated ? 'running' : undefined, "data-atmos-motion-property": animated ? 'svg-attributes' : undefined, className: cn('block size-full overflow-visible select-none', className), style: style, "aria-hidden": props['aria-label'] ? undefined : true, ...props, children: _jsxs("g", { ref: rootRef, children: [_jsx("path", { ref: bodyRef }), _jsxs("g", { ref: eyesRef, children: [_jsx("path", { ref: eyeLRef, fill: "var(--naut-avatar-eye, var(--background))" }), _jsx("path", { ref: eyeRRef, fill: "var(--naut-avatar-eye, var(--background))" })] })] }) }));
|
|
201
|
+
return (_jsx("svg", { ref: svgRef, viewBox: NAUT_VIEWBOX, "data-slot": "naut-avatar", "data-shape": shape, "data-color": color, "data-expression": expression, "data-animation-rate": animationRate, "data-atmos-motion": animated ? 'running' : undefined, "data-atmos-motion-property": animated ? 'svg-attributes' : undefined, className: cn('block size-full overflow-visible select-none', className), style: style, "aria-hidden": props['aria-label'] ? undefined : true, ...props, children: _jsxs("g", { ref: rootRef, children: [_jsx("path", { ref: bodyRef }), _jsxs("g", { ref: eyesRef, children: [_jsx("path", { ref: eyeLRef, fill: "var(--naut-avatar-eye, var(--background))" }), _jsx("path", { ref: eyeRRef, fill: "var(--naut-avatar-eye, var(--background))" })] })] }) }));
|
|
195
202
|
});
|
|
@@ -2,6 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
import { type VariantProps } from 'class-variance-authority';
|
|
3
3
|
declare const selectableCardVariants: (props?: ({
|
|
4
4
|
layout?: "grid" | "row" | null | undefined;
|
|
5
|
+
appearance?: "list" | "card" | null | undefined;
|
|
5
6
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
6
7
|
type SelectableCardOwnProps = {
|
|
7
8
|
selected?: boolean;
|
|
@@ -19,6 +20,7 @@ type SelectableCardOwnProps = {
|
|
|
19
20
|
export type SelectableCardProps = Omit<React.ComponentPropsWithoutRef<'button'>, keyof SelectableCardOwnProps | 'value'> & SelectableCardOwnProps & VariantProps<typeof selectableCardVariants>;
|
|
20
21
|
declare const SelectableCard: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref">, "value" | keyof SelectableCardOwnProps> & SelectableCardOwnProps & VariantProps<(props?: ({
|
|
21
22
|
layout?: "grid" | "row" | null | undefined;
|
|
23
|
+
appearance?: "list" | "card" | null | undefined;
|
|
22
24
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & React.RefAttributes<HTMLButtonElement>>;
|
|
23
25
|
export type SelectableCardGroupProps = Omit<React.ComponentPropsWithoutRef<'div'>, 'onChange'> & {
|
|
24
26
|
value?: string | null;
|
|
@@ -30,4 +32,4 @@ declare function SelectableCardGroup({ value, onChange, orientation, className,
|
|
|
30
32
|
declare const SelectableCardGroupItem: React.ForwardRefExoticComponent<Omit<SelectableCardProps, "role" | "selected"> & {
|
|
31
33
|
value: string;
|
|
32
34
|
} & React.RefAttributes<HTMLButtonElement>>;
|
|
33
|
-
export { SelectableCard, SelectableCardGroup, SelectableCardGroupItem, selectableCardVariants
|
|
35
|
+
export { SelectableCard, SelectableCardGroup, SelectableCardGroupItem, selectableCardVariants };
|
|
@@ -12,10 +12,18 @@ const selectableCardVariants = cva([
|
|
|
12
12
|
], {
|
|
13
13
|
variants: {
|
|
14
14
|
layout: { row: 'items-center gap-3 p-3', grid: 'flex-col items-start gap-2 p-4' },
|
|
15
|
+
appearance: {
|
|
16
|
+
card: '',
|
|
17
|
+
list: [
|
|
18
|
+
'border-transparent bg-transparent shadow-none',
|
|
19
|
+
'hover:border-transparent hover:bg-accent/60',
|
|
20
|
+
'data-[selected=true]:border-transparent data-[selected=true]:bg-accent/70 data-[selected=true]:shadow-none data-[selected=true]:ring-0',
|
|
21
|
+
],
|
|
22
|
+
},
|
|
15
23
|
},
|
|
16
|
-
defaultVariants: { layout: 'row' },
|
|
24
|
+
defaultVariants: { layout: 'row', appearance: 'card' },
|
|
17
25
|
});
|
|
18
|
-
const SelectableCard = React.forwardRef(function SelectableCard({ selected = false, onSelect, icon, title, description, trailing, hideIndicator = false, indicator = 'check', role = 'radio', layout = 'row', disabled, className, children, onClick, onKeyDown, ...props }, ref) {
|
|
26
|
+
const SelectableCard = React.forwardRef(function SelectableCard({ selected = false, onSelect, icon, title, description, trailing, hideIndicator = false, indicator = 'check', role = 'radio', layout = 'row', appearance = 'card', disabled, className, children, onClick, onKeyDown, ...props }, ref) {
|
|
19
27
|
const handleClick = (event) => {
|
|
20
28
|
onClick?.(event);
|
|
21
29
|
if (!event.defaultPrevented && !disabled)
|
|
@@ -30,8 +38,10 @@ const SelectableCard = React.forwardRef(function SelectableCard({ selected = fal
|
|
|
30
38
|
};
|
|
31
39
|
const showIndicator = !hideIndicator && trailing === undefined;
|
|
32
40
|
const trailingNode = trailing ??
|
|
33
|
-
(showIndicator && indicator === 'radio' ? (_jsx("span", { "aria-hidden": true, className: cn('flex size-[18px] shrink-0 items-center justify-center rounded-full border transition-colors', layout === 'grid' && 'absolute end-3 top-3', selected
|
|
34
|
-
|
|
41
|
+
(showIndicator && indicator === 'radio' ? (_jsx("span", { "aria-hidden": true, className: cn('flex size-[18px] shrink-0 items-center justify-center rounded-full border transition-colors', layout === 'grid' && 'absolute end-3 top-3', selected
|
|
42
|
+
? 'border-primary'
|
|
43
|
+
: 'border-border group-hover/selectable-card:border-primary/50'), children: _jsx("span", { className: cn('bg-primary size-2.5 rounded-full transition-transform', selected ? 'scale-100' : 'scale-0') }) })) : showIndicator ? (_jsx(Check, { "aria-hidden": true, className: cn('text-primary size-4 shrink-0 transition-opacity', layout === 'grid' && 'absolute end-3 top-3', selected ? 'opacity-100' : 'opacity-0'), strokeWidth: 2 })) : null);
|
|
44
|
+
return (_jsxs("button", { ref: ref, type: "button", role: role, "aria-checked": selected, "aria-disabled": disabled || undefined, disabled: disabled, "data-slot": "selectable-card", "data-selected": selected, "data-layout": layout, "data-appearance": appearance, onClick: handleClick, onKeyDown: handleKeyDown, className: cn(selectableCardVariants({ layout, appearance }), className), ...props, children: [icon != null ? (_jsx("span", { "data-slot": "selectable-card-icon", className: "flex shrink-0 items-center justify-center [&_svg:not([class*='size-'])]:size-4", children: icon })) : null, children, title != null || description != null ? (_jsxs("span", { "data-slot": "selectable-card-body", className: "min-w-0 flex-1", children: [title != null ? (_jsx("span", { className: "block truncate text-sm font-medium", children: title })) : null, description != null ? (_jsx("span", { className: "text-muted-foreground mt-0.5 block text-xs leading-snug", children: description })) : null] })) : null, trailingNode] }));
|
|
35
45
|
});
|
|
36
46
|
const SelectableCardGroupContext = React.createContext(null);
|
|
37
47
|
function SelectableCardGroup({ value, onChange, orientation = 'vertical', className, children, onKeyDown, ...props }) {
|
|
@@ -63,4 +73,4 @@ const SelectableCardGroupItem = React.forwardRef(function SelectableCardGroupIte
|
|
|
63
73
|
context?.onChange?.(value);
|
|
64
74
|
}, ...props }));
|
|
65
75
|
});
|
|
66
|
-
export { SelectableCard, SelectableCardGroup, SelectableCardGroupItem, selectableCardVariants
|
|
76
|
+
export { SelectableCard, SelectableCardGroup, SelectableCardGroupItem, selectableCardVariants };
|
package/dist/index.d.ts
CHANGED
|
@@ -68,7 +68,7 @@ export { PropertyPanel, type PropertyPanelProps, type PropertyPanelLabels, } fro
|
|
|
68
68
|
export { SortableList, SortableItem, SortableRow, DragHandle, DragPreview, useSortableHandle, useSortableRow, reorderById, type SortableListProps, type ReorderDetail, type DragHandleProps, DndContext, KeyboardSensor, PointerSensor, SortableContext, arrayMove, closestCenter, sortableKeyboardCoordinates, useDraggable, useDroppable, useSensor, useSensors, verticalListSortingStrategy, type DragEndEvent, type DragOverEvent, type DragStartEvent, } from './components/sortable.js';
|
|
69
69
|
export { VoiceLine, type VoiceLineProps, type VoiceLineState } from './components/voice-line.js';
|
|
70
70
|
export { ShimmerText, type ShimmerTextProps } from './components/shimmer-text.js';
|
|
71
|
-
export { NautAvatar, NAUT_COLORS, NAUT_COLOR_KEYS, NAUT_EXPRESSIONS, NAUT_GESTURES, NAUT_SHAPES, isNautColor, isNautShape, nautAppearanceForSeed, type NautAppearance, type NautAvatarHandle, type NautAvatarProps, type NautColor, type NautExpression, type NautGesture, type NautShape, } from './components/naut-avatar.js';
|
|
71
|
+
export { NautAvatar, NAUT_COLORS, NAUT_COLOR_KEYS, NAUT_EXPRESSIONS, NAUT_GESTURES, NAUT_SHAPES, isNautColor, isNautShape, nautAppearanceForSeed, type NautAnimationRate, type NautAppearance, type NautAvatarHandle, type NautAvatarProps, type NautColor, type NautExpression, type NautGesture, type NautShape, } from './components/naut-avatar.js';
|
|
72
72
|
export { NautScene, NAUT_SCENES, type NautSceneDef, type NautSceneFace, type NautSceneName, type NautSceneProps, } from './components/naut-scene.js';
|
|
73
73
|
export { TranscriptionOverlay, type TranscriptionOverlayLabels, type TranscriptionOverlayProps, type TranscriptionOverlaySelector, type TranscriptionOverlayState, } from './components/transcription-overlay.js';
|
|
74
74
|
export { TimestampedTranscript, type TimestampedTranscriptLabels, type TimestampedTranscriptProps, type TimestampedTranscriptUtterance, type TimestampedTranscriptWord, } from './components/timestamped-transcript.js';
|