@askrjs/askr 0.0.9 → 0.0.11
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/dist/chunk-37RC6ZT3.js +1 -0
- package/dist/chunk-4RTKQ7SC.js +1 -0
- package/dist/chunk-62D2TNHX.js +1 -0
- package/dist/chunk-72PLUBKM.js +1 -0
- package/dist/chunk-CI6AOGB5.js +1 -0
- package/dist/chunk-D2JSJKCW.js +1 -0
- package/dist/chunk-DPHQ4JD6.js +1 -0
- package/dist/chunk-FFSNBDHN.js +1 -0
- package/dist/chunk-IOUHU5SL.js +1 -0
- package/dist/chunk-JJDSOK6C.js +1 -0
- package/dist/chunk-OQMK7H2R.js +1 -0
- package/dist/chunk-SUQBZD2N.js +2 -0
- package/dist/chunk-UJ6R5KBB.js +2 -0
- package/dist/chunk-WKLC6NZC.js +1 -0
- package/dist/chunk-YRY4OLQF.js +1 -0
- package/dist/{component-DHAn9JxU.d.ts → component-mtwBWhUr.d.ts} +1 -1
- package/dist/for/index.d.ts +61 -0
- package/dist/for/index.js +1 -0
- package/dist/foundations/core.d.ts +449 -0
- package/dist/foundations/core.js +1 -0
- package/dist/foundations/index.d.ts +4 -722
- package/dist/foundations/index.js +1 -1274
- package/dist/foundations/structures.d.ts +223 -0
- package/dist/foundations/structures.js +1 -0
- package/dist/fx/index.js +1 -636
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -3955
- package/dist/jsx-dev-runtime.js +1 -17
- package/dist/jsx-runtime.js +1 -23
- package/dist/logger-MPMIVXAP.js +1 -0
- package/dist/main-EPE35NMW.js +65 -0
- package/dist/navigate-FFGHH7CQ.js +1 -0
- package/dist/resources/index.d.ts +1 -1
- package/dist/resources/index.js +1 -792
- package/dist/route-I3FHHF6U.js +1 -0
- package/dist/router/index.d.ts +5 -0
- package/dist/router/index.js +1 -3194
- package/dist/ssr/index.js +1 -3935
- package/dist/vite/index.js +1 -2306
- package/package.json +19 -5
- package/dist/foundations/index.js.map +0 -1
- package/dist/fx/index.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/jsx-dev-runtime.js.map +0 -1
- package/dist/jsx-runtime.js.map +0 -1
- package/dist/resources/index.js.map +0 -1
- package/dist/router/index.js.map +0 -1
- package/dist/ssr/index.js.map +0 -1
- package/dist/vite/index.js.map +0 -1
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
import { S as State } from '../component-mtwBWhUr.js';
|
|
2
|
+
import '../jsx-CSWf4VFg.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* composeHandlers
|
|
6
|
+
*
|
|
7
|
+
* Compose two event handlers into one. The first handler runs, and unless it
|
|
8
|
+
* calls `event.preventDefault()` (or sets `defaultPrevented`), the second
|
|
9
|
+
* handler runs. This prevents accidental clobbering of child handlers when
|
|
10
|
+
* injecting props.
|
|
11
|
+
*
|
|
12
|
+
* POLICY DECISIONS (LOCKED):
|
|
13
|
+
*
|
|
14
|
+
* 1. Execution Order
|
|
15
|
+
* First handler runs before second (injected before base).
|
|
16
|
+
* This allows injected handlers to prevent default behavior.
|
|
17
|
+
*
|
|
18
|
+
* 2. Default Prevention Check
|
|
19
|
+
* By default, checks `defaultPrevented` on first argument.
|
|
20
|
+
* Can be disabled via options.checkDefaultPrevented = false.
|
|
21
|
+
*
|
|
22
|
+
* 3. Undefined Handler Support
|
|
23
|
+
* Undefined handlers are skipped (no-op). This simplifies usage
|
|
24
|
+
* where handlers are optional.
|
|
25
|
+
*
|
|
26
|
+
* 4. Type Safety
|
|
27
|
+
* Args are readonly to prevent mutation. Return type matches input.
|
|
28
|
+
*/
|
|
29
|
+
interface ComposeHandlersOptions {
|
|
30
|
+
/**
|
|
31
|
+
* When true (default), do not run the second handler if the first prevented default.
|
|
32
|
+
* When false, always run both handlers.
|
|
33
|
+
*/
|
|
34
|
+
checkDefaultPrevented?: boolean;
|
|
35
|
+
}
|
|
36
|
+
declare function composeHandlers<A extends readonly unknown[]>(first?: (...args: A) => void, second?: (...args: A) => void, options?: ComposeHandlersOptions): (...args: A) => void;
|
|
37
|
+
|
|
38
|
+
declare function mergeProps<TBase extends object, TInjected extends object>(base: TBase, injected: TInjected): TInjected & TBase;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Tiny aria helpers
|
|
42
|
+
*/
|
|
43
|
+
declare function ariaDisabled(disabled?: boolean): {
|
|
44
|
+
'aria-disabled'?: 'true';
|
|
45
|
+
};
|
|
46
|
+
declare function ariaExpanded(expanded?: boolean): {
|
|
47
|
+
'aria-expanded'?: 'true' | 'false';
|
|
48
|
+
};
|
|
49
|
+
declare function ariaSelected(selected?: boolean): {
|
|
50
|
+
'aria-selected'?: 'true' | 'false';
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Ref composition utilities
|
|
55
|
+
*
|
|
56
|
+
* POLICY DECISIONS (LOCKED):
|
|
57
|
+
*
|
|
58
|
+
* 1. Ref Types Supported
|
|
59
|
+
* - Callback refs: (value: T | null) => void
|
|
60
|
+
* - Object refs: { current: T | null }
|
|
61
|
+
* - null/undefined (no-op)
|
|
62
|
+
*
|
|
63
|
+
* 2. Write Failure Handling
|
|
64
|
+
* setRef catches write failures (readonly refs) and ignores them.
|
|
65
|
+
* This is intentional — refs may be readonly in some contexts.
|
|
66
|
+
*
|
|
67
|
+
* 3. Composition Order
|
|
68
|
+
* composeRefs applies refs in array order (left to right).
|
|
69
|
+
* All refs are called even if one fails.
|
|
70
|
+
*/
|
|
71
|
+
type Ref<T> = ((value: T | null) => void) | {
|
|
72
|
+
current: T | null;
|
|
73
|
+
} | null | undefined;
|
|
74
|
+
declare function setRef<T>(ref: Ref<T>, value: T | null): void;
|
|
75
|
+
declare function composeRefs<T>(...refs: Array<Ref<T>>): (value: T | null) => void;
|
|
76
|
+
|
|
77
|
+
interface FormatIdOptions {
|
|
78
|
+
/** Defaults to 'askr' */
|
|
79
|
+
prefix?: string;
|
|
80
|
+
/** Stable, caller-provided identity */
|
|
81
|
+
id: string | number;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* formatId
|
|
85
|
+
*
|
|
86
|
+
* Formats a stable ID from a caller-provided identity.
|
|
87
|
+
* - Pure and deterministic (no time/randomness/global counters)
|
|
88
|
+
* - SSR-safe
|
|
89
|
+
*
|
|
90
|
+
* POLICY DECISIONS (LOCKED):
|
|
91
|
+
*
|
|
92
|
+
* 1. No Auto-Generation
|
|
93
|
+
* Caller must provide the `id`. No random/sequential generation.
|
|
94
|
+
* This ensures determinism and SSR safety.
|
|
95
|
+
*
|
|
96
|
+
* 2. Format Convention
|
|
97
|
+
* IDs are formatted as `{prefix}-{id}`.
|
|
98
|
+
* Default prefix is "askr".
|
|
99
|
+
*
|
|
100
|
+
* 3. Type Coercion
|
|
101
|
+
* Numbers are coerced to strings via String().
|
|
102
|
+
* This is deterministic and consistent.
|
|
103
|
+
*/
|
|
104
|
+
declare function formatId(options: FormatIdOptions): string;
|
|
105
|
+
|
|
106
|
+
interface DefaultPreventable {
|
|
107
|
+
defaultPrevented?: boolean;
|
|
108
|
+
preventDefault?: () => void;
|
|
109
|
+
}
|
|
110
|
+
interface PropagationStoppable {
|
|
111
|
+
stopPropagation?: () => void;
|
|
112
|
+
}
|
|
113
|
+
interface KeyboardLikeEvent extends DefaultPreventable, PropagationStoppable {
|
|
114
|
+
key: string;
|
|
115
|
+
}
|
|
116
|
+
interface PointerLikeEvent extends DefaultPreventable, PropagationStoppable {
|
|
117
|
+
target?: unknown;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* pressable
|
|
122
|
+
*
|
|
123
|
+
* Interaction helper that produces VNode props for 'press' semantics.
|
|
124
|
+
* - Pure and deterministic: no DOM construction or mutation here
|
|
125
|
+
* - The runtime owns event attachment and scheduling
|
|
126
|
+
* - This helper returns plain props (handlers) intended to be attached by the runtime
|
|
127
|
+
*
|
|
128
|
+
* Behaviour:
|
|
129
|
+
* - For native buttons: only an `onClick` prop is provided (no ARIA or keyboard shims)
|
|
130
|
+
* - For non-button elements: add `role="button"` and `tabIndex` and keyboard handlers
|
|
131
|
+
* - Activation: `Enter` activates on keydown, `Space` activates on keyup (matches native button)
|
|
132
|
+
* - Disabled: handlers short-circuit and `aria-disabled` is set for all hosts
|
|
133
|
+
*
|
|
134
|
+
* POLICY DECISIONS (LOCKED):
|
|
135
|
+
*
|
|
136
|
+
* 1. Activation Timing (Platform Parity)
|
|
137
|
+
* - Enter fires on keydown (immediate response)
|
|
138
|
+
* - Space fires on keyup (allows cancel by moving focus, matches native)
|
|
139
|
+
* - Space keydown prevents scroll (matches native button behavior)
|
|
140
|
+
*
|
|
141
|
+
* 2. Disabled Enforcement Strategy
|
|
142
|
+
* - Native buttons: Use HTML `disabled` attribute (platform-enforced non-interactivity)
|
|
143
|
+
* AND `aria-disabled` (consistent a11y signaling)
|
|
144
|
+
* - Non-native: Use `tabIndex=-1` (removes from tab order)
|
|
145
|
+
* AND `aria-disabled` (signals disabled state to AT)
|
|
146
|
+
* - Click handler short-circuits as defense-in-depth (prevents leaked focus issues)
|
|
147
|
+
*
|
|
148
|
+
* 3. Key Repeat Behavior
|
|
149
|
+
* - Held Enter/Space will fire onPress repeatedly (matches native button)
|
|
150
|
+
* - No debouncing or repeat prevention (platform parity)
|
|
151
|
+
*/
|
|
152
|
+
interface PressableOptions {
|
|
153
|
+
disabled?: boolean;
|
|
154
|
+
onPress?: (e: PressEvent) => void;
|
|
155
|
+
/**
|
|
156
|
+
* Whether the host is a native button. Defaults to false.
|
|
157
|
+
*/
|
|
158
|
+
isNativeButton?: boolean;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
type PressEvent = DefaultPreventable & PropagationStoppable;
|
|
162
|
+
interface PressableResult {
|
|
163
|
+
onClick: (e: PressEvent) => void;
|
|
164
|
+
disabled?: true;
|
|
165
|
+
role?: 'button';
|
|
166
|
+
tabIndex?: number;
|
|
167
|
+
onKeyDown?: (e: KeyboardLikeEvent) => void;
|
|
168
|
+
onKeyUp?: (e: KeyboardLikeEvent) => void;
|
|
169
|
+
'aria-disabled'?: 'true';
|
|
170
|
+
}
|
|
171
|
+
declare function pressable({ disabled, onPress, isNativeButton, }: PressableOptions): PressableResult;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* dismissable
|
|
175
|
+
*
|
|
176
|
+
* THE dismissal primitive. Handles Escape key and outside interactions.
|
|
177
|
+
*
|
|
178
|
+
* INVARIANTS:
|
|
179
|
+
* 1. Returns props that compose via mergeProps (no factories)
|
|
180
|
+
* 2. Disabled state respected exactly once, here
|
|
181
|
+
* 3. No side effects - pure props generation
|
|
182
|
+
* 4. Outside detection requires explicit node reference
|
|
183
|
+
* 5. This is the ONLY dismissal primitive - do not create alternatives
|
|
184
|
+
*
|
|
185
|
+
* DESIGN:
|
|
186
|
+
* - Returns standard event handler props (onKeyDown, onPointerDownCapture)
|
|
187
|
+
* - Composable via mergeProps with other foundations
|
|
188
|
+
* - Caller provides node reference for outside detection
|
|
189
|
+
* - Single onDismiss callback for all dismiss triggers
|
|
190
|
+
*
|
|
191
|
+
* PIT OF SUCCESS:
|
|
192
|
+
* ✓ Can't accidentally bypass (only way to get dismiss behavior)
|
|
193
|
+
* ✓ Can't duplicate (disabled checked once)
|
|
194
|
+
* ✓ Composes via mergeProps (standard props)
|
|
195
|
+
* ✓ Wrong usage is hard (no factories to misuse)
|
|
196
|
+
*
|
|
197
|
+
* USAGE:
|
|
198
|
+
* const props = dismissable({
|
|
199
|
+
* node: elementRef,
|
|
200
|
+
* disabled: false,
|
|
201
|
+
* onDismiss: () => close()
|
|
202
|
+
* });
|
|
203
|
+
*
|
|
204
|
+
* <div ref={elementRef} {...props}>Content</div>
|
|
205
|
+
*
|
|
206
|
+
* MISUSE EXAMPLE (PREVENTED):
|
|
207
|
+
* ❌ Can't forget to check disabled - checked inside dismissable
|
|
208
|
+
* ❌ Can't create custom escape handler - this is the only one
|
|
209
|
+
* ❌ Can't bypass via direct event listeners - mergeProps composes correctly
|
|
210
|
+
*/
|
|
211
|
+
interface DismissableOptions {
|
|
212
|
+
/**
|
|
213
|
+
* Reference to the element for outside click detection
|
|
214
|
+
*/
|
|
215
|
+
node?: Node | null;
|
|
216
|
+
/**
|
|
217
|
+
* Whether dismiss is disabled
|
|
218
|
+
*/
|
|
219
|
+
disabled?: boolean;
|
|
220
|
+
/**
|
|
221
|
+
* Called when dismiss is triggered (Escape or outside click)
|
|
222
|
+
*/
|
|
223
|
+
onDismiss?: (trigger: 'escape' | 'outside') => void;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
declare function dismissable({ node, disabled, onDismiss }: DismissableOptions): {
|
|
227
|
+
onKeyDown: (e: KeyboardLikeEvent) => void;
|
|
228
|
+
onPointerDownCapture: (e: PointerLikeEvent) => void;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* focusable
|
|
233
|
+
*
|
|
234
|
+
* Normalize focus-related props for hosts.
|
|
235
|
+
* - No DOM manipulation here; returns props that the runtime may attach.
|
|
236
|
+
*/
|
|
237
|
+
interface FocusableOptions {
|
|
238
|
+
disabled?: boolean;
|
|
239
|
+
tabIndex?: number | undefined;
|
|
240
|
+
}
|
|
241
|
+
interface FocusableResult {
|
|
242
|
+
tabIndex?: number;
|
|
243
|
+
'aria-disabled'?: 'true';
|
|
244
|
+
}
|
|
245
|
+
declare function focusable({ disabled, tabIndex, }: FocusableOptions): FocusableResult;
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* hoverable
|
|
249
|
+
*
|
|
250
|
+
* Produces props for pointer enter/leave handling. Pure and deterministic.
|
|
251
|
+
*/
|
|
252
|
+
interface HoverableOptions {
|
|
253
|
+
disabled?: boolean;
|
|
254
|
+
onEnter?: (e: HoverEvent) => void;
|
|
255
|
+
onLeave?: (e: HoverEvent) => void;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
type HoverEvent = DefaultPreventable & PropagationStoppable;
|
|
259
|
+
interface HoverableResult {
|
|
260
|
+
onPointerEnter?: (e: HoverEvent) => void;
|
|
261
|
+
onPointerLeave?: (e: HoverEvent) => void;
|
|
262
|
+
}
|
|
263
|
+
declare function hoverable({ disabled, onEnter, onLeave, }: HoverableOptions): HoverableResult;
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* rovingFocus
|
|
267
|
+
*
|
|
268
|
+
* Single tab stop navigation with arrow-key control.
|
|
269
|
+
*
|
|
270
|
+
* INVARIANTS:
|
|
271
|
+
* 1. Only one item in the group is reachable via Tab (single tab stop)
|
|
272
|
+
* 2. Arrow keys move focus within the group
|
|
273
|
+
* 3. Orientation determines which arrow keys are active
|
|
274
|
+
* 4. Looping is opt-in
|
|
275
|
+
* 5. Disabled items are skipped
|
|
276
|
+
* 6. Returns props objects, never factories (composes via mergeProps)
|
|
277
|
+
*
|
|
278
|
+
* DESIGN:
|
|
279
|
+
* - Container gets onKeyDown for arrow navigation
|
|
280
|
+
* - Each item gets tabIndex based on current selection
|
|
281
|
+
* - Navigation logic is pure - caller controls focus application
|
|
282
|
+
* - Disabled check happens per-item via predicate
|
|
283
|
+
*
|
|
284
|
+
* PIT OF SUCCESS:
|
|
285
|
+
* ✓ Can't accidentally break tab order (tabIndex assigned correctly)
|
|
286
|
+
* ✓ Can't duplicate navigation logic (single source)
|
|
287
|
+
* ✓ Composes via mergeProps (all standard props)
|
|
288
|
+
* ✓ Type-safe - invalid indices caught at call site
|
|
289
|
+
*
|
|
290
|
+
* USAGE:
|
|
291
|
+
* const nav = rovingFocus({
|
|
292
|
+
* currentIndex: 0,
|
|
293
|
+
* itemCount: 3,
|
|
294
|
+
* orientation: 'horizontal',
|
|
295
|
+
* onNavigate: setIndex
|
|
296
|
+
* });
|
|
297
|
+
*
|
|
298
|
+
* <div {...nav.container}>
|
|
299
|
+
* <button {...nav.item(0)}>First</button>
|
|
300
|
+
* <button {...nav.item(1)}>Second</button>
|
|
301
|
+
* </div>
|
|
302
|
+
*
|
|
303
|
+
* MISUSE EXAMPLE (PREVENTED):
|
|
304
|
+
* ❌ Can't forget to set tabIndex - returned in item props
|
|
305
|
+
* ❌ Can't create conflicting arrow handlers - mergeProps composes
|
|
306
|
+
* ❌ Can't skip disabled items incorrectly - logic is internal
|
|
307
|
+
*/
|
|
308
|
+
|
|
309
|
+
type Orientation = 'horizontal' | 'vertical' | 'both';
|
|
310
|
+
interface RovingFocusOptions {
|
|
311
|
+
/**
|
|
312
|
+
* Current focused index
|
|
313
|
+
*/
|
|
314
|
+
currentIndex: number;
|
|
315
|
+
/**
|
|
316
|
+
* Total number of items
|
|
317
|
+
*/
|
|
318
|
+
itemCount: number;
|
|
319
|
+
/**
|
|
320
|
+
* Navigation orientation
|
|
321
|
+
* - horizontal: ArrowLeft/ArrowRight
|
|
322
|
+
* - vertical: ArrowUp/ArrowDown
|
|
323
|
+
* - both: all arrow keys
|
|
324
|
+
*/
|
|
325
|
+
orientation?: Orientation;
|
|
326
|
+
/**
|
|
327
|
+
* Whether to loop when reaching the end
|
|
328
|
+
*/
|
|
329
|
+
loop?: boolean;
|
|
330
|
+
/**
|
|
331
|
+
* Callback when navigation occurs
|
|
332
|
+
*/
|
|
333
|
+
onNavigate?: (index: number) => void;
|
|
334
|
+
/**
|
|
335
|
+
* Optional disabled state check per index
|
|
336
|
+
*/
|
|
337
|
+
isDisabled?: (index: number) => boolean;
|
|
338
|
+
}
|
|
339
|
+
interface RovingFocusResult {
|
|
340
|
+
/**
|
|
341
|
+
* Props for the container element (composes via mergeProps)
|
|
342
|
+
*/
|
|
343
|
+
container: {
|
|
344
|
+
onKeyDown: (e: KeyboardLikeEvent) => void;
|
|
345
|
+
};
|
|
346
|
+
/**
|
|
347
|
+
* Generate props for an item at the given index (composes via mergeProps)
|
|
348
|
+
*/
|
|
349
|
+
item: (index: number) => {
|
|
350
|
+
tabIndex: number;
|
|
351
|
+
'data-roving-index': number;
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
declare function rovingFocus(options: RovingFocusOptions): RovingFocusResult;
|
|
355
|
+
|
|
356
|
+
interface InteractionPolicyInput {
|
|
357
|
+
/** Whether the host element is a native interactive element (button, a, etc) */
|
|
358
|
+
isNative: boolean;
|
|
359
|
+
/** Disabled state - checked ONLY here, never in components */
|
|
360
|
+
disabled: boolean;
|
|
361
|
+
/** User-provided press handler - semantic action, not DOM event */
|
|
362
|
+
onPress?: (e: Event) => void;
|
|
363
|
+
/** Optional ref to compose */
|
|
364
|
+
ref?: Ref<unknown>;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* THE interaction policy. Components MUST use this, NEVER implement
|
|
368
|
+
* interaction logic directly.
|
|
369
|
+
*/
|
|
370
|
+
declare function applyInteractionPolicy({ isNative, disabled, onPress, ref, }: InteractionPolicyInput): {
|
|
371
|
+
disabled: true | undefined;
|
|
372
|
+
onClick: (e: Event) => void;
|
|
373
|
+
ref: Ref<unknown>;
|
|
374
|
+
} | {
|
|
375
|
+
'aria-disabled': true | undefined;
|
|
376
|
+
tabIndex: number;
|
|
377
|
+
ref: Ref<unknown>;
|
|
378
|
+
onClick: (e: DefaultPreventable & PropagationStoppable) => void;
|
|
379
|
+
disabled?: true;
|
|
380
|
+
role?: "button";
|
|
381
|
+
onKeyDown?: (e: KeyboardLikeEvent) => void;
|
|
382
|
+
onKeyUp?: (e: KeyboardLikeEvent) => void;
|
|
383
|
+
};
|
|
384
|
+
/**
|
|
385
|
+
* Merge rule for Slot / asChild
|
|
386
|
+
*
|
|
387
|
+
* Precedence:
|
|
388
|
+
* policy → user → child
|
|
389
|
+
*
|
|
390
|
+
* Event handlers are composed (policy first).
|
|
391
|
+
* Refs are always composed.
|
|
392
|
+
* Policy props MUST take precedence to enforce invariants.
|
|
393
|
+
*/
|
|
394
|
+
declare function mergeInteractionProps(childProps: Record<string, unknown>, policyProps: Record<string, unknown>, userProps?: Record<string, unknown>): Record<string, unknown>;
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* controllable
|
|
398
|
+
*
|
|
399
|
+
* Small utilities for controlled vs uncontrolled components. These helpers are
|
|
400
|
+
* intentionally minimal and do not manage state themselves; they help component
|
|
401
|
+
* implementations make correct decisions about when to call `onChange` vs
|
|
402
|
+
* update internal state.
|
|
403
|
+
*
|
|
404
|
+
* POLICY DECISIONS (LOCKED):
|
|
405
|
+
*
|
|
406
|
+
* 1. Controlled Detection
|
|
407
|
+
* A value is "controlled" if it is not `undefined`.
|
|
408
|
+
* This matches React conventions and is SSR-safe.
|
|
409
|
+
*
|
|
410
|
+
* 2. onChange Timing
|
|
411
|
+
* - Controlled mode: onChange called immediately, no internal update
|
|
412
|
+
* - Uncontrolled mode: internal state updated first, then onChange called
|
|
413
|
+
* This ensures onChange sees the new value in both modes.
|
|
414
|
+
*
|
|
415
|
+
* 3. Value Equality
|
|
416
|
+
* controllableState uses Object.is() to prevent unnecessary onChange calls.
|
|
417
|
+
* This is intentional — strict equality, no deep comparison.
|
|
418
|
+
*/
|
|
419
|
+
|
|
420
|
+
declare function isControlled<T>(value: T | undefined): value is T;
|
|
421
|
+
declare function resolveControllable<T>(value: T | undefined, defaultValue: T): {
|
|
422
|
+
value: T;
|
|
423
|
+
isControlled: boolean;
|
|
424
|
+
};
|
|
425
|
+
declare function makeControllable<T>(options: {
|
|
426
|
+
value: T | undefined;
|
|
427
|
+
defaultValue: T;
|
|
428
|
+
onChange?: (next: T) => void;
|
|
429
|
+
setInternal?: (next: T) => void;
|
|
430
|
+
}): {
|
|
431
|
+
set: (next: T) => void;
|
|
432
|
+
isControlled: boolean;
|
|
433
|
+
};
|
|
434
|
+
type ControllableState<T> = State<T> & {
|
|
435
|
+
isControlled: boolean;
|
|
436
|
+
};
|
|
437
|
+
/**
|
|
438
|
+
* controllableState
|
|
439
|
+
*
|
|
440
|
+
* Hook-like primitive that mirrors `state()` semantics while supporting
|
|
441
|
+
* controlled/uncontrolled behavior.
|
|
442
|
+
*/
|
|
443
|
+
declare function controllableState<T>(options: {
|
|
444
|
+
value: T | undefined;
|
|
445
|
+
defaultValue: T;
|
|
446
|
+
onChange?: (next: T) => void;
|
|
447
|
+
}): ControllableState<T>;
|
|
448
|
+
|
|
449
|
+
export { type ComposeHandlersOptions, type ControllableState, type DismissableOptions, type FocusableOptions, type FocusableResult, type FormatIdOptions, type HoverableOptions, type HoverableResult, type InteractionPolicyInput, type Orientation, type PressableOptions, type PressableResult, type Ref, type RovingFocusOptions, type RovingFocusResult, applyInteractionPolicy, ariaDisabled, ariaExpanded, ariaSelected, composeHandlers, composeRefs, controllableState, dismissable, focusable, formatId, hoverable, isControlled, makeControllable, mergeInteractionProps, mergeProps, pressable, resolveControllable, rovingFocus, setRef };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{i as controllableState,b as dismissable,c as focusable,a as formatId,d as hoverable,f as isControlled,h as makeControllable,g as resolveControllable,e as rovingFocus}from'../chunk-72PLUBKM.js';import'../chunk-WKLC6NZC.js';export{i as applyInteractionPolicy,c as ariaDisabled,d as ariaExpanded,e as ariaSelected,a as composeHandlers,g as composeRefs,j as mergeInteractionProps,b as mergeProps,h as pressable,f as setRef}from'../chunk-DPHQ4JD6.js';import'../chunk-UJ6R5KBB.js';import'../chunk-JJDSOK6C.js';import'../chunk-OQMK7H2R.js';import'../chunk-37RC6ZT3.js';import'../chunk-62D2TNHX.js';
|