praxis-kit 7.0.0 → 7.4.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.
- package/dist/_shared/diagnostics.d.ts +6 -0
- package/dist/{chunk-35CJAOJW.js → chunk-EIKPSL26.js} +63 -6
- package/dist/codemod/index.js +121 -72
- package/dist/contract/index.d.ts +92 -0
- package/dist/guards/index.d.ts +6 -0
- package/dist/guards/index.js +4 -4
- package/dist/html/index.d.ts +6 -0
- package/dist/lit/index.d.ts +158 -6
- package/dist/lit/index.js +83 -9
- package/dist/preact/index.d.ts +164 -3
- package/dist/preact/index.js +104 -31
- package/dist/react/index.d.ts +26 -3
- package/dist/react/index.js +34 -8
- package/dist/react/legacy.d.ts +25 -3
- package/dist/react/legacy.js +17 -3
- package/dist/{react-options-Cm99IE5J.d.ts → react-options-BnjZpdVh.d.ts} +141 -3
- package/dist/solid/index.d.ts +163 -3
- package/dist/solid/index.js +100 -17
- package/dist/svelte/Polymorphic.svelte +25 -0
- package/dist/svelte/index.d.ts +231 -13
- package/dist/svelte/index.js +16 -8
- package/dist/tailwind/index.d.ts +12 -0
- package/dist/ts-plugin/index.cjs +5 -2
- package/dist/vite-plugin/index.d.ts +6 -0
- package/dist/vue/index.d.ts +166 -3
- package/dist/vue/index.js +119 -37
- package/dist/web/index.d.ts +132 -4
- package/dist/web/index.js +94 -13
- package/package.json +4 -4
|
@@ -2,9 +2,72 @@ import { Ref, PropsWithChildren, ReactElement, ComponentType, JSX, ReactNode } f
|
|
|
2
2
|
import { RequireAtLeastOne, Simplify, ReadonlyDeep, NonEmptyTuple } from 'type-fest';
|
|
3
3
|
import { Diagnostics, DiagnosticInput, DiagnosticsMode } from './_shared/diagnostics.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* A string-keyed object whose values are of type `T`.
|
|
7
|
+
*/
|
|
5
8
|
type StringMap<T = unknown> = Record<string, T>;
|
|
9
|
+
/**
|
|
10
|
+
* A string-keyed object with values of unknown type.
|
|
11
|
+
*/
|
|
6
12
|
type AnyRecord = StringMap<unknown>;
|
|
13
|
+
/**
|
|
14
|
+
* An object type with no named properties.
|
|
15
|
+
*
|
|
16
|
+
* Unlike `{}`, this excludes arbitrary properties during type operations while
|
|
17
|
+
* still satisfying `extends object`.
|
|
18
|
+
*/
|
|
7
19
|
type EmptyRecord = Record<never, never>;
|
|
20
|
+
/**
|
|
21
|
+
* A compound component's named sub-components, for example
|
|
22
|
+
* `{ Header, Content, Footer }`.
|
|
23
|
+
*/
|
|
24
|
+
type SubComponentMap = Readonly<AnyRecord>;
|
|
25
|
+
/**
|
|
26
|
+
* Default `Variants` type for components that declare no variants.
|
|
27
|
+
*
|
|
28
|
+
* Structurally identical to `Readonly<EmptyRecord>`, but named separately so
|
|
29
|
+
* editor hovers remain self-descriptive.
|
|
30
|
+
*/
|
|
31
|
+
type NoVariants = Readonly<EmptyRecord>;
|
|
32
|
+
/**
|
|
33
|
+
* Default `TPreset` type for components that declare no named presets.
|
|
34
|
+
*
|
|
35
|
+
* Structurally identical to `Readonly<EmptyRecord>`, but named separately so
|
|
36
|
+
* editor hovers remain self-descriptive.
|
|
37
|
+
*/
|
|
38
|
+
type NoPreset = Readonly<EmptyRecord>;
|
|
39
|
+
/**
|
|
40
|
+
* Fallback for `ExtractPluginProps<TPlugin>` when a plugin contributes no
|
|
41
|
+
* props, including the no-plugin case.
|
|
42
|
+
*
|
|
43
|
+
* Structurally identical to `EmptyRecord`, but named separately so editor
|
|
44
|
+
* hovers remain self-descriptive.
|
|
45
|
+
*/
|
|
46
|
+
type NoPluginProps = EmptyRecord;
|
|
47
|
+
/**
|
|
48
|
+
* Determines whether an object type should be treated as empty.
|
|
49
|
+
*
|
|
50
|
+
* `keyof T` ignores call and construct signatures...
|
|
51
|
+
*/
|
|
52
|
+
type IsEmptyRecord<T extends object> = T extends (...args: never[]) => unknown ? false : T extends new (...args: never[]) => unknown ? false : keyof T extends never ? true : false;
|
|
53
|
+
/**
|
|
54
|
+
* Merges two object types while eliding empty operands.
|
|
55
|
+
*
|
|
56
|
+
* If either operand is {@link EmptyRecord}, the other operand is returned
|
|
57
|
+
* directly instead of producing intersections such as
|
|
58
|
+
* `Component & EmptyRecord` in editor hovers.
|
|
59
|
+
*
|
|
60
|
+
* Unlike a homomorphic mapped type (for example `Simplify<T>`), this preserves
|
|
61
|
+
* call and construct signatures. Many component types are callable objects,
|
|
62
|
+
* and mapped types silently discard those signatures.
|
|
63
|
+
*
|
|
64
|
+
* @remarks
|
|
65
|
+
* Instantiate `MergeRecords` directly. Introducing an intermediate alias for
|
|
66
|
+
* one operand (for example `type C = PolymorphicComponent<G>`) can prevent
|
|
67
|
+
* `IsEmptyRecord` from evaluating eagerly, which breaks assignability under
|
|
68
|
+
* `exactOptionalPropertyTypes`.
|
|
69
|
+
*/
|
|
70
|
+
type MergeRecords<A extends object, B extends object> = IsEmptyRecord<A> extends true ? B : IsEmptyRecord<B> extends true ? A : A & B;
|
|
8
71
|
|
|
9
72
|
type IntrinsicTag = keyof HTMLElementTagNameMap;
|
|
10
73
|
|
|
@@ -188,7 +251,7 @@ type ClassPluginFactory<TProps extends AnyRecord = EmptyRecord> = <V extends Var
|
|
|
188
251
|
* wherever a factory's concrete plugin-props shape isn't tracked (factory generics,
|
|
189
252
|
* capability wiring). */
|
|
190
253
|
type AnyClassPluginFactory = ClassPluginFactory<AnyRecord> | undefined;
|
|
191
|
-
type ExtractPluginProps<TPlugin extends AnyClassPluginFactory> = TPlugin extends ClassPluginFactory<infer T> ? string extends keyof T ?
|
|
254
|
+
type ExtractPluginProps<TPlugin extends AnyClassPluginFactory> = TPlugin extends ClassPluginFactory<infer T> ? string extends keyof T ? NoPluginProps : T : NoPluginProps;
|
|
192
255
|
|
|
193
256
|
type AriaContext = {
|
|
194
257
|
readonly tag: IntrinsicTag;
|
|
@@ -252,6 +315,12 @@ type EnforcementOptions<TAllowed extends ElementType = ElementType> = {
|
|
|
252
315
|
* `@praxis-kit/diagnostics`.
|
|
253
316
|
*/
|
|
254
317
|
readonly diagnostics?: Diagnostics | DiagnosticsMode;
|
|
318
|
+
/**
|
|
319
|
+
* ARIA/accessibility rules evaluated against the resolved tag and props on every render.
|
|
320
|
+
* Each rule is a function receiving the current context and returning zero or more
|
|
321
|
+
* violations, some of which can carry an auto-applicable fix (see `createRemoveAttributeRule`
|
|
322
|
+
* and friends in `praxis-kit/contract`).
|
|
323
|
+
*/
|
|
255
324
|
readonly aria?: readonly AriaRule[];
|
|
256
325
|
/**
|
|
257
326
|
* Rules that need `AriaPolicyEngine`'s fix-application/caching machinery
|
|
@@ -263,6 +332,11 @@ type EnforcementOptions<TAllowed extends ElementType = ElementType> = {
|
|
|
263
332
|
* misleading `aria` name to get the machinery it needs.
|
|
264
333
|
*/
|
|
265
334
|
readonly rules?: readonly AriaRule[];
|
|
335
|
+
/**
|
|
336
|
+
* Declares which children are valid, by name, match predicate, and cardinality (e.g. "at
|
|
337
|
+
* least 1, at most 4 `Button` children"). Open by default — children matching no rule are
|
|
338
|
+
* still allowed unless `exclusiveChildren` is set.
|
|
339
|
+
*/
|
|
266
340
|
readonly children?: readonly ChildRuleInput[];
|
|
267
341
|
/**
|
|
268
342
|
* When true, only children matching a `children` rule (or text, per `allowText`)
|
|
@@ -275,19 +349,49 @@ type EnforcementOptions<TAllowed extends ElementType = ElementType> = {
|
|
|
275
349
|
* or any listed rule. Default: true.
|
|
276
350
|
*/
|
|
277
351
|
readonly allowText?: boolean;
|
|
352
|
+
/**
|
|
353
|
+
* Prop transforms composed with the component's own `normalize` (from `FactoryOptions`) and
|
|
354
|
+
* run before it. Unlike `normalize`, these live in the enforcement bucket because they
|
|
355
|
+
* typically encode a built-in HTML/ARIA fact rather than component-specific behavior.
|
|
356
|
+
*/
|
|
278
357
|
readonly props?: readonly PropNormalizer[];
|
|
279
358
|
/** Restricts the `as` prop to this set of tags. Violations route through diagnostics. */
|
|
280
359
|
readonly allowedAs?: readonly TAllowed[];
|
|
281
360
|
};
|
|
282
361
|
|
|
283
362
|
type StylingOptions<V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<V> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory> = {
|
|
363
|
+
/** Class applied to every instance regardless of variant selection. */
|
|
284
364
|
readonly base?: ClassName;
|
|
365
|
+
/**
|
|
366
|
+
* Named variant groups (e.g. `intent`, `size`), each mapping its possible values to a
|
|
367
|
+
* class string. A consumer selects a value per group as a prop (`<Button intent="primary">`).
|
|
368
|
+
*/
|
|
285
369
|
readonly variants?: V;
|
|
370
|
+
/** Value used for a variant group when the consumer doesn't pass one explicitly. */
|
|
286
371
|
readonly defaults?: Partial<DefaultVariants<V>>;
|
|
372
|
+
/**
|
|
373
|
+
* Applies an extra class only when a specific *combination* of variant selections matches —
|
|
374
|
+
* for cases `variants` alone can't express (e.g. `intent: 'primary'` + `size: 'lg'` together
|
|
375
|
+
* need a class neither variant would add on its own).
|
|
376
|
+
*/
|
|
287
377
|
readonly compounds?: readonly CompoundVariant<V>[];
|
|
378
|
+
/**
|
|
379
|
+
* Named bundles of variant values, selectable as a single unit via the `recipe` prop (e.g.
|
|
380
|
+
* `<Button recipe="cta">` instead of setting `intent`/`size` individually).
|
|
381
|
+
*/
|
|
288
382
|
readonly presets?: TPreset;
|
|
383
|
+
/** Maps a resolved tag directly to a raw class string, independent of the variant system. */
|
|
289
384
|
readonly tags?: Readonly<TagMap>;
|
|
385
|
+
/**
|
|
386
|
+
* A `ClassPluginFactory` (e.g. the Tailwind layout pipeline) that extends class resolution
|
|
387
|
+
* with its own owned props, layered on top of `variants`/`presets`/`tags`.
|
|
388
|
+
*/
|
|
290
389
|
readonly plugin?: TPlugin;
|
|
390
|
+
/**
|
|
391
|
+
* A cache-key → resolved-class-string lookup for every statically-known variant
|
|
392
|
+
* combination, skipping runtime class computation entirely when a match is found. Normally
|
|
393
|
+
* generated by a build-time class-extraction plugin rather than hand-authored.
|
|
394
|
+
*/
|
|
291
395
|
readonly precomputedClasses?: Readonly<Record<string, string>>;
|
|
292
396
|
};
|
|
293
397
|
|
|
@@ -296,17 +400,51 @@ type NormalizeFn<Props extends AnyRecord = AnyRecord> = {
|
|
|
296
400
|
}['normalize'];
|
|
297
401
|
type AnyFactoryOptions = FactoryOptions<ElementType, AnyRecord, VariantMap, RecipeMap<VariantMap>, AnyClassPluginFactory>;
|
|
298
402
|
type FactoryOptions<TDefault extends ElementType = ElementType, Props extends AnyRecord = EmptyRecord, V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<V> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType> = {
|
|
403
|
+
/** The intrinsic tag the component renders by default. Overridable per instance via `as`. */
|
|
299
404
|
readonly tag?: TDefault;
|
|
405
|
+
/** Display name used in diagnostics, dev tools, and generated component naming. */
|
|
300
406
|
readonly name?: string;
|
|
407
|
+
/** Values used for the component's own (non-variant) props when the consumer omits them. */
|
|
301
408
|
readonly defaults?: Partial<NoInfer<Props>>;
|
|
409
|
+
/**
|
|
410
|
+
* A pure `(props) => props` transform run on every render, after `enforcement.props`'s
|
|
411
|
+
* normalizers see the same input. Use this for component-specific prop shaping — anything
|
|
412
|
+
* that depends on live instance state or the real DOM element belongs in `onElement` instead.
|
|
413
|
+
*/
|
|
302
414
|
readonly normalize?: NormalizeFn<NoInfer<Props>>;
|
|
415
|
+
/** Variant groups, base classes, presets, and the optional class-resolution plugin. */
|
|
303
416
|
readonly styling?: StylingOptions<V, TPreset, TPlugin>;
|
|
417
|
+
/** ARIA rules, child-content contracts, and other runtime validation for this component. */
|
|
304
418
|
readonly enforcement?: EnforcementOptions<TAllowed>;
|
|
305
419
|
/**
|
|
306
420
|
* Adapter-resolved diagnostics default, spread in by `resolveAdapterCommonOptions`. Not meant to
|
|
307
421
|
* be set directly by component authors — use `enforcement.diagnostics` to override per component.
|
|
308
422
|
*/
|
|
309
423
|
readonly diagnostics?: Diagnostics;
|
|
424
|
+
/**
|
|
425
|
+
* Sub-components to attach to the generated root component, producing a
|
|
426
|
+
* compound component API (for example, `Card.Header`, `Card.Content`,
|
|
427
|
+
* and `Card.Footer`). Purely additive — has no effect on
|
|
428
|
+
* `enforcement.children`; author child rules explicitly if the component
|
|
429
|
+
* needs to validate its children.
|
|
430
|
+
*/
|
|
431
|
+
readonly subComponents?: SubComponentMap;
|
|
432
|
+
/**
|
|
433
|
+
* Called once per instance, when the real underlying DOM element first
|
|
434
|
+
* exists, in every adapter — via that adapter's own native mount
|
|
435
|
+
* lifecycle, never through the props/attribute pipeline. Use this for
|
|
436
|
+
* wiring that needs the actual element (native imperative methods like
|
|
437
|
+
* `dialogEl.showModal()`, native events like `close`/`cancel` that have
|
|
438
|
+
* no prop-based equivalent), not for anything expressible as a plain
|
|
439
|
+
* prop.
|
|
440
|
+
*
|
|
441
|
+
* `getProps` returns the instance's *current* resolved props at call
|
|
442
|
+
* time — read it from inside a listener registered once at mount, rather
|
|
443
|
+
* than re-subscribing on every prop change.
|
|
444
|
+
*
|
|
445
|
+
* Return a cleanup function to run when the instance unmounts.
|
|
446
|
+
*/
|
|
447
|
+
readonly onElement?: (element: Element, getProps: () => Readonly<Props>) => void | (() => void);
|
|
310
448
|
};
|
|
311
449
|
|
|
312
450
|
type MetadataMap = AnyRecord;
|
|
@@ -507,7 +645,7 @@ interface CompiledArtifact {
|
|
|
507
645
|
* Extends FactoryOptions with React-specific configuration.
|
|
508
646
|
* slotComponent is intentionally not in core — it is a React rendering concern.
|
|
509
647
|
*/
|
|
510
|
-
type ReactFactoryOptions<TDefault extends ElementType, Props extends UnknownProps, Variants extends Readonly<VariantMap>, TPreset extends RecipeMap<Variants> =
|
|
648
|
+
type ReactFactoryOptions<TDefault extends ElementType, Props extends UnknownProps, Variants extends Readonly<VariantMap>, TPreset extends RecipeMap<Variants> = NoPreset, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType> = FactoryOptions<TDefault, Props, Variants, TPreset, TPlugin, TAllowed> & {
|
|
511
649
|
/** Component used to render the asChild slot. Defaults to the built-in Slot. */
|
|
512
650
|
slotComponent?: SlotComponent;
|
|
513
651
|
/**
|
|
@@ -521,4 +659,4 @@ type ReactFactoryOptions<TDefault extends ElementType, Props extends UnknownProp
|
|
|
521
659
|
artifact?: CompiledArtifact;
|
|
522
660
|
};
|
|
523
661
|
|
|
524
|
-
export { type AnyClassPluginFactory as A, type ElementType as E, type FactoryOptions as F, type PolymorphicComponent as P, type RecipeMap as R, Slottable as S, type UnknownProps as U, type VariantMap as V, type EmptyRecord as a, type
|
|
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 };
|
package/dist/solid/index.d.ts
CHANGED
|
@@ -2,9 +2,72 @@ import { RequireAtLeastOne, Simplify, ReadonlyDeep, OmitIndexSignature } from 't
|
|
|
2
2
|
import { Diagnostics, DiagnosticInput, DiagnosticsMode } from '../_shared/diagnostics.js';
|
|
3
3
|
import { JSX } from 'solid-js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* A string-keyed object whose values are of type `T`.
|
|
7
|
+
*/
|
|
5
8
|
type StringMap<T = unknown> = Record<string, T>;
|
|
9
|
+
/**
|
|
10
|
+
* A string-keyed object with values of unknown type.
|
|
11
|
+
*/
|
|
6
12
|
type AnyRecord = StringMap<unknown>;
|
|
13
|
+
/**
|
|
14
|
+
* An object type with no named properties.
|
|
15
|
+
*
|
|
16
|
+
* Unlike `{}`, this excludes arbitrary properties during type operations while
|
|
17
|
+
* still satisfying `extends object`.
|
|
18
|
+
*/
|
|
7
19
|
type EmptyRecord = Record<never, never>;
|
|
20
|
+
/**
|
|
21
|
+
* A compound component's named sub-components, for example
|
|
22
|
+
* `{ Header, Content, Footer }`.
|
|
23
|
+
*/
|
|
24
|
+
type SubComponentMap = Readonly<AnyRecord>;
|
|
25
|
+
/**
|
|
26
|
+
* Default `Variants` type for components that declare no variants.
|
|
27
|
+
*
|
|
28
|
+
* Structurally identical to `Readonly<EmptyRecord>`, but named separately so
|
|
29
|
+
* editor hovers remain self-descriptive.
|
|
30
|
+
*/
|
|
31
|
+
type NoVariants = Readonly<EmptyRecord>;
|
|
32
|
+
/**
|
|
33
|
+
* Default `TPreset` type for components that declare no named presets.
|
|
34
|
+
*
|
|
35
|
+
* Structurally identical to `Readonly<EmptyRecord>`, but named separately so
|
|
36
|
+
* editor hovers remain self-descriptive.
|
|
37
|
+
*/
|
|
38
|
+
type NoPreset = Readonly<EmptyRecord>;
|
|
39
|
+
/**
|
|
40
|
+
* Fallback for `ExtractPluginProps<TPlugin>` when a plugin contributes no
|
|
41
|
+
* props, including the no-plugin case.
|
|
42
|
+
*
|
|
43
|
+
* Structurally identical to `EmptyRecord`, but named separately so editor
|
|
44
|
+
* hovers remain self-descriptive.
|
|
45
|
+
*/
|
|
46
|
+
type NoPluginProps = EmptyRecord;
|
|
47
|
+
/**
|
|
48
|
+
* Determines whether an object type should be treated as empty.
|
|
49
|
+
*
|
|
50
|
+
* `keyof T` ignores call and construct signatures...
|
|
51
|
+
*/
|
|
52
|
+
type IsEmptyRecord<T extends object> = T extends (...args: never[]) => unknown ? false : T extends new (...args: never[]) => unknown ? false : keyof T extends never ? true : false;
|
|
53
|
+
/**
|
|
54
|
+
* Merges two object types while eliding empty operands.
|
|
55
|
+
*
|
|
56
|
+
* If either operand is {@link EmptyRecord}, the other operand is returned
|
|
57
|
+
* directly instead of producing intersections such as
|
|
58
|
+
* `Component & EmptyRecord` in editor hovers.
|
|
59
|
+
*
|
|
60
|
+
* Unlike a homomorphic mapped type (for example `Simplify<T>`), this preserves
|
|
61
|
+
* call and construct signatures. Many component types are callable objects,
|
|
62
|
+
* and mapped types silently discard those signatures.
|
|
63
|
+
*
|
|
64
|
+
* @remarks
|
|
65
|
+
* Instantiate `MergeRecords` directly. Introducing an intermediate alias for
|
|
66
|
+
* one operand (for example `type C = PolymorphicComponent<G>`) can prevent
|
|
67
|
+
* `IsEmptyRecord` from evaluating eagerly, which breaks assignability under
|
|
68
|
+
* `exactOptionalPropertyTypes`.
|
|
69
|
+
*/
|
|
70
|
+
type MergeRecords<A extends object, B extends object> = IsEmptyRecord<A> extends true ? B : IsEmptyRecord<B> extends true ? A : A & B;
|
|
8
71
|
|
|
9
72
|
type IntrinsicTag = keyof HTMLElementTagNameMap;
|
|
10
73
|
|
|
@@ -187,7 +250,7 @@ type ClassPluginFactory<TProps extends AnyRecord = EmptyRecord> = <V extends Var
|
|
|
187
250
|
* wherever a factory's concrete plugin-props shape isn't tracked (factory generics,
|
|
188
251
|
* capability wiring). */
|
|
189
252
|
type AnyClassPluginFactory = ClassPluginFactory<AnyRecord> | undefined;
|
|
190
|
-
type ExtractPluginProps<TPlugin extends AnyClassPluginFactory> = TPlugin extends ClassPluginFactory<infer T> ? string extends keyof T ?
|
|
253
|
+
type ExtractPluginProps<TPlugin extends AnyClassPluginFactory> = TPlugin extends ClassPluginFactory<infer T> ? string extends keyof T ? NoPluginProps : T : NoPluginProps;
|
|
191
254
|
|
|
192
255
|
type AriaContext = {
|
|
193
256
|
readonly tag: IntrinsicTag;
|
|
@@ -251,6 +314,12 @@ type EnforcementOptions<TAllowed extends ElementType = ElementType> = {
|
|
|
251
314
|
* `@praxis-kit/diagnostics`.
|
|
252
315
|
*/
|
|
253
316
|
readonly diagnostics?: Diagnostics | DiagnosticsMode;
|
|
317
|
+
/**
|
|
318
|
+
* ARIA/accessibility rules evaluated against the resolved tag and props on every render.
|
|
319
|
+
* Each rule is a function receiving the current context and returning zero or more
|
|
320
|
+
* violations, some of which can carry an auto-applicable fix (see `createRemoveAttributeRule`
|
|
321
|
+
* and friends in `praxis-kit/contract`).
|
|
322
|
+
*/
|
|
254
323
|
readonly aria?: readonly AriaRule[];
|
|
255
324
|
/**
|
|
256
325
|
* Rules that need `AriaPolicyEngine`'s fix-application/caching machinery
|
|
@@ -262,6 +331,11 @@ type EnforcementOptions<TAllowed extends ElementType = ElementType> = {
|
|
|
262
331
|
* misleading `aria` name to get the machinery it needs.
|
|
263
332
|
*/
|
|
264
333
|
readonly rules?: readonly AriaRule[];
|
|
334
|
+
/**
|
|
335
|
+
* Declares which children are valid, by name, match predicate, and cardinality (e.g. "at
|
|
336
|
+
* least 1, at most 4 `Button` children"). Open by default — children matching no rule are
|
|
337
|
+
* still allowed unless `exclusiveChildren` is set.
|
|
338
|
+
*/
|
|
265
339
|
readonly children?: readonly ChildRuleInput[];
|
|
266
340
|
/**
|
|
267
341
|
* When true, only children matching a `children` rule (or text, per `allowText`)
|
|
@@ -274,19 +348,49 @@ type EnforcementOptions<TAllowed extends ElementType = ElementType> = {
|
|
|
274
348
|
* or any listed rule. Default: true.
|
|
275
349
|
*/
|
|
276
350
|
readonly allowText?: boolean;
|
|
351
|
+
/**
|
|
352
|
+
* Prop transforms composed with the component's own `normalize` (from `FactoryOptions`) and
|
|
353
|
+
* run before it. Unlike `normalize`, these live in the enforcement bucket because they
|
|
354
|
+
* typically encode a built-in HTML/ARIA fact rather than component-specific behavior.
|
|
355
|
+
*/
|
|
277
356
|
readonly props?: readonly PropNormalizer[];
|
|
278
357
|
/** Restricts the `as` prop to this set of tags. Violations route through diagnostics. */
|
|
279
358
|
readonly allowedAs?: readonly TAllowed[];
|
|
280
359
|
};
|
|
281
360
|
|
|
282
361
|
type StylingOptions<V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<V> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory> = {
|
|
362
|
+
/** Class applied to every instance regardless of variant selection. */
|
|
283
363
|
readonly base?: ClassName;
|
|
364
|
+
/**
|
|
365
|
+
* Named variant groups (e.g. `intent`, `size`), each mapping its possible values to a
|
|
366
|
+
* class string. A consumer selects a value per group as a prop (`<Button intent="primary">`).
|
|
367
|
+
*/
|
|
284
368
|
readonly variants?: V;
|
|
369
|
+
/** Value used for a variant group when the consumer doesn't pass one explicitly. */
|
|
285
370
|
readonly defaults?: Partial<DefaultVariants<V>>;
|
|
371
|
+
/**
|
|
372
|
+
* Applies an extra class only when a specific *combination* of variant selections matches —
|
|
373
|
+
* for cases `variants` alone can't express (e.g. `intent: 'primary'` + `size: 'lg'` together
|
|
374
|
+
* need a class neither variant would add on its own).
|
|
375
|
+
*/
|
|
286
376
|
readonly compounds?: readonly CompoundVariant<V>[];
|
|
377
|
+
/**
|
|
378
|
+
* Named bundles of variant values, selectable as a single unit via the `recipe` prop (e.g.
|
|
379
|
+
* `<Button recipe="cta">` instead of setting `intent`/`size` individually).
|
|
380
|
+
*/
|
|
287
381
|
readonly presets?: TPreset;
|
|
382
|
+
/** Maps a resolved tag directly to a raw class string, independent of the variant system. */
|
|
288
383
|
readonly tags?: Readonly<TagMap>;
|
|
384
|
+
/**
|
|
385
|
+
* A `ClassPluginFactory` (e.g. the Tailwind layout pipeline) that extends class resolution
|
|
386
|
+
* with its own owned props, layered on top of `variants`/`presets`/`tags`.
|
|
387
|
+
*/
|
|
289
388
|
readonly plugin?: TPlugin;
|
|
389
|
+
/**
|
|
390
|
+
* A cache-key → resolved-class-string lookup for every statically-known variant
|
|
391
|
+
* combination, skipping runtime class computation entirely when a match is found. Normally
|
|
392
|
+
* generated by a build-time class-extraction plugin rather than hand-authored.
|
|
393
|
+
*/
|
|
290
394
|
readonly precomputedClasses?: Readonly<Record<string, string>>;
|
|
291
395
|
};
|
|
292
396
|
|
|
@@ -295,17 +399,51 @@ type NormalizeFn<Props extends AnyRecord = AnyRecord> = {
|
|
|
295
399
|
}['normalize'];
|
|
296
400
|
type AnyFactoryOptions = FactoryOptions<ElementType, AnyRecord, VariantMap, RecipeMap<VariantMap>, AnyClassPluginFactory>;
|
|
297
401
|
type FactoryOptions<TDefault extends ElementType = ElementType, Props extends AnyRecord = EmptyRecord, V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<V> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType> = {
|
|
402
|
+
/** The intrinsic tag the component renders by default. Overridable per instance via `as`. */
|
|
298
403
|
readonly tag?: TDefault;
|
|
404
|
+
/** Display name used in diagnostics, dev tools, and generated component naming. */
|
|
299
405
|
readonly name?: string;
|
|
406
|
+
/** Values used for the component's own (non-variant) props when the consumer omits them. */
|
|
300
407
|
readonly defaults?: Partial<NoInfer<Props>>;
|
|
408
|
+
/**
|
|
409
|
+
* A pure `(props) => props` transform run on every render, after `enforcement.props`'s
|
|
410
|
+
* normalizers see the same input. Use this for component-specific prop shaping — anything
|
|
411
|
+
* that depends on live instance state or the real DOM element belongs in `onElement` instead.
|
|
412
|
+
*/
|
|
301
413
|
readonly normalize?: NormalizeFn<NoInfer<Props>>;
|
|
414
|
+
/** Variant groups, base classes, presets, and the optional class-resolution plugin. */
|
|
302
415
|
readonly styling?: StylingOptions<V, TPreset, TPlugin>;
|
|
416
|
+
/** ARIA rules, child-content contracts, and other runtime validation for this component. */
|
|
303
417
|
readonly enforcement?: EnforcementOptions<TAllowed>;
|
|
304
418
|
/**
|
|
305
419
|
* Adapter-resolved diagnostics default, spread in by `resolveAdapterCommonOptions`. Not meant to
|
|
306
420
|
* be set directly by component authors — use `enforcement.diagnostics` to override per component.
|
|
307
421
|
*/
|
|
308
422
|
readonly diagnostics?: Diagnostics;
|
|
423
|
+
/**
|
|
424
|
+
* Sub-components to attach to the generated root component, producing a
|
|
425
|
+
* compound component API (for example, `Card.Header`, `Card.Content`,
|
|
426
|
+
* and `Card.Footer`). Purely additive — has no effect on
|
|
427
|
+
* `enforcement.children`; author child rules explicitly if the component
|
|
428
|
+
* needs to validate its children.
|
|
429
|
+
*/
|
|
430
|
+
readonly subComponents?: SubComponentMap;
|
|
431
|
+
/**
|
|
432
|
+
* Called once per instance, when the real underlying DOM element first
|
|
433
|
+
* exists, in every adapter — via that adapter's own native mount
|
|
434
|
+
* lifecycle, never through the props/attribute pipeline. Use this for
|
|
435
|
+
* wiring that needs the actual element (native imperative methods like
|
|
436
|
+
* `dialogEl.showModal()`, native events like `close`/`cancel` that have
|
|
437
|
+
* no prop-based equivalent), not for anything expressible as a plain
|
|
438
|
+
* prop.
|
|
439
|
+
*
|
|
440
|
+
* `getProps` returns the instance's *current* resolved props at call
|
|
441
|
+
* time — read it from inside a listener registered once at mount, rather
|
|
442
|
+
* than re-subscribing on every prop change.
|
|
443
|
+
*
|
|
444
|
+
* Return a cleanup function to run when the instance unmounts.
|
|
445
|
+
*/
|
|
446
|
+
readonly onElement?: (element: Element, getProps: () => Readonly<Props>) => void | (() => void);
|
|
309
447
|
};
|
|
310
448
|
|
|
311
449
|
declare function defineContractComponent<O extends FactoryOptions>(options: O): <R>(factory: (options: O) => R) => R;
|
|
@@ -314,7 +452,7 @@ type UnknownProps = AnyRecord;
|
|
|
314
452
|
type SolidElement = JSX.Element;
|
|
315
453
|
type SlotRenderFn = (props: UnknownProps) => SolidElement;
|
|
316
454
|
|
|
317
|
-
type SolidFactoryOptions<TDefault extends ElementType, Props extends UnknownProps, Variants extends Readonly<VariantMap>, TPreset extends RecipeMap<Variants> =
|
|
455
|
+
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> & {
|
|
318
456
|
/**
|
|
319
457
|
* Return true for any prop key that should be consumed but not forwarded to the DOM.
|
|
320
458
|
* Receives `runtime.options.variantKeys` as a convenience if needed.
|
|
@@ -357,6 +495,28 @@ type PolymorphicComponent<G extends PolymorphicGenerics> = {
|
|
|
357
495
|
displayName?: string;
|
|
358
496
|
};
|
|
359
497
|
|
|
360
|
-
|
|
498
|
+
/**
|
|
499
|
+
* Creates a polymorphic Solid component with praxis-kit contracts applied.
|
|
500
|
+
*
|
|
501
|
+
* ```tsx
|
|
502
|
+
* const Button = createContractComponent({
|
|
503
|
+
* tag: 'button',
|
|
504
|
+
* name: 'Button',
|
|
505
|
+
* styling: {
|
|
506
|
+
* base: 'btn',
|
|
507
|
+
* variants: { intent: { primary: 'btn--primary', ghost: 'btn--ghost' } },
|
|
508
|
+
* defaults: { intent: 'primary' },
|
|
509
|
+
* },
|
|
510
|
+
* })
|
|
511
|
+
*
|
|
512
|
+
* <Button intent="ghost" as="a" href="/home">Home</Button>
|
|
513
|
+
* ```
|
|
514
|
+
*
|
|
515
|
+
* `ref` is forwarded as an ordinary Solid ref callback. Pass `subComponents` to attach named
|
|
516
|
+
* sub-components (`Card.Header`) and `onElement` to run setup once the real DOM element exists.
|
|
517
|
+
*/
|
|
518
|
+
declare function createContractComponent<TDefault extends ElementType, Props extends UnknownProps = EmptyRecord, Variants extends Readonly<VariantMap> = NoVariants, TPreset extends RecipeMap<Variants> = NoPreset, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TSubComponents extends Readonly<AnyRecord> = EmptyRecord>(options: SolidFactoryOptions<TDefault, Props, Variants, TPreset, TPlugin> & {
|
|
519
|
+
readonly subComponents?: TSubComponents;
|
|
520
|
+
}): MergeRecords<PolymorphicComponent<PolymorphicGenerics<TDefault, MergeRecords<Props, ExtractPluginProps<TPlugin>>, Variants, TPreset>>, TSubComponents>;
|
|
361
521
|
|
|
362
522
|
export { type AnyFactoryOptions, type ElementRef, type ElementType, type EmptyRecord, type PolymorphicComponent, type PolymorphicGenerics, type PolymorphicProps, type SolidFactoryOptions, createContractComponent, defineContractComponent };
|
package/dist/solid/index.js
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
// ../../lib/adapter-utils/src/invariant.ts
|
|
2
|
+
function panic(message) {
|
|
3
|
+
throw new Error(message);
|
|
4
|
+
}
|
|
5
|
+
function invariant(condition, message) {
|
|
6
|
+
if (!condition) panic(message);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// ../../lib/adapter-utils/src/runtime/apply-display-name.ts
|
|
10
|
+
function applyDisplayName(component, name) {
|
|
11
|
+
Object.assign(component, { displayName: name ?? "PolymorphicComponent" });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// ../../lib/adapter-utils/src/runtime/define-component.ts
|
|
15
|
+
function defineContractComponent(options) {
|
|
16
|
+
return (factory) => factory(options);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ../../lib/adapter-utils/src/runtime/assemble-compound-component.ts
|
|
20
|
+
function assembleCompoundComponent(root, subComponents) {
|
|
21
|
+
if (!subComponents) return root;
|
|
22
|
+
return Object.assign(root, subComponents);
|
|
23
|
+
}
|
|
24
|
+
|
|
1
25
|
// ../../lib/primitive/src/tag/resolve-tag.ts
|
|
2
26
|
function makeResolveTag(defaultTag) {
|
|
3
27
|
return function tag(as) {
|
|
@@ -40,10 +64,13 @@ function isString(value) {
|
|
|
40
64
|
function isNumber(value) {
|
|
41
65
|
return typeof value === "number";
|
|
42
66
|
}
|
|
67
|
+
function isFunction(value) {
|
|
68
|
+
return typeof value === "function";
|
|
69
|
+
}
|
|
43
70
|
|
|
44
71
|
// ../../lib/primitive/src/rule/is-dynamic-rule.ts
|
|
45
72
|
function isDynamicRule(rule) {
|
|
46
|
-
return isObject(rule, true) && rule
|
|
73
|
+
return isObject(rule, true) && Reflect.get(rule, RULE_BRAND) === true;
|
|
47
74
|
}
|
|
48
75
|
|
|
49
76
|
// ../../lib/primitive/src/rule/resolve-rule.ts
|
|
@@ -673,17 +700,17 @@ var COMPONENT_DEFAULT_TAG = /* @__PURE__ */ Symbol.for("praxis.component-default
|
|
|
673
700
|
// ../../lib/primitive/src/guards/children/is-tag.ts
|
|
674
701
|
function getAsProp(child) {
|
|
675
702
|
if (!isObject(child) || !("props" in child)) return void 0;
|
|
676
|
-
const props = child
|
|
703
|
+
const { props } = child;
|
|
677
704
|
if (!isObject(props)) return void 0;
|
|
678
|
-
const as = props
|
|
705
|
+
const as = Reflect.get(props, "as");
|
|
679
706
|
return isString(as) && as !== "" ? as : void 0;
|
|
680
707
|
}
|
|
681
708
|
function getTag(child) {
|
|
682
709
|
if (!isObject(child) || !("type" in child)) return void 0;
|
|
683
|
-
const t = child
|
|
710
|
+
const { type: t } = child;
|
|
684
711
|
if (isString(t)) return t;
|
|
685
712
|
if (typeof t === "function" || isObject(t)) {
|
|
686
|
-
const defaultTag = t
|
|
713
|
+
const defaultTag = Reflect.get(t, COMPONENT_DEFAULT_TAG);
|
|
687
714
|
if (!isString(defaultTag)) return void 0;
|
|
688
715
|
return getAsProp(child) ?? defaultTag;
|
|
689
716
|
}
|
|
@@ -703,14 +730,34 @@ function isTag(...args) {
|
|
|
703
730
|
return tag !== void 0 && set2.has(tag);
|
|
704
731
|
}
|
|
705
732
|
|
|
706
|
-
// ../../lib/adapter-utils/src/runtime/
|
|
707
|
-
function
|
|
708
|
-
|
|
733
|
+
// ../../lib/adapter-utils/src/runtime/finalize-component.ts
|
|
734
|
+
function finalizeComponent(component, defaultTag, subComponents) {
|
|
735
|
+
if (typeof defaultTag === "string") {
|
|
736
|
+
Object.assign(component, { [COMPONENT_DEFAULT_TAG]: defaultTag });
|
|
737
|
+
}
|
|
738
|
+
return assembleCompoundComponent(component, subComponents);
|
|
709
739
|
}
|
|
710
740
|
|
|
711
|
-
// ../../lib/adapter-utils/src/runtime/
|
|
712
|
-
|
|
713
|
-
|
|
741
|
+
// ../../lib/adapter-utils/src/runtime/is-factory-options-like.ts
|
|
742
|
+
var FACTORY_OPTIONS_FIELD_VALIDATORS = {
|
|
743
|
+
tag: (v) => v === void 0 || isString(v),
|
|
744
|
+
name: (v) => v === void 0 || isString(v),
|
|
745
|
+
defaults: (v) => v === void 0 || isObject(v),
|
|
746
|
+
normalize: (v) => v === void 0 || isFunction(v),
|
|
747
|
+
styling: (v) => v === void 0 || isObject(v),
|
|
748
|
+
enforcement: (v) => v === void 0 || isObject(v),
|
|
749
|
+
diagnostics: (v) => v === void 0 || isObject(v),
|
|
750
|
+
subComponents: (v) => v === void 0 || isObject(v),
|
|
751
|
+
onElement: (v) => v === void 0 || isFunction(v)
|
|
752
|
+
};
|
|
753
|
+
function isFactoryOptionsLike(options, extraFieldValidators) {
|
|
754
|
+
if (!isObject(options)) return false;
|
|
755
|
+
const validators = { ...FACTORY_OPTIONS_FIELD_VALIDATORS, ...extraFieldValidators };
|
|
756
|
+
for (const [key, value] of Object.entries(options)) {
|
|
757
|
+
const validate = validators[key];
|
|
758
|
+
if (!validate || !validate(value)) return false;
|
|
759
|
+
}
|
|
760
|
+
return true;
|
|
714
761
|
}
|
|
715
762
|
|
|
716
763
|
// ../../lib/contract/src/aria/aria-role-policy.ts
|
|
@@ -2863,7 +2910,7 @@ function getChildProp(child, key) {
|
|
|
2863
2910
|
if (!isVNodeLike(child)) return void 0;
|
|
2864
2911
|
const { props } = child;
|
|
2865
2912
|
if (!isObject(props)) return void 0;
|
|
2866
|
-
return props
|
|
2913
|
+
return Reflect.get(props, key);
|
|
2867
2914
|
}
|
|
2868
2915
|
|
|
2869
2916
|
// ../core/src/html/contracts/aria/landmarks.ts
|
|
@@ -3512,6 +3559,9 @@ function composeFilter(ownedKeys, filterProps) {
|
|
|
3512
3559
|
return (key, variantKeys) => defaultFilter(key, variantKeys) || filterProps(key, variantKeys);
|
|
3513
3560
|
}
|
|
3514
3561
|
|
|
3562
|
+
// ../../adapters/solid/src/create-contract-component.ts
|
|
3563
|
+
import { mergeProps as mergeProps2, onCleanup } from "solid-js";
|
|
3564
|
+
|
|
3515
3565
|
// ../../adapters/solid/src/slot/slot-validator.ts
|
|
3516
3566
|
var SlotValidator = class extends InvariantBase {
|
|
3517
3567
|
#name;
|
|
@@ -3559,6 +3609,13 @@ function buildRuntime(options) {
|
|
|
3559
3609
|
};
|
|
3560
3610
|
}
|
|
3561
3611
|
|
|
3612
|
+
// ../../adapters/solid/src/is-polymorphic-component.ts
|
|
3613
|
+
function isPolymorphicComponent(value) {
|
|
3614
|
+
if (!isFunction(value)) return false;
|
|
3615
|
+
if (!("displayName" in value)) return true;
|
|
3616
|
+
return value.displayName === void 0 || isString(value.displayName);
|
|
3617
|
+
}
|
|
3618
|
+
|
|
3562
3619
|
// ../../adapters/solid/src/render.tsx
|
|
3563
3620
|
import { createComponent as _$createComponent } from "solid-js/web";
|
|
3564
3621
|
import { mergeProps as _$mergeProps } from "solid-js/web";
|
|
@@ -3673,20 +3730,46 @@ function render({
|
|
|
3673
3730
|
}, () => domProps()));
|
|
3674
3731
|
}
|
|
3675
3732
|
|
|
3733
|
+
// ../../adapters/solid/src/to-solid-factory-options.ts
|
|
3734
|
+
var SOLID_FIELD_VALIDATORS = {
|
|
3735
|
+
filterProps: (v) => v === void 0 || isFunction(v)
|
|
3736
|
+
};
|
|
3737
|
+
function isSolidFactoryOptions(options) {
|
|
3738
|
+
return isFactoryOptionsLike(options, SOLID_FIELD_VALIDATORS);
|
|
3739
|
+
}
|
|
3740
|
+
|
|
3676
3741
|
// ../../adapters/solid/src/create-contract-component.ts
|
|
3677
3742
|
function createContractComponent(options) {
|
|
3743
|
+
invariant(isSolidFactoryOptions(options), "options is not a valid SolidFactoryOptions object");
|
|
3678
3744
|
const bundle = buildRuntime(options);
|
|
3745
|
+
const { onElement } = options;
|
|
3679
3746
|
const Component = (props) => {
|
|
3747
|
+
const propsWithRef = onElement ? mergeProps2(props, {
|
|
3748
|
+
get ref() {
|
|
3749
|
+
const consumerRef = props.ref;
|
|
3750
|
+
return (el) => {
|
|
3751
|
+
if (typeof consumerRef === "function") consumerRef(el);
|
|
3752
|
+
const cleanup = onElement(el, () => props);
|
|
3753
|
+
if (cleanup) onCleanup(cleanup);
|
|
3754
|
+
};
|
|
3755
|
+
}
|
|
3756
|
+
}) : props;
|
|
3680
3757
|
return render({
|
|
3681
3758
|
...bundle,
|
|
3682
|
-
props
|
|
3759
|
+
props: propsWithRef
|
|
3683
3760
|
});
|
|
3684
3761
|
};
|
|
3685
3762
|
applyDisplayName(Component, options.name);
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3763
|
+
const assembled = finalizeComponent(
|
|
3764
|
+
Component,
|
|
3765
|
+
bundle.runtime.options.defaultTag,
|
|
3766
|
+
options.subComponents
|
|
3767
|
+
);
|
|
3768
|
+
invariant(
|
|
3769
|
+
isPolymorphicComponent(assembled),
|
|
3770
|
+
"Generated component failed to satisfy the PolymorphicComponent shape"
|
|
3771
|
+
);
|
|
3772
|
+
return assembled;
|
|
3690
3773
|
}
|
|
3691
3774
|
export {
|
|
3692
3775
|
createContractComponent,
|