praxis-kit 7.4.0 → 7.5.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.
@@ -25,6 +25,19 @@ type SubComponentMap = Readonly<AnyRecord>;
25
25
  type IntrinsicTag = keyof HTMLElementTagNameMap;
26
26
 
27
27
  type ElementType = IntrinsicTag | (string & {});
28
+ /**
29
+ * Resolves a component's default tag to its real DOM interface — `HTMLDialogElement` for
30
+ * `'dialog'`, `HTMLDetailsElement` for `'details'`, and so on — falling back to `HTMLElement`
31
+ * for custom-element tags or anything not in `HTMLElementTagNameMap`. Used to type
32
+ * `FactoryOptions.onElement`'s `element` param so component authors get direct, correctly-typed
33
+ * access to tag-specific native members (`dialogEl.showModal()`) without an unsafe cast.
34
+ *
35
+ * The fallback is `HTMLElement`, not the more generic `Element` — every tag reachable through
36
+ * `IntrinsicTag` extends it, and so does every custom element per spec, so members `HTMLElement`
37
+ * itself declares (`showPopover()`/`hidePopover()`/`togglePopover()`, the `popover` attribute)
38
+ * stay directly accessible even for tags with no dedicated entry in `HTMLElementTagNameMap`.
39
+ */
40
+ type ElementForTag<TDefault extends ElementType> = TDefault extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[TDefault] : HTMLElement;
28
41
 
29
42
  declare const KNOWN_ARIA_ROLES: readonly ["alert", "alertdialog", "application", "article", "banner", "blockquote", "button", "caption", "cell", "checkbox", "code", "columnheader", "combobox", "complementary", "contentinfo", "definition", "deletion", "dialog", "document", "emphasis", "feed", "figure", "form", "generic", "grid", "gridcell", "group", "heading", "img", "insertion", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "meter", "navigation", "none", "note", "option", "paragraph", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "strong", "subscript", "superscript", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "time", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"];
30
43
  type KnownAriaRole = (typeof KNOWN_ARIA_ROLES)[number];
@@ -373,13 +386,23 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
373
386
  * no prop-based equivalent), not for anything expressible as a plain
374
387
  * prop.
375
388
  *
389
+ * `element` is typed to the real DOM interface of every tag the rendered
390
+ * element could actually be — `TDefault` plus whatever `enforcement.allowed`
391
+ * permits via `as` (`HTMLDialogElement` for `tag: 'dialog'`,
392
+ * `HTMLDetailsElement` for `tag: 'details'`, and so on) — no cast needed to
393
+ * reach tag-specific members. A component that leaves `allowed`
394
+ * unconstrained (any tag reachable via `as`) falls back to `HTMLElement`,
395
+ * which still covers members every element shares (`showPopover()` and
396
+ * friends); restrict `enforcement.allowed` to the tags `onElement`
397
+ * actually knows how to handle to get real narrowing.
398
+ *
376
399
  * `getProps` returns the instance's *current* resolved props at call
377
400
  * time — read it from inside a listener registered once at mount, rather
378
401
  * than re-subscribing on every prop change.
379
402
  *
380
403
  * Return a cleanup function to run when the instance unmounts.
381
404
  */
382
- readonly onElement?: (element: Element, getProps: () => Readonly<Props>) => void | (() => void);
405
+ readonly onElement?: (element: ElementForTag<TDefault | TAllowed>, getProps: () => Readonly<Props>) => void | (() => void);
383
406
  };
384
407
 
385
408
  /** Shared input shape for `invalidWithFix`/`invalidWithoutFix`. */
@@ -72,6 +72,19 @@ type MergeRecords<A extends object, B extends object> = IsEmptyRecord<A> extends
72
72
  type IntrinsicTag = keyof HTMLElementTagNameMap;
73
73
 
74
74
  type ElementType = IntrinsicTag | (string & {});
75
+ /**
76
+ * Resolves a component's default tag to its real DOM interface — `HTMLDialogElement` for
77
+ * `'dialog'`, `HTMLDetailsElement` for `'details'`, and so on — falling back to `HTMLElement`
78
+ * for custom-element tags or anything not in `HTMLElementTagNameMap`. Used to type
79
+ * `FactoryOptions.onElement`'s `element` param so component authors get direct, correctly-typed
80
+ * access to tag-specific native members (`dialogEl.showModal()`) without an unsafe cast.
81
+ *
82
+ * The fallback is `HTMLElement`, not the more generic `Element` — every tag reachable through
83
+ * `IntrinsicTag` extends it, and so does every custom element per spec, so members `HTMLElement`
84
+ * itself declares (`showPopover()`/`hidePopover()`/`togglePopover()`, the `popover` attribute)
85
+ * stay directly accessible even for tags with no dedicated entry in `HTMLElementTagNameMap`.
86
+ */
87
+ type ElementForTag<TDefault extends ElementType> = TDefault extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[TDefault] : HTMLElement;
75
88
 
