Tab components

Summary: _typescript_

Reviewed By: passy

Differential Revision: D16830058

fbshipit-source-id: 22825fdad0924f1f8cdebcbb7a56e0c55ad3b5f3
This commit is contained in:
Daniel Büchele
2019-08-20 03:18:32 -07:00
committed by Facebook Github Bot
parent 00c2a4dd29
commit 56d4a83184
4 changed files with 69 additions and 51 deletions

View File

@@ -57,7 +57,15 @@ module.exports = {
parser: '@typescript-eslint/parser', parser: '@typescript-eslint/parser',
rules: { rules: {
'prettier/prettier': [2, {...prettierConfig, parser: 'typescript'}], 'prettier/prettier': [2, {...prettierConfig, parser: 'typescript'}],
'@typescript-eslint/no-unused-vars': [1, {argsIgnorePattern: '^_'}], '@typescript-eslint/no-unused-vars': [
1,
{
ignoreRestSiblings: true,
varsIgnorePattern: '^_',
argsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
}, },
}, },
], ],

View File

@@ -5,34 +5,39 @@
* @format * @format
*/ */
export default function Tab(props: {| import {WidthProperty} from 'csstype';
export type Props = {
/** /**
* Label of this tab to show in the tab list. * Label of this tab to show in the tab list.
*/ */
label: React$Node, label: React.ReactNode;
/** /**
* Whether this tab is closable. * Whether this tab is closable.
*/ */
closable?: boolean, closable?: boolean;
/** /**
* Whether this tab is hidden. Useful for when you want a tab to be * Whether this tab is hidden. Useful for when you want a tab to be
* inaccessible via the user but you want to manually set the `active` props * inaccessible via the user but you want to manually set the `active` props
* yourself. * yourself.
*/ */
hidden?: boolean, hidden?: boolean;
/** /**
* Whether this tab should always be included in the DOM and have its * Whether this tab should always be included in the DOM and have its
* visibility toggled. * visibility toggled.
*/ */
persist?: boolean, persist?: boolean;
/** /**
* Callback for when tab is closed. * Callback for when tab is closed.
*/ */
onClose?: () => void, onClose?: () => void;
/** /**
* Contents of this tab. * Contents of this tab.
*/ */
children?: React$Node, children?: React.ReactNode;
|}) { width?: WidthProperty<number>;
};
export default function Tab(_props: Props) {
throw new Error("don't render me"); throw new Error("don't render me");
} }

View File

