@stack-spot/citric-react 0.22.0 → 0.23.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.
Files changed (258) hide show
  1. package/dist/citric.css +17 -4
  2. package/dist/components/Accordion.d.ts +34 -0
  3. package/dist/components/Accordion.d.ts.map +1 -1
  4. package/dist/components/Accordion.js +34 -0
  5. package/dist/components/Accordion.js.map +1 -1
  6. package/dist/components/Alert.d.ts +8 -0
  7. package/dist/components/Alert.d.ts.map +1 -1
  8. package/dist/components/Alert.js +8 -0
  9. package/dist/components/Alert.js.map +1 -1
  10. package/dist/components/AsyncContent.d.ts +18 -4
  11. package/dist/components/AsyncContent.d.ts.map +1 -1
  12. package/dist/components/AsyncContent.js +18 -4
  13. package/dist/components/AsyncContent.js.map +1 -1
  14. package/dist/components/Avatar.d.ts +9 -0
  15. package/dist/components/Avatar.d.ts.map +1 -1
  16. package/dist/components/Avatar.js +11 -1
  17. package/dist/components/Avatar.js.map +1 -1
  18. package/dist/components/AvatarGroup.d.ts +8 -0
  19. package/dist/components/AvatarGroup.d.ts.map +1 -1
  20. package/dist/components/AvatarGroup.js +8 -0
  21. package/dist/components/AvatarGroup.js.map +1 -1
  22. package/dist/components/Badge.d.ts +13 -2
  23. package/dist/components/Badge.d.ts.map +1 -1
  24. package/dist/components/Badge.js +14 -3
  25. package/dist/components/Badge.js.map +1 -1
  26. package/dist/components/Blockquote.d.ts +8 -0
  27. package/dist/components/Blockquote.d.ts.map +1 -1
  28. package/dist/components/Blockquote.js +8 -0
  29. package/dist/components/Blockquote.js.map +1 -1
  30. package/dist/components/Breadcrumb.d.ts +8 -0
  31. package/dist/components/Breadcrumb.d.ts.map +1 -1
  32. package/dist/components/Breadcrumb.js +10 -1
  33. package/dist/components/Breadcrumb.js.map +1 -1
  34. package/dist/components/Button.d.ts +11 -0
  35. package/dist/components/Button.d.ts.map +1 -1
  36. package/dist/components/Button.js +14 -2
  37. package/dist/components/Button.js.map +1 -1
  38. package/dist/components/Card.d.ts +15 -4
  39. package/dist/components/Card.d.ts.map +1 -1
  40. package/dist/components/Card.js +13 -1
  41. package/dist/components/Card.js.map +1 -1
  42. package/dist/components/Checkbox.d.ts +14 -0
  43. package/dist/components/Checkbox.d.ts.map +1 -1
  44. package/dist/components/Checkbox.js +14 -0
  45. package/dist/components/Checkbox.js.map +1 -1
  46. package/dist/components/CheckboxGroup.d.ts +22 -3
  47. package/dist/components/CheckboxGroup.d.ts.map +1 -1
  48. package/dist/components/CheckboxGroup.js +23 -3
  49. package/dist/components/CheckboxGroup.js.map +1 -1
  50. package/dist/components/Circle.d.ts +16 -0
  51. package/dist/components/Circle.d.ts.map +1 -1
  52. package/dist/components/Circle.js +8 -0
  53. package/dist/components/Circle.js.map +1 -1
  54. package/dist/components/CitricComponent.d.ts +14 -0
  55. package/dist/components/CitricComponent.d.ts.map +1 -1
  56. package/dist/components/CitricComponent.js +14 -0
  57. package/dist/components/CitricComponent.js.map +1 -1
  58. package/dist/components/Divider.d.ts +4 -1
  59. package/dist/components/Divider.d.ts.map +1 -1
  60. package/dist/components/Divider.js +4 -1
  61. package/dist/components/Divider.js.map +1 -1
  62. package/dist/components/ErrorBoundary.d.ts +13 -0
  63. package/dist/components/ErrorBoundary.d.ts.map +1 -1
  64. package/dist/components/ErrorBoundary.js +13 -0
  65. package/dist/components/ErrorBoundary.js.map +1 -1
  66. package/dist/components/ErrorMessage.js +1 -1
  67. package/dist/components/ErrorMessage.js.map +1 -1
  68. package/dist/components/FallbackBoundary.d.ts +12 -1
  69. package/dist/components/FallbackBoundary.d.ts.map +1 -1
  70. package/dist/components/FallbackBoundary.js +12 -1
  71. package/dist/components/FallbackBoundary.js.map +1 -1
  72. package/dist/components/Favorite.d.ts +12 -0
  73. package/dist/components/Favorite.d.ts.map +1 -1
  74. package/dist/components/Favorite.js +12 -0
  75. package/dist/components/Favorite.js.map +1 -1
  76. package/dist/components/FieldGroup.d.ts +13 -0
  77. package/dist/components/FieldGroup.d.ts.map +1 -1
  78. package/dist/components/FieldGroup.js +13 -0
  79. package/dist/components/FieldGroup.js.map +1 -1
  80. package/dist/components/Form.d.ts +18 -0
  81. package/dist/components/Form.d.ts.map +1 -1
  82. package/dist/components/Form.js +18 -0
  83. package/dist/components/Form.js.map +1 -1
  84. package/dist/components/FormGroup.d.ts +12 -0
  85. package/dist/components/FormGroup.d.ts.map +1 -1
  86. package/dist/components/FormGroup.js +12 -0
  87. package/dist/components/FormGroup.js.map +1 -1
  88. package/dist/components/IconBox.d.ts +33 -8
  89. package/dist/components/IconBox.d.ts.map +1 -1
  90. package/dist/components/IconBox.js +37 -11
  91. package/dist/components/IconBox.js.map +1 -1
  92. package/dist/components/ImageBox.d.ts +32 -8
  93. package/dist/components/ImageBox.d.ts.map +1 -1
  94. package/dist/components/ImageBox.js +36 -11
  95. package/dist/components/ImageBox.js.map +1 -1
  96. package/dist/components/ImageWithFallback.d.ts +18 -0
  97. package/dist/components/ImageWithFallback.d.ts.map +1 -1
  98. package/dist/components/ImageWithFallback.js +11 -0
  99. package/dist/components/ImageWithFallback.js.map +1 -1
  100. package/dist/components/Input.d.ts +15 -3
  101. package/dist/components/Input.d.ts.map +1 -1
  102. package/dist/components/Input.js +16 -3
  103. package/dist/components/Input.js.map +1 -1
  104. package/dist/components/Link.d.ts +6 -0
  105. package/dist/components/Link.d.ts.map +1 -1
  106. package/dist/components/Link.js +6 -0
  107. package/dist/components/Link.js.map +1 -1
  108. package/dist/components/MenuOverlay/index.d.ts +20 -0
  109. package/dist/components/MenuOverlay/index.d.ts.map +1 -1
  110. package/dist/components/MenuOverlay/index.js +20 -0
  111. package/dist/components/MenuOverlay/index.js.map +1 -1
  112. package/dist/components/Overlay/index.d.ts +16 -0
  113. package/dist/components/Overlay/index.d.ts.map +1 -1
  114. package/dist/components/Overlay/index.js +16 -0
  115. package/dist/components/Overlay/index.js.map +1 -1
  116. package/dist/components/Pagination.d.ts +27 -8
  117. package/dist/components/Pagination.d.ts.map +1 -1
  118. package/dist/components/Pagination.js +18 -5
  119. package/dist/components/Pagination.js.map +1 -1
  120. package/dist/components/ProgressBar.d.ts +14 -0
  121. package/dist/components/ProgressBar.d.ts.map +1 -1
  122. package/dist/components/ProgressBar.js +14 -0
  123. package/dist/components/ProgressBar.js.map +1 -1
  124. package/dist/components/ProgressCircular.d.ts +14 -0
  125. package/dist/components/ProgressCircular.d.ts.map +1 -1
  126. package/dist/components/ProgressCircular.js +14 -0
  127. package/dist/components/ProgressCircular.js.map +1 -1
  128. package/dist/components/RadioGroup.d.ts +24 -3
  129. package/dist/components/RadioGroup.d.ts.map +1 -1
  130. package/dist/components/RadioGroup.js +25 -3
  131. package/dist/components/RadioGroup.js.map +1 -1
  132. package/dist/components/Rating.d.ts +10 -0
  133. package/dist/components/Rating.d.ts.map +1 -1
  134. package/dist/components/Rating.js +10 -0
  135. package/dist/components/Rating.js.map +1 -1
  136. package/dist/components/Select/RichSelect.d.ts +3 -5
  137. package/dist/components/Select/RichSelect.d.ts.map +1 -1
  138. package/dist/components/Select/RichSelect.js +4 -4
  139. package/dist/components/Select/RichSelect.js.map +1 -1
  140. package/dist/components/Select/SimpleSelect.d.ts +2 -3
  141. package/dist/components/Select/SimpleSelect.d.ts.map +1 -1
  142. package/dist/components/Select/SimpleSelect.js +2 -3
  143. package/dist/components/Select/SimpleSelect.js.map +1 -1
  144. package/dist/components/Select/index.d.ts +25 -2
  145. package/dist/components/Select/index.d.ts.map +1 -1
  146. package/dist/components/Select/index.js +26 -3
  147. package/dist/components/Select/index.js.map +1 -1
  148. package/dist/components/Select/types.d.ts +1 -2
  149. package/dist/components/Select/types.d.ts.map +1 -1
  150. package/dist/components/SelectBox.d.ts +31 -2
  151. package/dist/components/SelectBox.d.ts.map +1 -1
  152. package/dist/components/SelectBox.js +32 -3
  153. package/dist/components/SelectBox.js.map +1 -1
  154. package/dist/components/Skeleton.d.ts +11 -0
  155. package/dist/components/Skeleton.d.ts.map +1 -1
  156. package/dist/components/Skeleton.js +11 -0
  157. package/dist/components/Skeleton.js.map +1 -1
  158. package/dist/components/Slider.d.ts +12 -0
  159. package/dist/components/Slider.d.ts.map +1 -1
  160. package/dist/components/Slider.js +12 -0
  161. package/dist/components/Slider.js.map +1 -1
  162. package/dist/components/SmartTable.d.ts +36 -2
  163. package/dist/components/SmartTable.d.ts.map +1 -1
  164. package/dist/components/SmartTable.js +37 -3
  165. package/dist/components/SmartTable.js.map +1 -1
  166. package/dist/components/Stepper.d.ts +20 -3
  167. package/dist/components/Stepper.d.ts.map +1 -1
  168. package/dist/components/Stepper.js +21 -3
  169. package/dist/components/Stepper.js.map +1 -1
  170. package/dist/components/Table.d.ts +10 -0
  171. package/dist/components/Table.d.ts.map +1 -1
  172. package/dist/components/Table.js +10 -0
  173. package/dist/components/Table.js.map +1 -1
  174. package/dist/components/Tabs/TabController.d.ts +14 -0
  175. package/dist/components/Tabs/TabController.d.ts.map +1 -1
  176. package/dist/components/Tabs/TabController.js +14 -0
  177. package/dist/components/Tabs/TabController.js.map +1 -1
  178. package/dist/components/Tabs/index.d.ts +20 -3
  179. package/dist/components/Tabs/index.d.ts.map +1 -1
  180. package/dist/components/Tabs/index.js +21 -3
  181. package/dist/components/Tabs/index.js.map +1 -1
  182. package/dist/components/Text.d.ts +16 -2
  183. package/dist/components/Text.d.ts.map +1 -1
  184. package/dist/components/Text.js +17 -3
  185. package/dist/components/Text.js.map +1 -1
  186. package/dist/components/Textarea.d.ts +11 -1
  187. package/dist/components/Textarea.d.ts.map +1 -1
  188. package/dist/components/Textarea.js +12 -2
  189. package/dist/components/Textarea.js.map +1 -1
  190. package/dist/components/Tooltip.d.ts +14 -1
  191. package/dist/components/Tooltip.d.ts.map +1 -1
  192. package/dist/components/Tooltip.js +13 -0
  193. package/dist/components/Tooltip.js.map +1 -1
  194. package/dist/components/layout.d.ts +41 -7
  195. package/dist/components/layout.d.ts.map +1 -1
  196. package/dist/components/layout.js +44 -9
  197. package/dist/components/layout.js.map +1 -1
  198. package/dist/index.d.ts +0 -1
  199. package/dist/index.d.ts.map +1 -1
  200. package/dist/index.js +0 -1
  201. package/dist/index.js.map +1 -1
  202. package/package.json +2 -2
  203. package/src/components/Accordion.tsx +34 -0
  204. package/src/components/Alert.tsx +8 -0
  205. package/src/components/AsyncContent.tsx +18 -4
  206. package/src/components/Avatar.tsx +11 -1
  207. package/src/components/AvatarGroup.tsx +8 -0
  208. package/src/components/Badge.tsx +24 -8
  209. package/src/components/Blockquote.tsx +8 -0
  210. package/src/components/Breadcrumb.tsx +10 -1
  211. package/src/components/Button.tsx +17 -2
  212. package/src/components/Card.tsx +34 -14
  213. package/src/components/Checkbox.tsx +14 -0
  214. package/src/components/CheckboxGroup.tsx +61 -40
  215. package/src/components/Circle.tsx +16 -0
  216. package/src/components/CitricComponent.ts +14 -0
  217. package/src/components/Divider.tsx +6 -5
  218. package/src/components/ErrorBoundary.tsx +13 -0
  219. package/src/components/ErrorMessage.tsx +1 -1
  220. package/src/components/FallbackBoundary.tsx +12 -1
  221. package/src/components/Favorite.tsx +12 -0
  222. package/src/components/FieldGroup.tsx +13 -0
  223. package/src/components/Form.tsx +18 -0
  224. package/src/components/FormGroup.tsx +12 -0
  225. package/src/components/IconBox.tsx +61 -30
  226. package/src/components/ImageBox.tsx +60 -30
  227. package/src/components/ImageWithFallback.tsx +18 -0
  228. package/src/components/Input.tsx +28 -14
  229. package/src/components/Link.tsx +6 -0
  230. package/src/components/MenuOverlay/index.tsx +20 -0
  231. package/src/components/Overlay/index.tsx +17 -0
  232. package/src/components/Pagination.tsx +40 -17
  233. package/src/components/ProgressBar.tsx +14 -0
  234. package/src/components/ProgressCircular.tsx +14 -0
  235. package/src/components/RadioGroup.tsx +62 -39
  236. package/src/components/Rating.tsx +10 -0
  237. package/src/components/Select/RichSelect.tsx +183 -182
  238. package/src/components/Select/SimpleSelect.tsx +57 -57
  239. package/src/components/Select/index.tsx +29 -5
  240. package/src/components/Select/types.ts +1 -1
  241. package/src/components/SelectBox.tsx +92 -62
  242. package/src/components/Skeleton.tsx +11 -0
  243. package/src/components/Slider.tsx +12 -0
  244. package/src/components/SmartTable.tsx +91 -56
  245. package/src/components/Stepper.tsx +76 -57
  246. package/src/components/Table.tsx +10 -0
  247. package/src/components/Tabs/TabController.ts +14 -0
  248. package/src/components/Tabs/index.tsx +56 -37
  249. package/src/components/Text.ts +36 -21
  250. package/src/components/Textarea.tsx +14 -4
  251. package/src/components/Tooltip.tsx +14 -1
  252. package/src/components/layout.tsx +56 -13
  253. package/src/index.ts +0 -1
  254. package/dist/components/Switch.d.ts +0 -10
  255. package/dist/components/Switch.d.ts.map +0 -1
  256. package/dist/components/Switch.js +0 -8
  257. package/dist/components/Switch.js.map +0 -1
  258. package/src/components/Switch.tsx +0 -30
