@stack-spot/citric-react 0.22.0 → 0.24.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
@@ -10,204 +10,207 @@ import { ProgressCircular } from '../ProgressCircular'
10
10
  import { SimpleSelect } from './SimpleSelect'
11
11
  import { SelectProps } from './types'
12
12
 
13
- function _RichSelect<T>({
14
- ref,
15
- options,
16
- value,
17
- onChange,
18
- renderLabel = defaultRenderLabel,
19
- renderKey = defaultRenderKey,
20
- required = true,
21
- disabled,
22
- loading,
23
- renderOption,
24
- renderHeader,
25
- searchable,
26
- maxHeight,
27
- style,
28
- className,
29
- onFocus,
30
- onBlur,
31
- showArrow,
32
- ...props
33
- }: SelectProps<T> & { type?: 'rich' },
34
- ) {
35
- const [search, setSearch] = useState('')
36
- const _element = useRef<HTMLDivElement | null>(null)
37
- const element = ref ?? _element
38
- const [open, setOpen] = useState(false)
39
- const [focused, setFocused] = useState(false)
40
- const t = useTranslate(dictionary)
13
+ export const RichSelect = withRef(
14
+ function RichSelect<T>({
15
+ ref,
16
+ options,
17
+ value,
18
+ onChange,
19
+ renderLabel = defaultRenderLabel,
20
+ renderKey = defaultRenderKey,
21
+ required = true,
22
+ disabled,
23
+ loading,
24
+ renderOption,
25
+ renderHeader,
26
+ searchable,
27
+ maxHeight,
28
+ style,
29
+ className,
30
+ onFocus,
31
+ onBlur,
32
+ showArrow,
33
+ ...props
34
+ }: SelectProps<T> & { type?: 'rich' },
35
+ ) {
36
+ const [search, setSearch] = useState('')
37
+ const _element = useRef<HTMLDivElement | null>(null)
38
+ const element = ref ?? _element
39
+ const [open, setOpen] = useState(false)
40
+ const [focused, setFocused] = useState(false)
41
+ const t = useTranslate(dictionary)
41
42
 
42
- const change = useCallback((option: T | undefined) => () => {
43
- onChange?.(option)
44
- setOpen(false)
45
- }, [])
46
-
47
- const renderedOptions = useMemo(() => {
48
- const items = required ? [] : [<li key="" className="empty" onClick={change(undefined)}>{renderLabel(undefined) || t.empty}</li>]
49
- options.forEach((o) => {
50
- const key = renderKey(o)
51
- const label = renderLabel(o)
52
- if (!search.trim() || label.toLocaleLowerCase().includes(search.trim().toLocaleLowerCase())) {
53
- items.push(
54
- <li key={key} onClick={change(o)}>
55
- {renderOption?.(o) ?? label}
56
- </li>,
57
- )
58
- }
59
- })
60
- return items
61
- }, [options, value, required, search])
43
+ const change = useCallback((option: T | undefined) => () => {
44
+ onChange?.(option)
45
+ setOpen(false)
46
+ }, [])
62
47
 
63
- /* this runs whenever the selection panel is opened */
64
- useEffect(() => {
65
- if (open) {
66
- setSearch('')
67
- const selectionPanel = element.current?.querySelector('.selection-panel') as HTMLElement | undefined
68
- selectionPanel?.querySelector('ul')?.scrollTo({ top: 0 })
69
- const getCurrent = () => selectionPanel?.querySelector('li.focused') as HTMLElement | undefined
70
- const scrollTo = (li: HTMLElement) => {
71
- const ul = li.closest('ul')
72
- if (!ul) return
73
- const { top: ulTop, height: ulHeight } = ul.getBoundingClientRect()
74
- const { height: liHeight, top: liTop } = li.getBoundingClientRect()
75
- const offset = liTop + ul.scrollTop - ulTop
76
- if ((ul.scrollTop + ulHeight < offset + liHeight) || ul.scrollTop > offset) {
77
- ul.scrollTo({ top: offset })
78
- }
79
- }
80
- /* keyboard and mouse controls */
81
- const listenToMouse = (event: Event) => {
82
- if (!selectionPanel?.contains(event.target as HTMLElement)) {
83
- setOpen(false)
84
- }
85
- }
86
- const listenToKeyboard = (event: KeyboardEvent) => {
87
- const isCharacter = event.key.length === 1
88
- if (['Escape', 'ArrowUp', 'ArrowDown', 'Enter'].includes(event.key) || (searchable && (isCharacter || event.key === 'Backspace'))) {
89
- event.preventDefault()
90
- event.stopPropagation()
48
+ const renderedOptions = useMemo(() => {
49
+ const items = required ? [] : [<li key="" className="empty" onClick={change(undefined)}>{renderLabel(undefined) || t.empty}</li>]
50
+ options.forEach((o) => {
51
+ const key = renderKey(o)
52
+ const label = renderLabel(o)
53
+ if (!search.trim() || label.toLocaleLowerCase().includes(search.trim().toLocaleLowerCase())) {
54
+ items.push(
55
+ <li key={key} onClick={change(o)}>
56
+ {renderOption?.(o) ?? label}
57
+ </li>,
58
+ )
91
59
  }
92
- if (event.key === 'Escape') setOpen(false)
93
- if (searchable) {
94
- if (isCharacter) setSearch(v => `${v}${event.key}`)
95
- if (event.key === 'Backspace') setSearch(v => v.substring(0, v.length - 1))
60
+ })
61
+ return items
62
+ }, [options, value, required, search])
63
+
64
+ /* this runs whenever the selection panel is opened */
65
+ useEffect(() => {
66
+ if (open) {
67
+ setSearch('')
68
+ const selectionPanel = element.current?.querySelector('.selection-panel') as HTMLElement | undefined
69
+ selectionPanel?.querySelector('ul')?.scrollTo({ top: 0 })
70
+ const getCurrent = () => selectionPanel?.querySelector('li.focused') as HTMLElement | undefined
71
+ const scrollTo = (li: HTMLElement) => {
72
+ const ul = li.closest('ul')
73
+ if (!ul) return
74
+ const { top: ulTop, height: ulHeight } = ul.getBoundingClientRect()
75
+ const { height: liHeight, top: liTop } = li.getBoundingClientRect()
76
+ const offset = liTop + ul.scrollTop - ulTop
77
+ if ((ul.scrollTop + ulHeight < offset + liHeight) || ul.scrollTop > offset) {
78
+ ul.scrollTo({ top: offset })
79
+ }
96
80
  }
97
- if (event.key === 'ArrowDown') {
98
- const current = getCurrent()
99
- const next = (current?.nextElementSibling ?? selectionPanel?.querySelector('li')) as HTMLAreaElement | undefined
100
- if (next) {
101
- current?.classList.remove('focused')
102
- next.classList.add('focused')
103
- scrollTo(next)
81
+ /* keyboard and mouse controls */
82
+ const listenToMouse = (event: Event) => {
83
+ if (!selectionPanel?.contains(event.target as HTMLElement)) {
84
+ setOpen(false)
104
85
  }
105
86
  }
106
- if (event.key === 'ArrowUp') {
107
- const current = getCurrent()
108
- const prev = (current?.previousElementSibling ?? selectionPanel?.querySelector('li:last-child')) as HTMLAreaElement | undefined
109
- if (prev) {
110
- current?.classList.remove('focused')
111
- prev.classList.add('focused')
112
- scrollTo(prev)
87
+ const listenToKeyboard = (event: KeyboardEvent) => {
88
+ const isCharacter = event.key.length === 1
89
+ if (['Escape', 'ArrowUp', 'ArrowDown', 'Enter'].includes(event.key) ||
90
+ (searchable && (isCharacter || event.key === 'Backspace'))) {
91
+ event.preventDefault()
92
+ event.stopPropagation()
93
+ }
94
+ if (event.key === 'Escape') setOpen(false)
95
+ if (searchable) {
96
+ if (isCharacter) setSearch(v => `${v}${event.key}`)
97
+ if (event.key === 'Backspace') setSearch(v => v.substring(0, v.length - 1))
98
+ }
99
+ if (event.key === 'ArrowDown') {
100
+ const current = getCurrent()
101
+ const next = (current?.nextElementSibling ?? selectionPanel?.querySelector('li')) as HTMLAreaElement | undefined
102
+ if (next) {
103
+ current?.classList.remove('focused')
104
+ next.classList.add('focused')
105
+ scrollTo(next)
106
+ }
107
+ }
108
+ if (event.key === 'ArrowUp') {
109
+ const current = getCurrent()
110
+ const prev = (current?.previousElementSibling ?? selectionPanel?.querySelector('li:last-child')) as HTMLAreaElement | undefined
111
+ if (prev) {
112
+ current?.classList.remove('focused')
113
+ prev.classList.add('focused')
114
+ scrollTo(prev)
115
+ }
116
+ }
117
+ if (event.key === 'Enter') {
118
+ setTimeout(() => getCurrent()?.click(), 0)
113
119
  }
114
120
  }
115
- if (event.key === 'Enter') {
116
- setTimeout(() => getCurrent()?.click(), 0)
121
+ // below, we wait 20ms so the same click that opened the select doesn't close it. Removing it will cause problems with selects under
122
+ // labels.
123
+ setTimeout(() => document.addEventListener('click', listenToMouse), 20)
124
+ document.addEventListener('keydown', listenToKeyboard)
125
+ return () => {
126
+ document.removeEventListener('click', listenToMouse)
127
+ document.removeEventListener('keydown', listenToKeyboard)
128
+ getCurrent()?.classList.remove('focused')
117
129
  }
118
130
  }
119
- // below, we wait 20ms so the same click that opened the select doesn't close it. Removing it will cause problems with selects under
120
- // labels.
121
- setTimeout(() => document.addEventListener('click', listenToMouse), 20)
122
- document.addEventListener('keydown', listenToKeyboard)
123
- return () => {
124
- document.removeEventListener('click', listenToMouse)
125
- document.removeEventListener('keydown', listenToKeyboard)
126
- getCurrent()?.classList.remove('focused')
127
- }
128
- }
129
- }, [open])
131
+ }, [open])
130
132
 
131
- /* this runs whenever the select is focused */
132
- useEffect(() => {
133
- if (focused) {
134
- const listenToMouse = (event: MouseEvent) => {
135
- if (!element.current?.contains(event.target as HTMLElement)) {
136
- setFocused(false)
133
+ /* this runs whenever the select is focused */
134
+ useEffect(() => {
135
+ if (focused) {
136
+ const listenToMouse = (event: MouseEvent) => {
137
+ if (!element.current?.contains(event.target as HTMLElement)) {
138
+ setFocused(false)
139
+ }
137
140
  }
138
- }
139
- const listenToKeyboard = (event: KeyboardEvent) => {
140
- if (['Enter', 'ArrowDown', 'ArrowUp'].includes(event.key)) {
141
- event.preventDefault()
142
- if (!element.current?.classList.contains('open')) setOpen(true)
141
+ const listenToKeyboard = (event: KeyboardEvent) => {
142
+ if (['Enter', 'ArrowDown', 'ArrowUp'].includes(event.key)) {
143
+ event.preventDefault()
144
+ if (!element.current?.classList.contains('open')) setOpen(true)
145
+ }
146
+ if (event.key === 'Tab') {
147
+ setFocused(false)
148
+ if (element.current?.classList.contains('open')) setOpen(false)
149
+ }
143
150
  }
144
- if (event.key === 'Tab') {
145
- setFocused(false)
146
- if (element.current?.classList.contains('open')) setOpen(false)
151
+ document.addEventListener('click', listenToMouse)
152
+ document.addEventListener('keydown', listenToKeyboard)
153
+ return () => {
154
+ document.removeEventListener('click', listenToMouse)
155
+ document.removeEventListener('keydown', listenToKeyboard)
147
156
  }
148
157
  }
149
- document.addEventListener('click', listenToMouse)
150
- document.addEventListener('keydown', listenToKeyboard)
151
- return () => {
152
- document.removeEventListener('click', listenToMouse)
153
- document.removeEventListener('keydown', listenToKeyboard)
154
- }
155
- }
156
- }, [focused])
158
+ }, [focused])
157
159
 
158
- useEffect(() => {
159
- if (disabled) {
160
- setOpen(false)
161
- setFocused(false)
162
- }
163
- }, [disabled])
160
+ useEffect(() => {
161
+ if (disabled) {
162
+ setOpen(false)
163
+ setFocused(false)
164
+ }
165
+ }, [disabled])
164
166
 
165
- return (
166
- <CitricComponent
167
- tag="div"
168
- component="rich-select"
169
- style={maxHeight ? applyCSSVariable(style, 'max-height', `${maxHeight}px`) : style}
170
- className={listToClass([className, showArrow === false && 'hide-arrow', open && 'open', focused && 'focused'])}
171
- ref={element}
172
- aria-busy={loading}
173
- {...props}
174
- >
175
- <SimpleSelect
176
- options={options}
177
- value={value}
178
- renderLabel={renderLabel}
179
- renderKey={renderKey}
180
- required={required}
181
- disabled={disabled}
182
- onChange={onChange}
183
- onFocus={onFocus}
184
- onBlur={onBlur}
185
- wrap={false}
186
- />
187
- <header
188
- onClick={(e) => {
189
- if (disabled) return
190
- if (!open) e.stopPropagation()
191
- setFocused(true)
192
- setOpen(true)
193
- }}
194
- aria-hidden
167
+ return (
168
+ <CitricComponent
169
+ tag="div"
170
+ component="rich-select"
171
+ style={maxHeight ? applyCSSVariable(style, 'max-height', `${maxHeight}px`) : style}
172
+ className={listToClass([className, showArrow === false && 'hide-arrow', open && 'open', focused && 'focused'])}
173
+ ref={element}
174
+ aria-busy={loading}
175
+ {...props}
195
176
  >
196
- {(renderHeader?.(value) ?? renderOption?.(value) ?? renderLabel(value)) || <span></span>}
197
- {loading && <ProgressCircular size="xs" className="loader" />}
198
- </header>
199
- <div className="selection-panel" aria-hidden>
200
- {searchable && <div className="search-bar">
201
- <div data-citric="field-group" className="auto">
202
- <i data-citric="icon-box" className="citric-icon outline Search"></i>
203
- <Input type="search" value={search} onChange={setSearch} tabIndex={-1} />
204
- </div>
205
- </div>}
206
- <ul>{renderedOptions}</ul>
207
- </div>
208
- </CitricComponent>
209
- )
210
- }
177
+ <SimpleSelect
178
+ options={options}
179
+ value={value}
180
+ renderLabel={renderLabel}
181
+ renderKey={renderKey}
182
+ required={required}
183
+ disabled={disabled}
184
+ onChange={onChange}
185
+ onFocus={onFocus}
186
+ onBlur={onBlur}
187
+ wrap={false}
188
+ />
189
+ <header
190
+ onClick={(e) => {
191
+ if (disabled) return
192
+ if (!open) e.stopPropagation()
193
+ setFocused(true)
194
+ setOpen(true)
195
+ }}
196
+ aria-hidden
197
+ >
198
+ {(renderHeader?.(value) ?? renderOption?.(value) ?? renderLabel(value)) || <span></span>}
199
+ {loading && <ProgressCircular size="xs" className="loader" />}
200
+ </header>
201
+ <div className="selection-panel" aria-hidden>
202
+ {searchable && <div className="search-bar">
203
+ <div data-citric="field-group" className="auto">
204
+ <i data-citric="icon-box" className="citric-icon outline Search"></i>
205
+ <Input type="search" value={search} onChange={setSearch} tabIndex={-1} />
206
+ </div>
207
+ </div>}
208
+ <ul>{renderedOptions}</ul>
209
+ </div>
210
+ </CitricComponent>
211
+ )
212
+ },
213
+ )
211
214
 
212
215
  const dictionary = {
213
216
  en: {
@@ -217,5 +220,3 @@ const dictionary = {
217
220
  empty: 'Vazio',
218
221
  },
219
222
  }
220
-
221
- export const RichSelect = withRef(_RichSelect)
@@ -5,69 +5,69 @@ import { CitricComponent } from '../CitricComponent'
5
5
  import { ProgressCircular } from '../ProgressCircular'
6
6
  import { SelectProps } from './types'
7
7
 
8
- export function _SimpleSelect<T>({
9
- ref,
10
- options,
11
- value,
12
- onChange,
13
- renderLabel = defaultRenderLabel,
14
- renderKey = defaultRenderKey,
15
- required = true,
16
- loading,
17
- disabled,
18
- onBlur,
19
- onFocus,
20
- wrap,
21
- ...props
22
- }: SelectProps<T> & { wrap?: boolean },
23
- ) {
24
- const handleChange = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {
25
- const selectedIndex = e.target.options.selectedIndex - (e.target.options.length > options.length ? 1 : 0)
26
- onChange?.(selectedIndex >= 0 ? options[selectedIndex] : undefined)
27
- }, [options])
8
+ export const SimpleSelect = withRef(
9
+ function SimpleSelect<T>({
10
+ ref,
11
+ options,
12
+ value,
13
+ onChange,
14
+ renderLabel = defaultRenderLabel,
15
+ renderKey = defaultRenderKey,
16
+ required = true,
17
+ loading,
18
+ disabled,
19
+ onBlur,
20
+ onFocus,
21
+ wrap,
22
+ ...props
23
+ }: SelectProps<T> & { wrap?: boolean },
24
+ ) {
25
+ const handleChange = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {
26
+ const selectedIndex = e.target.options.selectedIndex - (e.target.options.length > options.length ? 1 : 0)
27
+ onChange?.(selectedIndex >= 0 ? options[selectedIndex] : undefined)
28
+ }, [options])
28
29
 
29
- const renderedOptions = useMemo(() => {
30
- const items = (!value || !required) ? [<option key="">{renderLabel(undefined)}</option>] : []
31
- options.forEach((o) => {
32
- const key = renderKey(o)
33
- items.push(
34
- <option key={key} value={key}>
35
- {renderLabel(o)}
36
- </option>,
37
- )
38
- })
39
- return items
40
- }, [options, value, required])
30
+ const renderedOptions = useMemo(() => {
31
+ const items = (!value || !required) ? [<option key="">{renderLabel(undefined)}</option>] : []
32
+ options.forEach((o) => {
33
+ const key = renderKey(o)
34
+ items.push(
35
+ <option key={key} value={key}>
36
+ {renderLabel(o)}
37
+ </option>,
38
+ )
39
+ })
40
+ return items
41
+ }, [options, value, required])
41
42
 
42
- return wrap === false ? (
43
- <CitricComponent
44
- tag="select"
45
- ref={ref as MutableRefObject<HTMLSelectElement | null>}
46
- required={required}
47
- onChange={handleChange}
48
- disabled={disabled || loading}
49
- component="select"
50
- onFocus={onFocus}
51
- onBlur={onBlur}
52
- value={value ? renderKey(value) : ''}
53
- >
54
- {renderedOptions}
55
- </CitricComponent>
56
- ) : (
57
- <CitricComponent ref={ref as MutableRefObject<HTMLDivElement | null>} tag="div" component="select" aria-busy={loading} {...props}>
58
- <select
59
- value={value ? renderKey(value) : undefined}
43
+ return wrap === false ? (
44
+ <CitricComponent
45
+ tag="select"
46
+ ref={ref as MutableRefObject<HTMLSelectElement | null>}
60
47
  required={required}
61
48
  onChange={handleChange}
62
49
  disabled={disabled || loading}
50
+ component="select"
63
51
  onFocus={onFocus}
64
52
  onBlur={onBlur}
53
+ value={value ? renderKey(value) : ''}
65
54
  >
66
55
  {renderedOptions}
67
- </select>
68
- {loading && <ProgressCircular className="loader" size="xs" />}
69
- </CitricComponent>
70
- )
71
- }
72
-
73
- export const SimpleSelect = withRef(_SimpleSelect)
56
+ </CitricComponent>
57
+ ) : (
58
+ <CitricComponent ref={ref as MutableRefObject<HTMLDivElement | null>} tag="div" component="select" aria-busy={loading} {...props}>
59
+ <select
60
+ value={value ? renderKey(value) : undefined}
61
+ required={required}
62
+ onChange={handleChange}
63
+ disabled={disabled || loading}
64
+ onFocus={onFocus}
65
+ onBlur={onBlur}
66
+ >
67
+ {renderedOptions}
68
+ </select>
69
+ {loading && <ProgressCircular className="loader" size="xs" />}
70
+ </CitricComponent>
71
+ )
72
+ },
73
+ )
@@ -4,8 +4,32 @@ import { SimpleSelect } from './SimpleSelect'
4
4
  import { SelectProps } from './types'
5
5
  export type * from './types'
6
6
 
7
- export function _Select<T>(props: SelectProps<T>) {
8
- return props.type === 'simple' ? <SimpleSelect {...props} /> : <RichSelect {...props} />
9
- }
10
-
11
- export const Select = withRef(_Select)
7
+ /**
8
+ * Renders a Select form component. By default, it renders a rich, fully styled component (rich = true). You can set `rich = false` to use
9
+ * only the default select of the browser.
10
+ *
11
+ * The default select from the browser is also rendered when `rich = true`, but it is only accessible when navigating with the keyboard
12
+ * (accessibility).
13
+ *
14
+ * This Select is searchable! You just need to set `selectable = true`.
15
+ *
16
+ * Attention: this component doesn't accept children, instead of manually writing the tag "option", use the property "options".
17
+ *
18
+ * @example
19
+ *
20
+ * ```
21
+ * const options: = useMemo(() => [
22
+ * { id: 1, name: 'Lorem' },
23
+ * { id: 2, name: 'Ipsum' },
24
+ * { id: 3, name: 'Dolor' },
25
+ * ], [])
26
+ * const [value, setValue] = useState(options[0])
27
+ *
28
+ * return <Select options={options} renderLabel={o => o.name} renderKey={o => o.id} value={value} onChange={setValue} />
29
+ * ```
30
+ */
31
+ export const Select = withRef(
32
+ function Select<T>(props: SelectProps<T>) {
33
+ return props.type === 'simple' ? <SimpleSelect {...props} /> : <RichSelect {...props} />
34
+ },
35
+ )
@@ -1,6 +1,6 @@
1
1
  import { WithColorScheme } from '../../types'
2
2
 
3
- interface CommonSelectProps<T> extends WithColorScheme {
3
+ export interface CommonSelectProps<T> extends WithColorScheme {
4
4
  /**
5
5
  * All the items (options) to render.
6
6
  */