stablekit.ts 0.2.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 +207 -0
- package/dist/eslint.cjs +157 -0
- package/dist/eslint.d.cts +55 -0
- package/dist/eslint.d.ts +55 -0
- package/dist/eslint.js +132 -0
- package/dist/index.cjs +823 -0
- package/dist/index.d.cts +473 -0
- package/dist/index.d.ts +473 -0
- package/dist/index.js +816 -0
- package/dist/stylelint.cjs +117 -0
- package/dist/stylelint.d.cts +47 -0
- package/dist/stylelint.d.ts +47 -0
- package/dist/stylelint.js +92 -0
- package/dist/styles.css +178 -0
- package/llms.txt +463 -0
- package/package.json +92 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { HTMLAttributes, ReactNode, ElementType, Ref, ReactElement, JSX } from 'react';
|
|
4
|
+
|
|
5
|
+
interface StateSwapProps extends HTMLAttributes<HTMLElement> {
|
|
6
|
+
/** The boolean state that drives which content is visible. */
|
|
7
|
+
state: boolean;
|
|
8
|
+
/** Content shown when `state` is true. */
|
|
9
|
+
true: ReactNode;
|
|
10
|
+
/** Content shown when `state` is false. */
|
|
11
|
+
false: ReactNode;
|
|
12
|
+
/** HTML element to render. Default: "span" (safe inside buttons). */
|
|
13
|
+
as?: ElementType;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Boolean content swap with zero layout shift.
|
|
17
|
+
*
|
|
18
|
+
* Reserves the width of the wider option so the container never changes
|
|
19
|
+
* dimensions when toggling between states.
|
|
20
|
+
*
|
|
21
|
+
* Renders as an inline `<span>` by default — safe inside buttons,
|
|
22
|
+
* table cells, and any inline context.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* <button onClick={toggle}>
|
|
27
|
+
* <StateSwap state={open} true="Close" false="Open" />
|
|
28
|
+
* </button>
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```tsx
|
|
33
|
+
* <StateSwap
|
|
34
|
+
* state={expanded}
|
|
35
|
+
* true={<ChevronUp />}
|
|
36
|
+
* false={<ChevronDown />}
|
|
37
|
+
* />
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
declare function StateSwap({ state, true: onTrue, false: onFalse, as: Tag, ...props }: StateSwapProps): react_jsx_runtime.JSX.Element;
|
|
41
|
+
|
|
42
|
+
type Axis = "width" | "height" | "both";
|
|
43
|
+
interface LayoutGroupProps extends HTMLAttributes<HTMLElement> {
|
|
44
|
+
/** Which axis to stabilize. Default: "both". */
|
|
45
|
+
axis?: Axis;
|
|
46
|
+
/**
|
|
47
|
+
* Active value identifier. LayoutViews with a matching `name` prop become visible.
|
|
48
|
+
*
|
|
49
|
+
* **AI agent note:** LayoutGroup is a spatial stability container.
|
|
50
|
+
* All children overlap via CSS grid. The container auto-sizes to the
|
|
51
|
+
* largest child. Use LayoutView as children, not arbitrary elements.
|
|
52
|
+
* For boolean swaps, prefer StateSwap instead.
|
|
53
|
+
* For dictionary-based state mapping, prefer LayoutMap instead.
|
|
54
|
+
*/
|
|
55
|
+
value?: string;
|
|
56
|
+
/** HTML element to render. Use "span" inside buttons. Default: "div". */
|
|
57
|
+
as?: ElementType;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Multi-state spatial stability container.
|
|
61
|
+
*
|
|
62
|
+
* All children overlap in the same grid cell (1/1). The container
|
|
63
|
+
* auto-sizes to the largest child — zero JS measurement, pure CSS grid.
|
|
64
|
+
*
|
|
65
|
+
* Use `<LayoutView name="...">` as children. The view whose `name`
|
|
66
|
+
* matches the group's `value` becomes active; others are hidden via
|
|
67
|
+
* `[inert]` + inline styles.
|
|
68
|
+
*
|
|
69
|
+
* Focus handoff: tracks whether focus is inside the container via native
|
|
70
|
+
* focusin/focusout listeners. When `inert` ejects focus (relatedTarget is
|
|
71
|
+
* null), the flag stays set so the incoming active LayoutView can reclaim it.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```tsx
|
|
75
|
+
* <LayoutGroup value={step}>
|
|
76
|
+
* <LayoutView name="shipping"><ShippingForm /></LayoutView>
|
|
77
|
+
* <LayoutView name="payment"><PaymentForm /></LayoutView>
|
|
78
|
+
* <LayoutView name="confirm"><Confirmation /></LayoutView>
|
|
79
|
+
* </LayoutGroup>
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
declare const LayoutGroup: react.ForwardRefExoticComponent<LayoutGroupProps & react.RefAttributes<HTMLElement>>;
|
|
83
|
+
|
|
84
|
+
interface LayoutViewProps extends HTMLAttributes<HTMLElement> {
|
|
85
|
+
/**
|
|
86
|
+
* Whether this view is the active (visible) variant.
|
|
87
|
+
* Overrides context-based matching. Use for manual control.
|
|
88
|
+
*/
|
|
89
|
+
active?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* View name. Active when it matches the parent LayoutGroup's `value`.
|
|
92
|
+
*
|
|
93
|
+
* **AI agent note:** LayoutView must be a direct child of LayoutGroup.
|
|
94
|
+
* Do not use LayoutView outside of a LayoutGroup — it has no effect.
|
|
95
|
+
* For boolean swaps, use StateSwap instead.
|
|
96
|
+
*/
|
|
97
|
+
name?: string;
|
|
98
|
+
/** HTML element to render. Use "span" inside buttons. Default: "div". */
|
|
99
|
+
as?: ElementType;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Single view inside a LayoutGroup.
|
|
103
|
+
*
|
|
104
|
+
* All views overlap via CSS grid. Inactive views are hidden but still
|
|
105
|
+
* contribute to grid cell sizing — the container never changes dimensions.
|
|
106
|
+
*
|
|
107
|
+
* Inactive hiding uses a `data-state` attribute driven by CSS
|
|
108
|
+
* (`.sk-layout-view[data-state="inactive"]`) plus the `[inert]` attribute
|
|
109
|
+
* for accessibility (non-focusable, non-interactive). Because hiding is
|
|
110
|
+
* CSS-driven rather than inline-style-driven, consumers can override
|
|
111
|
+
* transitions on the `.sk-layout-view` class without specificity fights.
|
|
112
|
+
*
|
|
113
|
+
* Active views get tabIndex={-1} so they are programmatically focusable
|
|
114
|
+
* (but excluded from the tab ring) for focus handoff.
|
|
115
|
+
*/
|
|
116
|
+
declare const LayoutView: react.ForwardRefExoticComponent<LayoutViewProps & react.RefAttributes<HTMLElement>>;
|
|
117
|
+
|
|
118
|
+
interface LayoutMapProps<K extends string> extends HTMLAttributes<HTMLElement> {
|
|
119
|
+
/**
|
|
120
|
+
* The active key — must match one of the keys in `map`.
|
|
121
|
+
*
|
|
122
|
+
* Uses `NoInfer<K>` so TypeScript infers the key union from `map`
|
|
123
|
+
* and checks `value` against it — typos are compile-time errors.
|
|
124
|
+
*/
|
|
125
|
+
value: NoInfer<K>;
|
|
126
|
+
/** Dictionary of views keyed by state name. TypeScript infers and enforces the keys. */
|
|
127
|
+
map: Record<K, ReactNode>;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Typo-proof dictionary-based state mapping.
|
|
131
|
+
*
|
|
132
|
+
* Replaces manual LayoutGroup + LayoutView trees with a single component.
|
|
133
|
+
* TypeScript infers the key union from `map` and enforces that `value`
|
|
134
|
+
* matches — no string typos, no orphaned views.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```tsx
|
|
138
|
+
* <LayoutMap
|
|
139
|
+
* value={activeTab}
|
|
140
|
+
* map={{
|
|
141
|
+
* profile: <ProfileTab />,
|
|
142
|
+
* invoices: <InvoicesTab />,
|
|
143
|
+
* settings: <SettingsTab />,
|
|
144
|
+
* }}
|
|
145
|
+
* />
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
declare function LayoutMap<K extends string>({ value, map, ...props }: LayoutMapProps<K>): react_jsx_runtime.JSX.Element;
|
|
149
|
+
|
|
150
|
+
interface SizeRatchetProps extends HTMLAttributes<HTMLElement> {
|
|
151
|
+
/** Which axis to ratchet. Default: "height". */
|
|
152
|
+
axis?: Axis;
|
|
153
|
+
/** When this key changes, the ratchet floor resets so the container can shrink to its new intrinsic size. */
|
|
154
|
+
resetKey?: unknown;
|
|
155
|
+
/** HTML element to render. Default: "div". */
|
|
156
|
+
as?: ElementType;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Container that never shrinks.
|
|
160
|
+
*
|
|
161
|
+
* Remembers its largest-ever size (ResizeObserver ratchet) and applies
|
|
162
|
+
* min-width/min-height so the container only grows. Swap a spinner for
|
|
163
|
+
* a table inside — no reflow upstream.
|
|
164
|
+
*
|
|
165
|
+
* Uses `contain: layout style` to isolate internal reflow from ancestors.
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```tsx
|
|
169
|
+
* <SizeRatchet>
|
|
170
|
+
* {isLoading ? <Spinner /> : <DataTable rows={rows} />}
|
|
171
|
+
* </SizeRatchet>
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
declare const SizeRatchet: react.ForwardRefExoticComponent<SizeRatchetProps & react.RefAttributes<HTMLElement>>;
|
|
175
|
+
|
|
176
|
+
interface StableCounterProps extends HTMLAttributes<HTMLElement> {
|
|
177
|
+
/** The actual value to display. */
|
|
178
|
+
value: ReactNode;
|
|
179
|
+
/** The maximum expected string/node to pre-allocate width (e.g., "999" or "$99,999"). */
|
|
180
|
+
reserve: ReactNode;
|
|
181
|
+
/** HTML element to render. Default: "span" (safe inside inline contexts). */
|
|
182
|
+
as?: ElementType;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Numeric/text content swap with zero horizontal layout shift.
|
|
186
|
+
*
|
|
187
|
+
* Uses CSS Grid overlap to render a hidden `reserve` node that props open
|
|
188
|
+
* the bounding box to its maximum expected width. The visible `value` node
|
|
189
|
+
* renders on top. The container never changes dimensions regardless of
|
|
190
|
+
* what `value` displays.
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* ```tsx
|
|
194
|
+
* <StableCounter value={count} reserve="999" />
|
|
195
|
+
* ```
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```tsx
|
|
199
|
+
* <StableCounter value={`$${revenue.toLocaleString()}`} reserve="$99,999" />
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
declare const StableCounter: react.ForwardRefExoticComponent<StableCounterProps & react.RefAttributes<HTMLElement>>;
|
|
203
|
+
|
|
204
|
+
interface StableFieldProps extends HTMLAttributes<HTMLElement> {
|
|
205
|
+
/** The actual error message to display. `undefined`/`null` hides the error. */
|
|
206
|
+
error?: ReactNode;
|
|
207
|
+
/** The string/node used to permanently reserve the vertical height of the error slot. */
|
|
208
|
+
reserve: ReactNode;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Form field wrapper that pre-allocates vertical space for error messages.
|
|
212
|
+
*
|
|
213
|
+
* Uses CSS Grid overlap to render a hidden `reserve` node that props open
|
|
214
|
+
* the error slot to its maximum expected height. The actual error renders
|
|
215
|
+
* on top when present. The field container never changes height when errors
|
|
216
|
+
* appear or disappear.
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* ```tsx
|
|
220
|
+
* <StableField error={errors.email} reserve="Please enter a valid email address">
|
|
221
|
+
* <label htmlFor="email">Email</label>
|
|
222
|
+
* <input id="email" type="email" />
|
|
223
|
+
* </StableField>
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
declare const StableField: react.ForwardRefExoticComponent<StableFieldProps & react.RefAttributes<HTMLDivElement>>;
|
|
227
|
+
|
|
228
|
+
interface LoadingBoundaryProps extends HTMLAttributes<HTMLElement> {
|
|
229
|
+
/** Whether data is loading. Sets LoadingContext for all nested components. */
|
|
230
|
+
loading: boolean;
|
|
231
|
+
/**
|
|
232
|
+
* Crossfade duration in ms. Sets `--sk-loading-exit-duration` on the container
|
|
233
|
+
* so all nested skeleton transitions use the same timing.
|
|
234
|
+
*/
|
|
235
|
+
exitDuration: number;
|
|
236
|
+
/** HTML element to render. Default: "div". */
|
|
237
|
+
as?: ElementType;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Loading orchestrator — context + ratchet in one component.
|
|
241
|
+
*
|
|
242
|
+
* Composes two behaviors:
|
|
243
|
+
* - **LoadingContext**: every nested `<TextSkeleton>`, `<StableText>`,
|
|
244
|
+
* and `<MediaSkeleton>` reads loading state automatically.
|
|
245
|
+
* - **SizeRatchet**: container never shrinks during transitions.
|
|
246
|
+
*
|
|
247
|
+
* Skeleton components handle their own crossfade via CSS opacity
|
|
248
|
+
* transitions on permanently-mounted layers. The `exitDuration` prop
|
|
249
|
+
* sets `--sk-loading-exit-duration` on the container so all nested transitions
|
|
250
|
+
* use the same timing.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```tsx
|
|
254
|
+
* <LoadingBoundary loading={isLoading} exitDuration={150}>
|
|
255
|
+
* <MediaSkeleton aspectRatio={1} className="w-16 rounded-full">
|
|
256
|
+
* <img src={user.avatar} alt={user.name} />
|
|
257
|
+
* </MediaSkeleton>
|
|
258
|
+
* <StableText as="h2" className="text-xl font-bold">{user.name}</StableText>
|
|
259
|
+
* <StableText as="p" className="text-sm text-muted">{user.email}</StableText>
|
|
260
|
+
* </LoadingBoundary>
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
declare const LoadingBoundary: react.ForwardRefExoticComponent<LoadingBoundaryProps & react.RefAttributes<HTMLElement>>;
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Read the nearest LoadingContext's loading state.
|
|
267
|
+
* Returns `false` when no LoadingContext ancestor exists.
|
|
268
|
+
*/
|
|
269
|
+
declare function useLoadingState(): boolean;
|
|
270
|
+
interface LoadingContextProps {
|
|
271
|
+
/** Whether the subtree is in a loading state. */
|
|
272
|
+
loading: boolean;
|
|
273
|
+
children: ReactNode;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Ambient loading provider.
|
|
277
|
+
*
|
|
278
|
+
* Wrapping a subtree in `<LoadingContext loading>` lets every nested
|
|
279
|
+
* `<TextSkeleton>` and `<StableText>` pick up the loading state
|
|
280
|
+
* automatically, without threading an explicit `loading` prop
|
|
281
|
+
* through every component.
|
|
282
|
+
*
|
|
283
|
+
* @example
|
|
284
|
+
* ```tsx
|
|
285
|
+
* <LoadingContext loading={isLoading}>
|
|
286
|
+
* <StableText as="h2">{user.name}</StableText>
|
|
287
|
+
* <StableText as="p">{user.bio}</StableText>
|
|
288
|
+
* </LoadingContext>
|
|
289
|
+
* ```
|
|
290
|
+
*/
|
|
291
|
+
declare function LoadingContext({ loading, children }: LoadingContextProps): react_jsx_runtime.JSX.Element;
|
|
292
|
+
|
|
293
|
+
interface StableTextProps extends HTMLAttributes<HTMLElement> {
|
|
294
|
+
/** HTML element to render (p, h1, h2, span, etc.). Default: "p". */
|
|
295
|
+
as?: ElementType;
|
|
296
|
+
/** Explicit loading override. Falls back to nearest LoadingContext. */
|
|
297
|
+
loading?: boolean;
|
|
298
|
+
/** Forwarded ref (React 19 style). */
|
|
299
|
+
ref?: Ref<HTMLElement>;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Typography + skeleton in one tag.
|
|
303
|
+
*
|
|
304
|
+
* Combines the semantic HTML wrapper and loading shimmer into one component.
|
|
305
|
+
* Inside a `<LoadingBoundary>`, every `<StableText>` automatically shimmers.
|
|
306
|
+
* No forgetting to wrap text in `<TextSkeleton>`. No Swiss-cheese loading states.
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* ```tsx
|
|
310
|
+
* <LoadingBoundary loading={isLoading} exitDuration={150}>
|
|
311
|
+
* <StableText as="h1" className="text-2xl font-bold">{user.name}</StableText>
|
|
312
|
+
* <StableText className="text-sm text-muted">{user.bio}</StableText>
|
|
313
|
+
* </LoadingBoundary>
|
|
314
|
+
* ```
|
|
315
|
+
*/
|
|
316
|
+
declare function StableText({ as: Tag, loading, children, ...props }: StableTextProps): react_jsx_runtime.JSX.Element;
|
|
317
|
+
|
|
318
|
+
interface TextSkeletonProps extends HTMLAttributes<HTMLElement> {
|
|
319
|
+
/**
|
|
320
|
+
* Whether data is loading. Shows shimmer when true, children when false.
|
|
321
|
+
* When omitted, falls back to the nearest `<LoadingContext>` ancestor's loading state.
|
|
322
|
+
*/
|
|
323
|
+
loading?: boolean;
|
|
324
|
+
/** HTML element to render. Default: "span". */
|
|
325
|
+
as?: ElementType;
|
|
326
|
+
/** Forwarded ref (React 19 style). */
|
|
327
|
+
ref?: Ref<HTMLElement>;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Inline loading shimmer for text.
|
|
331
|
+
*
|
|
332
|
+
* Both shimmer and content layers are permanently mounted in the DOM,
|
|
333
|
+
* stacked via `display: inline-grid` at `grid-area: 1/1`. Loading
|
|
334
|
+
* state controls only opacity and interactivity (`inert`). CSS
|
|
335
|
+
* transitions handle the crossfade — no JS state machine, no
|
|
336
|
+
* structural mutation, no flash.
|
|
337
|
+
*
|
|
338
|
+
* The className is passed through so `1lh` inherits the correct font
|
|
339
|
+
* metrics from the consuming context.
|
|
340
|
+
*
|
|
341
|
+
* When no explicit `loading` prop is provided, TextSkeleton reads from
|
|
342
|
+
* the nearest `<LoadingContext>` ancestor.
|
|
343
|
+
*
|
|
344
|
+
* @example
|
|
345
|
+
* ```tsx
|
|
346
|
+
* <TextSkeleton loading={isLoading}>{user.name}</TextSkeleton>
|
|
347
|
+
* ```
|
|
348
|
+
*/
|
|
349
|
+
declare function TextSkeleton({ loading, as: Tag, className, style, children, ...props }: TextSkeletonProps): react_jsx_runtime.JSX.Element;
|
|
350
|
+
|
|
351
|
+
interface MediaSkeletonProps extends HTMLAttributes<HTMLElement> {
|
|
352
|
+
/**
|
|
353
|
+
* Aspect ratio for the media container (e.g. `16/9`, `1`, `4/3`).
|
|
354
|
+
* Applied as the CSS `aspect-ratio` property, so the container
|
|
355
|
+
* reserves exact space before any content loads.
|
|
356
|
+
*/
|
|
357
|
+
aspectRatio: number;
|
|
358
|
+
/**
|
|
359
|
+
* Whether media is loading. Shows shimmer when true, children when false.
|
|
360
|
+
* When omitted, falls back to the nearest `<LoadingContext>` ancestor's loading state.
|
|
361
|
+
*/
|
|
362
|
+
loading?: boolean;
|
|
363
|
+
/**
|
|
364
|
+
* Must be a single React element (img, video, etc.).
|
|
365
|
+
*
|
|
366
|
+
* **Do not add dimension classes to the child.** MediaSkeleton enforces
|
|
367
|
+
* child constraints automatically via React.cloneElement. The child
|
|
368
|
+
* cannot break out of the frame.
|
|
369
|
+
*/
|
|
370
|
+
children: ReactElement;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Aspect-ratio media placeholder with automatic child constraints.
|
|
374
|
+
*
|
|
375
|
+
* Reserves space via `aspect-ratio` so the bounding box is stable
|
|
376
|
+
* before media loads. Enforces child constraints via `cloneElement`
|
|
377
|
+
* inline styles — no CSS `!important`, no developer discipline needed.
|
|
378
|
+
*
|
|
379
|
+
* The shimmer stays visible until **both** the loading context resolves
|
|
380
|
+
* AND the child media fires `onLoad`. This prevents an empty-frame
|
|
381
|
+
* flash between skeleton exit and image paint.
|
|
382
|
+
*
|
|
383
|
+
* The child can override `objectFit` (e.g. `contain`) via its own
|
|
384
|
+
* inline `style` prop, but positional constraints are non-negotiable.
|
|
385
|
+
*
|
|
386
|
+
* @example
|
|
387
|
+
* ```tsx
|
|
388
|
+
* <MediaSkeleton aspectRatio={16/9}>
|
|
389
|
+
* <img src={src} alt={alt} />
|
|
390
|
+
* </MediaSkeleton>
|
|
391
|
+
* ```
|
|
392
|
+
*
|
|
393
|
+
* @example
|
|
394
|
+
* ```tsx
|
|
395
|
+
* <MediaSkeleton aspectRatio={1} className="rounded-full">
|
|
396
|
+
* <img src={avatar} alt={name} />
|
|
397
|
+
* </MediaSkeleton>
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
declare function MediaSkeleton({ aspectRatio, loading, className, style, children, ...props }: MediaSkeletonProps): react_jsx_runtime.JSX.Element;
|
|
401
|
+
|
|
402
|
+
interface CollectionSkeletonProps<T> extends Omit<HTMLAttributes<HTMLElement>, "children"> {
|
|
403
|
+
/** Data items to render. */
|
|
404
|
+
items: T[];
|
|
405
|
+
/** Whether data is loading. Shows skeleton stubs when true. */
|
|
406
|
+
loading: boolean;
|
|
407
|
+
/** Render function for each item. */
|
|
408
|
+
renderItem: (item: T, index: number) => ReactNode;
|
|
409
|
+
/** Number of placeholder rows during loading. */
|
|
410
|
+
stubCount: number;
|
|
411
|
+
/** Crossfade duration in ms. Sets --sk-loading-exit-duration for the opacity transition. */
|
|
412
|
+
exitDuration: number;
|
|
413
|
+
/** HTML element to render. Default: "div". */
|
|
414
|
+
as?: ElementType;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Loading-aware list with automatic skeleton stubs.
|
|
418
|
+
*
|
|
419
|
+
* Both the skeleton grid and rendered items are permanently mounted
|
|
420
|
+
* in the DOM, stacked via CSS grid overlap. Loading state controls
|
|
421
|
+
* only opacity and interactivity. CSS transitions handle the crossfade.
|
|
422
|
+
*
|
|
423
|
+
* Wrapped in a SizeRatchet so the container never shrinks.
|
|
424
|
+
*
|
|
425
|
+
* @example
|
|
426
|
+
* ```tsx
|
|
427
|
+
* <CollectionSkeleton
|
|
428
|
+
* items={users}
|
|
429
|
+
* loading={isLoading}
|
|
430
|
+
* stubCount={5}
|
|
431
|
+
* exitDuration={150}
|
|
432
|
+
* renderItem={(user) => <UserRow key={user.id} user={user} />}
|
|
433
|
+
* />
|
|
434
|
+
* ```
|
|
435
|
+
*/
|
|
436
|
+
declare const CollectionSkeleton: <T>(props: CollectionSkeletonProps<T> & {
|
|
437
|
+
ref?: Ref<HTMLElement>;
|
|
438
|
+
}) => ReactElement | null;
|
|
439
|
+
|
|
440
|
+
interface FadeTransitionProps extends HTMLAttributes<HTMLElement> {
|
|
441
|
+
/** Whether the content is visible. */
|
|
442
|
+
show: boolean;
|
|
443
|
+
/** HTML element to render. Default: "div". */
|
|
444
|
+
as?: ElementType;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Enter/exit animation wrapper.
|
|
448
|
+
*
|
|
449
|
+
* Mounts when `show` becomes true, plays enter animation.
|
|
450
|
+
* When `show` becomes false, plays exit animation then unmounts.
|
|
451
|
+
*
|
|
452
|
+
* CSS classes applied: `sk-fade-entering`, `sk-fade-exiting`.
|
|
453
|
+
* Duration controlled via `--sk-fade-duration` CSS variable.
|
|
454
|
+
*
|
|
455
|
+
* @example
|
|
456
|
+
* ```tsx
|
|
457
|
+
* <FadeTransition show={isOpen}>
|
|
458
|
+
* <DropdownPanel />
|
|
459
|
+
* </FadeTransition>
|
|
460
|
+
* ```
|
|
461
|
+
*/
|
|
462
|
+
declare const FadeTransition: react.ForwardRefExoticComponent<FadeTransitionProps & react.RefAttributes<HTMLElement>>;
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Creates a firewalled UI primitive.
|
|
466
|
+
*
|
|
467
|
+
* - className and style are blocked at the type level
|
|
468
|
+
* - Variant props are auto-mapped to data-attributes
|
|
469
|
+
* - All other HTML attributes pass through
|
|
470
|
+
*/
|
|
471
|
+
declare function createPrimitive<Tag extends keyof JSX.IntrinsicElements, const Variants extends Record<string, readonly string[]> = {}>(tag: Tag, baseClass: string, variants?: Variants): (props: Omit<JSX.IntrinsicElements[Tag], "style" | "className" | keyof Variants> & { [K in keyof Variants]: Variants[K][number]; }) => react.DOMElement<Record<string, unknown>, Element>;
|
|
472
|
+
|
|
473
|
+
export { type Axis, CollectionSkeleton, type CollectionSkeletonProps, FadeTransition, type FadeTransitionProps, LayoutGroup, type LayoutGroupProps, LayoutMap, type LayoutMapProps, LayoutView, type LayoutViewProps, LoadingBoundary, type LoadingBoundaryProps, LoadingContext, type LoadingContextProps, MediaSkeleton, type MediaSkeletonProps, SizeRatchet, type SizeRatchetProps, StableCounter, type StableCounterProps, StableField, type StableFieldProps, StableText, type StableTextProps, StateSwap, type StateSwapProps, TextSkeleton, type TextSkeletonProps, createPrimitive, useLoadingState };
|