@transferwise/components 0.0.0-experimental-0400704 → 0.0.0-experimental-c09cb11
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/build/dateLookup/dateTrigger/DateTrigger.js +8 -4
- package/build/dateLookup/dateTrigger/DateTrigger.js.map +1 -1
- package/build/dateLookup/dateTrigger/DateTrigger.mjs +8 -4
- package/build/dateLookup/dateTrigger/DateTrigger.mjs.map +1 -1
- package/build/field/Field.js +34 -7
- package/build/field/Field.js.map +1 -1
- package/build/field/Field.mjs +34 -7
- package/build/field/Field.mjs.map +1 -1
- package/build/i18n/en.json +1 -0
- package/build/i18n/en.json.js +1 -0
- package/build/i18n/en.json.js.map +1 -1
- package/build/i18n/en.json.mjs +1 -0
- package/build/i18n/en.json.mjs.map +1 -1
- package/build/index.js +2 -2
- package/build/index.mjs +1 -1
- package/build/inlineAlert/InlineAlert.js +13 -6
- package/build/inlineAlert/InlineAlert.js.map +1 -1
- package/build/inlineAlert/InlineAlert.mjs +13 -6
- package/build/inlineAlert/InlineAlert.mjs.map +1 -1
- package/build/label/Label.js +49 -5
- package/build/label/Label.js.map +1 -1
- package/build/label/Label.messages.js +12 -0
- package/build/label/Label.messages.js.map +1 -0
- package/build/label/Label.messages.mjs +10 -0
- package/build/label/Label.messages.mjs.map +1 -0
- package/build/label/Label.mjs +50 -6
- package/build/label/Label.mjs.map +1 -1
- package/build/main.css +4 -8
- package/build/styles/dateLookup/dateTrigger/DateTrigger.css +0 -8
- package/build/styles/field/Field.css +4 -0
- package/build/styles/main.css +4 -8
- package/build/tabs/Tab.js +38 -13
- package/build/tabs/Tab.js.map +1 -1
- package/build/tabs/Tab.mjs +34 -13
- package/build/tabs/Tab.mjs.map +1 -1
- package/build/tabs/TabList.js +11 -3
- package/build/tabs/TabList.js.map +1 -1
- package/build/tabs/TabList.mjs +7 -3
- package/build/tabs/TabList.mjs.map +1 -1
- package/build/tabs/TabPanel.js +16 -3
- package/build/tabs/TabPanel.js.map +1 -1
- package/build/tabs/TabPanel.mjs +12 -3
- package/build/tabs/TabPanel.mjs.map +1 -1
- package/build/tabs/Tabs.js +48 -24
- package/build/tabs/Tabs.js.map +1 -1
- package/build/tabs/Tabs.mjs +47 -24
- package/build/tabs/Tabs.mjs.map +1 -1
- package/build/tabs/utils.js +1 -0
- package/build/tabs/utils.js.map +1 -1
- package/build/tabs/utils.mjs +1 -0
- package/build/tabs/utils.mjs.map +1 -1
- package/build/types/dateLookup/dateTrigger/DateTrigger.d.ts.map +1 -1
- package/build/types/field/Field.d.ts +4 -2
- package/build/types/field/Field.d.ts.map +1 -1
- package/build/types/index.d.ts +1 -2
- package/build/types/index.d.ts.map +1 -1
- package/build/types/inlineAlert/InlineAlert.d.ts +9 -0
- package/build/types/inlineAlert/InlineAlert.d.ts.map +1 -1
- package/build/types/label/Label.d.ts +39 -3
- package/build/types/label/Label.d.ts.map +1 -1
- package/build/types/label/Label.messages.d.ts +8 -0
- package/build/types/label/Label.messages.d.ts.map +1 -0
- package/build/types/label/index.d.ts +3 -0
- package/build/types/label/index.d.ts.map +1 -0
- package/build/types/tabs/Tab.d.ts +1 -12
- package/build/types/tabs/Tab.d.ts.map +1 -1
- package/build/types/tabs/TabList.d.ts +8 -3
- package/build/types/tabs/TabList.d.ts.map +1 -1
- package/build/types/tabs/TabPanel.d.ts +14 -6
- package/build/types/tabs/TabPanel.d.ts.map +1 -1
- package/build/types/tabs/Tabs.d.ts +30 -83
- package/build/types/tabs/Tabs.d.ts.map +1 -1
- package/build/types/tabs/index.d.ts +1 -2
- package/build/types/tabs/index.d.ts.map +1 -1
- package/build/types/tabs/utils.d.ts +7 -12
- package/build/types/tabs/utils.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/dateInput/DateInput.tests.story.tsx +6 -41
- package/src/dateLookup/DateLookup.rtl.spec.tsx +1 -1
- package/src/dateLookup/dateTrigger/DateTrigger.css +0 -8
- package/src/dateLookup/dateTrigger/DateTrigger.less +0 -8
- package/src/dateLookup/dateTrigger/DateTrigger.spec.js +1 -1
- package/src/dateLookup/dateTrigger/DateTrigger.tsx +9 -4
- package/src/field/Field.css +4 -0
- package/src/field/Field.less +5 -0
- package/src/field/Field.spec.tsx +41 -5
- package/src/field/Field.story.tsx +105 -7
- package/src/field/Field.tsx +33 -9
- package/src/i18n/en.json +1 -0
- package/src/index.ts +1 -2
- package/src/inlineAlert/InlineAlert.story.tsx +7 -72
- package/src/inlineAlert/InlineAlert.tsx +14 -3
- package/src/inputWithDisplayFormat/InputWithDisplayFormat.story.js +5 -10
- package/src/inputs/InputGroup.spec.tsx +1 -1
- package/src/inputs/SearchInput.spec.tsx +1 -1
- package/src/inputs/SelectInput.spec.tsx +1 -1
- package/src/label/Label.messages.tsx +8 -0
- package/src/label/Label.spec.tsx +42 -7
- package/src/label/Label.story.tsx +30 -25
- package/src/label/Label.tsx +66 -8
- package/src/label/index.ts +2 -0
- package/src/main.css +4 -8
- package/src/main.less +1 -0
- package/src/moneyInput/MoneyInput.story.tsx +11 -11
- package/src/radioGroup/RadioGroup.rtl.spec.tsx +1 -1
- package/src/select/Select.rtl.spec.tsx +1 -1
- package/src/switch/Switch.spec.tsx +1 -1
- package/src/switch/Switch.story.tsx +19 -21
- package/src/tabs/Tab.js +71 -0
- package/src/tabs/TabList.js +15 -0
- package/src/tabs/TabPanel.js +20 -0
- package/src/tabs/{Tabs.tsx → Tabs.js} +74 -111
- package/src/tabs/{Tabs.story.tsx → Tabs.story.js} +1 -1
- package/src/tabs/index.js +1 -0
- package/src/tabs/{utils.ts → utils.js} +9 -15
- package/src/tabs/{utils.spec.ts → utils.spec.js} +21 -24
- package/src/field/Field.tests.story.tsx +0 -33
- package/src/tabs/Tab.tsx +0 -72
- package/src/tabs/TabList.tsx +0 -11
- package/src/tabs/TabPanel.tsx +0 -14
- package/src/tabs/index.ts +0 -2
|
@@ -4,8 +4,7 @@ import { Lock } from '@transferwise/icons';
|
|
|
4
4
|
import { useState } from 'react';
|
|
5
5
|
|
|
6
6
|
import MoneyInput, { CurrencyOptionItem } from '.';
|
|
7
|
-
import
|
|
8
|
-
import translations from '../i18n';
|
|
7
|
+
import { Field } from '../field/Field';
|
|
9
8
|
|
|
10
9
|
export default {
|
|
11
10
|
component: MoneyInput,
|
|
@@ -17,14 +16,13 @@ export default {
|
|
|
17
16
|
const handleOnCurrencyChange = (value: CurrencyOptionItem) => setSelectedCurrency(value);
|
|
18
17
|
|
|
19
18
|
return (
|
|
20
|
-
|
|
21
|
-
<label htmlFor={args.id}>Editable money input label</label>
|
|
19
|
+
<Field id={args.id} label="Editable money input label" required>
|
|
22
20
|
<MoneyInput
|
|
23
21
|
{...args}
|
|
24
22
|
selectedCurrency={selectedCurrency}
|
|
25
23
|
onCurrencyChange={handleOnCurrencyChange}
|
|
26
24
|
/>
|
|
27
|
-
|
|
25
|
+
</Field>
|
|
28
26
|
);
|
|
29
27
|
},
|
|
30
28
|
args: {
|
|
@@ -161,17 +159,19 @@ export const SmallInput: Story = {
|
|
|
161
159
|
render: (args) => {
|
|
162
160
|
return (
|
|
163
161
|
<>
|
|
164
|
-
<
|
|
165
|
-
|
|
162
|
+
<Field id={args.id} label="Money inputs" required>
|
|
163
|
+
<MoneyInput {...args} {...SingleCurrency.args} />
|
|
164
|
+
</Field>
|
|
166
165
|
<br />
|
|
167
166
|
<MoneyInput {...args} {...MultipleCurrencies.args} />
|
|
168
167
|
<hr />
|
|
169
|
-
<
|
|
170
|
-
<label htmlFor={args.id}>Error states</label>
|
|
168
|
+
<Field id={args.id} label="Error states" sentiment="negative" required>
|
|
171
169
|
<MoneyInput {...args} {...SingleCurrency.args} />
|
|
172
|
-
|
|
170
|
+
</Field>
|
|
171
|
+
<br />
|
|
172
|
+
<Field sentiment="negative">
|
|
173
173
|
<MoneyInput {...args} {...MultipleCurrencies.args} />
|
|
174
|
-
</
|
|
174
|
+
</Field>
|
|
175
175
|
</>
|
|
176
176
|
);
|
|
177
177
|
},
|
|
@@ -12,6 +12,6 @@ describe('Select', () => {
|
|
|
12
12
|
<Select options={options} selected={options[0]} onChange={() => {}} />
|
|
13
13
|
</Field>,
|
|
14
14
|
);
|
|
15
|
-
expect(screen.getByLabelText(
|
|
15
|
+
expect(screen.getByLabelText(/Currency/)).toHaveTextContent('USD');
|
|
16
16
|
});
|
|
17
17
|
});
|
|
@@ -89,6 +89,6 @@ describe('Switch', () => {
|
|
|
89
89
|
<Switch checked onClick={props.onClick} />
|
|
90
90
|
</Field>,
|
|
91
91
|
);
|
|
92
|
-
expect(screen.getByLabelText(
|
|
92
|
+
expect(screen.getByLabelText(/Dark mode/)).toHaveAttribute('role', 'switch');
|
|
93
93
|
});
|
|
94
94
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
|
|
3
3
|
import Switch from './Switch';
|
|
4
|
+
import { Field } from '../field/Field';
|
|
4
5
|
|
|
5
6
|
export default {
|
|
6
7
|
component: Switch,
|
|
@@ -13,16 +14,14 @@ export const Basic = () => {
|
|
|
13
14
|
|
|
14
15
|
return (
|
|
15
16
|
<div className="d-flex flex-column">
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
onClick={() => setCheck1(!checked1)}
|
|
25
|
-
/>
|
|
17
|
+
<Field id="switchId" label="A switch with a label" required>
|
|
18
|
+
<Switch
|
|
19
|
+
checked={checked1}
|
|
20
|
+
className="a-class-name"
|
|
21
|
+
id="switchId"
|
|
22
|
+
onClick={() => setCheck1(!checked1)}
|
|
23
|
+
/>
|
|
24
|
+
</Field>
|
|
26
25
|
|
|
27
26
|
<Switch
|
|
28
27
|
checked={checked2}
|
|
@@ -39,17 +38,16 @@ export const Disabled = () => {
|
|
|
39
38
|
|
|
40
39
|
return (
|
|
41
40
|
<div className="d-flex flex-column">
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
/>
|
|
41
|
+
<Field id="switchId" label="A switch with a label" required>
|
|
42
|
+
<Switch
|
|
43
|
+
checked={checked}
|
|
44
|
+
disabled
|
|
45
|
+
className="a-class-name"
|
|
46
|
+
aria-labelledby="labelID"
|
|
47
|
+
id="switchId"
|
|
48
|
+
onClick={() => setCheck(!checked)}
|
|
49
|
+
/>
|
|
50
|
+
</Field>
|
|
53
51
|
|
|
54
52
|
<Switch
|
|
55
53
|
checked={!checked}
|
package/src/tabs/Tab.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { clsx } from 'clsx';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { forwardRef, useEffect, useRef } from 'react';
|
|
4
|
+
|
|
5
|
+
const Tab = forwardRef(
|
|
6
|
+
(
|
|
7
|
+
{ children, id, disabled, panelId, selected, onKeyDown, onClick, style, focusTab },
|
|
8
|
+
reference,
|
|
9
|
+
) => {
|
|
10
|
+
const firstUpdate = useRef(true);
|
|
11
|
+
|
|
12
|
+
const checkFocus = () => {
|
|
13
|
+
if (firstUpdate.current) {
|
|
14
|
+
firstUpdate.current = false;
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (selected && focusTab) {
|
|
19
|
+
focusTab();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
checkFocus();
|
|
25
|
+
}, [selected]);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<li
|
|
29
|
+
ref={reference}
|
|
30
|
+
className={clsx('tabs__tab', selected ? 'np-text-body-large-bold' : 'np-text-body-large', {
|
|
31
|
+
'tabs__tab--selected': selected,
|
|
32
|
+
clickable: !disabled,
|
|
33
|
+
disabled,
|
|
34
|
+
})}
|
|
35
|
+
role="tab"
|
|
36
|
+
id={id}
|
|
37
|
+
aria-selected={selected ? 'true' : 'false'}
|
|
38
|
+
aria-disabled={disabled ? 'true' : 'false'}
|
|
39
|
+
aria-controls={disabled ? null : panelId}
|
|
40
|
+
tabIndex="0"
|
|
41
|
+
style={style}
|
|
42
|
+
onKeyDown={disabled ? null : onKeyDown}
|
|
43
|
+
onClick={onClick}
|
|
44
|
+
>
|
|
45
|
+
<span className="tabs__tab__content">{children}</span>
|
|
46
|
+
</li>
|
|
47
|
+
);
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
Tab.defaultProps = {
|
|
52
|
+
disabled: false,
|
|
53
|
+
selected: false,
|
|
54
|
+
onClick: null,
|
|
55
|
+
style: null,
|
|
56
|
+
focusTab: null,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
Tab.propTypes = {
|
|
60
|
+
children: PropTypes.node.isRequired,
|
|
61
|
+
disabled: PropTypes.bool,
|
|
62
|
+
selected: PropTypes.bool,
|
|
63
|
+
id: PropTypes.string.isRequired,
|
|
64
|
+
panelId: PropTypes.string.isRequired,
|
|
65
|
+
style: PropTypes.shape({ width: PropTypes.string }),
|
|
66
|
+
focusTab: PropTypes.func,
|
|
67
|
+
onKeyDown: PropTypes.func.isRequired,
|
|
68
|
+
onClick: PropTypes.func,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export default Tab;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
|
|
3
|
+
const TabList = ({ children }) => {
|
|
4
|
+
return (
|
|
5
|
+
<ul className="tabs__tab-list" role="tablist">
|
|
6
|
+
{children}
|
|
7
|
+
</ul>
|
|
8
|
+
);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
TabList.propTypes = {
|
|
12
|
+
children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default TabList;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
|
|
3
|
+
const TabPanel = ({ children, id, tabId, style }) => {
|
|
4
|
+
return (
|
|
5
|
+
<div className="tabs__panel" role="tabpanel" id={id} aria-labelledby={tabId} style={style}>
|
|
6
|
+
{children}
|
|
7
|
+
</div>
|
|
8
|
+
);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
TabPanel.propTypes = {
|
|
12
|
+
children: PropTypes.node.isRequired,
|
|
13
|
+
id: PropTypes.string.isRequired,
|
|
14
|
+
tabId: PropTypes.string.isRequired,
|
|
15
|
+
style: PropTypes.shape({
|
|
16
|
+
width: PropTypes.string.isRequired,
|
|
17
|
+
}).isRequired,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default TabPanel;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable react/forbid-dom-props */
|
|
2
|
+
import { Spring, SpringValue, animated } from '@react-spring/web';
|
|
2
3
|
import { clsx } from 'clsx';
|
|
3
4
|
import clamp from 'lodash.clamp';
|
|
5
|
+
import PropTypes from 'prop-types';
|
|
4
6
|
import { Component, createRef, Fragment } from 'react';
|
|
5
7
|
|
|
6
8
|
import { Size, Width, Direction } from '../common';
|
|
@@ -16,69 +18,19 @@ import {
|
|
|
16
18
|
swipedRightToLeft,
|
|
17
19
|
swipeShouldChangeTab,
|
|
18
20
|
getVelocity,
|
|
19
|
-
Swipe,
|
|
20
21
|
} from './utils';
|
|
21
22
|
|
|
22
23
|
const MIN_INDEX = 0;
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
title: string;
|
|
26
|
-
content: React.ReactNode;
|
|
27
|
-
disabled: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
type TabsTransitionSpacing =
|
|
31
|
-
| 'default'
|
|
32
|
-
| `${Size.EXTRA_SMALL | Size.SMALL | Size.MEDIUM | Size.LARGE}`;
|
|
33
|
-
|
|
34
|
-
export interface TabsProps {
|
|
35
|
-
tabs: TabItem[];
|
|
36
|
-
selected: number;
|
|
37
|
-
name: string;
|
|
38
|
-
animatePanelsOnClick?: boolean;
|
|
39
|
-
changeTabOnSwipe?: boolean;
|
|
40
|
-
className?: string;
|
|
41
|
-
transitionSpacing?: TabsTransitionSpacing;
|
|
42
|
-
headerWidth?: `${Width}`;
|
|
43
|
-
id?: string;
|
|
44
|
-
onTabSelect: (index: number) => void;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
interface TabsState {
|
|
48
|
-
start: Swipe | null;
|
|
49
|
-
translateX: number;
|
|
50
|
-
translateFrom: number;
|
|
51
|
-
translateTo: number;
|
|
52
|
-
translateLineX: string | null;
|
|
53
|
-
isAnimating: boolean;
|
|
54
|
-
isSwiping: boolean;
|
|
55
|
-
isScrolling: boolean;
|
|
56
|
-
lastSwipeVelocity: number;
|
|
57
|
-
fullWidthTabs: boolean;
|
|
58
|
-
currentSwipe: Swipe[];
|
|
59
|
-
selectedTabIndex: number;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const SpacerWidth = {
|
|
63
|
-
default: 0,
|
|
64
|
-
xs: 8,
|
|
65
|
-
sm: 16,
|
|
66
|
-
md: 24,
|
|
67
|
-
lg: 32,
|
|
68
|
-
} satisfies Record<TabsTransitionSpacing, number>;
|
|
25
|
+
const enabledTabsFilter = (tab) => !tab.disabled;
|
|
69
26
|
|
|
70
|
-
|
|
71
|
-
declare props: TabsProps & Required<Pick<TabsProps, keyof typeof Tabs.defaultProps>>;
|
|
27
|
+
const SpacerWidth = { default: 0, xs: 8, sm: 16, md: 24, lg: 32 };
|
|
72
28
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
headerWidth: Width.BLOCK,
|
|
77
|
-
} satisfies Partial<TabsProps>;
|
|
29
|
+
class Tabs extends Component {
|
|
30
|
+
/** @type {React.RefObject<HTMLDivElement | null>} */
|
|
31
|
+
containerReference = createRef();
|
|
78
32
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
constructor(props: Tabs['props']) {
|
|
33
|
+
constructor(props) {
|
|
82
34
|
super(props);
|
|
83
35
|
this.state = {
|
|
84
36
|
start: null,
|
|
@@ -91,19 +43,17 @@ export default class Tabs extends Component<TabsProps, TabsState> {
|
|
|
91
43
|
isScrolling: false,
|
|
92
44
|
lastSwipeVelocity: 0,
|
|
93
45
|
fullWidthTabs: props.headerWidth === Width.BLOCK,
|
|
94
|
-
currentSwipe: [],
|
|
95
|
-
selectedTabIndex: props.selected,
|
|
96
46
|
};
|
|
97
47
|
}
|
|
98
48
|
|
|
99
|
-
container
|
|
49
|
+
container = null;
|
|
100
50
|
|
|
101
51
|
containerWidth = 0;
|
|
102
52
|
|
|
103
|
-
tabRefs
|
|
53
|
+
tabRefs = [];
|
|
104
54
|
|
|
105
55
|
get filteredTabsLength() {
|
|
106
|
-
return this.props.tabs.filter(
|
|
56
|
+
return this.props.tabs.filter(enabledTabsFilter).length;
|
|
107
57
|
}
|
|
108
58
|
|
|
109
59
|
get MAX_INDEX() {
|
|
@@ -121,17 +71,17 @@ export default class Tabs extends Component<TabsProps, TabsState> {
|
|
|
121
71
|
window.addEventListener('resize', this.handleResize);
|
|
122
72
|
}
|
|
123
73
|
|
|
124
|
-
componentDidUpdate(previousProps
|
|
74
|
+
componentDidUpdate(previousProps, previousState) {
|
|
125
75
|
const currentSelected = this.props.selected;
|
|
126
76
|
const previousSelected = previousProps.selected;
|
|
127
77
|
const currentSelectedTab = this.props.tabs[currentSelected];
|
|
128
|
-
const currentSelectedTabIsDisabled = currentSelectedTab
|
|
78
|
+
const currentSelectedTabIsDisabled = currentSelectedTab && currentSelectedTab.disabled;
|
|
129
79
|
const previousSelectedTab = previousProps.tabs[previousSelected];
|
|
130
|
-
const previousSelectedTabIsDisabled = previousSelectedTab
|
|
80
|
+
const previousSelectedTabIsDisabled = previousSelectedTab && previousSelectedTab.disabled;
|
|
131
81
|
const currentTabsLength = this.props.tabs.length;
|
|
132
82
|
const previousTabsLength = previousProps.tabs.length;
|
|
133
|
-
const currentDisabledTabsLength = this.props.tabs.filter(
|
|
134
|
-
const previousDisabledTabsLength = previousProps.tabs.filter(
|
|
83
|
+
const currentDisabledTabsLength = this.props.tabs.filter(enabledTabsFilter).length;
|
|
84
|
+
const previousDisabledTabsLength = previousProps.tabs.filter(enabledTabsFilter).length;
|
|
135
85
|
const currentHeaderWidth = this.props.headerWidth;
|
|
136
86
|
const previousFullHeaderWidth = previousProps.headerWidth;
|
|
137
87
|
const { animatePanelsOnClick } = this.props;
|
|
@@ -170,12 +120,12 @@ export default class Tabs extends Component<TabsProps, TabsState> {
|
|
|
170
120
|
}));
|
|
171
121
|
};
|
|
172
122
|
|
|
173
|
-
setContainerRefAndWidth = (node
|
|
123
|
+
setContainerRefAndWidth = (node) => {
|
|
174
124
|
this.container = node;
|
|
175
125
|
this.setContainerWidth(node);
|
|
176
126
|
};
|
|
177
127
|
|
|
178
|
-
setContainerWidth = (node
|
|
128
|
+
setContainerWidth = (node) => {
|
|
179
129
|
if (!node) {
|
|
180
130
|
return;
|
|
181
131
|
}
|
|
@@ -185,10 +135,10 @@ export default class Tabs extends Component<TabsProps, TabsState> {
|
|
|
185
135
|
this.containerWidth = width;
|
|
186
136
|
};
|
|
187
137
|
|
|
188
|
-
isTabDisabled = (index
|
|
138
|
+
isTabDisabled = (index) => {
|
|
189
139
|
const { tabs } = this.props;
|
|
190
140
|
|
|
191
|
-
return tabs[index]
|
|
141
|
+
return tabs[index] && tabs[index].disabled;
|
|
192
142
|
};
|
|
193
143
|
|
|
194
144
|
getAllTabsWidth = () => {
|
|
@@ -199,7 +149,7 @@ export default class Tabs extends Component<TabsProps, TabsState> {
|
|
|
199
149
|
.reduce((a, b) => a + b, 0);
|
|
200
150
|
};
|
|
201
151
|
|
|
202
|
-
getDistanceToSelectedTab = (selectedTabIndex
|
|
152
|
+
getDistanceToSelectedTab = (selectedTabIndex) => {
|
|
203
153
|
return this.tabRefs
|
|
204
154
|
.filter((_, idx) => idx < selectedTabIndex)
|
|
205
155
|
.map((reference) => (reference ? reference.getBoundingClientRect().width : 0))
|
|
@@ -240,7 +190,7 @@ export default class Tabs extends Component<TabsProps, TabsState> {
|
|
|
240
190
|
* Gets the next tab that should be selected based on the swipe direction
|
|
241
191
|
* and the current selected tab (is called recursively to account for disabled tabs).
|
|
242
192
|
*/
|
|
243
|
-
getTabToSelect = (selected
|
|
193
|
+
getTabToSelect = (selected, start, end) => {
|
|
244
194
|
let nextSelected = selected;
|
|
245
195
|
|
|
246
196
|
if (swipedLeftToRight(start, end)) {
|
|
@@ -270,19 +220,9 @@ export default class Tabs extends Component<TabsProps, TabsState> {
|
|
|
270
220
|
return nextSelected;
|
|
271
221
|
};
|
|
272
222
|
|
|
273
|
-
swipedOverHalfOfContainer = (difference
|
|
274
|
-
|
|
275
|
-
calculateApplicableDragDifference = ({
|
|
276
|
-
currentSelected,
|
|
277
|
-
nextSelected,
|
|
278
|
-
start,
|
|
279
|
-
end,
|
|
280
|
-
}: {
|
|
281
|
-
currentSelected: number;
|
|
282
|
-
nextSelected: number;
|
|
283
|
-
start: Swipe;
|
|
284
|
-
end: Swipe;
|
|
285
|
-
}) => {
|
|
223
|
+
swipedOverHalfOfContainer = (difference) => difference / this.containerWidth >= 0.5;
|
|
224
|
+
|
|
225
|
+
calculateApplicableDragDifference = ({ currentSelected, nextSelected, start, end }) => {
|
|
286
226
|
const difference = getSwipeDifference(start, end);
|
|
287
227
|
const elasticDrag = getElasticDragDifference(difference);
|
|
288
228
|
|
|
@@ -302,22 +242,22 @@ export default class Tabs extends Component<TabsProps, TabsState> {
|
|
|
302
242
|
return false;
|
|
303
243
|
};
|
|
304
244
|
|
|
305
|
-
switchTab = (index
|
|
245
|
+
switchTab = (index) => {
|
|
306
246
|
const { onTabSelect } = this.props;
|
|
307
247
|
onTabSelect(index);
|
|
308
248
|
};
|
|
309
249
|
|
|
310
|
-
getTabIndexWithoutDisabledTabs(index
|
|
311
|
-
return index - this.props.tabs.slice(0, index).filter((tab) => tab
|
|
250
|
+
getTabIndexWithoutDisabledTabs(index) {
|
|
251
|
+
return index - this.props.tabs.slice(0, index).filter((tab) => !enabledTabsFilter(tab)).length;
|
|
312
252
|
}
|
|
313
253
|
|
|
314
|
-
animateToTab = (index
|
|
254
|
+
animateToTab = (index, instant) => {
|
|
315
255
|
this.animateLine(index);
|
|
316
256
|
|
|
317
257
|
this.animatePanel(this.getTabIndexWithoutDisabledTabs(index), instant);
|
|
318
258
|
};
|
|
319
259
|
|
|
320
|
-
animateLine = (index
|
|
260
|
+
animateLine = (index) => {
|
|
321
261
|
this.setState((previousState) => ({
|
|
322
262
|
translateLineX: previousState.fullWidthTabs
|
|
323
263
|
? `${index * 100}%`
|
|
@@ -326,7 +266,7 @@ export default class Tabs extends Component<TabsProps, TabsState> {
|
|
|
326
266
|
};
|
|
327
267
|
|
|
328
268
|
// Pass `instant` to set the `translateX` to the new panel with no transition
|
|
329
|
-
animatePanel = (index
|
|
269
|
+
animatePanel = (index, instant = false) => {
|
|
330
270
|
const { translateTo: currentTranslateTo } = this.state;
|
|
331
271
|
|
|
332
272
|
const translateFrom = currentTranslateTo;
|
|
@@ -340,7 +280,7 @@ export default class Tabs extends Component<TabsProps, TabsState> {
|
|
|
340
280
|
});
|
|
341
281
|
};
|
|
342
282
|
|
|
343
|
-
disableScroll = (event
|
|
283
|
+
disableScroll = (event) => {
|
|
344
284
|
const { isSwiping } = this.state;
|
|
345
285
|
|
|
346
286
|
if (isSwiping) {
|
|
@@ -348,17 +288,17 @@ export default class Tabs extends Component<TabsProps, TabsState> {
|
|
|
348
288
|
}
|
|
349
289
|
};
|
|
350
290
|
|
|
351
|
-
handleTabClick = (index
|
|
291
|
+
handleTabClick = (index) => () => {
|
|
352
292
|
this.switchTab(index);
|
|
353
293
|
};
|
|
354
294
|
|
|
355
|
-
onKeyDown = (index
|
|
295
|
+
onKeyDown = (index) => (event) => {
|
|
356
296
|
if (event && event.key === 'Enter') {
|
|
357
297
|
this.switchTab(index);
|
|
358
298
|
}
|
|
359
299
|
};
|
|
360
300
|
|
|
361
|
-
handleTouchStart
|
|
301
|
+
handleTouchStart = (event) => {
|
|
362
302
|
const start = {
|
|
363
303
|
x: event.nativeEvent.touches[0].clientX,
|
|
364
304
|
y: event.nativeEvent.touches[0].clientY,
|
|
@@ -372,15 +312,11 @@ export default class Tabs extends Component<TabsProps, TabsState> {
|
|
|
372
312
|
event.persist();
|
|
373
313
|
};
|
|
374
314
|
|
|
375
|
-
handleTouchMove
|
|
315
|
+
handleTouchMove = (event) => {
|
|
376
316
|
const { start } = this.state;
|
|
377
|
-
if (start == null) {
|
|
378
|
-
return;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
317
|
const { selected: currentSelectedFromProps } = this.props;
|
|
382
318
|
const selected = this.getTabIndexWithoutDisabledTabs(currentSelectedFromProps);
|
|
383
|
-
const end
|
|
319
|
+
const end = {
|
|
384
320
|
x: event.nativeEvent.changedTouches[0].clientX,
|
|
385
321
|
y: event.nativeEvent.changedTouches[0].clientY,
|
|
386
322
|
time: Date.now(),
|
|
@@ -427,14 +363,10 @@ export default class Tabs extends Component<TabsProps, TabsState> {
|
|
|
427
363
|
}
|
|
428
364
|
};
|
|
429
365
|
|
|
430
|
-
handleTouchEnd
|
|
366
|
+
handleTouchEnd = (event) => {
|
|
431
367
|
const { start, isSwiping } = this.state;
|
|
432
|
-
if (start == null) {
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
368
|
const { selected } = this.props;
|
|
437
|
-
const end
|
|
369
|
+
const end = {
|
|
438
370
|
x: event.nativeEvent.changedTouches[0].clientX,
|
|
439
371
|
y: event.nativeEvent.changedTouches[0].clientY,
|
|
440
372
|
time: Date.now(),
|
|
@@ -501,7 +433,7 @@ export default class Tabs extends Component<TabsProps, TabsState> {
|
|
|
501
433
|
const sliderWidth = tabsLength * this.containerWidth + spacer * 2;
|
|
502
434
|
|
|
503
435
|
// Uses `props.panelTransitionSpacing` to add a spacer in-between the `TabPanel` you're transitioning to/from
|
|
504
|
-
const Spacer = ({ id }
|
|
436
|
+
const Spacer = ({ id }) =>
|
|
505
437
|
spacer > 0 && (
|
|
506
438
|
<div
|
|
507
439
|
key={id}
|
|
@@ -540,10 +472,10 @@ export default class Tabs extends Component<TabsProps, TabsState> {
|
|
|
540
472
|
disabled={disabled}
|
|
541
473
|
focusTab={() => {
|
|
542
474
|
if (this.containerReference.current?.contains(document.activeElement)) {
|
|
543
|
-
this.tabRefs[index]
|
|
475
|
+
this.tabRefs[index].focus();
|
|
544
476
|
}
|
|
545
477
|
}}
|
|
546
|
-
onClick={disabled ?
|
|
478
|
+
onClick={disabled ? null : this.handleTabClick(index)}
|
|
547
479
|
onKeyDown={this.onKeyDown(index)}
|
|
548
480
|
{...(fullWidthTabs
|
|
549
481
|
? { style: { width: `${(1 / tabs.length) * 100}%` } }
|
|
@@ -632,3 +564,34 @@ export default class Tabs extends Component<TabsProps, TabsState> {
|
|
|
632
564
|
);
|
|
633
565
|
}
|
|
634
566
|
}
|
|
567
|
+
|
|
568
|
+
const SpacerSizes = { ...Size, NONE: 'default' };
|
|
569
|
+
|
|
570
|
+
Tabs.propTypes = {
|
|
571
|
+
tabs: PropTypes.arrayOf(
|
|
572
|
+
PropTypes.shape({
|
|
573
|
+
title: PropTypes.string.isRequired,
|
|
574
|
+
content: PropTypes.node.isRequired,
|
|
575
|
+
disabled: PropTypes.bool.isRequired,
|
|
576
|
+
}),
|
|
577
|
+
).isRequired,
|
|
578
|
+
selected: PropTypes.number.isRequired,
|
|
579
|
+
name: PropTypes.string.isRequired,
|
|
580
|
+
animatePanelsOnClick: PropTypes.bool,
|
|
581
|
+
changeTabOnSwipe: PropTypes.bool,
|
|
582
|
+
className: PropTypes.string,
|
|
583
|
+
transitionSpacing: PropTypes.oneOf(['default', 'xs', 'sm', 'md', 'lg']),
|
|
584
|
+
headerWidth: PropTypes.oneOf(['auto', 'block']),
|
|
585
|
+
id: PropTypes.string,
|
|
586
|
+
onTabSelect: PropTypes.func.isRequired,
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
Tabs.defaultProps = {
|
|
590
|
+
animatePanelsOnClick: false,
|
|
591
|
+
changeTabOnSwipe: true,
|
|
592
|
+
className: '',
|
|
593
|
+
transitionSpacing: SpacerSizes.NONE,
|
|
594
|
+
headerWidth: Width.BLOCK,
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
export default Tabs;
|
|
@@ -97,7 +97,7 @@ export const NoPanelAnimation = () => {
|
|
|
97
97
|
const [selected, setSelected] = useState(0);
|
|
98
98
|
return (
|
|
99
99
|
<Tabs
|
|
100
|
-
|
|
100
|
+
animatePanels={false}
|
|
101
101
|
className="tabs-custom-class"
|
|
102
102
|
name="tabs-docs"
|
|
103
103
|
transitionSpacing={Size.MEDIUM}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Tabs';
|
|
@@ -1,42 +1,36 @@
|
|
|
1
1
|
const MIN_SWIPE_DISTANCE = 50;
|
|
2
2
|
const MIN_SWIPE_VELOCITY = 0.1;
|
|
3
3
|
|
|
4
|
-
const swipedSignificantDistance = (difference
|
|
4
|
+
const swipedSignificantDistance = (difference) => Math.abs(difference) > MIN_SWIPE_DISTANCE;
|
|
5
5
|
|
|
6
|
-
const swipedWithSignificantVelocity = (velocity
|
|
6
|
+
const swipedWithSignificantVelocity = (velocity) => velocity > MIN_SWIPE_VELOCITY;
|
|
7
7
|
|
|
8
|
-
export
|
|
9
|
-
x: number;
|
|
10
|
-
y: number;
|
|
11
|
-
time: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const getSwipeVelocity = (start: Swipe, end: Swipe) => {
|
|
8
|
+
export const getSwipeVelocity = (start, end) => {
|
|
15
9
|
const timePassed = end.time - start.time;
|
|
16
10
|
|
|
17
11
|
return getSwipeDifference(start, end) / timePassed;
|
|
18
12
|
};
|
|
19
13
|
|
|
20
|
-
export const getSwipeDifference = (start
|
|
14
|
+
export const getSwipeDifference = (start, end, axis = 'x') => {
|
|
21
15
|
return Math.abs(start[axis] - end[axis]);
|
|
22
16
|
};
|
|
23
17
|
|
|
24
|
-
export const swipedLeftToRight = (start
|
|
18
|
+
export const swipedLeftToRight = (start, end) => {
|
|
25
19
|
return end.x > start.x;
|
|
26
20
|
};
|
|
27
21
|
|
|
28
|
-
export const swipedRightToLeft = (start
|
|
22
|
+
export const swipedRightToLeft = (start, end) => {
|
|
29
23
|
return start.x > end.x;
|
|
30
24
|
};
|
|
31
25
|
|
|
32
|
-
export const swipeShouldChangeTab = (start
|
|
26
|
+
export const swipeShouldChangeTab = (start, end) => {
|
|
33
27
|
const difference = getSwipeDifference(start, end);
|
|
34
28
|
const velocity = getSwipeVelocity(start, end);
|
|
35
29
|
|
|
36
30
|
return swipedSignificantDistance(difference) && swipedWithSignificantVelocity(velocity);
|
|
37
31
|
};
|
|
38
32
|
|
|
39
|
-
export function getVelocity(coords
|
|
33
|
+
export function getVelocity(coords) {
|
|
40
34
|
try {
|
|
41
35
|
const relevant = coords.slice(Math.max(coords.length - 5, 1));
|
|
42
36
|
const first = relevant[0];
|
|
@@ -53,5 +47,5 @@ export function getVelocity(coords: Swipe[]) {
|
|
|
53
47
|
`1 - Math.E ** (-0.005 * difference)` provides a % value of how far we want to translate (0.005 being the rate)
|
|
54
48
|
`Math.min(150, window.innerWidth / 3)` provides the maximum translate value
|
|
55
49
|
*/
|
|
56
|
-
export const getElasticDragDifference = (difference
|
|
50
|
+
export const getElasticDragDifference = (difference) =>
|
|
57
51
|
Math.min(150, window.innerWidth / 3) * (1 - Math.E ** (-0.005 * difference));
|