@uniaria/react 0.0.0-stage → 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Andrey Yamanov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,51 @@
1
- # Temporary Holding Version
1
+ # @uniaria/react
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The React adapter for UniAria: one generic hook, `useComponent`, binds any
4
+ component definition from [`@uniaria/core`](https://www.npmjs.com/package/@uniaria/core)
5
+ to your elements. React 18 or 19.
6
+
7
+ > **Pre-release.** The API can change in any 0.x version.
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ pnpm add @uniaria/core @uniaria/react
13
+ ```
14
+
15
+ ## Use
16
+
17
+ Pass a definition and its props to `useComponent`, then spread each part's
18
+ props onto your element:
19
+
20
+ ```tsx
21
+ import { toggleSwitch } from '@uniaria/core/switch';
22
+ import { useComponent } from '@uniaria/react';
23
+
24
+ export function WifiSwitch() {
25
+ const wifi = useComponent(toggleSwitch, {
26
+ defaultSelected: true,
27
+ labelElementType: 'span',
28
+ });
29
+ return (
30
+ <label {...wifi.parts.root}>
31
+ <input {...wifi.parts.input} name="wifi" />
32
+ <span {...wifi.parts.indicator} />
33
+ <span {...wifi.parts.label}>Wi-Fi</span>
34
+ </label>
35
+ );
36
+ }
37
+ ```
38
+
39
+ Hovering, pressing or toggling doesn't render your component again: UniAria
40
+ writes the new state straight to the elements. When your render needs a
41
+ value, select it with `useComponentState`.
42
+
43
+ ## Documentation
44
+
45
+ [Getting started](https://github.com/tenphi/uniaria/blob/main/docs/guide/getting-started.mdx)
46
+ and every component's page are in the repository's
47
+ [`docs`](https://github.com/tenphi/uniaria/tree/main/docs) folder.
48
+
49
+ ## License
50
+
51
+ [MIT](https://github.com/tenphi/uniaria/blob/main/LICENSE)
@@ -0,0 +1,51 @@
1
+ import { ReactNode } from "react";
2
+ import { ActionsOf, AnyDefinition, Instance, ItemPartOf, PartOf, PropsOf, StateOf } from "@uniaria/core";
3
+ //#region src/owner.d.ts
4
+ interface Scope {
5
+ readonly instance: Instance;
6
+ readonly parent: Scope | null;
7
+ }
8
+ interface OwnerProviderProps<D extends AnyDefinition = AnyDefinition> {
9
+ /** The owner: an instance, or the handle `useComponent` returned. */
10
+ readonly owner: Instance<D> | {
11
+ readonly instance: Instance<D>;
12
+ };
13
+ readonly children?: ReactNode;
14
+ }
15
+ export declare function OwnerProvider<D extends AnyDefinition>({ owner, children }: OwnerProviderProps<D>): import("react").FunctionComponentElement<import("react").ProviderProps<Scope | null>>;
16
+ //#endregion
17
+ //#region src/use-component.d.ts
18
+ /** Props to spread onto a part element: attributes, stable handlers, a ref. */
19
+ type ReactPartProps = Readonly<Record<string, unknown>> & {
20
+ readonly ref: (element: Element | null) => void;
21
+ };
22
+ interface ComponentHandle<D extends AnyDefinition> {
23
+ /** The stable instance: pass it to `useComponentState` or as an `owner`. */
24
+ readonly instance: Instance<D>;
25
+ /** The component's public actions, such as a popover's `open()` and `close()`. */
26
+ readonly actions: ActionsOf<D>;
27
+ /** Props for each part, from this render. */
28
+ readonly parts: Readonly<Record<PartOf<D>, ReactPartProps>>;
29
+ /**
30
+ * Props for one item part of one item (Listbox spec, Item parts), from
31
+ * this render. Later changes reach the element through the binder, so the
32
+ * component rendering the rows doesn't render again for them. Item parts
33
+ * take no live DOM properties as React defaults.
34
+ */
35
+ getItemProps(part: ItemPartOf<D>, key: string): ReactPartProps;
36
+ }
37
+ /**
38
+ * Creates an instance of `definition` for this component and returns props
39
+ * for its parts. Props are stored silently during render and synced after
40
+ * commit; internal state changes never re-render this component.
41
+ */
42
+ export declare function useComponent<D extends AnyDefinition>(definition: D, props: NoInfer<PropsOf<D>>): ComponentHandle<D>;
43
+ /**
44
+ * Reads one value of an instance's public state and re-renders only when it
45
+ * changes (`Object.is`, or `isEqual`). Takes the instance or the handle
46
+ * `useComponent` returned.
47
+ */
48
+ export declare function useComponentState<D extends AnyDefinition, T>(source: Instance<D> | ComponentHandle<D>, selector: (state: StateOf<D>) => T, isEqual?: (a: T, b: T) => boolean): T;
49
+ //#endregion
50
+ export type { ComponentHandle, OwnerProviderProps, ReactPartProps };
51
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,444 @@
1
+ "use client";
2
+ import { createContext, createElement, useCallback, useContext, useEffect, useId, useInsertionEffect, useLayoutEffect, useMemo, useRef, useSyncExternalStore } from "react";
3
+ import { createInstance, isOwnerDefinition } from "@uniaria/core";
4
+ import { createBinder, isDevelopment, provideOwner, requestOwner, warn } from "@uniaria/core/dom";
5
+ //#region src/owner.ts
6
+ const OwnerContext = createContext(null);
7
+ function OwnerProvider({ owner, children }) {
8
+ const parent = useContext(OwnerContext);
9
+ const instance = "instance" in owner ? owner.instance : owner;
10
+ const scope = useMemo(() => ({
11
+ instance,
12
+ parent
13
+ }), [instance, parent]);
14
+ return createElement(OwnerContext.Provider, { value: scope }, children);
15
+ }
16
+ /** The nearest owner of `definition` in the context, if any. */
17
+ function findOwner(scope, definition) {
18
+ for (let current = scope; current; current = current.parent) if (current.instance.definition === definition) return current.instance;
19
+ }
20
+ //#endregion
21
+ //#region src/props.ts
22
+ /**
23
+ * Neutral part props → React props (design decision 4): DOM attribute names
24
+ * become React prop names, empty boolean attributes become `true`, and DOM
25
+ * event names become React handler props.
26
+ */
27
+ /** DOM attribute → React prop, where they differ. */
28
+ const ATTRIBUTES = {
29
+ class: "className",
30
+ for: "htmlFor",
31
+ tabindex: "tabIndex",
32
+ readonly: "readOnly",
33
+ autofocus: "autoFocus",
34
+ autocomplete: "autoComplete",
35
+ autocorrect: "autoCorrect",
36
+ contenteditable: "contentEditable",
37
+ enterkeyhint: "enterKeyHint",
38
+ inputmode: "inputMode",
39
+ maxlength: "maxLength",
40
+ minlength: "minLength",
41
+ spellcheck: "spellCheck",
42
+ colspan: "colSpan",
43
+ rowspan: "rowSpan",
44
+ formaction: "formAction",
45
+ formenctype: "formEncType",
46
+ formmethod: "formMethod",
47
+ formnovalidate: "formNoValidate",
48
+ formtarget: "formTarget",
49
+ novalidate: "noValidate"
50
+ };
51
+ /** React props that take a boolean, so `''` must become `true`. */
52
+ const BOOLEANS = /* @__PURE__ */ new Set([
53
+ "disabled",
54
+ "hidden",
55
+ "checked",
56
+ "selected",
57
+ "required",
58
+ "readOnly",
59
+ "multiple",
60
+ "open",
61
+ "inert",
62
+ "autoFocus",
63
+ "noValidate",
64
+ "formNoValidate"
65
+ ]);
66
+ /** React's DOM event props; the map key is the DOM event name. */
67
+ const EVENTS = new Map([
68
+ "onAnimationEnd",
69
+ "onAnimationIteration",
70
+ "onAnimationStart",
71
+ "onAuxClick",
72
+ "onBeforeInput",
73
+ "onBeforeToggle",
74
+ "onChange",
75
+ "onClick",
76
+ "onCompositionEnd",
77
+ "onCompositionStart",
78
+ "onCompositionUpdate",
79
+ "onContextMenu",
80
+ "onCopy",
81
+ "onCut",
82
+ "onDrag",
83
+ "onDragEnd",
84
+ "onDragEnter",
85
+ "onDragExit",
86
+ "onDragLeave",
87
+ "onDragOver",
88
+ "onDragStart",
89
+ "onDrop",
90
+ "onError",
91
+ "onGotPointerCapture",
92
+ "onInvalid",
93
+ "onKeyDown",
94
+ "onKeyUp",
95
+ "onLoad",
96
+ "onLostPointerCapture",
97
+ "onMouseDown",
98
+ "onMouseEnter",
99
+ "onMouseLeave",
100
+ "onMouseMove",
101
+ "onMouseOut",
102
+ "onMouseOver",
103
+ "onMouseUp",
104
+ "onPaste",
105
+ "onPointerCancel",
106
+ "onPointerDown",
107
+ "onPointerEnter",
108
+ "onPointerLeave",
109
+ "onPointerMove",
110
+ "onPointerOut",
111
+ "onPointerOver",
112
+ "onPointerUp",
113
+ "onReset",
114
+ "onScroll",
115
+ "onScrollEnd",
116
+ "onSelect",
117
+ "onSubmit",
118
+ "onToggle",
119
+ "onTouchCancel",
120
+ "onTouchEnd",
121
+ "onTouchMove",
122
+ "onTouchStart",
123
+ "onTransitionEnd",
124
+ "onWheel"
125
+ ].map((name) => [name.slice(2).toLowerCase(), name]));
126
+ EVENTS.set("focusin", "onFocus");
127
+ EVENTS.set("focusout", "onBlur");
128
+ EVENTS.set("input", "onInput");
129
+ EVENTS.set("dblclick", "onDoubleClick");
130
+ /** The React prop for a DOM attribute. */
131
+ function reactPropName(attribute) {
132
+ return ATTRIBUTES[attribute] ?? attribute;
133
+ }
134
+ /** The React value for an attribute value: `''` on a boolean prop is `true`. */
135
+ function reactPropValue(name, value) {
136
+ return value === "" && BOOLEANS.has(name) ? true : value;
137
+ }
138
+ /** The React handler prop for a DOM event name. */
139
+ function reactEventName(type) {
140
+ return EVENTS.get(type) ?? `on${type.charAt(0).toUpperCase()}${type.slice(1)}`;
141
+ }
142
+ //#endregion
143
+ //#region src/use-component.ts
144
+ /**
145
+ * Live DOM properties React renders once as defaults, so it never controls
146
+ * the element; the binder sets them after that.
147
+ */
148
+ const DEFAULT_PROPS = {
149
+ value: "defaultValue",
150
+ checked: "defaultChecked"
151
+ };
152
+ /**
153
+ * Instances whose new props `useComponent` is syncing after a commit. React's
154
+ * `useSyncExternalStore` records the snapshot it rendered only in a passive
155
+ * effect, so a notification from this layout-effect sync is compared with the
156
+ * previous snapshot, and a component whose render already showed the new
157
+ * props would render again with the same value.
158
+ */
159
+ const syncing = /* @__PURE__ */ new WeakSet();
160
+ const useIsomorphicLayoutEffect = typeof document === "undefined" ? useEffect : useLayoutEffect;
161
+ /**
162
+ * Warns when the element's attributes differ from the part props React just
163
+ * rendered: the consumer wrote an attribute the binder owns, which the binder
164
+ * would silently overwrite.
165
+ */
166
+ function checkOwnership(internal, part, element, rendered = (internal.rendered?.connected)?.[part]) {
167
+ const isItem = internal.instance.definition.itemParts?.includes(part) ?? false;
168
+ for (const [name, value] of Object.entries(rendered?.attrs ?? {})) {
169
+ if (name === "id" && !isItem) continue;
170
+ const actual = element.getAttribute(name);
171
+ if (actual === (value ?? null)) continue;
172
+ warn(`${internal.instance.definition.name}: "${name}" on the ${part} part is written by UniAria, but the element has ${actual === null ? "none" : `"${actual}"`}. Pass it as a prop, or drop it from the JSX.`);
173
+ }
174
+ }
175
+ /**
176
+ * A style property as React names it: custom properties as they are,
177
+ * others in camelCase (`inset-block-start` → `insetBlockStart`).
178
+ */
179
+ const reactStyleName = (name) => name.startsWith("--") ? name : name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
180
+ /** Attributes and style as React props. */
181
+ function toReactAttrs(props, result) {
182
+ for (const [name, value] of Object.entries(props.attrs)) {
183
+ if (value === void 0) continue;
184
+ const prop = reactPropName(name);
185
+ result[prop] = reactPropValue(prop, value);
186
+ }
187
+ if (props.style) {
188
+ const style = {};
189
+ for (const [name, value] of Object.entries(props.style)) if (value !== void 0) style[reactStyleName(name)] = value;
190
+ result.style = style;
191
+ }
192
+ return result;
193
+ }
194
+ /**
195
+ * Hands the binder every item element attached or detached since the last
196
+ * flush: in a microtask, or before the owner's layout effects when the
197
+ * owner rendered in the same commit, so its instance starts and syncs with
198
+ * its items attached.
199
+ */
200
+ function flushAttaching(internal) {
201
+ if (internal.attaching.size === 0) return;
202
+ const list = Array.from(internal.attaching.values());
203
+ internal.attaching.clear();
204
+ internal.instance.batch(() => {
205
+ for (const { part, key, element } of list) internal.binder.attachItem(part, key, element);
206
+ });
207
+ }
208
+ /**
209
+ * `getItemProps` for one instance: React props from the cached
210
+ * `connectItem()`, reused while it returns the same object, with handlers
211
+ * stable per part and event type and a ref stable per part and key.
212
+ */
213
+ function createGetItemProps(internal) {
214
+ return (part, key) => {
215
+ const { instance, binder, itemHandlers, items } = internal();
216
+ const props = instance.connectItem(part, key);
217
+ const id = `${part}\n${key}`;
218
+ let item = items.get(id);
219
+ if (!item) {
220
+ const ref = (element) => {
221
+ const current = internal();
222
+ const entry = current.items.get(id);
223
+ if (element && isDevelopment() && !current.checked.has(element)) {
224
+ current.checked.add(element);
225
+ checkOwnership(current, part, element, entry?.rendered?.props);
226
+ }
227
+ const pending = current.attaching.get(id);
228
+ if (pending) pending.element = element;
229
+ else {
230
+ if (current.attaching.size === 0) queueMicrotask(() => flushAttaching(current));
231
+ current.attaching.set(id, {
232
+ part,
233
+ key,
234
+ element
235
+ });
236
+ }
237
+ if (!element && entry?.ref === ref) current.items.delete(id);
238
+ };
239
+ item = { ref };
240
+ items.set(id, item);
241
+ }
242
+ if (item.rendered?.props === props) return item.rendered.react;
243
+ const result = toReactAttrs(props, {});
244
+ for (const type of Object.keys(props.on ?? {})) {
245
+ const handlerKey = `${part}:${type}`;
246
+ let handler = itemHandlers.get(handlerKey);
247
+ if (!handler) {
248
+ handler = (event) => {
249
+ const target = binder.itemOf(event.currentTarget);
250
+ if (!target) return;
251
+ instance.connectItem(target.part, target.key).on?.[type]?.(event.nativeEvent);
252
+ };
253
+ itemHandlers.set(handlerKey, handler);
254
+ }
255
+ result[reactEventName(type)] = handler;
256
+ }
257
+ const react = {
258
+ ...result,
259
+ ref: item.ref
260
+ };
261
+ item.rendered = {
262
+ props,
263
+ react
264
+ };
265
+ return react;
266
+ };
267
+ }
268
+ function toReactParts(internal, connected) {
269
+ const { instance, binder, handlers, refs, defaults } = internal;
270
+ const parts = {};
271
+ for (const [part, props] of Object.entries(connected)) {
272
+ const result = {};
273
+ let initial = defaults.get(part);
274
+ if (!initial) {
275
+ const first = {};
276
+ for (const [name, value] of Object.entries(props.properties ?? {})) {
277
+ const prop = DEFAULT_PROPS[name];
278
+ if (prop && value !== void 0) first[prop] = value;
279
+ }
280
+ initial = first;
281
+ defaults.set(part, initial);
282
+ }
283
+ Object.assign(result, initial);
284
+ toReactAttrs(props, result);
285
+ for (const type of Object.keys(props.on ?? {})) {
286
+ const key = `${part}:${type}`;
287
+ let handler = handlers.get(key);
288
+ if (!handler) {
289
+ handler = (event) => {
290
+ instance.connect()[part]?.on?.[type]?.(event.nativeEvent);
291
+ };
292
+ handlers.set(key, handler);
293
+ }
294
+ result[reactEventName(type)] = handler;
295
+ }
296
+ let ref = refs.get(part);
297
+ if (!ref) {
298
+ ref = (element) => {
299
+ if (element && isDevelopment() && !internal.checked.has(element)) {
300
+ internal.checked.add(element);
301
+ checkOwnership(internal, part, element);
302
+ }
303
+ binder.attach(part, element);
304
+ };
305
+ refs.set(part, ref);
306
+ }
307
+ parts[part] = {
308
+ ...result,
309
+ ref
310
+ };
311
+ }
312
+ return parts;
313
+ }
314
+ /**
315
+ * Creates an instance of `definition` for this component and returns props
316
+ * for its parts. Props are stored silently during render and synced after
317
+ * commit; internal state changes never re-render this component.
318
+ */
319
+ function useComponent(definition, props) {
320
+ const id = useId();
321
+ const ref = useRef(null);
322
+ if (ref.current?.instance.definition !== definition) {
323
+ const instance = createInstance(definition, {
324
+ props,
325
+ id
326
+ });
327
+ ref.current = {
328
+ instance,
329
+ binder: createBinder(instance),
330
+ handlers: /* @__PURE__ */ new Map(),
331
+ refs: /* @__PURE__ */ new Map(),
332
+ itemHandlers: /* @__PURE__ */ new Map(),
333
+ items: /* @__PURE__ */ new Map(),
334
+ getItemProps: createGetItemProps(() => ref.current),
335
+ attaching: /* @__PURE__ */ new Map(),
336
+ defaults: /* @__PURE__ */ new Map(),
337
+ checked: /* @__PURE__ */ new WeakSet(),
338
+ isLinkedByContext: false
339
+ };
340
+ } else ref.current.instance.setProps(props);
341
+ const internal = ref.current;
342
+ const { instance, binder } = internal;
343
+ const scope = useContext(OwnerContext);
344
+ const ownerLink = definition.owner;
345
+ const contextOwner = ownerLink ? findOwner(scope, ownerLink.definition) : void 0;
346
+ if (contextOwner) {
347
+ instance.link(contextOwner);
348
+ internal.isLinkedByContext = true;
349
+ } else if (internal.isLinkedByContext) {
350
+ instance.link(void 0);
351
+ internal.isLinkedByContext = false;
352
+ }
353
+ const isLinkedByDom = ownerLink !== void 0 && contextOwner === void 0 && props.owner === void 0;
354
+ useIsomorphicLayoutEffect(() => {
355
+ flushAttaching(internal);
356
+ instance.batch(() => {
357
+ instance.start();
358
+ binder.start();
359
+ });
360
+ const unprovide = isOwnerDefinition(definition) ? definition.parts.flatMap((part) => {
361
+ const element = instance.element(part);
362
+ return element ? [provideOwner(instance, element)] : [];
363
+ }) : [];
364
+ return () => {
365
+ for (const stop of unprovide) stop();
366
+ binder.stop();
367
+ instance.stop();
368
+ };
369
+ }, [instance, binder]);
370
+ useIsomorphicLayoutEffect(() => {
371
+ if (!isLinkedByDom) return;
372
+ const element = definition.parts.map((part) => instance.element(part)).find(Boolean);
373
+ if (!element) return;
374
+ const forget = requestOwner(element, ownerLink.definition, (owner) => {
375
+ instance.link(owner);
376
+ instance.sync();
377
+ });
378
+ return () => {
379
+ forget();
380
+ instance.link(void 0);
381
+ instance.sync();
382
+ };
383
+ }, [instance, isLinkedByDom]);
384
+ useIsomorphicLayoutEffect(() => {
385
+ flushAttaching(internal);
386
+ syncing.add(instance);
387
+ try {
388
+ instance.sync();
389
+ } finally {
390
+ syncing.delete(instance);
391
+ }
392
+ });
393
+ const connected = instance.connect();
394
+ if (internal.rendered?.connected !== connected) internal.rendered = {
395
+ connected,
396
+ parts: toReactParts(internal, connected)
397
+ };
398
+ return {
399
+ instance,
400
+ actions: instance.actions,
401
+ parts: internal.rendered.parts,
402
+ getItemProps: internal.getItemProps
403
+ };
404
+ }
405
+ /**
406
+ * Reads one value of an instance's public state and re-renders only when it
407
+ * changes (`Object.is`, or `isEqual`). Takes the instance or the handle
408
+ * `useComponent` returned.
409
+ */
410
+ function useComponentState(source, selector, isEqual = Object.is) {
411
+ const instance = "instance" in source ? source.instance : source;
412
+ const cache = useRef(null);
413
+ const getSnapshot = () => {
414
+ const state = instance.getState();
415
+ const last = cache.current;
416
+ if (last?.state === state && last.selector === selector) return last.value;
417
+ const value = selector(state);
418
+ const stable = last && isEqual(last.value, value) ? last.value : value;
419
+ cache.current = {
420
+ state,
421
+ selector,
422
+ value: stable
423
+ };
424
+ return stable;
425
+ };
426
+ const committed = useRef(null);
427
+ const subscribe = useCallback((onChange) => instance.subscribe(() => {
428
+ const last = committed.current;
429
+ if (syncing.has(instance) && last && Object.is(last.value, last.getSnapshot())) return;
430
+ onChange();
431
+ }), [instance]);
432
+ const value = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
433
+ useInsertionEffect(() => {
434
+ committed.current = {
435
+ value,
436
+ getSnapshot
437
+ };
438
+ });
439
+ return value;
440
+ }
441
+ //#endregion
442
+ export { OwnerProvider, useComponent, useComponentState };
443
+
444
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/owner.ts","../src/props.ts","../src/use-component.ts"],"sourcesContent":["/**\n * Owners in React (decision 11): `OwnerProvider` makes an instance the owner\n * of the UniAria components rendered inside it, through a React context that\n * holds only the stable instance, so it re-renders nothing. A component\n * finds its owner during render, which gives the first render and the server\n * HTML the owner's state. Contexts nest: each component takes the nearest\n * owner of the definition it names.\n */\n\nimport type { AnyDefinition, Instance } from '@uniaria/core';\nimport type { ReactNode } from 'react';\nimport { createContext, createElement, useContext, useMemo } from 'react';\n\ninterface Scope {\n readonly instance: Instance;\n readonly parent: Scope | null;\n}\n\nexport const OwnerContext = createContext<Scope | null>(null);\n\nexport interface OwnerProviderProps<D extends AnyDefinition = AnyDefinition> {\n /** The owner: an instance, or the handle `useComponent` returned. */\n readonly owner: Instance<D> | { readonly instance: Instance<D> };\n readonly children?: ReactNode;\n}\n\n// Generic, since an `Instance<D>` isn't an `Instance`: its typed callback\n// props don't take `unknown`.\nexport function OwnerProvider<D extends AnyDefinition>({\n owner,\n children,\n}: OwnerProviderProps<D>) {\n const parent = useContext(OwnerContext);\n const instance = ('instance' in owner ? owner.instance : owner) as Instance;\n const scope = useMemo(() => ({ instance, parent }), [instance, parent]);\n return createElement(OwnerContext.Provider, { value: scope }, children);\n}\n\n/** The nearest owner of `definition` in the context, if any. */\nexport function findOwner(\n scope: Scope | null,\n definition: AnyDefinition,\n): Instance | undefined {\n for (let current = scope; current; current = current.parent) {\n if (current.instance.definition === definition) return current.instance;\n }\n return undefined;\n}\n","/**\n * Neutral part props → React props (design decision 4): DOM attribute names\n * become React prop names, empty boolean attributes become `true`, and DOM\n * event names become React handler props.\n */\n\n/** DOM attribute → React prop, where they differ. */\nconst ATTRIBUTES: Readonly<Record<string, string>> = {\n class: 'className',\n for: 'htmlFor',\n tabindex: 'tabIndex',\n readonly: 'readOnly',\n autofocus: 'autoFocus',\n autocomplete: 'autoComplete',\n autocorrect: 'autoCorrect',\n contenteditable: 'contentEditable',\n enterkeyhint: 'enterKeyHint',\n inputmode: 'inputMode',\n maxlength: 'maxLength',\n minlength: 'minLength',\n spellcheck: 'spellCheck',\n colspan: 'colSpan',\n rowspan: 'rowSpan',\n formaction: 'formAction',\n formenctype: 'formEncType',\n formmethod: 'formMethod',\n formnovalidate: 'formNoValidate',\n formtarget: 'formTarget',\n novalidate: 'noValidate',\n};\n\n/** React props that take a boolean, so `''` must become `true`. */\nconst BOOLEANS = new Set([\n 'disabled',\n 'hidden',\n 'checked',\n 'selected',\n 'required',\n 'readOnly',\n 'multiple',\n 'open',\n 'inert',\n 'autoFocus',\n 'noValidate',\n 'formNoValidate',\n]);\n\n/** React's DOM event props; the map key is the DOM event name. */\nconst EVENTS = new Map(\n [\n 'onAnimationEnd',\n 'onAnimationIteration',\n 'onAnimationStart',\n 'onAuxClick',\n 'onBeforeInput',\n 'onBeforeToggle',\n 'onChange',\n 'onClick',\n 'onCompositionEnd',\n 'onCompositionStart',\n 'onCompositionUpdate',\n 'onContextMenu',\n 'onCopy',\n 'onCut',\n 'onDrag',\n 'onDragEnd',\n 'onDragEnter',\n 'onDragExit',\n 'onDragLeave',\n 'onDragOver',\n 'onDragStart',\n 'onDrop',\n 'onError',\n 'onGotPointerCapture',\n 'onInvalid',\n 'onKeyDown',\n 'onKeyUp',\n 'onLoad',\n 'onLostPointerCapture',\n 'onMouseDown',\n 'onMouseEnter',\n 'onMouseLeave',\n 'onMouseMove',\n 'onMouseOut',\n 'onMouseOver',\n 'onMouseUp',\n 'onPaste',\n 'onPointerCancel',\n 'onPointerDown',\n 'onPointerEnter',\n 'onPointerLeave',\n 'onPointerMove',\n 'onPointerOut',\n 'onPointerOver',\n 'onPointerUp',\n 'onReset',\n 'onScroll',\n 'onScrollEnd',\n 'onSelect',\n 'onSubmit',\n 'onToggle',\n 'onTouchCancel',\n 'onTouchEnd',\n 'onTouchMove',\n 'onTouchStart',\n 'onTransitionEnd',\n 'onWheel',\n ].map((name) => [name.slice(2).toLowerCase(), name]),\n);\n// Focus entering or leaving a subtree: React's onFocus and onBlur bubble and\n// are backed by focusin and focusout. The input event is React's onInput:\n// its onChange fires on click for a checkbox, before a cancelled click is\n// undone, and skips changes its value tracker missed.\nEVENTS.set('focusin', 'onFocus');\nEVENTS.set('focusout', 'onBlur');\nEVENTS.set('input', 'onInput');\nEVENTS.set('dblclick', 'onDoubleClick');\n\n/** The React prop for a DOM attribute. */\nexport function reactPropName(attribute: string): string {\n return ATTRIBUTES[attribute] ?? attribute;\n}\n\n/** The React value for an attribute value: `''` on a boolean prop is `true`. */\nexport function reactPropValue(name: string, value: string): string | boolean {\n return value === '' && BOOLEANS.has(name) ? true : value;\n}\n\n/** The React handler prop for a DOM event name. */\nexport function reactEventName(type: string): string {\n return (\n EVENTS.get(type) ?? `on${type.charAt(0).toUpperCase()}${type.slice(1)}`\n );\n}\n","/**\n * The React adapter (design decisions 2 and 9). Internal state never\n * re-renders: each part renders once with its current props and a ref, and\n * the DOM binder from `@uniaria/core/dom` writes later changes straight to the\n * element. A component re-renders for state only through\n * `useComponentState`, and only when the selected value changes.\n */\n\nimport type {\n ActionsOf,\n AnyDefinition,\n Instance,\n ItemPartOf,\n OwnerLink,\n PartOf,\n PartProps,\n PropsOf,\n StateOf,\n} from '@uniaria/core';\nimport { createInstance, isOwnerDefinition } from '@uniaria/core';\nimport type { Binder } from '@uniaria/core/dom';\nimport {\n createBinder,\n isDevelopment,\n provideOwner,\n requestOwner,\n warn,\n} from '@uniaria/core/dom';\nimport type { SyntheticEvent } from 'react';\nimport {\n useCallback,\n useContext,\n useEffect,\n useId,\n useInsertionEffect,\n useLayoutEffect,\n useRef,\n useSyncExternalStore,\n} from 'react';\n\nimport { OwnerContext, findOwner } from './owner';\nimport { reactEventName, reactPropName, reactPropValue } from './props';\n\n/** Props to spread onto a part element: attributes, stable handlers, a ref. */\nexport type ReactPartProps = Readonly<Record<string, unknown>> & {\n readonly ref: (element: Element | null) => void;\n};\n\nexport interface ComponentHandle<D extends AnyDefinition> {\n /** The stable instance: pass it to `useComponentState` or as an `owner`. */\n readonly instance: Instance<D>;\n /** The component's public actions, such as a popover's `open()` and `close()`. */\n readonly actions: ActionsOf<D>;\n /** Props for each part, from this render. */\n readonly parts: Readonly<Record<PartOf<D>, ReactPartProps>>;\n /**\n * Props for one item part of one item (Listbox spec, Item parts), from\n * this render. Later changes reach the element through the binder, so the\n * component rendering the rows doesn't render again for them. Item parts\n * take no live DOM properties as React defaults.\n */\n getItemProps(part: ItemPartOf<D>, key: string): ReactPartProps;\n}\n\n/**\n * Live DOM properties React renders once as defaults, so it never controls\n * the element; the binder sets them after that.\n */\nconst DEFAULT_PROPS: Readonly<Record<string, string>> = {\n value: 'defaultValue',\n checked: 'defaultChecked',\n};\n\ninterface Internal<D extends AnyDefinition> {\n readonly instance: Instance<D>;\n readonly binder: Binder;\n readonly handlers: Map<string, (event: SyntheticEvent) => void>;\n readonly refs: Map<string, (element: Element | null) => void>;\n /** Per item part and event type; the key comes from the element. */\n readonly itemHandlers: Map<string, (event: SyntheticEvent) => void>;\n /** Per item part and key: its ref, and the props it last rendered. */\n readonly items: Map<\n string,\n {\n readonly ref: (element: Element | null) => void;\n rendered?: { readonly props: PartProps; readonly react: ReactPartProps };\n }\n >;\n readonly getItemProps: (part: string, key: string) => ReactPartProps;\n /**\n * Item elements React attached or detached since the last flush, per part\n * and key: each row has its own ref, so a commit that swaps a range of\n * rows reaches the binder as one batch, in a microtask.\n */\n readonly attaching: Map<\n string,\n { readonly part: string; readonly key: string; element: Element | null }\n >;\n /** Default props from the first render, kept stable after it. */\n readonly defaults: Map<string, Readonly<Record<string, unknown>>>;\n /** Elements whose attributes were checked against the render. */\n readonly checked: WeakSet<Element>;\n /** Whether the instance's owner came from React context. */\n isLinkedByContext: boolean;\n rendered?: {\n readonly connected: unknown;\n readonly parts: Record<string, ReactPartProps>;\n };\n}\n\n/**\n * Instances whose new props `useComponent` is syncing after a commit. React's\n * `useSyncExternalStore` records the snapshot it rendered only in a passive\n * effect, so a notification from this layout-effect sync is compared with the\n * previous snapshot, and a component whose render already showed the new\n * props would render again with the same value.\n */\nconst syncing = new WeakSet<object>();\n\n// Layout effects attach the binder before paint; on the server they never\n// run, and React 18 would warn about them.\nconst useIsomorphicLayoutEffect =\n typeof document === 'undefined' ? useEffect : useLayoutEffect;\n\n/**\n * Warns when the element's attributes differ from the part props React just\n * rendered: the consumer wrote an attribute the binder owns, which the binder\n * would silently overwrite.\n */\nfunction checkOwnership<D extends AnyDefinition>(\n internal: Internal<D>,\n part: string,\n element: Element,\n rendered: PartProps | undefined = (\n internal.rendered?.connected as\n Readonly<Record<string, PartProps>> | undefined\n )?.[part],\n): void {\n // A named part keeps an id the JSX gives it after the spread, and the\n // binder points every reference at it; an item's id is made from its key.\n const isItem =\n internal.instance.definition.itemParts?.includes(part) ?? false;\n for (const [name, value] of Object.entries(rendered?.attrs ?? {})) {\n if (name === 'id' && !isItem) continue;\n const actual = element.getAttribute(name);\n if (actual === (value ?? null)) continue;\n warn(\n `${internal.instance.definition.name}: \"${name}\" on the ${part} part is ` +\n `written by UniAria, but the element has ${actual === null ? 'none' : `\"${actual}\"`}. ` +\n 'Pass it as a prop, or drop it from the JSX.',\n );\n }\n}\n\n/**\n * A style property as React names it: custom properties as they are,\n * others in camelCase (`inset-block-start` → `insetBlockStart`).\n */\nconst reactStyleName = (name: string): string =>\n name.startsWith('--')\n ? name\n : name.replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase());\n\n/** Attributes and style as React props. */\nfunction toReactAttrs(\n props: PartProps,\n result: Record<string, unknown>,\n): Record<string, unknown> {\n for (const [name, value] of Object.entries(props.attrs)) {\n if (value === undefined) continue;\n const prop = reactPropName(name);\n result[prop] = reactPropValue(prop, value);\n }\n if (props.style) {\n const style: Record<string, string> = {};\n for (const [name, value] of Object.entries(props.style)) {\n if (value !== undefined) style[reactStyleName(name)] = value;\n }\n result.style = style;\n }\n return result;\n}\n\n/**\n * Hands the binder every item element attached or detached since the last\n * flush: in a microtask, or before the owner's layout effects when the\n * owner rendered in the same commit, so its instance starts and syncs with\n * its items attached.\n */\nfunction flushAttaching<D extends AnyDefinition>(internal: Internal<D>): void {\n if (internal.attaching.size === 0) return;\n const list = Array.from(internal.attaching.values());\n internal.attaching.clear();\n internal.instance.batch(() => {\n for (const { part, key, element } of list) {\n internal.binder.attachItem(part, key, element);\n }\n });\n}\n\n/**\n * `getItemProps` for one instance: React props from the cached\n * `connectItem()`, reused while it returns the same object, with handlers\n * stable per part and event type and a ref stable per part and key.\n */\nfunction createGetItemProps<D extends AnyDefinition>(\n internal: () => Internal<D>,\n): (part: string, key: string) => ReactPartProps {\n return (part, key) => {\n const { instance, binder, itemHandlers, items } = internal();\n const props = instance.connectItem(part as never, key);\n const id = `${part}\\n${key}`;\n let item = items.get(id);\n if (!item) {\n const ref = (element: Element | null) => {\n const current = internal();\n const entry = current.items.get(id);\n if (element && isDevelopment() && !current.checked.has(element)) {\n current.checked.add(element);\n checkOwnership(current, part, element, entry?.rendered?.props);\n }\n const pending = current.attaching.get(id);\n if (pending) pending.element = element;\n else {\n if (current.attaching.size === 0) {\n queueMicrotask(() => flushAttaching(current));\n }\n current.attaching.set(id, { part, key, element });\n }\n // An item that unmounts takes its cache with it; a remount gets a\n // new ref.\n if (!element && entry?.ref === ref) current.items.delete(id);\n };\n item = { ref };\n items.set(id, item);\n }\n if (item.rendered?.props === props) return item.rendered.react;\n const result = toReactAttrs(props, {});\n for (const type of Object.keys(props.on ?? {})) {\n const handlerKey = `${part}:${type}`;\n let handler = itemHandlers.get(handlerKey);\n if (!handler) {\n handler = (event: SyntheticEvent) => {\n const target = binder.itemOf(event.currentTarget as Element);\n if (!target) return;\n instance\n .connectItem(target.part as never, target.key)\n .on?.[type]?.(event.nativeEvent as never);\n };\n itemHandlers.set(handlerKey, handler);\n }\n result[reactEventName(type)] = handler;\n }\n const react = { ...result, ref: item.ref } as ReactPartProps;\n item.rendered = { props, react };\n return react;\n };\n}\n\nfunction toReactParts<D extends AnyDefinition>(\n internal: Internal<D>,\n connected: Readonly<Record<string, PartProps>>,\n): Record<string, ReactPartProps> {\n const { instance, binder, handlers, refs, defaults } = internal;\n const parts: Record<string, ReactPartProps> = {};\n for (const [part, props] of Object.entries(connected)) {\n const result: Record<string, unknown> = {};\n // From the first render only: a later default would reset the value\n // attribute, which a form reset goes back to.\n let initial = defaults.get(part);\n if (!initial) {\n const first: Record<string, unknown> = {};\n for (const [name, value] of Object.entries(props.properties ?? {})) {\n const prop = DEFAULT_PROPS[name];\n if (prop && value !== undefined) first[prop] = value;\n }\n initial = first;\n defaults.set(part, initial);\n }\n Object.assign(result, initial);\n toReactAttrs(props, result);\n // Handlers keep their identity across renders and call the handler from\n // the latest connect() with the native event.\n for (const type of Object.keys(props.on ?? {})) {\n const key = `${part}:${type}`;\n let handler = handlers.get(key);\n if (!handler) {\n handler = (event: SyntheticEvent) => {\n const latest = instance.connect() as Readonly<\n Record<string, PartProps>\n >;\n latest[part]?.on?.[type]?.(event.nativeEvent as never);\n };\n handlers.set(key, handler);\n }\n result[reactEventName(type)] = handler;\n }\n let ref = refs.get(part);\n if (!ref) {\n ref = (element: Element | null) => {\n // Once per element: one attached again (StrictMode does) carries\n // what the binder wrote since the render.\n if (element && isDevelopment() && !internal.checked.has(element)) {\n internal.checked.add(element);\n checkOwnership(internal, part, element);\n }\n binder.attach(part, element);\n };\n refs.set(part, ref);\n }\n parts[part] = { ...result, ref };\n }\n return parts;\n}\n\n/**\n * Creates an instance of `definition` for this component and returns props\n * for its parts. Props are stored silently during render and synced after\n * commit; internal state changes never re-render this component.\n */\nexport function useComponent<D extends AnyDefinition>(\n definition: D,\n props: NoInfer<PropsOf<D>>,\n): ComponentHandle<D> {\n const id = useId();\n const ref = useRef<Internal<D> | null>(null);\n if (ref.current?.instance.definition !== definition) {\n const instance = createInstance(definition, { props, id });\n ref.current = {\n instance,\n binder: createBinder(instance),\n handlers: new Map(),\n refs: new Map(),\n itemHandlers: new Map(),\n items: new Map(),\n getItemProps: createGetItemProps(() => ref.current!),\n attaching: new Map(),\n defaults: new Map(),\n checked: new WeakSet(),\n isLinkedByContext: false,\n };\n } else {\n ref.current.instance.setProps(props);\n }\n const internal = ref.current;\n const { instance, binder } = internal;\n\n // Decision 11: the `owner` prop, else React context during render (so the\n // first render and SSR have it), else the DOM after mount.\n const scope = useContext(OwnerContext);\n const ownerLink = (definition as { owner?: OwnerLink }).owner;\n const contextOwner = ownerLink\n ? findOwner(scope, ownerLink.definition)\n : undefined;\n if (contextOwner) {\n instance.link(contextOwner);\n internal.isLinkedByContext = true;\n } else if (internal.isLinkedByContext) {\n instance.link(undefined);\n internal.isLinkedByContext = false;\n }\n const isLinkedByDom =\n ownerLink !== undefined &&\n contextOwner === undefined &&\n (props as { owner?: unknown }).owner === undefined;\n\n useIsomorphicLayoutEffect(() => {\n flushAttaching(internal);\n // In one batch, so the start's element effects run once the binder has\n // written the parts: an overlay open from the start is `data-entering`\n // as it shows.\n instance.batch(() => {\n instance.start();\n binder.start();\n });\n // Answers owner requests from custom elements and React components that\n // find their owner through the DOM.\n const unprovide = isOwnerDefinition(definition)\n ? definition.parts.flatMap((part: string) => {\n const element = instance.element(part as PartOf<D>);\n return element\n ? [\n provideOwner(\n instance as unknown as Instance,\n element as Element,\n ),\n ]\n : [];\n })\n : [];\n return () => {\n for (const stop of unprovide) stop();\n binder.stop();\n instance.stop();\n };\n }, [instance, binder]);\n\n useIsomorphicLayoutEffect(() => {\n if (!isLinkedByDom) return;\n const element = definition.parts\n .map((part: string) => instance.element(part as PartOf<D>))\n .find(Boolean) as Element | undefined;\n if (!element) return;\n const forget = requestOwner(element, ownerLink!.definition, (owner) => {\n instance.link(owner);\n instance.sync();\n });\n return () => {\n forget();\n instance.link(undefined);\n instance.sync();\n };\n }, [instance, isLinkedByDom]);\n\n useIsomorphicLayoutEffect(() => {\n flushAttaching(internal);\n syncing.add(instance);\n try {\n instance.sync();\n } finally {\n syncing.delete(instance);\n }\n });\n\n const connected = instance.connect();\n if (internal.rendered?.connected !== connected) {\n internal.rendered = {\n connected,\n parts: toReactParts(internal, connected),\n };\n }\n\n return {\n instance,\n actions: instance.actions,\n parts: internal.rendered.parts as ComponentHandle<D>['parts'],\n getItemProps: internal.getItemProps as ComponentHandle<D>['getItemProps'],\n };\n}\n\n/**\n * Reads one value of an instance's public state and re-renders only when it\n * changes (`Object.is`, or `isEqual`). Takes the instance or the handle\n * `useComponent` returned.\n */\nexport function useComponentState<D extends AnyDefinition, T>(\n source: Instance<D> | ComponentHandle<D>,\n selector: (state: StateOf<D>) => T,\n isEqual: (a: T, b: T) => boolean = Object.is,\n): T {\n const instance = 'instance' in source ? source.instance : source;\n const cache = useRef<{\n state: StateOf<D>;\n selector: (state: StateOf<D>) => T;\n value: T;\n } | null>(null);\n\n // The selected value is the snapshot, so it must be stable between\n // changes: reuse it while the state object is the same, or while the new\n // selection is equal to the last one.\n const getSnapshot = (): T => {\n const state = instance.getState();\n const last = cache.current;\n if (last?.state === state && last.selector === selector) return last.value;\n const value = selector(state);\n const stable = last && isEqual(last.value, value) ? last.value : value;\n cache.current = { state, selector, value: stable };\n return stable;\n };\n\n // The committed value and snapshot function. Insertion effects run before\n // every layout effect, so they are current when `useComponent` syncs.\n const committed = useRef<{ value: T; getSnapshot: () => T } | null>(null);\n\n // Skips the notification from the prop sync after a commit when this hook\n // already rendered the value it would bring (see `syncing`). A change the\n // sync itself makes, such as hover ending on disable, still re-renders.\n const subscribe = useCallback(\n (onChange: () => void) =>\n instance.subscribe(() => {\n const last = committed.current;\n if (\n syncing.has(instance) &&\n last &&\n Object.is(last.value, last.getSnapshot())\n ) {\n return;\n }\n onChange();\n }),\n [instance],\n );\n\n const value = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n useInsertionEffect(() => {\n committed.current = { value, getSnapshot };\n });\n return value;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,63 @@
1
1
  {
2
2
  "name": "@uniaria/react",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "React adapter for UniAria: one generic useComponent hook over any UniAria component definition",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "exports": {
8
+ ".": {
9
+ "uniaria-source": "./src/index.ts",
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/tenphi/uniaria.git",
21
+ "directory": "packages/react"
22
+ },
23
+ "keywords": [
24
+ "headless",
25
+ "ui",
26
+ "react",
27
+ "accessibility",
28
+ "a11y",
29
+ "aria"
30
+ ],
31
+ "author": "Andrey Yamanov",
32
+ "license": "MIT",
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "bugs": {
37
+ "url": "https://github.com/tenphi/uniaria/issues"
38
+ },
39
+ "homepage": "https://github.com/tenphi/uniaria#readme",
40
+ "engines": {
41
+ "node": ">=22"
42
+ },
43
+ "dependencies": {
44
+ "@uniaria/core": "0.1.0"
45
+ },
46
+ "peerDependencies": {
47
+ "react": "^18.0.0 || ^19.0.0"
48
+ },
49
+ "devDependencies": {
50
+ "@uniaria/conformance": "0.0.0",
51
+ "@uniaria/testing": "0.1.0",
52
+ "@types/react": "^19.3.0",
53
+ "@types/react-dom": "^19.3.0",
54
+ "react": "^19.3.0",
55
+ "react-aria": "^3.52.1",
56
+ "react-dom": "^19.3.0"
57
+ },
58
+ "scripts": {
59
+ "build": "tsdown",
60
+ "typecheck": "tsc -p tsconfig.json && tsc -p bench/tsconfig.json",
61
+ "bench": "node bench/run.ts"
62
+ }
6
63
  }