@stack-spot/citric-react 0.1.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 (345) hide show
  1. package/dist/citric.css +2580 -0
  2. package/dist/components/Accordion.d.ts +33 -0
  3. package/dist/components/Accordion.d.ts.map +1 -0
  4. package/dist/components/Accordion.js +19 -0
  5. package/dist/components/Accordion.js.map +1 -0
  6. package/dist/components/Alert.d.ts +11 -0
  7. package/dist/components/Alert.d.ts.map +1 -0
  8. package/dist/components/Alert.js +5 -0
  9. package/dist/components/Alert.js.map +1 -0
  10. package/dist/components/AsyncContent.d.ts +30 -0
  11. package/dist/components/AsyncContent.d.ts.map +1 -0
  12. package/dist/components/AsyncContent.js +33 -0
  13. package/dist/components/AsyncContent.js.map +1 -0
  14. package/dist/components/Avatar.d.ts +22 -0
  15. package/dist/components/Avatar.d.ts.map +1 -0
  16. package/dist/components/Avatar.js +9 -0
  17. package/dist/components/Avatar.js.map +1 -0
  18. package/dist/components/AvatarGroup.d.ts +25 -0
  19. package/dist/components/AvatarGroup.d.ts.map +1 -0
  20. package/dist/components/AvatarGroup.js +9 -0
  21. package/dist/components/AvatarGroup.js.map +1 -0
  22. package/dist/components/Badge.d.ts +18 -0
  23. package/dist/components/Badge.d.ts.map +1 -0
  24. package/dist/components/Badge.js +7 -0
  25. package/dist/components/Badge.js.map +1 -0
  26. package/dist/components/Blockquote.d.ts +5 -0
  27. package/dist/components/Blockquote.d.ts.map +1 -0
  28. package/dist/components/Blockquote.js +4 -0
  29. package/dist/components/Blockquote.js.map +1 -0
  30. package/dist/components/Breadcrumb.d.ts +12 -0
  31. package/dist/components/Breadcrumb.d.ts.map +1 -0
  32. package/dist/components/Breadcrumb.js +8 -0
  33. package/dist/components/Breadcrumb.js.map +1 -0
  34. package/dist/components/Button.d.ts +42 -0
  35. package/dist/components/Button.d.ts.map +1 -0
  36. package/dist/components/Button.js +25 -0
  37. package/dist/components/Button.js.map +1 -0
  38. package/dist/components/Card.d.ts +19 -0
  39. package/dist/components/Card.d.ts.map +1 -0
  40. package/dist/components/Card.js +5 -0
  41. package/dist/components/Card.js.map +1 -0
  42. package/dist/components/Checkbox.d.ts +14 -0
  43. package/dist/components/Checkbox.d.ts.map +1 -0
  44. package/dist/components/Checkbox.js +7 -0
  45. package/dist/components/Checkbox.js.map +1 -0
  46. package/dist/components/CheckboxGroup.d.ts +53 -0
  47. package/dist/components/CheckboxGroup.d.ts.map +1 -0
  48. package/dist/components/CheckboxGroup.js +17 -0
  49. package/dist/components/CheckboxGroup.js.map +1 -0
  50. package/dist/components/Circle.d.ts +18 -0
  51. package/dist/components/Circle.d.ts.map +1 -0
  52. package/dist/components/Circle.js +5 -0
  53. package/dist/components/Circle.js.map +1 -0
  54. package/dist/components/CitricComponent.d.ts +14 -0
  55. package/dist/components/CitricComponent.d.ts.map +1 -0
  56. package/dist/components/CitricComponent.js +15 -0
  57. package/dist/components/CitricComponent.js.map +1 -0
  58. package/dist/components/Divider.d.ts +14 -0
  59. package/dist/components/Divider.d.ts.map +1 -0
  60. package/dist/components/Divider.js +5 -0
  61. package/dist/components/Divider.js.map +1 -0
  62. package/dist/components/ErrorBoundary.d.ts +32 -0
  63. package/dist/components/ErrorBoundary.d.ts.map +1 -0
  64. package/dist/components/ErrorBoundary.js +46 -0
  65. package/dist/components/ErrorBoundary.js.map +1 -0
  66. package/dist/components/ErrorMessage.d.ts +4 -0
  67. package/dist/components/ErrorMessage.d.ts.map +1 -0
  68. package/dist/components/ErrorMessage.js +7 -0
  69. package/dist/components/ErrorMessage.js.map +1 -0
  70. package/dist/components/FallbackBoundary.d.ts +13 -0
  71. package/dist/components/FallbackBoundary.d.ts.map +1 -0
  72. package/dist/components/FallbackBoundary.js +11 -0
  73. package/dist/components/FallbackBoundary.js.map +1 -0
  74. package/dist/components/Favorite.d.ts +23 -0
  75. package/dist/components/Favorite.d.ts.map +1 -0
  76. package/dist/components/Favorite.js +5 -0
  77. package/dist/components/Favorite.js.map +1 -0
  78. package/dist/components/FieldGroup.d.ts +14 -0
  79. package/dist/components/FieldGroup.d.ts.map +1 -0
  80. package/dist/components/FieldGroup.js +5 -0
  81. package/dist/components/FieldGroup.js.map +1 -0
  82. package/dist/components/Form.d.ts +5 -0
  83. package/dist/components/Form.d.ts.map +1 -0
  84. package/dist/components/Form.js +6 -0
  85. package/dist/components/Form.js.map +1 -0
  86. package/dist/components/FormGroup.d.ts +22 -0
  87. package/dist/components/FormGroup.d.ts.map +1 -0
  88. package/dist/components/FormGroup.js +8 -0
  89. package/dist/components/FormGroup.js.map +1 -0
  90. package/dist/components/IconBox.d.ts +46 -0
  91. package/dist/components/IconBox.d.ts.map +1 -0
  92. package/dist/components/IconBox.js +29 -0
  93. package/dist/components/IconBox.js.map +1 -0
  94. package/dist/components/Input.d.ts +15 -0
  95. package/dist/components/Input.d.ts.map +1 -0
  96. package/dist/components/Input.js +18 -0
  97. package/dist/components/Input.js.map +1 -0
  98. package/dist/components/Link.d.ts +20 -0
  99. package/dist/components/Link.d.ts.map +1 -0
  100. package/dist/components/Link.js +21 -0
  101. package/dist/components/Link.js.map +1 -0
  102. package/dist/components/LoadingPanel.d.ts +2 -0
  103. package/dist/components/LoadingPanel.d.ts.map +1 -0
  104. package/dist/components/LoadingPanel.js +5 -0
  105. package/dist/components/LoadingPanel.js.map +1 -0
  106. package/dist/components/MenuOverlay/Menu.d.ts +6 -0
  107. package/dist/components/MenuOverlay/Menu.d.ts.map +1 -0
  108. package/dist/components/MenuOverlay/Menu.js +100 -0
  109. package/dist/components/MenuOverlay/Menu.js.map +1 -0
  110. package/dist/components/MenuOverlay/context.d.ts +6 -0
  111. package/dist/components/MenuOverlay/context.d.ts.map +1 -0
  112. package/dist/components/MenuOverlay/context.js +16 -0
  113. package/dist/components/MenuOverlay/context.js.map +1 -0
  114. package/dist/components/MenuOverlay/index.d.ts +3 -0
  115. package/dist/components/MenuOverlay/index.d.ts.map +1 -0
  116. package/dist/components/MenuOverlay/index.js +23 -0
  117. package/dist/components/MenuOverlay/index.js.map +1 -0
  118. package/dist/components/MenuOverlay/keyboard.d.ts +2 -0
  119. package/dist/components/MenuOverlay/keyboard.d.ts.map +1 -0
  120. package/dist/components/MenuOverlay/keyboard.js +66 -0
  121. package/dist/components/MenuOverlay/keyboard.js.map +1 -0
  122. package/dist/components/MenuOverlay/types.d.ts +166 -0
  123. package/dist/components/MenuOverlay/types.d.ts.map +1 -0
  124. package/dist/components/MenuOverlay/types.js +2 -0
  125. package/dist/components/MenuOverlay/types.js.map +1 -0
  126. package/dist/components/Overlay/context.d.ts +4 -0
  127. package/dist/components/Overlay/context.d.ts.map +1 -0
  128. package/dist/components/Overlay/context.js +7 -0
  129. package/dist/components/Overlay/context.js.map +1 -0
  130. package/dist/components/Overlay/index.d.ts +14 -0
  131. package/dist/components/Overlay/index.d.ts.map +1 -0
  132. package/dist/components/Overlay/index.js +120 -0
  133. package/dist/components/Overlay/index.js.map +1 -0
  134. package/dist/components/Overlay/types.d.ts +67 -0
  135. package/dist/components/Overlay/types.d.ts.map +1 -0
  136. package/dist/components/Overlay/types.js +2 -0
  137. package/dist/components/Overlay/types.js.map +1 -0
  138. package/dist/components/Pagination.d.ts +28 -0
  139. package/dist/components/Pagination.d.ts.map +1 -0
  140. package/dist/components/Pagination.js +30 -0
  141. package/dist/components/Pagination.js.map +1 -0
  142. package/dist/components/ProgressBar.d.ts +12 -0
  143. package/dist/components/ProgressBar.d.ts.map +1 -0
  144. package/dist/components/ProgressBar.js +7 -0
  145. package/dist/components/ProgressBar.js.map +1 -0
  146. package/dist/components/ProgressCircular.d.ts +16 -0
  147. package/dist/components/ProgressCircular.d.ts.map +1 -0
  148. package/dist/components/ProgressCircular.js +7 -0
  149. package/dist/components/ProgressCircular.js.map +1 -0
  150. package/dist/components/RadioGroup.d.ts +48 -0
  151. package/dist/components/RadioGroup.d.ts.map +1 -0
  152. package/dist/components/RadioGroup.js +17 -0
  153. package/dist/components/RadioGroup.js.map +1 -0
  154. package/dist/components/Rating.d.ts +13 -0
  155. package/dist/components/Rating.d.ts.map +1 -0
  156. package/dist/components/Rating.js +4 -0
  157. package/dist/components/Rating.js.map +1 -0
  158. package/dist/components/Select/RichSelect.d.ts +5 -0
  159. package/dist/components/Select/RichSelect.d.ts.map +1 -0
  160. package/dist/components/Select/RichSelect.js +152 -0
  161. package/dist/components/Select/RichSelect.js.map +1 -0
  162. package/dist/components/Select/SimpleSelect.d.ts +5 -0
  163. package/dist/components/Select/SimpleSelect.d.ts.map +1 -0
  164. package/dist/components/Select/SimpleSelect.js +24 -0
  165. package/dist/components/Select/SimpleSelect.js.map +1 -0
  166. package/dist/components/Select/index.d.ts +4 -0
  167. package/dist/components/Select/index.d.ts.map +1 -0
  168. package/dist/components/Select/index.js +7 -0
  169. package/dist/components/Select/index.js.map +1 -0
  170. package/dist/components/Select/types.d.ts +118 -0
  171. package/dist/components/Select/types.d.ts.map +1 -0
  172. package/dist/components/Select/types.js +2 -0
  173. package/dist/components/Select/types.js.map +1 -0
  174. package/dist/components/SelectBox.d.ts +65 -0
  175. package/dist/components/SelectBox.d.ts.map +1 -0
  176. package/dist/components/SelectBox.js +26 -0
  177. package/dist/components/SelectBox.js.map +1 -0
  178. package/dist/components/Skeleton.d.ts +30 -0
  179. package/dist/components/Skeleton.d.ts.map +1 -0
  180. package/dist/components/Skeleton.js +5 -0
  181. package/dist/components/Skeleton.js.map +1 -0
  182. package/dist/components/Slider.d.ts +32 -0
  183. package/dist/components/Slider.d.ts.map +1 -0
  184. package/dist/components/Slider.js +19 -0
  185. package/dist/components/Slider.js.map +1 -0
  186. package/dist/components/SmartTable.d.ts +87 -0
  187. package/dist/components/SmartTable.d.ts.map +1 -0
  188. package/dist/components/SmartTable.js +16 -0
  189. package/dist/components/SmartTable.js.map +1 -0
  190. package/dist/components/Stepper.d.ts +52 -0
  191. package/dist/components/Stepper.d.ts.map +1 -0
  192. package/dist/components/Stepper.js +53 -0
  193. package/dist/components/Stepper.js.map +1 -0
  194. package/dist/components/Switch.d.ts +10 -0
  195. package/dist/components/Switch.d.ts.map +1 -0
  196. package/dist/components/Switch.js +7 -0
  197. package/dist/components/Switch.js.map +1 -0
  198. package/dist/components/Table.d.ts +106 -0
  199. package/dist/components/Table.d.ts.map +1 -0
  200. package/dist/components/Table.js +86 -0
  201. package/dist/components/Table.js.map +1 -0
  202. package/dist/components/Tabs/TabController.d.ts +11 -0
  203. package/dist/components/Tabs/TabController.d.ts.map +1 -0
  204. package/dist/components/Tabs/TabController.js +39 -0
  205. package/dist/components/Tabs/TabController.js.map +1 -0
  206. package/dist/components/Tabs/index.d.ts +5 -0
  207. package/dist/components/Tabs/index.d.ts.map +1 -0
  208. package/dist/components/Tabs/index.js +37 -0
  209. package/dist/components/Tabs/index.js.map +1 -0
  210. package/dist/components/Tabs/types.d.ts +46 -0
  211. package/dist/components/Tabs/types.d.ts.map +1 -0
  212. package/dist/components/Tabs/types.js +2 -0
  213. package/dist/components/Tabs/types.js.map +1 -0
  214. package/dist/components/Tabs/utils.d.ts +3 -0
  215. package/dist/components/Tabs/utils.d.ts.map +1 -0
  216. package/dist/components/Tabs/utils.js +5 -0
  217. package/dist/components/Tabs/utils.js.map +1 -0
  218. package/dist/components/Text.d.ts +27 -0
  219. package/dist/components/Text.d.ts.map +1 -0
  220. package/dist/components/Text.js +45 -0
  221. package/dist/components/Text.js.map +1 -0
  222. package/dist/components/Textarea.d.ts +8 -0
  223. package/dist/components/Textarea.d.ts.map +1 -0
  224. package/dist/components/Textarea.js +4 -0
  225. package/dist/components/Textarea.js.map +1 -0
  226. package/dist/components/Tooltip.d.ts +25 -0
  227. package/dist/components/Tooltip.d.ts.map +1 -0
  228. package/dist/components/Tooltip.js +18 -0
  229. package/dist/components/Tooltip.js.map +1 -0
  230. package/dist/components/layout.d.ts +46 -0
  231. package/dist/components/layout.d.ts.map +1 -0
  232. package/dist/components/layout.js +18 -0
  233. package/dist/components/layout.js.map +1 -0
  234. package/dist/context/CitricContext.d.ts +3 -0
  235. package/dist/context/CitricContext.d.ts.map +1 -0
  236. package/dist/context/CitricContext.js +3 -0
  237. package/dist/context/CitricContext.js.map +1 -0
  238. package/dist/context/CitricProvider.d.ts +9 -0
  239. package/dist/context/CitricProvider.d.ts.map +1 -0
  240. package/dist/context/CitricProvider.js +8 -0
  241. package/dist/context/CitricProvider.js.map +1 -0
  242. package/dist/context/hooks.d.ts +2 -0
  243. package/dist/context/hooks.d.ts.map +1 -0
  244. package/dist/context/hooks.js +6 -0
  245. package/dist/context/hooks.js.map +1 -0
  246. package/dist/index.d.ts +48 -0
  247. package/dist/index.d.ts.map +1 -0
  248. package/dist/index.js +48 -0
  249. package/dist/index.js.map +1 -0
  250. package/dist/overlay.d.ts +83 -0
  251. package/dist/overlay.d.ts.map +1 -0
  252. package/dist/overlay.js +199 -0
  253. package/dist/overlay.js.map +1 -0
  254. package/dist/theme.css +419 -0
  255. package/dist/types.d.ts +175 -0
  256. package/dist/types.d.ts.map +1 -0
  257. package/dist/types.js +2 -0
  258. package/dist/types.js.map +1 -0
  259. package/dist/utils/ValueController.d.ts +10 -0
  260. package/dist/utils/ValueController.d.ts.map +1 -0
  261. package/dist/utils/ValueController.js +32 -0
  262. package/dist/utils/ValueController.js.map +1 -0
  263. package/dist/utils/acessibility.d.ts +52 -0
  264. package/dist/utils/acessibility.d.ts.map +1 -0
  265. package/dist/utils/acessibility.js +80 -0
  266. package/dist/utils/acessibility.js.map +1 -0
  267. package/dist/utils/css.d.ts +12 -0
  268. package/dist/utils/css.d.ts.map +1 -0
  269. package/dist/utils/css.js +72 -0
  270. package/dist/utils/css.js.map +1 -0
  271. package/dist/utils/options.d.ts +3 -0
  272. package/dist/utils/options.d.ts.map +1 -0
  273. package/dist/utils/options.js +7 -0
  274. package/dist/utils/options.js.map +1 -0
  275. package/package.json +51 -0
  276. package/scripts/build-css.ts +49 -0
  277. package/src/components/Accordion.tsx +74 -0
  278. package/src/components/Alert.tsx +16 -0
  279. package/src/components/AsyncContent.tsx +54 -0
  280. package/src/components/Avatar.tsx +34 -0
  281. package/src/components/AvatarGroup.tsx +40 -0
  282. package/src/components/Badge.tsx +28 -0
  283. package/src/components/Blockquote.tsx +9 -0
  284. package/src/components/Breadcrumb.tsx +24 -0
  285. package/src/components/Button.tsx +88 -0
  286. package/src/components/Card.tsx +32 -0
  287. package/src/components/Checkbox.tsx +36 -0
  288. package/src/components/CheckboxGroup.tsx +93 -0
  289. package/src/components/Circle.tsx +26 -0
  290. package/src/components/CitricComponent.ts +34 -0
  291. package/src/components/Divider.tsx +22 -0
  292. package/src/components/ErrorBoundary.tsx +62 -0
  293. package/src/components/ErrorMessage.tsx +11 -0
  294. package/src/components/FallbackBoundary.tsx +29 -0
  295. package/src/components/Favorite.tsx +37 -0
  296. package/src/components/FieldGroup.tsx +22 -0
  297. package/src/components/Form.tsx +17 -0
  298. package/src/components/FormGroup.tsx +45 -0
  299. package/src/components/IconBox.tsx +78 -0
  300. package/src/components/Input.tsx +32 -0
  301. package/src/components/Link.tsx +40 -0
  302. package/src/components/LoadingPanel.tsx +8 -0
  303. package/src/components/MenuOverlay/Menu.tsx +157 -0
  304. package/src/components/MenuOverlay/context.ts +20 -0
  305. package/src/components/MenuOverlay/index.tsx +35 -0
  306. package/src/components/MenuOverlay/keyboard.ts +60 -0
  307. package/src/components/MenuOverlay/types.ts +178 -0
  308. package/src/components/Overlay/context.ts +10 -0
  309. package/src/components/Overlay/index.tsx +137 -0
  310. package/src/components/Overlay/types.ts +71 -0
  311. package/src/components/Pagination.tsx +90 -0
  312. package/src/components/ProgressBar.tsx +25 -0
  313. package/src/components/ProgressCircular.tsx +29 -0
  314. package/src/components/RadioGroup.tsx +87 -0
  315. package/src/components/Rating.tsx +25 -0
  316. package/src/components/Select/RichSelect.tsx +214 -0
  317. package/src/components/Select/SimpleSelect.tsx +66 -0
  318. package/src/components/Select/index.tsx +8 -0
  319. package/src/components/Select/types.ts +121 -0
  320. package/src/components/SelectBox.tsx +134 -0
  321. package/src/components/Skeleton.tsx +41 -0
  322. package/src/components/Slider.tsx +77 -0
  323. package/src/components/SmartTable.tsx +148 -0
  324. package/src/components/Stepper.tsx +142 -0
  325. package/src/components/Switch.tsx +29 -0
  326. package/src/components/Table.tsx +219 -0
  327. package/src/components/Tabs/TabController.ts +40 -0
  328. package/src/components/Tabs/index.tsx +64 -0
  329. package/src/components/Tabs/types.ts +48 -0
  330. package/src/components/Tabs/utils.ts +6 -0
  331. package/src/components/Text.ts +75 -0
  332. package/src/components/Textarea.tsx +12 -0
  333. package/src/components/Tooltip.tsx +53 -0
  334. package/src/components/layout.tsx +53 -0
  335. package/src/context/CitricContext.tsx +4 -0
  336. package/src/context/CitricProvider.tsx +14 -0
  337. package/src/context/hooks.ts +6 -0
  338. package/src/index.ts +47 -0
  339. package/src/overlay.ts +276 -0
  340. package/src/types.ts +226 -0
  341. package/src/utils/ValueController.ts +28 -0
  342. package/src/utils/acessibility.ts +92 -0
  343. package/src/utils/css.ts +106 -0
  344. package/src/utils/options.ts +7 -0
  345. package/tsconfig.json +10 -0
