@stack-spot/citric-react 0.37.1 → 0.39.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 (189) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/citric.css +2844 -2844
  3. package/dist/components/Accordion.d.ts +1 -1
  4. package/dist/components/Accordion.js +1 -1
  5. package/dist/components/Alert.d.ts +1 -1
  6. package/dist/components/Alert.js +1 -1
  7. package/dist/components/AsyncContent.d.ts +1 -1
  8. package/dist/components/AsyncContent.js +1 -1
  9. package/dist/components/Avatar.d.ts +1 -1
  10. package/dist/components/Avatar.js +1 -1
  11. package/dist/components/AvatarGroup.d.ts +1 -1
  12. package/dist/components/AvatarGroup.js +1 -1
  13. package/dist/components/Badge.d.ts +1 -1
  14. package/dist/components/Badge.js +1 -1
  15. package/dist/components/Blockquote.d.ts +1 -1
  16. package/dist/components/Blockquote.js +1 -1
  17. package/dist/components/Breadcrumb.d.ts +1 -1
  18. package/dist/components/Breadcrumb.js +1 -1
  19. package/dist/components/Button.d.ts +1 -1
  20. package/dist/components/Button.js +1 -1
  21. package/dist/components/ButtonLink.d.ts +1 -1
  22. package/dist/components/ButtonLink.js +1 -1
  23. package/dist/components/Card.d.ts +1 -1
  24. package/dist/components/Card.js +1 -1
  25. package/dist/components/Checkbox.d.ts +1 -1
  26. package/dist/components/Checkbox.js +1 -1
  27. package/dist/components/CheckboxGroup.d.ts +1 -1
  28. package/dist/components/CheckboxGroup.d.ts.map +1 -1
  29. package/dist/components/CheckboxGroup.js +2 -2
  30. package/dist/components/CheckboxGroup.js.map +1 -1
  31. package/dist/components/Circle.d.ts +1 -1
  32. package/dist/components/Circle.js +1 -1
  33. package/dist/components/Divider.d.ts +1 -1
  34. package/dist/components/Divider.js +1 -1
  35. package/dist/components/ErrorBoundary.d.ts +1 -1
  36. package/dist/components/ErrorBoundary.js +1 -1
  37. package/dist/components/ErrorMessage.d.ts +1 -1
  38. package/dist/components/ErrorMessage.js +1 -1
  39. package/dist/components/FallbackBoundary.d.ts +1 -1
  40. package/dist/components/FallbackBoundary.js +1 -1
  41. package/dist/components/Favorite.d.ts +1 -1
  42. package/dist/components/Favorite.js +1 -1
  43. package/dist/components/FieldGroup.d.ts +1 -1
  44. package/dist/components/FieldGroup.js +1 -1
  45. package/dist/components/Form.d.ts +2 -2
  46. package/dist/components/Form.js +1 -1
  47. package/dist/components/FormGroup.d.ts +1 -1
  48. package/dist/components/FormGroup.js +1 -1
  49. package/dist/components/Icon.d.ts +1 -1
  50. package/dist/components/Icon.js +1 -1
  51. package/dist/components/IconBox.d.ts +3 -3
  52. package/dist/components/IconBox.js +1 -1
  53. package/dist/components/ImageBox.d.ts +3 -3
  54. package/dist/components/ImageBox.js +1 -1
  55. package/dist/components/ImageWithFallback.d.ts +1 -1
  56. package/dist/components/ImageWithFallback.js +1 -1
  57. package/dist/components/Input.d.ts +1 -1
  58. package/dist/components/Input.js +1 -1
  59. package/dist/components/Link.d.ts +1 -1
  60. package/dist/components/Link.js +1 -1
  61. package/dist/components/LoadingPanel.d.ts +1 -1
  62. package/dist/components/LoadingPanel.js +1 -1
  63. package/dist/components/MenuOverlay/Menu.d.ts +1 -1
  64. package/dist/components/MenuOverlay/Menu.js +1 -1
  65. package/dist/components/MenuOverlay/index.d.ts +1 -1
  66. package/dist/components/MenuOverlay/index.js +1 -1
  67. package/dist/components/Overlay/index.d.ts +4 -1
  68. package/dist/components/Overlay/index.d.ts.map +1 -1
  69. package/dist/components/Overlay/index.js +4 -1
  70. package/dist/components/Overlay/index.js.map +1 -1
  71. package/dist/components/Pagination.d.ts +1 -1
  72. package/dist/components/Pagination.js +1 -1
  73. package/dist/components/ProgressBar.d.ts +1 -1
  74. package/dist/components/ProgressBar.js +1 -1
  75. package/dist/components/ProgressCircular.d.ts +1 -1
  76. package/dist/components/ProgressCircular.js +1 -1
  77. package/dist/components/RadioGroup.d.ts +1 -1
  78. package/dist/components/RadioGroup.js +1 -1
  79. package/dist/components/Rating.d.ts +1 -1
  80. package/dist/components/Rating.js +1 -1
  81. package/dist/components/Select/MultiSelect.d.ts +1 -1
  82. package/dist/components/Select/MultiSelect.js +1 -1
  83. package/dist/components/Select/RichSelect.d.ts +1 -1
  84. package/dist/components/Select/RichSelect.js +1 -1
  85. package/dist/components/Select/SimpleSelect.d.ts +1 -1
  86. package/dist/components/Select/SimpleSelect.js +1 -1
  87. package/dist/components/Select/index.d.ts +1 -1
  88. package/dist/components/Select/index.js +1 -1
  89. package/dist/components/SelectBox.d.ts +1 -1
  90. package/dist/components/SelectBox.js +1 -1
  91. package/dist/components/Skeleton.d.ts +1 -1
  92. package/dist/components/Skeleton.js +1 -1
  93. package/dist/components/Slider.d.ts +1 -1
  94. package/dist/components/Slider.js +1 -1
  95. package/dist/components/SmartTable.d.ts +1 -1
  96. package/dist/components/SmartTable.js +1 -1
  97. package/dist/components/Stepper.d.ts +1 -1
  98. package/dist/components/Stepper.js +1 -1
  99. package/dist/components/Table.d.ts +3 -3
  100. package/dist/components/Table.js +1 -1
  101. package/dist/components/Tabs/index.d.ts +1 -1
  102. package/dist/components/Tabs/index.js +1 -1
  103. package/dist/components/Textarea.d.ts +1 -1
  104. package/dist/components/Textarea.js +1 -1
  105. package/dist/components/Tooltip.d.ts +1 -1
  106. package/dist/components/Tooltip.js +1 -1
  107. package/dist/context/CitricProvider.d.ts +1 -1
  108. package/dist/context/CitricProvider.js +1 -1
  109. package/dist/overlay.js +1 -1
  110. package/dist/theme.css +415 -415
  111. package/package.json +2 -1
  112. package/scripts/build-css.ts +49 -49
  113. package/src/components/Accordion.tsx +130 -130
  114. package/src/components/Alert.tsx +24 -24
  115. package/src/components/AsyncContent.tsx +70 -70
  116. package/src/components/Avatar.tsx +45 -45
  117. package/src/components/AvatarGroup.tsx +49 -49
  118. package/src/components/Badge.tsx +47 -47
  119. package/src/components/Blockquote.tsx +18 -18
  120. package/src/components/Breadcrumb.tsx +33 -33
  121. package/src/components/Button.tsx +105 -105
  122. package/src/components/ButtonLink.tsx +45 -45
  123. package/src/components/Card.tsx +68 -68
  124. package/src/components/Checkbox.tsx +51 -51
  125. package/src/components/CheckboxGroup.tsx +153 -152
  126. package/src/components/Circle.tsx +43 -43
  127. package/src/components/CitricComponent.ts +47 -47
  128. package/src/components/Divider.tsx +24 -24
  129. package/src/components/ErrorBoundary.tsx +75 -75
  130. package/src/components/ErrorMessage.tsx +11 -11
  131. package/src/components/FallbackBoundary.tsx +40 -40
  132. package/src/components/Favorite.tsx +57 -57
  133. package/src/components/FieldGroup.tsx +46 -46
  134. package/src/components/Form.tsx +36 -36
  135. package/src/components/FormGroup.tsx +57 -57
  136. package/src/components/Icon.tsx +35 -35
  137. package/src/components/IconBox.tsx +134 -134
  138. package/src/components/ImageBox.tsx +125 -125
  139. package/src/components/ImageWithFallback.tsx +65 -65
  140. package/src/components/Input.tsx +49 -49
  141. package/src/components/Link.tsx +55 -55
  142. package/src/components/LoadingPanel.tsx +8 -8
  143. package/src/components/MenuOverlay/Menu.tsx +158 -158
  144. package/src/components/MenuOverlay/context.ts +20 -20
  145. package/src/components/MenuOverlay/index.tsx +55 -55
  146. package/src/components/MenuOverlay/keyboard.ts +60 -60
  147. package/src/components/MenuOverlay/types.ts +171 -171
  148. package/src/components/Overlay/context.ts +10 -10
  149. package/src/components/Overlay/index.tsx +167 -164
  150. package/src/components/Overlay/types.ts +70 -70
  151. package/src/components/Pagination.tsx +133 -133
  152. package/src/components/ProgressBar.tsx +45 -45
  153. package/src/components/ProgressCircular.tsx +45 -45
  154. package/src/components/RadioGroup.tsx +146 -146
  155. package/src/components/Rating.tsx +98 -98
  156. package/src/components/Select/MultiSelect.tsx +217 -217
  157. package/src/components/Select/RichSelect.tsx +128 -128
  158. package/src/components/Select/SimpleSelect.tsx +73 -73
  159. package/src/components/Select/hooks.ts +133 -133
  160. package/src/components/Select/index.tsx +35 -35
  161. package/src/components/Select/types.ts +134 -134
  162. package/src/components/SelectBox.tsx +167 -167
  163. package/src/components/Skeleton.tsx +53 -53
  164. package/src/components/Slider.tsx +89 -89
  165. package/src/components/SmartTable.tsx +227 -227
  166. package/src/components/Stepper.tsx +163 -163
  167. package/src/components/Table.tsx +234 -234
  168. package/src/components/Tabs/TabController.ts +54 -54
  169. package/src/components/Tabs/index.tsx +87 -87
  170. package/src/components/Tabs/types.ts +54 -54
  171. package/src/components/Tabs/utils.ts +6 -6
  172. package/src/components/Text.ts +111 -111
  173. package/src/components/Textarea.tsx +27 -27
  174. package/src/components/Tooltip.tsx +72 -72
  175. package/src/components/layout.tsx +101 -101
  176. package/src/context/CitricContext.tsx +4 -4
  177. package/src/context/CitricProvider.tsx +14 -14
  178. package/src/context/hooks.ts +6 -6
  179. package/src/index.ts +58 -58
  180. package/src/overlay.ts +341 -341
  181. package/src/types.ts +216 -216
  182. package/src/utils/ValueController.ts +28 -28
  183. package/src/utils/acessibility.ts +92 -92
  184. package/src/utils/checkbox.ts +121 -121
  185. package/src/utils/css.ts +119 -119
  186. package/src/utils/options.ts +9 -9
  187. package/src/utils/radio.ts +93 -93
  188. package/src/utils/react.ts +6 -6
  189. package/tsconfig.json +10 -10
