@stack-spot/portal-components 2.27.0 → 2.27.2

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 (250) hide show
  1. package/CHANGELOG.md +635 -621
  2. package/dist/components/AnimatedHeight.d.ts +1 -1
  3. package/dist/components/AnimatedHeight.js +26 -26
  4. package/dist/components/AsyncContent.d.ts +1 -1
  5. package/dist/components/AsyncContent.js +1 -1
  6. package/dist/components/BannerWarning.d.ts +1 -1
  7. package/dist/components/BannerWarning.js +1 -1
  8. package/dist/components/Breadcrumb/index.d.ts +2 -2
  9. package/dist/components/Breadcrumb/index.js +1 -1
  10. package/dist/components/Breadcrumb/styled.js +31 -31
  11. package/dist/components/ButtonLoading.d.ts +1 -1
  12. package/dist/components/ButtonLoading.js +1 -1
  13. package/dist/components/ChatBot.d.ts +1 -1
  14. package/dist/components/ChatBot.js +1 -1
  15. package/dist/components/ContentValidateFilter.d.ts +1 -1
  16. package/dist/components/ContentValidateFilter.js +1 -1
  17. package/dist/components/FadingOverflow.d.ts +1 -1
  18. package/dist/components/FadingOverflow.js +69 -69
  19. package/dist/components/FileTreeView/More.d.ts +1 -1
  20. package/dist/components/FileTreeView/More.js +1 -1
  21. package/dist/components/FileTreeView/index.d.ts +1 -1
  22. package/dist/components/FileTreeView/index.js +1 -1
  23. package/dist/components/InfiniteScroll.d.ts +1 -1
  24. package/dist/components/InfiniteScroll.js +1 -1
  25. package/dist/components/InfoMaintenanceBanner.d.ts +1 -1
  26. package/dist/components/InfoMaintenanceBanner.js +2 -2
  27. package/dist/components/LazyMarkdown/BlockquoteMd.d.ts +1 -1
  28. package/dist/components/LazyMarkdown/BlockquoteMd.js +1 -1
  29. package/dist/components/LazyMarkdown/CodeViewer.d.ts +1 -1
  30. package/dist/components/LazyMarkdown/CodeViewer.js +76 -76
  31. package/dist/components/LazyMarkdown/Markdown.d.ts +1 -1
  32. package/dist/components/LazyMarkdown/Markdown.js +1 -1
  33. package/dist/components/LazyMarkdown/MarkdownButton.d.ts +1 -1
  34. package/dist/components/LazyMarkdown/MarkdownButton.js +1 -1
  35. package/dist/components/LazyMarkdown/Video.d.ts +1 -1
  36. package/dist/components/LazyMarkdown/Video.js +1 -1
  37. package/dist/components/LazyMarkdown/index.d.ts +1 -1
  38. package/dist/components/LazyMarkdown/index.js +1 -1
  39. package/dist/components/Placeholder.d.ts +7 -3
  40. package/dist/components/Placeholder.d.ts.map +1 -1
  41. package/dist/components/Placeholder.js +3 -3
  42. package/dist/components/Placeholder.js.map +1 -1
  43. package/dist/components/ScrollView.js +16 -16
  44. package/dist/components/Select/BadgeItem.d.ts +1 -1
  45. package/dist/components/Select/BadgeItem.js +1 -1
  46. package/dist/components/Select/ClearInput.d.ts +1 -1
  47. package/dist/components/Select/ClearInput.js +1 -1
  48. package/dist/components/Select/CloseItem.d.ts +1 -1
  49. package/dist/components/Select/CloseItem.js +1 -1
  50. package/dist/components/Select/CreatableSelect.js +1 -1
  51. package/dist/components/Select/CustomMenu.d.ts +1 -1
  52. package/dist/components/Select/CustomMenu.js +1 -1
  53. package/dist/components/Select/LabelItem.d.ts +1 -1
  54. package/dist/components/Select/LabelItem.js +1 -1
  55. package/dist/components/Select/MultiValue.d.ts +1 -1
  56. package/dist/components/Select/MultiValue.js +1 -1
  57. package/dist/components/Select/SelectInfiniteScroll.d.ts +1 -1
  58. package/dist/components/Select/SelectInfiniteScroll.js +1 -1
  59. package/dist/components/Select/SelectSearch.d.ts +1 -1
  60. package/dist/components/Select/SelectSearch.js +1 -1
  61. package/dist/components/SelectionList.d.ts +1 -1
  62. package/dist/components/SelectionList.js +61 -61
  63. package/dist/components/StatusCircle.d.ts +1 -1
  64. package/dist/components/StatusCircle.js +6 -6
  65. package/dist/components/Stepper/Navigation.js +4 -4
  66. package/dist/components/Stepper/Step.js +3 -3
  67. package/dist/components/Stepper/Stepper.js +6 -6
  68. package/dist/components/Stepper/headers.js +22 -22
  69. package/dist/components/Table/HeaderItem.js +1 -1
  70. package/dist/components/Table/SettingsVerticalMenu.d.ts +1 -1
  71. package/dist/components/Table/SettingsVerticalMenu.js +1 -1
  72. package/dist/components/Table/StyledLinkTable.d.ts +1 -1
  73. package/dist/components/Table/StyledLinkTable.js +5 -5
  74. package/dist/components/Table/TableData.d.ts +1 -1
  75. package/dist/components/Table/TableData.js +25 -25
  76. package/dist/components/TimelineSection.d.ts +1 -1
  77. package/dist/components/TimelineSection.js +14 -14
  78. package/dist/components/error/ErrorFeedback.d.ts +1 -1
  79. package/dist/components/error/ErrorFeedback.js +35 -35
  80. package/dist/components/error/NotFound.d.ts +1 -1
  81. package/dist/components/error/NotFound.js +1 -1
  82. package/dist/components/error/UnderMaintenance.d.ts +1 -1
  83. package/dist/components/error/UnderMaintenance.js +1 -1
  84. package/dist/components/form/Form/Form.d.ts +1 -1
  85. package/dist/components/form/Form/Form.js +1 -1
  86. package/dist/components/form/Form/FormGroup.d.ts +2 -2
  87. package/dist/components/form/Form/FormGroup.js +1 -1
  88. package/dist/components/form/SearchInput.d.ts +1 -1
  89. package/dist/components/form/SearchInput.js +1 -1
  90. package/dist/components/form/Select/CustomSelect.d.ts +1 -1
  91. package/dist/components/form/Select/CustomSelect.js +1 -1
  92. package/dist/components/form/Select/DetailedSelect.d.ts +1 -1
  93. package/dist/components/form/Select/DetailedSelect.js +1 -1
  94. package/dist/components/form/Select/Select.d.ts +1 -1
  95. package/dist/components/form/Select/Select.js +1 -1
  96. package/dist/components/form/Select/styled.js +161 -161
  97. package/dist/components/form/Select/utils.js +1 -1
  98. package/dist/components/notification/NotificationComponent.d.ts +1 -1
  99. package/dist/components/notification/NotificationComponent.js +54 -54
  100. package/dist/components/notification/NotificationItem.d.ts +1 -1
  101. package/dist/components/notification/NotificationItem.d.ts.map +1 -1
  102. package/dist/components/notification/NotificationItem.js +11 -5
  103. package/dist/components/notification/NotificationItem.js.map +1 -1
  104. package/dist/components/notification/NotificationList.d.ts +1 -1
  105. package/dist/components/notification/NotificationList.d.ts.map +1 -1
  106. package/dist/components/notification/NotificationList.js +44 -44
  107. package/dist/components/notification/NotificationList.js.map +1 -1
  108. package/dist/components/notification/NotificationPlaceholder.d.ts +1 -1
  109. package/dist/components/notification/NotificationPlaceholder.d.ts.map +1 -1
  110. package/dist/components/notification/NotificationPlaceholder.js +2 -2
  111. package/dist/components/notification/NotificationPlaceholder.js.map +1 -1
  112. package/dist/containers/NotificationsPage.d.ts +1 -1
  113. package/dist/containers/NotificationsPage.d.ts.map +1 -1
  114. package/dist/containers/NotificationsPage.js +24 -11
  115. package/dist/containers/NotificationsPage.js.map +1 -1
  116. package/dist/context/anchor.d.ts +1 -1
  117. package/dist/context/anchor.js +1 -1
  118. package/dist/context/loading.d.ts +1 -1
  119. package/dist/context/loading.js +1 -1
  120. package/dist/context/notification/context.d.ts +1 -1
  121. package/dist/context/notification/context.js +1 -1
  122. package/dist/context/notification/types.d.ts +1 -0
  123. package/dist/context/notification/types.d.ts.map +1 -1
  124. package/dist/hooks/date.js +1 -1
  125. package/dist/hooks/service-now.js +28 -28
  126. package/dist/svg/AI.d.ts +1 -1
  127. package/dist/svg/AI.js +1 -1
  128. package/dist/svg/CS.d.ts +1 -1
  129. package/dist/svg/CS.js +1 -1
  130. package/dist/svg/EDP.d.ts +1 -1
  131. package/dist/svg/EDP.js +1 -1
  132. package/dist/svg/Forbidden.d.ts +1 -1
  133. package/dist/svg/Forbidden.js +1 -1
  134. package/dist/svg/GenericPlaceholder.d.ts +4 -2
  135. package/dist/svg/GenericPlaceholder.d.ts.map +1 -1
  136. package/dist/svg/GenericPlaceholder.js +2 -2
  137. package/dist/svg/GenericPlaceholder.js.map +1 -1
  138. package/dist/svg/HUB.d.ts +1 -1
  139. package/dist/svg/HUB.js +1 -1
  140. package/dist/svg/Logo.d.ts +1 -1
  141. package/dist/svg/Logo.js +1 -1
  142. package/dist/svg/MiniLogo.d.ts +1 -1
  143. package/dist/svg/MiniLogo.js +1 -1
  144. package/dist/svg/NotFound.d.ts +1 -1
  145. package/dist/svg/NotFound.js +1 -1
  146. package/dist/svg/ServerError.d.ts +1 -1
  147. package/dist/svg/ServerError.js +1 -1
  148. package/dist/svg/Unauthenticated.d.ts +1 -1
  149. package/dist/svg/Unauthenticated.js +1 -1
  150. package/package.json +6 -6
  151. package/readme.md +66 -66
  152. package/src/components/AnimatedHeight.tsx +174 -174
  153. package/src/components/AsyncContent.tsx +78 -78
  154. package/src/components/BannerWarning.tsx +91 -91
  155. package/src/components/Breadcrumb/index.tsx +76 -76
  156. package/src/components/Breadcrumb/styled.ts +37 -37
  157. package/src/components/ButtonLoading.tsx +29 -29
  158. package/src/components/ChatBot.tsx +82 -82
  159. package/src/components/ContentValidateFilter.tsx +15 -15
  160. package/src/components/FadingOverflow.tsx +265 -265
  161. package/src/components/FileTreeView/More.tsx +114 -114
  162. package/src/components/FileTreeView/index.tsx +186 -186
  163. package/src/components/InfiniteScroll.tsx +24 -24
  164. package/src/components/InfoMaintenanceBanner.tsx +29 -29
  165. package/src/components/LazyMarkdown/BlockquoteMd.tsx +107 -107
  166. package/src/components/LazyMarkdown/CodeViewer.tsx +161 -161
  167. package/src/components/LazyMarkdown/Markdown.tsx +122 -122
  168. package/src/components/LazyMarkdown/MarkdownButton.tsx +24 -24
  169. package/src/components/LazyMarkdown/Video.tsx +13 -13
  170. package/src/components/LazyMarkdown/index.tsx +21 -21
  171. package/src/components/Placeholder.tsx +123 -118
  172. package/src/components/ScrollView.tsx +57 -57
  173. package/src/components/Select/BadgeItem.tsx +58 -58
  174. package/src/components/Select/ClearInput.tsx +24 -24
  175. package/src/components/Select/CloseItem.tsx +38 -38
  176. package/src/components/Select/CreatableSelect.tsx +155 -155
  177. package/src/components/Select/CustomMenu.tsx +16 -16
  178. package/src/components/Select/LabelItem.tsx +8 -8
  179. package/src/components/Select/MultiValue.tsx +49 -49
  180. package/src/components/Select/SelectInfiniteScroll.tsx +82 -82
  181. package/src/components/Select/SelectSearch.tsx +195 -195
  182. package/src/components/Select/index.tsx +7 -7
  183. package/src/components/Select/types.ts +8 -8
  184. package/src/components/SelectionList.tsx +427 -427
  185. package/src/components/StatusCircle.tsx +67 -67
  186. package/src/components/Stepper/Navigation.tsx +97 -97
  187. package/src/components/Stepper/Step.tsx +30 -30
  188. package/src/components/Stepper/Stepper.tsx +113 -113
  189. package/src/components/Stepper/headers.tsx +64 -64
  190. package/src/components/Stepper/index.ts +3 -3
  191. package/src/components/Table/HeaderItem.tsx +52 -52
  192. package/src/components/Table/SettingsVerticalMenu.tsx +50 -50
  193. package/src/components/Table/StyledLinkTable.tsx +22 -22
  194. package/src/components/Table/TableData.tsx +251 -251
  195. package/src/components/Table/index.tsx +2 -2
  196. package/src/components/TimelineSection.tsx +66 -66
  197. package/src/components/error/ErrorFeedback.tsx +217 -217
  198. package/src/components/error/NotFound.tsx +24 -24
  199. package/src/components/error/UnderMaintenance.tsx +30 -30
  200. package/src/components/error/index.ts +4 -4
  201. package/src/components/form/Form/Form.tsx +101 -101
  202. package/src/components/form/Form/FormGroup.tsx +221 -221
  203. package/src/components/form/Form/index.ts +2 -2
  204. package/src/components/form/SearchInput.tsx +69 -69
  205. package/src/components/form/Select/CustomSelect.tsx +232 -232
  206. package/src/components/form/Select/DetailedSelect.tsx +85 -85
  207. package/src/components/form/Select/Select.tsx +67 -67
  208. package/src/components/form/Select/index.ts +4 -4
  209. package/src/components/form/Select/styled.ts +165 -165
  210. package/src/components/form/Select/types.ts +112 -112
  211. package/src/components/form/Select/utils.tsx +28 -28
  212. package/src/components/notification/NotificationComponent.tsx +340 -340
  213. package/src/components/notification/NotificationItem.tsx +345 -336
  214. package/src/components/notification/NotificationList.tsx +179 -178
  215. package/src/components/notification/NotificationPlaceholder.tsx +44 -43
  216. package/src/components/notification/types.ts +72 -72
  217. package/src/containers/NotificationsPage.tsx +119 -98
  218. package/src/context/anchor.tsx +37 -37
  219. package/src/context/loading.tsx +36 -36
  220. package/src/context/notification/LazyNotificationList.ts +103 -103
  221. package/src/context/notification/NotificationController.ts +104 -104
  222. package/src/context/notification/context.tsx +23 -23
  223. package/src/context/notification/hooks.ts +98 -98
  224. package/src/context/notification/types.ts +66 -65
  225. package/src/hooks/date.ts +31 -31
  226. package/src/hooks/keyboard.tsx +128 -128
  227. package/src/hooks/manual-render.tsx +10 -10
  228. package/src/hooks/service-now.tsx +233 -233
  229. package/src/hooks/text.tsx +30 -30
  230. package/src/hooks/title.tsx +28 -28
  231. package/src/hooks/use-effect-once.tsx +43 -43
  232. package/src/index.ts +19 -19
  233. package/src/notifications.ts +11 -11
  234. package/src/svg/AI.tsx +41 -41
  235. package/src/svg/CS.tsx +48 -48
  236. package/src/svg/EDP.tsx +31 -31
  237. package/src/svg/Forbidden.tsx +22 -22
  238. package/src/svg/GenericPlaceholder.tsx +20 -20
  239. package/src/svg/HUB.tsx +48 -48
  240. package/src/svg/Logo.tsx +16 -16
  241. package/src/svg/MiniLogo.tsx +12 -12
  242. package/src/svg/NotFound.tsx +16 -16
  243. package/src/svg/ServerError.tsx +33 -33
  244. package/src/svg/Unauthenticated.tsx +16 -16
  245. package/src/svg/index.ts +11 -11
  246. package/src/utils/accessibility.ts +135 -135
  247. package/src/utils/cookie.ts +73 -73
  248. package/src/utils/promise.ts +5 -5
  249. package/src/utils/read-file.ts +16 -16
  250. package/tsconfig.json +10 -10
