@proyecto-viviana/solidaria-components 0.4.1 → 0.5.1

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.
Files changed (82) hide show
  1. package/dist/Breadcrumbs.d.ts.map +1 -1
  2. package/dist/Breadcrumbs.js +3 -2
  3. package/dist/Breadcrumbs.js.map +1 -1
  4. package/dist/Breadcrumbs.jsx +3 -3
  5. package/dist/Breadcrumbs.jsx.map +1 -1
  6. package/dist/DateField.d.ts.map +1 -1
  7. package/dist/DateField2.js.map +1 -1
  8. package/dist/DateField2.jsx.map +1 -1
  9. package/dist/DatePicker2.js.map +1 -1
  10. package/dist/DatePicker2.jsx.map +1 -1
  11. package/dist/ElementTag.d.ts +29 -0
  12. package/dist/ElementTag.d.ts.map +1 -0
  13. package/dist/ElementTag.js +220 -0
  14. package/dist/ElementTag.js.map +1 -0
  15. package/dist/ElementTag.jsx +67 -0
  16. package/dist/ElementTag.jsx.map +1 -0
  17. package/dist/Form.d.ts.map +1 -1
  18. package/dist/Form.js +2 -6
  19. package/dist/Form.js.map +1 -1
  20. package/dist/Form.jsx +2 -4
  21. package/dist/Form.jsx.map +1 -1
  22. package/dist/Landmark.js +4 -3
  23. package/dist/Landmark.js.map +1 -1
  24. package/dist/Landmark.jsx +3 -3
  25. package/dist/Landmark.jsx.map +1 -1
  26. package/dist/Link.js +3 -2
  27. package/dist/Link.js.map +1 -1
  28. package/dist/Link.jsx +3 -3
  29. package/dist/Link.jsx.map +1 -1
  30. package/dist/ListBox.d.ts.map +1 -1
  31. package/dist/ListBox.js.map +1 -1
  32. package/dist/ListBox.jsx +51 -51
  33. package/dist/ListBox.jsx.map +1 -1
  34. package/dist/SearchField.d.ts.map +1 -1
  35. package/dist/SearchField.js.map +1 -1
  36. package/dist/SearchField.jsx.map +1 -1
  37. package/dist/Select.d.ts.map +1 -1
  38. package/dist/Select.js +2 -2
  39. package/dist/Select.js.map +1 -1
  40. package/dist/Select.jsx +2 -2
  41. package/dist/Select.jsx.map +1 -1
  42. package/dist/Separator.js +3 -2
  43. package/dist/Separator.js.map +1 -1
  44. package/dist/Separator.jsx +2 -2
  45. package/dist/Separator.jsx.map +1 -1
  46. package/dist/SharedElementTransition.d.ts.map +1 -1
  47. package/dist/SharedElementTransition.js.map +1 -1
  48. package/dist/SharedElementTransition.jsx.map +1 -1
  49. package/dist/Text.d.ts.map +1 -1
  50. package/dist/Text.js +9 -8
  51. package/dist/Text.js.map +1 -1
  52. package/dist/Text.jsx +3 -3
  53. package/dist/Text.jsx.map +1 -1
  54. package/dist/TimeField.d.ts.map +1 -1
  55. package/dist/TimeField.js.map +1 -1
  56. package/dist/TimeField.jsx.map +1 -1
  57. package/dist/VisuallyHidden.d.ts.map +1 -1
  58. package/dist/VisuallyHidden.js +3 -11
  59. package/dist/VisuallyHidden.js.map +1 -1
  60. package/dist/VisuallyHidden.jsx +3 -4
  61. package/dist/VisuallyHidden.jsx.map +1 -1
  62. package/dist/index.d.ts +1 -0
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +2 -1
  65. package/dist/index.jsx +2 -1
  66. package/package.json +5 -3
  67. package/src/Breadcrumbs.tsx +5 -7
  68. package/src/DateField.tsx +5 -7
  69. package/src/DatePicker.tsx +25 -25
  70. package/src/ElementTag.tsx +116 -0
  71. package/src/Form.tsx +18 -1
  72. package/src/Landmark.tsx +4 -4
  73. package/src/Link.tsx +4 -4
  74. package/src/ListBox.tsx +123 -124
  75. package/src/SearchField.tsx +1 -3
  76. package/src/Select.tsx +13 -2
  77. package/src/Separator.tsx +3 -3
  78. package/src/SharedElementTransition.tsx +64 -67
  79. package/src/Text.tsx +5 -4
  80. package/src/TimeField.tsx +3 -10
  81. package/src/VisuallyHidden.tsx +8 -12
  82. package/src/index.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proyecto-viviana/solidaria-components",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