@@ -0,0 +1,88 @@
1
+ import { listToClass } from '@stack-spot/portal-theme'
2
+ import { useState } from 'react'
3
+ import { useCitricController } from '../context/hooks'
4
+ import { HTMLExtension, WithColorScheme } from '../types'
5
+ import { CitricComponent } from './CitricComponent'
6
+ import { ProgressCircular } from './ProgressCircular'
7
+
8
+ export interface BaseButtonProps extends WithColorScheme {
9
+ /**
10
+ * - contained for buttons with background;
11
+ * - outlined for transparent buttons with borders;
12
+ * - text for buttons with no borders or background.
13
+ * @default 'contained'
14
+ */
15
+ appearance?: 'outlined' | 'text' | 'contained',
16
+ /**
17
+ * Size of the button.
18
+ * @default 'md'
19
+ */
20
+ size?: 'sm' | 'md' | 'lg',
21
+ /**
22
+ * Animated text to show on when the button is clicked.
23
+ */
24
+ feedback?: string,
25
+ /**
26
+ * Whether or not to show a loading feedback instead of the button's content.
27
+ *
28
+ * Buttons in the loading state are also disabled.
29
+ */
30
+ loading?: boolean,
31
+ /**
32
+ * Function to run when the button is clicked. If this function returns a promise, the button will automatically show a loading feedback
33
+ * until the promise completes.
34
+ */
35
+ onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => any,
36
+ /**
37
+ * Whether or not a click in this button should generate analytics data.
38
+ *
39
+ * This only takes effect if there's a CitricController in React's context. The value of `analytics` is passed to the function
40
+ * `onClickButton` of the controller.
41
+ *
42
+ * @default false
43
+ */
44
+ analytics?: boolean,
45
+ }
46
+
47
+ export type ButtonProps = HTMLExtension<'button', BaseButtonProps, 'onClick'>
48
+
49
+ export const Button = (
50
+ { appearance, size, feedback, loading, disabled, onClick, className, children, type = 'button', analytics, ...props }: ButtonProps,
51
+ ) => {
52
+ const citric = useCitricController()
53
+ const [waiting, setWaiting] = useState(false)
54
+ const busy = loading || waiting
55
+
56
+ async function handleClick(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
57
+ const result = onClick?.(e)
58
+ citric?.onClickButton?.(e, analytics ?? false)
59
+ if (result instanceof Promise) {
60
+ setWaiting(true)
61
+ try {
62
+ await result
63
+ } catch { /* empty */ }
64
+ setWaiting(false)
65
+ }
66
+ }
67
+
68
+ return (
69
+ <CitricComponent
70
+ tag="button"
71
+ component="button"
72
+ className={listToClass([size, appearance, className])}
73
+ data-feedback={feedback}
74
+ onClick={handleClick}
75
+ aria-busy={busy}
76
+ disabled={disabled || busy}
77
+ type={type}
78
+ {...props}
79
+ >
80
+ {busy ? <div>{children}</div> : children}
81
+ {busy && (
82
+ <div className="loader">
83
+ <ProgressCircular size="sm" />
84
+ </div>
85
+ )}
86
+ </CitricComponent>
87
+ )
88
+ }
@@ -0,0 +1,32 @@
1
+ import { listToClass } from '@stack-spot/portal-theme'
2
+ import { HTMLExtension, WithColorScheme } from '../types'
3
+ import { CitricComponent } from './CitricComponent'
4
+
5
+ export interface BaseCardProps extends WithColorScheme {
6
+ onClick?: (event: React.MouseEvent<HTMLDivElement>) => void,
7
+ /**
8
+ * The size of the card.
9
+ *
10
+ * @default 'md'
11
+ */
12
+ size?: 'xxs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl',
13
+ /**
14
+ * The background color level. The main color will depend on the "colorScheme", which is "light" by default.
15
+ *
16
+ * @default 300
17
+ */
18
+ bgLevel?: 300 | 400 | 500 | 600,
19
+ }
20
+
21
+ export type CardProps = HTMLExtension<'div', BaseCardProps>
22
+
23
+ export const Card = ({ onClick, size, bgLevel, className, children, ...props }: CardProps) => (
24
+ <CitricComponent
25
+ tag="div"
26
+ component="card"
27
+ className={listToClass([onClick && 'clickable', size, bgLevel && `bg-${bgLevel}`, className])}
28
+ {...props}
29
+ >
30
+ {children}
31
+ </CitricComponent>
32
+ )
@@ -0,0 +1,36 @@
1
+ import { HTMLExtension, WithColorScheme } from '../types'
2
+ import { CitricComponent } from './CitricComponent'
3
+
4
+ export interface BaseCheckboxProps extends WithColorScheme {
5
+ /**
6
+ * "checkbox" for common check boxes, "switch" for toggles.
7
+ * @default 'checkbox'
8
+ */
9
+ appearance?: 'checkbox' | 'switch',
10
+ value?: boolean,
11
+ onChange?: (value: boolean) => void,
12
+ children?: React.ReactNode,
13
+ }
14
+
15
+ export type CheckboxProps = HTMLExtension<'input', BaseCheckboxProps, 'type' | 'onChange' | 'value'>
16
+
17
+ export const Checkbox = (
18
+ { appearance = 'checkbox', value, onChange, colorScheme, children, className, style, ...props }: CheckboxProps,
19
+ ) => {
20
+ const handleChange = onChange ? () => onChange(!value) : undefined
21
+ return children ? (
22
+ <CitricComponent tag="label" component={`${appearance}-row`} colorScheme={colorScheme} style={style} className={className}>
23
+ <input type="checkbox" checked={value} onChange={handleChange} {...props} />
24
+ {children}
25
+ </CitricComponent>
26
+ ) : <CitricComponent
27
+ tag="input"
28
+ type="checkbox"
29
+ component={appearance}
30
+ checked={value}
31
+ onChange={handleChange}
32
+ className={className}
33
+ style={style}
34
+ {...props}
35
+ />
36
+ }
@@ -0,0 +1,93 @@
1
+ import { isNil } from 'lodash'
2
+ import { useMemo } from 'react'
3
+ import { HTMLExtension, WithColorScheme } from '../types'
4
+ import { defaultRenderKey, defaultRenderLabel } from '../utils/options'
5
+ import { CitricComponent } from './CitricComponent'
6
+ import { Column } from './layout'
7
+
8
+ export interface BaseCheckboxGroupProps<T = any> extends WithColorScheme {
9
+ /**
10
+ * "checkbox" for common check boxes, "switch" for toggles.
11
+ * @default 'checkbox'
12
+ */
13
+ appearance?: 'checkbox' | 'switch',
14
+ /**
15
+ * The field name.
16
+ */
17
+ name?: string,
18
+ /**
19
+ * All items corresponding to the selected checkboxes.
20
+ */
21
+ value: T[],
22
+ /**
23
+ * All the items (checkboxes) to render.
24
+ */
25
+ options: T[],
26
+ /**
27
+ * Called whenever the selected checkboxes changes.
28
+ * @param value the currently selected items.
29
+ */
30
+ onChange: (value: T[]) => void,
31
+ /**
32
+ * A function to render the item label.
33
+ * @example
34
+ * `(option) => option.name`
35
+ * @default "the item's toString() result."
36
+ * @param option the item to render.
37
+ * @returns a React Node to render.
38
+ */
39
+ renderLabel?: (option: T) => React.ReactNode,
40
+ /**
41
+ * A function to render the item value, a unique identifier for the option.
42
+ * @example
43
+ * `(option) => option.id`
44
+ * @default "if the item is a string or a number, the stringified item. Otherwise, undefined."
45
+ * @param option the item to compute a key for.
46
+ * @returns a string key.
47
+ */
48
+ renderKey?: (option: T) => string | number | undefined,
49
+ /**
50
+ * If this function returns true for the item, this option is disabled.
51
+ * @default "nothing is disabled"
52
+ * @param option the item to calculate "disabled" for.
53
+ * @returns true if the item should be disabled, false otherwise.
54
+ */
55
+ isDisabled?: (option: T) => boolean,
56
+ }
57
+
58
+ export type CheckboxGroupProps<T> = HTMLExtension<'div', BaseCheckboxGroupProps<T>, 'onChange'>
59
+
60
+ export function CheckboxGroup<T>({
61
+ appearance = 'checkbox',
62
+ name,
63
+ value = [],
64
+ options,
65
+ onChange,
66
+ renderLabel = defaultRenderLabel,
67
+ renderKey = defaultRenderKey,
68
+ isDisabled,
69
+ colorScheme,
70
+ style,
71
+ ...props
72
+ }: CheckboxGroupProps<T>) {
73
+ const items = useMemo(() => {
74
+ const valueKeys = value.map(renderKey)
75
+ return options.map((o) => {
76
+ const key = renderKey(o)
77
+ return (
78
+ <CitricComponent tag="label" component={`${appearance}-row`} key={key} colorScheme={colorScheme}>
79
+ <input
80
+ type="checkbox"
81
+ name={name}
82
+ value={key}
83
+ checked={value.includes(o) || (!isNil(key) && valueKeys.includes(key))}
84
+ onChange={(e) => onChange?.(e.target.checked ? [...(value ?? []), o] : (value?.filter(item => item != o) ?? []))}
85
+ disabled={isDisabled?.(o)}
86
+ />
87
+ {renderLabel(o)}
88
+ </CitricComponent>
89
+ )
90
+ })
91
+ }, [options, value, name, colorScheme, appearance])
92
+ return <Column {...props} style={{ gap: '8px', ...style }}>{items}</Column>
93
+ }
@@ -0,0 +1,26 @@
1
+ import { listToClass } from '@stack-spot/portal-theme'
2
+ import { HTMLExtension, WithColorPalette, WithColorScheme } from '../types'
3
+ import { CitricComponent } from './CitricComponent'
4
+
5
+ export interface BaseCircleProps extends WithColorPalette, WithColorScheme {
6
+ /**
7
+ * Whether or not to show borders.
8
+ *
9
+ * @default false
10
+ */
11
+ showBorders?: boolean,
12
+ /**
13
+ * The size of the circle.
14
+ *
15
+ * @default 'xs'
16
+ */
17
+ size?: 'xxs' | 'xs' | 'sm' | 'lg' | 'xl' | 'xxl',
18
+ }
19
+
20
+ export type CircleProps = HTMLExtension<'div', BaseCircleProps>
21
+
22
+ export const Circle = ({ showBorders, className, size, children, ...props }: CircleProps) => (
23
+ <CitricComponent tag="div" component="circle" className={listToClass([className, showBorders && 'bordered', size])} {...props}>
24
+ {children}
25
+ </CitricComponent>
26
+ )
@@ -0,0 +1,34 @@
1
+ import { createElement, forwardRef } from 'react'
2
+ import { WithColorPalette, WithColorScheme } from '../types'
3
+
4
+ export type CitricComponentName = 'alert' | 'avatar' | 'badge' | 'blockquote' | 'breadcrumb' | 'button' | 'card' | 'checkbox' |
5
+ 'checkbox-row' | 'divider' | 'field-group' | 'form-group' | 'form' | 'icon-box' | 'input' | 'link' | 'pagination' | 'progress-bar' |
6
+ 'progress-circular' | 'radio' | 'radio-row' | 'rating' | 'select' | 'select-box' | 'skeleton' | 'slider' | 'switch' | 'switch-row' |
7
+ 'table' | 'tabs' | 'accordion' | 'favorite' | 'textarea' | 'avatar-group' | 'labeled-slider' | 'rich-select' | 'tooltip' | 'menu' |
8
+ 'circle'
9
+
10
+ interface CustomProps<T extends keyof JSX.IntrinsicElements> extends WithColorScheme, WithColorPalette {
11
+ tag: T,
12
+ component: CitricComponentName,
13
+ }
14
+
15
+ type Props<T extends keyof JSX.IntrinsicElements> = CustomProps<T> & JSX.IntrinsicElements[T]
16
+
17
+ interface CitricComponentType {
18
+ <T extends keyof JSX.IntrinsicElements>(props: Props<T>): React.ReactNode,
19
+ }
20
+
21
+ export function asCitricProps({ colorScheme, colorPalette, component, ...props }: Omit<Props<any>, 'tag'>) {
22
+ const citricProps: Record<string, any> = { ...props, 'data-citric': component }
23
+ if (colorScheme) citricProps['data-color-scheme'] = colorScheme
24
+ if (colorPalette) citricProps['data-color-palette'] = colorPalette
25
+ return citricProps
26
+ }
27
+
28
+ // eslint-disable-next-line react/display-name
29
+ export const CitricComponent: CitricComponentType = forwardRef<Props<any>, any>(
30
+ (props, ref) => {
31
+ const { tag, ...citricProps } = asCitricProps(props)
32
+ return createElement(tag, { ...citricProps, ref })
33
+ },
34
+ )
@@ -0,0 +1,22 @@
1
+ import { listToClass } from '@stack-spot/portal-theme'
2
+ import { HTMLExtension, WithColorScheme } from '../types'
3
+ import { CitricComponent } from './CitricComponent'
4
+
5
+ export interface BaseDividerProps extends WithColorScheme {
6
+ /**
7
+ * @default 'horizontal'
8
+ */
9
+ direction?: 'horizontal' | 'vertical',
10
+ /**
11
+ * @default 'sm'
12
+ */
13
+ size?: 'sm' | 'md' | 'lg',
14
+ }
15
+
16
+ export type DividerProps = HTMLExtension<'hr', BaseDividerProps>
17
+
18
+ export const Divider = ({ size, direction, className, children, ...props }: DividerProps) => (
19
+ <CitricComponent tag="hr" component="divider" className={listToClass([className, size, direction])} {...props}>
20
+ {children}
21
+ </CitricComponent>
22
+ )
@@ -0,0 +1,62 @@
1
+ import { Component } from 'react'
2
+ import { CitricContext } from '../context/CitricContext'
3
+ import { ErrorMessage } from './ErrorMessage'
4
+
5
+ interface State {
6
+ error?: any,
7
+ }
8
+
9
+ interface Props {
10
+ children: React.ReactNode,
11
+ /**
12
+ * Sets a default error string when a string is provided.
13
+ *
14
+ * Replaces the error component, if a function is provided.
15
+ */
16
+ message?: React.ReactNode | ((error: any) => React.ReactNode),
17
+ }
18
+
19
+ /**
20
+ * An Error Boundary that renders an error feedback instead of its content if any of its children throws.
21
+ */
22
+ export class ErrorBoundary extends Component<Props, State> {
23
+ static contextType = CitricContext
24
+ declare context: React.ContextType<typeof CitricContext>
25
+
26
+ constructor(props: Props) {
27
+ super(props)
28
+ this.state = {}
29
+ }
30
+
31
+ static getDerivedStateFromError(error: any) {
32
+ return { error }
33
+ }
34
+
35
+ componentDidCatch(error: any, errorInfo: any) {
36
+ this.context?.onError?.(error)
37
+ // eslint-disable-next-line no-console
38
+ console.error(error, errorInfo)
39
+ }
40
+
41
+ componentDidUpdate(prevProps: Readonly<Props>) {
42
+ if (this.state.error && this.props.children !== prevProps.children) {
43
+ this.setState({})
44
+ }
45
+ }
46
+
47
+ private renderCustomErrorUI() {
48
+ return this.context?.renderError?.(this.state.error)
49
+ }
50
+
51
+ private renderErrorUI() {
52
+ if (typeof this.props.message === 'function') return this.props.message(this.state.error)
53
+ return (this.props.message || !this.context?.renderError)
54
+ ? <ErrorMessage error={this.props.message || this.state.error} />
55
+ : this.renderCustomErrorUI()
56
+ }
57
+
58
+
59
+ render() {
60
+ return this.state.error ? this.renderErrorUI() : this.props.children
61
+ }
62
+ }
@@ -0,0 +1,11 @@
1
+ import { Icon } from '@stack-spot/citric-icons'
2
+ import { theme } from '@stack-spot/portal-theme'
3
+ import { Row } from './layout'
4
+ import { Text } from './Text'
5
+
6
+ export const ErrorMessage = ({ error }: { error: any }) => (
7
+ <Row gap="18px" p="20px" justifyContent="center">
8
+ <Icon icon="TimesCircle" size="xl" style={{ color: theme.color.danger[500] }} />
9
+ <Text style={{ margin: '80px' }}>{error.message || `${error}`}</Text>
10
+ </Row>
11
+ )
@@ -0,0 +1,29 @@
1
+ import { Suspense } from 'react'
2
+ import { ErrorBoundary } from './ErrorBoundary'
3
+ import { Center } from './layout'
4
+ import { ProgressCircular } from './ProgressCircular'
5
+
6
+ interface Props {
7
+ /**
8
+ * Replaces the error message with this text.
9
+ */
10
+ message?: string,
11
+ children: React.ReactNode,
12
+ }
13
+
14
+ const loading = (
15
+ <Center flex={1} p="80px" data-test-hint="loading">
16
+ <ProgressCircular />
17
+ </Center>
18
+ )
19
+
20
+ /**
21
+ * Fallbacks for errors and loadings (suspense).
22
+ */
23
+ export const FallbackBoundary = ({ children, message }: Props) => (
24
+ <ErrorBoundary message={message}>
25
+ <Suspense fallback={loading}>
26
+ {children}
27
+ </Suspense>
28
+ </ErrorBoundary>
29
+ )
@@ -0,0 +1,37 @@
1
+ import { listToClass } from '@stack-spot/portal-theme'
2
+ import { HTMLExtension } from '../types'
3
+ import { CitricComponent } from './CitricComponent'
4
+
5
+ type FavoriteAppearance = 'square' | 'circle' | 'text'
6
+
7
+ export interface BaseFavoriteProps {
8
+ value: boolean | undefined,
9
+ /**
10
+ * Size of the button.
11
+ *
12
+ * @default 'md'
13
+ */
14
+ size?: 'sm' | 'md' | 'lg' | 'fit-parent',
15
+ /**
16
+ * Button appearance.
17
+ *
18
+ * @default 'text'
19
+ */
20
+ appearance?: FavoriteAppearance,
21
+ onChange: (value: boolean) => void,
22
+ isLoading?: boolean,
23
+ }
24
+
25
+ export type FavoriteProps = HTMLExtension<'input', BaseFavoriteProps, 'type' | 'checked' | 'onChange' | 'size' | 'value'>
26
+
27
+ export const Favorite = ({ value, size, appearance, onChange, isLoading, className, ...props }: FavoriteProps) =>
28
+ <CitricComponent
29
+ tag="input"
30
+ type="checkbox"
31
+ component="favorite"
32
+ className={listToClass([appearance, size, className])}
33
+ checked={!!value}
34
+ onChange={() => onChange(!value)}
35
+ aria-busy={isLoading}
36
+ {...props}
37
+ />
@@ -0,0 +1,22 @@
1
+ import { listToClass } from '@stack-spot/portal-theme'
2
+ import { HTMLExtension } from '../types'
3
+ import { CitricComponent } from './CitricComponent'
4
+
5
+ export interface BaseFieldGroupProps {
6
+ /**
7
+ * When this is true, the component will automatically mark the addons.
8
+ *
9
+ * When this is false, you must add the class "addon" manually to each of the addons.
10
+ *
11
+ * @default true
12
+ */
13
+ auto?: boolean,
14
+ }
15
+
16
+ export type FieldGroupProps = HTMLExtension<'div', BaseFieldGroupProps>
17
+
18
+ export const FieldGroup = ({ auto = true, className, children, ...props }: FieldGroupProps) => (
19
+ <CitricComponent tag="div" component="field-group" className={listToClass([className, auto && 'auto'])} {...props}>
20
+ {children}
21
+ </CitricComponent>
22
+ )
@@ -0,0 +1,17 @@
1
+ import { listToClass } from '@stack-spot/portal-theme'
2
+ import { CitricComponent } from './CitricComponent'
3
+
4
+ export type FormProps = JSX.IntrinsicElements['form']
5
+ export type ButtonGroupProps = JSX.IntrinsicElements['div']
6
+
7
+ export const Form = ({ children, ...props }: FormProps) => (
8
+ <CitricComponent tag="form" component="form" {...props}>
9
+ {children}
10
+ </CitricComponent>
11
+ )
12
+
13
+ export const ButtonGroup = ({ className, children, ...props }: ButtonGroupProps) => (
14
+ <div className={listToClass([className, 'button-group'])} {...props}>
15
+ {children}
16
+ </div>
17
+ )
@@ -0,0 +1,45 @@
1
+ import { listToClass } from '@stack-spot/portal-theme'
2
+ import { HTMLExtension } from '../types'
3
+ import { CitricComponent } from './CitricComponent'
4
+ import { IconButton } from './IconBox'
5
+ import { Text } from './Text'
6
+ import { Tooltip } from './Tooltip'
7
+
8
+ export interface BaseFormGroupProps {
9
+ /**
10
+ * The error string to show, if any. The empty string is equivalent to undefined, i.e. no errors.
11
+ */
12
+ error?: string,
13
+ /**
14
+ * The input's label.
15
+ */
16
+ label?: string,
17
+ /**
18
+ * A help string, to show as a tooltip.
19
+ */
20
+ help?: string,
21
+ /**
22
+ * A warning string, to show right below the field, at all times.
23
+ */
24
+ warning?: string,
25
+ }
26
+
27
+ export type FormGroupProps = HTMLExtension<'div', BaseFormGroupProps>
28
+
29
+ export const FormGroup = ({ error, help, label, warning, className, children, ...props }: FormGroupProps) => (
30
+ <CitricComponent tag="div" component="form-group" className={listToClass([className, error && 'error'])} {...props}>
31
+ <label>
32
+ {help ? (
33
+ <div className="row">
34
+ <Text>{label}</Text>
35
+ <Tooltip content={help}>
36
+ <IconButton icon="InfoCircle" size="xs" />
37
+ </Tooltip>
38
+ </div>
39
+ ): label}
40
+ {children}
41
+ <div className="feedback">{error}</div>
42
+ {warning && <div className="warning">{warning}</div>}
43
+ </label>
44
+ </CitricComponent>
45
+ )
@@ -0,0 +1,78 @@
1
+ import { IconGroup, IconOwnProps } from '@stack-spot/citric-icons'
2
+ import { listToClass } from '@stack-spot/portal-theme'
3
+ import { useCallback } from 'react'
4
+ import { useCitricController } from '../context/hooks'
5
+ import { HTMLExtension, WithColorPalette, WithColorScheme } from '../types'
6
+ import { CitricComponent } from './CitricComponent'
7
+
8
+ type IconBoxTag = 'a' | 'button' | 'i' | 'span' | 'div'
9
+
10
+ export interface BaseIconBoxProps<T extends IconBoxTag, G extends IconGroup> extends IconOwnProps<G>, WithColorScheme, WithColorPalette {
11
+ /**
12
+ * The HTML element to render.
13
+ *
14
+ * @default 'i'
15
+ */
16
+ tag?: T,
17
+ /**
18
+ * The box appearance.
19
+ *
20
+ * @default 'circle'
21
+ */
22
+ appearance?: 'circle' | 'square',
23
+ /**
24
+ * Size of the box.
25
+ *
26
+ * @default 'sm'
27
+ */
28
+ size?: 'xs' | 'sm' | 'md' | 'lg',
29
+ /**
30
+ * Only valid if `tag` is "button" or "a".
31
+ *
32
+ * Whether or not a click in this button/link should generate analytics data.
33
+ *
34
+ * This only takes effect if there's a CitricController in React's context. The value of `analytics` is passed to the function
35
+ * `onClickButton/onClickLink` of the controller.
36
+ *
37
+ * @default false
38
+ */
39
+ analytics?: boolean,
40
+ }
41
+
42
+ export type IconBoxProps<T extends IconBoxTag, G extends IconGroup> = HTMLExtension<T, BaseIconBoxProps<T, G>>
43
+
44
+ export function IconBox<T extends IconBoxTag = 'i', G extends IconGroup = 'outline'>(
45
+ { group, icon, tag, colorScheme, colorPalette, appearance, size, className, analytics, onClick, ...props }: IconBoxProps<T, G>,
46
+ ) {
47
+ const citric = useCitricController()
48
+
49
+ const handleClick = useCallback((e: React.MouseEvent<any>) => {
50
+ onClick?.(e)
51
+ if (tag === 'button') citric?.onClickButton?.(e, analytics ?? false)
52
+ else if (tag === 'a') citric?.onClickLink?.(e, analytics ?? false)
53
+ }, [tag])
54
+
55
+ return <CitricComponent
56
+ tag={(tag || 'i') as any}
57
+ component="icon-box"
58
+ className={listToClass(['citric-icon', group || 'outline', icon, appearance, size, className])}
59
+ colorScheme={colorScheme}
60
+ colorPalette={colorPalette}
61
+ onClick={['button', 'a'].includes(tag ?? '') ? handleClick : onClick}
62
+ {...props}
63
+ />
64
+ }
65
+
66
+ /**
67
+ * Shortcut to <IconBox tag="button" />
68
+ */
69
+ export function IconButton<G extends IconGroup = 'outline'>(props: Omit<IconBoxProps<'button', G>, 'tag'>) {
70
+ return IconBox({ ...props, tag: 'button', type: props.type || 'button' })
71
+ }
72
+
73
+ /**
74
+ * Shortcut to <IconBox tag="a" />
75
+ */
76
+ export function IconLink<G extends IconGroup = 'outline'>(props: Omit<IconBoxProps<'a', G>, 'tag'>) {
77
+ return IconBox({ ...props, tag: 'a' })
78
+ }
@@ -0,0 +1,32 @@
1
+ import { HTMLExtension, WithColorScheme } from '../types'
2
+ import { CitricComponent } from './CitricComponent'
3
+
4
+ export type SupportedInputType = 'color' | 'date' | 'datetime-local' | 'email' | 'month' | 'number' | 'password' | 'search' | 'tel' |
5
+ 'text' | 'time' | 'url' | 'week'
6
+
7
+ export interface BaseInputProps<T extends SupportedInputType> extends WithColorScheme {
8
+ /**
9
+ * @default 'text'
10
+ */
11
+ type?: T,
12
+ value?: T extends 'number' ? number : string,
13
+ onChange?: (value: T extends 'number' ? (number | undefined) : string) => void,
14
+ }
15
+
16
+ export type InputProps<T extends SupportedInputType = 'text'> =
17
+ HTMLExtension<'input', BaseInputProps<T> & { type?: T }, 'type' | 'value' | 'onChange'>
18
+
19
+ export function Input<T extends SupportedInputType = 'text'>({ type, value, onChange, ...props }: InputProps<T>) {
20
+ function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
21
+ if (!onChange) return
22
+ const newValue = e.target.value
23
+ if (type === 'number') {
24
+ const parsed = newValue ? parseFloat(newValue) : undefined
25
+ onChange(parsed as any)
26
+ } else {
27
+ onChange(newValue as any)
28
+ }
29
+ }
30
+
31
+ return <CitricComponent tag="input" component="input" type={type} value={value} onChange={handleChange} {...props} />
32
+ }