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.
@@ -99,7 +99,7 @@ interface BaseClassOptions {
99
99
  type ClassPipelineFn = (tag: unknown, props: AnyRecord, className?: ClassName, recipe?: string) => string | undefined;
100
100
 
101
101
  interface RecipeOptions<TVariants extends VariantMap = VariantMap> {
102
- recipeMap?: Record<string, RecipeTarget<TVariants>>;
102
+ recipeMap?: StringMap<RecipeTarget<TVariants>>;
103
103
  }
104
104
 
105
105
  interface TagMapOptions {
@@ -75,10 +75,13 @@ declare enum DiagnosticCode {
75
75
  HtmlInputAltIgnoredForType = "HTML3113",
76
76
  HtmlInputHeightIgnoredForType = "HTML3114",
77
77
  HtmlInputWidthIgnoredForType = "HTML3115",
78
+ HtmlAnchorDangerousHref = "HTML3201",
78
79
  A11yInputMissingAccessibleName = "A11Y8100",
79
80
  A11yInputPlaceholderNotLabel = "A11Y8101",
80
81
  A11yInputPasswordAutocomplete = "A11Y8102",
81
82
  A11yInputRequiredReadOnlyConflict = "A11Y8103",
83
+ A11yAnchorRoleButtonWithHref = "A11Y8200",
84
+ A11yAnchorAriaDisabledInert = "A11Y8201",
82
85
  InvalidRenderingTarget = "RENDER4001",
83
86
  LintDeadCompoundKey = "LINT5001",
84
87
  LintDeadCompoundValue = "LINT5002",
@@ -294,7 +297,7 @@ declare function analyze(code: string, filename: string, options?: PluginOptions
294
297
  * Returns null when static extraction is not possible (non-literal values,
295
298
  * no variants, or combination count exceeds MAX_COMBINATIONS).
296
299
  */
297
- declare function buildPrecomputedClasses(stylingObj: ts.ObjectLiteralExpression): Record<string, string> | null;
300
+ declare function buildPrecomputedClasses(stylingObj: ts.ObjectLiteralExpression): StringMap<string> | null;
298
301
  /**
299
302
  * Injects precomputed variant class maps into all factory calls in the given
300
303
  * source file that have fully-static `styling.variants` configurations.
@@ -334,7 +337,7 @@ type ComponentTokens = {
334
337
  tagClasses: string[];
335
338
  };
336
339
  type DesignTokenManifest = {
337
- components: Record<string, ComponentTokens>;
340
+ components: StringMap<ComponentTokens>;
338
341
  allClasses: string[];
339
342
  };
340
343
  /**
@@ -440,7 +443,7 @@ declare function transformAsChild(source: ts.SourceFile): string | null;
440
443
  type StaticComponent = {
441
444
  readonly defaultTag: string;
442
445
  readonly variantKeys: ReadonlySet<string>;
443
- readonly precomputedClasses: Readonly<Record<string, string>>;
446
+ readonly precomputedClasses: Readonly<StringMap<string>>;
444
447
  };
445
448
  /**
446
449
  * Walks the source file and extracts metadata for each same-file factory call
@@ -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];
@@ -177,20 +190,75 @@ type DefaultVariants<V extends VariantMap> = {
177
190
  * Presets are named bundles of variant props that callers activate by key,
178
191
  * avoiding the need to repeat variant combinations at each call site.
179
192
  */
180
- type RecipeMap<V extends VariantMap = VariantMap> = Readonly<Record<string, VariantSelection<V>>>;
193
+ type RecipeMap<V extends VariantMap = VariantMap> = Readonly<StringMap<VariantSelection<V>>>;
181
194
 
182
195
  type RecipeTarget<TVariants extends VariantMap = VariantMap> = VariantSelection<TVariants>;
183
196
 
184
- 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> {
185
247
  default: TDefault;
186
248
  props: Props;
187
249
  variants: Variants;
188
250
  preset: TPreset;
189
251
  allowed: TAllowed;
190
252
  }
253
+ /** This component's variant definitions. See `PolymorphicGenerics`'s `Variants` parameter. */
191
254
  type VariantsOf<T extends PolymorphicGenerics> = T['variants'];
255
+ /** This component's named presets. See `PolymorphicGenerics`'s `TPreset` parameter. */
192
256
  type RecipeOf<T extends PolymorphicGenerics> = T['preset'];
257
+ /** The element/tag this component renders as by default. See `PolymorphicGenerics`'s `TDefault`
258
+ * parameter. */
193
259
  type DefaultOf<T extends PolymorphicGenerics> = T['default'];
260
+ /** This component's own declared props, before variants are mixed in. See `PolymorphicGenerics`'s
261
+ * `Props` parameter. */
194
262
  type PropsOf<T extends PolymorphicGenerics> = T['props'];
195
263
 
196
264
  type RequireAtLeastOneIfNotEmpty<T> = keyof T extends never ? EmptyRecord : RequireAtLeastOne<T>;
@@ -223,7 +291,7 @@ interface BaseClassOptions {
223
291
  type ClassPipelineFn = (tag: unknown, props: AnyRecord, className?: ClassName, recipe?: string) => string | undefined;
224
292
 
225
293
  interface RecipeOptions<TVariants extends VariantMap = VariantMap> {
226
- recipeMap?: Record<string, RecipeTarget<TVariants>>;
294
+ recipeMap?: StringMap<RecipeTarget<TVariants>>;
227
295
  }
228
296
 
229
297
  interface TagMapOptions {
@@ -392,7 +460,7 @@ type StylingOptions<V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPre
392
460
  * combination, skipping runtime class computation entirely when a match is found. Normally
393
461
  * generated by a build-time class-extraction plugin rather than hand-authored.
394
462
  */
395
- readonly precomputedClasses?: Readonly<Record<string, string>>;
463
+ readonly precomputedClasses?: Readonly<StringMap<string>>;
396
464
  };
397
465
 
398
466
  type NormalizeFn<Props extends AnyRecord = AnyRecord> = {
@@ -438,13 +506,23 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
438
506
  * no prop-based equivalent), not for anything expressible as a plain
439
507
  * prop.
440
508
  *
509
+ * `element` is typed to the real DOM interface of every tag the rendered
510
+ * element could actually be — `TDefault` plus whatever `enforcement.allowed`
511
+ * permits via `as` (`HTMLDialogElement` for `tag: 'dialog'`,
512
+ * `HTMLDetailsElement` for `tag: 'details'`, and so on) — no cast needed to
513
+ * reach tag-specific members. A component that leaves `allowed`
514
+ * unconstrained (any tag reachable via `as`) falls back to `HTMLElement`,
515
+ * which still covers members every element shares (`showPopover()` and
516
+ * friends); restrict `enforcement.allowed` to the tags `onElement`
517
+ * actually knows how to handle to get real narrowing.
518
+ *
441
519
  * `getProps` returns the instance's *current* resolved props at call
442
520
  * time — read it from inside a listener registered once at mount, rather
443
521
  * than re-subscribing on every prop change.
444
522
  *
445
523
  * Return a cleanup function to run when the instance unmounts.
446
524
  */
447
- readonly onElement?: (element: Element, getProps: () => Readonly<Props>) => void | (() => void);
525
+ readonly onElement?: (element: ElementForTag<TDefault | TAllowed>, getProps: () => Readonly<Props>) => void | (() => void);
448
526
  };
449
527
 
450
528
  declare function defineContractComponent<O extends FactoryOptions>(options: O): <R>(factory: (options: O) => R) => R;
@@ -507,6 +585,16 @@ type PolymorphicComponent<G extends PolymorphicGenerics> = {
507
585
  };
508
586
  displayName?: string;
509
587
  };
588
+ /**
589
+ * A component's full prop contract, both render modes at once — naming symmetry with React's/
590
+ * Preact's `ContractProps<T, Mode>` (`@praxis-kit/contract-props`), not a fix for a gap: Vue has
591
+ * no version of the overload-resolution ceiling those two adapters need a marker to work around.
592
+ * `PolymorphicComponent<G>`'s single `new()` construct signature already exposes both modes
593
+ * unioned together (`$props: PolymorphicProps<G> | PolymorphicWithAsChild<G>`), so this alias is
594
+ * just that same union under a familiar name — no phantom marker involved, `G` is already an
595
+ * ordinary, ambient type parameter.
596
+ */
597
+ type ContractProps<G extends PolymorphicGenerics> = PolymorphicProps<G> | PolymorphicWithAsChild<G>;
510
598
 
511
599
  /**
512
600
  * Creates a polymorphic Vue component with praxis-kit contracts applied.
@@ -535,4 +623,4 @@ declare function createContractComponent<TDefault extends ElementType, Props ext
535
623
  readonly subComponents?: TSubComponents;
536
624
  }): MergeRecords<PolymorphicComponent<PolymorphicGenerics<TDefault, MergeRecords<Props, ExtractPluginProps<TPlugin>>, Variants, TPreset>>, TSubComponents>;
537
625
 
538
- export { type AnyFactoryOptions, type ElementType, type EmptyRecord, type PolymorphicComponent, type PolymorphicGenerics, type PolymorphicProps, type PolymorphicWithAsChild, Slottable, type SlottableProps, type VueFactoryOptions, createContractComponent, defineContractComponent };
626
+ export { type AnyFactoryOptions, type ContractProps, type ElementType, type EmptyRecord, type PolymorphicComponent, type PolymorphicGenerics, type PolymorphicProps, type PolymorphicWithAsChild, Slottable, type SlottableProps, type VueFactoryOptions, createContractComponent, defineContractComponent };