praxis-kit 7.4.0 → 7.8.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.
@@ -1,8 +1,9 @@
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-DU3EdFOv.js';
2
+ export { g as AnyFactoryOptions, C as ContractProps, 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-DU3EdFOv.js';
3
3
  import * as react from 'react';
4
4
  import { ReactElement, Ref } from 'react';
5
5
  import 'type-fest';
6
+ import '@praxis-kit/contract-props';
6
7
  import '../_shared/diagnostics.js';
7
8
 
8
9
  /**
@@ -15,7 +15,7 @@ import {
15
15
  makeCloneSlotChild,
16
16
  mergeRefs,
17
17
  render
18
- } from "../chunk-EIKPSL26.js";
18
+ } from "../chunk-QYP7KLRE.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,7 +1,8 @@
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, c as AnyRecord, d as ReactFactoryOptions, M as MergeRecords, P as PolymorphicComponent, e as PolymorphicGenerics, f as ExtractPluginProps } from '../react-options-DU3EdFOv.js';
2
+ export { g as AnyFactoryOptions, C as ContractProps, 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-DU3EdFOv.js';
3
3
  import * as react from 'react';
4
4
  import 'type-fest';
5
+ import '@praxis-kit/contract-props';
5
6
  import '../_shared/diagnostics.js';
6
7
 
7
8
  /**
@@ -23,10 +24,12 @@ import '../_shared/diagnostics.js';
23
24
  * ```
24
25
  *
25
26
  * Returns a `forwardRef` component — `ref` is forwarded to the rendered host element the same
26
- * way it works in `praxis-kit/react`. Pass `onElement` to run setup once the real DOM element
27
- * exists; this adapter doesn't support `subComponents`.
27
+ * way it works in `praxis-kit/react`. Pass `subComponents` to attach named sub-components
28
+ * (`Card.Header`) and `onElement` to run setup once the real DOM element exists.
28
29
  */
29
- declare function createContractComponent<TDefault extends ElementType, Props extends UnknownProps = EmptyRecord, Variants extends Readonly<VariantMap> = NoVariants, TPreset extends RecipeMap<Variants> = NoPreset, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType>(options: ReactFactoryOptions<TDefault, Props, Variants, TPreset, TPlugin, TAllowed>): PolymorphicComponent<PolymorphicGenerics<TDefault, MergeRecords<Props, ExtractPluginProps<TPlugin>>, Variants, TPreset, TAllowed>>;
30
+ declare function createContractComponent<TDefault extends ElementType, Props extends UnknownProps = EmptyRecord, Variants extends Readonly<VariantMap> = NoVariants, TPreset extends RecipeMap<Variants> = NoPreset, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType, TSubComponents extends Readonly<AnyRecord> = EmptyRecord>(options: ReactFactoryOptions<TDefault, Props, Variants, TPreset, TPlugin, TAllowed> & {
31
+ readonly subComponents?: TSubComponents;
32
+ }): MergeRecords<PolymorphicComponent<PolymorphicGenerics<TDefault, MergeRecords<Props, ExtractPluginProps<TPlugin>>, Variants, TPreset, TAllowed>>, TSubComponents>;
30
33
 
31
34
  type SlotProps = {
32
35
  [key: string]: unknown;
@@ -1,19 +1,18 @@
1
1
  import {
2
- COMPONENT_DEFAULT_TAG,
3
2
  SLOT_NAME,
4
3
  Slottable,
5
4
  applyDisplayName,
6
5
  applySlot,
7
6
  buildRuntime,
8
7
  defineContractComponent,
8
+ finalizeComponent,
9
9
  getElementRef,
10
10
  getPropsRef,
11
11
  hasWarningGetter,
12
- isString,
13
12
  makeCloneSlotChild,
14
13
  mergeRefs,
15
14
  render
16
- } from "../chunk-EIKPSL26.js";
15
+ } from "../chunk-QYP7KLRE.js";
17
16
 
18
17
  // ../../adapters/react/src/legacy/create-contract-component.ts
19
18
  import { forwardRef as forwardRef2, useCallback, useRef } from "react";
@@ -57,7 +56,11 @@ function createContractComponent(options) {
57
56
  const onElementRef = useCallback((el) => {
58
57
  if (!onElement) return;
59
58
  if (el) {
60
- cleanupRef.current = onElement(el, () => propsRef.current) ?? void 0;
59
+ cleanupRef.current?.();
60
+ cleanupRef.current = onElement(
61
+ el,
62
+ () => propsRef.current
63
+ ) ?? void 0;
61
64
  } else {
62
65
  cleanupRef.current?.();
63
66
  cleanupRef.current = void 0;
@@ -67,11 +70,12 @@ function createContractComponent(options) {
67
70
  return render({ ...bundle, props, ref: mergedRef });
68
71
  });
69
72
  applyDisplayName(Component, options.name);
70
- const defaultTag = bundle.runtime.options.defaultTag;
71
- if (isString(defaultTag)) {
72
- Object.assign(Component, { [COMPONENT_DEFAULT_TAG]: defaultTag });
73
- }
74
- return Component;
73
+ const assembled = finalizeComponent(
74
+ Component,
75
+ bundle.runtime.options.defaultTag,
76
+ options.subComponents
77
+ );
78
+ return assembled;
75
79
  }
76
80
  export {
77
81
  Slot,
@@ -1,5 +1,6 @@
1
1
  import { Ref, PropsWithChildren, ReactElement, ComponentType, JSX, ReactNode } from 'react';
2
2
  import { RequireAtLeastOne, Simplify, ReadonlyDeep, NonEmptyTuple } from 'type-fest';
3
+ import { HasGenerics, Mode, PickMode } from '@praxis-kit/contract-props';
3
4
  import { Diagnostics, DiagnosticInput, DiagnosticsMode } from './_shared/diagnostics.js';
4
5
 
5
6
  /**
@@ -72,6 +73,19 @@ type MergeRecords<A extends object, B extends object> = IsEmptyRecord<A> extends
72
73
  type IntrinsicTag = keyof HTMLElementTagNameMap;
73
74
 
74
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;
75
89
 
76
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"];
77
91
  type KnownAriaRole = (typeof KNOWN_ARIA_ROLES)[number];
@@ -176,21 +190,78 @@ type DefaultVariants<V extends VariantMap> = {
176
190
  * Presets are named bundles of variant props that callers activate by key,
177
191
  * avoiding the need to repeat variant combinations at each call site.
178
192
  */
179
- type RecipeMap<V extends VariantMap = VariantMap> = Readonly<Record<string, VariantSelection<V>>>;
193
+ type RecipeMap<V extends VariantMap = VariantMap> = Readonly<StringMap<VariantSelection<V>>>;
180
194
 
181
195
  type RecipeTarget<TVariants extends VariantMap = VariantMap> = VariantSelection<TVariants>;
182
196
 
183
- interface PolymorphicGenerics<TDefault extends ElementType = ElementType, Props extends AnyRecord = AnyRecord, Variants extends Readonly<VariantMap> = Readonly<VariantMap>, TPreset extends RecipeMap<Variants> = Readonly<EmptyRecord>, TAllowed extends ElementType = ElementType> {
197
+ /**
198
+ * The framework-neutral descriptor for a single praxis-kit component's contract — every render
199
+ * mechanism (tag resolution, prop merging, classes, ARIA) and every framework adapter's
200
+ * component type is built from this one shape. Deliberately just data: five type parameters and
201
+ * their corresponding properties, with no notion of JSX, call signatures, refs, or any
202
+ * framework-specific rendering concern. Each adapter (React, Vue, Svelte, Solid, Lit, Web) builds
203
+ * its own idiomatic component type on top of a `PolymorphicGenerics<...>` instantiation — see
204
+ * `PolymorphicComponent<G>` (`adapters/react/src/shared/types/polymorphic-props.ts`) for the
205
+ * React example — rather than this interface knowing anything about any of them.
206
+ *
207
+ * Use the `*Of<T>` accessor aliases below (`DefaultOf<G>`, `PropsOf<G>`, etc.) to read a single
208
+ * field back out of an already-resolved `G`, instead of indexing `G['default']` etc. directly at
209
+ * call sites — same rationale as any accessor: the property name stays an implementation detail,
210
+ * and every reader benefits together if it ever needs to change.
211
+ */
212
+ interface PolymorphicGenerics<
213
+ /**
214
+ * The element/tag this component renders as when the consumer doesn't override it via `as`
215
+ * (`AllowedOf<G>` permitting) — e.g. `'button'`, `'div'`. Defaults to the widest `ElementType`
216
+ * so a generic `PolymorphicGenerics` reference (with nothing else specified) still compiles.
217
+ */
218
+ TDefault extends ElementType = ElementType,
219
+ /**
220
+ * The props this specific component declares — its own contract, before variants are mixed
221
+ * in. Defaults to `AnyRecord` for the same "still compiles unspecified" reason as `TDefault`.
222
+ */
223
+ Props extends AnyRecord = AnyRecord,
224
+ /**
225
+ * This component's variant definitions (e.g. `{ intent: { primary: ..., ghost: ... } }`).
226
+ * Constrained to `Readonly<VariantMap>` — not the wider `AnyRecord` — specifically so `TPreset`
227
+ * below can be expressed as `RecipeMap<Variants>` and get real per-variant-key checking,
228
+ * instead of falling back to an unconstrained `RecipeMap<VariantMap>`.
229
+ */
230
+ Variants extends Readonly<VariantMap> = Readonly<VariantMap>,
231
+ /**
232
+ * Named presets (`RecipeMap<Variants>`) — bundles of variant selections a consumer activates
233
+ * by key instead of repeating the same variant combination at every call site. Tied to
234
+ * `Variants`, not `AnyRecord`, precisely so a preset can only ever select keys/values that
235
+ * `Variants` actually defines — an invalid preset is a type error, not a silent no-op.
236
+ * Defaults to `Readonly<EmptyRecord>` (no presets), which is the common case: a component can
237
+ * have variants without necessarily defining any named presets over them, and most don't.
238
+ */
239
+ TPreset extends RecipeMap<Variants> = Readonly<EmptyRecord>,
240
+ /**
241
+ * The set of elements/tags a consumer is allowed to switch to via `as`. Defaults to the widest
242
+ * `ElementType`, under which `AllowedOf<G>` imposes no restriction at all (see
243
+ * `PolymorphicControlProps.as`'s own comment in the React adapter for the concrete effect this
244
+ * has at a component's actual call site).
245
+ */
246
+ TAllowed extends ElementType = ElementType> {
184
247
  default: TDefault;
185
248
  props: Props;
186
249
  variants: Variants;
187
250
  preset: TPreset;
188
251
  allowed: TAllowed;
189
252
  }
253
+ /** This component's variant definitions. See `PolymorphicGenerics`'s `Variants` parameter. */
190
254
  type VariantsOf<T extends PolymorphicGenerics> = T['variants'];
255
+ /** This component's named presets. See `PolymorphicGenerics`'s `TPreset` parameter. */
191
256
  type RecipeOf<T extends PolymorphicGenerics> = T['preset'];
257
+ /** The set of elements/tags this component may render as via `as`. See `PolymorphicGenerics`'s
258
+ * `TAllowed` parameter. */
192
259
  type AllowedOf<T extends PolymorphicGenerics> = T['allowed'];
260
+ /** The element/tag this component renders as by default. See `PolymorphicGenerics`'s `TDefault`
261
+ * parameter. */
193
262
  type DefaultOf<T extends PolymorphicGenerics> = T['default'];
263
+ /** This component's own declared props, before variants are mixed in. See `PolymorphicGenerics`'s
264
+ * `Props` parameter. */
194
265
  type PropsOf<T extends PolymorphicGenerics> = T['props'];
195
266
 
196
267
  type RequireAtLeastOneIfNotEmpty<T> = keyof T extends never ? EmptyRecord : RequireAtLeastOne<T>;
@@ -223,7 +294,7 @@ interface BaseClassOptions {
223
294
  type ClassPipelineFn = (tag: unknown, props: AnyRecord, className?: ClassName, recipe?: string) => string | undefined;
224
295
 
225
296
  interface RecipeOptions<TVariants extends VariantMap = VariantMap> {
226
- recipeMap?: Record<string, RecipeTarget<TVariants>>;
297
+ recipeMap?: StringMap<RecipeTarget<TVariants>>;
227
298
  }
228
299
 
229
300
  interface TagMapOptions {
@@ -392,7 +463,7 @@ type StylingOptions<V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPre
392
463
  * combination, skipping runtime class computation entirely when a match is found. Normally
393
464
  * generated by a build-time class-extraction plugin rather than hand-authored.
394
465
  */
395
- readonly precomputedClasses?: Readonly<Record<string, string>>;
466
+ readonly precomputedClasses?: Readonly<StringMap<string>>;
396
467
  };
397
468
 
398
469
  type NormalizeFn<Props extends AnyRecord = AnyRecord> = {
@@ -438,13 +509,23 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
438
509
  * no prop-based equivalent), not for anything expressible as a plain
439
510
  * prop.
440
511
  *
512
+ * `element` is typed to the real DOM interface of every tag the rendered
513
+ * element could actually be — `TDefault` plus whatever `enforcement.allowed`
514
+ * permits via `as` (`HTMLDialogElement` for `tag: 'dialog'`,
515
+ * `HTMLDetailsElement` for `tag: 'details'`, and so on) — no cast needed to
516
+ * reach tag-specific members. A component that leaves `allowed`
517
+ * unconstrained (any tag reachable via `as`) falls back to `HTMLElement`,
518
+ * which still covers members every element shares (`showPopover()` and
519
+ * friends); restrict `enforcement.allowed` to the tags `onElement`
520
+ * actually knows how to handle to get real narrowing.
521
+ *
441
522
  * `getProps` returns the instance's *current* resolved props at call
442
523
  * time — read it from inside a listener registered once at mount, rather
443
524
  * than re-subscribing on every prop change.
444
525
  *
445
526
  * Return a cleanup function to run when the instance unmounts.
446
527
  */
447
- readonly onElement?: (element: Element, getProps: () => Readonly<Props>) => void | (() => void);
528
+ readonly onElement?: (element: ElementForTag<TDefault | TAllowed>, getProps: () => Readonly<Props>) => void | (() => void);
448
529
  };
449
530
 
450
531
  type MetadataMap = AnyRecord;
@@ -631,14 +712,52 @@ type PolymorphicComponent<G extends PolymorphicGenerics> = {
631
712
  * `React.ComponentProps`.
632
713
  */
633
714
  (props: PolymorphicProps<G, DefaultOf<G>>): ReactElement;
715
+ /**
716
+ * Type-only; never assigned at runtime. See `HasGenerics<G>` (`@praxis-kit/contract-props`) for
717
+ * the full rationale — kept as an inline field rather than `HasGenerics<G> & {...}` because
718
+ * intersecting it onto this callable type changes how `PolymorphicComponent<any>` (used by
719
+ * test helpers like `box()`) resolves against concrete instantiations; structurally identical
720
+ * to `HasGenerics<G>` either way, which is what lets `ContractProps` constrain against it.
721
+ */
722
+ readonly __generics?: G;
634
723
  displayName?: string;
635
724
  };
725
+ /**
726
+ * Recovers a built `PolymorphicComponent<G>`'s prop shape for a specific render mode, from
727
+ * outside the file that built it — the missing piece `React.ComponentProps<typeof Component>`
728
+ * can't provide, since it always resolves against `PolymorphicComponent`'s normal-mode fallback
729
+ * overload (see that type's own doc comment).
730
+ *
731
+ * ```tsx
732
+ * const Container = createContractComponent({ tag: 'div', name: 'Container', /* ... *\/ })
733
+ *
734
+ * // Normal-mode props (equivalent to ComponentProps<typeof Container>, but works for every mode):
735
+ * type ContainerProps = ContractProps<typeof Container>
736
+ *
737
+ * // A wrapper that always renders Container with asChild — ComponentProps<typeof Container>
738
+ * // fails here ("Type 'true' is not assignable to type 'false'"); ContractProps doesn't.
739
+ * type ContainerAsChildProps = ContractProps<typeof Container, 'asChild'>
740
+ * ```
741
+ *
742
+ * `T` accepts any built component value (`PolymorphicComponent<G>` or `CompoundComponent<G, S>` —
743
+ * the latter's sub-component intersection doesn't disturb `__generics`, which lives on the root
744
+ * call signature) via its own `__generics` marker; the `never` branch below only fires for a
745
+ * non-praxis-kit component, which has no `__generics` field to infer from at all.
746
+ *
747
+ * Always resolves against the component's *default* element (`PolymorphicWithAsChild<G,
748
+ * DefaultOf<G>>`, etc.) — the same ceiling `React.ComponentProps<typeof Component>` already has
749
+ * for its one mode, not a new limitation `ContractProps` introduces. `ContractProps<typeof
750
+ * Button>` is "`Button`'s contract for its default element," not a substitute for
751
+ * `PolymorphicProps<G, 'a'>` when a caller needs a specific non-default `as` — those remain two
752
+ * different questions with two different answers.
753
+ */
754
+ type ContractProps<T extends HasGenerics<PolymorphicGenerics>, M extends Mode = 'normal'> = T extends HasGenerics<infer G extends PolymorphicGenerics> ? PickMode<M, PolymorphicProps<G, DefaultOf<G>>, PolymorphicWithAsChild<G, DefaultOf<G>>, PolymorphicWithRender<G, DefaultOf<G>>> : never;
636
755
 
637
756
  /** Structural subset of `CompiledComponentArtifact` consumed by the React adapter. */
638
757
  interface CompiledArtifact {
639
758
  readonly definition: ComponentDefinition;
640
759
  readonly precomputed?: {
641
- readonly variantLookup?: Record<string, string>;
760
+ readonly variantLookup?: StringMap<string>;
642
761
  };
643
762
  }
644
763
  /**
@@ -659,4 +778,4 @@ type ReactFactoryOptions<TDefault extends ElementType, Props extends UnknownProp
659
778
  artifact?: CompiledArtifact;
660
779
  };
661
780
 
662
- export { type AnyClassPluginFactory as A, type ElementType as E, type FactoryOptions as F, type MergeRecords as M, type NoVariants as N, type PolymorphicComponent as P, type RecipeMap as R, Slottable as S, type UnknownProps as U, type VariantMap as V, type EmptyRecord as a, type NoPreset as b, type AnyRecord as c, type ReactFactoryOptions as d, type PolymorphicGenerics as e, type ExtractPluginProps as f, type AnyFactoryOptions as g, type ElementRef as h, type PolymorphicProps as i, type PolymorphicWithAsChild as j, type PolymorphicWithRender as k, type RenderCallbackProps as l, type SlottableProps as m, mergeRefs as n, defineContractComponent as o };
781
+ export { type AnyClassPluginFactory as A, type ContractProps as C, type ElementType as E, type FactoryOptions as F, type MergeRecords as M, type NoVariants as N, type PolymorphicComponent as P, type RecipeMap as R, Slottable as S, type UnknownProps as U, type VariantMap as V, type EmptyRecord as a, type NoPreset as b, type AnyRecord as c, type ReactFactoryOptions as d, type PolymorphicGenerics as e, type ExtractPluginProps as f, type AnyFactoryOptions as g, type ElementRef as h, type PolymorphicProps as i, type PolymorphicWithAsChild as j, type PolymorphicWithRender as k, type RenderCallbackProps as l, type SlottableProps as m, mergeRefs as n, defineContractComponent as o };
@@ -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];
@@ -176,20 +189,75 @@ type DefaultVariants<V extends VariantMap> = {
176
189
  * Presets are named bundles of variant props that callers activate by key,
177
190
  * avoiding the need to repeat variant combinations at each call site.
178
191
  */
179
- type RecipeMap<V extends VariantMap = VariantMap> = Readonly<Record<string, VariantSelection<V>>>;
192
+ type RecipeMap<V extends VariantMap = VariantMap> = Readonly<StringMap<VariantSelection<V>>>;
180
193
 
181
194
  type RecipeTarget<TVariants extends VariantMap = VariantMap> = VariantSelection<TVariants>;
182
195
 
183
- interface PolymorphicGenerics<TDefault extends ElementType = ElementType, Props extends AnyRecord = AnyRecord, Variants extends Readonly<VariantMap> = Readonly<VariantMap>, TPreset extends RecipeMap<Variants> = Readonly<EmptyRecord>, TAllowed extends ElementType = ElementType> {
196
+ /**
197
+ * The framework-neutral descriptor for a single praxis-kit component's contract — every render
198
+ * mechanism (tag resolution, prop merging, classes, ARIA) and every framework adapter's
199
+ * component type is built from this one shape. Deliberately just data: five type parameters and
200
+ * their corresponding properties, with no notion of JSX, call signatures, refs, or any
201
+ * framework-specific rendering concern. Each adapter (React, Vue, Svelte, Solid, Lit, Web) builds
202
+ * its own idiomatic component type on top of a `PolymorphicGenerics<...>` instantiation — see
203
+ * `PolymorphicComponent<G>` (`adapters/react/src/shared/types/polymorphic-props.ts`) for the
204
+ * React example — rather than this interface knowing anything about any of them.
205
+ *
206
+ * Use the `*Of<T>` accessor aliases below (`DefaultOf<G>`, `PropsOf<G>`, etc.) to read a single
207
+ * field back out of an already-resolved `G`, instead of indexing `G['default']` etc. directly at
208
+ * call sites — same rationale as any accessor: the property name stays an implementation detail,
209
+ * and every reader benefits together if it ever needs to change.
210
+ */
211
+ interface PolymorphicGenerics<
212
+ /**
213
+ * The element/tag this component renders as when the consumer doesn't override it via `as`
214
+ * (`AllowedOf<G>` permitting) — e.g. `'button'`, `'div'`. Defaults to the widest `ElementType`
215
+ * so a generic `PolymorphicGenerics` reference (with nothing else specified) still compiles.
216
+ */
217
+ TDefault extends ElementType = ElementType,
218
+ /**
219
+ * The props this specific component declares — its own contract, before variants are mixed
220
+ * in. Defaults to `AnyRecord` for the same "still compiles unspecified" reason as `TDefault`.
221
+ */
222
+ Props extends AnyRecord = AnyRecord,
223
+ /**
224
+ * This component's variant definitions (e.g. `{ intent: { primary: ..., ghost: ... } }`).
225
+ * Constrained to `Readonly<VariantMap>` — not the wider `AnyRecord` — specifically so `TPreset`
226
+ * below can be expressed as `RecipeMap<Variants>` and get real per-variant-key checking,
227
+ * instead of falling back to an unconstrained `RecipeMap<VariantMap>`.
228
+ */
229
+ Variants extends Readonly<VariantMap> = Readonly<VariantMap>,
230
+ /**
231
+ * Named presets (`RecipeMap<Variants>`) — bundles of variant selections a consumer activates
232
+ * by key instead of repeating the same variant combination at every call site. Tied to
233
+ * `Variants`, not `AnyRecord`, precisely so a preset can only ever select keys/values that
234
+ * `Variants` actually defines — an invalid preset is a type error, not a silent no-op.
235
+ * Defaults to `Readonly<EmptyRecord>` (no presets), which is the common case: a component can
236
+ * have variants without necessarily defining any named presets over them, and most don't.
237
+ */
238
+ TPreset extends RecipeMap<Variants> = Readonly<EmptyRecord>,
239
+ /**
240
+ * The set of elements/tags a consumer is allowed to switch to via `as`. Defaults to the widest
241
+ * `ElementType`, under which `AllowedOf<G>` imposes no restriction at all (see
242
+ * `PolymorphicControlProps.as`'s own comment in the React adapter for the concrete effect this
243
+ * has at a component's actual call site).
244
+ */
245
+ TAllowed extends ElementType = ElementType> {
184
246
  default: TDefault;
185
247
  props: Props;
186
248
  variants: Variants;
187
249
  preset: TPreset;
188
250
  allowed: TAllowed;
189
251
  }
252
+ /** This component's variant definitions. See `PolymorphicGenerics`'s `Variants` parameter. */
190
253
  type VariantsOf<T extends PolymorphicGenerics> = T['variants'];
254
+ /** This component's named presets. See `PolymorphicGenerics`'s `TPreset` parameter. */
191
255
  type RecipeOf<T extends PolymorphicGenerics> = T['preset'];
256
+ /** The element/tag this component renders as by default. See `PolymorphicGenerics`'s `TDefault`
257
+ * parameter. */
192
258
  type DefaultOf<T extends PolymorphicGenerics> = T['default'];
259
+ /** This component's own declared props, before variants are mixed in. See `PolymorphicGenerics`'s
260
+ * `Props` parameter. */
193
261
  type PropsOf<T extends PolymorphicGenerics> = T['props'];
194
262
 
195
263
  type RequireAtLeastOneIfNotEmpty<T> = keyof T extends never ? EmptyRecord : RequireAtLeastOne<T>;
@@ -222,7 +290,7 @@ interface BaseClassOptions {
222
290
  type ClassPipelineFn = (tag: unknown, props: AnyRecord, className?: ClassName, recipe?: string) => string | undefined;
223
291
 
224
292
  interface RecipeOptions<TVariants extends VariantMap = VariantMap> {
225
- recipeMap?: Record<string, RecipeTarget<TVariants>>;
293
+ recipeMap?: StringMap<RecipeTarget<TVariants>>;
226
294
  }
227
295
 
228
296
  interface TagMapOptions {
@@ -391,7 +459,7 @@ type StylingOptions<V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPre
391
459
  * combination, skipping runtime class computation entirely when a match is found. Normally
392
460
  * generated by a build-time class-extraction plugin rather than hand-authored.
393
461
  */
394
- readonly precomputedClasses?: Readonly<Record<string, string>>;
462
+ readonly precomputedClasses?: Readonly<StringMap<string>>;
395
463
  };
396
464
 
397
465
  type NormalizeFn<Props extends AnyRecord = AnyRecord> = {
@@ -437,20 +505,29 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
437
505
  * no prop-based equivalent), not for anything expressible as a plain
438
506
  * prop.
439
507
  *
508
+ * `element` is typed to the real DOM interface of every tag the rendered
509
+ * element could actually be — `TDefault` plus whatever `enforcement.allowed`
510
+ * permits via `as` (`HTMLDialogElement` for `tag: 'dialog'`,
511
+ * `HTMLDetailsElement` for `tag: 'details'`, and so on) — no cast needed to
512
+ * reach tag-specific members. A component that leaves `allowed`
513
+ * unconstrained (any tag reachable via `as`) falls back to `HTMLElement`,
514
+ * which still covers members every element shares (`showPopover()` and
515
+ * friends); restrict `enforcement.allowed` to the tags `onElement`
516
+ * actually knows how to handle to get real narrowing.
517
+ *
440
518
  * `getProps` returns the instance's *current* resolved props at call
441
519
  * time — read it from inside a listener registered once at mount, rather
442
520
  * than re-subscribing on every prop change.
443
521
  *
444
522
  * Return a cleanup function to run when the instance unmounts.
445
523
  */
446
- readonly onElement?: (element: Element, getProps: () => Readonly<Props>) => void | (() => void);
524
+ readonly onElement?: (element: ElementForTag<TDefault | TAllowed>, getProps: () => Readonly<Props>) => void | (() => void);
447
525
  };
448
526
 
449
527
  declare function defineContractComponent<O extends FactoryOptions>(options: O): <R>(factory: (options: O) => R) => R;
450
528
 
451
529
  type UnknownProps = AnyRecord;
452
530
  type SolidElement = JSX.Element;
453
- type SlotRenderFn = (props: UnknownProps) => SolidElement;
454
531
 
455
532
  type SolidFactoryOptions<TDefault extends ElementType, Props extends UnknownProps, Variants extends Readonly<VariantMap>, TPreset extends RecipeMap<Variants> = NoPreset, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory> = FactoryOptions<TDefault, Props, Variants, TPreset, TPlugin> & {
456
533
  /**
@@ -469,10 +546,43 @@ type ControlProps<G extends PolymorphicGenerics, TAs extends ElementType> = Omit
469
546
  ref?: (el: ElementRef<TAs>) => void;
470
547
  };
471
548
  type SharedProps<G extends PolymorphicGenerics, TAs extends ElementType> = Omit<IntrinsicJSXProps<TAs>, keyof ControlProps<G, TAs> | 'children' | 'ref'> & ControlProps<G, TAs>;
549
+ /**
550
+ * The exact props object an `asChild` render function receives at runtime, once defaults,
551
+ * variant classes, and ARIA role resolution have all run — see `buildSlotProps` in `render.tsx`.
552
+ * `PropsOf<G>` stays `Partial` for the same reason `AsChildProps` below does: the type system
553
+ * can't prove every prop actually received a default, only that the runtime *tried*. `class` is
554
+ * narrowed to a plain resolved `string` (not the wider `ClassName` a caller may pass in).
555
+ *
556
+ * `ref` is `(el: Element) => void` rather than `AsChildProps.ref`'s bare `unknown` — a render
557
+ * function's own job is spreading this object directly onto a concrete element (`(props) => <a
558
+ * {...props} />`), so it needs a callback-ref shape assignable to that element's own `ref` prop
559
+ * type; contravariance makes an `Element`-typed callback assignable to any more specific one
560
+ * (`(el: HTMLAnchorElement) => void`, etc.) without knowing `TAs` in advance. `AsChildProps.ref`
561
+ * itself stays `unknown` on purpose — that field types what a *caller* hands in before the render
562
+ * function has even run, not what the render function receives back out.
563
+ *
564
+ * `role` (the value `buildSlotProps` adds only when `isKnownAriaRole` narrows it) is deliberately
565
+ * left OFF this type entirely, rather than given any explicit type. Every candidate representation
566
+ * fails the same way `ref` almost did: Solid's own per-element JSX types (`AnchorHTMLAttributes
567
+ * ['role']`, etc.) each narrow `role` to only the ARIA roles valid for *that* element, a strict
568
+ * subset of the full ARIA vocabulary — so a `KnownAriaRole`-typed field fails to spread onto any
569
+ * of them, and unlike `ref`, there's no contravariance trick available for a plain string-literal
570
+ * property (`unknown` fails the same assignability check `KnownAriaRole` does; only `any` would
571
+ * satisfy every per-element union, and this codebase doesn't use `any`). Omitting the key entirely
572
+ * keeps `{...props}` spreads honest and compiling; a render function that specifically needs to
573
+ * read `role` off this object needs an explicit, locally-scoped cast to do so. Previously this
574
+ * whole parameter was bare `UnknownProps`, giving the render function no type checking at all.
575
+ */
576
+ type ResolvedSlotProps<G extends PolymorphicGenerics> = Partial<OmitIndexSignature<PropsOf<G>>> & OmitIndexSignature<VariantProps<VariantsOf<G>>> & {
577
+ class?: string | undefined;
578
+ ref?: (el: Element) => void;
579
+ };
580
+ /** An `asChild` render function, receiving the fully-resolved `ResolvedSlotProps<G>`. */
581
+ type SlotRenderFn<G extends PolymorphicGenerics> = (props: ResolvedSlotProps<G>) => SolidElement;
472
582
  type AsChildProps<G extends PolymorphicGenerics> = Partial<OmitIndexSignature<PropsOf<G>>> & OmitIndexSignature<VariantProps<VariantsOf<G>>> & {
473
583
  as?: never;
474
584
  asChild: true;
475
- children: SlotRenderFn;
585
+ children: SlotRenderFn<G>;
476
586
  class?: ClassName | undefined;
477
587
  recipe?: keyof RecipeOf<G>;
478
588
  ref?: unknown;
@@ -494,6 +604,15 @@ type PolymorphicComponent<G extends PolymorphicGenerics> = {
494
604
  (props: PolymorphicProps<G, DefaultOf<G>>): JSX.Element;
495
605
  displayName?: string;
496
606
  };
607
+ /**
608
+ * A component's full prop contract — naming symmetry with React's/Preact's `ContractProps<T,
609
+ * Mode>` (`@praxis-kit/contract-props`), not a fix for a gap: Solid has no version of the
610
+ * overload-resolution ceiling those two adapters need a marker to work around. `PolymorphicProps<G,
611
+ * TAs>` already folds both render modes into one unioned type (rather than two separate types the
612
+ * way React/Preact split them), and `PolymorphicComponent<G>`'s fallback overload already returns
613
+ * that whole union — so this alias is just `PolymorphicProps<G>` under a familiar name.
614
+ */
615
+ type ContractProps<G extends PolymorphicGenerics> = PolymorphicProps<G>;
497
616
 
498
617
  /**
499
618
  * Creates a polymorphic Solid component with praxis-kit contracts applied.
@@ -519,4 +638,4 @@ declare function createContractComponent<TDefault extends ElementType, Props ext
519
638
  readonly subComponents?: TSubComponents;
520
639
  }): MergeRecords<PolymorphicComponent<PolymorphicGenerics<TDefault, MergeRecords<Props, ExtractPluginProps<TPlugin>>, Variants, TPreset>>, TSubComponents>;
521
640
 
522
- export { type AnyFactoryOptions, type ElementRef, type ElementType, type EmptyRecord, type PolymorphicComponent, type PolymorphicGenerics, type PolymorphicProps, type SolidFactoryOptions, createContractComponent, defineContractComponent };
641
+ export { type AnyFactoryOptions, type ContractProps, type ElementRef, type ElementType, type EmptyRecord, type PolymorphicComponent, type PolymorphicGenerics, type PolymorphicProps, type ResolvedSlotProps, type SlotRenderFn, type SolidFactoryOptions, createContractComponent, defineContractComponent };