@@ -73,65 +73,95 @@ export type BaseSelectBoxProps<T> = RadioProps<T> | CheckboxProps<T>
73
73
 
74
74
  export type SelectBoxProps<T> = Omit<React.JSX.IntrinsicElements['div'], 'onChange' | 'children'> & BaseSelectBoxProps<T>
75
75
 
76
- function _SelectBox<T>({
77
- multiple,
78
- name,
79
- value,
80
- options,
81
- onChange,
82
- renderLabel = o => ({ title: defaultRenderLabel(o) }),
83
- renderKey = defaultRenderKey,
84
- isDisabled,
85
- className,
86
- style,
87
- direction,
88
- bgLevel,
89
- colorPalette,
90
- colorScheme,
91
- ...props
92
- }: SelectBoxProps<T>) {
93
- const items = useMemo(() => {
94
- const valueAsArray = value ? (Array.isArray(value) ? value : [value]) : []
95
- const valueKeys = valueAsArray.map(renderKey)
96
- return options.map((o) => {
97
- const key = renderKey(o)
98
- const label = renderLabel(o)
99
- return (
100
- <CitricComponent key={key} tag="label" component="select-box" className={bgLevel ? `bg-${bgLevel}` : undefined}>
101
- <input
102
- type={multiple ? 'checkbox' : 'radio'}
103
- name={name}
104
- value={key}
105
- checked={value ? valueKeys.includes(key) : undefined}
106
- disabled={isDisabled?.(o)}
107
- onChange={onChange ? (e) => {
108
- if (multiple) {
109
- onChange(e.target.checked ? [...valueAsArray, o] : valueAsArray.filter(v => renderKey(v) !== key))
110
- } else {
111
- onChange(o)
112
- }
113
- } : undefined}
114
- />
115
- <div className="option">
116
- {label.icon}
117
- <p className="title">{label.title}</p>
118
- {label.description && <p className="description">{label.description}</p>}
119
- </div>
120
- </CitricComponent>
121
- )
122
- })
123
- }, [options, value, name, multiple, bgLevel])
124
- return (
125
- <div
126
- data-color-palette={colorPalette}
127
- data-color-scheme={colorScheme}
128
- className={listToClass([className, direction === 'vertical' ? layout.column : layout.row])}
129
- style={{ gap: '5px', ...style }}
130
- {...props}
131
- >
132
- {items}
133
- </div>
134
- )
135
- }
136
-
137
- export const SelectBox = withRef(_SelectBox)
76
+ /**
77
+ * This component does exactly the same thing as "CheckboxGroup" and "RadioGroup", but with different visuals and less customization.
78
+ *
79
+ * Each option is rendered as a selectable card. The user can select a single option (radio buttons) or multiple options (checkboxes),
80
+ * depending on the value of the property "multiple", which is false by default.
81
+ *
82
+ * Use `renderLabel` to determine what to render inside each card. This is not a free react element, it must return an object containing an
83
+ * icon, a title and a description.
84
+ *
85
+ * @example
86
+ *
87
+ * ```
88
+ * const options = useMemo(() => [
89
+ * { id: 1, image: 'https://images.com/1.png', name: 'Basic plan', price: 59 },
90
+ * { id: 2, image: 'https://images.com/2.png', name: 'Gold plan', price: 69 },
91
+ * { id: 3, image: 'https://images.com/3.png', name: 'Safira plan', price: 79' },
92
+ * { id: 4, image: 'https://images.com/4.png', name: 'Diamond plan', price: 99 },
93
+ * ], [])
94
+ *
95
+ * return <SelectBox
96
+ * options={options}
97
+ * renderKey={o => o.id}
98
+ * renderLabel={o => ({
99
+ * icon: <img src={o.image} />,
100
+ * title: o.name,
101
+ * description: `$${price.toFixed(2)}`,
102
+ * })}
103
+ * />
104
+ * ```
105
+ */
106
+ export const SelectBox = withRef(
107
+ function SelectBox<T>({
108
+ multiple,
109
+ name,
110
+ value,
111
+ options,
112
+ onChange,
113
+ renderLabel = o => ({ title: defaultRenderLabel(o) }),
114
+ renderKey = defaultRenderKey,
115
+ isDisabled,
116
+ className,
117
+ style,
118
+ direction,
119
+ bgLevel,
120
+ colorPalette,
121
+ colorScheme,
122
+ ...props
123
+ }: SelectBoxProps<T>) {
124
+ const items = useMemo(() => {
125
+ const valueAsArray = value ? (Array.isArray(value) ? value : [value]) : []
126
+ const valueKeys = valueAsArray.map(renderKey)
127
+ return options.map((o) => {
128
+ const key = renderKey(o)
129
+ const label = renderLabel(o)
130
+ return (
131
+ <CitricComponent key={key} tag="label" component="select-box" className={bgLevel ? `bg-${bgLevel}` : undefined}>
132
+ <input
133
+ type={multiple ? 'checkbox' : 'radio'}
134
+ name={name}
135
+ value={key}
136
+ checked={value ? valueKeys.includes(key) : undefined}
137
+ disabled={isDisabled?.(o)}
138
+ onChange={onChange ? (e) => {
139
+ if (multiple) {
140
+ onChange(e.target.checked ? [...valueAsArray, o] : valueAsArray.filter(v => renderKey(v) !== key))
141
+ } else {
142
+ onChange(o)
143
+ }
144
+ } : undefined}
145
+ />
146
+ <div className="option">
147
+ {label.icon}
148
+ <p className="title">{label.title}</p>
149
+ {label.description && <p className="description">{label.description}</p>}
150
+ </div>
151
+ </CitricComponent>
152
+ )
153
+ })
154
+ }, [options, value, name, multiple, bgLevel])
155
+ return (
156
+ <div
157
+ data-color-palette={colorPalette}
158
+ data-color-scheme={colorScheme}
159
+ className={listToClass([className, direction === 'vertical' ? layout.column : layout.row])}
160
+ style={{ gap: '5px', ...style }}
161
+ {...props}
162
+ >
163
+ {items}
164
+ </div>
165
+ )
166
+ },
167
+ )
@@ -31,6 +31,17 @@ export interface BaseSkeletonProps {
31
31
 
32
32
  export type SkeletonProps = React.JSX.IntrinsicElements['div'] & BaseSkeletonProps
33
33
 
34
+ /**
35
+ * A loading feedback with a discrete animation. Use this to replace elements on the page that are still loading and will be replaced by
36
+ * the same shape when the loading ends.
37
+ *
38
+ * Use the property "bgLevel" to manage how light/dark the background is.
39
+ *
40
+ * @example
41
+ * ```
42
+ * <Skeleton width="100%" height="20px" />
43
+ * ```
44
+ */
34
45
  export const Skeleton = withRef(({ appearance, className, style, width, height, bgLevel, ...props }: SkeletonProps) =>
35
46
  <CitricComponent
36
47
  tag="div"
@@ -35,6 +35,18 @@ export interface BaseSliderProps extends WithColorScheme, WithColorPalette {
35
35
 
36
36
  export type SliderProps = ControlledInput & BaseSliderProps
37
37
 
38
+ /**
39
+ * A UI element where the user can slide a knob over a bar to select a number. By default, a number between 0 and 100 can be selected, use
40
+ * the properties "min" and "max" to change this.
41
+ *
42
+ * Attention: "onChange" receives the new value (number) instead of the event.
43
+ *
44
+ * @example
45
+ * ```
46
+ * const [value, setValue] = useState(0)
47
+ * return <Slider value={value} setValue={setValue} />
48
+ * ```
49
+ */
38
50
  export const Slider = withRef((
39
51
  { value, onChange, min, max, style, showValue, renderValue, colorPalette, colorScheme, className, ...props }: SliderProps,
40
52
  ) => {
@@ -89,63 +89,98 @@ export type BaseSmartTableProps<T extends Record<string, any>> = BaseTableProps
89
89
 
90
90
  export type SmartTableProps<T extends Record<string, any>> = Omit<TableProps, 'children'> & ExtraSmartTableProps<T>
91
91
 
92
- function _SmartTable<T extends Record<string, any>>(
93
- { data, keygen, columns, renderAccordion, accordionTrigger, accordionMaxHeight, id, ...props }: SmartTableProps<T>,
94
- ) {
95
- id = useMemo(() => id || `${Math.random()}`, [id])
92
+ /**
93
+ * Renders a table. Always prefer using this component over the raw Table component.
94
+ *
95
+ * This receives a dataset and renders each item as a row. The columns are determined by the property "columns".
96
+ *
97
+ * To change the overall table style, use the property "appearance".
98
+ *
99
+ * To check more complex table examples, like tables with accordions and sorting, check the storybook.
100
+ *
101
+ * @example
102
+ *
103
+ * ```
104
+ * const data = useMemo(() => [
105
+ * { id: 1, name: 'Yuri Tyson', phone: '1-801-475-4561', country: 'Poland', age: 21 },
106
+ * { id: 2, name: 'Amal Mcclure', phone: '(695) 948-4102', country: 'Ukraine', age: 32 },
107
+ * { id: 3, name: 'Levi Glass', phone: '1-976-544-4872', country: 'Colombia', age: 45 },
108
+ * ], [])
109
+ * const columns = useMemo(() => [
110
+ * { key: 'name', label: 'Name', render: item => `${item.name} (${item.age})` },
111
+ * { key: 'phone', label: 'Phone' },
112
+ * { key: 'country', label: 'Country' },
113
+ * {
114
+ * key: 'settings',
115
+ * render: item => (
116
+ * <Row gap={2}>
117
+ * <IconButton icon="ExternalLink" title="View" />
118
+ * <IconButton icon="Pencil" title="Edit" />
119
+ * <IconButton icon="Trash" title="Delete" colorScheme="danger" />
120
+ * </Row>
121
+ * ),
122
+ * },
123
+ * ], [])
124
+ * return <SmartTable data={data} columns={columns} />
125
+ * ```
126
+ */
127
+ export const SmartTable = withRef(
128
+ function SmartTable<T extends Record<string, any>>(
129
+ { data, keygen, columns, renderAccordion, accordionTrigger, accordionMaxHeight, id, ...props }: SmartTableProps<T>,
130
+ ) {
131
+ id = useMemo(() => id || `${Math.random()}`, [id])
96
132
 
97
- const headers = useMemo(
98
- () => columns.map((c) => (
99
- <Th {...c.th} key={c.key} onSort={c.onSort} direction={c.direction} tabIndex={c.onSort ? 0 : undefined}>
100
- {c.label ?? c.key}
101
- </Th>
102
- )),
103
- [columns],
104
- )
133
+ const headers = useMemo(
134
+ () => columns.map((c) => (
135
+ <Th {...c.th} key={c.key} onSort={c.onSort} direction={c.direction} tabIndex={c.onSort ? 0 : undefined}>
136
+ {c.label ?? c.key}
137
+ </Th>
138
+ )),
139
+ [columns],
140
+ )
105
141
 
106
- const rows = useMemo(
107
- () => data.map((item, index) => {
108
- const key = keygen ? keygen(item) : index
109
- const accordionContent = renderAccordion?.(item)
110
- const row = (
111
- <Tr key={key} accordionTrigger={accordionContent ? accordionTrigger : undefined}>
112
- {columns.map(c => <td {...c.td} key={c.key}>{c.render ? c.render(item) : (item[c.key] || '')}</td>)}
113
- {renderAccordion && (
114
- <td>
115
- {accordionContent && (
116
- <input
117
- type="checkbox"
118
- aria-controls={`${id}-${key}`}
119
- onKeyDown={e => e.key === 'Enter' && e.target instanceof HTMLElement && e.target.click()}
120
- />
121
- )}
122
- </td>
123
- )}
124
- </Tr>
125
- )
126
- return renderAccordion ? (
127
- <tbody key={`${key}-group`}>
128
- {row}
129
- <Tr id={`${id}-${key}`} accordion accordionMaxHeight={accordionMaxHeight}>
130
- <td colSpan={columns.length + 1}><div>{accordionContent}</div></td>
142
+ const rows = useMemo(
143
+ () => data.map((item, index) => {
144
+ const key = keygen ? keygen(item) : index
145
+ const accordionContent = renderAccordion?.(item)
146
+ const row = (
147
+ <Tr key={key} accordionTrigger={accordionContent ? accordionTrigger : undefined}>
148
+ {columns.map(c => <td {...c.td} key={c.key}>{c.render ? c.render(item) : (item[c.key] || '')}</td>)}
149
+ {renderAccordion && (
150
+ <td>
151
+ {accordionContent && (
152
+ <input
153
+ type="checkbox"
154
+ aria-controls={`${id}-${key}`}
155
+ onKeyDown={e => e.key === 'Enter' && e.target instanceof HTMLElement && e.target.click()}
156
+ />
157
+ )}
158
+ </td>
159
+ )}
131
160
  </Tr>
132
- </tbody>
133
- ) : row
134
- }),
135
- [columns, data, !!renderAccordion, accordionTrigger],
136
- )
161
+ )
162
+ return renderAccordion ? (
163
+ <tbody key={`${key}-group`}>
164
+ {row}
165
+ <Tr id={`${id}-${key}`} accordion accordionMaxHeight={accordionMaxHeight}>
166
+ <td colSpan={columns.length + 1}><div>{accordionContent}</div></td>
167
+ </Tr>
168
+ </tbody>
169
+ ) : row
170
+ }),
171
+ [columns, data, !!renderAccordion, accordionTrigger],
172
+ )
137
173
 
138
- return (
139
- <Table id={id} accordionRows={!!renderAccordion} {...props}>
140
- <thead>
141
- <tr>
142
- {headers}
143
- {renderAccordion && <th></th>}
144
- </tr>
145
- </thead>
146
- {renderAccordion ? rows : <tbody>{rows}</tbody>}
147
- </Table>
148
- )
149
- }
150
-
151
- export const SmartTable = withRef(_SmartTable)
174
+ return (
175
+ <Table id={id} accordionRows={!!renderAccordion} {...props}>
176
+ <thead>
177
+ <tr>
178
+ {headers}
179
+ {renderAccordion && <th></th>}
180
+ </tr>
181
+ </thead>
182
+ {renderAccordion ? rows : <tbody>{rows}</tbody>}
183
+ </Table>
184
+ )
185
+ },
186
+ )
@@ -65,66 +65,87 @@ function getTabsWithDisabled<Key extends string>(tabs: Tab<Key>[], value: Key |
65
65
  return tabs.map((t, i) => ({ ...t, disabled: i > index }))
66
66
  }
67
67
 
68
- function _Stepper<Key extends string>(
69
- { tabs: initialTabs, controller: ctrl, value, onChange, buttons = true, className, ...props }: StepperProps<Key>,
70
- ) {
71
- const controller = useMemo(
72
- () => ctrl ?? new TabController<Key>(initialTabs.map(t => t.key), value || initialTabs[0]?.key),
73
- [],
74
- )
75
- const [tabs, setTabs] = useState(getTabsWithDisabled(initialTabs, value))
76
- const [selectedIndex, setSelectedIndex] = useState(findSelectedIndex(tabs, controller.getValue()))
77
- const t = useTranslate(dictionary)
68
+ /**
69
+ * A Stepper is a tab view with a different appearance. To control the current tab (step), retrieve the controller by calling
70
+ * `useTabsController()` from within a tab (step) content.
71
+ *
72
+ * Besides all the properties in "Tabs", the "Stepper" can create a set of "Previous" and "Next" buttons through the property "buttons". By
73
+ * default, the buttons are rendered, to disable them, pass `buttons = false`.
74
+ *
75
+ * @example
76
+ *
77
+ * ```
78
+ * const steps: Tab[] = useMemo(() => [
79
+ * { key: 'step1', label: 'Step 1', content: <><h1>First step</h1><p>Content of the first step</p></> },
80
+ * { key: 'step2', label: 'Step 2', content: <><h1>Second step</h1><p>Content of the second step</p></> },
81
+ * { key: 'step3', label: 'Step 3', content: <><h1>Third step</h1><p>Content of the third step</p></> },
82
+ * ], [])
83
+ *
84
+ * return <Stepper tabs={steps} />
85
+ * ```
86
+ */
87
+ export const Stepper = withRef(
88
+ function Stepper<Key extends string>(
89
+ { tabs: initialTabs, controller: ctrl, value, onChange, buttons = true, className, ...props }: StepperProps<Key>,
90
+ ) {
91
+ const controller = useMemo(
92
+ () => ctrl ?? new TabController<Key>(initialTabs.map(t => t.key), value || initialTabs[0]?.key),
93
+ [],
94
+ )
95
+ const [tabs, setTabs] = useState(getTabsWithDisabled(initialTabs, value))
96
+ const [selectedIndex, setSelectedIndex] = useState(findSelectedIndex(tabs, controller.getValue()))
97
+ const t = useTranslate(dictionary)
78
98
 
79
- useEffect(() => controller.onChange((v) => {
80
- setSelectedIndex(findSelectedIndex(tabs, v))
81
- }), [tabs])
99
+ useEffect(() => controller.onChange((v) => {
100
+ setSelectedIndex(findSelectedIndex(tabs, v))
101
+ }), [tabs])
82
102
 
83
- useEffect(() => controller.onChange((value) => {
84
- setTabs(getTabsWithDisabled(initialTabs, value))
85
- }), [])
103
+ useEffect(() => controller.onChange((value) => {
104
+ setTabs(getTabsWithDisabled(initialTabs, value))
105
+ }), [])
86
106
 
87
- const onPrevious = useCallback(() => {
88
- controller.previous()
89
- if (typeof buttons === 'object') buttons.onPrevious?.(controller.getValue())
90
- }, [])
107
+ const onPrevious = useCallback(() => {
108
+ controller.previous()
109
+ if (typeof buttons === 'object') buttons.onPrevious?.(controller.getValue())
110
+ }, [])
91
111
 
92
- const onNext = useCallback(() => {
93
- controller.next()
94
- if (typeof buttons === 'object') buttons.onNext?.(controller.getValue())
95
- }, [])
112
+ const onNext = useCallback(() => {
113
+ controller.next()
114
+ if (typeof buttons === 'object') buttons.onNext?.(controller.getValue())
115
+ }, [])
96
116
 
97
- return buttons ? (
98
- <Column {...props} className={className} gap="20px">
99
- <Tabs tabs={tabs} controller={controller} value={value} onChange={onChange} className="stepper" />
100
- <Row justifyContent={(typeof buttons !== 'object' || !buttons.onCancel) && selectedIndex === 0 ? 'end' : 'space-between'}>
101
- {selectedIndex === 0 && typeof buttons === 'object' && buttons.onCancel && (
102
- <Button onClick={buttons.onCancel} colorScheme="inverse" appearance="outlined">{buttons.cancel || t.cancel}</Button>
103
- )}
104
- {selectedIndex > 0 && buttons && (
105
- <Button onClick={onPrevious} colorScheme="inverse" appearance="outlined">
106
- {(typeof buttons === 'object' && buttons.previous) || t.previous}
107
- </Button>
108
- )}
109
- {selectedIndex < tabs.length - 1 && buttons && (
110
- <Button onClick={onNext}>
111
- {(typeof buttons === 'object' && buttons.next) || t.next}
112
- </Button>
113
- )}
114
- {selectedIndex === tabs.length - 1 && typeof buttons === 'object' && buttons.onFinish && (
115
- <Button onClick={buttons.onFinish}>{buttons.finish || t.finish}</Button>
116
- )}
117
- </Row>
118
- </Column>
119
- ) : <Tabs
120
- tabs={tabs}
121
- controller={controller}
122
- value={value}
123
- onChange={onChange}
124
- className={listToClass([className, 'stepper'])}
125
- {...props}
126
- />
127
- }
117
+ return buttons ? (
118
+ <Column {...props} className={className} gap="20px">
119
+ <Tabs tabs={tabs} controller={controller} value={value} onChange={onChange} className="stepper" />
120
+ <Row justifyContent={(typeof buttons !== 'object' || !buttons.onCancel) && selectedIndex === 0 ? 'end' : 'space-between'}>
121
+ {selectedIndex === 0 && typeof buttons === 'object' && buttons.onCancel && (
122
+ <Button onClick={buttons.onCancel} colorScheme="inverse" appearance="outlined">{buttons.cancel || t.cancel}</Button>
123
+ )}
124
+ {selectedIndex > 0 && buttons && (
125
+ <Button onClick={onPrevious} colorScheme="inverse" appearance="outlined">
126
+ {(typeof buttons === 'object' && buttons.previous) || t.previous}
127
+ </Button>
128
+ )}
129
+ {selectedIndex < tabs.length - 1 && buttons && (
130
+ <Button onClick={onNext}>
131
+ {(typeof buttons === 'object' && buttons.next) || t.next}
132
+ </Button>
133
+ )}
134
+ {selectedIndex === tabs.length - 1 && typeof buttons === 'object' && buttons.onFinish && (
135
+ <Button onClick={buttons.onFinish}>{buttons.finish || t.finish}</Button>
136
+ )}
137
+ </Row>
138
+ </Column>
139
+ ) : <Tabs
140
+ tabs={tabs}
141
+ controller={controller}
142
+ value={value}
143
+ onChange={onChange}
144
+ className={listToClass([className, 'stepper'])}
145
+ {...props}
146
+ />
147
+ },
148
+ )
128
149
 
129
150
  const dictionary = {
130
151
  en: {
@@ -140,5 +161,3 @@ const dictionary = {
140
161
  finish: 'Finalizar',
141
162
  },
142
163
  } satisfies Dictionary
143
-
144
- export const Stepper = withRef(_Stepper)
@@ -110,6 +110,16 @@ export type TableProps = React.JSX.IntrinsicElements['table'] & BaseTableProps
110
110
  export type ThProps = React.JSX.IntrinsicElements['th'] & BaseThProps
111
111
  export type TrProps = React.JSX.IntrinsicElements['tr'] & BaseTrProps
112
112
 
113
+ /**
114
+ * Renders an HTML table. Use its props for customizing the appearance.
115
+ *
116
+ * - If you need to use Accordion rows, use `<Tr>`instead of `<tr>`.
117
+ * - If you need to sort columns, use `<Th>`instead of `<th>`.
118
+ *
119
+ * This works exactly like the HTML tag "table".
120
+ *
121
+ * Attention: prefer using the component "SmartTable". Use this only if you need full control over the table.
122
+ */
113
123
  export const Table = withRef(({
114
124
  appearance, stripped, compressed, showBorders, showHeaderBorders, showRowBorders, rounded, roundedRows, uppercaseHeader, className,
115
125
  children, accordionRows, ...props
@@ -12,16 +12,26 @@ export class TabController<Key extends string> extends ValueController<Key> {
12
12
  return this.tabOrder.findIndex(t => t === this.value)
13
13
  }
14
14
 
15
+ /**
16
+ * @returns true if there's another tab after the one currently selected. False otherwise.
17
+ */
15
18
  hasNext(): boolean {
16
19
  const current = this.getCurrentIndex()
17
20
  return current > -1 && current + 1 < this.tabOrder.length
18
21
  }
19
22
 
23
+ /**
24
+ * @returns true if there's a tab before the one currently selected. False otherwise.
25
+ */
20
26
  hasPrevious(): boolean {
21
27
  const current = this.getCurrentIndex()
22
28
  return current > -1 && current - 1 >= 0
23
29
  }
24
30
 
31
+ /**
32
+ * Selects the tab on the right of the one currently selected. If there's no next tab, nothing happens.
33
+ * @returns true if the tab is changed, false otherwise.
34
+ */
25
35
  next(): boolean {
26
36
  if (this.hasNext()) {
27
37
  this.setValue(this.tabOrder[this.getCurrentIndex() + 1])
@@ -30,6 +40,10 @@ export class TabController<Key extends string> extends ValueController<Key> {
30
40
  return false
31
41
  }
32
42
 
43
+ /**
44
+ * Selects the tab on the left of the one currently selected. If there's no previous tab, nothing happens.
45
+ * @returns true if the tab is changed, false otherwise.
46
+ */
33
47
  previous(): boolean {
34
48
  if (this.hasPrevious()) {
35
49
  this.setValue(this.tabOrder[this.getCurrentIndex() - 1])