76
89
  declare const KNOWN_ARIA_ROLES: readonly ["alert", "alertdialog", "application", "article", "banner", "blockquote", "button", "caption", "cell", "checkbox", "code", "columnheader", "combobox", "complementary", "contentinfo", "definition", "deletion", "dialog", "document", "emphasis", "feed", "figure", "form", "generic", "grid", "gridcell", "group", "heading", "img", "insertion", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "meter", "navigation", "none", "note", "option", "paragraph", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "strong", "subscript", "superscript", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "time", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"];
77
90
  type KnownAriaRole = (typeof KNOWN_ARIA_ROLES)[number];
@@ -420,13 +433,23 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
420
433
  * no prop-based equivalent), not for anything expressible as a plain
421
434
  * prop.
422
435
  *
436
+ * `element` is typed to the real DOM interface of every tag the rendered
437
+ * element could actually be — `TDefault` plus whatever `enforcement.allowed`
438
+ * permits via `as` (`HTMLDialogElement` for `tag: 'dialog'`,
439
+ * `HTMLDetailsElement` for `tag: 'details'`, and so on) — no cast needed to
440
+ * reach tag-specific members. A component that leaves `allowed`
441
+ * unconstrained (any tag reachable via `as`) falls back to `HTMLElement`,
442
+ * which still covers members every element shares (`showPopover()` and
443
+ * friends); restrict `enforcement.allowed` to the tags `onElement`
444
+ * actually knows how to handle to get real narrowing.
445
+ *
423
446
  * `getProps` returns the instance's *current* resolved props at call
424
447
  * time — read it from inside a listener registered once at mount, rather
425
448
  * than re-subscribing on every prop change.
426
449
  *
427
450
  * Return a cleanup function to run when the instance unmounts.
428
451
  */
429
- readonly onElement?: (element: Element, getProps: () => Readonly<Props>) => void | (() => void);
452
+ readonly onElement?: (element: ElementForTag<TDefault | TAllowed>, getProps: () => Readonly<Props>) => void | (() => void);
430
453
  };
431
454
 
432
455
  /**
@@ -72,6 +72,19 @@ type MergeRecords<A extends object, B extends object> = IsEmptyRecord<A> extends
72
72
  type IntrinsicTag = keyof HTMLElementTagNameMap;
73
73
 
74
74
  type ElementType = IntrinsicTag | (string & {});
75
+ /**
76
+ * Resolves a component's default tag to its real DOM interface — `HTMLDialogElement` for
77
+ * `'dialog'`, `HTMLDetailsElement` for `'details'`, and so on — falling back to `HTMLElement`
78
+ * for custom-element tags or anything not in `HTMLElementTagNameMap`. Used to type
79
+ * `FactoryOptions.onElement`'s `element` param so component authors get direct, correctly-typed
80
+ * access to tag-specific native members (`dialogEl.showModal()`) without an unsafe cast.
81
+ *
82
+ * The fallback is `HTMLElement`, not the more generic `Element` — every tag reachable through
83
+ * `IntrinsicTag` extends it, and so does every custom element per spec, so members `HTMLElement`
84
+ * itself declares (`showPopover()`/`hidePopover()`/`togglePopover()`, the `popover` attribute)
85
+ * stay directly accessible even for tags with no dedicated entry in `HTMLElementTagNameMap`.
86
+ */
87
+ type ElementForTag<TDefault extends ElementType> = TDefault extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[TDefault] : HTMLElement;
75
88
 
76
89
  declare const KNOWN_ARIA_ROLES: readonly ["alert", "alertdialog", "application", "article", "banner", "blockquote", "button", "caption", "cell", "checkbox", "code", "columnheader", "combobox", "complementary", "contentinfo", "definition", "deletion", "dialog", "document", "emphasis", "feed", "figure", "form", "generic", "grid", "gridcell", "group", "heading", "img", "insertion", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "meter", "navigation", "none", "note", "option", "paragraph", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "strong", "subscript", "superscript", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "time", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"];
77
90
  type KnownAriaRole = (typeof KNOWN_ARIA_ROLES)[number];
@@ -437,13 +450,23 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
437
450
  * no prop-based equivalent), not for anything expressible as a plain
438
451
  * prop.
439
452
  *