@@ -1,101 +1,101 @@
1
- import { get } from 'lodash'
2
- import { FormEvent, ReactNode } from 'react'
3
- import { FieldValues, FormProvider, UseFormReturn, useForm, useFormContext } from 'react-hook-form'
4
-
5
- export interface FormProps {
6
- id?: string,
7
- name: string,
8
- defaultValues?: FieldValues,
9
- autoComplete?: HTMLInputElement['autocomplete'],
10
- children?: ReactNode,
11
- onSubmit?: (params: FieldValues) => unknown,
12
- form?: UseFormReturn<any, any>,
13
- style?: React.CSSProperties,
14
- className?: string,
15
- }
16
-
17
-
18
- function handleGraphqlErrors(error: unknown, methods: UseFormReturn<FieldValues>) {
19
- const errors: any = get(error, 'cause.extensions.fields')
20
-
21
- if (errors) {
22
- Object.keys(errors).forEach((key) => {
23
- if (Object.keys(methods.control._fields).includes(key)) {
24
- methods.setError(key, { type: 'server', message: errors[`${key}`] })
25
- }
26
- })
27
- }
28
- }
29
-
30
- const handleFormSubmit = async (
31
- data: FormEvent<HTMLFormElement>,
32
- methods: UseFormReturn,
33
- onSubmit?: (params: Record<string, any>) => Promise<unknown> | unknown,
34
- ) => {
35
-
36
- if (!onSubmit) {
37
- return
38
- }
39
-
40
- const result = methods.handleSubmit(onSubmit)(data)
41
- const isPromise = result instanceof Promise
42
-
43
- if (!isPromise) {
44
- return result
45
- }
46
-
47
- try {
48
- return await result
49
- } catch (error: any) {
50
- const isGraphqlError = error.message === 'GraphqlError'
51
- if (!isGraphqlError) {
52
- throw error
53
- }
54
- handleGraphqlErrors(error, methods)
55
- }
56
- }
57
-
58
- const BasicForm = ({
59
- onSubmit,
60
- id,
61
- name,
62
- children,
63
- methods,
64
- autoComplete,
65
- ...props
66
- }: FormProps & { methods: UseFormReturn<FieldValues> }) => (
67
- <form
68
- id={id}
69
- autoComplete={autoComplete}
70
- name={name}
71
- onSubmit={(data: FormEvent<HTMLFormElement>) => handleFormSubmit(data, methods, onSubmit)}
72
- noValidate
73
- style={props.style}
74
- className={props.className}
75
- >
76
- {children}
77
- </form>
78
- )
79
-
80
- const FormWithProvider = (props: FormProps) => {
81
- const methods = useForm({ defaultValues: props.defaultValues })
82
- const form = props.form || methods
83
-
84
- return (
85
- <FormProvider {...form}>
86
- <BasicForm {...props} methods={form} />
87
- </FormProvider>
88
- )
89
- }
90
-
91
- export const Form = (props: FormProps) => {
92
- const useFormMethods = useFormContext()
93
- const hasFormProvider = !!useFormMethods
94
-
95
- if (hasFormProvider) {
96
- return <BasicForm {...props} methods={useFormMethods} />
97
- }
98
-
99
- return <FormWithProvider {...props} />
100
- }
101
-
1
+ import { get } from 'lodash'
2
+ import { FormEvent, ReactNode } from 'react'
3
+ import { FieldValues, FormProvider, UseFormReturn, useForm, useFormContext } from 'react-hook-form'
4
+
5
+ export interface FormProps {
6
+ id?: string,
7
+ name: string,
8
+ defaultValues?: FieldValues,
9
+ autoComplete?: HTMLInputElement['autocomplete'],
10
+ children?: ReactNode,
11
+ onSubmit?: (params: FieldValues) => unknown,
12
+ form?: UseFormReturn<any, any>,
13
+ style?: React.CSSProperties,
14
+ className?: string,
15
+ }
16
+
17
+
18
+ function handleGraphqlErrors(error: unknown, methods: UseFormReturn<FieldValues>) {
19
+ const errors: any = get(error, 'cause.extensions.fields')
20
+
21
+ if (errors) {
22
+ Object.keys(errors).forEach((key) => {
23
+ if (Object.keys(methods.control._fields).includes(key)) {
24
+ methods.setError(key, { type: 'server', message: errors[`${key}`] })
25
+ }
26
+ })
27
+ }
28
+ }
29
+
30
+ const handleFormSubmit = async (
31
+ data: FormEvent<HTMLFormElement>,
32
+ methods: UseFormReturn,
33
+ onSubmit?: (params: Record<string, any>) => Promise<unknown> | unknown,
34
+ ) => {
35
+
36
+ if (!onSubmit) {
37
+ return
38
+ }
39
+
40
+ const result = methods.handleSubmit(onSubmit)(data)
41
+ const isPromise = result instanceof Promise
42
+
43
+ if (!isPromise) {
44
+ return result
45
+ }
46
+
47
+ try {
48
+ return await result
49
+ } catch (error: any) {
50
+ const isGraphqlError = error.message === 'GraphqlError'
51
+ if (!isGraphqlError) {
52
+ throw error
53
+ }
54
+ handleGraphqlErrors(error, methods)
55
+ }
56
+ }
57
+
58
+ const BasicForm = ({
59
+ onSubmit,
60
+ id,
61
+ name,
62
+ children,
63
+ methods,
64
+ autoComplete,
65
+ ...props
66
+ }: FormProps & { methods: UseFormReturn<FieldValues> }) => (
67
+ <form
68
+ id={id}
69
+ autoComplete={autoComplete}
70
+ name={name}
71
+ onSubmit={(data: FormEvent<HTMLFormElement>) => handleFormSubmit(data, methods, onSubmit)}
72
+ noValidate
73
+ style={props.style}
74
+ className={props.className}
75
+ >
76
+ {children}
77
+ </form>
78
+ )
79
+
80
+ const FormWithProvider = (props: FormProps) => {
81
+ const methods = useForm({ defaultValues: props.defaultValues })
82
+ const form = props.form || methods
83
+
84
+ return (
85
+ <FormProvider {...form}>
86
+ <BasicForm {...props} methods={form} />
87
+ </FormProvider>
88
+ )
89
+ }
90
+
91
+ export const Form = (props: FormProps) => {
92
+ const useFormMethods = useFormContext()
93
+ const hasFormProvider = !!useFormMethods
94
+
95
+ if (hasFormProvider) {
96
+ return <BasicForm {...props} methods={useFormMethods} />
97
+ }
98
+
99
+ return <FormWithProvider {...props} />
100
+ }
101
+
@@ -1,221 +1,221 @@
1
- import { Box, Flex, IconBox, Input, Label } from '@citric/core'
2
- import { SxProperties } from '@citric/core/dist/sx'
3
- import { InfoCircle, TimesCircle } from '@citric/icons'
4
- import { FieldAddon, FieldGroup, FormHelper, FormItem, Tooltip } from '@citric/ui'
5
- import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
6
- import { debounce, get } from 'lodash'
7
- import { ChangeEvent, ComponentProps, ComponentPropsWithoutRef, ElementType, ReactElement, ReactNode, useEffect } from 'react'
8
- import { FieldPath, FieldValues, RegisterOptions, useFormContext, UseFormRegister, UseFormRegisterReturn, UseFormTrigger } from 'react-hook-form'
9
-
10
- const registerFieldWithDebounceChangeValidation = <T extends FieldValues>(
11
- name: FieldPath<T>,
12
- delay: number,
13
- trigger: UseFormTrigger<T>,
14
- register: UseFormRegister<T>,
15
- options?: RegisterOptions<T, FieldPath<T>>,
16
- ) => {
17
- const useFormRegisterReturn: UseFormRegisterReturn = register(name, options)
18
- const { onChange } = useFormRegisterReturn
19
- const debouncedValidate = debounce(() => {
20
- trigger(name)
21
- }, delay)
22
-
23
- const debouncedChange = debounce((event: ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLSelectElement>) => {
24
- onChange?.(event)
25
- }, delay)
26
- return {
27
- ...useFormRegisterReturn,
28
- onChange: (e: any) => {
29
- debouncedChange(e)
30
- debouncedValidate()
31
- },
32
- }
33
- }
34
-
35
- interface FormGroupProps<T extends ElementType> {
36
- fieldAddonAfter?: ReactNode,
37
- fieldAddonBefore?: ReactNode,
38
- formHelper?: ReactNode,
39
- name: string,
40
- id?: string,
41
- component?: T,
42
- label?: string | ReactElement,
43
- registerOptions?: RegisterOptions,
44
- formItemSx?: SxProperties,
45
- formGroupSx?: SxProperties,
46
- componentSx?: SxProperties,
47
- fieldAddonBeforeSx?: SxProperties,
48
- fieldAddonAfterSx?: SxProperties,
49
- $formName?: string,
50
- required?: boolean,
51
- onChange?: (event: ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLSelectElement>) => void,
52
- tooltipProps?: Omit<ComponentProps<typeof Tooltip>, 'children'>,
53
- readOnly?: boolean,
54
- disabled?: boolean,
55
- // When true, input will not be registered on form
56
- standalone?: boolean,
57
- delay?: number,
58
- // prop for tests of usability
59
- hideRequiredSymbol?: boolean,
60
- shouldUnregister?: boolean,
61
- }
62
-
63
- type Props<T extends ElementType> = FormGroupProps<T> & Omit<ComponentPropsWithoutRef<T>, keyof FormGroupProps<T>>
64
-
65
- export const FormGroup = <T extends ElementType>({
66
- fieldAddonAfter,
67
- fieldAddonBefore,
68
- formHelper,
69
- id,
70
- name,
71
- component,
72
- label,
73
- registerOptions = {},
74
- formGroupSx = {},
75
- formItemSx = {},
76
- componentSx = {},
77
- disabled,
78
- readOnly,
79
- onChange,
80
- tooltipProps,
81
- required,
82
- colorScheme: propColorScheme,
83
- fieldAddonBeforeSx,
84
- fieldAddonAfterSx,
85
- standalone,
86
- delay,
87
- hideRequiredSymbol,
88
- shouldUnregister = false,
89
- ...props
90
- }: Props<T>) => {
91
- const t = useTranslate(dictionary)
92
- const { register, getFieldState, trigger, unregister } = useFormContext()
93
- const Component = component || Input
94
- const colorScheme = propColorScheme
95
-
96
- let formRegistry = {}
97
-
98
- if (!standalone) {
99
- const options = {
100
- onChange,
101
- required: { message: hideRequiredSymbol ? t.requiredTest : t.required, value: !!required }, validate: (value: string) => {
102
- if (required && typeof value === 'string') {
103
- return !!value?.trim()
104
- }
105
- },
106
- ...registerOptions,
107
- }
108
-
109
- formRegistry = delay ? registerFieldWithDebounceChangeValidation(name, delay, trigger, register, options) : register(name, options)
110
- }
111
-
112
- useEffect(() => () => {
113
- shouldUnregister && unregister(name)
114
- }, [name, unregister])
115
-
116
- return (
117
- <FormItem sx={formItemSx}>
118
- {label && (
119
- <Flex>
120
- <Label appearance="body2" htmlFor={id || name} sx={{ pointerEvents: disabled || readOnly ? 'none' : undefined }}>
121
- {label}
122
- &nbsp;
123
- {required && !hideRequiredSymbol && '*'}
124
- </Label>
125
- {tooltipProps?.text && (
126
- <Tooltip {...(tooltipProps as typeof tooltipProps & { text: string })}>
127
- <IconBox>
128
- <InfoCircle />
129
- </IconBox>
130
- </Tooltip>
131
- )}
132
- </Flex>
133
- )}
134
-
135
- {!fieldAddonBefore && !fieldAddonAfter ? (
136
- <Component
137
- id={id || name}
138
- disabled={disabled}
139
- readOnly={readOnly}
140
- {...props}
141
- {...{ colorScheme }}
142
- {...formRegistry}
143
- required={required}
144
- sx={componentSx}
145
- aria-errormessage={`err${(id || name)}`}
146
- aria-invalid={getFieldState(name).invalid}
147
- />
148
- ) : (
149
- <FieldGroup sx={formGroupSx}>
150
- {fieldAddonBefore && (
151
- <FieldAddon sx={{ borderColor: colorScheme ? colorScheme : 'light.600', ...fieldAddonBeforeSx }}>
152
- {fieldAddonBefore}
153
- </FieldAddon>
154
- )}
155
-
156
- <Component
157
- disabled={disabled}
158
- readOnly={readOnly}
159
- id={id || name}
160
- {...props}
161
- {...{ colorScheme }}
162
- {...formRegistry}
163
- required={required}
164
- sx={componentSx}
165
- aria-errormessage={`err${(id || name)}`}
166
- aria-invalid={getFieldState(name).invalid}
167
- />
168
-
169
- {fieldAddonAfter && (
170
- <FieldAddon sx={{ borderColor: colorScheme ? colorScheme : 'light.600', ...fieldAddonAfterSx }}>
171
- {fieldAddonAfter}
172
- </FieldAddon>
173
- )}
174
- </FieldGroup>
175
- )}
176
- <FormItemError name={name} id={id || name} />
177
- {formHelper && (
178
- <Box mt="2">
179
- <FormHelper>{formHelper}</FormHelper>
180
- </Box>
181
- )}
182
- </FormItem>
183
- )
184
- }
185
-
186
- export const FormItemError = ({ name, id }: { name: string, id: string }) => {
187
- const t = useTranslate(dictionary)
188
- const { formState } = useFormContext()
189
- const formItemError = get(formState.errors, name)
190
-
191
- if (formItemError) {
192
- return (
193
- <Flex alignItems="center" flexWrap="nowrap">
194
- <IconBox size="xs" appearance="circle" colorIcon="danger" colorBg="danger.500">
195
- <TimesCircle />
196
- </IconBox>
197
- <Box ml="3">
198
- <FormHelper colorScheme="danger" aria-live="assertive" id={`err${id}`}>
199
- {formItemError.message?.toString() || formItemError?.root?.message?.toString() || t.invalidGenericError}
200
- </FormHelper>
201
- </Box>
202
- </Flex>
203
- )
204
- }
205
- return null
206
- }
207
-
208
- const dictionary = {
209
- en: {
210
- invalidGenericError: 'Invalid field',
211
- required: 'Required field',
212
- // text for tests of usability
213
- requiredTest: 'Fill in the required field to continue.',
214
- },
215
- pt: {
216
- invalidGenericError: 'Campo inválido',
217
- required: 'Campo obrigatório',
218
- // text for tests of usability
219
- requiredTest: 'Preencha este campo obrigatório para continuar.',
220
- },
221
- } satisfies Dictionary
1
+ import { Box, Flex, IconBox, Input, Label } from '@citric/core'
2
+ import { SxProperties } from '@citric/core/dist/sx'
3
+ import { InfoCircle, TimesCircle } from '@citric/icons'
4
+ import { FieldAddon, FieldGroup, FormHelper, FormItem, Tooltip } from '@citric/ui'
5
+ import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
6
+ import { debounce, get } from 'lodash'
7
+ import { ChangeEvent, ComponentProps, ComponentPropsWithoutRef, ElementType, ReactElement, ReactNode, useEffect } from 'react'
8
+ import { FieldPath, FieldValues, RegisterOptions, useFormContext, UseFormRegister, UseFormRegisterReturn, UseFormTrigger } from 'react-hook-form'
9
+
10
+ const registerFieldWithDebounceChangeValidation = <T extends FieldValues>(
11
+ name: FieldPath<T>,
12
+ delay: number,
13
+ trigger: UseFormTrigger<T>,
14
+ register: UseFormRegister<T>,
15
+ options?: RegisterOptions<T, FieldPath<T>>,
16
+ ) => {
17
+ const useFormRegisterReturn: UseFormRegisterReturn = register(name, options)
18
+ const { onChange } = useFormRegisterReturn
19
+ const debouncedValidate = debounce(() => {
20
+ trigger(name)
21
+ }, delay)
22
+
23
+ const debouncedChange = debounce((event: ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLSelectElement>) => {
24
+ onChange?.(event)
25
+ }, delay)
26
+ return {
27
+ ...useFormRegisterReturn,
28
+ onChange: (e: any) => {
29
+ debouncedChange(e)
30
+ debouncedValidate()
31
+ },
32
+ }
33
+ }
34
+
35
+ interface FormGroupProps<T extends ElementType> {
36
+ fieldAddonAfter?: ReactNode,
37
+ fieldAddonBefore?: ReactNode,
38
+ formHelper?: ReactNode,
39
+ name: string,
40
+ id?: string,
41
+ component?: T,
42
+ label?: string | ReactElement,
43
+ registerOptions?: RegisterOptions,
44
+ formItemSx?: SxProperties,
45
+ formGroupSx?: SxProperties,
46
+ componentSx?: SxProperties,
47
+ fieldAddonBeforeSx?: SxProperties,
48
+ fieldAddonAfterSx?: SxProperties,
49
+ $formName?: string,
50
+ required?: boolean,
51
+ onChange?: (event: ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLSelectElement>) => void,
52
+ tooltipProps?: Omit<ComponentProps<typeof Tooltip>, 'children'>,
53
+ readOnly?: boolean,
54
+ disabled?: boolean,
55
+ // When true, input will not be registered on form
56
+ standalone?: boolean,
57
+ delay?: number,
58
+ // prop for tests of usability
59
+ hideRequiredSymbol?: boolean,
60
+ shouldUnregister?: boolean,
61
+ }
62
+
63
+ type Props<T extends ElementType> = FormGroupProps<T> & Omit<ComponentPropsWithoutRef<T>, keyof FormGroupProps<T>>
64
+
65
+ export const FormGroup = <T extends ElementType>({
66
+ fieldAddonAfter,
67
+ fieldAddonBefore,
68
+ formHelper,
69
+ id,
70
+ name,
71
+ component,
72
+ label,
73
+ registerOptions = {},
74
+ formGroupSx = {},
75
+ formItemSx = {},
76
+ componentSx = {},
77
+ disabled,
78
+ readOnly,
79
+ onChange,
80
+ tooltipProps,
81
+ required,
82
+ colorScheme: propColorScheme,
83
+ fieldAddonBeforeSx,
84
+ fieldAddonAfterSx,
85
+ standalone,
86
+ delay,
87
+ hideRequiredSymbol,
88
+ shouldUnregister = false,
89
+ ...props
90
+ }: Props<T>) => {
91
+ const t = useTranslate(dictionary)
92
+ const { register, getFieldState, trigger, unregister } = useFormContext()
93
+ const Component = component || Input
94
+ const colorScheme = propColorScheme
95
+
96
+ let formRegistry = {}
97
+
98
+ if (!standalone) {
99
+ const options = {
100
+ onChange,
101
+ required: { message: hideRequiredSymbol ? t.requiredTest : t.required, value: !!required }, validate: (value: string) => {
102
+ if (required && typeof value === 'string') {
103
+ return !!value?.trim()
104
+ }
105
+ },
106
+ ...registerOptions,
107
+ }
108
+
109
+ formRegistry = delay ? registerFieldWithDebounceChangeValidation(name, delay, trigger, register, options) : register(name, options)
110
+ }
111
+
112
+ useEffect(() => () => {
113
+ shouldUnregister && unregister(name)
114
+ }, [name, unregister])
115
+
116
+ return (
117
+ <FormItem sx={formItemSx}>
118
+ {label && (
119
+ <Flex>
120
+ <Label appearance="body2" htmlFor={id || name} sx={{ pointerEvents: disabled || readOnly ? 'none' : undefined }}>
121
+ {label}
122
+ &nbsp;
123
+ {required && !hideRequiredSymbol && '*'}
124
+ </Label>
125
+ {tooltipProps?.text && (
126
+ <Tooltip {...(tooltipProps as typeof tooltipProps & { text: string })}>
127
+ <IconBox>
128
+ <InfoCircle />
129
+ </IconBox>
130
+ </Tooltip>
131
+ )}
132
+ </Flex>
133
+ )}
134
+
135
+ {!fieldAddonBefore && !fieldAddonAfter ? (
136
+ <Component
137
+ id={id || name}
138
+ disabled={disabled}
139
+ readOnly={readOnly}
140
+ {...props}
141
+ {...{ colorScheme }}
142
+ {...formRegistry}
143
+ required={required}
144
+ sx={componentSx}
145
+ aria-errormessage={`err${(id || name)}`}
146
+ aria-invalid={getFieldState(name).invalid}
147
+ />
148
+ ) : (
149
+ <FieldGroup sx={formGroupSx}>
150
+ {fieldAddonBefore && (
151
+ <FieldAddon sx={{ borderColor: colorScheme ? colorScheme : 'light.600', ...fieldAddonBeforeSx }}>
152
+ {fieldAddonBefore}
153
+ </FieldAddon>
154
+ )}
155
+
156
+ <Component
157
+ disabled={disabled}
158
+ readOnly={readOnly}
159
+ id={id || name}
160
+ {...props}
161
+ {...{ colorScheme }}
162
+ {...formRegistry}
163
+ required={required}
164
+ sx={componentSx}
165
+ aria-errormessage={`err${(id || name)}`}
166
+ aria-invalid={getFieldState(name).invalid}
167
+ />
168
+
169
+ {fieldAddonAfter && (
170
+ <FieldAddon sx={{ borderColor: colorScheme ? colorScheme : 'light.600', ...fieldAddonAfterSx }}>
171
+ {fieldAddonAfter}
172
+ </FieldAddon>
173
+ )}
174
+ </FieldGroup>
175
+ )}
176
+ <FormItemError name={name} id={id || name} />
177
+ {formHelper && (
178
+ <Box mt="2">
179
+ <FormHelper>{formHelper}</FormHelper>
180
+ </Box>
181
+ )}
182
+ </FormItem>
183
+ )
184
+ }
185
+
186
+ export const FormItemError = ({ name, id }: { name: string, id: string }) => {
187
+ const t = useTranslate(dictionary)
188
+ const { formState } = useFormContext()
189
+ const formItemError = get(formState.errors, name)
190
+
191
+ if (formItemError) {
192
+ return (
193
+ <Flex alignItems="center" flexWrap="nowrap">
194
+ <IconBox size="xs" appearance="circle" colorIcon="danger" colorBg="danger.500">
195
+ <TimesCircle />
196
+ </IconBox>
197
+ <Box ml="3">
198
+ <FormHelper colorScheme="danger" aria-live="assertive" id={`err${id}`}>
199
+ {formItemError.message?.toString() || formItemError?.root?.message?.toString() || t.invalidGenericError}
200
+ </FormHelper>
201
+ </Box>
202
+ </Flex>
203
+ )
204
+ }
205
+ return null
206
+ }
207
+
208
+ const dictionary = {
209
+ en: {
210
+ invalidGenericError: 'Invalid field',
211
+ required: 'Required field',
212
+ // text for tests of usability
213
+ requiredTest: 'Fill in the required field to continue.',
214
+ },
215
+ pt: {
216
+ invalidGenericError: 'Campo inválido',
217
+ required: 'Campo obrigatório',
218
+ // text for tests of usability
219
+ requiredTest: 'Preencha este campo obrigatório para continuar.',
220
+ },
221
+ } satisfies Dictionary
@@ -1,2 +1,2 @@
1
- export { FormGroup } from './FormGroup'
2
- export { Form } from './Form'
1
+ export { FormGroup } from './FormGroup'
2
+ export { Form } from './Form'