Page MenuHomePhabricator

D3369.id10345.diff
No OneTemporary

D3369.id10345.diff

diff --git a/web/components/tabs-header.js b/web/components/tabs-header.js
new file mode 100644
--- /dev/null
+++ b/web/components/tabs-header.js
@@ -0,0 +1,28 @@
+// @flow
+
+import classnames from 'classnames';
+import * as React from 'react';
+
+import css from './tabs.css';
+
+type Props<T: string> = {
+ +children?: React.Node,
+ +isActive: boolean,
+ +setTab: T => mixed,
+ +id: T,
+};
+
+function TabsHeader<T: string>(props: Props<T>): React.Node {
+ const { children, isActive, setTab, id } = props;
+ const headerClasses = classnames(css.tabHeader, {
+ [css.backgroundTabHeader]: !isActive,
+ });
+ const onClickSetTab = React.useCallback(() => setTab(id), [setTab, id]);
+ return (
+ <div className={headerClasses} onClick={onClickSetTab}>
+ {children}
+ </div>
+ );
+}
+
+export default TabsHeader;
diff --git a/web/components/tabs.css b/web/components/tabs.css
new file mode 100644
--- /dev/null
+++ b/web/components/tabs.css
@@ -0,0 +1,29 @@
+div.tabsContainer {
+ color: var(--fg);
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+}
+div.tabsHeaderContainer {
+ display: flex;
+}
+
+div.tabHeader {
+ flex: 1;
+ padding: 16px;
+ display: flex;
+ justify-content: center;
+ color: var(--tabs-header-active-color);
+ border-bottom: 2px solid var(--tabs-header-active-border);
+}
+
+div.backgroundTabHeader {
+ cursor: pointer;
+ color: var(--tabs-header-background-color);
+ border-bottom-color: var(--tabs-header-background-border);
+}
+
+div.backgroundTabHeader:hover {
+ color: var(--tabs-header-background-color-hover);
+ border-bottom-color: var(--tabs-header-background-border-hover);
+}
diff --git a/web/components/tabs.react.js b/web/components/tabs.react.js
new file mode 100644
--- /dev/null
+++ b/web/components/tabs.react.js
@@ -0,0 +1,58 @@
+// @flow
+
+import * as React from 'react';
+
+import TabsHeader from './tabs-header';
+import css from './tabs.css';
+
+type TabsContainerProps<T: string> = {
+ +children?: React.ChildrenArray<?React.Element<typeof TabsItem>>,
+ +activeTab: T,
+ +setTab: T => mixed,
+};
+
+function TabsContainer<T: string>(props: TabsContainerProps<T>): React.Node {
+ const { children, activeTab, setTab } = props;
+
+ const headers = React.Children.map(children, tab => {
+ const { id, header } = tab.props;
+
+ const isActive = id === activeTab;
+ return (
+ <TabsHeader id={id} isActive={isActive} setTab={setTab}>
+ {header}
+ </TabsHeader>
+ );
+ });
+
+ const currentTab = React.Children.toArray(children).find(
+ tab => tab.props.id === activeTab,
+ );
+
+ const currentContent = currentTab ? currentTab.props.children : null;
+
+ return (
+ <div className={css.tabsContainer}>
+ <div className={css.tabsHeaderContainer}>{headers}</div>
+ {currentContent}
+ </div>
+ );
+}
+
+type TabsItemProps<T: string> = {
+ +children: React.Node,
+ +id: T,
+ +header: React.Node,
+};
+
+function TabsItem<T: string>(props: TabsItemProps<T>): React.Node {
+ const { children } = props;
+ return children;
+}
+
+const Tabs = {
+ Container: TabsContainer,
+ Item: TabsItem,
+};
+
+export default Tabs;
diff --git a/web/theme.css b/web/theme.css
--- a/web/theme.css
+++ b/web/theme.css
@@ -118,4 +118,10 @@
--search-input-color: var(--shades-white-100);
--search-input-placeholder: var(--shades-black-60);
--search-icon-color: var(--shades-black-60);
+ --tabs-header-active-color: var(--shades-white-100);
+ --tabs-header-active-border: var(--violet-light-100);
+ --tabs-header-background-color: var(--shades-black-60);
+ --tabs-header-background-border: var(--shades-black-80);
+ --tabs-header-background-color-hover: var(--shades-white-80);
+ --tabs-header-background-border-hover: var(--shades-black-70);
}

File Metadata

Mime Type
text/plain
Expires
Sun, Nov 17, 7:31 PM (20 h, 47 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2531481
Default Alt Text
D3369.id10345.diff (3 KB)

Event Timeline