+ TabViewer

This commit is contained in:
Pogodaanton 2020-06-24 17:33:37 +02:00
parent 9f282388f4
commit 793f051925
4 changed files with 142 additions and 3 deletions

View File

@ -75,7 +75,7 @@ const TabBar: React.ComponentType<TabBarProps> = React.memo(
/**
* Stylesheet for the active tab indicator
*/
const [activeIndicatorStyles, setAactiveIndicatorStyles] = useState<
const [activeIndicatorStyles, setActiveIndicatorStyles] = useState<
React.CSSProperties
>({});
@ -177,15 +177,23 @@ const TabBar: React.ComponentType<TabBarProps> = React.memo(
useEffect(() => {
if (!activeId) return;
const activeItem = items.find(getActiveState);
tabEventEmitter.emit(`${id}-change`, activeItem);
const setNewId = setActiveId;
tabEventEmitter.emit(`${id}-change`, activeItem.id);
/**
* Sends necessary tabbar data to new subscribers.
*/
const subHandler = () => tabEventEmitter.emit(`${id}-subPub`, activeItem);
tabEventEmitter.on(`${id}-sub`, subHandler);
/**
* Programatic change listener.
* Used to change both tabbar and tabcontent state at once.
*/
tabEventEmitter.on(`${id}-update`, setNewId);
return () => {
tabEventEmitter.off(`${id}-sub`, subHandler);
tabEventEmitter.off(`${id}-update`, setNewId);
};
}, [activeId, getActiveState, id, items]);
@ -231,7 +239,7 @@ const TabBar: React.ComponentType<TabBarProps> = React.memo(
width;
}
setAactiveIndicatorStyles({
setActiveIndicatorStyles({
transform: `scaleX(${width}) translateX(${posX}px)`,
});
}

View File

@ -0,0 +1,44 @@
import { TabItem } from "../TabBar/TabBar.props";
import { ManagedClasses } from "@microsoft/fast-jss-manager-react";
/**
* Class name contract for the component
*/
export interface TabViewerClassNameContract {
tabViewer: string;
tabPanel: string;
tabPanel__visible?: string;
}
/**
* Object in which specific content is assigned to a tab
*/
export interface TabPanel {
/**
* The id of the tab the panel is referring
*/
id: TabItem["id"];
/**
* The contents of the panel
*/
children: React.ReactNode | null;
/**
* Is triggered if the panel becomes visible
*/
onLoad?: () => any;
}
/**
* Props for the component
*/
export interface TabViewerProps extends ManagedClasses<TabViewerClassNameContract> {
/**
* Unique identifier used to establish connection with tabbar
*/
id: string;
/**
* The list of tab panels
*/
items: TabPanel[];
}

View File

@ -0,0 +1,85 @@
import React, { useState, useEffect, useCallback } from "react";
import { TabViewerProps, TabViewerClassNameContract, TabPanel } from "./TabViewer.props";
import { DesignSystem } from "@microsoft/fast-components-styles-msft";
import manageJss, { ComponentStyles } from "@microsoft/fast-jss-manager-react";
import tabEventEmitter from "../TabEvents";
import { TabItem } from "../TabBar/TabBar.props";
import { classNames } from "@microsoft/fast-web-utilities";
const styles: ComponentStyles<TabViewerClassNameContract, DesignSystem> = {
tabViewer: {},
tabPanel: {
display: "none",
position: "absolute",
width: "100%",
height: "100%",
},
tabPanel__visible: {
display: "block",
},
};
const TabViewer: React.ComponentType<TabViewerProps> = ({
managedClasses,
items,
id,
...props
}) => {
const [activeId, setActiveId] = useState<string>(null);
/**
* Looks up whether the given tab panel is currently visible or not
*/
const getActiveState = useCallback(
(tabItem: TabPanel, index: number): boolean => {
if (activeId) return activeId === tabItem.id;
return index === 0;
},
[activeId]
);
/**
* Listening to change events from <TabBar/>
*/
useEffect(() => {
// We need to do this in order to cache the function
const changeHandler = (id: TabItem["id"]): void => setActiveId(id);
// Handle initial data
tabEventEmitter.once(`${id}-subPub`, changeHandler);
// Subscribe to change handler
tabEventEmitter.on(`${id}-change`, changeHandler);
// Tell tabbar to broadcast initial data
tabEventEmitter.emit(`${id}-sub`);
return () => {
tabEventEmitter.off(`${id}-subPub`, changeHandler);
tabEventEmitter.off(`${id}-change`, changeHandler);
};
}, [id]);
useEffect(() => {
if (!activeId) return;
const func = items.find(getActiveState)["onLoad"];
if (typeof func === "function") func();
}, [activeId, getActiveState, items]);
return (
<div className={managedClasses.tabViewer}>
{items.map((tabItem, i) => (
<div
key={tabItem.id}
className={classNames(managedClasses.tabPanel, [
managedClasses.tabPanel__visible,
getActiveState(tabItem, i),
])}
>
{tabItem.children}
</div>
))}
</div>
);
};
export default manageJss(styles)(TabViewer);

View File

@ -17,6 +17,7 @@ import useMotionValueFactory from "./Hooks/useMotionValueFactory";
import { useScaleFactor } from "./Hooks/useScaleFactor";
import { ToastManager, toast } from "./Toasts/toast";
import TabBar from "./Tabs/TabBar/TabBar";
import TabViewer from "./Tabs/TabViewer/TabViewer";
export {
Button,
@ -39,4 +40,5 @@ export {
ToastManager,
toast,
TabBar,
TabViewer,
};