feat(components): add control and visual components
This commit is contained in:
parent
5f0d8e2737
commit
7156c22a56
5 changed files with 204 additions and 23 deletions
80
src/client/web/src/components/control/tabs.tsx
Normal file
80
src/client/web/src/components/control/tabs.tsx
Normal file
|
@ -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<string, IconProps>; // 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<Props, State, {}> {
|
||||||
|
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;
|
||||||
|
|
||||||
|
// <RiFolder2Fill size="1.6rem" className="margin-r-s cyan0-font" />,
|
||||||
|
const icon = getIcon(iconProps.name, iconProps.size, iconProps.color);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={`${targetSwitch}-${option}`}
|
||||||
|
onClick={() => {
|
||||||
|
this.setTab(targetSwitch, option);
|
||||||
|
}}
|
||||||
|
className="float"
|
||||||
|
>
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<span className="margin-r-s">{icon}</span>,
|
||||||
|
<span>
|
||||||
|
{this.props.msg.pkg.get(`switch.${targetSwitch}.${option}`)}
|
||||||
|
</span>,
|
||||||
|
])}
|
||||||
|
childrenStyles={List([{ flex: "30%" }, { flex: "70%" }])}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`tabs control-${this.props.targetSwitch}`}>{tabs}</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,10 +11,6 @@ import { AdminProps } from "./pane_admin";
|
||||||
import { MsgPackage } from "../i18n/msger";
|
import { MsgPackage } from "../i18n/msger";
|
||||||
import { User, MetadataResp } from "../client";
|
import { User, MetadataResp } from "../client";
|
||||||
|
|
||||||
export interface PanelsProps {
|
|
||||||
displaying: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MsgProps {
|
export interface MsgProps {
|
||||||
lan: string;
|
lan: string;
|
||||||
pkg: Map<string, string>;
|
pkg: Map<string, string>;
|
||||||
|
@ -30,15 +26,18 @@ export interface UIProps {
|
||||||
position: string;
|
position: string;
|
||||||
align: string;
|
align: string;
|
||||||
};
|
};
|
||||||
|
control: {
|
||||||
|
controls: Map<string, string>;
|
||||||
|
options: Map<string, Set<string>>;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
export interface ICoreState {
|
export interface ICoreState {
|
||||||
panels: PanelsProps;
|
|
||||||
filesInfo: FilesProps;
|
filesInfo: FilesProps;
|
||||||
uploadingsInfo: UploadingsProps;
|
uploadingsInfo: UploadingsProps;
|
||||||
sharingsInfo: SharingsProps;
|
sharingsInfo: SharingsProps;
|
||||||
panes: PanesProps;
|
|
||||||
login: LoginProps;
|
|
||||||
admin: AdminProps;
|
admin: AdminProps;
|
||||||
|
login: LoginProps;
|
||||||
|
panes: PanesProps;
|
||||||
ui: UIProps;
|
ui: UIProps;
|
||||||
msg: MsgProps;
|
msg: MsgProps;
|
||||||
}
|
}
|
||||||
|
@ -49,9 +48,6 @@ export function newState(): ICoreState {
|
||||||
|
|
||||||
export function initState(): ICoreState {
|
export function initState(): ICoreState {
|
||||||
return {
|
return {
|
||||||
panels: {
|
|
||||||
displaying: "item",
|
|
||||||
},
|
|
||||||
filesInfo: {
|
filesInfo: {
|
||||||
dirPath: List<string>([]),
|
dirPath: List<string>([]),
|
||||||
items: List<MetadataResp>([]),
|
items: List<MetadataResp>([]),
|
||||||
|
@ -112,6 +108,10 @@ export function initState(): ICoreState {
|
||||||
position: "",
|
position: "",
|
||||||
align: "",
|
align: "",
|
||||||
},
|
},
|
||||||
|
control: {
|
||||||
|
controls: Map<string, string>(),
|
||||||
|
options: Map<string, Set<string>>(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -554,20 +554,34 @@ export class Updater {
|
||||||
};
|
};
|
||||||
|
|
||||||
setTab = (tabName: string) => {
|
setTab = (tabName: string) => {
|
||||||
switch (tabName) {
|
// switch (tabName) {
|
||||||
case "item":
|
// case "item":
|
||||||
this.props.panels.displaying = tabName;
|
// this.props.panels.displaying = tabName;
|
||||||
break;
|
// break;
|
||||||
case "uploading":
|
// case "uploading":
|
||||||
this.props.panels.displaying = tabName;
|
// this.props.panels.displaying = tabName;
|
||||||
break;
|
// break;
|
||||||
case "sharing":
|
// case "sharing":
|
||||||
this.props.panels.displaying = tabName;
|
// this.props.panels.displaying = tabName;
|
||||||
break;
|
// break;
|
||||||
default:
|
// default:
|
||||||
this.props.panels.displaying = "item";
|
// this.props.panels.displaying = "item";
|
||||||
break;
|
// 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<boolean> => {
|
generateHash = async (filePath: string): Promise<boolean> => {
|
||||||
|
|
37
src/client/web/src/components/visual/colors.tsx
Normal file
37
src/client/web/src/components/visual/colors.tsx
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { Set, Map } from "immutable";
|
||||||
|
|
||||||
|
export const colors = Set<string>([
|
||||||
|
"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);
|
||||||
|
}
|
50
src/client/web/src/components/visual/icons.tsx
Normal file
50
src/client/web/src/components/visual/icons.tsx
Normal file
|
@ -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<string, IconType>({
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue