praxis-kit 6.6.1 → 7.3.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/lit/index.js CHANGED
@@ -1,8 +1,22 @@
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
+
1
9
  // ../../lib/adapter-utils/src/runtime/define-component.ts
2
10
  function defineContractComponent(options) {
3
11
  return (factory) => factory(options);
4
12
  }
5
13
 
14
+ // ../../lib/adapter-utils/src/runtime/assemble-compound-component.ts
15
+ function assembleCompoundComponent(root, subComponents) {
16
+ if (!subComponents) return root;
17
+ return Object.assign(root, subComponents);
18
+ }
19
+
6
20
  // ../../lib/primitive/src/tag/resolve-tag.ts
7
21
  function makeResolveTag(defaultTag) {
8
22
  return function tag(as) {
@@ -13,6 +27,11 @@ function makeResolveTag(defaultTag) {
13
27
  // ../../lib/primitive/src/rule/rule-brand.ts
14
28
  var RULE_BRAND = /* @__PURE__ */ Symbol("praxis-kit/dynamic-rule");
15
29
 
30
+ // ../../lib/primitive/src/rule/dynamic.ts
31
+ function dynamic(resolve) {
32
+ return { [RULE_BRAND]: true, resolve };
33
+ }
34
+
16
35
  // ../../lib/primitive/src/guards/foundational/is-defined.ts
17
36
  function isUndefined(value) {
18
37
  return value === void 0;
@@ -26,7 +45,7 @@ function isNonNull(value) {
26
45
  return value != null;
27
46
  }
28
47
  function isNullish(value) {
29
- return isNull(value) || value === void 0;
48
+ return isNull(value) || isUndefined(value);
30
49
  }
31
50
 
32
51
  // ../../lib/primitive/src/utils/type-guards.ts
@@ -40,10 +59,13 @@ function isString(value) {
40
59
  function isNumber(value) {
41
60
  return typeof value === "number";
42
61
  }
62
+ function isFunction(value) {
63
+ return typeof value === "function";
64
+ }
43
65
 
44
66
  // ../../lib/primitive/src/rule/is-dynamic-rule.ts
45
67
  function isDynamicRule(rule) {
46
- return isObject(rule, true) && rule[RULE_BRAND] === true;
68
+ return isObject(rule, true) && Reflect.get(rule, RULE_BRAND) === true;
47
69
  }
48
70
 
49
71
  // ../../lib/primitive/src/rule/resolve-rule.ts
@@ -517,6 +539,24 @@ var ROLE_RESTRICTED_ATTRIBUTES = /* @__PURE__ */ new Map([
517
539
  ]
518
540
  ]);
519
541
 
542
+ // ../../lib/primitive/src/constants/html/void-tags.ts
543
+ var VOID_TAGS = [
544
+ "area",
545
+ "base",
546
+ "br",
547
+ "col",
548
+ "embed",
549
+ "hr",
550
+ "img",
551
+ "input",
552
+ "link",
553
+ "meta",
554
+ "param",
555
+ "source",
556
+ "track",
557
+ "wbr"
558
+ ];
559
+
520
560
  // ../../lib/primitive/src/guards/aria/is-aria-attribute.ts
521
561
  function isGlobalAriaAttribute(attr) {
522
562
  return GLOBAL_ARIA_ATTRIBUTES.has(attr);
@@ -564,17 +604,17 @@ var COMPONENT_DEFAULT_TAG = /* @__PURE__ */ Symbol.for("praxis.component-default
564
604
  // ../../lib/primitive/src/guards/children/is-tag.ts
565
605
  function getAsProp(child) {
566
606
  if (!isObject(child) || !("props" in child)) return void 0;
567
- const props = child.props;
607
+ const { props } = child;
568
608
  if (!isObject(props)) return void 0;
569
- const as = props.as;
609
+ const as = Reflect.get(props, "as");
570
610
  return isString(as) && as !== "" ? as : void 0;
571
611
  }
572
612
  function getTag(child) {
573
613
  if (!isObject(child) || !("type" in child)) return void 0;
574
- const t = child.type;
614
+ const { type: t } = child;
575
615
  if (isString(t)) return t;
576
616
  if (typeof t === "function" || isObject(t)) {
577
- const defaultTag = t[COMPONENT_DEFAULT_TAG];
617
+ const defaultTag = Reflect.get(t, COMPONENT_DEFAULT_TAG);
578
618
  if (!isString(defaultTag)) return void 0;
579
619
  return getAsProp(child) ?? defaultTag;
580
620
  }
@@ -594,6 +634,28 @@ function isTag(...args) {
594
634
  return tag !== void 0 && set2.has(tag);
595
635
  }
596
636
 
637
+ // ../../lib/adapter-utils/src/runtime/is-factory-options-like.ts
638
+ var FACTORY_OPTIONS_FIELD_VALIDATORS = {
639
+ tag: (v) => v === void 0 || isString(v),
640
+ name: (v) => v === void 0 || isString(v),
641
+ defaults: (v) => v === void 0 || isObject(v),
642
+ normalize: (v) => v === void 0 || isFunction(v),
643
+ styling: (v) => v === void 0 || isObject(v),
644
+ enforcement: (v) => v === void 0 || isObject(v),
645
+ diagnostics: (v) => v === void 0 || isObject(v),
646
+ subComponents: (v) => v === void 0 || isObject(v),
647
+ onElement: (v) => v === void 0 || isFunction(v)
648
+ };
649
+ function isFactoryOptionsLike(options, extraFieldValidators) {
650
+ if (!isObject(options)) return false;
651
+ const validators = { ...FACTORY_OPTIONS_FIELD_VALIDATORS, ...extraFieldValidators };
652
+ for (const [key, value] of Object.entries(options)) {
653
+ const validate = validators[key];
654
+ if (!validate || !validate(value)) return false;
655
+ }
656
+ return true;
657
+ }
658
+
597
659
  // ../../lib/contract/src/aria/aria-role-policy.ts
598
660
  function getImplicitRole(tag, props) {
599
661
  if (tag in IMPLICIT_ROLE_RECORD) return IMPLICIT_ROLE_RECORD[tag];
@@ -1446,7 +1508,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1446
1508
  const cached = _AriaPolicyEngine.#removeAttributeFixCache.get(attr);
1447
1509
  if (cached) return cached;
1448
1510
  const fix = {
1449
- kind: `removeAttribute:${attr}`,
1511
+ kind: "removeAttribute",
1512
+ attribute: attr,
1450
1513
  apply: ({ props }) => {
1451
1514
  if (!(attr in props)) return { applied: false, next: props };
1452
1515
  return { applied: true, next: omitProp(props, attr), previous: props };
@@ -1717,7 +1780,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1717
1780
  if (!impliedLive) return NO_VIOLATIONS2;
1718
1781
  if ("aria-live" in props) return NO_VIOLATIONS2;
1719
1782
  const injectLive = {
1720
- kind: `injectLive:${effectiveRole}`,
1783
+ kind: "injectLive",
1784
+ attribute: "aria-live",
1721
1785
  apply: (ctx) => ({
1722
1786
  applied: true,
1723
1787
  next: { ...ctx.props, "aria-live": impliedLive },
@@ -1786,6 +1850,23 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1786
1850
  }
1787
1851
  };
1788
1852
 
1853
+ // ../../lib/contract/src/aria/factories.ts
1854
+ function removeProp(props, key) {
1855
+ const next = { ...props };
1856
+ delete next[key];
1857
+ return next;
1858
+ }
1859
+ function removeAttributeFix(attribute) {
1860
+ return Object.freeze({
1861
+ kind: "removeAttribute",
1862
+ attribute,
1863
+ apply: ({ props }) => {
1864
+ if (!(attribute in props)) return { applied: false, next: props };
1865
+ return { applied: true, next: removeProp(props, attribute), previous: props };
1866
+ }
1867
+ });
1868
+ }
1869
+
1789
1870
  // ../../lib/contract/src/children/get-type-name.ts
1790
1871
  function getTypeName(value) {
1791
1872
  if (value === null) return "null";
@@ -2104,22 +2185,6 @@ import { warnDiagnostics as warnDiagnostics2 } from "../_shared/diagnostics.js";
2104
2185
 
2105
2186
  // ../core/src/html/contracts/categories.ts
2106
2187
  var METADATA_TAGS = ["script", "template"];
2107
- var VOID_TAGS = [
2108
- "area",
2109
- "base",
2110
- "br",
2111
- "col",
2112
- "embed",
2113
- "hr",
2114
- "img",
2115
- "input",
2116
- "link",
2117
- "meta",
2118
- "param",
2119
- "source",
2120
- "track",
2121
- "wbr"
2122
- ];
2123
2188
  var TEXT_ONLY_TAGS = [
2124
2189
  "option",
2125
2190
  "script",
@@ -2243,20 +2308,6 @@ var INPUT_MUTUALLY_EXCLUSIVE_POLICIES = [
2243
2308
 
2244
2309
  // ../core/src/html/spec/validators/attribute-type-validator.ts
2245
2310
  var DEFAULT_INPUT_TYPE = "text";
2246
- function omit(props, key) {
2247
- const next = { ...props };
2248
- delete next[key];
2249
- return next;
2250
- }
2251
- function removeAttributeFix(attribute) {
2252
- return {
2253
- kind: `removeAttribute:${attribute}`,
2254
- apply: ({ props }) => {
2255
- if (!(attribute in props)) return { applied: false, next: props };
2256
- return { applied: true, next: omit(props, attribute), previous: props };
2257
- }
2258
- };
2259
- }
2260
2311
  function createInputAttributeTypeRule({
2261
2312
  attribute,
2262
2313
  allowedTypes
@@ -2709,7 +2760,7 @@ function getChildProp(child, key) {
2709
2760
  if (!isVNodeLike(child)) return void 0;
2710
2761
  const { props } = child;
2711
2762
  if (!isObject(props)) return void 0;
2712
- return props[key];
2763
+ return Reflect.get(props, key);
2713
2764
  }
2714
2765
 
2715
2766
  // ../core/src/html/contracts/aria/landmarks.ts
@@ -2762,6 +2813,13 @@ function isLabelableControl(child) {
2762
2813
  const type = getChildProp(child, "type");
2763
2814
  return !isString(type) || type !== "hidden";
2764
2815
  }
2816
+ function hasAccessibleNameProp(props) {
2817
+ return "aria-label" in props || "aria-labelledby" in props;
2818
+ }
2819
+ function isNonEmptyTextChild(child) {
2820
+ if (isNumber(child)) return true;
2821
+ return isString(child) && child.trim().length > 0;
2822
+ }
2765
2823
  var labelContract = contract([
2766
2824
  { name: "control", match: isLabelableControl, cardinality: { max: 1 } },
2767
2825
  { name: "nested-label", match: isTag("label"), cardinality: { max: 0 } },
@@ -2769,6 +2827,13 @@ var labelContract = contract([
2769
2827
  name: "other-interactive-content",
2770
2828
  match: isTag(...OTHER_INTERACTIVE_TAGS),
2771
2829
  cardinality: { max: 0 }
2830
+ },
2831
+ {
2832
+ name: "accessible-name",
2833
+ match: isNonEmptyTextChild,
2834
+ cardinality: dynamic(
2835
+ (ctx) => hasAccessibleNameProp(ctx.props) ? { min: 0 } : { min: 1 }
2836
+ )
2772
2837
  }
2773
2838
  ]);
2774
2839
 
@@ -3477,7 +3542,7 @@ function diffAndApplyAttributes(host, state, prevPipelineAttrs, incomingProps) {
3477
3542
  }
3478
3543
 
3479
3544
  // ../../adapters/lit/src/create-contract-component.ts
3480
- import { LitElement, html } from "lit";
3545
+ import { LitElement as LitElement2, html } from "lit";
3481
3546
 
3482
3547
  // ../../adapters/lit/src/build-runtime.ts
3483
3548
  import { silentDiagnostics as silentDiagnostics4 } from "../_shared/diagnostics.js";
@@ -3505,6 +3570,12 @@ function buildRuntime(options) {
3505
3570
  };
3506
3571
  }
3507
3572
 
3573
+ // ../../adapters/lit/src/is-lit-contract-component.ts
3574
+ import { LitElement } from "lit";
3575
+ function isLitContractComponent(value) {
3576
+ return isFunction(value) && value.prototype instanceof LitElement;
3577
+ }
3578
+
3508
3579
  // ../../adapters/lit/src/render-to-string.ts
3509
3580
  var ssrRegistry = /* @__PURE__ */ new WeakMap();
3510
3581
  function registerForSsr(cls, bundle) {
@@ -3521,8 +3592,17 @@ function renderToString(component, props = {}, innerHTML = "") {
3521
3592
  return renderBundleToString(entry.bundle, props, innerHTML);
3522
3593
  }
3523
3594
 
3595
+ // ../../adapters/lit/src/to-lit-factory-options.ts
3596
+ var LIT_FIELD_VALIDATORS = {
3597
+ filterProps: (v) => v === void 0 || isFunction(v)
3598
+ };
3599
+ function isLitFactoryOptions(options) {
3600
+ return isFactoryOptionsLike(options, LIT_FIELD_VALIDATORS);
3601
+ }
3602
+
3524
3603
  // ../../adapters/lit/src/create-contract-component.ts
3525
3604
  function createContractComponent(options) {
3605
+ invariant(isLitFactoryOptions(options), "options is not a valid LitFactoryOptions object");
3526
3606
  const bundle = buildRuntime(options);
3527
3607
  const looseBundle = toLooseBundle(bundle);
3528
3608
  const variantKeys = options.styling?.variants ? Object.keys(options.styling.variants) : [];
@@ -3547,7 +3627,7 @@ function createContractComponent(options) {
3547
3627
  iterate.forEach(pluginKeys, (key) => {
3548
3628
  staticProps[key] = { type: String, attribute: key };
3549
3629
  });
3550
- class PolymorphicLitElement extends LitElement {
3630
+ class PolymorphicLitElement extends LitElement2 {
3551
3631
  // Tracks keys set by the pipeline last render so stale attrs are removed.
3552
3632
  _pipelineAttrs = /* @__PURE__ */ new Set();
3553
3633
  // Starts true so the first update always runs the pipeline regardless of
@@ -3609,21 +3689,41 @@ function createContractComponent(options) {
3609
3689
  const props = this._buildProps();
3610
3690
  diffAndApplyAttributes(this, resolveHostState(looseBundle, props), this._pipelineAttrs, props);
3611
3691
  }
3692
+ // Light DOM means `this` already is the real host element — no ref/indirection needed.
3693
+ // connectedCallback/disconnectedCallback are the native mount/unmount lifecycle, called
3694
+ // once per instance regardless of how many pipeline re-runs `_applyPraxis` does.
3695
+ _onElementCleanup;
3696
+ connectedCallback() {
3697
+ super.connectedCallback();
3698
+ if (options.onElement) {
3699
+ this._onElementCleanup = options.onElement(this, () => this._buildProps()) ?? void 0;
3700
+ }
3701
+ }
3702
+ disconnectedCallback() {
3703
+ super.disconnectedCallback();
3704
+ this._onElementCleanup?.();
3705
+ this._onElementCleanup = void 0;
3706
+ }
3612
3707
  render() {
3613
3708
  const children = Array.from(this.childNodes);
3614
3709
  const { tag, normalizedProps } = resolveTagAndNormalizedProps(looseBundle, this._buildProps());
3615
3710
  if (bundle.childrenEvaluator) {
3616
3711
  bundle.childrenEvaluator.evaluate(children, { tag, props: normalizedProps });
3617
3712
  }
3618
- bundle.runtime.options.htmlChildrenEvaluatorFn?.(tag)?.evaluate(children);
3713
+ bundle.runtime.options.htmlChildrenEvaluatorFn?.(tag)?.evaluate(children, { tag, props: normalizedProps });
3619
3714
  return html`<slot></slot>`;
3620
3715
  }
3621
3716
  }
3622
3717
  if (options.name) {
3623
3718
  Object.defineProperty(PolymorphicLitElement, "name", { value: options.name });
3624
3719
  }
3720
+ invariant(
3721
+ isLitContractComponent(PolymorphicLitElement),
3722
+ "Generated class failed to satisfy the LitContractComponent shape"
3723
+ );
3625
3724
  registerForSsr(PolymorphicLitElement, looseBundle);
3626
- return PolymorphicLitElement;
3725
+ const assembled = assembleCompoundComponent(PolymorphicLitElement, options.subComponents);
3726
+ return assembled;
3627
3727
  }
3628
3728
  export {
3629
3729
  createContractComponent,
@@ -5,6 +5,8 @@ import { ComponentChildren, VNode, ComponentType, JSX, Ref } from 'preact';
5
5
  type StringMap<T = unknown> = Record<string, T>;
6
6
  type AnyRecord = StringMap<unknown>;
7
7
  type EmptyRecord = Record<never, never>;
8
+ /** A compound component's named sub-components, e.g. `{ Header, Content, Footer }`. */
9
+ type SubComponentMap = Readonly<AnyRecord>;
8
10
 
9
11
  type IntrinsicTag = keyof HTMLElementTagNameMap;
10
12
 
@@ -196,8 +198,8 @@ type AriaContext = {
196
198
  readonly props: ReadonlyDeep<IntrinsicProps>;
197
199
  };
198
200
 
199
- type RemoveAttributeFixKind = `removeAttribute:${string}`;
200
- type InjectLiveFixKind = `injectLive:${string}`;
201
+ type RemoveAttributeFixKind = 'removeAttribute';
202
+ type InjectLiveFixKind = 'injectLive';
201
203
  type FixKind = 'removeRole' | 'setRole' | 'normalizeRelevantAll' | RemoveAttributeFixKind | InjectLiveFixKind;
202
204
 
203
205
  type AriaFixResult = {
@@ -210,6 +212,9 @@ type AriaFixResult = {
210
212
  };
211
213
  type AriaFix = {
212
214
  readonly kind: FixKind;
215
+ /** The attribute a `'removeAttribute'`/`'injectLive'` fix targets — always set for those
216
+ * kinds, absent for kinds with no single-attribute target (`'removeRole'`, etc.). */
217
+ readonly attribute?: string;
213
218
  readonly priority?: number;
214
219
  readonly source?: string;
215
220
  readonly apply: (context: AriaContext) => AriaFixResult;
@@ -303,6 +308,30 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
303
308
  * be set directly by component authors — use `enforcement.diagnostics` to override per component.
304
309
  */
305
310
  readonly diagnostics?: Diagnostics;
311
+ /**
312
+ * Sub-components to attach to the generated root component, producing a
313
+ * compound component API (for example, `Card.Header`, `Card.Content`,
314
+ * and `Card.Footer`). Purely additive — has no effect on
315
+ * `enforcement.children`; author child rules explicitly if the component
316
+ * needs to validate its children.
317
+ */
318
+ readonly subComponents?: SubComponentMap;
319
+ /**
320
+ * Called once per instance, when the real underlying DOM element first
321
+ * exists, in every adapter — via that adapter's own native mount
322
+ * lifecycle, never through the props/attribute pipeline. Use this for
323
+ * wiring that needs the actual element (native imperative methods like
324
+ * `dialogEl.showModal()`, native events like `close`/`cancel` that have
325
+ * no prop-based equivalent), not for anything expressible as a plain
326
+ * prop.
327
+ *
328
+ * `getProps` returns the instance's *current* resolved props at call
329
+ * time — read it from inside a listener registered once at mount, rather
330
+ * than re-subscribing on every prop change.
331
+ *
332
+ * Return a cleanup function to run when the instance unmounts.
333
+ */
334
+ readonly onElement?: (element: Element, getProps: () => Readonly<Props>) => void | (() => void);
306
335
  };
307
336
 
308
337
  declare function defineContractComponent<O extends FactoryOptions>(options: O): <R>(factory: (options: O) => R) => R;
@@ -359,6 +388,8 @@ type PolymorphicComponent<G extends PolymorphicGenerics> = {
359
388
  displayName?: string;
360
389
  };
361
390
 
362
- declare function createContractComponent<TDefault extends ElementType, Props extends UnknownProps = EmptyRecord, Variants extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<Variants> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory>(options: PreactFactoryOptions<TDefault, Props, Variants, TPreset, TPlugin>): PolymorphicComponent<PolymorphicGenerics<TDefault, Props & ExtractPluginProps<TPlugin>, Variants, TPreset>>;
391
+ declare function createContractComponent<TDefault extends ElementType, Props extends UnknownProps = EmptyRecord, Variants extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<Variants> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TSubComponents extends Readonly<AnyRecord> = EmptyRecord>(options: PreactFactoryOptions<TDefault, Props, Variants, TPreset, TPlugin> & {
392
+ readonly subComponents?: TSubComponents;
393
+ }): PolymorphicComponent<PolymorphicGenerics<TDefault, Props & ExtractPluginProps<TPlugin>, Variants, TPreset>> & TSubComponents;
363
394
 
364
395
  export { type AnyFactoryOptions, type ElementRef, type ElementType, type EmptyRecord, type PolymorphicComponent, type PolymorphicGenerics, type PolymorphicProps, type PolymorphicWithAsChild, type PreactFactoryOptions, Slottable, createContractComponent, defineContractComponent };