@@ -1,125 +1,125 @@
1
- import { listToClass } from '@stack-spot/portal-theme'
2
- import { useCitricController } from '../context/hooks'
3
- import { HTMLTag, WithColorPalette, WithColorScheme } from '../types'
4
- import { withRef } from '../utils/react'
5
- import { asCitricProps, CitricComponent } from './CitricComponent'
6
-
7
- type ImageBoxTag = 'a' | 'button' | 'span' | 'div'
8
-
9
- export interface BaseImageBoxProps<T extends ImageBoxTag> extends WithColorPalette, WithColorScheme {
10
- /**
11
- * The HTML element to render.
12
- *
13
- * @default 'div'
14
- */
15
- tag?: T,
16
- /**
17
- * The box appearance.
18
- *
19
- * @default 'circle'
20
- */
21
- appearance?: 'circle' | 'square',
22
- /**
23
- * Size of the box.
24
- *
25
- * - xs: 20px;
26
- * - sm: 24px;
27
- * - md: 32px;
28
- * - lg: 40px.
29
- *
30
- * @default 'sm'
31
- */
32
- size?: 'xs' | 'sm' | 'md' | 'lg',
33
- /**
34
- * Animated text to show on when the button/link is clicked. This is only valid for buttons and anchors.
35
- */
36
- feedback?: string,
37
- /**
38
- * Only valid if `tag` is "button" or "a".
39
- *
40
- * Metadata for the general onClick event, set by the CitricController. Useful for creating analytics data.
41
- *
42
- * This only takes effect if there's a CitricController in React's context. The value of `metadata` is passed to the function
43
- * `onClickButton/onClickLink` of the controller.
44
- *
45
- * @default false
46
- */
47
- metadata?: any,
48
- }
49
-
50
- export type ImageBoxProps<T extends ImageBoxTag> = HTMLTag[T] & BaseImageBoxProps<T>
51
-
52
- /**
53
- * Renders a wrapper for its child (normally an image). The image will be resized and cropped to fit the container. The image is not cropped
54
- * if the property "feedback" is set.
55
- *
56
- * Hover and focus effects are applied if the ImageBox is focusable.
57
- *
58
- * This works exactly like an IconBox, but instead of an Icon, it can contain anything.
59
- *
60
- * @example
61
- * ```
62
- * <ImageBox><img src="https://images.com/myimage.png" /></ImageBox>
63
- * ```
64
- */
65
- export const ImageBox = withRef(
66
- function ImageBox<T extends ImageBoxTag = 'div'>(
67
- { tag, appearance, size, className, metadata, onClick, feedback, ...props }: ImageBoxProps<T>,
68
- ) {
69
- props['aria-label'] ||= props.title // accessibility
70
- const citric = useCitricController()
71
-
72
- function handleClick(e: React.MouseEvent<any>) {
73
- onClick?.(e)
74
- if (tag === 'button') citric?.onClickButton?.(e, metadata)
75
- else if (tag === 'a') citric?.onClickLink?.(e, metadata)
76
- }
77
-
78
- const componentProps = {
79
- tag: (tag || 'i') as any,
80
- component: 'icon-box',
81
- className: listToClass([appearance, size, className]),
82
- 'data-feedback': feedback || undefined,
83
- onClick: ['button', 'a'].includes(tag ?? '') ? handleClick : onClick,
84
- ...props,
85
- } as const
86
-
87
- return tag === 'a' && citric?.renderLink
88
- ? citric.renderLink(asCitricProps(componentProps))
89
- : <CitricComponent {...componentProps} />
90
- },
91
- )
92
-
93
- /**
94
- * A shortcut for `<ImageBox tag="button">`.
95
- *
96
- * Whenever a button is clicked, the function `onClickButton` of the nearest CitricController is called with the event and the value of the
97
- * prop `metadata`.
98
- *
99
- * @example
100
- * ```
101
- * <ImageButton><img src="https://images.com/myimage.png" /></ImageButton>
102
- * ```
103
- */
104
- export const ImageButton = withRef(
105
- function ImageButton(props: Omit<ImageBoxProps<'button'>, 'tag'>) {
106
- return <ImageBox {...props} tag="button" type={props.type || 'button'} />
107
- },
108
- )
109
-
110
- /**
111
- * A shortcut for `<ImageBox tag="a">`.
112
- *
113
- * Whenever a link is clicked, the function `onClickLink` of the nearest CitricController is called with the event and the value of the
114
- * prop `metadata`.
115
- *
116
- * @example
117
- * ```
118
- * <ImageLink href="#"><img src="https://images.com/myimage.png" /></ImageButton>
119
- * ```
120
- */
121
- export const ImageLink = withRef(
122
- function ImageLink(props: Omit<ImageBoxProps<'a'>, 'tag'>) {
123
- return <ImageBox {...props} tag="a" />
124
- },
125
- )
1
+ import { listToClass } from '@stack-spot/portal-theme'
2
+ import { useCitricController } from '../context/hooks'
3
+ import { HTMLTag, WithColorPalette, WithColorScheme } from '../types'
4
+ import { withRef } from '../utils/react'
5
+ import { asCitricProps, CitricComponent } from './CitricComponent'
6
+
7
+ type ImageBoxTag = 'a' | 'button' | 'span' | 'div'
8
+
9
+ export interface BaseImageBoxProps<T extends ImageBoxTag> extends WithColorPalette, WithColorScheme {
10
+ /**
11
+ * The HTML element to render.
12
+ *
13
+ * @default 'div'
14
+ */
15
+ tag?: T,
16
+ /**
17
+ * The box appearance.
18
+ *
19
+ * @default 'circle'
20
+ */
21
+ appearance?: 'circle' | 'square',
22
+ /**
23
+ * Size of the box.
24
+ *
25
+ * - xs: 20px;
26
+ * - sm: 24px;
27
+ * - md: 32px;
28
+ * - lg: 40px.
29
+ *
30
+ * @default 'sm'
31
+ */
32
+ size?: 'xs' | 'sm' | 'md' | 'lg',
33
+ /**
34
+ * Animated text to show on when the button/link is clicked. This is only valid for buttons and anchors.
35
+ */
36
+ feedback?: string,
37
+ /**
38
+ * Only valid if `tag` is "button" or "a".
39
+ *
40
+ * Metadata for the general onClick event, set by the CitricController. Useful for creating analytics data.
41
+ *
42
+ * This only takes effect if there's a CitricController in React's context. The value of `metadata` is passed to the function
43
+ * `onClickButton/onClickLink` of the controller.
44
+ *
45
+ * @default false
46
+ */
47
+ metadata?: any,
48
+ }
49
+
50
+ export type ImageBoxProps<T extends ImageBoxTag> = HTMLTag[T] & BaseImageBoxProps<T>
51
+
52
+ /**
53
+ * Renders a wrapper for its child (normally an image). The image will be resized and cropped to fit the container. The image is not cropped
54
+ * if the property "feedback" is set.
55
+ *
56
+ * Hover and focus effects are applied if the ImageBox is focusable.
57
+ *
58
+ * This works exactly like an IconBox, but instead of an Icon, it can contain anything.
59
+ *
60
+ * @example
61
+ * ```
62
+ * <ImageBox><img src="https://images.com/myimage.png" /></ImageBox>
63
+ * ```
64
+ */
65
+ export const ImageBox = withRef(
66
+ function ImageBox<T extends ImageBoxTag = 'div'>(
67
+ { tag, appearance, size, className, metadata, onClick, feedback, ...props }: ImageBoxProps<T>,
68
+ ) {
69
+ props['aria-label'] ||= props.title // accessibility
70
+ const citric = useCitricController()
71
+
72
+ function handleClick(e: React.MouseEvent<any>) {
73
+ onClick?.(e)
74
+ if (tag === 'button') citric?.onClickButton?.(e, metadata)
75
+ else if (tag === 'a') citric?.onClickLink?.(e, metadata)
76
+ }
77
+
78
+ const componentProps = {
79
+ tag: (tag || 'i') as any,
80
+ component: 'icon-box',
81
+ className: listToClass([appearance, size, className]),
82
+ 'data-feedback': feedback || undefined,
83
+ onClick: ['button', 'a'].includes(tag ?? '') ? handleClick : onClick,
84
+ ...props,
85
+ } as const
86
+
87
+ return tag === 'a' && citric?.renderLink
88
+ ? citric.renderLink(asCitricProps(componentProps))
89
+ : <CitricComponent {...componentProps} />
90
+ },
91
+ )
92
+
93
+ /**
94
+ * A shortcut for `<ImageBox tag="button">`.
95
+ *
96
+ * Whenever a button is clicked, the function `onClickButton` of the nearest CitricController is called with the event and the value of the
97
+ * prop `metadata`.
98
+ *
99
+ * @example
100
+ * ```
101
+ * <ImageButton><img src="https://images.com/myimage.png" /></ImageButton>
102
+ * ```
103
+ */
104
+ export const ImageButton = withRef(
105
+ function ImageButton(props: Omit<ImageBoxProps<'button'>, 'tag'>) {
106
+ return <ImageBox {...props} tag="button" type={props.type || 'button'} />
107
+ },
108
+ )
109
+
110
+ /**
111
+ * A shortcut for `<ImageBox tag="a">`.
112
+ *
113
+ * Whenever a link is clicked, the function `onClickLink` of the nearest CitricController is called with the event and the value of the
114
+ * prop `metadata`.
115
+ *
116
+ * @example
117
+ * ```
118
+ * <ImageLink href="#"><img src="https://images.com/myimage.png" /></ImageButton>
119
+ * ```
120
+ */
121
+ export const ImageLink = withRef(
122
+ function ImageLink(props: Omit<ImageBoxProps<'a'>, 'tag'>) {
123
+ return <ImageBox {...props} tag="a" />
124
+ },
125
+ )
@@ -1,65 +1,65 @@
1
- import { listToClass } from '@stack-spot/portal-theme'
2
- import { useEffect, useMemo, useState } from 'react'
3
- import { WithStyleShortcuts } from '../types'
4
- import { getStyleFromProps } from '../utils/css'
5
- import { Skeleton } from './Skeleton'
6
-
7
- export interface BaseImageWithFallbackProps extends Pick<WithStyleShortcuts, 'w' | 'h' | 'radius' | 'bg'> {
8
- /**
9
- * The react element to fallback to if the image can't be rendered.
10
- */
11
- fallback: React.ReactNode,
12
- /**
13
- * Whether or not to show a skeleton when the image is still loading.
14
- * @default false
15
- */
16
- showLoading?: boolean,
17
- }
18
-
19
- export type ImageWithFallbackProps = JSX.IntrinsicElements['img'] & BaseImageWithFallbackProps
20
-
21
- /**
22
- * Attempts to render an image. If it succeeds, the image is displayed, otherwise, a fallback is rendered.
23
- *
24
- * This is very useful if the value of "src" may be empty or if it may be broken. Instead of rendering nothing or the default browser error,
25
- * it renders the fallback passed as parameter.
26
- *
27
- * @example
28
- * ```
29
- * <ImageWithFallback src={item.image} fallback={<Icon icon="Agent" />} />
30
- * ```
31
- */
32
- export const ImageWithFallback = (
33
- { onLoad, onError, className, style: ogStyle, fallback, showLoading, w, h, radius, bg, ...props }: ImageWithFallbackProps,
34
- ) => {
35
- const [state, setState] = useState<'loading' | 'ready' | 'error'>(props.src ? 'loading' : 'error')
36
- const isLoading = state === 'loading' && showLoading
37
- const style = useMemo(() => ({ ...getStyleFromProps({ w, h, radius, bg }), ...ogStyle }), [ogStyle, w, h, radius, bg])
38
-
39
- function handleLoad(e: React.SyntheticEvent<HTMLImageElement, Event>) {
40
- if (state === 'loading') setState('ready')
41
- onLoad?.(e)
42
- }
43
-
44
- function handleError(e: React.SyntheticEvent<HTMLImageElement, Event>) {
45
- setState('error')
46
- onError?.(e)
47
- }
48
-
49
- useEffect(() => {
50
- if (state !== 'loading') setState(props.src ? 'loading' : 'error')
51
- }, [props.src])
52
-
53
- if (state === 'error') return <div className={listToClass([className, 'center'])} style={style}>{fallback}</div>
54
- return <>
55
- {isLoading && <Skeleton className={className} style={style} />}
56
- <img
57
- onLoad={handleLoad}
58
- onError={handleError}
59
- style={{ ...style, ...(isLoading ? { pointerEvents: 'none', position: 'absolute', opacity: 0 } : {}) }}
60
- aria-hidden={isLoading}
61
- className={className}
62
- {...props}
63
- />
64
- </>
65
- }
1
+ import { listToClass } from '@stack-spot/portal-theme'
2
+ import { useEffect, useMemo, useState } from 'react'
3
+ import { WithStyleShortcuts } from '../types'
4
+ import { getStyleFromProps } from '../utils/css'
5
+ import { Skeleton } from './Skeleton'
6
+
7
+ export interface BaseImageWithFallbackProps extends Pick<WithStyleShortcuts, 'w' | 'h' | 'radius' | 'bg'> {
8
+ /**
9
+ * The react element to fallback to if the image can't be rendered.
10
+ */
11
+ fallback: React.ReactNode,
12
+ /**
13
+ * Whether or not to show a skeleton when the image is still loading.
14
+ * @default false
15
+ */
16
+ showLoading?: boolean,
17
+ }
18
+
19
+ export type ImageWithFallbackProps = JSX.IntrinsicElements['img'] & BaseImageWithFallbackProps
20
+
21
+ /**
22
+ * Attempts to render an image. If it succeeds, the image is displayed, otherwise, a fallback is rendered.
23
+ *
24
+ * This is very useful if the value of "src" may be empty or if it may be broken. Instead of rendering nothing or the default browser error,
25
+ * it renders the fallback passed as parameter.
26
+ *
27
+ * @example
28
+ * ```
29
+ * <ImageWithFallback src={item.image} fallback={<Icon icon="Agent" />} />
30
+ * ```
31
+ */
32
+ export const ImageWithFallback = (
33
+ { onLoad, onError, className, style: ogStyle, fallback, showLoading, w, h, radius, bg, ...props }: ImageWithFallbackProps,
34
+ ) => {
35
+ const [state, setState] = useState<'loading' | 'ready' | 'error'>(props.src ? 'loading' : 'error')
36
+ const isLoading = state === 'loading' && showLoading
37
+ const style = useMemo(() => ({ ...getStyleFromProps({ w, h, radius, bg }), ...ogStyle }), [ogStyle, w, h, radius, bg])
38
+
39
+ function handleLoad(e: React.SyntheticEvent<HTMLImageElement, Event>) {
40
+ if (state === 'loading') setState('ready')
41
+ onLoad?.(e)
42
+ }
43
+
44
+ function handleError(e: React.SyntheticEvent<HTMLImageElement, Event>) {
45
+ setState('error')
46
+ onError?.(e)
47
+ }
48
+
49
+ useEffect(() => {
50
+ if (state !== 'loading') setState(props.src ? 'loading' : 'error')
51
+ }, [props.src])
52
+
53
+ if (state === 'error') return <div className={listToClass([className, 'center'])} style={style}>{fallback}</div>
54
+ return <>
55
+ {isLoading && <Skeleton className={className} style={style} />}
56
+ <img
57
+ onLoad={handleLoad}
58
+ onError={handleError}
59
+ style={{ ...style, ...(isLoading ? { pointerEvents: 'none', position: 'absolute', opacity: 0 } : {}) }}
60
+ aria-hidden={isLoading}
61
+ className={className}
62
+ {...props}
63
+ />
64
+ </>
65
+ }
@@ -1,49 +1,49 @@
1
- import { ControlledInput, WithColorScheme } from '../types'
2
- import { withRef } from '../utils/react'
3
- import { CitricComponent } from './CitricComponent'
4
-
5
- export type SupportedInputType = 'color' | 'date' | 'datetime-local' | 'email' | 'month' | 'number' | 'password' | 'search' | 'tel' |
6
- 'text' | 'time' | 'url' | 'week'
7
-
8
- export interface BaseInputProps<T extends SupportedInputType> extends WithColorScheme {
9
- /**
10
- * @default 'text'
11
- */
12
- type?: T,
13
- value?: T extends 'number' ? number : string,
14
- onChange?: (value: T extends 'number' ? (number | undefined) : string) => void,
15
- }
16
-
17
- export type InputProps<T extends SupportedInputType = 'text'> =
18
- ControlledInput & BaseInputProps<T> & { type?: T }
19
-
20
- /**
21
- * Renders a input.
22
- *
23
- * Attention: "onChange" doesn't receive an event, instead, it receives the new value of the input: a string, unless "type" is "number",
24
- * in this case, it receives a number.
25
- *
26
- * @example
27
- *
28
- * ```
29
- * const [value, setValue] = useState('')
30
- *
31
- * return <Input value={value} onChange={setValue} />
32
- * ```
33
- */
34
- export const Input = withRef(
35
- function Input<T extends SupportedInputType = 'text'>({ type, value, onChange, ...props }: InputProps<T>) {
36
- function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
37
- if (!onChange) return
38
- const newValue = e.target.value
39
- if (type === 'number') {
40
- const parsed = newValue ? parseFloat(newValue) : undefined
41
- onChange(parsed as any)
42
- } else {
43
- onChange(newValue as any)
44
- }
45
- }
46
-
47
- return <CitricComponent tag="input" component="input" type={type} value={value} onChange={handleChange} {...props} />
48
- },
49
- )
1
+ import { ControlledInput, WithColorScheme } from '../types'
2
+ import { withRef } from '../utils/react'
3
+ import { CitricComponent } from './CitricComponent'
4
+
5
+ export type SupportedInputType = 'color' | 'date' | 'datetime-local' | 'email' | 'month' | 'number' | 'password' | 'search' | 'tel' |
6
+ 'text' | 'time' | 'url' | 'week'
7
+
8
+ export interface BaseInputProps<T extends SupportedInputType> extends WithColorScheme {
9
+ /**
10
+ * @default 'text'
11
+ */
12
+ type?: T,
13
+ value?: T extends 'number' ? number : string,
14
+ onChange?: (value: T extends 'number' ? (number | undefined) : string) => void,
15
+ }
16
+
17
+ export type InputProps<T extends SupportedInputType = 'text'> =
18
+ ControlledInput & BaseInputProps<T> & { type?: T }
19
+
20
+ /**
21
+ * Renders a input.
22
+ *
23
+ * Attention: "onChange" doesn't receive an event, instead, it receives the new value of the input: a string, unless "type" is "number",
24
+ * in this case, it receives a number.
25
+ *
26
+ * @example
27
+ *
28
+ * ```
29
+ * const [value, setValue] = useState('')
30
+ *
31
+ * return <Input value={value} onChange={setValue} />
32
+ * ```
33
+ */
34
+ export const Input = withRef(
35
+ function Input<T extends SupportedInputType = 'text'>({ type, value, onChange, ...props }: InputProps<T>) {
36
+ function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
37
+ if (!onChange) return
38
+ const newValue = e.target.value
39
+ if (type === 'number') {
40
+ const parsed = newValue ? parseFloat(newValue) : undefined
41
+ onChange(parsed as any)
42
+ } else {
43
+ onChange(newValue as any)
44
+ }
45
+ }
46
+
47
+ return <CitricComponent tag="input" component="input" type={type} value={value} onChange={handleChange} {...props} />
48
+ },
49
+ )
@@ -1,55 +1,55 @@
1
- import { useCitricController } from '../context/hooks'
2
- import { TextAppearance, WithColor } from '../types'
3
- import { applyColor, applyTextAppearance } from '../utils/css'
4
- import { withRef } from '../utils/react'
5
- import { asCitricProps, CitricComponent } from './CitricComponent'
6
-
7
- export interface BaseLinkProps extends WithColor {
8
- /**
9
- * The text appearance (font and size).
10
- * By default, it inherits from the parent element.
11
- */
12
- appearance?: TextAppearance,
13
- /**
14
- * Metadata for the general onClick event, set by the CitricController. Useful for creating analytics data.
15
- *
16
- * This only takes effect if there's a CitricController in React's context. The value of `metadata` is passed to the function
17
- * `onClickLink` of the controller.
18
- *
19
- * @default false
20
- */
21
- metadata?: any,
22
- /**
23
- * Whether or not to underline the link.
24
- *
25
- * @default true
26
- */
27
- underline?: boolean,
28
- }
29
-
30
- export type LinkProps = React.JSX.IntrinsicElements['a'] & BaseLinkProps
31
-
32
- /**
33
- * Renders an html anchor by default, the actual component to render may be set on a CitricController, through the function `renderLink`.
34
- *
35
- * Whenever a link is clicked, the function `onClickLink` of the nearest CitricController is called with the event and the value of the
36
- * prop `metadata`.
37
- */
38
- export const Link = withRef(
39
- ({ appearance, color, style, className, children, onClick, metadata, underline = true, ...props }: LinkProps) => {
40
- const citric = useCitricController()
41
- const linkProps = {
42
- component: 'link',
43
- style: applyColor({ ...style, textDecoration: underline ? undefined : 'none' }, color),
44
- className: applyTextAppearance(className, appearance),
45
- onClick: (e: React.MouseEvent<HTMLAnchorElement>) => {
46
- onClick?.(e)
47
- citric?.onClickLink?.(e, metadata ?? false)
48
- },
49
- ...props,
50
- } as const
51
- return citric?.renderLink
52
- ? citric.renderLink(asCitricProps({ ...linkProps, children }))
53
- : <CitricComponent tag="a" {...linkProps}>{children}</CitricComponent>
54
- },
55
- )
1
+ import { useCitricController } from '../context/hooks'
2
+ import { TextAppearance, WithColor } from '../types'
3
+ import { applyColor, applyTextAppearance } from '../utils/css'
4
+ import { withRef } from '../utils/react'
5
+ import { asCitricProps, CitricComponent } from './CitricComponent'
6
+
7
+ export interface BaseLinkProps extends WithColor {
8
+ /**
9
+ * The text appearance (font and size).
10
+ * By default, it inherits from the parent element.
11
+ */
12
+ appearance?: TextAppearance,
13
+ /**
14
+ * Metadata for the general onClick event, set by the CitricController. Useful for creating analytics data.
15
+ *
16
+ * This only takes effect if there's a CitricController in React's context. The value of `metadata` is passed to the function
17
+ * `onClickLink` of the controller.
18
+ *
19
+ * @default false
20
+ */
21
+ metadata?: any,
22
+ /**
23
+ * Whether or not to underline the link.
24
+ *
25
+ * @default true
26
+ */
27
+ underline?: boolean,
28
+ }
29
+
30
+ export type LinkProps = React.JSX.IntrinsicElements['a'] & BaseLinkProps
31
+
32
+ /**
33
+ * Renders an html anchor by default, the actual component to render may be set on a CitricController, through the function `renderLink`.
34
+ *
35
+ * Whenever a link is clicked, the function `onClickLink` of the nearest CitricController is called with the event and the value of the
36
+ * prop `metadata`.
37
+ */
38
+ export const Link = withRef(
39
+ ({ appearance, color, style, className, children, onClick, metadata, underline = true, ...props }: LinkProps) => {
40
+ const citric = useCitricController()
41
+ const linkProps = {
42
+ component: 'link',
43
+ style: applyColor({ ...style, textDecoration: underline ? undefined : 'none' }, color),
44
+ className: applyTextAppearance(className, appearance),
45
+ onClick: (e: React.MouseEvent<HTMLAnchorElement>) => {
46
+ onClick?.(e)
47
+ citric?.onClickLink?.(e, metadata ?? false)
48
+ },
49
+ ...props,
50
+ } as const
51
+ return citric?.renderLink
52
+ ? citric.renderLink(asCitricProps({ ...linkProps, children }))
53
+ : <CitricComponent tag="a" {...linkProps}>{children}</CitricComponent>
54
+ },
55
+ )
@@ -1,8 +1,8 @@
1
- import { Center } from './layout'
2
- import { ProgressCircular } from './ProgressCircular'
3
-
4
- export const LoadingPanel = () => (
5
- <Center flex={1} p="80px" style={{ alignSelf: 'stretch' }} data-test-hint="loading">
6
- <ProgressCircular />
7
- </Center>
8
- )
1
+ import { Center } from './layout'
2
+ import { ProgressCircular } from './ProgressCircular'
3
+
4
+ export const LoadingPanel = () => (
5
+ <Center flex={1} p="80px" style={{ alignSelf: 'stretch' }} data-test-hint="loading">
6
+ <ProgressCircular />
7
+ </Center>
8
+ )