@stack-spot/citric-react 0.36.0 → 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.
- package/dist/citric.css +2844 -2832
- package/dist/components/Accordion.d.ts +1 -1
- package/dist/components/Accordion.js +1 -1
- package/dist/components/Alert.d.ts +1 -1
- package/dist/components/Alert.js +1 -1
- package/dist/components/AsyncContent.d.ts +1 -1
- package/dist/components/AsyncContent.js +1 -1
- package/dist/components/Avatar.d.ts +1 -1
- package/dist/components/Avatar.js +1 -1
- package/dist/components/AvatarGroup.d.ts +1 -1
- package/dist/components/AvatarGroup.js +1 -1
- package/dist/components/Badge.d.ts +1 -1
- package/dist/components/Badge.js +1 -1
- package/dist/components/Blockquote.d.ts +1 -1
- package/dist/components/Blockquote.js +1 -1
- package/dist/components/Breadcrumb.d.ts +1 -1
- package/dist/components/Breadcrumb.js +1 -1
- package/dist/components/Button.d.ts +1 -1
- package/dist/components/Button.js +1 -1
- package/dist/components/ButtonLink.d.ts +1 -1
- package/dist/components/ButtonLink.js +1 -1
- package/dist/components/Card.d.ts +1 -1
- package/dist/components/Card.js +1 -1
- package/dist/components/Checkbox.d.ts +1 -1
- package/dist/components/Checkbox.js +1 -1
- package/dist/components/CheckboxGroup.d.ts +1 -1
- package/dist/components/CheckboxGroup.js +1 -1
- package/dist/components/Circle.d.ts +1 -1
- package/dist/components/Circle.js +1 -1
- package/dist/components/Divider.d.ts +1 -1
- package/dist/components/Divider.js +1 -1
- package/dist/components/ErrorBoundary.d.ts +1 -1
- package/dist/components/ErrorBoundary.js +1 -1
- package/dist/components/ErrorMessage.d.ts +1 -1
- package/dist/components/ErrorMessage.js +1 -1
- package/dist/components/FallbackBoundary.d.ts +1 -1
- package/dist/components/FallbackBoundary.js +1 -1
- package/dist/components/Favorite.d.ts +1 -1
- package/dist/components/Favorite.js +1 -1
- package/dist/components/FieldGroup.d.ts +1 -1
- package/dist/components/FieldGroup.js +1 -1
- package/dist/components/Form.d.ts +2 -2
- package/dist/components/Form.js +1 -1
- package/dist/components/FormGroup.d.ts +1 -1
- package/dist/components/FormGroup.js +1 -1
- package/dist/components/Icon.d.ts +1 -1
- package/dist/components/Icon.js +1 -1
- package/dist/components/IconBox.d.ts +3 -3
- package/dist/components/IconBox.js +1 -1
- package/dist/components/ImageBox.d.ts +3 -3
- package/dist/components/ImageBox.js +1 -1
- package/dist/components/ImageWithFallback.d.ts +1 -1
- package/dist/components/ImageWithFallback.js +1 -1
- package/dist/components/Input.d.ts +1 -1
- package/dist/components/Input.js +1 -1
- package/dist/components/Link.d.ts +1 -1
- package/dist/components/Link.js +1 -1
- package/dist/components/LoadingPanel.d.ts +1 -1
- package/dist/components/LoadingPanel.js +1 -1
- package/dist/components/MenuOverlay/Menu.d.ts +1 -1
- package/dist/components/MenuOverlay/Menu.js +1 -1
- package/dist/components/MenuOverlay/index.d.ts +1 -1
- package/dist/components/MenuOverlay/index.js +1 -1
- package/dist/components/Overlay/index.d.ts +1 -1
- package/dist/components/Overlay/index.js +1 -1
- package/dist/components/Pagination.d.ts +1 -1
- package/dist/components/Pagination.js +1 -1
- package/dist/components/ProgressBar.d.ts +1 -1
- package/dist/components/ProgressBar.js +1 -1
- package/dist/components/ProgressCircular.d.ts +1 -1
- package/dist/components/ProgressCircular.js +1 -1
- package/dist/components/RadioGroup.d.ts +1 -1
- package/dist/components/RadioGroup.js +1 -1
- package/dist/components/Rating.d.ts +17 -3
- package/dist/components/Rating.d.ts.map +1 -1
- package/dist/components/Rating.js +11 -3
- package/dist/components/Rating.js.map +1 -1
- package/dist/components/Select/MultiSelect.d.ts +1 -1
- package/dist/components/Select/MultiSelect.js +1 -1
- package/dist/components/Select/RichSelect.d.ts +1 -1
- package/dist/components/Select/RichSelect.js +1 -1
- package/dist/components/Select/SimpleSelect.d.ts +1 -1
- package/dist/components/Select/SimpleSelect.js +1 -1
- package/dist/components/Select/index.d.ts +1 -1
- package/dist/components/Select/index.js +1 -1
- package/dist/components/SelectBox.d.ts +1 -1
- package/dist/components/SelectBox.js +1 -1
- package/dist/components/Skeleton.d.ts +1 -1
- package/dist/components/Skeleton.js +1 -1
- package/dist/components/Slider.d.ts +1 -1
- package/dist/components/Slider.js +1 -1
- package/dist/components/SmartTable.d.ts +1 -1
- package/dist/components/SmartTable.js +1 -1
- package/dist/components/Stepper.d.ts +1 -1
- package/dist/components/Stepper.js +1 -1
- package/dist/components/Table.d.ts +3 -3
- package/dist/components/Table.js +1 -1
- package/dist/components/Tabs/index.d.ts +1 -1
- package/dist/components/Tabs/index.js +1 -1
- package/dist/components/Textarea.d.ts +1 -1
- package/dist/components/Textarea.js +1 -1
- package/dist/components/Tooltip.d.ts +1 -1
- package/dist/components/Tooltip.js +1 -1
- package/dist/context/CitricProvider.d.ts +1 -1
- package/dist/context/CitricProvider.js +1 -1
- package/dist/overlay.js +1 -1
- package/dist/theme.css +415 -415
- package/package.json +3 -2
- package/scripts/build-css.ts +49 -49
- package/src/components/Accordion.tsx +130 -130
- package/src/components/Alert.tsx +24 -24
- package/src/components/AsyncContent.tsx +70 -70
- package/src/components/Avatar.tsx +45 -45
- package/src/components/AvatarGroup.tsx +49 -49
- package/src/components/Badge.tsx +47 -47
- package/src/components/Blockquote.tsx +18 -18
- package/src/components/Breadcrumb.tsx +33 -33
- package/src/components/Button.tsx +105 -105
- package/src/components/ButtonLink.tsx +45 -45
- package/src/components/Card.tsx +68 -68
- package/src/components/Checkbox.tsx +51 -51
- package/src/components/CheckboxGroup.tsx +152 -152
- package/src/components/Circle.tsx +43 -43
- package/src/components/CitricComponent.ts +47 -47
- package/src/components/Divider.tsx +24 -24
- package/src/components/ErrorBoundary.tsx +75 -75
- package/src/components/ErrorMessage.tsx +11 -11
- package/src/components/FallbackBoundary.tsx +40 -40
- package/src/components/Favorite.tsx +57 -57
- package/src/components/FieldGroup.tsx +46 -46
- package/src/components/Form.tsx +36 -36
- package/src/components/FormGroup.tsx +57 -57
- package/src/components/Icon.tsx +35 -35
- package/src/components/IconBox.tsx +134 -134
- package/src/components/ImageBox.tsx +125 -125
- package/src/components/ImageWithFallback.tsx +65 -65
- package/src/components/Input.tsx +49 -49
- package/src/components/Link.tsx +55 -55
- package/src/components/LoadingPanel.tsx +8 -8
- package/src/components/MenuOverlay/Menu.tsx +158 -158
- package/src/components/MenuOverlay/context.ts +20 -20
- package/src/components/MenuOverlay/index.tsx +55 -55
- package/src/components/MenuOverlay/keyboard.ts +60 -60
- package/src/components/MenuOverlay/types.ts +171 -171
- package/src/components/Overlay/context.ts +10 -10
- package/src/components/Overlay/index.tsx +164 -164
- package/src/components/Overlay/types.ts +70 -70
- package/src/components/Pagination.tsx +113 -113
- package/src/components/ProgressBar.tsx +45 -45
- package/src/components/ProgressCircular.tsx +45 -45
- package/src/components/RadioGroup.tsx +146 -146
- package/src/components/Rating.tsx +98 -35
- package/src/components/Select/MultiSelect.tsx +217 -217
- package/src/components/Select/RichSelect.tsx +128 -128
- package/src/components/Select/SimpleSelect.tsx +73 -73
- package/src/components/Select/hooks.ts +133 -133
- package/src/components/Select/index.tsx +35 -35
- package/src/components/Select/types.ts +134 -134
- package/src/components/SelectBox.tsx +167 -167
- package/src/components/Skeleton.tsx +53 -53
- package/src/components/Slider.tsx +89 -89
- package/src/components/SmartTable.tsx +227 -227
- package/src/components/Stepper.tsx +163 -163
- package/src/components/Table.tsx +234 -234
- package/src/components/Tabs/TabController.ts +54 -54
- package/src/components/Tabs/index.tsx +87 -87
- package/src/components/Tabs/types.ts +54 -54
- package/src/components/Tabs/utils.ts +6 -6
- package/src/components/Text.ts +111 -111
- package/src/components/Textarea.tsx +27 -27
- package/src/components/Tooltip.tsx +72 -72
- package/src/components/layout.tsx +101 -101
- package/src/context/CitricContext.tsx +4 -4
- package/src/context/CitricProvider.tsx +14 -14
- package/src/context/hooks.ts +6 -6
- package/src/index.ts +58 -58
- package/src/overlay.ts +341 -341
- package/src/types.ts +216 -216
- package/src/utils/ValueController.ts +28 -28
- package/src/utils/acessibility.ts +92 -92
- package/src/utils/checkbox.ts +121 -121
- package/src/utils/css.ts +119 -119
- package/src/utils/options.ts +9 -9
- package/src/utils/radio.ts +93 -93
- package/src/utils/react.ts +6 -6
- package/tsconfig.json +10 -10
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
|
-
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
3
|
-
import { createContext, Suspense, useContext, useEffect, useMemo, useState } from 'react'
|
|
4
|
-
import { withRef } from '../../utils/react'
|
|
5
|
-
import { CitricComponent } from '../CitricComponent'
|
|
6
|
-
import { ErrorBoundary } from '../ErrorBoundary'
|
|
7
|
-
import { ProgressCircular } from '../ProgressCircular'
|
|
8
|
-
import { Center } from '../layout'
|
|
9
|
-
import { TabController } from './TabController'
|
|
10
|
-
import { TabsProps } from './types'
|
|
11
|
-
import { findSelectedIndex } from './utils'
|
|
12
|
-
|
|
13
|
-
const ctx = createContext<TabController<any> | undefined>(undefined)
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Builds a Tab selector. The content of a tab is a React component and it can suspend, i.e. tabs can be asynchronously loaded.
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
*
|
|
20
|
-
* ```
|
|
21
|
-
* const tabs: Tab[] = useMemo(() => [
|
|
22
|
-
* { key: 'tab1', label: 'Tab 1', content: <><h1>First tab</h1><p>Content of the first tab</p></> },
|
|
23
|
-
* { key: 'tab2', label: 'Tab 2', content: <><h1>Second tab</h1><p>Content of the second tab</p></> },
|
|
24
|
-
* { key: 'tab3', label: 'Tab 3', content: <><h1>Third tab</h1><p>Content of the third tab</p></> },
|
|
25
|
-
* ], [])
|
|
26
|
-
*
|
|
27
|
-
* return <Tabs tabs={tabs} />
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export const Tabs = withRef(
|
|
31
|
-
function Tabs<Key extends string>({ tabs, value, equallySized, onChange, controller: ctrl, className, ...props }: TabsProps<Key>) {
|
|
32
|
-
const controller = useMemo(() => ctrl ?? new TabController<Key>(tabs.map(t => t.key), value || tabs[0]?.key), [])
|
|
33
|
-
const t = useTranslate(dictionary)
|
|
34
|
-
const [selectedIndex, setSelectedIndex] = useState(findSelectedIndex(tabs, controller.getValue()))
|
|
35
|
-
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
if (value) controller.setValue(value)
|
|
38
|
-
}, [value])
|
|
39
|
-
|
|
40
|
-
useEffect(() => controller.onChange((v) => {
|
|
41
|
-
setSelectedIndex(findSelectedIndex(tabs, v))
|
|
42
|
-
onChange?.(v)
|
|
43
|
-
}), [tabs])
|
|
44
|
-
|
|
45
|
-
const nav = useMemo(
|
|
46
|
-
() => tabs.map(({ key, label, disabled }, index) => (
|
|
47
|
-
<label key={key}>
|
|
48
|
-
<input type="radio" role="tab" checked={index === selectedIndex} onChange={() => controller.setValue(key)} disabled={disabled} />
|
|
49
|
-
{label || key}
|
|
50
|
-
</label>
|
|
51
|
-
)),
|
|
52
|
-
[tabs, selectedIndex],
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
return (
|
|
56
|
-
<ctx.Provider value={controller}>
|
|
57
|
-
<CitricComponent tag="div" component="tabs" className={listToClass([className, equallySized && 'equally-sized'])} {...props}>
|
|
58
|
-
<nav>{nav}</nav>
|
|
59
|
-
<section>
|
|
60
|
-
<ErrorBoundary key={selectedIndex} message={t.error}>
|
|
61
|
-
<Suspense fallback={<Center style={{ padding: '20px' }}><ProgressCircular /></Center>}>
|
|
62
|
-
{selectedIndex === -1 ? null : tabs[selectedIndex]?.content}
|
|
63
|
-
</Suspense>
|
|
64
|
-
</ErrorBoundary>
|
|
65
|
-
</section>
|
|
66
|
-
</CitricComponent>
|
|
67
|
-
</ctx.Provider>
|
|
68
|
-
)
|
|
69
|
-
},
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Request the tab controller of the current context. Use this to control the tabs in the parent components. This returns undefined when
|
|
74
|
-
* no tab context is found.
|
|
75
|
-
*/
|
|
76
|
-
export function useTabController<Key extends string = string>(): TabController<Key> | undefined {
|
|
77
|
-
return useContext(ctx)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const dictionary = {
|
|
81
|
-
en: {
|
|
82
|
-
error: 'Error while loading the tab.',
|
|
83
|
-
},
|
|
84
|
-
pt: {
|
|
85
|
-
error: 'Ocorreu um erro ao carregar a aba.',
|
|
86
|
-
},
|
|
87
|
-
} satisfies Dictionary
|
|
1
|
+
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
|
+
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
3
|
+
import { createContext, Suspense, useContext, useEffect, useMemo, useState } from 'react'
|
|
4
|
+
import { withRef } from '../../utils/react'
|
|
5
|
+
import { CitricComponent } from '../CitricComponent'
|
|
6
|
+
import { ErrorBoundary } from '../ErrorBoundary'
|
|
7
|
+
import { ProgressCircular } from '../ProgressCircular'
|
|
8
|
+
import { Center } from '../layout'
|
|
9
|
+
import { TabController } from './TabController'
|
|
10
|
+
import { TabsProps } from './types'
|
|
11
|
+
import { findSelectedIndex } from './utils'
|
|
12
|
+
|
|
13
|
+
const ctx = createContext<TabController<any> | undefined>(undefined)
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Builds a Tab selector. The content of a tab is a React component and it can suspend, i.e. tabs can be asynchronously loaded.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
*
|
|
20
|
+
* ```
|
|
21
|
+
* const tabs: Tab[] = useMemo(() => [
|
|
22
|
+
* { key: 'tab1', label: 'Tab 1', content: <><h1>First tab</h1><p>Content of the first tab</p></> },
|
|
23
|
+
* { key: 'tab2', label: 'Tab 2', content: <><h1>Second tab</h1><p>Content of the second tab</p></> },
|
|
24
|
+
* { key: 'tab3', label: 'Tab 3', content: <><h1>Third tab</h1><p>Content of the third tab</p></> },
|
|
25
|
+
* ], [])
|
|
26
|
+
*
|
|
27
|
+
* return <Tabs tabs={tabs} />
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export const Tabs = withRef(
|
|
31
|
+
function Tabs<Key extends string>({ tabs, value, equallySized, onChange, controller: ctrl, className, ...props }: TabsProps<Key>) {
|
|
32
|
+
const controller = useMemo(() => ctrl ?? new TabController<Key>(tabs.map(t => t.key), value || tabs[0]?.key), [])
|
|
33
|
+
const t = useTranslate(dictionary)
|
|
34
|
+
const [selectedIndex, setSelectedIndex] = useState(findSelectedIndex(tabs, controller.getValue()))
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (value) controller.setValue(value)
|
|
38
|
+
}, [value])
|
|
39
|
+
|
|
40
|
+
useEffect(() => controller.onChange((v) => {
|
|
41
|
+
setSelectedIndex(findSelectedIndex(tabs, v))
|
|
42
|
+
onChange?.(v)
|
|
43
|
+
}), [tabs])
|
|
44
|
+
|
|
45
|
+
const nav = useMemo(
|
|
46
|
+
() => tabs.map(({ key, label, disabled }, index) => (
|
|
47
|
+
<label key={key}>
|
|
48
|
+
<input type="radio" role="tab" checked={index === selectedIndex} onChange={() => controller.setValue(key)} disabled={disabled} />
|
|
49
|
+
{label || key}
|
|
50
|
+
</label>
|
|
51
|
+
)),
|
|
52
|
+
[tabs, selectedIndex],
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<ctx.Provider value={controller}>
|
|
57
|
+
<CitricComponent tag="div" component="tabs" className={listToClass([className, equallySized && 'equally-sized'])} {...props}>
|
|
58
|
+
<nav>{nav}</nav>
|
|
59
|
+
<section>
|
|
60
|
+
<ErrorBoundary key={selectedIndex} message={t.error}>
|
|
61
|
+
<Suspense fallback={<Center style={{ padding: '20px' }}><ProgressCircular /></Center>}>
|
|
62
|
+
{selectedIndex === -1 ? null : tabs[selectedIndex]?.content}
|
|
63
|
+
</Suspense>
|
|
64
|
+
</ErrorBoundary>
|
|
65
|
+
</section>
|
|
66
|
+
</CitricComponent>
|
|
67
|
+
</ctx.Provider>
|
|
68
|
+
)
|
|
69
|
+
},
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Request the tab controller of the current context. Use this to control the tabs in the parent components. This returns undefined when
|
|
74
|
+
* no tab context is found.
|
|
75
|
+
*/
|
|
76
|
+
export function useTabController<Key extends string = string>(): TabController<Key> | undefined {
|
|
77
|
+
return useContext(ctx)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const dictionary = {
|
|
81
|
+
en: {
|
|
82
|
+
error: 'Error while loading the tab.',
|
|
83
|
+
},
|
|
84
|
+
pt: {
|
|
85
|
+
error: 'Ocorreu um erro ao carregar a aba.',
|
|
86
|
+
},
|
|
87
|
+
} satisfies Dictionary
|
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
import { WithColorPalette, WithColorScheme } from '../../types'
|
|
2
|
-
import { TabController } from './TabController'
|
|
3
|
-
|
|
4
|
-
export interface Tab<Key extends string = string> {
|
|
5
|
-
/**
|
|
6
|
-
* A unique identifier for the tab.
|
|
7
|
-
*/
|
|
8
|
-
key: Key,
|
|
9
|
-
/**
|
|
10
|
-
* The tab's label. Will be the "key" if not provided.
|
|
11
|
-
*/
|
|
12
|
-
label?: string,
|
|
13
|
-
/**
|
|
14
|
-
* The tab's content. This can be a suspended component.
|
|
15
|
-
*
|
|
16
|
-
* Attention: consider using React's lazy loading to render the tab view.
|
|
17
|
-
*/
|
|
18
|
-
content: React.ReactNode,
|
|
19
|
-
/**
|
|
20
|
-
* Whether or not this tab is disabled.
|
|
21
|
-
*
|
|
22
|
-
* @default false
|
|
23
|
-
*/
|
|
24
|
-
disabled?: boolean,
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface BaseTabsProps<Key extends string> extends WithColorScheme, WithColorPalette {
|
|
28
|
-
/**
|
|
29
|
-
* The tabs to render.
|
|
30
|
-
*/
|
|
31
|
-
tabs: Tab<Key>[],
|
|
32
|
-
/**
|
|
33
|
-
* The key of the current tab. Use this only if you intend to control the tabs from the outside.
|
|
34
|
-
*/
|
|
35
|
-
value?: Key,
|
|
36
|
-
/**
|
|
37
|
-
* What to do when the tab changes. Use this only if you intend to control the tabs from the outside.
|
|
38
|
-
*
|
|
39
|
-
* @param key the key of the selected tab.
|
|
40
|
-
*/
|
|
41
|
-
onChange?: (key: Key) => void,
|
|
42
|
-
/**
|
|
43
|
-
* A tab-controller, useful if you want to control the tabs outside the component context.
|
|
44
|
-
*/
|
|
45
|
-
controller?: TabController<Key>,
|
|
46
|
-
/**
|
|
47
|
-
* Instead of each tab occupying only the space it needs, all tabs will have their sizes equally distributed.
|
|
48
|
-
*
|
|
49
|
-
* @default false
|
|
50
|
-
*/
|
|
51
|
-
equallySized?: boolean,
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export type TabsProps<Key extends string> = Omit<React.JSX.IntrinsicElements['div'], 'onChange' | 'children'> & BaseTabsProps<Key>
|
|
1
|
+
import { WithColorPalette, WithColorScheme } from '../../types'
|
|
2
|
+
import { TabController } from './TabController'
|
|
3
|
+
|
|
4
|
+
export interface Tab<Key extends string = string> {
|
|
5
|
+
/**
|
|
6
|
+
* A unique identifier for the tab.
|
|
7
|
+
*/
|
|
8
|
+
key: Key,
|
|
9
|
+
/**
|
|
10
|
+
* The tab's label. Will be the "key" if not provided.
|
|
11
|
+
*/
|
|
12
|
+
label?: string,
|
|
13
|
+
/**
|
|
14
|
+
* The tab's content. This can be a suspended component.
|
|
15
|
+
*
|
|
16
|
+
* Attention: consider using React's lazy loading to render the tab view.
|
|
17
|
+
*/
|
|
18
|
+
content: React.ReactNode,
|
|
19
|
+
/**
|
|
20
|
+
* Whether or not this tab is disabled.
|
|
21
|
+
*
|
|
22
|
+
* @default false
|
|
23
|
+
*/
|
|
24
|
+
disabled?: boolean,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface BaseTabsProps<Key extends string> extends WithColorScheme, WithColorPalette {
|
|
28
|
+
/**
|
|
29
|
+
* The tabs to render.
|
|
30
|
+
*/
|
|
31
|
+
tabs: Tab<Key>[],
|
|
32
|
+
/**
|
|
33
|
+
* The key of the current tab. Use this only if you intend to control the tabs from the outside.
|
|
34
|
+
*/
|
|
35
|
+
value?: Key,
|
|
36
|
+
/**
|
|
37
|
+
* What to do when the tab changes. Use this only if you intend to control the tabs from the outside.
|
|
38
|
+
*
|
|
39
|
+
* @param key the key of the selected tab.
|
|
40
|
+
*/
|
|
41
|
+
onChange?: (key: Key) => void,
|
|
42
|
+
/**
|
|
43
|
+
* A tab-controller, useful if you want to control the tabs outside the component context.
|
|
44
|
+
*/
|
|
45
|
+
controller?: TabController<Key>,
|
|
46
|
+
/**
|
|
47
|
+
* Instead of each tab occupying only the space it needs, all tabs will have their sizes equally distributed.
|
|
48
|
+
*
|
|
49
|
+
* @default false
|
|
50
|
+
*/
|
|
51
|
+
equallySized?: boolean,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type TabsProps<Key extends string> = Omit<React.JSX.IntrinsicElements['div'], 'onChange' | 'children'> & BaseTabsProps<Key>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Tab } from './types'
|
|
2
|
-
|
|
3
|
-
export function findSelectedIndex<Key extends string>(tabs: Tab<Key>[], selectedTabKey: Key) {
|
|
4
|
-
const index = tabs.findIndex(t => t.key === selectedTabKey)
|
|
5
|
-
return index === -1 ? 0 : index
|
|
6
|
-
}
|
|
1
|
+
import { Tab } from './types'
|
|
2
|
+
|
|
3
|
+
export function findSelectedIndex<Key extends string>(tabs: Tab<Key>[], selectedTabKey: Key) {
|
|
4
|
+
const index = tabs.findIndex(t => t.key === selectedTabKey)
|
|
5
|
+
return index === -1 ? 0 : index
|
|
6
|
+
}
|
package/src/components/Text.ts
CHANGED
|
@@ -1,111 +1,111 @@
|
|
|
1
|
-
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
|
-
import { createElement } from 'react'
|
|
3
|
-
import { HTMLTag, TextAppearance, WithColor } from '../types'
|
|
4
|
-
import { applyColor, applyTextAppearance } from '../utils/css'
|
|
5
|
-
import { withRef } from '../utils/react'
|
|
6
|
-
|
|
7
|
-
type SupportedTags = 'span' | 'small' | 'p' | `h${1|2|3|4|5|6}` | 'pre'
|
|
8
|
-
|
|
9
|
-
export interface BaseTextProps<T extends SupportedTags = SupportedTags> extends WithColor {
|
|
10
|
-
/**
|
|
11
|
-
* If "tag" is not defined, it will be based on the value of "appearance".
|
|
12
|
-
* If neither "appearance" or "tag" are defined, the tag will be "p".
|
|
13
|
-
*/
|
|
14
|
-
tag?: T,
|
|
15
|
-
/**
|
|
16
|
-
* If "appearance" is not defined, it will be based on the value of "tag".
|
|
17
|
-
* If neither "appearance" or "tag" are defined, the appearance will be inherited from the parent HTML element.
|
|
18
|
-
*/
|
|
19
|
-
appearance?: TextAppearance,
|
|
20
|
-
/**
|
|
21
|
-
* Add default margins to the text.
|
|
22
|
-
*
|
|
23
|
-
* @default false
|
|
24
|
-
*/
|
|
25
|
-
showMargins?: boolean,
|
|
26
|
-
/**
|
|
27
|
-
* Shortcut to `style.fontWeight`.
|
|
28
|
-
*/
|
|
29
|
-
weight?: React.CSSProperties['fontWeight'],
|
|
30
|
-
/**
|
|
31
|
-
* Shortcut to `style.textAlign`.
|
|
32
|
-
*/
|
|
33
|
-
align?: React.CSSProperties['textAlign'],
|
|
34
|
-
/**
|
|
35
|
-
* Shortcut to `style.whiteSpace = 'nowrap'; style.overflow = 'hidden'; style.textOverflow = 'ellipsis'`.
|
|
36
|
-
*/
|
|
37
|
-
nowrapEllipsis?: boolean,
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export type TextProps<T extends SupportedTags> = HTMLTag[T] & BaseTextProps<T>
|
|
41
|
-
|
|
42
|
-
const tagToAppearance: Record<SupportedTags, TextAppearance | undefined> = {
|
|
43
|
-
h1: 'h1',
|
|
44
|
-
h2: 'h2',
|
|
45
|
-
h3: 'h3',
|
|
46
|
-
h4: 'h4',
|
|
47
|
-
h5: 'h5',
|
|
48
|
-
h6: 'h6',
|
|
49
|
-
span: undefined,
|
|
50
|
-
small: 'microtext1',
|
|
51
|
-
p: 'body2',
|
|
52
|
-
pre: 'code2',
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const appearanceToTag: Record<TextAppearance, SupportedTags> = {
|
|
56
|
-
h1: 'h1',
|
|
57
|
-
h2: 'h2',
|
|
58
|
-
h3: 'h3',
|
|
59
|
-
h4: 'h4',
|
|
60
|
-
h5: 'h5',
|
|
61
|
-
h6: 'h6',
|
|
62
|
-
body1: 'p',
|
|
63
|
-
body2: 'p',
|
|
64
|
-
code1: 'pre',
|
|
65
|
-
code2: 'pre',
|
|
66
|
-
display1: 'h1',
|
|
67
|
-
microtext1: 'small',
|
|
68
|
-
overheader1: 'p',
|
|
69
|
-
overheader2: 'p',
|
|
70
|
-
subtitle1: 'p',
|
|
71
|
-
subtitle2: 'p',
|
|
72
|
-
subtitle3: 'p',
|
|
73
|
-
subtitle4: 'p',
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Renders a text. The tag can be set by the property "tag". If unset, the tag rendered will be based on the property "appearance". If
|
|
78
|
-
* "appearance" is also unset, the tag "p" is rendered.
|
|
79
|
-
*
|
|
80
|
-
* Texts have no margin by default, to enable margins, use `showMargins = true`.
|
|
81
|
-
*
|
|
82
|
-
* If the appearance is not set, it will be inherited from the parent.
|
|
83
|
-
*
|
|
84
|
-
* A font can be applied to a text without this component, just use `{ font: theme.font.$textAppearance }` in your style.
|
|
85
|
-
*
|
|
86
|
-
* @example
|
|
87
|
-
* ```
|
|
88
|
-
* <Text appearance="body2">Hello World!</Text>
|
|
89
|
-
* ```
|
|
90
|
-
*/
|
|
91
|
-
export const Text = withRef(
|
|
92
|
-
function Text<T extends SupportedTags>(
|
|
93
|
-
{ tag, appearance, color, showMargins, weight, align, nowrapEllipsis, style, className, children, ...props }: TextProps<T>,
|
|
94
|
-
) {
|
|
95
|
-
const renderedTag = tag || appearanceToTag[appearance || 'body2']
|
|
96
|
-
const renderedAppearance = appearance || (tag ? tagToAppearance[tag] : undefined)
|
|
97
|
-
if (!appearance && !showMargins) style = { margin: 0, ...style }
|
|
98
|
-
if (weight) style = { fontWeight: weight, ...style }
|
|
99
|
-
if (align) style = { textAlign: align, ...style }
|
|
100
|
-
if (nowrapEllipsis) style = { whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', ...style }
|
|
101
|
-
return createElement(
|
|
102
|
-
renderedTag,
|
|
103
|
-
{
|
|
104
|
-
className: applyTextAppearance(listToClass([className, showMargins && 'text-with-margins']), renderedAppearance),
|
|
105
|
-
style: applyColor(style, color),
|
|
106
|
-
...props,
|
|
107
|
-
},
|
|
108
|
-
children,
|
|
109
|
-
)
|
|
110
|
-
},
|
|
111
|
-
)
|
|
1
|
+
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
|
+
import { createElement } from 'react'
|
|
3
|
+
import { HTMLTag, TextAppearance, WithColor } from '../types'
|
|
4
|
+
import { applyColor, applyTextAppearance } from '../utils/css'
|
|
5
|
+
import { withRef } from '../utils/react'
|
|
6
|
+
|
|
7
|
+
type SupportedTags = 'span' | 'small' | 'p' | `h${1|2|3|4|5|6}` | 'pre'
|
|
8
|
+
|
|
9
|
+
export interface BaseTextProps<T extends SupportedTags = SupportedTags> extends WithColor {
|
|
10
|
+
/**
|
|
11
|
+
* If "tag" is not defined, it will be based on the value of "appearance".
|
|
12
|
+
* If neither "appearance" or "tag" are defined, the tag will be "p".
|
|
13
|
+
*/
|
|
14
|
+
tag?: T,
|
|
15
|
+
/**
|
|
16
|
+
* If "appearance" is not defined, it will be based on the value of "tag".
|
|
17
|
+
* If neither "appearance" or "tag" are defined, the appearance will be inherited from the parent HTML element.
|
|
18
|
+
*/
|
|
19
|
+
appearance?: TextAppearance,
|
|
20
|
+
/**
|
|
21
|
+
* Add default margins to the text.
|
|
22
|
+
*
|
|
23
|
+
* @default false
|
|
24
|
+
*/
|
|
25
|
+
showMargins?: boolean,
|
|
26
|
+
/**
|
|
27
|
+
* Shortcut to `style.fontWeight`.
|
|
28
|
+
*/
|
|
29
|
+
weight?: React.CSSProperties['fontWeight'],
|
|
30
|
+
/**
|
|
31
|
+
* Shortcut to `style.textAlign`.
|
|
32
|
+
*/
|
|
33
|
+
align?: React.CSSProperties['textAlign'],
|
|
34
|
+
/**
|
|
35
|
+
* Shortcut to `style.whiteSpace = 'nowrap'; style.overflow = 'hidden'; style.textOverflow = 'ellipsis'`.
|
|
36
|
+
*/
|
|
37
|
+
nowrapEllipsis?: boolean,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type TextProps<T extends SupportedTags> = HTMLTag[T] & BaseTextProps<T>
|
|
41
|
+
|
|
42
|
+
const tagToAppearance: Record<SupportedTags, TextAppearance | undefined> = {
|
|
43
|
+
h1: 'h1',
|
|
44
|
+
h2: 'h2',
|
|
45
|
+
h3: 'h3',
|
|
46
|
+
h4: 'h4',
|
|
47
|
+
h5: 'h5',
|
|
48
|
+
h6: 'h6',
|
|
49
|
+
span: undefined,
|
|
50
|
+
small: 'microtext1',
|
|
51
|
+
p: 'body2',
|
|
52
|
+
pre: 'code2',
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const appearanceToTag: Record<TextAppearance, SupportedTags> = {
|
|
56
|
+
h1: 'h1',
|
|
57
|
+
h2: 'h2',
|
|
58
|
+
h3: 'h3',
|
|
59
|
+
h4: 'h4',
|
|
60
|
+
h5: 'h5',
|
|
61
|
+
h6: 'h6',
|
|
62
|
+
body1: 'p',
|
|
63
|
+
body2: 'p',
|
|
64
|
+
code1: 'pre',
|
|
65
|
+
code2: 'pre',
|
|
66
|
+
display1: 'h1',
|
|
67
|
+
microtext1: 'small',
|
|
68
|
+
overheader1: 'p',
|
|
69
|
+
overheader2: 'p',
|
|
70
|
+
subtitle1: 'p',
|
|
71
|
+
subtitle2: 'p',
|
|
72
|
+
subtitle3: 'p',
|
|
73
|
+
subtitle4: 'p',
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Renders a text. The tag can be set by the property "tag". If unset, the tag rendered will be based on the property "appearance". If
|
|
78
|
+
* "appearance" is also unset, the tag "p" is rendered.
|
|
79
|
+
*
|
|
80
|
+
* Texts have no margin by default, to enable margins, use `showMargins = true`.
|
|
81
|
+
*
|
|
82
|
+
* If the appearance is not set, it will be inherited from the parent.
|
|
83
|
+
*
|
|
84
|
+
* A font can be applied to a text without this component, just use `{ font: theme.font.$textAppearance }` in your style.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```
|
|
88
|
+
* <Text appearance="body2">Hello World!</Text>
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export const Text = withRef(
|
|
92
|
+
function Text<T extends SupportedTags>(
|
|
93
|
+
{ tag, appearance, color, showMargins, weight, align, nowrapEllipsis, style, className, children, ...props }: TextProps<T>,
|
|
94
|
+
) {
|
|
95
|
+
const renderedTag = tag || appearanceToTag[appearance || 'body2']
|
|
96
|
+
const renderedAppearance = appearance || (tag ? tagToAppearance[tag] : undefined)
|
|
97
|
+
if (!appearance && !showMargins) style = { margin: 0, ...style }
|
|
98
|
+
if (weight) style = { fontWeight: weight, ...style }
|
|
99
|
+
if (align) style = { textAlign: align, ...style }
|
|
100
|
+
if (nowrapEllipsis) style = { whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', ...style }
|
|
101
|
+
return createElement(
|
|
102
|
+
renderedTag,
|
|
103
|
+
{
|
|
104
|
+
className: applyTextAppearance(listToClass([className, showMargins && 'text-with-margins']), renderedAppearance),
|
|
105
|
+
style: applyColor(style, color),
|
|
106
|
+
...props,
|
|
107
|
+
},
|
|
108
|
+
children,
|
|
109
|
+
)
|
|
110
|
+
},
|
|
111
|
+
)
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { ControlledTextarea, WithColorScheme } from '../types'
|
|
3
|
-
import { withRef } from '../utils/react'
|
|
4
|
-
import { CitricComponent } from './CitricComponent'
|
|
5
|
-
|
|
6
|
-
export interface BaseTextareaProps extends WithColorScheme {
|
|
7
|
-
value?: string,
|
|
8
|
-
onChange?: (value: string) => void,
|
|
9
|
-
ref?: React.MutableRefObject<HTMLTextAreaElement | null>,
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export type TextareaProps = ControlledTextarea & BaseTextareaProps
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Renders a textarea HTML element. With the difference that the value is not determined by the children, but the property "value" instead.
|
|
16
|
-
*
|
|
17
|
-
* Attention: "onChange" receives the new value, not the event.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```
|
|
21
|
-
* const [value, setValue] = useState('')
|
|
22
|
-
* return <Textarea value={value} onChange={setValue} />
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
export const Textarea = withRef(({ value, onChange, ...props }: TextareaProps) =>
|
|
26
|
-
<CitricComponent tag="textarea" component="textarea" value={value} onChange={e => onChange?.(e.target.value)} {...props} />,
|
|
27
|
-
)
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { ControlledTextarea, WithColorScheme } from '../types'
|
|
3
|
+
import { withRef } from '../utils/react'
|
|
4
|
+
import { CitricComponent } from './CitricComponent'
|
|
5
|
+
|
|
6
|
+
export interface BaseTextareaProps extends WithColorScheme {
|
|
7
|
+
value?: string,
|
|
8
|
+
onChange?: (value: string) => void,
|
|
9
|
+
ref?: React.MutableRefObject<HTMLTextAreaElement | null>,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type TextareaProps = ControlledTextarea & BaseTextareaProps
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Renders a textarea HTML element. With the difference that the value is not determined by the children, but the property "value" instead.
|
|
16
|
+
*
|
|
17
|
+
* Attention: "onChange" receives the new value, not the event.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```
|
|
21
|
+
* const [value, setValue] = useState('')
|
|
22
|
+
* return <Textarea value={value} onChange={setValue} />
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export const Textarea = withRef(({ value, onChange, ...props }: TextareaProps) =>
|
|
26
|
+
<CitricComponent tag="textarea" component="textarea" value={value} onChange={e => onChange?.(e.target.value)} {...props} />,
|
|
27
|
+
)
|