@arun-dev/headless 2.0.0 → 2.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.
@@ -60,10 +60,20 @@ function isSyntheticEvent(event) {
60
60
  return typeof event === "object" && event !== null && "nativeEvent" in event;
61
61
  }
62
62
  function makeEventPreventable(event) {
63
+ if (Object.prototype.hasOwnProperty.call(event, "preventComponentHandler")) return;
63
64
  const target = event;
64
- target.preventComponentHandler = () => {
65
+ const prevent = () => {
65
66
  target.componentHandlerPrevented = true;
66
67
  };
68
+ target.preventComponentHandler = prevent;
69
+ const native = event.nativeEvent;
70
+ const stopImmediate = native?.stopImmediatePropagation?.bind(native);
71
+ if (native && stopImmediate) {
72
+ native.stopImmediatePropagation = () => {
73
+ stopImmediate();
74
+ prevent();
75
+ };
76
+ }
67
77
  }
68
78
  function isComponentHandlerPrevented(event) {
69
79
  return typeof event === "object" && event !== null && event.componentHandlerPrevented === true;
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  retractActivationProps,
3
3
  useButton
4
- } from "../chunk-ER7IAVR5.js";
4
+ } from "../chunk-TNMGZWHA.js";
5
5
  import {
6
6
  useRender
7
- } from "../chunk-IGKX2XWC.js";
7
+ } from "../chunk-PYLPZVWQ.js";
8
8
 
9
9
  // src/button/Button.tsx
10
10
  import { cloneElement } from "react";
@@ -34,10 +34,20 @@ function isSyntheticEvent(event) {
34
34
  return typeof event === "object" && event !== null && "nativeEvent" in event;
35
35
  }
36
36
  function makeEventPreventable(event) {
37
+ if (Object.prototype.hasOwnProperty.call(event, "preventComponentHandler")) return;
37
38
  const target = event;
38
- target.preventComponentHandler = () => {
39
+ const prevent = () => {
39
40
  target.componentHandlerPrevented = true;
40
41
  };
42
+ target.preventComponentHandler = prevent;
43
+ const native = event.nativeEvent;
44
+ const stopImmediate = native?.stopImmediatePropagation?.bind(native);
45
+ if (native && stopImmediate) {
46
+ native.stopImmediatePropagation = () => {
47
+ stopImmediate();
48
+ prevent();
49
+ };
50
+ }
41
51
  }
42
52
  function isComponentHandlerPrevented(event) {
43
53
  return typeof event === "object" && event !== null && event.componentHandlerPrevented === true;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  mergeProps
3
- } from "./chunk-IGKX2XWC.js";
3
+ } from "./chunk-PYLPZVWQ.js";
4
4
 
5
5
  // src/useButton.ts
6
6
  import { useEffect, useRef } from "react";
package/dist/index.cjs CHANGED
@@ -59,10 +59,20 @@ function isSyntheticEvent(event) {
59
59
  return typeof event === "object" && event !== null && "nativeEvent" in event;
60
60
  }
