@stack-spot/citric-react 0.35.1 → 0.37.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 (186) hide show
  1. package/dist/citric.css +2844 -2832
  2. package/dist/components/Accordion.d.ts +1 -1
  3. package/dist/components/Accordion.js +1 -1
  4. package/dist/components/Alert.d.ts +1 -1
  5. package/dist/components/Alert.js +1 -1
  6. package/dist/components/AsyncContent.d.ts +1 -1
  7. package/dist/components/AsyncContent.js +1 -1
  8. package/dist/components/Avatar.d.ts +1 -1
  9. package/dist/components/Avatar.js +1 -1
  10. package/dist/components/AvatarGroup.d.ts +1 -1
  11. package/dist/components/AvatarGroup.js +1 -1
  12. package/dist/components/Badge.d.ts +1 -1
  13. package/dist/components/Badge.js +1 -1
  14. package/dist/components/Blockquote.d.ts +1 -1
  15. package/dist/components/Blockquote.js +1 -1
  16. package/dist/components/Breadcrumb.d.ts +1 -1
  17. package/dist/components/Breadcrumb.js +1 -1
  18. package/dist/components/Button.d.ts +1 -1
  19. package/dist/components/Button.js +1 -1
  20. package/dist/components/ButtonLink.d.ts +1 -1
  21. package/dist/components/ButtonLink.js +1 -1
  22. package/dist/components/Card.d.ts +1 -1
  23. package/dist/components/Card.js +1 -1
  24. package/dist/components/Checkbox.d.ts +1 -1
  25. package/dist/components/Checkbox.js +1 -1
  26. package/dist/components/CheckboxGroup.d.ts +1 -1
  27. package/dist/components/CheckboxGroup.js +1 -1
  28. package/dist/components/Circle.d.ts +1 -1
  29. package/dist/components/Circle.js +1 -1
  30. package/dist/components/Divider.d.ts +1 -1
  31. package/dist/components/Divider.js +1 -1
  32. package/dist/components/ErrorBoundary.d.ts +1 -1
  33. package/dist/components/ErrorBoundary.js +1 -1
  34. package/dist/components/ErrorMessage.d.ts +1 -1
  35. package/dist/components/ErrorMessage.js +1 -1
  36. package/dist/components/FallbackBoundary.d.ts +1 -1
  37. package/dist/components/FallbackBoundary.js +1 -1
  38. package/dist/components/Favorite.d.ts +1 -1
  39. package/dist/components/Favorite.js +1 -1
  40. package/dist/components/FieldGroup.d.ts +1 -1
  41. package/dist/components/FieldGroup.js +1 -1
  42. package/dist/components/Form.d.ts +2 -2
  43. package/dist/components/Form.js +1 -1
  44. package/dist/components/FormGroup.d.ts +1 -1
  45. package/dist/components/FormGroup.js +1 -1
  46. package/dist/components/Icon.d.ts +1 -1
  47. package/dist/components/Icon.js +1 -1
  48. package/dist/components/IconBox.d.ts +3 -3
  49. package/dist/components/IconBox.js +1 -1
  50. package/dist/components/ImageBox.d.ts +3 -3
  51. package/dist/components/ImageBox.js +1 -1
  52. package/dist/components/ImageWithFallback.d.ts +1 -1
  53. package/dist/components/ImageWithFallback.js +1 -1
  54. package/dist/components/Input.d.ts +1 -1
  55. package/dist/components/Input.js +1 -1
  56. package/dist/components/Link.d.ts +1 -1
  57. package/dist/components/Link.js +1 -1
  58. package/dist/components/LoadingPanel.d.ts +1 -1
  59. package/dist/components/LoadingPanel.js +1 -1
  60. package/dist/components/MenuOverlay/Menu.d.ts +1 -1
  61. package/dist/components/MenuOverlay/Menu.js +1 -1
  62. package/dist/components/MenuOverlay/index.d.ts +1 -1
  63. package/dist/components/MenuOverlay/index.js +1 -1
  64. package/dist/components/Overlay/index.d.ts +1 -1
  65. package/dist/components/Overlay/index.js +1 -1
  66. package/dist/components/Pagination.d.ts +1 -1
  67. package/dist/components/Pagination.js +1 -1
  68. package/dist/components/ProgressBar.d.ts +1 -1
  69. package/dist/components/ProgressBar.js +1 -1
  70. package/dist/components/ProgressCircular.d.ts +1 -1
  71. package/dist/components/ProgressCircular.js +1 -1
  72. package/dist/components/RadioGroup.d.ts +1 -1
  73. package/dist/components/RadioGroup.js +1 -1
  74. package/dist/components/Rating.d.ts +17 -3
  75. package/dist/components/Rating.d.ts.map +1 -1
  76. package/dist/components/Rating.js +11 -3
  77. package/dist/components/Rating.js.map +1 -1
  78. package/dist/components/Select/MultiSelect.d.ts +1 -1
  79. package/dist/components/Select/MultiSelect.js +1 -1
  80. package/dist/components/Select/RichSelect.d.ts +1 -1
  81. package/dist/components/Select/RichSelect.js +1 -1
  82. package/dist/components/Select/SimpleSelect.d.ts +1 -1
  83. package/dist/components/Select/SimpleSelect.js +1 -1
  84. package/dist/components/Select/index.d.ts +1 -1
  85. package/dist/components/Select/index.js +1 -1
  86. package/dist/components/SelectBox.d.ts +1 -1
  87. package/dist/components/SelectBox.js +1 -1
  88. package/dist/components/Skeleton.d.ts +1 -1
  89. package/dist/components/Skeleton.js +1 -1
  90. package/dist/components/Slider.d.ts +1 -1
  91. package/dist/components/Slider.js +1 -1
  92. package/dist/components/SmartTable.d.ts +1 -1
  93. package/dist/components/SmartTable.js +1 -1
  94. package/dist/components/Stepper.d.ts +1 -1
  95. package/dist/components/Stepper.js +1 -1
  96. package/dist/components/Table.d.ts +3 -3
  97. package/dist/components/Table.js +1 -1
  98. package/dist/components/Tabs/index.d.ts +1 -1
  99. package/dist/components/Tabs/index.js +1 -1
  100. package/dist/components/Textarea.d.ts +1 -1
  101. package/dist/components/Textarea.js +1 -1
  102. package/dist/components/Tooltip.d.ts +1 -1
  103. package/dist/components/Tooltip.js +1 -1
  104. package/dist/context/CitricProvider.d.ts +1 -1
  105. package/dist/context/CitricProvider.js +1 -1
  106. package/dist/overlay.js +1 -1
  107. package/dist/theme.css +415 -415
  108. package/package.json +7 -6
  109. package/scripts/build-css.ts +49 -49
  110. package/src/components/Accordion.tsx +130 -130
  111. package/src/components/Alert.tsx +24 -24
  112. package/src/components/AsyncContent.tsx +70 -70
  113. package/src/components/Avatar.tsx +45 -45
  114. package/src/components/AvatarGroup.tsx +49 -49
  115. package/src/components/Badge.tsx +47 -47
  116. package/src/components/Blockquote.tsx +18 -18
  117. package/src/components/Breadcrumb.tsx +33 -33
  118. package/src/components/Button.tsx +105 -105
  119. package/src/components/ButtonLink.tsx +45 -45
  120. package/src/components/Card.tsx +68 -68
  121. package/src/components/Checkbox.tsx +51 -51
  122. package/src/components/CheckboxGroup.tsx +152 -152
  123. package/src/components/Circle.tsx +43 -43
  124. package/src/components/CitricComponent.ts +47 -47
  125. package/src/components/Divider.tsx +24 -24
  126. package/src/components/ErrorBoundary.tsx +75 -75
  127. package/src/components/ErrorMessage.tsx +11 -11
  128. package/src/components/FallbackBoundary.tsx +40 -40
  129. package/src/components/Favorite.tsx +57 -57
  130. package/src/components/FieldGroup.tsx +46 -46
  131. package/src/components/Form.tsx +36 -36
  132. package/src/components/FormGroup.tsx +57 -57
  133. package/src/components/Icon.tsx +35 -35
  134. package/src/components/IconBox.tsx +134 -134
  135. package/src/components/ImageBox.tsx +125 -125
  136. package/src/components/ImageWithFallback.tsx +65 -65
  137. package/src/components/Input.tsx +49 -49
  138. package/src/components/Link.tsx +55 -55
  139. package/src/components/LoadingPanel.tsx +8 -8
  140. package/src/components/MenuOverlay/Menu.tsx +158 -158
  141. package/src/components/MenuOverlay/context.ts +20 -20
  142. package/src/components/MenuOverlay/index.tsx +55 -55
  143. package/src/components/MenuOverlay/keyboard.ts +60 -60
  144. package/src/components/MenuOverlay/types.ts +171 -171
  145. package/src/components/Overlay/context.ts +10 -10
  146. package/src/components/Overlay/index.tsx +164 -164
  147. package/src/components/Overlay/types.ts +70 -70
  148. package/src/components/Pagination.tsx +113 -113
  149. package/src/components/ProgressBar.tsx +45 -45
  150. package/src/components/ProgressCircular.tsx +45 -45
  151. package/src/components/RadioGroup.tsx +146 -146
  152. package/src/components/Rating.tsx +98 -35
  153. package/src/components/Select/MultiSelect.tsx +217 -217
  154. package/src/components/Select/RichSelect.tsx +128 -128
  155. package/src/components/Select/SimpleSelect.tsx +73 -73
  156. package/src/components/Select/hooks.ts +133 -133
  157. package/src/components/Select/index.tsx +35 -35
  158. package/src/components/Select/types.ts +134 -134
  159. package/src/components/SelectBox.tsx +167 -167
  160. package/src/components/Skeleton.tsx +53 -53
  161. package/src/components/Slider.tsx +89 -89
  162. package/src/components/SmartTable.tsx +227 -227
  163. package/src/components/Stepper.tsx +163 -163
  164. package/src/components/Table.tsx +234 -234
  165. package/src/components/Tabs/TabController.ts +54 -54
  166. package/src/components/Tabs/index.tsx +87 -87
  167. package/src/components/Tabs/types.ts +54 -54
  168. package/src/components/Tabs/utils.ts +6 -6
  169. package/src/components/Text.ts +111 -111
  170. package/src/components/Textarea.tsx +27 -27
  171. package/src/components/Tooltip.tsx +72 -72
  172. package/src/components/layout.tsx +101 -101
  173. package/src/context/CitricContext.tsx +4 -4
  174. package/src/context/CitricProvider.tsx +14 -14
  175. package/src/context/hooks.ts +6 -6
  176. package/src/index.ts +58 -58
  177. package/src/overlay.ts +341 -341
  178. package/src/types.ts +216 -216
  179. package/src/utils/ValueController.ts +28 -28
  180. package/src/utils/acessibility.ts +92 -92
  181. package/src/utils/checkbox.ts +121 -121
  182. package/src/utils/css.ts +119 -119
  183. package/src/utils/options.ts +9 -9
  184. package/src/utils/radio.ts +93 -93
  185. package/src/utils/react.ts +6 -6
  186. 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
+ )