diff --git a/src/client/web/src/components/control/tabs.tsx b/src/client/web/src/components/control/tabs.tsx new file mode 100644 index 0000000..1abe089 --- /dev/null +++ b/src/client/web/src/components/control/tabs.tsx @@ -0,0 +1,80 @@ +import * as React from "react"; +import { List, Map, update } from "immutable"; + +import { updater } from "../state_updater"; +import { getIcon } from "../visual/icons"; +import { Flexbox } from "../layout/flexbox"; +import { ICoreState, MsgProps, UIProps } from "../core_state"; +import { AdminProps } from "../pane_admin"; +import { LoginProps } from "../pane_login"; +import { alertMsg } from "../../common/env"; +import { IconProps } from "../visual/icons"; +import { colorClass } from "../visual/colors"; + +const defaultIconProps: IconProps = { + name: "RiFolder2Fill", + size: "1.6rem", + color: `${colorClass("cyan0")}-font`, +}; + +export interface Props { + targetSwitch: string; + tabIcons: Map; // option name -> icon name + login: LoginProps; + admin: AdminProps; + ui: UIProps; + msg: MsgProps; + update?: (updater: (prevState: ICoreState) => ICoreState) => void; +} + +export interface State {} +export class Tabs extends React.Component { + constructor(p: Props) { + super(p); + } + + setTab = (targetSwitch: string, targetOption: string) => { + if (!updater().setControlOption(targetSwitch, targetOption)) { + alertMsg(this.props.msg.pkg.get("op.fail")); + } + }; + + render() { + const displaying = this.props.ui.control.controls.get( + this.props.targetSwitch + ); + const options = this.props.ui.control.options.get(this.props.targetSwitch); + const tabs = options.map((option: string, targetSwitch: string) => { + const iconProps = this.props.tabIcons.has(option) + ? this.props.tabIcons.get(option) + : defaultIconProps; + + // , + const icon = getIcon(iconProps.name, iconProps.size, iconProps.color); + + return ( + + ); + }); + + return ( +
{tabs}
+ ); + } +} diff --git a/src/client/web/src/components/core_state.ts b/src/client/web/src/components/core_state.ts index a547eb9..362932f 100644 --- a/src/client/web/src/components/core_state.ts +++ b/src/client/web/src/components/core_state.ts @@ -11,10 +11,6 @@ import { AdminProps } from "./pane_admin"; import { MsgPackage } from "../i18n/msger"; import { User, MetadataResp } from "../client"; -export interface PanelsProps { - displaying: string; -} - export interface MsgProps { lan: string; pkg: Map; @@ -30,15 +26,18 @@ export interface UIProps { position: string; align: string; }; + control: { + controls: Map; + options: Map>; + }; } export interface ICoreState { - panels: PanelsProps; filesInfo: FilesProps; uploadingsInfo: UploadingsProps; sharingsInfo: SharingsProps; - panes: PanesProps; - login: LoginProps; admin: AdminProps; + login: LoginProps; + panes: PanesProps; ui: UIProps; msg: MsgProps; } @@ -49,9 +48,6 @@ export function newState(): ICoreState { export function initState(): ICoreState { return { - panels: { - displaying: "item", - }, filesInfo: { dirPath: List([]), items: List([]), @@ -112,6 +108,10 @@ export function initState(): ICoreState { position: "", align: "", }, + control: { + controls: Map(), + options: Map>(), + }, }, }; } diff --git a/src/client/web/src/components/state_updater.ts b/src/client/web/src/components/state_updater.ts index 53554ee..0e67414 100644 --- a/src/client/web/src/components/state_updater.ts +++ b/src/client/web/src/components/state_updater.ts @@ -554,20 +554,34 @@ export class Updater { }; setTab = (tabName: string) => { - switch (tabName) { - case "item": - this.props.panels.displaying = tabName; - break; - case "uploading": - this.props.panels.displaying = tabName; - break; - case "sharing": - this.props.panels.displaying = tabName; - break; - default: - this.props.panels.displaying = "item"; - break; + // switch (tabName) { + // case "item": + // this.props.panels.displaying = tabName; + // break; + // case "uploading": + // this.props.panels.displaying = tabName; + // break; + // case "sharing": + // this.props.panels.displaying = tabName; + // break; + // default: + // this.props.panels.displaying = "item"; + // break; + // } + }; + + setControlOption = (controlName: string, option: string): boolean => { + const controlExists = this.props.ui.control.controls.has(controlName); + const options = this.props.ui.control.options.get(controlName); + if (!controlExists || !options.has(option)) { + return false; } + + this.props.ui.control.controls = this.props.ui.control.controls.set( + controlName, + option + ); + return true; }; generateHash = async (filePath: string): Promise => { diff --git a/src/client/web/src/components/visual/colors.tsx b/src/client/web/src/components/visual/colors.tsx new file mode 100644 index 0000000..164818b --- /dev/null +++ b/src/client/web/src/components/visual/colors.tsx @@ -0,0 +1,37 @@ +import { Set, Map } from "immutable"; + +export const colors = Set([ + "blue0", + "blue1", + "cyan0", + "cyan1", + "purple0", + "purple1", + "red0", + "red1", + "yellow0", + "yellow1", + "yellow2", + "yellow3", + "green0", + "green1", + "green2", + "white", + "white0", + "white1", + "grey0", + "grey1", + "grey2", + "grey3", + "black", + "black0", + "black1", +]); + +export function colorClass(name: string): string { + if (!colors.has(name)) { + console.error(`color ${name} not found`); + return colors.get("black"); + } + return colors.get(name); +} diff --git a/src/client/web/src/components/visual/icons.tsx b/src/client/web/src/components/visual/icons.tsx new file mode 100644 index 0000000..8db3e7b --- /dev/null +++ b/src/client/web/src/components/visual/icons.tsx @@ -0,0 +1,50 @@ +import * as React from "react"; +import { Map } from "immutable"; + +import { IconType, IconBaseProps } from "@react-icons/all-files"; +import { RiFolder2Fill } from "@react-icons/all-files/ri/RiFolder2Fill"; +import { RiShareBoxLine } from "@react-icons/all-files/ri/RiShareBoxLine"; +import { RiUploadCloudFill } from "@react-icons/all-files/ri/RiUploadCloudFill"; + +import { colorClass } from "./colors"; + +export interface IconProps { + name: string; + size: string; + color: string; +} + +const icons = Map({ + RiFolder2Fill: RiFolder2Fill, + RiShareBoxLine: RiShareBoxLine, + RiUploadCloudFill: RiUploadCloudFill, +}); + +export function getIconWithProps( + name: string, + props: IconBaseProps +): JSX.Element | null { + const icon = icons.get(name); + if (icon == null) { + throw Error(`icon "${name}" is not found`); + } + + return React.createElement(icon, { ...props }, null); +} + +export function getIcon( + name: string, + size: string, + color: string +): JSX.Element | null { + const icon = icons.get(name); + if (icon == null) { + throw Error(`icon "${name}" is not found`); + } + + return React.createElement( + icon, + { size, className: `${colorClass(color)}-font` }, + null + ); +}