praxis-kit 7.5.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.
@@ -80,6 +80,14 @@
80
80
  return bundle.runtime.resolveAria(tag, ep).props as ResolvedAttributes
81
81
  }
82
82
 
83
+ // Unlike buildDomProps above, this intentionally skips normalizeEventKeys and style
84
+ // serialization — the asChild path hands props straight to a caller-authored snippet, which
85
+ // spreads them onto its own element via ordinary JSX/attribute semantics, not through
86
+ // <svelte:element>'s own attribute application; event-handler keys keep their caller-authored
87
+ // casing (`onClick`, not lowercased to `onclick`), and `style` passes through however the
88
+ // caller wrote it (object or string), unserialized. See `ResolvedSlotProps<G>`
89
+ // (types/resolved-slot-props.ts) for the type this produces — it deliberately omits `style`
90
+ // rather than asserting either shape, for this exact reason.
83
91
  function buildSlotProps(props: UnknownProps, classStr: string | undefined): UnknownProps {
84
92
  const { role, ...r } = props
85
93
  return {
@@ -1,5 +1,6 @@
1
- import { RequireAtLeastOne, Simplify, ReadonlyDeep } from 'type-fest';
1
+ import { RequireAtLeastOne, Simplify, ReadonlyDeep, OmitIndexSignature } from 'type-fest';
2
2
  import { Diagnostics, DiagnosticInput, DiagnosticsMode } from '../_shared/diagnostics.js';
3
+ import { Snippet } from 'svelte';
3
4
 
4
5
  /**
5
6
  * A string-keyed object whose values are of type `T`.
@@ -185,6 +186,11 @@ type VariantSelection<V extends VariantMap> = {
185
186
  [K in keyof V]?: keyof V[K];
186
187
  };
187
188
 
189
+ /** The full optional prop surface exposed to callers for a given variant map. */
190
+ type VariantProps<V extends VariantMap> = {
191
+ [K in keyof V]?: VariantKey<V, K>;
192
+ };
193
+
188
194
  type NormalizedVariantValue<K extends string> = string extends K ? Primitive : K extends 'true' | 'false' ? Booleanish : K extends `${number}` ? Numberish : K;
189
195
  type DefaultVariants<V extends VariantMap> = {
190
196
  [K in keyof V]?: NormalizedVariantValue<keyof V[K] & string>;
@@ -196,19 +202,78 @@ type DefaultVariants<V extends VariantMap> = {
196
202
  * Presets are named bundles of variant props that callers activate by key,
197
203
  * avoiding the need to repeat variant combinations at each call site.
198
204
  */
199
- type RecipeMap<V extends VariantMap = VariantMap> = Readonly<Record<string, VariantSelection<V>>>;
205
+ type RecipeMap<V extends VariantMap = VariantMap> = Readonly<StringMap<VariantSelection<V>>>;
200
206
 
201
207
  type RecipeTarget<TVariants extends VariantMap = VariantMap> = VariantSelection<TVariants>;
202
208
 
203
- 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> {
209
+ /**
210
+ * The framework-neutral descriptor for a single praxis-kit component's contract — every render
211
+ * mechanism (tag resolution, prop merging, classes, ARIA) and every framework adapter's
212
+ * component type is built from this one shape. Deliberately just data: five type parameters and
213
+ * their corresponding properties, with no notion of JSX, call signatures, refs, or any
214
+ * framework-specific rendering concern. Each adapter (React, Vue, Svelte, Solid, Lit, Web) builds
215
+ * its own idiomatic component type on top of a `PolymorphicGenerics<...>` instantiation — see
216
+ * `PolymorphicComponent<G>` (`adapters/react/src/shared/types/polymorphic-props.ts`) for the
217
+ * React example — rather than this interface knowing anything about any of them.
218
+ *
219
+ * Use the `*Of<T>` accessor aliases below (`DefaultOf<G>`, `PropsOf<G>`, etc.) to read a single
220
+ * field back out of an already-resolved `G`, instead of indexing `G['default']` etc. directly at
221
+ * call sites — same rationale as any accessor: the property name stays an implementation detail,
222
+ * and every reader benefits together if it ever needs to change.
223
+ */
224
+ interface PolymorphicGenerics<
225
+ /**
226
+ * The element/tag this component renders as when the consumer doesn't override it via `as`
227
+ * (`AllowedOf<G>` permitting) — e.g. `'button'`, `'div'`. Defaults to the widest `ElementType`
228
+ * so a generic `PolymorphicGenerics` reference (with nothing else specified) still compiles.
229
+ */
230
+ TDefault extends ElementType = ElementType,
231
+ /**
232
+ * The props this specific component declares — its own contract, before variants are mixed
233
+ * in. Defaults to `AnyRecord` for the same "still compiles unspecified" reason as `TDefault`.
234
+ */
235
+ Props extends AnyRecord = AnyRecord,
236
+ /**
237
+ * This component's variant definitions (e.g. `{ intent: { primary: ..., ghost: ... } }`).
238
+ * Constrained to `Readonly<VariantMap>` — not the wider `AnyRecord` — specifically so `TPreset`
239
+ * below can be expressed as `RecipeMap<Variants>` and get real per-variant-key checking,
240
+ * instead of falling back to an unconstrained `RecipeMap<VariantMap>`.
241
+ */
242
+ Variants extends Readonly<VariantMap> = Readonly<VariantMap>,
243
+ /**
244
+ * Named presets (`RecipeMap<Variants>`) — bundles of variant selections a consumer activates
245
+ * by key instead of repeating the same variant combination at every call site. Tied to
246
+ * `Variants`, not `AnyRecord`, precisely so a preset can only ever select keys/values that
247
+ * `Variants` actually defines — an invalid preset is a type error, not a silent no-op.
248
+ * Defaults to `Readonly<EmptyRecord>` (no presets), which is the common case: a component can
249
+ * have variants without necessarily defining any named presets over them, and most don't.
250
+ */
251
+ TPreset extends RecipeMap<Variants> = Readonly<EmptyRecord>,
252
+ /**
253
+ * The set of elements/tags a consumer is allowed to switch to via `as`. Defaults to the widest
254
+ * `ElementType`, under which `AllowedOf<G>` imposes no restriction at all (see
255
+ * `PolymorphicControlProps.as`'s own comment in the React adapter for the concrete effect this
256
+ * has at a component's actual call site).
257
+ */
258
+ TAllowed extends ElementType = ElementType> {
204
259
  default: TDefault;
205
260
  props: Props;
206
261
  variants: Variants;
207
262
  preset: TPreset;
208
263
  allowed: TAllowed;
209
264
  }
265
+ /** This component's variant definitions. See `PolymorphicGenerics`'s `Variants` parameter. */
266
+ type VariantsOf<T extends PolymorphicGenerics> = T['variants'];
267
+ /** This component's named presets. See `PolymorphicGenerics`'s `TPreset` parameter. */
268
+ type RecipeOf<T extends PolymorphicGenerics> = T['preset'];
269
+ /** The set of elements/tags this component may render as via `as`. See `PolymorphicGenerics`'s
270
+ * `TAllowed` parameter. */
210
271
  type AllowedOf<T extends PolymorphicGenerics> = T['allowed'];
272
+ /** The element/tag this component renders as by default. See `PolymorphicGenerics`'s `TDefault`
273
+ * parameter. */
211
274
  type DefaultOf<T extends PolymorphicGenerics> = T['default'];
275
+ /** This component's own declared props, before variants are mixed in. See `PolymorphicGenerics`'s
276
+ * `Props` parameter. */
212
277
  type PropsOf<T extends PolymorphicGenerics> = T['props'];
213
278
 
214
279
  type RequireAtLeastOneIfNotEmpty<T> = keyof T extends never ? EmptyRecord : RequireAtLeastOne<T>;
@@ -241,7 +306,7 @@ interface BaseClassOptions {
241
306
  type ClassPipelineFn = (tag: unknown, props: AnyRecord, className?: ClassName, recipe?: string) => string | undefined;
242
307
 
243
308
  interface RecipeOptions<TVariants extends VariantMap = VariantMap> {
244
- recipeMap?: Record<string, RecipeTarget<TVariants>>;
309
+ recipeMap?: StringMap<RecipeTarget<TVariants>>;
245
310
  }
246
311
 
247
312
  interface TagMapOptions {
@@ -411,7 +476,7 @@ type StylingOptions<V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPre
411
476
  * combination, skipping runtime class computation entirely when a match is found. Normally
412
477
  * generated by a build-time class-extraction plugin rather than hand-authored.
413
478
  */
414
- readonly precomputedClasses?: Readonly<Record<string, string>>;
479
+ readonly precomputedClasses?: Readonly<StringMap<string>>;
415
480
  };
416
481
 
417
482
  type NormalizeFn<Props extends AnyRecord = AnyRecord> = {
@@ -496,7 +561,7 @@ type ResolvedFactoryOptions<TDefault extends ElementType = ElementType, Props ex
496
561
  readonly allowText?: boolean;
497
562
  readonly ariaRules?: readonly AriaRule[];
498
563
  readonly allowedAs?: readonly ElementType[];
499
- readonly precomputedClasses?: Readonly<Record<string, string>>;
564
+ readonly precomputedClasses?: Readonly<StringMap<string>>;
500
565
  };
501
566
 
502
567
  type ResolveAriaFn = <P extends IntrinsicProps>(tag: ElementType, props: P) => {
@@ -514,7 +579,7 @@ type RuntimePluginField<TPlugin extends ClassPlugin | undefined> = TPlugin exten
514
579
  readonly hasStyling: true;
515
580
  } : EmptyRecord;
516
581
 
517
- type PolymorphicRuntime<TDefault extends ElementType, Props extends AnyRecord, Variants extends VariantMap, TSlot extends string = never, TPreset extends RecipeMap<Variants> = Readonly<Record<string, VariantSelection<Variants>>>, TPlugin extends ClassPlugin | undefined = ClassPlugin | undefined> = RuntimePluginField<TPlugin> & {
582
+ type PolymorphicRuntime<TDefault extends ElementType, Props extends AnyRecord, Variants extends VariantMap, TSlot extends string = never, TPreset extends RecipeMap<Variants> = Readonly<StringMap<VariantSelection<Variants>>>, TPlugin extends ClassPlugin | undefined = ClassPlugin | undefined> = RuntimePluginField<TPlugin> & {
518
583
  readonly options: Readonly<ResolvedFactoryOptions<TDefault, Props, Variants, TPreset>>;
519
584
  readonly resolveTag: ResolveTagFn<TDefault>;
520
585
  readonly resolveProps: ResolvePropsFn<Props>;
@@ -554,7 +619,7 @@ declare class ChildrenEvaluator extends InvariantBase {
554
619
  evaluate(children: unknown[], context?: ChildRuleContext): void;
555
620
  }
556
621
 
557
- declare function createPolymorphic2<TDefault extends ElementType, Props extends AnyRecord, Variants extends Readonly<VariantMap>, TPreset extends RecipeMap<Variants> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory>(options?: FactoryOptions<TDefault, Props, Variants, TPreset, TPlugin>): PolymorphicRuntime<TDefault, Props, Variants, Extract<keyof TPreset, string>, TPreset, PluginInstance<TPlugin>>;
622
+ declare function createPolymorphic<TDefault extends ElementType, Props extends AnyRecord, Variants extends Readonly<VariantMap>, TPreset extends RecipeMap<Variants> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory>(options?: FactoryOptions<TDefault, Props, Variants, TPreset, TPlugin>): PolymorphicRuntime<TDefault, Props, Variants, Extract<keyof TPreset, string>, TPreset, PluginInstance<TPlugin>>;
558
623
 
559
624
  /**
560
625
  * Matches option types that declare child enforcement rules.
@@ -618,6 +683,10 @@ declare class SlotValidator extends InvariantBase {
618
683
  declare function defineContractComponent<O extends FactoryOptions>(options: O): <R>(factory: (options: O) => R) => R;
619
684
 
620
685
  type UnknownProps = AnyRecord;
686
+ type ResolvedProps = Readonly<UnknownProps>;
687
+ type ResolvedAttributes = AnyRecord;
688
+ type StyleValue = string | number;
689
+ type StyleObject = Partial<StringMap<StyleValue | null | undefined>>;
621
690
 
622
691
  type SvelteFactoryOptions<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> & {
623
692
  /**
@@ -627,7 +696,25 @@ type SvelteFactoryOptions<TDefault extends ElementType, Props extends UnknownPro
627
696
  filterProps?: (key: string, variantKeys: ReadonlySet<string>) => boolean;
628
697
  };
629
698
 
630
- type TypedRuntime<G extends PolymorphicGenerics> = ReturnType<typeof createPolymorphic2<DefaultOf<G>, PropsOf<G>, VariantsOf<G>, RecipeOf<G>>>;
699
+ type RuntimeOptions = Readonly<Pick<ResolvedFactoryOptions, 'displayName' | 'diagnostics' | 'variantKeys' | 'childRules' | 'allowedAs' | 'normalizeFn' | 'htmlPropNormalizersFn' | 'htmlChildrenEvaluatorFn'>>;
700
+ type TagResolver = Readonly<{
701
+ resolveTag(as?: ElementType): ElementType;
702
+ }>;
703
+ type PropsResolver = Readonly<{
704
+ resolveProps(props: UnknownProps): ResolvedProps;
705
+ }>;
706
+ type ClassResolver = Readonly<{
707
+ resolveClasses(tag: ElementType, props: ResolvedProps, className?: ClassName, recipe?: string): string | undefined;
708
+ }>;
709
+ type AriaResolver = Readonly<{
710
+ resolveAria<P extends IntrinsicProps>(tag: ElementType, props: P): {
711
+ props: P;
712
+ };
713
+ }>;
714
+ type Runtime = Readonly<TagResolver & PropsResolver & ClassResolver & AriaResolver & {
715
+ options: RuntimeOptions;
716
+ }>;
717
+ type TypedRuntime<G extends PolymorphicGenerics> = ReturnType<typeof createPolymorphic<DefaultOf<G>, PropsOf<G>, VariantsOf<G>, RecipeOf<G>>>;
631
718
 
632
719
  type OnElementFn<G extends PolymorphicGenerics = PolymorphicGenerics> = (element: ElementForTag<DefaultOf<G> | AllowedOf<G>>, getProps: () => Readonly<PropsOf<G>>) => void | (() => void);
633
720
  type BuiltRuntime<G extends PolymorphicGenerics = PolymorphicGenerics, TOptions extends WithChildRules = WithChildRules> = BuiltChildrenEvaluator<TOptions> & {
@@ -636,6 +723,105 @@ type BuiltRuntime<G extends PolymorphicGenerics = PolymorphicGenerics, TOptions
636
723
  slotValidator: SlotValidator;
637
724
  onElement?: OnElementFn<G>;
638
725
  };
726
+ type AnyBuiltRuntime = {
727
+ runtime: Runtime;
728
+ filterProps: FilterPredicate;
729
+ slotValidator: SlotValidator;
730
+ childrenEvaluator?: ChildrenEvaluator;
731
+ onElement?: OnElementFn;
732
+ };
733
+
734
+ type AsProp = Readonly<{
735
+ as?: string;
736
+ }>;
737
+ type AsChildProp = Readonly<{
738
+ asChild?: boolean;
739
+ }>;
740
+ type PolymorphicPropsBase = Readonly<Simplify<{
741
+ children?: unknown;
742
+ class?: ClassName;
743
+ recipe?: string;
744
+ } & AsProp & AsChildProp>>;
745
+ type KnownProps = Readonly<PolymorphicPropsBase & UnknownProps>;
746
+ /**
747
+ * Props accepted by `<Polymorphic>`, the component every `createContractComponent` bundle
748
+ * renders through in the Svelte adapter — distinct from `KnownProps` above, which describes
749
+ * the abstract polymorphic-prop contract rather than this specific component's props (it also
750
+ * carries the runtime bundle).
751
+ */
752
+ interface PolymorphicComponentProps {
753
+ /** The contract bundle to render, from `createContractComponent`. */
754
+ bundle: AnyBuiltRuntime;
755
+ /** Overrides the default tag. String-only: `<svelte:element>` only accepts string tags. */
756
+ as?: string;
757
+ /** Renders `children` as a snippet receiving the resolved props, instead of the host element. */
758
+ asChild?: boolean;
759
+ /** Caller class, merged with the resolved variant classes. */
760
+ class?: string;
761
+ /** Selects a named preset from `styling.presets`. */
762
+ recipe?: string;
763
+ children?: Snippet | Snippet<[UnknownProps]>;
764
+ [key: string]: unknown;
765
+ }
766
+
767
+ type NormalizedOptions<G extends PolymorphicGenerics> = SvelteFactoryOptions<DefaultOf<G>, PropsOf<G>, VariantsOf<G>, RecipeOf<G>> & {
768
+ readonly name: string;
769
+ readonly diagnostics: Diagnostics;
770
+ };
771
+
772
+ /**
773
+ * Recovers a bundle's `PolymorphicGenerics` descriptor from its own value type — the Svelte
774
+ * analog of React's/Preact's `__generics` marker recovery (`@praxis-kit/contract-props`), but
775
+ * needs no marker at all: `createContractComponent` already returns `BuiltRuntime<G, TOptions>`
776
+ * directly (not an erased type), so `G` is a plain, ordinary type parameter to `infer` back out.
777
+ * The second type argument is fixed to `WithChildRules` (its own upper bound) rather than
778
+ * `infer`'d, since nothing here needs `TOptions` itself, only `G`. Falls back to the widest
779
+ * `PolymorphicGenerics` for any non-praxis-kit bundle, the same "no marker, nothing to recover"
780
+ * case `HasGenerics<G>`'s own `never` branch covers for React/Preact — a caller annotation, not an
781
+ * internal assertion, so a mismatched bundle should degrade gracefully rather than poison the
782
+ * whole expression with `never`.
783
+ */
784
+ type GenericsOf<T extends AnyBuiltRuntime> = T extends BuiltRuntime<infer G, WithChildRules> ? G : PolymorphicGenerics;
785
+ /**
786
+ * The exact props object an `asChild` snippet receives at runtime, once defaults, variant
787
+ * classes, and ARIA role resolution have all run — see `buildSlotProps` in `Polymorphic.svelte`
788
+ * (whose own doc comment covers the runtime-only asymmetries with the non-asChild path, such as
789
+ * skipped event-key normalization, that don't change this type's contract). `<Polymorphic>` itself
790
+ * can't be typed against this directly (its own `children` prop must stay `Snippet<[UnknownProps]>`,
791
+ * erased, since one `.svelte` file/`.d.ts` serves every bundle's `G`) — this type exists for a
792
+ * caller to annotate their own snippet parameter with instead:
793
+ *
794
+ * ```svelte
795
+ * <Polymorphic bundle={buttonBundle} asChild>
796
+ * {#snippet children(props: ResolvedSlotProps<GenericsOf<typeof buttonBundle>>)}
797
+ * <a {...props} href="/foo">Go</a>
798
+ * {/snippet}
799
+ * </Polymorphic>
800
+ * ```
801
+ *
802
+ * `PropsOf<G>` stays `Partial` for the same reason it does in every other adapter's asChild-mode
803
+ * type: the type system can't prove every prop actually received a default, only that the runtime
804
+ * *tried*. `VariantProps<VariantsOf<G>>` needs no extra `Partial` wrapper — it's already fully
805
+ * optional at its own definition (`{ [K in keyof V]?: ... }`). `class` is narrowed to a plain
806
+ * resolved `string`.
807
+ *
808
+ * Three fields other adapters' `ResolvedSlotProps<G>` carry, or a caller might expect, are
809
+ * deliberately absent here rather than given an explicit (and unsafe) type — omitting a key
810
+ * entirely keeps `{...props}` spreads onto a real element honest and compiling; reading one of
811
+ * these off the object directly needs an explicit, locally-scoped cast instead:
812
+ * - `ref` — Svelte has no `ref` prop concept (DOM access is via `bind:this`/`onElement`); a caller
813
+ * who writes a literal `ref` key gets it forwarded like any other unknown prop, untyped.
814
+ * - `role` — same reasoning Solid's `ResolvedSlotProps<G>` documents: every candidate type is
815
+ * either too wide to spread onto an unknown target element's own narrower per-element `role`
816
+ * union, or (`unknown`) fails that same assignability check outright, the same way `unknown`
817
+ * would for `style` below.
818
+ * - `style` — unlike the non-asChild `<svelte:element>` path (`buildDomProps`), the asChild path
819
+ * skips `style`-object serialization entirely: a caller's `style` prop passes through however
820
+ * they wrote it (object or string), so there's no one shape to assert here either.
821
+ */
822
+ type ResolvedSlotProps<G extends PolymorphicGenerics> = Partial<OmitIndexSignature<PropsOf<G>>> & OmitIndexSignature<VariantProps<VariantsOf<G>>> & {
823
+ class?: string | undefined;
824
+ };
639
825
 
640
826
  /**
641
827
  * Creates a praxis-kit contract bundle for use with Svelte's `<Polymorphic>` component.
@@ -675,4 +861,4 @@ declare function createContractComponent<TDefault extends ElementType, Props ext
675
861
  readonly subComponents?: TSubComponents;
676
862
  }): MergeRecords<BuiltRuntime<PolymorphicGenerics<TDefault, MergeRecords<Props, ExtractPluginProps<TPlugin>>, Variants, TPreset>, TOptions>, TSubComponents>;
677
863
 
678
- export { type AnyFactoryOptions, type BuiltRuntime, type ElementType, type EmptyRecord, type FilterPredicate, type PolymorphicGenerics, type SvelteFactoryOptions, type UnknownProps, type WithChildRules, createContractComponent, defineContractComponent };
864
+ export { type AnyBuiltRuntime, type AnyFactoryOptions, type AsProp, type BuiltChildrenEvaluator, type BuiltRuntime, type ClassResolver, type ElementType, type EmptyRecord, type FilterPredicate, type GenericsOf, type KnownProps, type NormalizedOptions, type PolymorphicComponentProps, type PolymorphicGenerics, type PolymorphicPropsBase, type PropsResolver, type ResolvedAttributes, type ResolvedProps, type ResolvedSlotProps, type Runtime, type RuntimeOptions, type StyleObject, type StyleValue, type SvelteFactoryOptions, type TagResolver, type TypedRuntime, type UnknownProps, type WithChildRules, createContractComponent, defineContractComponent };