453
+ * `element` is typed to the real DOM interface of every tag the rendered
454
+ * element could actually be — `TDefault` plus whatever `enforcement.allowed`
455
+ * permits via `as` (`HTMLDialogElement` for `tag: 'dialog'`,
456
+ * `HTMLDetailsElement` for `tag: 'details'`, and so on) — no cast needed to
457
+ * reach tag-specific members. A component that leaves `allowed`
458
+ * unconstrained (any tag reachable via `as`) falls back to `HTMLElement`,
459
+ * which still covers members every element shares (`showPopover()` and
460
+ * friends); restrict `enforcement.allowed` to the tags `onElement`
461
+ * actually knows how to handle to get real narrowing.
462
+ *
440
463
  * `getProps` returns the instance's *current* resolved props at call
441
464
  * time — read it from inside a listener registered once at mount, rather
442
465
  * than re-subscribing on every prop change.
443
466
  *
444
467
  * Return a cleanup function to run when the instance unmounts.
445
468
  */
446
- readonly onElement?: (element: Element, getProps: () => Readonly<Props>) => void | (() => void);
469
+ readonly onElement?: (element: ElementForTag<TDefault | TAllowed>, getProps: () => Readonly<Props>) => void | (() => void);
447
470
  };
448
471
 
449
472
  declare function defineContractComponent<O extends FactoryOptions>(options: O): <R>(factory: (options: O) => R) => R;
