@stack-spot/citric-react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/citric.css +2580 -0
- package/dist/components/Accordion.d.ts +33 -0
- package/dist/components/Accordion.d.ts.map +1 -0
- package/dist/components/Accordion.js +19 -0
- package/dist/components/Accordion.js.map +1 -0
- package/dist/components/Alert.d.ts +11 -0
- package/dist/components/Alert.d.ts.map +1 -0
- package/dist/components/Alert.js +5 -0
- package/dist/components/Alert.js.map +1 -0
- package/dist/components/AsyncContent.d.ts +30 -0
- package/dist/components/AsyncContent.d.ts.map +1 -0
- package/dist/components/AsyncContent.js +33 -0
- package/dist/components/AsyncContent.js.map +1 -0
- package/dist/components/Avatar.d.ts +22 -0
- package/dist/components/Avatar.d.ts.map +1 -0
- package/dist/components/Avatar.js +9 -0
- package/dist/components/Avatar.js.map +1 -0
- package/dist/components/AvatarGroup.d.ts +25 -0
- package/dist/components/AvatarGroup.d.ts.map +1 -0
- package/dist/components/AvatarGroup.js +9 -0
- package/dist/components/AvatarGroup.js.map +1 -0
- package/dist/components/Badge.d.ts +18 -0
- package/dist/components/Badge.d.ts.map +1 -0
- package/dist/components/Badge.js +7 -0
- package/dist/components/Badge.js.map +1 -0
- package/dist/components/Blockquote.d.ts +5 -0
- package/dist/components/Blockquote.d.ts.map +1 -0
- package/dist/components/Blockquote.js +4 -0
- package/dist/components/Blockquote.js.map +1 -0
- package/dist/components/Breadcrumb.d.ts +12 -0
- package/dist/components/Breadcrumb.d.ts.map +1 -0
- package/dist/components/Breadcrumb.js +8 -0
- package/dist/components/Breadcrumb.js.map +1 -0
- package/dist/components/Button.d.ts +42 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Button.js +25 -0
- package/dist/components/Button.js.map +1 -0
- package/dist/components/Card.d.ts +19 -0
- package/dist/components/Card.d.ts.map +1 -0
- package/dist/components/Card.js +5 -0
- package/dist/components/Card.js.map +1 -0
- package/dist/components/Checkbox.d.ts +14 -0
- package/dist/components/Checkbox.d.ts.map +1 -0
- package/dist/components/Checkbox.js +7 -0
- package/dist/components/Checkbox.js.map +1 -0
- package/dist/components/CheckboxGroup.d.ts +53 -0
- package/dist/components/CheckboxGroup.d.ts.map +1 -0
- package/dist/components/CheckboxGroup.js +17 -0
- package/dist/components/CheckboxGroup.js.map +1 -0
- package/dist/components/Circle.d.ts +18 -0
- package/dist/components/Circle.d.ts.map +1 -0
- package/dist/components/Circle.js +5 -0
- package/dist/components/Circle.js.map +1 -0
- package/dist/components/CitricComponent.d.ts +14 -0
- package/dist/components/CitricComponent.d.ts.map +1 -0
- package/dist/components/CitricComponent.js +15 -0
- package/dist/components/CitricComponent.js.map +1 -0
- package/dist/components/Divider.d.ts +14 -0
- package/dist/components/Divider.d.ts.map +1 -0
- package/dist/components/Divider.js +5 -0
- package/dist/components/Divider.js.map +1 -0
- package/dist/components/ErrorBoundary.d.ts +32 -0
- package/dist/components/ErrorBoundary.d.ts.map +1 -0
- package/dist/components/ErrorBoundary.js +46 -0
- package/dist/components/ErrorBoundary.js.map +1 -0
- package/dist/components/ErrorMessage.d.ts +4 -0
- package/dist/components/ErrorMessage.d.ts.map +1 -0
- package/dist/components/ErrorMessage.js +7 -0
- package/dist/components/ErrorMessage.js.map +1 -0
- package/dist/components/FallbackBoundary.d.ts +13 -0
- package/dist/components/FallbackBoundary.d.ts.map +1 -0
- package/dist/components/FallbackBoundary.js +11 -0
- package/dist/components/FallbackBoundary.js.map +1 -0
- package/dist/components/Favorite.d.ts +23 -0
- package/dist/components/Favorite.d.ts.map +1 -0
- package/dist/components/Favorite.js +5 -0
- package/dist/components/Favorite.js.map +1 -0
- package/dist/components/FieldGroup.d.ts +14 -0
- package/dist/components/FieldGroup.d.ts.map +1 -0
- package/dist/components/FieldGroup.js +5 -0
- package/dist/components/FieldGroup.js.map +1 -0
- package/dist/components/Form.d.ts +5 -0
- package/dist/components/Form.d.ts.map +1 -0
- package/dist/components/Form.js +6 -0
- package/dist/components/Form.js.map +1 -0
- package/dist/components/FormGroup.d.ts +22 -0
- package/dist/components/FormGroup.d.ts.map +1 -0
- package/dist/components/FormGroup.js +8 -0
- package/dist/components/FormGroup.js.map +1 -0
- package/dist/components/IconBox.d.ts +46 -0
- package/dist/components/IconBox.d.ts.map +1 -0
- package/dist/components/IconBox.js +29 -0
- package/dist/components/IconBox.js.map +1 -0
- package/dist/components/Input.d.ts +15 -0
- package/dist/components/Input.d.ts.map +1 -0
- package/dist/components/Input.js +18 -0
- package/dist/components/Input.js.map +1 -0
- package/dist/components/Link.d.ts +20 -0
- package/dist/components/Link.d.ts.map +1 -0
- package/dist/components/Link.js +21 -0
- package/dist/components/Link.js.map +1 -0
- package/dist/components/LoadingPanel.d.ts +2 -0
- package/dist/components/LoadingPanel.d.ts.map +1 -0
- package/dist/components/LoadingPanel.js +5 -0
- package/dist/components/LoadingPanel.js.map +1 -0
- package/dist/components/MenuOverlay/Menu.d.ts +6 -0
- package/dist/components/MenuOverlay/Menu.d.ts.map +1 -0
- package/dist/components/MenuOverlay/Menu.js +100 -0
- package/dist/components/MenuOverlay/Menu.js.map +1 -0
- package/dist/components/MenuOverlay/context.d.ts +6 -0
- package/dist/components/MenuOverlay/context.d.ts.map +1 -0
- package/dist/components/MenuOverlay/context.js +16 -0
- package/dist/components/MenuOverlay/context.js.map +1 -0
- package/dist/components/MenuOverlay/index.d.ts +3 -0
- package/dist/components/MenuOverlay/index.d.ts.map +1 -0
- package/dist/components/MenuOverlay/index.js +23 -0
- package/dist/components/MenuOverlay/index.js.map +1 -0
- package/dist/components/MenuOverlay/keyboard.d.ts +2 -0
- package/dist/components/MenuOverlay/keyboard.d.ts.map +1 -0
- package/dist/components/MenuOverlay/keyboard.js +66 -0
- package/dist/components/MenuOverlay/keyboard.js.map +1 -0
- package/dist/components/MenuOverlay/types.d.ts +166 -0
- package/dist/components/MenuOverlay/types.d.ts.map +1 -0
- package/dist/components/MenuOverlay/types.js +2 -0
- package/dist/components/MenuOverlay/types.js.map +1 -0
- package/dist/components/Overlay/context.d.ts +4 -0
- package/dist/components/Overlay/context.d.ts.map +1 -0
- package/dist/components/Overlay/context.js +7 -0
- package/dist/components/Overlay/context.js.map +1 -0
- package/dist/components/Overlay/index.d.ts +14 -0
- package/dist/components/Overlay/index.d.ts.map +1 -0
- package/dist/components/Overlay/index.js +120 -0
- package/dist/components/Overlay/index.js.map +1 -0
- package/dist/components/Overlay/types.d.ts +67 -0
- package/dist/components/Overlay/types.d.ts.map +1 -0
- package/dist/components/Overlay/types.js +2 -0
- package/dist/components/Overlay/types.js.map +1 -0
- package/dist/components/Pagination.d.ts +28 -0
- package/dist/components/Pagination.d.ts.map +1 -0
- package/dist/components/Pagination.js +30 -0
- package/dist/components/Pagination.js.map +1 -0
- package/dist/components/ProgressBar.d.ts +12 -0
- package/dist/components/ProgressBar.d.ts.map +1 -0
- package/dist/components/ProgressBar.js +7 -0
- package/dist/components/ProgressBar.js.map +1 -0
- package/dist/components/ProgressCircular.d.ts +16 -0
- package/dist/components/ProgressCircular.d.ts.map +1 -0
- package/dist/components/ProgressCircular.js +7 -0
- package/dist/components/ProgressCircular.js.map +1 -0
- package/dist/components/RadioGroup.d.ts +48 -0
- package/dist/components/RadioGroup.d.ts.map +1 -0
- package/dist/components/RadioGroup.js +17 -0
- package/dist/components/RadioGroup.js.map +1 -0
- package/dist/components/Rating.d.ts +13 -0
- package/dist/components/Rating.d.ts.map +1 -0
- package/dist/components/Rating.js +4 -0
- package/dist/components/Rating.js.map +1 -0
- package/dist/components/Select/RichSelect.d.ts +5 -0
- package/dist/components/Select/RichSelect.d.ts.map +1 -0
- package/dist/components/Select/RichSelect.js +152 -0
- package/dist/components/Select/RichSelect.js.map +1 -0
- package/dist/components/Select/SimpleSelect.d.ts +5 -0
- package/dist/components/Select/SimpleSelect.d.ts.map +1 -0
- package/dist/components/Select/SimpleSelect.js +24 -0
- package/dist/components/Select/SimpleSelect.js.map +1 -0
- package/dist/components/Select/index.d.ts +4 -0
- package/dist/components/Select/index.d.ts.map +1 -0
- package/dist/components/Select/index.js +7 -0
- package/dist/components/Select/index.js.map +1 -0
- package/dist/components/Select/types.d.ts +118 -0
- package/dist/components/Select/types.d.ts.map +1 -0
- package/dist/components/Select/types.js +2 -0
- package/dist/components/Select/types.js.map +1 -0
- package/dist/components/SelectBox.d.ts +65 -0
- package/dist/components/SelectBox.d.ts.map +1 -0
- package/dist/components/SelectBox.js +26 -0
- package/dist/components/SelectBox.js.map +1 -0
- package/dist/components/Skeleton.d.ts +30 -0
- package/dist/components/Skeleton.d.ts.map +1 -0
- package/dist/components/Skeleton.js +5 -0
- package/dist/components/Skeleton.js.map +1 -0
- package/dist/components/Slider.d.ts +32 -0
- package/dist/components/Slider.d.ts.map +1 -0
- package/dist/components/Slider.js +19 -0
- package/dist/components/Slider.js.map +1 -0
- package/dist/components/SmartTable.d.ts +87 -0
- package/dist/components/SmartTable.d.ts.map +1 -0
- package/dist/components/SmartTable.js +16 -0
- package/dist/components/SmartTable.js.map +1 -0
- package/dist/components/Stepper.d.ts +52 -0
- package/dist/components/Stepper.d.ts.map +1 -0
- package/dist/components/Stepper.js +53 -0
- package/dist/components/Stepper.js.map +1 -0
- package/dist/components/Switch.d.ts +10 -0
- package/dist/components/Switch.d.ts.map +1 -0
- package/dist/components/Switch.js +7 -0
- package/dist/components/Switch.js.map +1 -0
- package/dist/components/Table.d.ts +106 -0
- package/dist/components/Table.d.ts.map +1 -0
- package/dist/components/Table.js +86 -0
- package/dist/components/Table.js.map +1 -0
- package/dist/components/Tabs/TabController.d.ts +11 -0
- package/dist/components/Tabs/TabController.d.ts.map +1 -0
- package/dist/components/Tabs/TabController.js +39 -0
- package/dist/components/Tabs/TabController.js.map +1 -0
- package/dist/components/Tabs/index.d.ts +5 -0
- package/dist/components/Tabs/index.d.ts.map +1 -0
- package/dist/components/Tabs/index.js +37 -0
- package/dist/components/Tabs/index.js.map +1 -0
- package/dist/components/Tabs/types.d.ts +46 -0
- package/dist/components/Tabs/types.d.ts.map +1 -0
- package/dist/components/Tabs/types.js +2 -0
- package/dist/components/Tabs/types.js.map +1 -0
- package/dist/components/Tabs/utils.d.ts +3 -0
- package/dist/components/Tabs/utils.d.ts.map +1 -0
- package/dist/components/Tabs/utils.js +5 -0
- package/dist/components/Tabs/utils.js.map +1 -0
- package/dist/components/Text.d.ts +27 -0
- package/dist/components/Text.d.ts.map +1 -0
- package/dist/components/Text.js +45 -0
- package/dist/components/Text.js.map +1 -0
- package/dist/components/Textarea.d.ts +8 -0
- package/dist/components/Textarea.d.ts.map +1 -0
- package/dist/components/Textarea.js +4 -0
- package/dist/components/Textarea.js.map +1 -0
- package/dist/components/Tooltip.d.ts +25 -0
- package/dist/components/Tooltip.d.ts.map +1 -0
- package/dist/components/Tooltip.js +18 -0
- package/dist/components/Tooltip.js.map +1 -0
- package/dist/components/layout.d.ts +46 -0
- package/dist/components/layout.d.ts.map +1 -0
- package/dist/components/layout.js +18 -0
- package/dist/components/layout.js.map +1 -0
- package/dist/context/CitricContext.d.ts +3 -0
- package/dist/context/CitricContext.d.ts.map +1 -0
- package/dist/context/CitricContext.js +3 -0
- package/dist/context/CitricContext.js.map +1 -0
- package/dist/context/CitricProvider.d.ts +9 -0
- package/dist/context/CitricProvider.d.ts.map +1 -0
- package/dist/context/CitricProvider.js +8 -0
- package/dist/context/CitricProvider.js.map +1 -0
- package/dist/context/hooks.d.ts +2 -0
- package/dist/context/hooks.d.ts.map +1 -0
- package/dist/context/hooks.js +6 -0
- package/dist/context/hooks.js.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/dist/overlay.d.ts +83 -0
- package/dist/overlay.d.ts.map +1 -0
- package/dist/overlay.js +199 -0
- package/dist/overlay.js.map +1 -0
- package/dist/theme.css +419 -0
- package/dist/types.d.ts +175 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/ValueController.d.ts +10 -0
- package/dist/utils/ValueController.d.ts.map +1 -0
- package/dist/utils/ValueController.js +32 -0
- package/dist/utils/ValueController.js.map +1 -0
- package/dist/utils/acessibility.d.ts +52 -0
- package/dist/utils/acessibility.d.ts.map +1 -0
- package/dist/utils/acessibility.js +80 -0
- package/dist/utils/acessibility.js.map +1 -0
- package/dist/utils/css.d.ts +12 -0
- package/dist/utils/css.d.ts.map +1 -0
- package/dist/utils/css.js +72 -0
- package/dist/utils/css.js.map +1 -0
- package/dist/utils/options.d.ts +3 -0
- package/dist/utils/options.d.ts.map +1 -0
- package/dist/utils/options.js +7 -0
- package/dist/utils/options.js.map +1 -0
- package/package.json +51 -0
- package/scripts/build-css.ts +49 -0
- package/src/components/Accordion.tsx +74 -0
- package/src/components/Alert.tsx +16 -0
- package/src/components/AsyncContent.tsx +54 -0
- package/src/components/Avatar.tsx +34 -0
- package/src/components/AvatarGroup.tsx +40 -0
- package/src/components/Badge.tsx +28 -0
- package/src/components/Blockquote.tsx +9 -0
- package/src/components/Breadcrumb.tsx +24 -0
- package/src/components/Button.tsx +88 -0
- package/src/components/Card.tsx +32 -0
- package/src/components/Checkbox.tsx +36 -0
- package/src/components/CheckboxGroup.tsx +93 -0
- package/src/components/Circle.tsx +26 -0
- package/src/components/CitricComponent.ts +34 -0
- package/src/components/Divider.tsx +22 -0
- package/src/components/ErrorBoundary.tsx +62 -0
- package/src/components/ErrorMessage.tsx +11 -0
- package/src/components/FallbackBoundary.tsx +29 -0
- package/src/components/Favorite.tsx +37 -0
- package/src/components/FieldGroup.tsx +22 -0
- package/src/components/Form.tsx +17 -0
- package/src/components/FormGroup.tsx +45 -0
- package/src/components/IconBox.tsx +78 -0
- package/src/components/Input.tsx +32 -0
- package/src/components/Link.tsx +40 -0
- package/src/components/LoadingPanel.tsx +8 -0
- package/src/components/MenuOverlay/Menu.tsx +157 -0
- package/src/components/MenuOverlay/context.ts +20 -0
- package/src/components/MenuOverlay/index.tsx +35 -0
- package/src/components/MenuOverlay/keyboard.ts +60 -0
- package/src/components/MenuOverlay/types.ts +178 -0
- package/src/components/Overlay/context.ts +10 -0
- package/src/components/Overlay/index.tsx +137 -0
- package/src/components/Overlay/types.ts +71 -0
- package/src/components/Pagination.tsx +90 -0
- package/src/components/ProgressBar.tsx +25 -0
- package/src/components/ProgressCircular.tsx +29 -0
- package/src/components/RadioGroup.tsx +87 -0
- package/src/components/Rating.tsx +25 -0
- package/src/components/Select/RichSelect.tsx +214 -0
- package/src/components/Select/SimpleSelect.tsx +66 -0
- package/src/components/Select/index.tsx +8 -0
- package/src/components/Select/types.ts +121 -0
- package/src/components/SelectBox.tsx +134 -0
- package/src/components/Skeleton.tsx +41 -0
- package/src/components/Slider.tsx +77 -0
- package/src/components/SmartTable.tsx +148 -0
- package/src/components/Stepper.tsx +142 -0
- package/src/components/Switch.tsx +29 -0
- package/src/components/Table.tsx +219 -0
- package/src/components/Tabs/TabController.ts +40 -0
- package/src/components/Tabs/index.tsx +64 -0
- package/src/components/Tabs/types.ts +48 -0
- package/src/components/Tabs/utils.ts +6 -0
- package/src/components/Text.ts +75 -0
- package/src/components/Textarea.tsx +12 -0
- package/src/components/Tooltip.tsx +53 -0
- package/src/components/layout.tsx +53 -0
- package/src/context/CitricContext.tsx +4 -0
- package/src/context/CitricProvider.tsx +14 -0
- package/src/context/hooks.ts +6 -0
- package/src/index.ts +47 -0
- package/src/overlay.ts +276 -0
- package/src/types.ts +226 -0
- package/src/utils/ValueController.ts +28 -0
- package/src/utils/acessibility.ts +92 -0
- package/src/utils/css.ts +106 -0
- package/src/utils/options.ts +7 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
|
+
import { useState } from 'react'
|
|
3
|
+
import { useCitricController } from '../context/hooks'
|
|
4
|
+
import { HTMLExtension, WithColorScheme } from '../types'
|
|
5
|
+
import { CitricComponent } from './CitricComponent'
|
|
6
|
+
import { ProgressCircular } from './ProgressCircular'
|
|
7
|
+
|
|
8
|
+
export interface BaseButtonProps extends WithColorScheme {
|
|
9
|
+
/**
|
|
10
|
+
* - contained for buttons with background;
|
|
11
|
+
* - outlined for transparent buttons with borders;
|
|
12
|
+
* - text for buttons with no borders or background.
|
|
13
|
+
* @default 'contained'
|
|
14
|
+
*/
|
|
15
|
+
appearance?: 'outlined' | 'text' | 'contained',
|
|
16
|
+
/**
|
|
17
|
+
* Size of the button.
|
|
18
|
+
* @default 'md'
|
|
19
|
+
*/
|
|
20
|
+
size?: 'sm' | 'md' | 'lg',
|
|
21
|
+
/**
|
|
22
|
+
* Animated text to show on when the button is clicked.
|
|
23
|
+
*/
|
|
24
|
+
feedback?: string,
|
|
25
|
+
/**
|
|
26
|
+
* Whether or not to show a loading feedback instead of the button's content.
|
|
27
|
+
*
|
|
28
|
+
* Buttons in the loading state are also disabled.
|
|
29
|
+
*/
|
|
30
|
+
loading?: boolean,
|
|
31
|
+
/**
|
|
32
|
+
* Function to run when the button is clicked. If this function returns a promise, the button will automatically show a loading feedback
|
|
33
|
+
* until the promise completes.
|
|
34
|
+
*/
|
|
35
|
+
onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => any,
|
|
36
|
+
/**
|
|
37
|
+
* Whether or not a click in this button should generate analytics data.
|
|
38
|
+
*
|
|
39
|
+
* This only takes effect if there's a CitricController in React's context. The value of `analytics` is passed to the function
|
|
40
|
+
* `onClickButton` of the controller.
|
|
41
|
+
*
|
|
42
|
+
* @default false
|
|
43
|
+
*/
|
|
44
|
+
analytics?: boolean,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type ButtonProps = HTMLExtension<'button', BaseButtonProps, 'onClick'>
|
|
48
|
+
|
|
49
|
+
export const Button = (
|
|
50
|
+
{ appearance, size, feedback, loading, disabled, onClick, className, children, type = 'button', analytics, ...props }: ButtonProps,
|
|
51
|
+
) => {
|
|
52
|
+
const citric = useCitricController()
|
|
53
|
+
const [waiting, setWaiting] = useState(false)
|
|
54
|
+
const busy = loading || waiting
|
|
55
|
+
|
|
56
|
+
async function handleClick(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
|
|
57
|
+
const result = onClick?.(e)
|
|
58
|
+
citric?.onClickButton?.(e, analytics ?? false)
|
|
59
|
+
if (result instanceof Promise) {
|
|
60
|
+
setWaiting(true)
|
|
61
|
+
try {
|
|
62
|
+
await result
|
|
63
|
+
} catch { /* empty */ }
|
|
64
|
+
setWaiting(false)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<CitricComponent
|
|
70
|
+
tag="button"
|
|
71
|
+
component="button"
|
|
72
|
+
className={listToClass([size, appearance, className])}
|
|
73
|
+
data-feedback={feedback}
|
|
74
|
+
onClick={handleClick}
|
|
75
|
+
aria-busy={busy}
|
|
76
|
+
disabled={disabled || busy}
|
|
77
|
+
type={type}
|
|
78
|
+
{...props}
|
|
79
|
+
>
|
|
80
|
+
{busy ? <div>{children}</div> : children}
|
|
81
|
+
{busy && (
|
|
82
|
+
<div className="loader">
|
|
83
|
+
<ProgressCircular size="sm" />
|
|
84
|
+
</div>
|
|
85
|
+
)}
|
|
86
|
+
</CitricComponent>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
|
+
import { HTMLExtension, WithColorScheme } from '../types'
|
|
3
|
+
import { CitricComponent } from './CitricComponent'
|
|
4
|
+
|
|
5
|
+
export interface BaseCardProps extends WithColorScheme {
|
|
6
|
+
onClick?: (event: React.MouseEvent<HTMLDivElement>) => void,
|
|
7
|
+
/**
|
|
8
|
+
* The size of the card.
|
|
9
|
+
*
|
|
10
|
+
* @default 'md'
|
|
11
|
+
*/
|
|
12
|
+
size?: 'xxs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl',
|
|
13
|
+
/**
|
|
14
|
+
* The background color level. The main color will depend on the "colorScheme", which is "light" by default.
|
|
15
|
+
*
|
|
16
|
+
* @default 300
|
|
17
|
+
*/
|
|
18
|
+
bgLevel?: 300 | 400 | 500 | 600,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type CardProps = HTMLExtension<'div', BaseCardProps>
|
|
22
|
+
|
|
23
|
+
export const Card = ({ onClick, size, bgLevel, className, children, ...props }: CardProps) => (
|
|
24
|
+
<CitricComponent
|
|
25
|
+
tag="div"
|
|
26
|
+
component="card"
|
|
27
|
+
className={listToClass([onClick && 'clickable', size, bgLevel && `bg-${bgLevel}`, className])}
|
|
28
|
+
{...props}
|
|
29
|
+
>
|
|
30
|
+
{children}
|
|
31
|
+
</CitricComponent>
|
|
32
|
+
)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { HTMLExtension, WithColorScheme } from '../types'
|
|
2
|
+
import { CitricComponent } from './CitricComponent'
|
|
3
|
+
|
|
4
|
+
export interface BaseCheckboxProps extends WithColorScheme {
|
|
5
|
+
/**
|
|
6
|
+
* "checkbox" for common check boxes, "switch" for toggles.
|
|
7
|
+
* @default 'checkbox'
|
|
8
|
+
*/
|
|
9
|
+
appearance?: 'checkbox' | 'switch',
|
|
10
|
+
value?: boolean,
|
|
11
|
+
onChange?: (value: boolean) => void,
|
|
12
|
+
children?: React.ReactNode,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type CheckboxProps = HTMLExtension<'input', BaseCheckboxProps, 'type' | 'onChange' | 'value'>
|
|
16
|
+
|
|
17
|
+
export const Checkbox = (
|
|
18
|
+
{ appearance = 'checkbox', value, onChange, colorScheme, children, className, style, ...props }: CheckboxProps,
|
|
19
|
+
) => {
|
|
20
|
+
const handleChange = onChange ? () => onChange(!value) : undefined
|
|
21
|
+
return children ? (
|
|
22
|
+
<CitricComponent tag="label" component={`${appearance}-row`} colorScheme={colorScheme} style={style} className={className}>
|
|
23
|
+
<input type="checkbox" checked={value} onChange={handleChange} {...props} />
|
|
24
|
+
{children}
|
|
25
|
+
</CitricComponent>
|
|
26
|
+
) : <CitricComponent
|
|
27
|
+
tag="input"
|
|
28
|
+
type="checkbox"
|
|
29
|
+
component={appearance}
|
|
30
|
+
checked={value}
|
|
31
|
+
onChange={handleChange}
|
|
32
|
+
className={className}
|
|
33
|
+
style={style}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { isNil } from 'lodash'
|
|
2
|
+
import { useMemo } from 'react'
|
|
3
|
+
import { HTMLExtension, WithColorScheme } from '../types'
|
|
4
|
+
import { defaultRenderKey, defaultRenderLabel } from '../utils/options'
|
|
5
|
+
import { CitricComponent } from './CitricComponent'
|
|
6
|
+
import { Column } from './layout'
|
|
7
|
+
|
|
8
|
+
export interface BaseCheckboxGroupProps<T = any> extends WithColorScheme {
|
|
9
|
+
/**
|
|
10
|
+
* "checkbox" for common check boxes, "switch" for toggles.
|
|
11
|
+
* @default 'checkbox'
|
|
12
|
+
*/
|
|
13
|
+
appearance?: 'checkbox' | 'switch',
|
|
14
|
+
/**
|
|
15
|
+
* The field name.
|
|
16
|
+
*/
|
|
17
|
+
name?: string,
|
|
18
|
+
/**
|
|
19
|
+
* All items corresponding to the selected checkboxes.
|
|
20
|
+
*/
|
|
21
|
+
value: T[],
|
|
22
|
+
/**
|
|
23
|
+
* All the items (checkboxes) to render.
|
|
24
|
+
*/
|
|
25
|
+
options: T[],
|
|
26
|
+
/**
|
|
27
|
+
* Called whenever the selected checkboxes changes.
|
|
28
|
+
* @param value the currently selected items.
|
|
29
|
+
*/
|
|
30
|
+
onChange: (value: T[]) => void,
|
|
31
|
+
/**
|
|
32
|
+
* A function to render the item label.
|
|
33
|
+
* @example
|
|
34
|
+
* `(option) => option.name`
|
|
35
|
+
* @default "the item's toString() result."
|
|
36
|
+
* @param option the item to render.
|
|
37
|
+
* @returns a React Node to render.
|
|
38
|
+
*/
|
|
39
|
+
renderLabel?: (option: T) => React.ReactNode,
|
|
40
|
+
/**
|
|
41
|
+
* A function to render the item value, a unique identifier for the option.
|
|
42
|
+
* @example
|
|
43
|
+
* `(option) => option.id`
|
|
44
|
+
* @default "if the item is a string or a number, the stringified item. Otherwise, undefined."
|
|
45
|
+
* @param option the item to compute a key for.
|
|
46
|
+
* @returns a string key.
|
|
47
|
+
*/
|
|
48
|
+
renderKey?: (option: T) => string | number | undefined,
|
|
49
|
+
/**
|
|
50
|
+
* If this function returns true for the item, this option is disabled.
|
|
51
|
+
* @default "nothing is disabled"
|
|
52
|
+
* @param option the item to calculate "disabled" for.
|
|
53
|
+
* @returns true if the item should be disabled, false otherwise.
|
|
54
|
+
*/
|
|
55
|
+
isDisabled?: (option: T) => boolean,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type CheckboxGroupProps<T> = HTMLExtension<'div', BaseCheckboxGroupProps<T>, 'onChange'>
|
|
59
|
+
|
|
60
|
+
export function CheckboxGroup<T>({
|
|
61
|
+
appearance = 'checkbox',
|
|
62
|
+
name,
|
|
63
|
+
value = [],
|
|
64
|
+
options,
|
|
65
|
+
onChange,
|
|
66
|
+
renderLabel = defaultRenderLabel,
|
|
67
|
+
renderKey = defaultRenderKey,
|
|
68
|
+
isDisabled,
|
|
69
|
+
colorScheme,
|
|
70
|
+
style,
|
|
71
|
+
...props
|
|
72
|
+
}: CheckboxGroupProps<T>) {
|
|
73
|
+
const items = useMemo(() => {
|
|
74
|
+
const valueKeys = value.map(renderKey)
|
|
75
|
+
return options.map((o) => {
|
|
76
|
+
const key = renderKey(o)
|
|
77
|
+
return (
|
|
78
|
+
<CitricComponent tag="label" component={`${appearance}-row`} key={key} colorScheme={colorScheme}>
|
|
79
|
+
<input
|
|
80
|
+
type="checkbox"
|
|
81
|
+
name={name}
|
|
82
|
+
value={key}
|
|
83
|
+
checked={value.includes(o) || (!isNil(key) && valueKeys.includes(key))}
|
|
84
|
+
onChange={(e) => onChange?.(e.target.checked ? [...(value ?? []), o] : (value?.filter(item => item != o) ?? []))}
|
|
85
|
+
disabled={isDisabled?.(o)}
|
|
86
|
+
/>
|
|
87
|
+
{renderLabel(o)}
|
|
88
|
+
</CitricComponent>
|
|
89
|
+
)
|
|
90
|
+
})
|
|
91
|
+
}, [options, value, name, colorScheme, appearance])
|
|
92
|
+
return <Column {...props} style={{ gap: '8px', ...style }}>{items}</Column>
|
|
93
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
|
+
import { HTMLExtension, WithColorPalette, WithColorScheme } from '../types'
|
|
3
|
+
import { CitricComponent } from './CitricComponent'
|
|
4
|
+
|
|
5
|
+
export interface BaseCircleProps extends WithColorPalette, WithColorScheme {
|
|
6
|
+
/**
|
|
7
|
+
* Whether or not to show borders.
|
|
8
|
+
*
|
|
9
|
+
* @default false
|
|
10
|
+
*/
|
|
11
|
+
showBorders?: boolean,
|
|
12
|
+
/**
|
|
13
|
+
* The size of the circle.
|
|
14
|
+
*
|
|
15
|
+
* @default 'xs'
|
|
16
|
+
*/
|
|
17
|
+
size?: 'xxs' | 'xs' | 'sm' | 'lg' | 'xl' | 'xxl',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type CircleProps = HTMLExtension<'div', BaseCircleProps>
|
|
21
|
+
|
|
22
|
+
export const Circle = ({ showBorders, className, size, children, ...props }: CircleProps) => (
|
|
23
|
+
<CitricComponent tag="div" component="circle" className={listToClass([className, showBorders && 'bordered', size])} {...props}>
|
|
24
|
+
{children}
|
|
25
|
+
</CitricComponent>
|
|
26
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createElement, forwardRef } from 'react'
|
|
2
|
+
import { WithColorPalette, WithColorScheme } from '../types'
|
|
3
|
+
|
|
4
|
+
export type CitricComponentName = 'alert' | 'avatar' | 'badge' | 'blockquote' | 'breadcrumb' | 'button' | 'card' | 'checkbox' |
|
|
5
|
+
'checkbox-row' | 'divider' | 'field-group' | 'form-group' | 'form' | 'icon-box' | 'input' | 'link' | 'pagination' | 'progress-bar' |
|
|
6
|
+
'progress-circular' | 'radio' | 'radio-row' | 'rating' | 'select' | 'select-box' | 'skeleton' | 'slider' | 'switch' | 'switch-row' |
|
|
7
|
+
'table' | 'tabs' | 'accordion' | 'favorite' | 'textarea' | 'avatar-group' | 'labeled-slider' | 'rich-select' | 'tooltip' | 'menu' |
|
|
8
|
+
'circle'
|
|
9
|
+
|
|
10
|
+
interface CustomProps<T extends keyof JSX.IntrinsicElements> extends WithColorScheme, WithColorPalette {
|
|
11
|
+
tag: T,
|
|
12
|
+
component: CitricComponentName,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type Props<T extends keyof JSX.IntrinsicElements> = CustomProps<T> & JSX.IntrinsicElements[T]
|
|
16
|
+
|
|
17
|
+
interface CitricComponentType {
|
|
18
|
+
<T extends keyof JSX.IntrinsicElements>(props: Props<T>): React.ReactNode,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function asCitricProps({ colorScheme, colorPalette, component, ...props }: Omit<Props<any>, 'tag'>) {
|
|
22
|
+
const citricProps: Record<string, any> = { ...props, 'data-citric': component }
|
|
23
|
+
if (colorScheme) citricProps['data-color-scheme'] = colorScheme
|
|
24
|
+
if (colorPalette) citricProps['data-color-palette'] = colorPalette
|
|
25
|
+
return citricProps
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// eslint-disable-next-line react/display-name
|
|
29
|
+
export const CitricComponent: CitricComponentType = forwardRef<Props<any>, any>(
|
|
30
|
+
(props, ref) => {
|
|
31
|
+
const { tag, ...citricProps } = asCitricProps(props)
|
|
32
|
+
return createElement(tag, { ...citricProps, ref })
|
|
33
|
+
},
|
|
34
|
+
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
|
+
import { HTMLExtension, WithColorScheme } from '../types'
|
|
3
|
+
import { CitricComponent } from './CitricComponent'
|
|
4
|
+
|
|
5
|
+
export interface BaseDividerProps extends WithColorScheme {
|
|
6
|
+
/**
|
|
7
|
+
* @default 'horizontal'
|
|
8
|
+
*/
|
|
9
|
+
direction?: 'horizontal' | 'vertical',
|
|
10
|
+
/**
|
|
11
|
+
* @default 'sm'
|
|
12
|
+
*/
|
|
13
|
+
size?: 'sm' | 'md' | 'lg',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type DividerProps = HTMLExtension<'hr', BaseDividerProps>
|
|
17
|
+
|
|
18
|
+
export const Divider = ({ size, direction, className, children, ...props }: DividerProps) => (
|
|
19
|
+
<CitricComponent tag="hr" component="divider" className={listToClass([className, size, direction])} {...props}>
|
|
20
|
+
{children}
|
|
21
|
+
</CitricComponent>
|
|
22
|
+
)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Component } from 'react'
|
|
2
|
+
import { CitricContext } from '../context/CitricContext'
|
|
3
|
+
import { ErrorMessage } from './ErrorMessage'
|
|
4
|
+
|
|
5
|
+
interface State {
|
|
6
|
+
error?: any,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
children: React.ReactNode,
|
|
11
|
+
/**
|
|
12
|
+
* Sets a default error string when a string is provided.
|
|
13
|
+
*
|
|
14
|
+
* Replaces the error component, if a function is provided.
|
|
15
|
+
*/
|
|
16
|
+
message?: React.ReactNode | ((error: any) => React.ReactNode),
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* An Error Boundary that renders an error feedback instead of its content if any of its children throws.
|
|
21
|
+
*/
|
|
22
|
+
export class ErrorBoundary extends Component<Props, State> {
|
|
23
|
+
static contextType = CitricContext
|
|
24
|
+
declare context: React.ContextType<typeof CitricContext>
|
|
25
|
+
|
|
26
|
+
constructor(props: Props) {
|
|
27
|
+
super(props)
|
|
28
|
+
this.state = {}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static getDerivedStateFromError(error: any) {
|
|
32
|
+
return { error }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
componentDidCatch(error: any, errorInfo: any) {
|
|
36
|
+
this.context?.onError?.(error)
|
|
37
|
+
// eslint-disable-next-line no-console
|
|
38
|
+
console.error(error, errorInfo)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
componentDidUpdate(prevProps: Readonly<Props>) {
|
|
42
|
+
if (this.state.error && this.props.children !== prevProps.children) {
|
|
43
|
+
this.setState({})
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private renderCustomErrorUI() {
|
|
48
|
+
return this.context?.renderError?.(this.state.error)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private renderErrorUI() {
|
|
52
|
+
if (typeof this.props.message === 'function') return this.props.message(this.state.error)
|
|
53
|
+
return (this.props.message || !this.context?.renderError)
|
|
54
|
+
? <ErrorMessage error={this.props.message || this.state.error} />
|
|
55
|
+
: this.renderCustomErrorUI()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
render() {
|
|
60
|
+
return this.state.error ? this.renderErrorUI() : this.props.children
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Icon } from '@stack-spot/citric-icons'
|
|
2
|
+
import { theme } from '@stack-spot/portal-theme'
|
|
3
|
+
import { Row } from './layout'
|
|
4
|
+
import { Text } from './Text'
|
|
5
|
+
|
|
6
|
+
export const ErrorMessage = ({ error }: { error: any }) => (
|
|
7
|
+
<Row gap="18px" p="20px" justifyContent="center">
|
|
8
|
+
<Icon icon="TimesCircle" size="xl" style={{ color: theme.color.danger[500] }} />
|
|
9
|
+
<Text style={{ margin: '80px' }}>{error.message || `${error}`}</Text>
|
|
10
|
+
</Row>
|
|
11
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Suspense } from 'react'
|
|
2
|
+
import { ErrorBoundary } from './ErrorBoundary'
|
|
3
|
+
import { Center } from './layout'
|
|
4
|
+
import { ProgressCircular } from './ProgressCircular'
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
/**
|
|
8
|
+
* Replaces the error message with this text.
|
|
9
|
+
*/
|
|
10
|
+
message?: string,
|
|
11
|
+
children: React.ReactNode,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const loading = (
|
|
15
|
+
<Center flex={1} p="80px" data-test-hint="loading">
|
|
16
|
+
<ProgressCircular />
|
|
17
|
+
</Center>
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Fallbacks for errors and loadings (suspense).
|
|
22
|
+
*/
|
|
23
|
+
export const FallbackBoundary = ({ children, message }: Props) => (
|
|
24
|
+
<ErrorBoundary message={message}>
|
|
25
|
+
<Suspense fallback={loading}>
|
|
26
|
+
{children}
|
|
27
|
+
</Suspense>
|
|
28
|
+
</ErrorBoundary>
|
|
29
|
+
)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
|
+
import { HTMLExtension } from '../types'
|
|
3
|
+
import { CitricComponent } from './CitricComponent'
|
|
4
|
+
|
|
5
|
+
type FavoriteAppearance = 'square' | 'circle' | 'text'
|
|
6
|
+
|
|
7
|
+
export interface BaseFavoriteProps {
|
|
8
|
+
value: boolean | undefined,
|
|
9
|
+
/**
|
|
10
|
+
* Size of the button.
|
|
11
|
+
*
|
|
12
|
+
* @default 'md'
|
|
13
|
+
*/
|
|
14
|
+
size?: 'sm' | 'md' | 'lg' | 'fit-parent',
|
|
15
|
+
/**
|
|
16
|
+
* Button appearance.
|
|
17
|
+
*
|
|
18
|
+
* @default 'text'
|
|
19
|
+
*/
|
|
20
|
+
appearance?: FavoriteAppearance,
|
|
21
|
+
onChange: (value: boolean) => void,
|
|
22
|
+
isLoading?: boolean,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type FavoriteProps = HTMLExtension<'input', BaseFavoriteProps, 'type' | 'checked' | 'onChange' | 'size' | 'value'>
|
|
26
|
+
|
|
27
|
+
export const Favorite = ({ value, size, appearance, onChange, isLoading, className, ...props }: FavoriteProps) =>
|
|
28
|
+
<CitricComponent
|
|
29
|
+
tag="input"
|
|
30
|
+
type="checkbox"
|
|
31
|
+
component="favorite"
|
|
32
|
+
className={listToClass([appearance, size, className])}
|
|
33
|
+
checked={!!value}
|
|
34
|
+
onChange={() => onChange(!value)}
|
|
35
|
+
aria-busy={isLoading}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
|
+
import { HTMLExtension } from '../types'
|
|
3
|
+
import { CitricComponent } from './CitricComponent'
|
|
4
|
+
|
|
5
|
+
export interface BaseFieldGroupProps {
|
|
6
|
+
/**
|
|
7
|
+
* When this is true, the component will automatically mark the addons.
|
|
8
|
+
*
|
|
9
|
+
* When this is false, you must add the class "addon" manually to each of the addons.
|
|
10
|
+
*
|
|
11
|
+
* @default true
|
|
12
|
+
*/
|
|
13
|
+
auto?: boolean,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type FieldGroupProps = HTMLExtension<'div', BaseFieldGroupProps>
|
|
17
|
+
|
|
18
|
+
export const FieldGroup = ({ auto = true, className, children, ...props }: FieldGroupProps) => (
|
|
19
|
+
<CitricComponent tag="div" component="field-group" className={listToClass([className, auto && 'auto'])} {...props}>
|
|
20
|
+
{children}
|
|
21
|
+
</CitricComponent>
|
|
22
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
|
+
import { CitricComponent } from './CitricComponent'
|
|
3
|
+
|
|
4
|
+
export type FormProps = JSX.IntrinsicElements['form']
|
|
5
|
+
export type ButtonGroupProps = JSX.IntrinsicElements['div']
|
|
6
|
+
|
|
7
|
+
export const Form = ({ children, ...props }: FormProps) => (
|
|
8
|
+
<CitricComponent tag="form" component="form" {...props}>
|
|
9
|
+
{children}
|
|
10
|
+
</CitricComponent>
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
export const ButtonGroup = ({ className, children, ...props }: ButtonGroupProps) => (
|
|
14
|
+
<div className={listToClass([className, 'button-group'])} {...props}>
|
|
15
|
+
{children}
|
|
16
|
+
</div>
|
|
17
|
+
)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
|
+
import { HTMLExtension } from '../types'
|
|
3
|
+
import { CitricComponent } from './CitricComponent'
|
|
4
|
+
import { IconButton } from './IconBox'
|
|
5
|
+
import { Text } from './Text'
|
|
6
|
+
import { Tooltip } from './Tooltip'
|
|
7
|
+
|
|
8
|
+
export interface BaseFormGroupProps {
|
|
9
|
+
/**
|
|
10
|
+
* The error string to show, if any. The empty string is equivalent to undefined, i.e. no errors.
|
|
11
|
+
*/
|
|
12
|
+
error?: string,
|
|
13
|
+
/**
|
|
14
|
+
* The input's label.
|
|
15
|
+
*/
|
|
16
|
+
label?: string,
|
|
17
|
+
/**
|
|
18
|
+
* A help string, to show as a tooltip.
|
|
19
|
+
*/
|
|
20
|
+
help?: string,
|
|
21
|
+
/**
|
|
22
|
+
* A warning string, to show right below the field, at all times.
|
|
23
|
+
*/
|
|
24
|
+
warning?: string,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type FormGroupProps = HTMLExtension<'div', BaseFormGroupProps>
|
|
28
|
+
|
|
29
|
+
export const FormGroup = ({ error, help, label, warning, className, children, ...props }: FormGroupProps) => (
|
|
30
|
+
<CitricComponent tag="div" component="form-group" className={listToClass([className, error && 'error'])} {...props}>
|
|
31
|
+
<label>
|
|
32
|
+
{help ? (
|
|
33
|
+
<div className="row">
|
|
34
|
+
<Text>{label}</Text>
|
|
35
|
+
<Tooltip content={help}>
|
|
36
|
+
<IconButton icon="InfoCircle" size="xs" />
|
|
37
|
+
</Tooltip>
|
|
38
|
+
</div>
|
|
39
|
+
): label}
|
|
40
|
+
{children}
|
|
41
|
+
<div className="feedback">{error}</div>
|
|
42
|
+
{warning && <div className="warning">{warning}</div>}
|
|
43
|
+
</label>
|
|
44
|
+
</CitricComponent>
|
|
45
|
+
)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { IconGroup, IconOwnProps } from '@stack-spot/citric-icons'
|
|
2
|
+
import { listToClass } from '@stack-spot/portal-theme'
|
|
3
|
+
import { useCallback } from 'react'
|
|
4
|
+
import { useCitricController } from '../context/hooks'
|
|
5
|
+
import { HTMLExtension, WithColorPalette, WithColorScheme } from '../types'
|
|
6
|
+
import { CitricComponent } from './CitricComponent'
|
|
7
|
+
|
|
8
|
+
type IconBoxTag = 'a' | 'button' | 'i' | 'span' | 'div'
|
|
9
|
+
|
|
10
|
+
export interface BaseIconBoxProps<T extends IconBoxTag, G extends IconGroup> extends IconOwnProps<G>, WithColorScheme, WithColorPalette {
|
|
11
|
+
/**
|
|
12
|
+
* The HTML element to render.
|
|
13
|
+
*
|
|
14
|
+
* @default 'i'
|
|
15
|
+
*/
|
|
16
|
+
tag?: T,
|
|
17
|
+
/**
|
|
18
|
+
* The box appearance.
|
|
19
|
+
*
|
|
20
|
+
* @default 'circle'
|
|
21
|
+
*/
|
|
22
|
+
appearance?: 'circle' | 'square',
|
|
23
|
+
/**
|
|
24
|
+
* Size of the box.
|
|
25
|
+
*
|
|
26
|
+
* @default 'sm'
|
|
27
|
+
*/
|
|
28
|
+
size?: 'xs' | 'sm' | 'md' | 'lg',
|
|
29
|
+
/**
|
|
30
|
+
* Only valid if `tag` is "button" or "a".
|
|
31
|
+
*
|
|
32
|
+
* Whether or not a click in this button/link should generate analytics data.
|
|
33
|
+
*
|
|
34
|
+
* This only takes effect if there's a CitricController in React's context. The value of `analytics` is passed to the function
|
|
35
|
+
* `onClickButton/onClickLink` of the controller.
|
|
36
|
+
*
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
analytics?: boolean,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type IconBoxProps<T extends IconBoxTag, G extends IconGroup> = HTMLExtension<T, BaseIconBoxProps<T, G>>
|
|
43
|
+
|
|
44
|
+
export function IconBox<T extends IconBoxTag = 'i', G extends IconGroup = 'outline'>(
|
|
45
|
+
{ group, icon, tag, colorScheme, colorPalette, appearance, size, className, analytics, onClick, ...props }: IconBoxProps<T, G>,
|
|
46
|
+
) {
|
|
47
|
+
const citric = useCitricController()
|
|
48
|
+
|
|
49
|
+
const handleClick = useCallback((e: React.MouseEvent<any>) => {
|
|
50
|
+
onClick?.(e)
|
|
51
|
+
if (tag === 'button') citric?.onClickButton?.(e, analytics ?? false)
|
|
52
|
+
else if (tag === 'a') citric?.onClickLink?.(e, analytics ?? false)
|
|
53
|
+
}, [tag])
|
|
54
|
+
|
|
55
|
+
return <CitricComponent
|
|
56
|
+
tag={(tag || 'i') as any}
|
|
57
|
+
component="icon-box"
|
|
58
|
+
className={listToClass(['citric-icon', group || 'outline', icon, appearance, size, className])}
|
|
59
|
+
colorScheme={colorScheme}
|
|
60
|
+
colorPalette={colorPalette}
|
|
61
|
+
onClick={['button', 'a'].includes(tag ?? '') ? handleClick : onClick}
|
|
62
|
+
{...props}
|
|
63
|
+
/>
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Shortcut to <IconBox tag="button" />
|
|
68
|
+
*/
|
|
69
|
+
export function IconButton<G extends IconGroup = 'outline'>(props: Omit<IconBoxProps<'button', G>, 'tag'>) {
|
|
70
|
+
return IconBox({ ...props, tag: 'button', type: props.type || 'button' })
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Shortcut to <IconBox tag="a" />
|
|
75
|
+
*/
|
|
76
|
+
export function IconLink<G extends IconGroup = 'outline'>(props: Omit<IconBoxProps<'a', G>, 'tag'>) {
|
|
77
|
+
return IconBox({ ...props, tag: 'a' })
|
|
78
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { HTMLExtension, WithColorScheme } from '../types'
|
|
2
|
+
import { CitricComponent } from './CitricComponent'
|
|
3
|
+
|
|
4
|
+
export type SupportedInputType = 'color' | 'date' | 'datetime-local' | 'email' | 'month' | 'number' | 'password' | 'search' | 'tel' |
|
|
5
|
+
'text' | 'time' | 'url' | 'week'
|
|
6
|
+
|
|
7
|
+
export interface BaseInputProps<T extends SupportedInputType> extends WithColorScheme {
|
|
8
|
+
/**
|
|
9
|
+
* @default 'text'
|
|
10
|
+
*/
|
|
11
|
+
type?: T,
|
|
12
|
+
value?: T extends 'number' ? number : string,
|
|
13
|
+
onChange?: (value: T extends 'number' ? (number | undefined) : string) => void,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type InputProps<T extends SupportedInputType = 'text'> =
|
|
17
|
+
HTMLExtension<'input', BaseInputProps<T> & { type?: T }, 'type' | 'value' | 'onChange'>
|
|
18
|
+
|
|
19
|
+
export function Input<T extends SupportedInputType = 'text'>({ type, value, onChange, ...props }: InputProps<T>) {
|
|
20
|
+
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
|
|
21
|
+
if (!onChange) return
|
|
22
|
+
const newValue = e.target.value
|
|
23
|
+
if (type === 'number') {
|
|
24
|
+
const parsed = newValue ? parseFloat(newValue) : undefined
|
|
25
|
+
onChange(parsed as any)
|
|
26
|
+
} else {
|
|
27
|
+
onChange(newValue as any)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return <CitricComponent tag="input" component="input" type={type} value={value} onChange={handleChange} {...props} />
|
|
32
|
+
}
|