@@ -6,35 +6,39 @@
*/ */
import FlexColumn from './FlexColumn.js'; import FlexColumn from './FlexColumn.js';
import styled from '../styled/index.js'; import styled from 'react-emotion';
import Orderable from './Orderable.tsx'; import Orderable from './Orderable';
import FlexRow from './FlexRow.js'; import FlexRow from './FlexRow.js';
import {colors} from './colors.tsx'; import {colors} from './colors';
import Tab from './Tab.js'; import Tab, {Props as TabProps} from './Tab';
import {WidthProperty} from 'csstype';
import React from 'react';
const TabList = styled(FlexRow)({ const TabList = styled(FlexRow)({
alignItems: 'stretch', alignItems: 'stretch',
}); });
const TabListItem = styled('div')(props => ({ const TabListItem = styled('div')(
backgroundColor: props.active ? colors.light15 : colors.light02, (props: {active?: boolean; width?: WidthProperty<number>}) => ({
borderBottom: '1px solid #dddfe2', backgroundColor: props.active ? colors.light15 : colors.light02,
boxShadow: props.active ? 'inset 0px 0px 3px rgba(0,0,0,0.25)' : 'none', borderBottom: '1px solid #dddfe2',
color: colors.dark80, boxShadow: props.active ? 'inset 0px 0px 3px rgba(0,0,0,0.25)' : 'none',
flex: 1, color: colors.dark80,
fontSize: 13, flex: 1,
lineHeight: '28px', fontSize: 13,
overflow: 'hidden', lineHeight: '28px',
padding: '0 10px', overflow: 'hidden',
position: 'relative', padding: '0 10px',
textAlign: 'center', position: 'relative',
textOverflow: 'ellipsis', textAlign: 'center',
whiteSpace: 'nowrap', textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
'&:hover': { '&:hover': {
backgroundColor: props.active ? colors.light15 : colors.light05, backgroundColor: props.active ? colors.light15 : colors.light05,
}, },
})); }),
);
const TabListAddItem = styled(TabListItem)({ const TabListAddItem = styled(TabListItem)({
borderRight: 'none', borderRight: 'none',
@@ -75,59 +79,59 @@ const TabContent = styled('div')({
/** /**
* A Tabs component. * A Tabs component.
*/ */
export default function Tabs(props: {| export default function Tabs(props: {
/** /**
* Callback for when the active tab has changed. * Callback for when the active tab has changed.
*/ */
onActive?: (key: ?string) => void, onActive?: (key: string | null | undefined) => void;
/** /**
* The key of the default active tab. * The key of the default active tab.
*/ */
defaultActive?: string, defaultActive?: string;
/** /**
* The key of the currently active tab. * The key of the currently active tab.
*/ */
active?: ?string, active?: string | null | undefined;
/** /**
* Tab elements. * Tab elements.
*/ */
children?: Array<React$Element<any>>, children?: React.ReactElement<TabProps>[] | React.ReactElement<TabProps>;
/** /**
* Whether the tabs can be reordered by the user. * Whether the tabs can be reordered by the user.
*/ */
orderable?: boolean, orderable?: boolean;
/** /**
* Callback when the tab order changes. * Callback when the tab order changes.
*/ */
onOrder?: (order: Array<string>) => void, onOrder?: (order: Array<string>) => void;
/** /**
* Order of tabs. * Order of tabs.
*/ */
order?: Array<string>, order?: Array<string>;
/** /**
* Whether to include the contents of every tab in the DOM and just toggle * Whether to include the contents of every tab in the DOM and just toggle
* its visibility. * its visibility.
*/ */
persist?: boolean, persist?: boolean;
/** /**
* Whether to include a button to create additional items. * Whether to include a button to create additional items.
*/ */
newable?: boolean, newable?: boolean;
/** /**
* Callback for when the new button is clicked. * Callback for when the new button is clicked.
*/ */
onNew?: () => void, onNew?: () => void;
/** /**
* Elements to insert before all tabs in the tab list. * Elements to insert before all tabs in the tab list.
*/ */
before?: Array<React$Node>, before?: Array<React.ReactNode>;
/** /**
* Elements to insert after all tabs in the tab list. * Elements to insert after all tabs in the tab list.
*/ */
after?: Array<React$Node>, after?: Array<React.ReactNode>;
|}) { }) {
const {onActive} = props; const {onActive} = props;
const active: ?string = const active: string | undefined =
props.active == null ? props.defaultActive : props.active; props.active == null ? props.defaultActive : props.active;
// array of other components that aren't tabs // array of other components that aren't tabs
@@ -143,8 +147,9 @@ export default function Tabs(props: {|
const tabContents = []; const tabContents = [];
const tabSiblings = []; const tabSiblings = [];
function add(comps) { function add(comps: React.ReactElement | React.ReactElement[]) {
for (const comp of [].concat(comps || [])) { const compsArray: React.ReactElement<TabProps>[] = [].concat(comps || []);
for (const comp of compsArray) {
if (Array.isArray(comp)) { if (Array.isArray(comp)) {
add(comp); add(comp);
continue; continue;
@@ -194,7 +199,7 @@ export default function Tabs(props: {|
onMouseDown={ onMouseDown={
!isActive && !isActive &&
onActive && onActive &&
((event: SyntheticMouseEvent<>) => { ((event: React.MouseEvent) => {
if (event.target !== closeButton) { if (event.target !== closeButton) {
onActive(key); onActive(key);
} }

View File

@@ -67,8 +67,8 @@ export {
} from './components/data-inspector/DataDescription.js'; } from './components/data-inspector/DataDescription.js';
// tabs // tabs
export {default as Tabs} from './components/Tabs.js'; export {default as Tabs} from './components/Tabs.tsx';
export {default as Tab} from './components/Tab.js'; export {default as Tab} from './components/Tab.tsx';
// inputs // inputs
export {default as Input} from './components/Input.js'; export {default as Input} from './components/Input.js';