61
61
  function makeEventPreventable(event) {
62
+ if (Object.prototype.hasOwnProperty.call(event, "preventComponentHandler")) return;
62
63
  const target = event;
63
- target.preventComponentHandler = () => {
64
+ const prevent = () => {
64
65
  target.componentHandlerPrevented = true;
65
66
  };
67
+ target.preventComponentHandler = prevent;
68
+ const native = event.nativeEvent;
69
+ const stopImmediate = native?.stopImmediatePropagation?.bind(native);
70
+ if (native && stopImmediate) {
71
+ native.stopImmediatePropagation = () => {
72
+ stopImmediate();
73
+ prevent();
74
+ };
75
+ }
66
76
  }
67
77
  function isComponentHandlerPrevented(event) {
68
78
  return typeof event === "object" && event !== null && event.componentHandlerPrevented === true;
package/dist/index.d.cts CHANGED
@@ -1,12 +1,6 @@
1
1
  import { ReactElement } from 'react';
2
2
 
3
- /**
4
- * Prop merging for headless components.
5
- *
6
- * A component and its consumer both want to put things on the same element. Naive
7
- * spreading lets whichever runs last silently destroy the other's behaviour, so each
8
- * kind of prop is combined rather than replaced.
9
- */
3
+ /** The shape prop objects are handled as here: unknown keys, unknown values. */
10
4
  type UnknownProps = Record<string, unknown>;
11
5
  /**
12
6
  * An event a consumer's handler can use to stop the component's own handler running.
@@ -66,6 +60,17 @@ declare function mergeProps(...objects: (UnknownProps | undefined)[]): UnknownPr
66
60
  type StateAttributeMapping<State> = {
67
61
  [Key in keyof State]?: (value: State[Key]) => Record<string, string> | null;
68
62
  };
63
+ /**
64
+ * Turns a state object into the `data-*` attributes a mapping declares for it.
65
+ *
66
+ * Walks the state's own keys, so a field the mapping does not cover contributes
67
+ * nothing, and a mapping entry that returns `null` is skipped. Entries may each
68
+ * emit several attributes; later keys win if two entries name the same attribute.
69
+ *
70
+ * @param state - The component's current state.
71
+ * @param mapping - Which fields become attributes. `undefined` yields `{}`.
72
+ * @returns The attributes to spread onto the element.
73
+ */
69
74
  declare function getStateAttributes<State extends Record<string, unknown>>(state: State, mapping: StateAttributeMapping<State> | undefined): UnknownProps;
70
75
  /**
71
76
  * The common case: a boolean that emits one attribute when true and another when
package/dist/index.d.ts CHANGED
@@ -1,12 +1,6 @@
1
1
  import { ReactElement } from 'react';
2
2
 
3
- /**
4
- * Prop merging for headless components.
5
- *
6
- * A component and its consumer both want to put things on the same element. Naive
7
- * spreading lets whichever runs last silently destroy the other's behaviour, so each
8
- * kind of prop is combined rather than replaced.
9
- */
3
+ /** The shape prop objects are handled as here: unknown keys, unknown values. */
10
4
  type UnknownProps = Record<string, unknown>;
11
5
  /**
12
6
  * An event a consumer's handler can use to stop the component's own handler running.
@@ -66,6 +60,17 @@ declare function mergeProps(...objects: (UnknownProps | undefined)[]): UnknownPr
66
60
  type StateAttributeMapping<State> = {
67
61
  [Key in keyof State]?: (value: State[Key]) => Record<string, string> | null;
68
62
  };
63
+ /**
64
+ * Turns a state object into the `data-*` attributes a mapping declares for it.
65
+ *
66
+ * Walks the state's own keys, so a field the mapping does not cover contributes
67
+ * nothing, and a mapping entry that returns `null` is skipped. Entries may each
68
+ * emit several attributes; later keys win if two entries name the same attribute.
69
+ *
70
+ * @param state - The component's current state.
71
+ * @param mapping - Which fields become attributes. `undefined` yields `{}`.
72
+ * @returns The attributes to spread onto the element.
73
+ */
69
74
  declare function getStateAttributes<State extends Record<string, unknown>>(state: State, mapping: StateAttributeMapping<State> | undefined): UnknownProps;
70
75
  /**
71
76
  * The common case: a boolean that emits one attribute when true and another when
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  getStateAttributes,
8
8
  mergeProps,
9
9
  useRender
10
- } from "./chunk-IGKX2XWC.js";
10
+ } from "./chunk-PYLPZVWQ.js";
11
11
  export {
12
12
  booleanAttribute,
13
13
  disabledAttribute,
@@ -67,10 +67,20 @@ function isSyntheticEvent(event) {
67
67
  return typeof event === "object" && event !== null && "nativeEvent" in event;
68
68
  }
69
69
  function makeEventPreventable(event) {
70
+ if (Object.prototype.hasOwnProperty.call(event, "preventComponentHandler")) return;
70
71
  const target = event;
71
- target.preventComponentHandler = () => {
72
+ const prevent = () => {
72
73
  target.componentHandlerPrevented = true;
73
74
  };
75
+ target.preventComponentHandler = prevent;
76
+ const native = event.nativeEvent;
77
+ const stopImmediate = native?.stopImmediatePropagation?.bind(native);
78
+ if (native && stopImmediate) {
79
+ native.stopImmediatePropagation = () => {
80
+ stopImmediate();
81
+ prevent();
82
+ };
83
+ }
74
84
  }
75
85
  function isComponentHandlerPrevented(event) {
76
86
  return typeof event === "object" && event !== null && event.componentHandlerPrevented === true;
@@ -30,6 +30,16 @@ interface SwitchRootProps {
30
30
  name?: string;
31
31
  /** Value submitted when checked. Defaults to `"on"`, as a native checkbox does. */
32
32
  value?: string;
33
+ /**
34
+ * Names the switch when paired with a `<label htmlFor>`.
35
+ *
36
+ * Declared rather than left to the prop spread because it is how a switch gets an
37
+ * accessible name: `Switch.Root` renders a `<button>`, which a wrapping `<label>`
38
+ * would name implicitly, but `jsx-a11y/label-has-associated-control` rejects a
39
+ * `<button>` as a nested control. The explicit association keeps the rule quiet
40
+ * without a disable at the call site.
41
+ */
42
+ id?: string;
33
43
  className?: string;
34
44
  children?: ReactNode;
35
45
  /**
@@ -47,8 +57,8 @@ interface SwitchRootProps {
47
57
  * activation, and the disabled semantics for free. Per the WAI-ARIA switch pattern
48
58
  * it carries `role="switch"` and `aria-checked`.
49
59
  *
50
- * It has no accessible name of its own — wrap it in a `<label>`, or pass `aria-label`
51
- * or `aria-labelledby`. That is the consumer's decision, not something a headless
60
+ * It has no accessible name of its own — pair it with a `<label htmlFor>` by `id`, or
61
+ * pass `aria-label` or `aria-labelledby`. That is the consumer's decision, not a headless
52
62
  * component should guess.
53
63
  */
54
64
  declare function SwitchRoot({ checked: checkedProp, defaultChecked, nativeButton, onCheckedChange, disabled, name, value, className, children, render, ...rest }: SwitchRootProps & Record<string, unknown>): react.JSX.Element;
@@ -30,6 +30,16 @@ interface SwitchRootProps {
30
30
  name?: string;
31
31
  /** Value submitted when checked. Defaults to `"on"`, as a native checkbox does. */
32
32
  value?: string;
33
+ /**
34
+ * Names the switch when paired with a `<label htmlFor>`.
35
+ *
36
+ * Declared rather than left to the prop spread because it is how a switch gets an
37
+ * accessible name: `Switch.Root` renders a `<button>`, which a wrapping `<label>`
38
+ * would name implicitly, but `jsx-a11y/label-has-associated-control` rejects a
39
+ * `<button>` as a nested control. The explicit association keeps the rule quiet
40
+ * without a disable at the call site.
41
+ */
42
+ id?: string;
33
43
  className?: string;
34
44
  children?: ReactNode;
35
45
  /**
@@ -47,8 +57,8 @@ interface SwitchRootProps {
47
57
  * activation, and the disabled semantics for free. Per the WAI-ARIA switch pattern
48
58
  * it carries `role="switch"` and `aria-checked`.
49
59
  *
50
- * It has no accessible name of its own — wrap it in a `<label>`, or pass `aria-label`
51
- * or `aria-labelledby`. That is the consumer's decision, not something a headless
60
+ * It has no accessible name of its own — pair it with a `<label htmlFor>` by `id`, or
61
+ * pass `aria-label` or `aria-labelledby`. That is the consumer's decision, not a headless
52
62
  * component should guess.
53
63
  */
54
64
  declare function SwitchRoot({ checked: checkedProp, defaultChecked, nativeButton, onCheckedChange, disabled, name, value, className, children, render, ...rest }: SwitchRootProps & Record<string, unknown>): react.JSX.Element;
@@ -4,13 +4,13 @@ import {
4
4
  import {
5
5
  retractActivationProps,
6
6
  useButton
7
- } from "../chunk-ER7IAVR5.js";
7
+ } from "../chunk-TNMGZWHA.js";
8
8
  import {
9
9
  __export,
10
10
  booleanAttribute,
11
11
  disabledAttribute,
12
12
  useRender
13
- } from "../chunk-IGKX2XWC.js";
13
+ } from "../chunk-PYLPZVWQ.js";
14
14
 
15
15
  // src/switch/index.parts.ts
16
16
  var index_parts_exports = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arun-dev/headless",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Unstyled React behaviour primitives — render engine, controlled/uncontrolled state, and data-* state projection. Ships no CSS and no class names.",
5
5
  "license": "MIT",
6
6
  "type": "module",