@@ -3946,7 +3946,11 @@ function createContractComponent(options) {
3946
3946
  const onElementRef = useCallback((el) => {
3947
3947
  if (!onElement) return;
3948
3948
  if (el) {
3949
- cleanupRef.current = onElement(el, () => propsRef.current) ?? void 0;
3949
+ cleanupRef.current?.();
3950
+ cleanupRef.current = onElement(
3951
+ el,
3952
+ () => propsRef.current
3953
+ ) ?? void 0;
3950
3954
  } else {
3951
3955
  cleanupRef.current?.();
3952
3956
  cleanupRef.current = void 0;
@@ -1,5 +1,5 @@
1
- import { U as UnknownProps, E as ElementType, a as EmptyRecord, V as VariantMap, N as NoVariants, R as RecipeMap, b as NoPreset, A as AnyClassPluginFactory, c as AnyRecord, d as ReactFactoryOptions, M as MergeRecords, P as PolymorphicComponent, e as PolymorphicGenerics, f as ExtractPluginProps } from '../react-options-BnjZpdVh.js';
2
- export { g as AnyFactoryOptions, h as ElementRef, F as FactoryOptions, i as PolymorphicProps, j as PolymorphicWithAsChild, k as PolymorphicWithRender, l as RenderCallbackProps, S as Slottable, m as SlottableProps, n as composeRefs, o as defineContractComponent, n as mergeRefs } from '../react-options-BnjZpdVh.js';
1
+ import { U as UnknownProps, E as ElementType, a as EmptyRecord, V as VariantMap, N as NoVariants, R as RecipeMap, b as NoPreset, A as AnyClassPluginFactory, c as AnyRecord, d as ReactFactoryOptions, M as MergeRecords, P as PolymorphicComponent, e as PolymorphicGenerics, f as ExtractPluginProps } from '../react-options-5GbZ8Tbv.js';
2
+ export { g as AnyFactoryOptions, h as ElementRef, F as FactoryOptions, i as PolymorphicProps, j as PolymorphicWithAsChild, k as PolymorphicWithRender, l as RenderCallbackProps, S as Slottable, m as SlottableProps, n as composeRefs, o as defineContractComponent, n as mergeRefs } from '../react-options-5GbZ8Tbv.js';
3
3
  import * as react from 'react';
4
4
  import { ReactElement, Ref } from 'react';
5
5
  import 'type-fest';
@@ -15,7 +15,7 @@ import {
15
15
  makeCloneSlotChild,
16
16
  mergeRefs,
17
17
  render
18
- } from "../chunk-EIKPSL26.js";
18
+ } from "../chunk-SMHJEFNE.js";
19
19
 
20
20
  // ../../adapters/react/src/current/create-contract-component.ts
21
21
  import { useCallback, useRef } from "react";
@@ -59,7 +59,12 @@ function createContractComponent(options) {
59
59
  const onElementRef = useCallback((el) => {
60
60
  if (!onElement) return;
61
61
  if (el) {
62
- cleanupRef.current = onElement(el, () => propsRef.current) ?? void 0;
62
+ cleanupRef.current?.();
63
+ cleanupRef.current = void 0;
64
+ cleanupRef.current = onElement(
65
+ el,
66
+ () => propsRef.current
67
+ ) ?? void 0;
63
68
  } else {
64
69
  cleanupRef.current?.();
65
70
  cleanupRef.current = void 0;
@@ -1,5 +1,5 @@
1
- import { E as ElementType, U as UnknownProps, a as EmptyRecord, V as VariantMap, N as NoVariants, R as RecipeMap, b as NoPreset, A as AnyClassPluginFactory, d as ReactFactoryOptions, P as PolymorphicComponent, e as PolymorphicGenerics, M as MergeRecords, f as ExtractPluginProps } from '../react-options-BnjZpdVh.js';
2
- export { g as AnyFactoryOptions, h as ElementRef, F as FactoryOptions, i as PolymorphicProps, j as PolymorphicWithAsChild, k as PolymorphicWithRender, l as RenderCallbackProps, S as Slottable, m as SlottableProps, o as defineContractComponent, n as mergeRefs } from '../react-options-BnjZpdVh.js';
1
+ import { E as ElementType, U as UnknownProps, a as EmptyRecord, V as VariantMap, N as NoVariants, R as RecipeMap, b as NoPreset, A as AnyClassPluginFactory, d as ReactFactoryOptions, P as PolymorphicComponent, e as PolymorphicGenerics, M as MergeRecords, f as ExtractPluginProps } from '../react-options-5GbZ8Tbv.js';
2
+ export { g as AnyFactoryOptions, h as ElementRef, F as FactoryOptions, i as PolymorphicProps, j as PolymorphicWithAsChild, k as PolymorphicWithRender, l as RenderCallbackProps, S as Slottable, m as SlottableProps, o as defineContractComponent, n as mergeRefs } from '../react-options-5GbZ8Tbv.js';
3
3
  import * as react from 'react';
4
4
  import 'type-fest';
5
5
  import '../_shared/diagnostics.js';
@@ -13,7 +13,7 @@ import {
13
13
  makeCloneSlotChild,
14
14
  mergeRefs,
15
15
  render
16
- } from "../chunk-EIKPSL26.js";
16
+ } from "../chunk-SMHJEFNE.js";
17
17
 
18
18
  // ../../adapters/react/src/legacy/create-contract-component.ts
19
19
  import { forwardRef as forwardRef2, useCallback, useRef } from "react";
@@ -57,7 +57,11 @@ function createContractComponent(options) {
57
57
  const onElementRef = useCallback((el) => {
58
58
  if (!onElement) return;
59
59
  if (el) {
60
- cleanupRef.current = onElement(el, () => propsRef.current) ?? void 0;
60
+ cleanupRef.current?.();
61
+ cleanupRef.current = onElement(
62
+ el,
63
+ () => propsRef.current
64
+ ) ?? void 0;
61
65
  } else {
62
66
  cleanupRef.current?.();
63
67
  cleanupRef.current = void 0;
@@ -72,6 +72,19 @@ type MergeRecords<A extends object, B extends object> = IsEmptyRecord<A> extends
72
72
  type IntrinsicTag = keyof HTMLElementTagNameMap;
73
73
 
74
74
  type ElementType = IntrinsicTag | (string & {});
75
+ /**
76
+ * Resolves a component's default tag to its real DOM interface — `HTMLDialogElement` for
77
+ * `'dialog'`, `HTMLDetailsElement` for `'details'`, and so on — falling back to `HTMLElement`
78
+ * for custom-element tags or anything not in `HTMLElementTagNameMap`. Used to type
79
+ * `FactoryOptions.onElement`'s `element` param so component authors get direct, correctly-typed
80
+ * access to tag-specific native members (`dialogEl.showModal()`) without an unsafe cast.
81
+ *
82
+ * The fallback is `HTMLElement`, not the more generic `Element` — every tag reachable through
83
+ * `IntrinsicTag` extends it, and so does every custom element per spec, so members `HTMLElement`
84
+ * itself declares (`showPopover()`/`hidePopover()`/`togglePopover()`, the `popover` attribute)
85
+ * stay directly accessible even for tags with no dedicated entry in `HTMLElementTagNameMap`.
86
+ */
87
+ type ElementForTag<TDefault extends ElementType> = TDefault extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[TDefault] : HTMLElement;
75
88
 
76
89
  declare const KNOWN_ARIA_ROLES: readonly ["alert", "alertdialog", "application", "article", "banner", "blockquote", "button", "caption", "cell", "checkbox", "code", "columnheader", "combobox", "complementary", "contentinfo", "definition", "deletion", "dialog", "document", "emphasis", "feed", "figure", "form", "generic", "grid", "gridcell", "group", "heading", "img", "insertion", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "meter", "navigation", "none", "note", "option", "paragraph", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "strong", "subscript", "superscript", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "time", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"];
77
90
  type KnownAriaRole = (typeof KNOWN_ARIA_ROLES)[number];
@@ -438,13 +451,23 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
438
451
  * no prop-based equivalent), not for anything expressible as a plain
439
452
  * prop.
440
453
  *
454
+ * `element` is typed to the real DOM interface of every tag the rendered
455
+ * element could actually be — `TDefault` plus whatever `enforcement.allowed`
456
+ * permits via `as` (`HTMLDialogElement` for `tag: 'dialog'`,
457
+ * `HTMLDetailsElement` for `tag: 'details'`, and so on) — no cast needed to
458
+ * reach tag-specific members. A component that leaves `allowed`
459
+ * unconstrained (any tag reachable via `as`) falls back to `HTMLElement`,
460
+ * which still covers members every element shares (`showPopover()` and
461
+ * friends); restrict `enforcement.allowed` to the tags `onElement`
462
+ * actually knows how to handle to get real narrowing.
463
+ *
441
464
  * `getProps` returns the instance's *current* resolved props at call
442
465
  * time — read it from inside a listener registered once at mount, rather
443
466
  * than re-subscribing on every prop change.
444
467
  *
445
468
  * Return a cleanup function to run when the instance unmounts.
446
469
  */
447
- readonly onElement?: (element: Element, getProps: () => Readonly<Props>) => void | (() => void);
470
+ readonly onElement?: (element: ElementForTag<TDefault | TAllowed>, getProps: () => Readonly<Props>) => void | (() => void);
448
471
  };
449
472
 
450
473
  type MetadataMap = AnyRecord;
@@ -72,6 +72,19 @@ type MergeRecords<A extends object, B extends object> = IsEmptyRecord<A> extends
72
72
  type IntrinsicTag = keyof HTMLElementTagNameMap;
73
73
 
74
74
  type ElementType = IntrinsicTag | (string & {});
75
+ /**
76
+ * Resolves a component's default tag to its real DOM interface — `HTMLDialogElement` for
77
+ * `'dialog'`, `HTMLDetailsElement` for `'details'`, and so on — falling back to `HTMLElement`
78
+ * for custom-element tags or anything not in `HTMLElementTagNameMap`. Used to type
79
+ * `FactoryOptions.onElement`'s `element` param so component authors get direct, correctly-typed
80
+ * access to tag-specific native members (`dialogEl.showModal()`) without an unsafe cast.
81
+ *
82
+ * The fallback is `HTMLElement`, not the more generic `Element` — every tag reachable through
83
+ * `IntrinsicTag` extends it, and so does every custom element per spec, so members `HTMLElement`
84
+ * itself declares (`showPopover()`/`hidePopover()`/`togglePopover()`, the `popover` attribute)
85
+ * stay directly accessible even for tags with no dedicated entry in `HTMLElementTagNameMap`.
86
+ */
87
+ type ElementForTag<TDefault extends ElementType> = TDefault extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[TDefault] : HTMLElement;
75
88
 
76
89
  declare const KNOWN_ARIA_ROLES: readonly ["alert", "alertdialog", "application", "article", "banner", "blockquote", "button", "caption", "cell", "checkbox", "code", "columnheader", "combobox", "complementary", "contentinfo", "definition", "deletion", "dialog", "document", "emphasis", "feed", "figure", "form", "generic", "grid", "gridcell", "group", "heading", "img", "insertion", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "meter", "navigation", "none", "note", "option", "paragraph", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "strong", "subscript", "superscript", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "time", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"];
77
90
  type KnownAriaRole = (typeof KNOWN_ARIA_ROLES)[number];
@@ -437,13 +450,23 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
437
450
  * no prop-based equivalent), not for anything expressible as a plain
438
451
  * prop.
439
452
  *
453
+ * `element` is typed to the real DOM interface of every tag the rendered
454
+ * element could actually be — `TDefault` plus whatever `enforcement.allowed`
455
+ * permits via `as` (`HTMLDialogElement` for `tag: 'dialog'`,
456
+ * `HTMLDetailsElement` for `tag: 'details'`, and so on) — no cast needed to
457
+ * reach tag-specific members. A component that leaves `allowed`
458
+ * unconstrained (any tag reachable via `as`) falls back to `HTMLElement`,
459
+ * which still covers members every element shares (`showPopover()` and
460
+ * friends); restrict `enforcement.allowed` to the tags `onElement`
461
+ * actually knows how to handle to get real narrowing.
462
+ *
440
463
  * `getProps` returns the instance's *current* resolved props at call
441
464
  * time — read it from inside a listener registered once at mount, rather
442
465
  * than re-subscribing on every prop change.
443
466
  *
444
467
  * Return a cleanup function to run when the instance unmounts.
445
468
  */
446
- readonly onElement?: (element: Element, getProps: () => Readonly<Props>) => void | (() => void);
469
+ readonly onElement?: (element: ElementForTag<TDefault | TAllowed>, getProps: () => Readonly<Props>) => void | (() => void);
447
470
  };
448
471
 
449
472
  declare function defineContractComponent<O extends FactoryOptions>(options: O): <R>(factory: (options: O) => R) => R;
@@ -3749,7 +3749,10 @@ function createContractComponent(options) {
3749
3749
  const consumerRef = props.ref;
3750
3750
  return (el) => {
3751
3751
  if (typeof consumerRef === "function") consumerRef(el);
3752
- const cleanup = onElement(el, () => props);
3752
+ const cleanup = onElement(
3753
+ el,
3754
+ () => props
3755
+ );
3753
3756
  if (cleanup) onCleanup(cleanup);
3754
3757
  };
3755
3758
  }
@@ -71,6 +71,19 @@ type MergeRecords<A extends object, B extends object> = IsEmptyRecord<A> extends
71
71
  type IntrinsicTag = keyof HTMLElementTagNameMap;
72
72
 
73
73
  type ElementType = IntrinsicTag | (string & {});
74
+ /**
75
+ * Resolves a component's default tag to its real DOM interface — `HTMLDialogElement` for
76
+ * `'dialog'`, `HTMLDetailsElement` for `'details'`, and so on — falling back to `HTMLElement`
77
+ * for custom-element tags or anything not in `HTMLElementTagNameMap`. Used to type
78
+ * `FactoryOptions.onElement`'s `element` param so component authors get direct, correctly-typed
79
+ * access to tag-specific native members (`dialogEl.showModal()`) without an unsafe cast.
80
+ *
81
+ * The fallback is `HTMLElement`, not the more generic `Element` — every tag reachable through
82
+ * `IntrinsicTag` extends it, and so does every custom element per spec, so members `HTMLElement`
83
+ * itself declares (`showPopover()`/`hidePopover()`/`togglePopover()`, the `popover` attribute)
84
+ * stay directly accessible even for tags with no dedicated entry in `HTMLElementTagNameMap`.
85
+ */
86
+ type ElementForTag<TDefault extends ElementType> = TDefault extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[TDefault] : HTMLElement;
74
87
 
75
88
  declare const KNOWN_ARIA_ROLES: readonly ["alert", "alertdialog", "application", "article", "banner", "blockquote", "button", "caption", "cell", "checkbox", "code", "columnheader", "combobox", "complementary", "contentinfo", "definition", "deletion", "dialog", "document", "emphasis", "feed", "figure", "form", "generic", "grid", "gridcell", "group", "heading", "img", "insertion", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "meter", "navigation", "none", "note", "option", "paragraph", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "strong", "subscript", "superscript", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "time", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"];
76
89
  type KnownAriaRole = (typeof KNOWN_ARIA_ROLES)[number];
@@ -194,6 +207,8 @@ interface PolymorphicGenerics<TDefault extends ElementType = ElementType, Props
194
207
  preset: TPreset;
195
208
  allowed: TAllowed;
196
209
  }
210
+ type AllowedOf<T extends PolymorphicGenerics> = T['allowed'];
211
+ type DefaultOf<T extends PolymorphicGenerics> = T['default'];
197
212
  type PropsOf<T extends PolymorphicGenerics> = T['props'];
198
213
 
199
214
  type RequireAtLeastOneIfNotEmpty<T> = keyof T extends never ? EmptyRecord : RequireAtLeastOne<T>;
@@ -442,13 +457,23 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
442
457
  * no prop-based equivalent), not for anything expressible as a plain
443
458
  * prop.
444
459
  *
460
+ * `element` is typed to the real DOM interface of every tag the rendered
461
+ * element could actually be — `TDefault` plus whatever `enforcement.allowed`
462
+ * permits via `as` (`HTMLDialogElement` for `tag: 'dialog'`,
463
+ * `HTMLDetailsElement` for `tag: 'details'`, and so on) — no cast needed to
464
+ * reach tag-specific members. A component that leaves `allowed`
465
+ * unconstrained (any tag reachable via `as`) falls back to `HTMLElement`,
466
+ * which still covers members every element shares (`showPopover()` and
467
+ * friends); restrict `enforcement.allowed` to the tags `onElement`
468
+ * actually knows how to handle to get real narrowing.
469
+ *
445
470
  * `getProps` returns the instance's *current* resolved props at call
446
471
  * time — read it from inside a listener registered once at mount, rather
447
472
  * than re-subscribing on every prop change.
448
473
  *
449
474
  * Return a cleanup function to run when the instance unmounts.
450
475
  */
451
- readonly onElement?: (element: Element, getProps: () => Readonly<Props>) => void | (() => void);
476
+ readonly onElement?: (element: ElementForTag<TDefault | TAllowed>, getProps: () => Readonly<Props>) => void | (() => void);
452
477
  };
453
478
 
454
479
  type ResolvedFactoryOptions<TDefault extends ElementType = ElementType, Props extends AnyRecord = EmptyRecord, V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<V> = Readonly<EmptyRecord>> = {
@@ -604,12 +629,12 @@ type SvelteFactoryOptions<TDefault extends ElementType, Props extends UnknownPro
604
629
 
605
630
  type TypedRuntime<G extends PolymorphicGenerics> = ReturnType<typeof createPolymorphic2<DefaultOf<G>, PropsOf<G>, VariantsOf<G>, RecipeOf<G>>>;
606
631
 
607
- type OnElementFn<Props = unknown> = (element: Element, getProps: () => Readonly<Props>) => void | (() => void);
632
+ type OnElementFn<G extends PolymorphicGenerics = PolymorphicGenerics> = (element: ElementForTag<DefaultOf<G> | AllowedOf<G>>, getProps: () => Readonly<PropsOf<G>>) => void | (() => void);
608
633
  type BuiltRuntime<G extends PolymorphicGenerics = PolymorphicGenerics, TOptions extends WithChildRules = WithChildRules> = BuiltChildrenEvaluator<TOptions> & {
609
634
  runtime: TypedRuntime<G>;
610
635
  filterProps: FilterPredicate;
611
636
  slotValidator: SlotValidator;
612
- onElement?: OnElementFn<PropsOf<G>>;
637
+ onElement?: OnElementFn<G>;
613
638
  };
614
639
 
615
640
  /**
@@ -73,6 +73,19 @@ type MergeRecords<A extends object, B extends object> = IsEmptyRecord<A> extends
73
73
  type IntrinsicTag = keyof HTMLElementTagNameMap;
74
74
 
75
75
  type ElementType = IntrinsicTag | (string & {});
76
+ /**
77
+ * Resolves a component's default tag to its real DOM interface — `HTMLDialogElement` for
78
+ * `'dialog'`, `HTMLDetailsElement` for `'details'`, and so on — falling back to `HTMLElement`
79
+ * for custom-element tags or anything not in `HTMLElementTagNameMap`. Used to type
80
+ * `FactoryOptions.onElement`'s `element` param so component authors get direct, correctly-typed
81
+ * access to tag-specific native members (`dialogEl.showModal()`) without an unsafe cast.
82
+ *
83
+ * The fallback is `HTMLElement`, not the more generic `Element` — every tag reachable through
84
+ * `IntrinsicTag` extends it, and so does every custom element per spec, so members `HTMLElement`
85
+ * itself declares (`showPopover()`/`hidePopover()`/`togglePopover()`, the `popover` attribute)
86
+ * stay directly accessible even for tags with no dedicated entry in `HTMLElementTagNameMap`.
87
+ */
88
+ type ElementForTag<TDefault extends ElementType> = TDefault extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[TDefault] : HTMLElement;
76
89
 
77
90
  declare const KNOWN_ARIA_ROLES: readonly ["alert", "alertdialog", "application", "article", "banner", "blockquote", "button", "caption", "cell", "checkbox", "code", "columnheader", "combobox", "complementary", "contentinfo", "definition", "deletion", "dialog", "document", "emphasis", "feed", "figure", "form", "generic", "grid", "gridcell", "group", "heading", "img", "insertion", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "meter", "navigation", "none", "note", "option", "paragraph", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "strong", "subscript", "superscript", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "time", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"];
78
91
  type KnownAriaRole = (typeof KNOWN_ARIA_ROLES)[number];
@@ -438,13 +451,23 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
438
451
  * no prop-based equivalent), not for anything expressible as a plain
439
452
  * prop.
440
453
  *
454
+ * `element` is typed to the real DOM interface of every tag the rendered
455
+ * element could actually be — `TDefault` plus whatever `enforcement.allowed`
456
+ * permits via `as` (`HTMLDialogElement` for `tag: 'dialog'`,
457
+ * `HTMLDetailsElement` for `tag: 'details'`, and so on) — no cast needed to
458
+ * reach tag-specific members. A component that leaves `allowed`
459
+ * unconstrained (any tag reachable via `as`) falls back to `HTMLElement`,
460
+ * which still covers members every element shares (`showPopover()` and
461
+ * friends); restrict `enforcement.allowed` to the tags `onElement`
462
+ * actually knows how to handle to get real narrowing.
463
+ *
441
464
  * `getProps` returns the instance's *current* resolved props at call
442
465
  * time — read it from inside a listener registered once at mount, rather
443
466
  * than re-subscribing on every prop change.
444
467
  *
445
468
  * Return a cleanup function to run when the instance unmounts.
446
469
  */
447
- readonly onElement?: (element: Element, getProps: () => Readonly<Props>) => void | (() => void);
470
+ readonly onElement?: (element: ElementForTag<TDefault | TAllowed>, getProps: () => Readonly<Props>) => void | (() => void);
448
471
  };
449
472
 
450
473
  declare function defineContractComponent<O extends FactoryOptions>(options: O): <R>(factory: (options: O) => R) => R;
package/dist/vue/index.js CHANGED
@@ -3811,7 +3811,10 @@ function createContractComponent(options) {
3811
3811
  }
3812
3812
  boundElement = element;
3813
3813
  if (element) {
3814
- cleanup = onElement(element, () => attrs) ?? void 0;
3814
+ cleanup = onElement(
3815
+ element,
3816
+ () => attrs
3817
+ ) ?? void 0;
3815
3818
  }
3816
3819
  } : void 0;
3817
3820
  return () => render({ ...bundle, state: state.value, slots, elementRef: onElementRef });
@@ -47,6 +47,19 @@ type NoPluginProps = EmptyRecord;
47
47
  type IntrinsicTag = keyof HTMLElementTagNameMap;
48
48
 
49
49
  type ElementType = IntrinsicTag | (string & {});
50
+ /**
51
+ * Resolves a component's default tag to its real DOM interface — `HTMLDialogElement` for
52
+ * `'dialog'`, `HTMLDetailsElement` for `'details'`, and so on — falling back to `HTMLElement`
53
+ * for custom-element tags or anything not in `HTMLElementTagNameMap`. Used to type
54
+ * `FactoryOptions.onElement`'s `element` param so component authors get direct, correctly-typed
55
+ * access to tag-specific native members (`dialogEl.showModal()`) without an unsafe cast.
56
+ *
57
+ * The fallback is `HTMLElement`, not the more generic `Element` — every tag reachable through
58
+ * `IntrinsicTag` extends it, and so does every custom element per spec, so members `HTMLElement`
59
+ * itself declares (`showPopover()`/`hidePopover()`/`togglePopover()`, the `popover` attribute)
60
+ * stay directly accessible even for tags with no dedicated entry in `HTMLElementTagNameMap`.
61
+ */
62
+ type ElementForTag<TDefault extends ElementType> = TDefault extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[TDefault] : HTMLElement;
50
63
 
51
64
  declare const KNOWN_ARIA_ROLES: readonly ["alert", "alertdialog", "application", "article", "banner", "blockquote", "button", "caption", "cell", "checkbox", "code", "columnheader", "combobox", "complementary", "contentinfo", "definition", "deletion", "dialog", "document", "emphasis", "feed", "figure", "form", "generic", "grid", "gridcell", "group", "heading", "img", "insertion", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "meter", "navigation", "none", "note", "option", "paragraph", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "strong", "subscript", "superscript", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "time", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"];
52
65
  type KnownAriaRole = (typeof KNOWN_ARIA_ROLES)[number];
@@ -395,13 +408,23 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
395
408
  * no prop-based equivalent), not for anything expressible as a plain
396
409
  * prop.
397
410
  *
411
+ * `element` is typed to the real DOM interface of every tag the rendered
412
+ * element could actually be — `TDefault` plus whatever `enforcement.allowed`
413
+ * permits via `as` (`HTMLDialogElement` for `tag: 'dialog'`,
414
+ * `HTMLDetailsElement` for `tag: 'details'`, and so on) — no cast needed to
415
+ * reach tag-specific members. A component that leaves `allowed`
416
+ * unconstrained (any tag reachable via `as`) falls back to `HTMLElement`,
417
+ * which still covers members every element shares (`showPopover()` and
418
+ * friends); restrict `enforcement.allowed` to the tags `onElement`
419
+ * actually knows how to handle to get real narrowing.
420
+ *
398
421
  * `getProps` returns the instance's *current* resolved props at call
399
422
  * time — read it from inside a listener registered once at mount, rather
400
423
  * than re-subscribing on every prop change.
401
424
  *
402
425
  * Return a cleanup function to run when the instance unmounts.
403
426
  */
404
- readonly onElement?: (element: Element, getProps: () => Readonly<Props>) => void | (() => void);
427
+ readonly onElement?: (element: ElementForTag<TDefault | TAllowed>, getProps: () => Readonly<Props>) => void | (() => void);
405
428
  };
406
429
 
407
430
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "praxis-kit",
3
- "version": "7.4.0",
3
+ "version": "7.5.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./react": {
@@ -203,11 +203,11 @@
203
203
  "typescript": "^6.0.3",
204
204
  "vite": "^8.1.5",
205
205
  "vue": "^3.5.40",
206
- "@praxis-kit/adapter-utils": "0.0.0",
207
206
  "@praxis-kit/core": "0.0.0",
207
+ "@praxis-kit/adapter-utils": "0.0.0",
208
208
  "@praxis-kit/primitive": "0.0.0",
209
- "@praxis-kit/pipeline": "0.0.0",
210
209
  "@praxis-kit/diagnostics": "0.0.0",
210
+ "@praxis-kit/pipeline": "0.0.0",
211
211
  "@praxis-kit/vite-plugin": "0.0.0"
212
212
  },
213
213
  "publishConfig": {
File without changes