@proyecto-viviana/solidaria-components 0.2.5 → 0.2.9

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 (194) hide show
  1. package/LICENSE +21 -0
  2. package/dist/ActionBar.d.ts +71 -0
  3. package/dist/ActionBar.d.ts.map +1 -0
  4. package/dist/ActionGroup.d.ts +74 -0
  5. package/dist/ActionGroup.d.ts.map +1 -0
  6. package/dist/Alert.d.ts +70 -0
  7. package/dist/Alert.d.ts.map +1 -0
  8. package/dist/Breadcrumbs.d.ts +10 -2
  9. package/dist/Breadcrumbs.d.ts.map +1 -1
  10. package/dist/Button.d.ts +4 -0
  11. package/dist/Button.d.ts.map +1 -1
  12. package/dist/Calendar.d.ts +13 -0
  13. package/dist/Calendar.d.ts.map +1 -1
  14. package/dist/Checkbox.d.ts +2 -2
  15. package/dist/Checkbox.d.ts.map +1 -1
  16. package/dist/Collection.d.ts +125 -0
  17. package/dist/Collection.d.ts.map +1 -0
  18. package/dist/Color.d.ts +114 -2
  19. package/dist/Color.d.ts.map +1 -1
  20. package/dist/ColorEditor.d.ts +42 -0
  21. package/dist/ColorEditor.d.ts.map +1 -0
  22. package/dist/ComboBox.d.ts +64 -0
  23. package/dist/ComboBox.d.ts.map +1 -1
  24. package/dist/ContextualHelpTrigger.d.ts +40 -0
  25. package/dist/ContextualHelpTrigger.d.ts.map +1 -0
  26. package/dist/DateField.d.ts +27 -2
  27. package/dist/DateField.d.ts.map +1 -1
  28. package/dist/DatePicker.d.ts +67 -2
  29. package/dist/DatePicker.d.ts.map +1 -1
  30. package/dist/Dialog.d.ts.map +1 -1
  31. package/dist/Disclosure.d.ts +2 -0
  32. package/dist/Disclosure.d.ts.map +1 -1
  33. package/dist/DragAndDrop.d.ts +80 -0
  34. package/dist/DragAndDrop.d.ts.map +1 -0
  35. package/dist/DragPreview.d.ts +14 -0
  36. package/dist/DragPreview.d.ts.map +1 -0
  37. package/dist/DropZone.d.ts +27 -0
  38. package/dist/DropZone.d.ts.map +1 -0
  39. package/dist/FieldError.d.ts +23 -0
  40. package/dist/FieldError.d.ts.map +1 -0
  41. package/dist/FileTrigger.d.ts +26 -0
  42. package/dist/FileTrigger.d.ts.map +1 -0
  43. package/dist/Focusable.d.ts +27 -0
  44. package/dist/Focusable.d.ts.map +1 -0
  45. package/dist/Form.d.ts +27 -0
  46. package/dist/Form.d.ts.map +1 -0
  47. package/dist/GridList.d.ts +40 -1
  48. package/dist/GridList.d.ts.map +1 -1
  49. package/dist/Icon.d.ts +57 -0
  50. package/dist/Icon.d.ts.map +1 -0
  51. package/dist/Keyboard.d.ts +13 -0
  52. package/dist/Keyboard.d.ts.map +1 -0
  53. package/dist/Link.d.ts.map +1 -1
  54. package/dist/ListBox.d.ts +43 -1
  55. package/dist/ListBox.d.ts.map +1 -1
  56. package/dist/ListDropTargetDelegate.d.ts +38 -0
  57. package/dist/ListDropTargetDelegate.d.ts.map +1 -0
  58. package/dist/Menu.d.ts +20 -2
  59. package/dist/Menu.d.ts.map +1 -1
  60. package/dist/Meter.d.ts +2 -2
  61. package/dist/Meter.d.ts.map +1 -1
  62. package/dist/Modal.d.ts +2 -0
  63. package/dist/Modal.d.ts.map +1 -1
  64. package/dist/NumberField.d.ts +2 -0
  65. package/dist/NumberField.d.ts.map +1 -1
  66. package/dist/Popover.d.ts +4 -2
  67. package/dist/Popover.d.ts.map +1 -1
  68. package/dist/Pressable.d.ts +27 -0
  69. package/dist/Pressable.d.ts.map +1 -0
  70. package/dist/ProgressBar.d.ts +2 -2
  71. package/dist/ProgressBar.d.ts.map +1 -1
  72. package/dist/RadioGroup.d.ts.map +1 -1
  73. package/dist/RangeCalendar.d.ts +5 -0
  74. package/dist/RangeCalendar.d.ts.map +1 -1
  75. package/dist/RouterProvider.d.ts +75 -0
  76. package/dist/RouterProvider.d.ts.map +1 -0
  77. package/dist/SearchField.d.ts +2 -3
  78. package/dist/SearchField.d.ts.map +1 -1
  79. package/dist/Select.d.ts +11 -0
  80. package/dist/Select.d.ts.map +1 -1
  81. package/dist/SelectionIndicator.d.ts +30 -0
  82. package/dist/SelectionIndicator.d.ts.map +1 -0
  83. package/dist/SharedElementTransition.d.ts +39 -0
  84. package/dist/SharedElementTransition.d.ts.map +1 -0
  85. package/dist/Slider.d.ts +6 -3
  86. package/dist/Slider.d.ts.map +1 -1
  87. package/dist/Table.d.ts +39 -0
  88. package/dist/Table.d.ts.map +1 -1
  89. package/dist/Tabs.d.ts +4 -3
  90. package/dist/Tabs.d.ts.map +1 -1
  91. package/dist/TagGroup.d.ts +12 -2
  92. package/dist/TagGroup.d.ts.map +1 -1
  93. package/dist/Text.d.ts +10 -0
  94. package/dist/Text.d.ts.map +1 -0
  95. package/dist/TextField.d.ts +4 -0
  96. package/dist/TextField.d.ts.map +1 -1
  97. package/dist/TimeField.d.ts +26 -1
  98. package/dist/TimeField.d.ts.map +1 -1
  99. package/dist/Toast.d.ts.map +1 -1
  100. package/dist/ToggleButton.d.ts +30 -0
  101. package/dist/ToggleButton.d.ts.map +1 -0
  102. package/dist/ToggleButtonGroup.d.ts +33 -0
  103. package/dist/ToggleButtonGroup.d.ts.map +1 -0
  104. package/dist/Toolbar.d.ts.map +1 -1
  105. package/dist/Tooltip.d.ts +9 -0
  106. package/dist/Tooltip.d.ts.map +1 -1
  107. package/dist/Tree.d.ts +44 -2
  108. package/dist/Tree.d.ts.map +1 -1
  109. package/dist/Virtualizer.d.ts +61 -0
  110. package/dist/Virtualizer.d.ts.map +1 -0
  111. package/dist/VirtualizerLayouts.d.ts +82 -0
  112. package/dist/VirtualizerLayouts.d.ts.map +1 -0
  113. package/dist/VisuallyHidden.d.ts +3 -1
  114. package/dist/VisuallyHidden.d.ts.map +1 -1
  115. package/dist/contexts.d.ts +1 -0
  116. package/dist/contexts.d.ts.map +1 -1
  117. package/dist/index.d.ts +57 -25
  118. package/dist/index.d.ts.map +1 -1
  119. package/dist/index.js +13961 -5946
  120. package/dist/index.js.map +1 -7
  121. package/dist/index.ssr.js +9612 -2401
  122. package/dist/index.ssr.js.map +1 -7
  123. package/dist/useDragAndDrop.d.ts +93 -0
  124. package/dist/useDragAndDrop.d.ts.map +1 -0
  125. package/dist/utils.d.ts +7 -1
  126. package/dist/utils.d.ts.map +1 -1
  127. package/dist/virtualizer/Layout.d.ts +79 -0
  128. package/dist/virtualizer/Layout.d.ts.map +1 -0
  129. package/package.json +8 -6
  130. package/src/ActionBar.tsx +248 -0
  131. package/src/ActionGroup.tsx +285 -0
  132. package/src/Alert.tsx +177 -0
  133. package/src/Autocomplete.tsx +1 -1
  134. package/src/Breadcrumbs.tsx +103 -17
  135. package/src/Button.tsx +65 -21
  136. package/src/Calendar.tsx +179 -53
  137. package/src/Checkbox.tsx +1 -2
  138. package/src/Collection.tsx +341 -0
  139. package/src/Color.tsx +652 -34
  140. package/src/ColorEditor.tsx +231 -0
  141. package/src/ComboBox.tsx +315 -81
  142. package/src/ContextualHelpTrigger.tsx +183 -0
  143. package/src/DateField.tsx +93 -19
  144. package/src/DatePicker.tsx +495 -25
  145. package/src/Dialog.tsx +40 -9
  146. package/src/Disclosure.tsx +33 -27
  147. package/src/DragAndDrop.tsx +334 -0
  148. package/src/DragPreview.tsx +45 -0
  149. package/src/DropZone.tsx +213 -0
  150. package/src/FieldError.tsx +67 -0
  151. package/src/FileTrigger.tsx +83 -0
  152. package/src/Focusable.tsx +106 -0
  153. package/src/Form.tsx +85 -0
  154. package/src/GridList.tsx +379 -41
  155. package/src/Icon.tsx +154 -0
  156. package/src/Keyboard.tsx +26 -0
  157. package/src/Link.tsx +14 -1
  158. package/src/ListBox.tsx +484 -33
  159. package/src/ListDropTargetDelegate.ts +282 -0
  160. package/src/Menu.tsx +388 -35
  161. package/src/Meter.tsx +7 -3
  162. package/src/Modal.tsx +32 -4
  163. package/src/NumberField.tsx +163 -43
  164. package/src/Popover.tsx +136 -180
  165. package/src/Pressable.tsx +108 -0
  166. package/src/ProgressBar.tsx +7 -3
  167. package/src/RadioGroup.tsx +35 -25
  168. package/src/RangeCalendar.tsx +100 -68
  169. package/src/RouterProvider.tsx +240 -0
  170. package/src/SearchField.tsx +142 -34
  171. package/src/Select.tsx +221 -73
  172. package/src/SelectionIndicator.tsx +105 -0
  173. package/src/SharedElementTransition.tsx +258 -0
  174. package/src/Slider.tsx +16 -6
  175. package/src/Table.tsx +417 -57
  176. package/src/Tabs.tsx +68 -35
  177. package/src/TagGroup.tsx +121 -36
  178. package/src/Text.tsx +18 -0
  179. package/src/TextField.tsx +25 -8
  180. package/src/TimeField.tsx +101 -151
  181. package/src/Toast.tsx +108 -14
  182. package/src/ToggleButton.tsx +159 -0
  183. package/src/ToggleButtonGroup.tsx +136 -0
  184. package/src/Toolbar.tsx +14 -8
  185. package/src/Tooltip.tsx +108 -19
  186. package/src/Tree.tsx +1143 -87
  187. package/src/Virtualizer.tsx +702 -0
  188. package/src/VirtualizerLayouts.ts +265 -0
  189. package/src/VisuallyHidden.tsx +15 -21
  190. package/src/contexts.ts +1 -0
  191. package/src/index.ts +1057 -620
  192. package/src/useDragAndDrop.ts +351 -0
  193. package/src/utils.tsx +37 -3
  194. package/src/virtualizer/Layout.ts +200 -0
