@stack-spot/citric-react 0.44.0 → 0.46.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 +2 -2
- package/dist/components/Stepper.d.ts +1 -1
- package/dist/components/Stepper.d.ts.map +1 -1
- package/dist/components/Stepper.js +30 -26
- package/dist/components/Stepper.js.map +1 -1
- package/dist/components/Tabs/TabController.d.ts +4 -0
- package/dist/components/Tabs/TabController.d.ts.map +1 -1
- package/dist/components/Tabs/TabController.js +18 -0
- package/dist/components/Tabs/TabController.js.map +1 -1
- package/dist/components/Tabs/index.d.ts +1 -1
- package/dist/components/Tabs/index.d.ts.map +1 -1
- package/dist/components/Tabs/index.js +13 -7
- package/dist/components/Tabs/index.js.map +1 -1
- package/dist/components/Tabs/types.d.ts +8 -5
- package/dist/components/Tabs/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Stepper.tsx +63 -54
- package/src/components/Tabs/TabController.ts +18 -0
- package/src/components/Tabs/index.tsx +16 -7
- package/src/components/Tabs/types.ts +8 -5
package/dist/citric.css
CHANGED
|
@@ -519,7 +519,7 @@
|
|
|
519
519
|
position: relative;
|
|
520
520
|
display: flex;
|
|
521
521
|
align-items: center;
|
|
522
|
-
gap:
|
|
522
|
+
gap: 8px;
|
|
523
523
|
background-color: transparent;
|
|
524
524
|
border: none;
|
|
525
525
|
outline: none;
|
|
@@ -609,7 +609,7 @@
|
|
|
609
609
|
padding: 8px 32px;
|
|
610
610
|
display: flex;
|
|
611
611
|
align-items: center;
|
|
612
|
-
gap:
|
|
612
|
+
gap: 8px;
|
|
613
613
|
background-color: transparent;
|
|
614
614
|
border: none;
|
|
615
615
|
border-radius: 2px;
|
|
@@ -66,5 +66,5 @@ export type StepperProps<Key extends string> = Omit<React.JSX.IntrinsicElements[
|
|
|
66
66
|
* return <Stepper tabs={steps} />
|
|
67
67
|
* ```
|
|
68
68
|
*/
|
|
69
|
-
export declare const Stepper: <Key extends string>({ tabs: initialTabs,
|
|
69
|
+
export declare const Stepper: <Key extends string>({ tabs: initialTabs, value, onChange, buttons, className, ...props }: StepperProps<Key>) => import("react/jsx-runtime").JSX.Element;
|
|
70
70
|
//# sourceMappingURL=Stepper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Stepper.d.ts","sourceRoot":"","sources":["../../src/components/Stepper.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Stepper.d.ts","sourceRoot":"","sources":["../../src/components/Stepper.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAE,aAAa,EAAO,MAAM,cAAc,CAAA;AAGjD,MAAM,WAAW,gBAAgB,CAAC,GAAG,SAAS,MAAM,CAAE,SAAQ,aAAa,CAAC,GAAG,CAAC;IAC9E;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG;QAClB;;WAEG;QACH,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB;;WAEG;QACH,IAAI,CAAC,EAAE,MAAM,CAAC;QACd;;WAEG;QACH,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB;;WAEG;QACH,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB;;;WAGG;QACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;QACtB;;;WAGG;QACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC;QAC1C;;;WAGG;QACH,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC;QACtC;;;WAGG;QACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;KACvB,CAAC;CACH;AAED,MAAM,MAAM,YAAY,CAAC,GAAG,SAAS,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;AAoDxI;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,OAAO,GACD,GAAG,SAAS,MAAM,wEAC4C,YAAY,CAAC,GAAG,CAAC,4CAyBjG,CAAA"}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { listToClass } from '@stack-spot/portal-theme';
|
|
3
3
|
import { useTranslate } from '@stack-spot/portal-translate';
|
|
4
|
-
import { useCallback, useEffect,
|
|
4
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
5
5
|
import { withRef } from '../utils/react.js';
|
|
6
6
|
import { Button } from './Button.js';
|
|
7
|
-
import {
|
|
8
|
-
import { Tabs } from './Tabs/index.js';
|
|
9
|
-
import { TabController } from './Tabs/TabController.js';
|
|
7
|
+
import { Row } from './layout.js';
|
|
8
|
+
import { Tabs, useTabController } from './Tabs/index.js';
|
|
10
9
|
import { findSelectedIndex } from './Tabs/utils.js';
|
|
11
10
|
function getTabsWithDisabled(tabs, value) {
|
|
12
11
|
let index = findSelectedIndex(tabs, value ?? '');
|
|
@@ -14,6 +13,27 @@ function getTabsWithDisabled(tabs, value) {
|
|
|
14
13
|
index = 0;
|
|
15
14
|
return tabs.map((t, i) => ({ ...t, disabled: i > index }));
|
|
16
15
|
}
|
|
16
|
+
const StepperButtons = ({ buttons, tabs }) => {
|
|
17
|
+
const controller = useTabController();
|
|
18
|
+
const [selectedIndex, setSelectedIndex] = useState(findSelectedIndex(tabs, controller.getValue()));
|
|
19
|
+
const t = useTranslate(dictionary);
|
|
20
|
+
const tabsRef = useRef(tabs);
|
|
21
|
+
tabsRef.current = tabs;
|
|
22
|
+
useEffect(() => controller.onChange((value) => {
|
|
23
|
+
setSelectedIndex(findSelectedIndex(tabsRef.current, value));
|
|
24
|
+
}), [controller]);
|
|
25
|
+
const onPrevious = useCallback(() => {
|
|
26
|
+
controller.previous();
|
|
27
|
+
if (typeof buttons === 'object')
|
|
28
|
+
buttons.onPrevious?.(controller.getValue());
|
|
29
|
+
}, [controller]);
|
|
30
|
+
const onNext = useCallback(() => {
|
|
31
|
+
controller.next();
|
|
32
|
+
if (typeof buttons === 'object')
|
|
33
|
+
buttons.onNext?.(controller.getValue());
|
|
34
|
+
}, [controller]);
|
|
35
|
+
return _jsxs(Row, { mt: "20px", justifyContent: (typeof buttons !== 'object' || !buttons.onCancel) && selectedIndex === 0 ? 'end' : 'space-between', children: [selectedIndex === 0 && typeof buttons === 'object' && buttons.onCancel && (_jsx(Button, { onClick: buttons.onCancel, colorScheme: "inverse", appearance: "outlined", children: buttons.cancel || t.cancel })), selectedIndex > 0 && buttons && (_jsx(Button, { onClick: onPrevious, colorScheme: "inverse", appearance: "outlined", children: (typeof buttons === 'object' && buttons.previous) || t.previous })), selectedIndex < tabs.length - 1 && buttons && (_jsx(Button, { onClick: onNext, children: (typeof buttons === 'object' && buttons.next) || t.next })), selectedIndex === tabs.length - 1 && typeof buttons === 'object' && buttons.onFinish && (_jsx(Button, { onClick: buttons.onFinish, children: buttons.finish || t.finish }))] });
|
|
36
|
+
};
|
|
17
37
|
/**
|
|
18
38
|
* A Stepper is a tab view with a different appearance. To control the current tab (step), retrieve the controller by calling
|
|
19
39
|
* `useTabsController()` from within a tab (step) content.
|
|
@@ -33,28 +53,12 @@ function getTabsWithDisabled(tabs, value) {
|
|
|
33
53
|
* return <Stepper tabs={steps} />
|
|
34
54
|
* ```
|
|
35
55
|
*/
|
|
36
|
-
export const Stepper = withRef(function Stepper({ tabs: initialTabs,
|
|
37
|
-
const controller = useMemo(() => ctrl ?? new TabController(initialTabs.map(t => t.key), value || initialTabs[0]?.key), []);
|
|
56
|
+
export const Stepper = withRef(function Stepper({ tabs: initialTabs, value, onChange, buttons = true, className, ...props }) {
|
|
38
57
|
const [tabs, setTabs] = useState(getTabsWithDisabled(initialTabs, value));
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}), [tabs]);
|
|
44
|
-
useEffect(() => controller.onChange((value) => {
|
|
45
|
-
setTabs(getTabsWithDisabled(initialTabs, value));
|
|
46
|
-
}), []);
|
|
47
|
-
const onPrevious = useCallback(() => {
|
|
48
|
-
controller.previous();
|
|
49
|
-
if (typeof buttons === 'object')
|
|
50
|
-
buttons.onPrevious?.(controller.getValue());
|
|
51
|
-
}, []);
|
|
52
|
-
const onNext = useCallback(() => {
|
|
53
|
-
controller.next();
|
|
54
|
-
if (typeof buttons === 'object')
|
|
55
|
-
buttons.onNext?.(controller.getValue());
|
|
56
|
-
}, []);
|
|
57
|
-
return buttons ? (_jsxs(Column, { ...props, className: className, gap: "20px", children: [_jsx(Tabs, { tabs: tabs, controller: controller, value: value, onChange: onChange, className: "stepper" }), _jsxs(Row, { justifyContent: (typeof buttons !== 'object' || !buttons.onCancel) && selectedIndex === 0 ? 'end' : 'space-between', children: [selectedIndex === 0 && typeof buttons === 'object' && buttons.onCancel && (_jsx(Button, { onClick: buttons.onCancel, colorScheme: "inverse", appearance: "outlined", children: buttons.cancel || t.cancel })), selectedIndex > 0 && buttons && (_jsx(Button, { onClick: onPrevious, colorScheme: "inverse", appearance: "outlined", children: (typeof buttons === 'object' && buttons.previous) || t.previous })), selectedIndex < tabs.length - 1 && buttons && (_jsx(Button, { onClick: onNext, children: (typeof buttons === 'object' && buttons.next) || t.next })), selectedIndex === tabs.length - 1 && typeof buttons === 'object' && buttons.onFinish && (_jsx(Button, { onClick: buttons.onFinish, children: buttons.finish || t.finish }))] })] })) : _jsx(Tabs, { tabs: tabs, controller: controller, value: value, onChange: onChange, className: listToClass([className, 'stepper']), ...props });
|
|
58
|
+
return buttons ? (_jsx(Tabs, { tabs: tabs, className: listToClass([className, 'stepper']), value: value, onChange: (value) => {
|
|
59
|
+
setTabs(getTabsWithDisabled(initialTabs, value));
|
|
60
|
+
onChange?.(value);
|
|
61
|
+
}, customRenderer: (selector, content) => _jsxs(_Fragment, { children: [selector, content, _jsx(StepperButtons, { buttons: buttons, tabs: initialTabs })] }) })) : _jsx(Tabs, { tabs: tabs, className: listToClass([className, 'stepper']), ...props });
|
|
58
62
|
});
|
|
59
63
|
const dictionary = {
|
|
60
64
|
en: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Stepper.js","sourceRoot":"","sources":["../../src/components/Stepper.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAc,YAAY,EAAE,MAAM,8BAA8B,CAAA;AACvE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"Stepper.js","sourceRoot":"","sources":["../../src/components/Stepper.tsx"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,10 @@ export declare class TabController<Key extends string> extends ValueController<K
|
|
|
3
3
|
private tabOrder;
|
|
4
4
|
constructor(tabOrder: Key[], value: Key);
|
|
5
5
|
private getCurrentIndex;
|
|
6
|
+
/**
|
|
7
|
+
* Replaces the current set of tabs.
|
|
8
|
+
*/
|
|
9
|
+
setTabOrder(tabOrder: Key[]): void;
|
|
6
10
|
/**
|
|
7
11
|
* @returns true if there's another tab after the one currently selected. False otherwise.
|
|
8
12
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabController.d.ts","sourceRoot":"","sources":["../../../src/components/Tabs/TabController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAE7D,qBAAa,aAAa,CAAC,GAAG,SAAS,MAAM,CAAE,SAAQ,eAAe,CAAC,GAAG,CAAC;IACzE,OAAO,CAAC,QAAQ,CAAO;gBAEX,QAAQ,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,GAAG;IAKvC,OAAO,CAAC,eAAe;IAIvB;;OAEG;IACH,OAAO,IAAI,OAAO;IAKlB;;OAEG;IACH,WAAW,IAAI,OAAO;IAKtB;;;OAGG;IACH,IAAI,IAAI,OAAO;IAQf;;;OAGG;IACH,QAAQ,IAAI,OAAO;CAOpB"}
|
|
1
|
+
{"version":3,"file":"TabController.d.ts","sourceRoot":"","sources":["../../../src/components/Tabs/TabController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAE7D,qBAAa,aAAa,CAAC,GAAG,SAAS,MAAM,CAAE,SAAQ,eAAe,CAAC,GAAG,CAAC;IACzE,OAAO,CAAC,QAAQ,CAAO;gBAEX,QAAQ,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,GAAG;IAKvC,OAAO,CAAC,eAAe;IAIvB;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,GAAG,EAAE;IAe3B;;OAEG;IACH,OAAO,IAAI,OAAO;IAKlB;;OAEG;IACH,WAAW,IAAI,OAAO;IAKtB;;;OAGG;IACH,IAAI,IAAI,OAAO;IAQf;;;OAGG;IACH,QAAQ,IAAI,OAAO;CAOpB"}
|
|
@@ -13,6 +13,24 @@ export class TabController extends ValueController {
|
|
|
13
13
|
getCurrentIndex() {
|
|
14
14
|
return this.tabOrder.findIndex(t => t === this.value);
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Replaces the current set of tabs.
|
|
18
|
+
*/
|
|
19
|
+
setTabOrder(tabOrder) {
|
|
20
|
+
let newValue = this.value;
|
|
21
|
+
if (!tabOrder.includes(this.value)) {
|
|
22
|
+
const prev = this.getCurrentIndex() - 1;
|
|
23
|
+
const key = this.tabOrder[prev];
|
|
24
|
+
if (key && tabOrder.includes(key)) {
|
|
25
|
+
newValue = key;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
newValue = tabOrder[0];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
this.tabOrder = tabOrder;
|
|
32
|
+
this.setValue(newValue);
|
|
33
|
+
}
|
|
16
34
|
/**
|
|
17
35
|
* @returns true if there's another tab after the one currently selected. False otherwise.
|
|
18
36
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabController.js","sourceRoot":"","sources":["../../../src/components/Tabs/TabController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAE7D,MAAM,OAAO,aAAkC,SAAQ,eAAoB;IAGzE,YAAY,QAAe,EAAE,KAAU;QACrC,KAAK,CAAC,KAAK,CAAC,CAAA;QAHN;;;;;WAAe;QAIrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;IAC1B,CAAC;IAEO,eAAe;QACrB,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAA;IACvD,CAAC;IAED;;OAEG;IACH,OAAO;QACL,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,EAAE,CAAA;QACtC,OAAO,OAAO,GAAG,CAAC,CAAC,IAAI,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAA;IAC3D,CAAC;IAED;;OAEG;IACH,WAAW;QACT,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,EAAE,CAAA;QACtC,OAAO,OAAO,GAAG,CAAC,CAAC,IAAI,OAAO,GAAG,CAAC,IAAI,CAAC,CAAA;IACzC,CAAC;IAED;;;OAGG;IACH,IAAI;QACF,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;YACnB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;YACxD,OAAO,IAAI,CAAA;QACb,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IAED;;;OAGG;IACH,QAAQ;QACN,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACvB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;YACxD,OAAO,IAAI,CAAA;QACb,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;CACF"}
|
|
1
|
+
{"version":3,"file":"TabController.js","sourceRoot":"","sources":["../../../src/components/Tabs/TabController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAE7D,MAAM,OAAO,aAAkC,SAAQ,eAAoB;IAGzE,YAAY,QAAe,EAAE,KAAU;QACrC,KAAK,CAAC,KAAK,CAAC,CAAA;QAHN;;;;;WAAe;QAIrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;IAC1B,CAAC;IAEO,eAAe;QACrB,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAA;IACvD,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,QAAe;QACzB,IAAI,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAA;QACzB,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,IAAI,CAAC,eAAe,EAAE,GAAG,CAAC,CAAA;YACvC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;YAC/B,IAAI,GAAG,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAClC,QAAQ,GAAG,GAAG,CAAA;YAChB,CAAC;iBAAM,CAAC;gBACN,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;YACxB,CAAC;QACH,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IACzB,CAAC;IAED;;OAEG;IACH,OAAO;QACL,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,EAAE,CAAA;QACtC,OAAO,OAAO,GAAG,CAAC,CAAC,IAAI,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAA;IAC3D,CAAC;IAED;;OAEG;IACH,WAAW;QACT,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,EAAE,CAAA;QACtC,OAAO,OAAO,GAAG,CAAC,CAAC,IAAI,OAAO,GAAG,CAAC,IAAI,CAAC,CAAA;IACzC,CAAC;IAED;;;OAGG;IACH,IAAI;QACF,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;YACnB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;YACxD,OAAO,IAAI,CAAA;QACb,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IAED;;;OAGG;IACH,QAAQ;QACN,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACvB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;YACxD,OAAO,IAAI,CAAA;QACb,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;CACF"}
|
|
@@ -15,7 +15,7 @@ import { TabsProps } from './types.js';
|
|
|
15
15
|
* return <Tabs tabs={tabs} />
|
|
16
16
|
* ```
|
|
17
17
|
*/
|
|
18
|
-
export declare const Tabs: <Key extends string>({ tabs, value, equallySized, onChange,
|
|
18
|
+
export declare const Tabs: <Key extends string>({ tabs, value, equallySized, onChange, appearance, customRenderer, className, ...props }: TabsProps<Key>) => import("react/jsx-runtime").JSX.Element;
|
|
19
19
|
/**
|
|
20
20
|
* Request the tab controller of the current context. Use this to control the tabs in the parent components. This returns undefined when
|
|
21
21
|
* no tab context is found.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Tabs/index.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAKnC;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,IAAI,GACD,GAAG,SAAS,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Tabs/index.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAKnC;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,IAAI,GACD,GAAG,SAAS,MAAM,4FAC4D,SAAS,CAAC,GAAG,CAAC,4CAkE3G,CAAA;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,KAAK,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS,CAE9F"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { listToClass } from '@stack-spot/portal-theme';
|
|
3
3
|
import { useTranslate } from '@stack-spot/portal-translate';
|
|
4
|
-
import { createContext, Suspense, useContext, useEffect, useMemo, useState } from 'react';
|
|
4
|
+
import { createContext, Suspense, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
5
|
import { withRef } from '../../utils/react.js';
|
|
6
6
|
import { CitricComponent } from '../CitricComponent.js';
|
|
7
7
|
import { ErrorBoundary } from '../ErrorBoundary.js';
|
|
@@ -25,20 +25,26 @@ const ctx = createContext(undefined);
|
|
|
25
25
|
* return <Tabs tabs={tabs} />
|
|
26
26
|
* ```
|
|
27
27
|
*/
|
|
28
|
-
export const Tabs = withRef(function Tabs({ tabs, value, equallySized, onChange,
|
|
29
|
-
const
|
|
28
|
+
export const Tabs = withRef(function Tabs({ tabs, value, equallySized, onChange, appearance, customRenderer, className, ...props }) {
|
|
29
|
+
const tabKeys = tabs.map(t => t.key);
|
|
30
|
+
const controller = useMemo(() => new TabController(tabKeys, value || tabs[0]?.key), []);
|
|
30
31
|
const t = useTranslate(dictionary);
|
|
31
32
|
const [selectedIndex, setSelectedIndex] = useState(findSelectedIndex(tabs, controller.getValue()));
|
|
33
|
+
const tabsRef = useRef(tabs);
|
|
34
|
+
tabsRef.current = tabs;
|
|
32
35
|
useEffect(() => {
|
|
33
36
|
if (value)
|
|
34
37
|
controller.setValue(value);
|
|
35
38
|
}, [value]);
|
|
36
39
|
useEffect(() => controller.onChange((v) => {
|
|
37
|
-
setSelectedIndex(findSelectedIndex(
|
|
40
|
+
setSelectedIndex(findSelectedIndex(tabsRef.current, v));
|
|
38
41
|
onChange?.(v);
|
|
39
|
-
}), [
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
}), [controller]);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
controller.setTabOrder(tabKeys);
|
|
45
|
+
}, [tabKeys.join(','), controller]);
|
|
46
|
+
const tabSelector = useMemo(() => (_jsx("nav", { className: "tab-selector", children: tabs.map(({ key, label, icon, after, disabled }, index) => (_jsxs("label", { children: [_jsx("input", { type: "radio", role: "tab", checked: index === selectedIndex, onChange: () => controller.setValue(key), disabled: disabled }), icon, label || key, after] }, key))) })), [tabs, selectedIndex]);
|
|
47
|
+
const content = useMemo(() => (_jsx("section", { className: "tab-content", children: _jsx(ErrorBoundary, { message: t.error, children: _jsx(Suspense, { fallback: _jsx(Center, { style: { padding: '20px' }, children: _jsx(ProgressCircular, {}) }), children: selectedIndex === -1 ? null : tabs[selectedIndex]?.content }) }, selectedIndex) })), [selectedIndex, value, t]);
|
|
42
48
|
return (_jsx(ctx.Provider, { value: controller, children: _jsx(CitricComponent, { tag: "div", component: "tabs", className: listToClass([className, equallySized && 'equally-sized', appearance]), ...props, children: customRenderer ? customRenderer(tabSelector, content) : _jsxs(_Fragment, { children: [tabSelector, content] }) }) }));
|
|
43
49
|
});
|
|
44
50
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/Tabs/index.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAc,YAAY,EAAE,MAAM,8BAA8B,CAAA;AACvE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/Tabs/index.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { WithColorPalette, WithColorScheme } from '../../types.js';
|
|
2
|
-
import { TabController } from './TabController.js';
|
|
3
2
|
export interface Tab<Key extends string = string> {
|
|
4
3
|
/**
|
|
5
4
|
* A unique identifier for the tab.
|
|
@@ -9,6 +8,14 @@ export interface Tab<Key extends string = string> {
|
|
|
9
8
|
* The tab's label. Will be the "key" if not provided.
|
|
10
9
|
*/
|
|
11
10
|
label?: string;
|
|
11
|
+
/**
|
|
12
|
+
* An icon for the tab.
|
|
13
|
+
*/
|
|
14
|
+
icon?: React.ReactElement;
|
|
15
|
+
/**
|
|
16
|
+
* Content to place after the tab's name.
|
|
17
|
+
*/
|
|
18
|
+
after?: React.ReactElement;
|
|
12
19
|
/**
|
|
13
20
|
* The tab's content. This can be a suspended component.
|
|
14
21
|
*
|
|
@@ -37,10 +44,6 @@ export interface BaseTabsProps<Key extends string> extends WithColorScheme, With
|
|
|
37
44
|
* @param key the key of the selected tab.
|
|
38
45
|
*/
|
|
39
46
|
onChange?: (key: Key) => void;
|
|
40
|
-
/**
|
|
41
|
-
* A tab-controller, useful if you want to control the tabs outside the component context.
|
|
42
|
-
*/
|
|
43
|
-
controller?: TabController<Key>;
|
|
44
47
|
/**
|
|
45
48
|
* Instead of each tab occupying only the space it needs, all tabs will have their sizes equally distributed.
|
|
46
49
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/Tabs/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/Tabs/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE/D,MAAM,WAAW,GAAG,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM;IAC9C;;OAEG;IACH,GAAG,EAAE,GAAG,CAAC;IACT;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,IAAI,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC;IAC1B;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC;IAC3B;;;;OAIG;IACH,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,aAAa,CAAC,GAAG,SAAS,MAAM,CAAE,SAAQ,eAAe,EAAE,gBAAgB;IAC1F;;OAEG;IACH,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;IACjB;;OAEG;IACH,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC;IAC9B;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,YAAY,EAAE,OAAO,EAAE,KAAK,CAAC,YAAY,KAAK,KAAK,CAAC,SAAS,CAAC;CACpG;AAED,MAAM,MAAM,SAAS,CAAC,GAAG,SAAS,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
2
|
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
3
|
-
import { useCallback, useEffect,
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
4
4
|
import { withRef } from '../utils/react'
|
|
5
5
|
import { Button } from './Button'
|
|
6
|
-
import {
|
|
7
|
-
import { Tabs } from './Tabs'
|
|
8
|
-
import { TabController } from './Tabs/TabController'
|
|
6
|
+
import { Row } from './layout'
|
|
7
|
+
import { Tabs, useTabController } from './Tabs'
|
|
9
8
|
import { BaseTabsProps, Tab } from './Tabs/types'
|
|
10
9
|
import { findSelectedIndex } from './Tabs/utils'
|
|
11
10
|
|
|
@@ -65,6 +64,50 @@ function getTabsWithDisabled<Key extends string>(tabs: Tab<Key>[], value: Key |
|
|
|
65
64
|
return tabs.map((t, i) => ({ ...t, disabled: i > index }))
|
|
66
65
|
}
|
|
67
66
|
|
|
67
|
+
const StepperButtons = ({ buttons, tabs }: { buttons: StepperProps<any>['buttons'], tabs: Tab[] }) => {
|
|
68
|
+
const controller = useTabController()!
|
|
69
|
+
const [selectedIndex, setSelectedIndex] = useState(findSelectedIndex(tabs, controller.getValue()))
|
|
70
|
+
const t = useTranslate(dictionary)
|
|
71
|
+
const tabsRef = useRef(tabs)
|
|
72
|
+
tabsRef.current = tabs
|
|
73
|
+
|
|
74
|
+
useEffect(() => controller.onChange((value) => {
|
|
75
|
+
setSelectedIndex(findSelectedIndex(tabsRef.current, value))
|
|
76
|
+
}), [controller])
|
|
77
|
+
|
|
78
|
+
const onPrevious = useCallback(() => {
|
|
79
|
+
controller.previous()
|
|
80
|
+
if (typeof buttons === 'object') buttons.onPrevious?.(controller.getValue())
|
|
81
|
+
}, [controller])
|
|
82
|
+
|
|
83
|
+
const onNext = useCallback(() => {
|
|
84
|
+
controller.next()
|
|
85
|
+
if (typeof buttons === 'object') buttons.onNext?.(controller.getValue())
|
|
86
|
+
}, [controller])
|
|
87
|
+
|
|
88
|
+
return <Row
|
|
89
|
+
mt="20px"
|
|
90
|
+
justifyContent={(typeof buttons !== 'object' || !buttons.onCancel) && selectedIndex === 0 ? 'end' : 'space-between'}
|
|
91
|
+
>
|
|
92
|
+
{selectedIndex === 0 && typeof buttons === 'object' && buttons.onCancel && (
|
|
93
|
+
<Button onClick={buttons.onCancel} colorScheme="inverse" appearance="outlined">{buttons.cancel || t.cancel}</Button>
|
|
94
|
+
)}
|
|
95
|
+
{selectedIndex > 0 && buttons && (
|
|
96
|
+
<Button onClick={onPrevious} colorScheme="inverse" appearance="outlined">
|
|
97
|
+
{(typeof buttons === 'object' && buttons.previous) || t.previous}
|
|
98
|
+
</Button>
|
|
99
|
+
)}
|
|
100
|
+
{selectedIndex < tabs.length - 1 && buttons && (
|
|
101
|
+
<Button onClick={onNext}>
|
|
102
|
+
{(typeof buttons === 'object' && buttons.next) || t.next}
|
|
103
|
+
</Button>
|
|
104
|
+
)}
|
|
105
|
+
{selectedIndex === tabs.length - 1 && typeof buttons === 'object' && buttons.onFinish && (
|
|
106
|
+
<Button onClick={buttons.onFinish}>{buttons.finish || t.finish}</Button>
|
|
107
|
+
)}
|
|
108
|
+
</Row>
|
|
109
|
+
}
|
|
110
|
+
|
|
68
111
|
/**
|
|
69
112
|
* A Stepper is a tab view with a different appearance. To control the current tab (step), retrieve the controller by calling
|
|
70
113
|
* `useTabsController()` from within a tab (step) content.
|
|
@@ -86,61 +129,27 @@ function getTabsWithDisabled<Key extends string>(tabs: Tab<Key>[], value: Key |
|
|
|
86
129
|
*/
|
|
87
130
|
export const Stepper = withRef(
|
|
88
131
|
function Stepper<Key extends string>(
|
|
89
|
-
{ tabs: initialTabs,
|
|
132
|
+
{ tabs: initialTabs, value, onChange, buttons = true, className, ...props }: StepperProps<Key>,
|
|
90
133
|
) {
|
|
91
|
-
const controller = useMemo(
|
|
92
|
-
() => ctrl ?? new TabController<Key>(initialTabs.map(t => t.key), value || initialTabs[0]?.key),
|
|
93
|
-
[],
|
|
94
|
-
)
|
|
95
134
|
const [tabs, setTabs] = useState(getTabsWithDisabled(initialTabs, value))
|
|
96
|
-
|
|
97
|
-
const t = useTranslate(dictionary)
|
|
98
|
-
|
|
99
|
-
useEffect(() => controller.onChange((v) => {
|
|
100
|
-
setSelectedIndex(findSelectedIndex(tabs, v))
|
|
101
|
-
}), [tabs])
|
|
102
|
-
|
|
103
|
-
useEffect(() => controller.onChange((value) => {
|
|
104
|
-
setTabs(getTabsWithDisabled(initialTabs, value))
|
|
105
|
-
}), [])
|
|
106
|
-
|
|
107
|
-
const onPrevious = useCallback(() => {
|
|
108
|
-
controller.previous()
|
|
109
|
-
if (typeof buttons === 'object') buttons.onPrevious?.(controller.getValue())
|
|
110
|
-
}, [])
|
|
111
|
-
|
|
112
|
-
const onNext = useCallback(() => {
|
|
113
|
-
controller.next()
|
|
114
|
-
if (typeof buttons === 'object') buttons.onNext?.(controller.getValue())
|
|
115
|
-
}, [])
|
|
116
|
-
|
|
135
|
+
|
|
117
136
|
return buttons ? (
|
|
118
|
-
<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
</Button>
|
|
133
|
-
)}
|
|
134
|
-
{selectedIndex === tabs.length - 1 && typeof buttons === 'object' && buttons.onFinish && (
|
|
135
|
-
<Button onClick={buttons.onFinish}>{buttons.finish || t.finish}</Button>
|
|
136
|
-
)}
|
|
137
|
-
</Row>
|
|
138
|
-
</Column>
|
|
137
|
+
<Tabs
|
|
138
|
+
tabs={tabs}
|
|
139
|
+
className={listToClass([className, 'stepper'])}
|
|
140
|
+
value={value}
|
|
141
|
+
onChange={(value) => {
|
|
142
|
+
setTabs(getTabsWithDisabled(initialTabs, value))
|
|
143
|
+
onChange?.(value)
|
|
144
|
+
}}
|
|
145
|
+
customRenderer={(selector, content) => <>
|
|
146
|
+
{selector}
|
|
147
|
+
{content}
|
|
148
|
+
<StepperButtons buttons={buttons} tabs={initialTabs} />
|
|
149
|
+
</>}
|
|
150
|
+
/>
|
|
139
151
|
) : <Tabs
|
|
140
152
|
tabs={tabs}
|
|
141
|
-
controller={controller}
|
|
142
|
-
value={value}
|
|
143
|
-
onChange={onChange}
|
|
144
153
|
className={listToClass([className, 'stepper'])}
|
|
145
154
|
{...props}
|
|
146
155
|
/>
|
|
@@ -12,6 +12,24 @@ export class TabController<Key extends string> extends ValueController<Key> {
|
|
|
12
12
|
return this.tabOrder.findIndex(t => t === this.value)
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Replaces the current set of tabs.
|
|
17
|
+
*/
|
|
18
|
+
setTabOrder(tabOrder: Key[]) {
|
|
19
|
+
let newValue = this.value
|
|
20
|
+
if (!tabOrder.includes(this.value)) {
|
|
21
|
+
const prev = this.getCurrentIndex() - 1
|
|
22
|
+
const key = this.tabOrder[prev]
|
|
23
|
+
if (key && tabOrder.includes(key)) {
|
|
24
|
+
newValue = key
|
|
25
|
+
} else {
|
|
26
|
+
newValue = tabOrder[0]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
this.tabOrder = tabOrder
|
|
30
|
+
this.setValue(newValue)
|
|
31
|
+
}
|
|
32
|
+
|
|
15
33
|
/**
|
|
16
34
|
* @returns true if there's another tab after the one currently selected. False otherwise.
|
|
17
35
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
2
|
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
3
|
-
import { createContext, Suspense, useContext, useEffect, useMemo, useState } from 'react'
|
|
3
|
+
import { createContext, Suspense, useContext, useEffect, useMemo, useRef, useState } from 'react'
|
|
4
4
|
import { withRef } from '../../utils/react'
|
|
5
5
|
import { CitricComponent } from '../CitricComponent'
|
|
6
6
|
import { ErrorBoundary } from '../ErrorBoundary'
|
|
@@ -29,25 +29,32 @@ const ctx = createContext<TabController<any> | undefined>(undefined)
|
|
|
29
29
|
*/
|
|
30
30
|
export const Tabs = withRef(
|
|
31
31
|
function Tabs<Key extends string>(
|
|
32
|
-
{ tabs, value, equallySized, onChange,
|
|
32
|
+
{ tabs, value, equallySized, onChange, appearance, customRenderer, className, ...props }: TabsProps<Key>,
|
|
33
33
|
) {
|
|
34
|
-
const
|
|
34
|
+
const tabKeys = tabs.map(t => t.key)
|
|
35
|
+
const controller = useMemo(() => new TabController<Key>(tabKeys, value || tabs[0]?.key), [])
|
|
35
36
|
const t = useTranslate(dictionary)
|
|
36
37
|
const [selectedIndex, setSelectedIndex] = useState(findSelectedIndex(tabs, controller.getValue()))
|
|
38
|
+
const tabsRef = useRef(tabs)
|
|
39
|
+
tabsRef.current = tabs
|
|
37
40
|
|
|
38
41
|
useEffect(() => {
|
|
39
42
|
if (value) controller.setValue(value)
|
|
40
43
|
}, [value])
|
|
41
44
|
|
|
42
45
|
useEffect(() => controller.onChange((v) => {
|
|
43
|
-
setSelectedIndex(findSelectedIndex(
|
|
46
|
+
setSelectedIndex(findSelectedIndex(tabsRef.current, v))
|
|
44
47
|
onChange?.(v)
|
|
45
|
-
}), [
|
|
48
|
+
}), [controller])
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
controller.setTabOrder(tabKeys)
|
|
52
|
+
}, [tabKeys.join(','), controller])
|
|
46
53
|
|
|
47
54
|
const tabSelector = useMemo(
|
|
48
55
|
() => (
|
|
49
56
|
<nav className="tab-selector">
|
|
50
|
-
{tabs.map(({ key, label, disabled }, index) => (
|
|
57
|
+
{tabs.map(({ key, label, icon, after, disabled }, index) => (
|
|
51
58
|
<label key={key}>
|
|
52
59
|
<input
|
|
53
60
|
type="radio"
|
|
@@ -55,7 +62,9 @@ export const Tabs = withRef(
|
|
|
55
62
|
checked={index === selectedIndex}
|
|
56
63
|
onChange={() => controller.setValue(key)} disabled={disabled}
|
|
57
64
|
/>
|
|
65
|
+
{icon}
|
|
58
66
|
{label || key}
|
|
67
|
+
{after}
|
|
59
68
|
</label>
|
|
60
69
|
))}
|
|
61
70
|
</nav>
|
|
@@ -71,7 +80,7 @@ export const Tabs = withRef(
|
|
|
71
80
|
</Suspense>
|
|
72
81
|
</ErrorBoundary>
|
|
73
82
|
</section>
|
|
74
|
-
), [selectedIndex, t])
|
|
83
|
+
), [selectedIndex, value, t])
|
|
75
84
|
|
|
76
85
|
return (
|
|
77
86
|
<ctx.Provider value={controller}>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { WithColorPalette, WithColorScheme } from '../../types'
|
|
2
|
-
import { TabController } from './TabController'
|
|
3
2
|
|
|
4
3
|
export interface Tab<Key extends string = string> {
|
|
5
4
|
/**
|
|
@@ -10,6 +9,14 @@ export interface Tab<Key extends string = string> {
|
|
|
10
9
|
* The tab's label. Will be the "key" if not provided.
|
|
11
10
|
*/
|
|
12
11
|
label?: string,
|
|
12
|
+
/**
|
|
13
|
+
* An icon for the tab.
|
|
14
|
+
*/
|
|
15
|
+
icon?: React.ReactElement,
|
|
16
|
+
/**
|
|
17
|
+
* Content to place after the tab's name.
|
|
18
|
+
*/
|
|
19
|
+
after?: React.ReactElement,
|
|
13
20
|
/**
|
|
14
21
|
* The tab's content. This can be a suspended component.
|
|
15
22
|
*
|
|
@@ -39,10 +46,6 @@ export interface BaseTabsProps<Key extends string> extends WithColorScheme, With
|
|
|
39
46
|
* @param key the key of the selected tab.
|
|
40
47
|
*/
|
|
41
48
|
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
49
|
/**
|
|
47
50
|
* Instead of each tab occupying only the space it needs, all tabs will have their sizes equally distributed.
|
|
48
51
|
*
|