4
4
  "description": "Pre-wired headless components for SolidJS - port of react-aria-components",
5
5
  "keywords": [
6
6
  "accessibility",
@@ -10,6 +10,8 @@
10
10
  "solid",
11
11
  "solidjs"
12
12
  ],
13
+ "homepage": "https://ui.proyectoviviana.org/solid-spectrum/ecosystem",
14
+ "bugs": "https://github.com/proyecto-viviana/ui/issues",
13
15
  "license": "MIT",
14
16
  "repository": {
15
17
  "type": "git",
@@ -429,8 +431,8 @@
429
431
  },
430
432
  "dependencies": {
431
433
  "@internationalized/date": "^3.8.0",
432
- "@proyecto-viviana/solid-stately": "0.4.1",
433
- "@proyecto-viviana/solidaria": "0.4.1"
434
+ "@proyecto-viviana/solid-stately": "0.5.1",
435
+ "@proyecto-viviana/solidaria": "0.4.3"
434
436
  },
435
437
  "devDependencies": {
436
438
  "solid-js": "^1.9.11"
@@ -17,7 +17,7 @@ import {
17
17
  For,
18
18
  Show,
19
19
  } from "solid-js";
20
- import { Dynamic } from "solid-js/web";
20
+ import { ElementTag } from "./ElementTag";
21
21
  import {
22
22
  createBreadcrumbs,
23
23
  createBreadcrumbItem,
@@ -219,9 +219,7 @@ export function Breadcrumbs<T>(props: BreadcrumbsProps<T>): JSX.Element {
219
219
  >
220
220
  <Show
221
221
  when={hasCollectionItems()}
222
- fallback={
223
- <StaticBreadcrumbItems>{local.children as JSX.Element}</StaticBreadcrumbItems>
224
- }
222
+ fallback={<StaticBreadcrumbItems>{local.children as JSX.Element}</StaticBreadcrumbItems>}
225
223
  >
226
224
  <For each={items()}>
227
225
  {(item, index) => {
@@ -415,8 +413,8 @@ export function BreadcrumbItem(props: BreadcrumbItemProps): JSX.Element {
415
413
  };
416
414
 
417
415
  return (
418
- <Dynamic
419
- component={elementType()}
416
+ <ElementTag
417
+ tag={elementType()}
420
418
  {...mergedItemProps()}
421
419
  ref={(element: HTMLElement) => assignRef(local.ref, element)}
422
420
  aria-current={isCurrent() ? (ariaProps["aria-current"] ?? "page") : undefined}
@@ -431,7 +429,7 @@ export function BreadcrumbItem(props: BreadcrumbItemProps): JSX.Element {
431
429
  data-hovered={isHovered() || undefined}
432
430
  >
433
431
  {renderProps.renderChildren()}
434
- </Dynamic>
432
+ </ElementTag>
435
433
  );
436
434
  }
437
435
 
package/src/DateField.tsx CHANGED
@@ -305,8 +305,7 @@ function DateFieldInner<T extends DateValue = CalendarDate>(
305
305
  // onto the <Input> via InputContext so useFormReset/useFormValidation target
306
306
  // that element. We mirror that: create it here, hand it to createDateField
307
307
  // below, and attach it to the same input through the context getter.
308
- const [validationInputRef, setValidationInputRef] =
309
- createSignal<HTMLInputElement | null>(null);
308
+ const [validationInputRef, setValidationInputRef] = createSignal<HTMLInputElement | null>(null);
310
309
 
311
310
  const state = createDateFieldState(stateProps);
312
311
 
@@ -474,9 +473,7 @@ function RootHiddenDateInput(props: RootHiddenDateInputProps): JSX.Element {
474
473
  const value = props.state.value();
475
474
  if (!value) return "";
476
475
  if (granularity() === "day") return toCalendarDate(value).toString();
477
- return toCalendarDateTime(
478
- "timeZone" in value ? toLocalTimeZone(value) : value,
479
- ).toString();
476
+ return toCalendarDateTime("timeZone" in value ? toLocalTimeZone(value) : value).toString();
480
477
  };
481
478
 
482
479
  const onChange: JSX.EventHandler<HTMLInputElement, Event> = (event) => {
@@ -484,8 +481,9 @@ function RootHiddenDateInput(props: RootHiddenDateInputProps): JSX.Element {
484
481
  if (!targetString) return;
485
482
  try {
486
483
  const g = granularity();
487
- const targetValue = (g === "day" ? parseDate(targetString) : parseDateTime(targetString)) as
488
- unknown as Record<string, number>;
484
+ const targetValue = (g === "day"
485
+ ? parseDate(targetString)
486
+ : parseDateTime(targetString)) as unknown as Record<string, number>;
489
487
  let timeSegments = HIDDEN_TIME_SEGMENTS;
490
488
  if (HIDDEN_TIME_SEGMENTS.includes(g)) {
491
489
  timeSegments = HIDDEN_TIME_SEGMENTS.slice(0, HIDDEN_GRANULARITY_MAP[g]);
@@ -601,10 +601,10 @@ function DatePickerInner<T extends DateValue = CalendarDate>(
601
601
  >
602
602
  <CalendarContext.Provider value={calendarState as unknown as CalendarState<DateValue>}>
603
603
  {/* BARE ROLELESS root — mirrors RAC `DatePicker`'s outer `<div>`. The
604
- * presentation FieldGroup shell (rendered as a child via
605
- * `DatePickerFieldGroup`) is what carries `pickerAria.groupProps`
606
- * (role="presentation" + label/describedby + arrow-nav/press). A
607
- * described node here would be a spurious AX entry the S2 oracle lacks. */}
604
+ * presentation FieldGroup shell (rendered as a child via
605
+ * `DatePickerFieldGroup`) is what carries `pickerAria.groupProps`
606
+ * (role="presentation" + label/describedby + arrow-nav/press). A
607
+ * described node here would be a spurious AX entry the S2 oracle lacks. */}
608
608
  <div
609
609
  ref={setFieldRef}
610
610
  class={renderProps.class()}
@@ -960,12 +960,12 @@ function DateRangePickerInner<T extends DateValue = CalendarDate>(
960
960
  value={calendarState as unknown as RangeCalendarState<DateValue>}
961
961
  >
962
962
  {/* BARE ROLELESS root — mirrors RAC `DateRangePicker`'s outer `<div>`.
963
- * The presentation FieldGroup shell (rendered as a child via the styled
964
- * DateRangeDisplay) is what carries `pickerAria.groupProps` (role=
965
- * "presentation" + label/describedby + outer arrow-nav/press). A described
966
- * or role="group" node here would be a spurious AX entry the S2 oracle
967
- * lacks. The ref scopes the shared segment focus manager across both
968
- * fields (see createDateRangePicker). */}
963
+ * The presentation FieldGroup shell (rendered as a child via the styled
964
+ * DateRangeDisplay) is what carries `pickerAria.groupProps` (role=
965
+ * "presentation" + label/describedby + outer arrow-nav/press). A described
966
+ * or role="group" node here would be a spurious AX entry the S2 oracle
967
+ * lacks. The ref scopes the shared segment focus manager across both
968
+ * fields (see createDateRangePicker). */}
969
969
  <div
970
970
  ref={setFieldRef}
971
971
  class={renderProps.class()}
@@ -1409,14 +1409,14 @@ export function DatePickerContent(props: DatePickerContentProps): JSX.Element {
1409
1409
  <Portal mount={portalContainer()}>
1410
1410
  <FocusScope contain restoreFocus>
1411
1411
  {/* Un-folded to mirror S2's `<Popover>` DOM. The OUTER role-null `<div>`
1412
- * is the positioned surface that carries the enter motion + chrome —
1413
- * this is the element RAC/S2 animate (`AriaPopover`'s div: role null, no
1414
- * tabindex, accessible name computed from the calendar's textContent).
1415
- * A nested `role="dialog"` element (RAC's `Dialog`, which S2's
1416
- * CalendarPopover wraps the Calendar in) carries the dialog semantics.
1417
- * Folding both onto one `<section role="dialog">` made D2 animate the
1418
- * dialog node instead of the role-null div; the role-null wrapper is
1419
- * pruned from the AX tree so D5/D6 are unaffected. */}
1412
+ * is the positioned surface that carries the enter motion + chrome —
1413
+ * this is the element RAC/S2 animate (`AriaPopover`'s div: role null, no
1414
+ * tabindex, accessible name computed from the calendar's textContent).
1415
+ * A nested `role="dialog"` element (RAC's `Dialog`, which S2's
1416
+ * CalendarPopover wraps the Calendar in) carries the dialog semantics.
1417
+ * Folding both onto one `<section role="dialog">` made D2 animate the
1418
+ * dialog node instead of the role-null div; the role-null wrapper is
1419
+ * pruned from the AX tree so D5/D6 are unaffected. */}
1420
1420
  <div
1421
1421
  ref={setContentRef}
1422
1422
  {...cleanPopoverProps()}
@@ -1426,8 +1426,8 @@ export function DatePickerContent(props: DatePickerContentProps): JSX.Element {
1426
1426
  data-entering={dataAttr(isEntering())}
1427
1427
  >
1428
1428
  {/* RAC's `Dialog` renders a `<section role="dialog">` by default — the
1429
- * D5 roving trail records this element's tag, so it must be `section`
1430
- * (a `<div>` here regressed the focus-trail tag match). */}
1429
+ * D5 roving trail records this element's tag, so it must be `section`
1430
+ * (a `<div>` here regressed the focus-trail tag match). */}
1431
1431
  <section {...context.pickerAria.dialogProps} tabIndex={-1}>
1432
1432
  {props.children}
1433
1433
  </section>
@@ -1514,11 +1514,11 @@ export function DateRangePickerContent(props: DateRangePickerContentProps): JSX.
1514
1514
  <Portal mount={portalContainer()}>
1515
1515
  <FocusScope contain restoreFocus>
1516
1516
  {/* Un-folded to mirror S2's `<Popover>` DOM (see DatePickerContent).
1517
- * OUTER role-null `<div>` = positioned surface carrying the enter
1518
- * motion + chrome (the element RAC/S2 animate; pruned from the AX
1519
- * tree). Nested `<section role="dialog">` carries the dialog
1520
- * semantics — the D5 roving trail records this tag, so it must be
1521
- * `section`, not a folded `div`. */}
1517
+ * OUTER role-null `<div>` = positioned surface carrying the enter
1518
+ * motion + chrome (the element RAC/S2 animate; pruned from the AX
1519
+ * tree). Nested `<section role="dialog">` carries the dialog
1520
+ * semantics — the D5 roving trail records this tag, so it must be
1521
+ * `section`, not a folded `div`. */}
1522
1522
  <div
1523
1523
  ref={setContentRef}
1524
1524
  {...cleanPopoverProps()}
@@ -0,0 +1,116 @@
1
+ import { type JSX, createMemo, splitProps } from "solid-js";
2
+ import { Dynamic } from "solid-js/web";
3
+
4
+ export interface ElementTagProps extends Record<string, unknown> {
5
+ /** The HTML tag name to render. */
6
+ tag: string;
7
+ children?: JSX.Element;
8
+ }
9
+
10
+ /**
11
+ * Renders a string tag as a *statically compiled* element.
12
+ *
13
+ * A drop-in replacement for `<Dynamic component={someString}>`, which is unsafe
14
+ * anywhere that can render after hydration settles. Solid's `createDynamic`
15
+ * string branch calls `getNextElement()` with **no** `template` argument
16
+ * (solid-js 1.9.14, `web/dist/dev.js:771`). `getNextElement(template)` only
17
+ * reaches the registry while `isHydrating()` holds; once `sharedConfig.done` is
18
+ * set — hydration finished, but `sharedConfig.context` still live — it falls
19
+ * through to `return template()` and throws `TypeError: template is not a
20
+ * function`. Any deferred child lands on exactly that path: a `<Show>` that
21
+ * flips, a Suspense resolution, a portal, a container that defers its children.
22
+ *
23
+ * Each branch below is real JSX, so the compiler emits a `template()` of its own
24
+ * and hands it to `getNextElement(template)` — which adopts the SSR node from the
25
+ * registry during hydration and clones the template afterwards. Both phases work.
26
+ *
27
+ * Props other than `tag` are spread through untouched, `children` included, so
28
+ * this matches `<Dynamic>`'s behaviour exactly (`Dynamic` likewise splits only
29
+ * `component` and spreads the rest onto the element).
30
+ */
31
+ export function ElementTag(props: ElementTagProps): JSX.Element {
32
+ const [local, rest] = splitProps(props, ["tag"]);
33
+ // Two memos, layered exactly as `<Dynamic>` layers them, and for the same reason.
34
+ // Reading a single key off a merged-props proxy forces the dynamic spread behind
35
+ // it to evaluate (to settle key precedence), which subscribes the reader to every
36
+ // signal that spread touches. Absorbing that read in `cached` keeps it off the memo
37
+ // below: `cached` re-runs freely, but its *value* only moves when the tag really
38
+ // changes, so the element is rebuilt only then. Inline the read into the memo below
39
+ // instead and any prop change recreates the DOM node — blowing away focus, and
40
+ // looping where an ancestor counts the children it just rendered.
41
+ const cached = createMemo(() => local.tag);
42
+ return createMemo(() => {
43
+ switch (cached()) {
44
+ // Text / inline
45
+ case "span":
46
+ return <span {...rest} />;
47
+ case "a":
48
+ return <a {...rest} />;
49
+ case "p":
50
+ return <p {...rest} />;
51
+ case "label":
52
+ return <label {...rest} />;
53
+ case "strong":
54
+ return <strong {...rest} />;
55
+ case "em":
56
+ return <em {...rest} />;
57
+ case "small":
58
+ return <small {...rest} />;
59
+ // Flow
60
+ case "div":
61
+ return <div {...rest} />;
62
+ case "hr":
63
+ return <hr {...rest} />;
64
+ case "li":
65
+ return <li {...rest} />;
66
+ case "ul":
67
+ return <ul {...rest} />;
68
+ case "ol":
69
+ return <ol {...rest} />;
70
+ case "figure":
71
+ return <figure {...rest} />;
72
+ case "blockquote":
73
+ return <blockquote {...rest} />;
74
+ case "address":
75
+ return <address {...rest} />;
76
+ // Headings
77
+ case "h1":
78
+ return <h1 {...rest} />;
79
+ case "h2":
80
+ return <h2 {...rest} />;
81
+ case "h3":
82
+ return <h3 {...rest} />;
83
+ case "h4":
84
+ return <h4 {...rest} />;
85
+ case "h5":
86
+ return <h5 {...rest} />;
87
+ case "h6":
88
+ return <h6 {...rest} />;
89
+ // Landmarks
90
+ case "main":
91
+ return <main {...rest} />;
92
+ case "nav":
93
+ return <nav {...rest} />;
94
+ case "header":
95
+ return <header {...rest} />;
96
+ case "footer":
97
+ return <footer {...rest} />;
98
+ case "aside":
99
+ return <aside {...rest} />;
100
+ case "section":
101
+ return <section {...rest} />;
102
+ case "article":
103
+ return <article {...rest} />;
104
+ case "form":
105
+ return <form {...rest} />;
106
+ case "search":
107
+ return <search {...rest} />;
108
+ default:
109
+ // A tag no component in this package emits. Kept on `<Dynamic>` rather
110
+ // than hand-built so hydration still adopts the SSR node; it inherits
111
+ // the post-hydration caveat above, which is why every tag the library
112
+ // itself renders is listed explicitly.
113
+ return <Dynamic component={local.tag} {...rest} />;
114
+ }
115
+ }) as unknown as JSX.Element;
116
+ }
package/src/Form.tsx CHANGED
@@ -121,6 +121,23 @@ export function Form(props: FormProps): JSX.Element {
121
121
 
122
122
  const filteredDomProps = filterFormDOMProps(domProps);
123
123
 
124
+ // FormContext is only consumed for `validationBehavior` (TextField, Checkbox,
125
+ // DateField, …). Never spread `props` into the context value: in Solid,
126
+ // `props.children` is a getter that *creates* the child tree when read. Eager
127
+ // `{...props}` therefore builds every child once for the discarded context
128
+ // object and again for `renderChildren()`, double-advancing createUniqueId
129
+ // and desyncing SSR hydration keys. RAC can spread props because React
130
+ // elements are already-built descriptors; Solid cannot.
131
+ //
132
+ // Surface symptom (effect-latam /perfil, /foros): Form+sole Spectrum Button
133
+ // → "Hydration Mismatch" / "template is not a function" and a blank route.
134
+ // Form+TextField (multiple children) often still hydrates because array/
135
+ // fragment child paths mask the slot drift — the double-create is still
136
+ // wrong either way.
137
+ const formContextValue: FormProps = {
138
+ validationBehavior,
139
+ };
140
+
124
141
  return (
125
142
  <form
126
143
  {...filteredDomProps}
@@ -130,7 +147,7 @@ export function Form(props: FormProps): JSX.Element {
130
147
  style={renderProps.style()}
131
148
  slot={local.slot}
132
149
  >
133
- <FormContext.Provider value={{ ...props, validationBehavior }}>
150
+ <FormContext.Provider value={formContextValue}>
134
151
  <FormValidationContext.Provider value={errors}>
135
152
  {renderProps.renderChildren()}
136
153
  </FormValidationContext.Provider>
package/src/Landmark.tsx CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { type JSX, createContext, createMemo, createSignal, splitProps } from "solid-js";
9
- import { Dynamic } from "solid-js/web";
9
+ import { ElementTag } from "./ElementTag";
10
10
  import {
11
11
  createLandmark,
12
12
  getLandmarkController,
@@ -147,8 +147,8 @@ export function Landmark(props: LandmarkProps): JSX.Element {
147
147
  const domProps = createMemo(() => filterDOMProps(ariaProps, { global: true }));
148
148
 
149
149
  return (
150
- <Dynamic
151
- component={elementType()}
150
+ <ElementTag
151
+ tag={elementType()}
152
152
  ref={setRef}
153
153
  {...domProps()}
154
154
  {...landmarkAria.landmarkProps}
@@ -157,7 +157,7 @@ export function Landmark(props: LandmarkProps): JSX.Element {
157
157
  slot={local.slot}
158
158
  >
159
159
  {props.children}
160
- </Dynamic>
160
+ </ElementTag>
161
161
  );
162
162
  }
163
163
 
package/src/Link.tsx CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { type JSX, type ParentProps, createContext, createMemo, splitProps } from "solid-js";
9
- import { Dynamic } from "solid-js/web";
9
+ import { ElementTag } from "./ElementTag";
10
10
  import {
11
11
  createLink,
12
12
  createFocusRing,
@@ -241,8 +241,8 @@ export function Link(props: ParentProps<LinkProps>): JSX.Element {
241
241
  };
242
242
 
243
243
  return (
244
- <Dynamic
245
- component={elementType()}
244
+ <ElementTag
245
+ tag={elementType()}
246
246
  {...domProps()}
247
247
  {...cleanLinkProps()}
248
248
  {...cleanHoverProps()}
@@ -261,6 +261,6 @@ export function Link(props: ParentProps<LinkProps>): JSX.Element {
261
261
  data-disabled={ariaProps.isDisabled || undefined}
262
262
  >
263
263
  {renderProps.renderChildren()}
264
- </Dynamic>
264
+ </ElementTag>
265
265
  );
266
266
  }