package/src/Tabs.tsx CHANGED
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import {
9
+ type Accessor,
9
10
  type JSX,
10
11
  createContext,
11
12
  createMemo,
@@ -39,6 +40,19 @@ import {
39
40
  useRenderProps,
40
41
  filterDOMProps,
41
42
  } from './utils';
43
+ import {
44
+ SelectionIndicator,
45
+ SelectionIndicatorContext,
46
+ type SelectionIndicatorContextValue,
47
+ } from './SelectionIndicator';
48
+ import { SharedElementTransition } from './SharedElementTransition';
49
+
50
+ export {
51
+ SelectionIndicator,
52
+ SelectionIndicatorContext,
53
+ type SelectionIndicatorProps,
54
+ type SelectionIndicatorRenderProps,
55
+ } from './SelectionIndicator';
42
56
 
43
57
  // ============================================
44
58
  // TYPES
@@ -154,7 +168,7 @@ export interface TabPanelProps extends AriaTabPanelProps, SlotProps {
154
168
 
155
169
  interface TabsContextValue<T> {
156
170
  state: TabListState<T>;
157
- items: T[];
171
+ items: Accessor<T[]>;
158
172
  }
159
173
 
160
174
  export const TabsContext = createContext<TabsContextValue<unknown> | null>(null);
@@ -232,7 +246,7 @@ export function Tabs<T>(props: TabsProps<T>): JSX.Element {
232
246
  const domProps = createMemo(() => filterDOMProps(rest as Record<string, unknown>, { global: true }));
233
247
 
234
248
  return (
235
- <TabsContext.Provider value={{ state, items: stateProps.items ?? [] }}>
249
+ <TabsContext.Provider value={{ state, items: () => stateProps.items ?? [] }}>
236
250
  <TabsStateContext.Provider value={state}>
237
251
  <div
238
252
  {...domProps()}
@@ -286,7 +300,7 @@ function TabListInner<T>(props: {
286
300
  children?: (item: T) => JSX.Element;
287
301
  }): JSX.Element {
288
302
  const state = props.context.state as TabListState<T>;
289
- const items = props.context.items as T[];
303
+ const items = props.context.items as Accessor<T[]>;
290
304
 
291
305
  // Create tab list aria props
292
306
  const { tabListProps } = createTabList<T>(props.ariaProps as AriaTabListProps, state);
@@ -314,14 +328,20 @@ function TabListInner<T>(props: {
314
328
 
315
329
  // Helper to safely call event handlers that may be bound tuples
316
330
  const callHandler = <E extends Event>(
317
- handler: ((e: E) => void) | [object, (e: E) => void] | undefined,
331
+ handler: JSX.EventHandlerUnion<HTMLElement, E> | undefined,
318
332
  event: E
319
333
  ) => {
320
334
  if (!handler) return;
321
335
  if (Array.isArray(handler)) {
322
336
  handler[1].call(handler[0], event);
323
- } else {
324
- handler(event);
337
+ return;
338
+ }
339
+ if (typeof handler === 'function') {
340
+ (handler as (evt: E) => void)(event);
341
+ return;
342
+ }
343
+ if (typeof handler === 'object' && 'handleEvent' in handler && typeof handler.handleEvent === 'function') {
344
+ (handler.handleEvent as (evt: E) => void)(event);
325
345
  }
326
346
  };
327
347
 
@@ -332,12 +352,12 @@ function TabListInner<T>(props: {
332
352
 
333
353
  const handleFocus = (e: FocusEvent) => {
334
354
  tabListProps.onFocus(e);
335
- callHandler(focusProps.onFocus as any, e);
355
+ callHandler(focusProps.onFocus, e);
336
356
  };
337
357
 
338
358
  const handleBlur = (e: FocusEvent) => {
339
359
  tabListProps.onBlur(e);
340
- callHandler(focusProps.onBlur as any, e);
360
+ callHandler(focusProps.onBlur, e);
341
361
  };
342
362
 
343
363
  return (
@@ -357,7 +377,9 @@ function TabListInner<T>(props: {
357
377
  data-orientation={state.orientation()}
358
378
  data-disabled={state.isDisabled() || undefined}
359
379
  >
360
- <For each={items}>{(item) => props.children?.(item)}</For>
380
+ <SharedElementTransition>
381
+ <For each={items()}>{(item) => props.children?.(item)}</For>
382
+ </SharedElementTransition>
361
383
  </div>
362
384
  );
363
385
  }
@@ -400,6 +422,8 @@ function TabInner(props: {
400
422
  ariaProps: Omit<TabProps, 'children' | 'class' | 'style' | 'slot' | 'id'>;
401
423
  children?: RenderChildren<TabRenderProps>;
402
424
  }): JSX.Element {
425
+ let tabRef: HTMLDivElement | undefined;
426
+
403
427
  // Create tab aria props
404
428
  const tabAria = createTab<unknown>(
405
429
  {
@@ -411,7 +435,8 @@ function TabInner(props: {
411
435
  return props.ariaProps['aria-label'];
412
436
  },
413
437
  },
414
- props.state
438
+ props.state,
439
+ () => tabRef ?? null
415
440
  );
416
441
 
417
442
  // Create hover
@@ -442,32 +467,39 @@ function TabInner(props: {
442
467
  renderValues
443
468
  );
444
469
 
470
+ const selectionIndicatorContext = createMemo<SelectionIndicatorContextValue>(() => ({
471
+ isSelected: tabAria.isSelected,
472
+ }));
473
+
445
474
  return (
446
- <div
447
- id={tabAria.tabProps.id}
448
- role={tabAria.tabProps.role}
449
- aria-selected={tabAria.isSelected()}
450
- aria-disabled={tabAria.isDisabled() || undefined}
451
- aria-controls={tabAria.isSelected() ? tabAria.tabProps['aria-controls'] : undefined}
452
- aria-label={tabAria.tabProps['aria-label']}
453
- tabIndex={tabAria.isSelected() && !tabAria.isDisabled() ? 0 : -1}
454
- class={renderProps.class()}
455
- style={renderProps.style()}
456
- onKeyDown={tabAria.tabProps.onKeyDown}
457
- onMouseDown={tabAria.tabProps.onMouseDown}
458
- onPointerDown={tabAria.tabProps.onPointerDown}
459
- onClick={tabAria.tabProps.onClick}
460
- onFocus={tabAria.tabProps.onFocus}
461
- {...hoverProps}
462
- data-selected={tabAria.isSelected() || undefined}
463
- data-focused={tabAria.isFocused() || undefined}
464
- data-focus-visible={tabAria.isFocusVisible() || undefined}
465
- data-pressed={tabAria.isPressed() || undefined}
466
- data-hovered={isHovered() || undefined}
467
- data-disabled={tabAria.isDisabled() || undefined}
468
- >
469
- {renderProps.renderChildren()}
470
- </div>
475
+ <SelectionIndicatorContext.Provider value={selectionIndicatorContext()}>
476
+ <div
477
+ ref={tabRef}
478
+ id={tabAria.tabProps.id}
479
+ role={tabAria.tabProps.role}
480
+ aria-selected={tabAria.isSelected()}
481
+ aria-disabled={tabAria.isDisabled() || undefined}
482
+ aria-controls={tabAria.isSelected() ? tabAria.tabProps['aria-controls'] : undefined}
483
+ aria-label={tabAria.tabProps['aria-label']}
484
+ tabIndex={tabAria.isSelected() && !tabAria.isDisabled() ? 0 : -1}
485
+ class={renderProps.class()}
486
+ style={renderProps.style()}
487
+ onKeyDown={tabAria.tabProps.onKeyDown}
488
+ onMouseDown={tabAria.tabProps.onMouseDown}
489
+ onPointerDown={tabAria.tabProps.onPointerDown}
490
+ onClick={tabAria.tabProps.onClick}
491
+ onFocus={tabAria.tabProps.onFocus}
492
+ {...hoverProps}
493
+ data-selected={tabAria.isSelected() || undefined}
494
+ data-focused={tabAria.isFocused() || undefined}
495
+ data-focus-visible={tabAria.isFocusVisible() || undefined}
496
+ data-pressed={tabAria.isPressed() || undefined}
497
+ data-hovered={isHovered() || undefined}
498
+ data-disabled={tabAria.isDisabled() || undefined}
499
+ >
500
+ {renderProps.renderChildren()}
501
+ </div>
502
+ </SelectionIndicatorContext.Provider>
471
503
  );
472
504
  }
473
505
 
@@ -550,3 +582,4 @@ export function TabPanel(props: TabPanelProps): JSX.Element {
550
582
  Tabs.List = TabList;
551
583
  Tabs.Tab = Tab;
552
584
  Tabs.Panel = TabPanel;
585
+ Tabs.SelectionIndicator = SelectionIndicator;
package/src/TagGroup.tsx CHANGED
@@ -39,6 +39,11 @@ import {
39
39
  filterDOMProps,
40
40
  dataAttr,
41
41
  } from './utils';
42
+ import { SharedElementTransition } from './SharedElementTransition';
43
+ import {
44
+ SelectionIndicatorContext,
45
+ type SelectionIndicatorContextValue,
46
+ } from './SelectionIndicator';
42
47
 
43
48
  // ============================================
44
49
  // TYPES
@@ -53,7 +58,8 @@ export interface TagGroupRenderProps {
53
58
 
54
59
  export interface TagGroupProps
55
60
  extends Omit<AriaTagGroupProps, 'id'>,
56
- SlotProps {
61
+ SlotProps,
62
+ Omit<JSX.HTMLAttributes<HTMLDivElement>, 'class' | 'style' | 'children'> {
57
63
  /** The children of the component. */
58
64
  children?: JSX.Element;
59
65
  /** The CSS className for the element. */
@@ -69,7 +75,7 @@ export interface TagListRenderProps {
69
75
  isFocused: boolean;
70
76
  }
71
77
 
72
- export interface TagListProps<T> extends SlotProps {
78
+ export interface TagListProps<T> extends SlotProps, Omit<JSX.HTMLAttributes<HTMLDivElement>, 'class' | 'style' | 'children' | 'onSelectionChange'> {
73
79
  /** The items to display in the tag list. */
74
80
  items: T[];
75
81
  /** Function to render each item. */
@@ -121,6 +127,8 @@ export interface TagRenderProps {
121
127
  allowsRemoving: boolean;
122
128
  /** The selection mode. */
123
129
  selectionMode: SelectionMode;
130
+ /** Props for the remove button when removal is allowed. */
131
+ removeButtonProps: Record<string, unknown>;
124
132
  }
125
133
 
126
134
  export interface TagProps extends SlotProps {
@@ -145,10 +153,17 @@ export interface TagProps extends SlotProps {
145
153
  interface TagGroupContextValue {
146
154
  state: ListState;
147
155
  onRemove?: (keys: Set<Key>) => void;
156
+ isDisabled?: boolean;
157
+ }
158
+
159
+ interface TagContextValue {
160
+ removeButtonProps: Record<string, unknown>;
161
+ allowsRemoving: boolean;
148
162
  }
149
163
 
150
164
  export const TagGroupContext = createContext<TagGroupContextValue | null>(null);
151
165
  export const TagListStateContext = createContext<ListState | null>(null);
166
+ export const TagContext = createContext<TagContextValue | null>(null);
152
167
 
153
168
  export function useTagGroupContext(): TagGroupContextValue | null {
154
169
  return useContext(TagGroupContext);
@@ -172,15 +187,17 @@ export function useTagGroupContext(): TagGroupContextValue | null {
172
187
  * ```
173
188
  */
174
189
  export function TagGroup(props: TagGroupProps): JSX.Element {
175
- const [local] = splitProps(props, [
190
+ const [local, domProps] = splitProps(props, [
176
191
  'class',
177
192
  'style',
178
193
  'slot',
194
+ 'children',
179
195
  ]);
180
196
 
181
197
  // We need TagList to provide the state, so TagGroup just provides context
182
198
  return (
183
199
  <div
200
+ {...domProps}
184
201
  class={typeof local.class === 'string' ? local.class : 'solidaria-TagGroup'}
185
202
  style={typeof local.style === 'object' ? local.style : undefined}
186
203
  slot={local.slot}
@@ -198,12 +215,13 @@ export function TagGroup(props: TagGroupProps): JSX.Element {
198
215
  * TagList contains the list of tags within a TagGroup.
199
216
  */
200
217
  export function TagList<T extends { id?: Key; key?: Key }>(props: TagListProps<T>): JSX.Element {
201
- const [local] = splitProps(props, [
218
+ const [local, domProps] = splitProps(props, [
202
219
  'items',
203
220
  'class',
204
221
  'style',
205
222
  'slot',
206
223
  'renderEmptyState',
224
+ 'children',
207
225
  'selectionMode',
208
226
  'selectionBehavior',
209
227
  'selectedKeys',
@@ -245,8 +263,7 @@ export function TagList<T extends { id?: Key; key?: Key }>(props: TagListProps<T
245
263
  // Create tag group accessibility props
246
264
  const tagGroupAria = createTagGroup(
247
265
  {
248
- get label() { return local.label; },
249
- get 'aria-label'() { return local['aria-label']; },
266
+ get 'aria-label'() { return local['aria-label'] ?? (!local['aria-labelledby'] ? local.label : undefined); },
250
267
  get 'aria-labelledby'() { return local['aria-labelledby']; },
251
268
  get 'aria-describedby'() { return local['aria-describedby']; },
252
269
  get isDisabled() { return local.isDisabled; },
@@ -279,6 +296,7 @@ export function TagList<T extends { id?: Key; key?: Key }>(props: TagListProps<T
279
296
  const contextValue: TagGroupContextValue = {
280
297
  state,
281
298
  get onRemove() { return local.onRemove; },
299
+ get isDisabled() { return local.isDisabled; },
282
300
  };
283
301
 
284
302
  return (
@@ -286,22 +304,36 @@ export function TagList<T extends { id?: Key; key?: Key }>(props: TagListProps<T
286
304
  <TagListStateContext.Provider value={state}>
287
305
  <div
288
306
  ref={setGridRef}
307
+ {...domProps}
289
308
  {...tagGroupAria.gridProps}
290
309
  class={renderProps.class()}
291
310
  style={renderProps.style()}
292
- onFocus={() => setIsFocused(true)}
293
- onBlur={() => setIsFocused(false)}
311
+ onFocus={() => {
312
+ setIsFocused(true);
313
+ state.setFocused(true);
314
+ }}
315
+ onBlur={(e) => {
316
+ const nextTarget = e.relatedTarget as Node | null;
317
+ if (nextTarget && e.currentTarget.contains(nextTarget)) {
318
+ return;
319
+ }
320
+
321
+ setIsFocused(false);
322
+ state.setFocused(false);
323
+ }}
294
324
  data-empty={dataAttr(local.items.length === 0)}
295
325
  data-focused={dataAttr(isFocused())}
296
326
  >
297
- <Show
298
- when={local.items.length > 0}
299
- fallback={local.renderEmptyState?.()}
300
- >
301
- <For each={local.items}>
302
- {(item) => props.children(item)}
303
- </For>
304
- </Show>
327
+ <SharedElementTransition>
328
+ <Show
329
+ when={local.items.length > 0}
330
+ fallback={local.renderEmptyState?.()}
331
+ >
332
+ <For each={local.items}>
333
+ {(item) => props.children(item)}
334
+ </For>
335
+ </Show>
336
+ </SharedElementTransition>
305
337
  </div>
306
338
  </TagListStateContext.Provider>
307
339
  </TagGroupContext.Provider>
@@ -326,6 +358,7 @@ export function Tag(props: TagProps): JSX.Element {
326
358
  ]);
327
359
 
328
360
  const state = useContext(TagListStateContext);
361
+ const groupContext = useContext(TagGroupContext);
329
362
 
330
363
  // Create a ref for the tag
331
364
  const [tagRef, setTagRef] = createSignal<HTMLDivElement | null>(null);
@@ -334,13 +367,28 @@ export function Tag(props: TagProps): JSX.Element {
334
367
  const tagAria = createTag(
335
368
  {
336
369
  get key() { return local.id; },
337
- get isDisabled() { return local.isDisabled; },
370
+ get isDisabled() { return local.isDisabled || groupContext?.isDisabled; },
338
371
  get textValue() { return local.textValue; },
339
372
  },
340
373
  state!,
341
374
  tagRef
342
375
  );
343
376
 
377
+ const normalizedRemoveButtonProps = createMemo<Record<string, unknown>>(() => {
378
+ const raw = tagAria.removeButtonProps;
379
+ const rawHandler = typeof raw.onPress === 'function' ? (raw.onPress as () => void) : undefined;
380
+ return {
381
+ ...raw,
382
+ onPress: () => {
383
+ if (!tagAria.isDisabled && groupContext?.onRemove) {
384
+ groupContext.onRemove(new Set([local.id]));
385
+ return;
386
+ }
387
+ rawHandler?.();
388
+ },
389
+ };
390
+ });
391
+
344
392
  // Render props values
345
393
  const renderValues = createMemo<TagRenderProps>(() => ({
346
394
  isSelected: tagAria.isSelected,
@@ -349,6 +397,7 @@ export function Tag(props: TagProps): JSX.Element {
349
397
  isPressed: tagAria.isPressed,
350
398
  allowsRemoving: tagAria.allowsRemoving,
351
399
  selectionMode: state?.selectionMode() ?? 'none',
400
+ removeButtonProps: normalizedRemoveButtonProps(),
352
401
  }));
353
402
 
354
403
  // Resolve render props
@@ -362,26 +411,39 @@ export function Tag(props: TagProps): JSX.Element {
362
411
  renderValues
363
412
  );
364
413
 
414
+ const selectionIndicatorContext = createMemo<SelectionIndicatorContextValue>(() => ({
415
+ isSelected: () => tagAria.isSelected,
416
+ }));
417
+
365
418
  // Filter DOM props
366
419
  const domProps = createMemo(() => filterDOMProps(rest, { global: true }));
367
420
 
368
421
  return (
369
- <div
370
- ref={setTagRef}
371
- {...domProps()}
372
- {...tagAria.rowProps}
373
- class={renderProps.class()}
374
- style={renderProps.style()}
375
- data-selected={dataAttr(tagAria.isSelected)}
376
- data-disabled={dataAttr(tagAria.isDisabled)}
377
- data-focused={dataAttr(tagAria.isFocused)}
378
- data-pressed={dataAttr(tagAria.isPressed)}
379
- data-allows-removing={dataAttr(tagAria.allowsRemoving)}
380
- >
381
- <div {...tagAria.gridCellProps} style={{ display: 'contents' }}>
382
- {renderProps.renderChildren()}
383
- </div>
384
- </div>
422
+ <SelectionIndicatorContext.Provider value={selectionIndicatorContext()}>
423
+ <TagContext.Provider
424
+ value={{
425
+ get removeButtonProps() { return normalizedRemoveButtonProps(); },
426
+ get allowsRemoving() { return tagAria.allowsRemoving; },
427
+ }}
428
+ >
429
+ <div
430
+ ref={setTagRef}
431
+ {...domProps()}
432
+ {...tagAria.rowProps}
433
+ class={renderProps.class()}
434
+ style={renderProps.style()}
435
+ data-selected={dataAttr(tagAria.isSelected)}
436
+ data-disabled={dataAttr(tagAria.isDisabled)}
437
+ data-focused={dataAttr(tagAria.isFocused)}
438
+ data-pressed={dataAttr(tagAria.isPressed)}
439
+ data-allows-removing={dataAttr(tagAria.allowsRemoving)}
440
+ >
441
+ <div {...tagAria.gridCellProps} style={{ display: 'contents' }}>
442
+ {renderProps.renderChildren()}
443
+ </div>
444
+ </div>
445
+ </TagContext.Provider>
446
+ </SelectionIndicatorContext.Provider>
385
447
  );
386
448
  }
387
449
 
@@ -396,6 +458,8 @@ export interface TagRemoveButtonProps {
396
458
  class?: string;
397
459
  /** The inline style for the element. */
398
460
  style?: JSX.CSSProperties;
461
+ /** Explicit button props from Tag render props. */
462
+ buttonProps?: Record<string, unknown>;
399
463
  }
400
464
 
401
465
  /**
@@ -403,14 +467,35 @@ export interface TagRemoveButtonProps {
403
467
  * It should be placed inside a Tag component.
404
468
  */
405
469
  export function TagRemoveButton(props: TagRemoveButtonProps): JSX.Element {
406
- // This is a simplified version - in a full implementation,
407
- // we'd get the remove button props from the Tag context
470
+ const tagContext = useContext(TagContext);
471
+ const getRemoveButtonProps = () => props.buttonProps ?? tagContext?.removeButtonProps ?? {};
472
+ const getIsDisabled = () => Boolean(getRemoveButtonProps().isDisabled);
473
+ const rawId = getRemoveButtonProps().id;
474
+ const rawAriaLabel = getRemoveButtonProps()['aria-label'];
475
+ const rawAriaLabelledBy = getRemoveButtonProps()['aria-labelledby'];
476
+ const buttonId: string | undefined = typeof rawId === 'string' ? rawId : undefined;
477
+ const ariaLabel: string = typeof rawAriaLabel === 'string' ? rawAriaLabel : 'Remove';
478
+ const ariaLabelledBy: string | undefined = typeof rawAriaLabelledBy === 'string' ? rawAriaLabelledBy : undefined;
479
+
480
+ const handleClick: JSX.EventHandler<HTMLButtonElement, MouseEvent> = (event) => {
481
+ event.stopPropagation();
482
+ const handler = getRemoveButtonProps().onPress;
483
+ if (typeof handler === 'function' && !getIsDisabled()) {
484
+ (handler as () => void)();
485
+ }
486
+ };
487
+
408
488
  return (
409
489
  <button
410
490
  type="button"
411
491
  class={props.class ?? 'solidaria-TagRemoveButton'}
412
492
  style={props.style}
413
- aria-label="Remove"
493
+ id={buttonId}
494
+ aria-label={ariaLabel}
495
+ aria-labelledby={ariaLabelledBy}
496
+ disabled={getIsDisabled()}
497
+ data-allows-removing={dataAttr(tagContext?.allowsRemoving ?? false)}
498
+ onClick={handleClick}
414
499
  >
415
500
  {props.children ?? '×'}
416
501
  </button>
package/src/Text.tsx ADDED
@@ -0,0 +1,18 @@
1
+ import { type JSX, createContext } from 'solid-js';
2
+ import type { SlotProps } from './utils';
3
+
4
+ export interface TextProps extends SlotProps {
5
+ children?: JSX.Element;
6
+ class?: string;
7
+ style?: JSX.CSSProperties;
8
+ }
9
+
10
+ export const TextContext = createContext<null>(null);
11
+
12
+ export function Text(props: TextProps): JSX.Element {
13
+ return (
14
+ <span class={props.class ?? 'solidaria-Text'} style={props.style}>
15
+ {props.children}
16
+ </span>
17
+ );
18
+ }
package/src/TextField.tsx CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  createTextField,
17
17
  createFocusRing,
18
18
  createHover,
19
+ mergeProps,
19
20
  type AriaTextFieldProps,
20
21
  } from '@proyecto-viviana/solidaria';
21
22
  import { createTextFieldState } from '@proyecto-viviana/solid-stately';
@@ -73,6 +74,10 @@ export interface TextFieldContextValue {
73
74
  }
74
75
 
75
76
  export const TextFieldContext = createContext<TextFieldContextValue | null>(null);
77
+ export const LabelContext = TextFieldContext;
78
+ export const InputContext = TextFieldContext;
79
+ export const TextAreaContext = TextFieldContext;
80
+ export const FieldInputContext = TextFieldContext;
76
81
 
77
82
  // ============================================
78
83
  // SUB-COMPONENTS
@@ -238,15 +243,27 @@ export function TextField(props: TextFieldProps): JSX.Element {
238
243
  return rest;
239
244
  };
240
245
 
241
- // Context value for sub-components
242
- // Note: We create the value object directly (not in a memo) so it's available
243
- // immediately when children access the context
246
+ // Context value for sub-components.
247
+ // Use property getters so sub-components always read the latest aria/focus state.
244
248
  const contextValue: TextFieldContextValue = {
245
- labelProps: textFieldAria.labelProps as JSX.LabelHTMLAttributes<HTMLLabelElement>,
246
- inputProps: { ...textFieldAria.inputProps, ...focusProps } as JSX.InputHTMLAttributes<HTMLInputElement>,
247
- descriptionProps: textFieldAria.descriptionProps as JSX.HTMLAttributes<HTMLElement>,
248
- errorMessageProps: textFieldAria.errorMessageProps as JSX.HTMLAttributes<HTMLElement>,
249
- isInvalid: textFieldAria.isInvalid,
249
+ get labelProps() {
250
+ return textFieldAria.labelProps as JSX.LabelHTMLAttributes<HTMLLabelElement>;
251
+ },
252
+ get inputProps() {
253
+ return mergeProps(
254
+ textFieldAria.inputProps as Record<string, unknown>,
255
+ focusProps as Record<string, unknown>
256
+ ) as JSX.InputHTMLAttributes<HTMLInputElement>;
257
+ },
258
+ get descriptionProps() {
259
+ return textFieldAria.descriptionProps as JSX.HTMLAttributes<HTMLElement>;
260
+ },
261
+ get errorMessageProps() {
262
+ return textFieldAria.errorMessageProps as JSX.HTMLAttributes<HTMLElement>;
263
+ },
264
+ get isInvalid() {
265
+ return textFieldAria.isInvalid;
266
+ },
250
267
  };
251
268
 
252
269
  return (