progress
This commit is contained in:
parent
c17515d571
commit
0c03906518
25 changed files with 475 additions and 201 deletions
|
@ -51,4 +51,10 @@ export const ArrayExtensions = () => {
|
|||
return this.indexOf(element) !== -1;
|
||||
};
|
||||
}
|
||||
if ([].repeat === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Array.prototype.repeat = function (quantity) {
|
||||
return Array(quantity).fill(this[0]);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ declare global {
|
|||
isEmpty(): boolean;
|
||||
isNotEmpty(): boolean;
|
||||
hasIncludeElement(element: T): boolean;
|
||||
repeat(quantity: number): Array<T>;
|
||||
}
|
||||
interface Number {
|
||||
fromArray(): number[];
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
import { Spin } from "antd";
|
||||
import { LoadingOutlined } from "@ant-design/icons";
|
||||
import poseIMG from "../../assets/images/pose_estemation.jpg";
|
||||
import { Icon } from "../icons/icons";
|
||||
import { CoreText, CoreTextType } from "../text/text";
|
||||
import { CoreButton } from "../button/button";
|
||||
|
||||
export const enum CardDataSetType {
|
||||
EMPTY = "EMPTY",
|
||||
COMPLETED = "COMPLETED",
|
||||
}
|
||||
|
||||
interface ICardDataSetProps {
|
||||
type: CardDataSetType;
|
||||
neuralNetworkAction?: string;
|
||||
neuralNetworkName?: string;
|
||||
objects?: string[];
|
||||
unixDate?: number;
|
||||
processStatus?: string;
|
||||
onClickButton?: (id: string) => void;
|
||||
onClickEmptyCard?: Function;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export const CardDataSet = (props: ICardDataSetProps) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: 272,
|
||||
height: 372,
|
||||
borderRadius: 12,
|
||||
border: "1px solid #CAC4D0",
|
||||
backgroundColor: "rgba(254, 247, 255, 1)",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
if (props.type.isEqual(CardDataSetType.EMPTY) && props.onClickEmptyCard) {
|
||||
props.onClickEmptyCard();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div style={{ height: 80 }}>
|
||||
{props.type.isEqual(CardDataSetType.EMPTY) ? null : (
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
||||
<div style={{ width: 70, marginTop: 11 }}></div>
|
||||
<div style={{ height: 80, alignContent: "center", marginRight: 40 }}>
|
||||
<CoreText text={props.neuralNetworkName ?? ""} type={CoreTextType.header} />
|
||||
<CoreText text={props.neuralNetworkAction ?? ""} type={CoreTextType.medium} />
|
||||
</div>
|
||||
<div>
|
||||
<Icon type="Settings" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<img alt="pose" style={{ width: "100%" }} src={poseIMG}></img>
|
||||
<div
|
||||
style={{
|
||||
textAlignLast: props.type.isEqual(CardDataSetType.EMPTY) ? "center" : "auto",
|
||||
marginTop: props.type.isEqual(CardDataSetType.EMPTY) ? 80 : 10,
|
||||
}}
|
||||
>
|
||||
{props.type === CardDataSetType.EMPTY ? (
|
||||
<Icon type="PlusCircle" />
|
||||
) : (
|
||||
<div style={{ margin: 10 }}>
|
||||
<CoreText text={`Объектов: ${props.objects?.length ?? 0}`} type={CoreTextType.large} />
|
||||
<CoreText text={Number(props.unixDate).unixFromDate()} type={CoreTextType.medium} color="#49454F" />
|
||||
<div style={{ height: 40 }} />
|
||||
<div style={{ width: 240, overflow: "hidden", whiteSpace: "nowrap", height: 40 }}>
|
||||
<CoreText text={props.objects?.join(", ") ?? ""} type={CoreTextType.medium} color="#49454F" />
|
||||
</div>
|
||||
<div style={{ display: "flex", flexDirection: "row", justifyContent: "flex-end", alignItems: "center" }}>
|
||||
{props.processStatus === "exec" ? (
|
||||
<Spin indicator={<LoadingOutlined style={{ fontSize: 34, color: "rgba(103, 80, 164, 1)" }} spin />} />
|
||||
) : null}
|
||||
<div style={{ width: 20 }} />
|
||||
{props.processStatus === "new" ? (
|
||||
<CoreButton
|
||||
onClick={() => {
|
||||
if (props.type.isEqual(CardDataSetType.COMPLETED) && props.onClickButton && props.id) {
|
||||
props.onClickButton(props.id);
|
||||
}
|
||||
}}
|
||||
filled={true}
|
||||
text="Старт"
|
||||
/>
|
||||
) : (
|
||||
<CoreButton text="Завершено" block={true} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,9 +1,12 @@
|
|||
import * as React from "react";
|
||||
import { Input, Select, Button } from "antd";
|
||||
import { InputBuilderViewModel, InputType } from "./form_view_model";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { FormBuilderStore } from "./form_builder_store";
|
||||
import { FormBuilderValidationModel } from "../../../features/dataset/dataset_model";
|
||||
import { SelectCore } from "../select/select";
|
||||
import { CoreInput } from "../input/input";
|
||||
import { Icon } from "../icons/icons";
|
||||
import { CoreText, CoreTextType } from "../text/text";
|
||||
|
||||
export interface IFormBuilder {
|
||||
context: string;
|
||||
|
@ -28,107 +31,107 @@ export const FormBuilder = observer((props: IFormBuilder) => {
|
|||
{store.formViewModel?.inputs.map((element) => {
|
||||
if (element.type.isEqual(InputType.ENUM)) {
|
||||
const values = element.values as string[];
|
||||
console.log(element.totalValue);
|
||||
return (
|
||||
<>
|
||||
<div>{element.name}</div>
|
||||
|
||||
<Select
|
||||
defaultValue={element.defaultValue}
|
||||
style={{ width: 120 }}
|
||||
onChange={(e) => {
|
||||
store.changeTotalValue(element.id, e);
|
||||
}}
|
||||
options={values?.map((el) => {
|
||||
return { value: el };
|
||||
})}
|
||||
/>
|
||||
</>
|
||||
<SelectCore
|
||||
items={values}
|
||||
value={element.totalValue ?? element.defaultValue}
|
||||
onChange={(value) => store.changeTotalValue(element.id, value)}
|
||||
label={element.name}
|
||||
style={{ margin: 10 }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (element.type.isEqual(InputType.ARRAY)) {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ fontSize: "large" }}>{element.name}</div>
|
||||
<div style={{ border: "1px black solid", margin: 10 }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
margin: 10,
|
||||
alignItems: "center",
|
||||
paddingRight: 10,
|
||||
}}
|
||||
onClick={() => {
|
||||
store.open(element.id);
|
||||
}}
|
||||
>
|
||||
<CoreText text={element.name} type={CoreTextType.large} />
|
||||
<Icon type="PlusCircle" style={{ width: 22 }} />
|
||||
</div>
|
||||
|
||||
{element.isOpen ? (
|
||||
<div>
|
||||
<div style={{ margin: 10 }}>
|
||||
{element.totalValue instanceof Array
|
||||
? element.totalValue?.map((subArray, index) => {
|
||||
return (
|
||||
<>
|
||||
<div style={{ paddingLeft: "10px" }}>
|
||||
index: {index}
|
||||
<Button onClick={() => store.deleteTotalValueSubItem(element.id, index)}>
|
||||
Delete item
|
||||
</Button>
|
||||
<div style={{ margin: 10 }}>
|
||||
<div style={{ display: "flex" }}>
|
||||
<CoreText text={(element.subType ?? "") + ` ${index}`} type={CoreTextType.medium} />
|
||||
<Icon
|
||||
style={{ paddingLeft: 10 }}
|
||||
type="DeleteCircle"
|
||||
onClick={() => store.deleteTotalValueSubItem(element.id, index)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{subArray.map((subSubArrayItem: InputBuilderViewModel, subIndex: number) => {
|
||||
if (subSubArrayItem.type.isEqual(InputType.ENUM)) {
|
||||
return (
|
||||
<>
|
||||
<div>{subSubArrayItem.name}</div>
|
||||
|
||||
<Select
|
||||
defaultValue={subSubArrayItem.defaultValue}
|
||||
style={{ width: 120 }}
|
||||
onChange={(e) => {
|
||||
store.changeTotalSubValue(element.id, subIndex, e);
|
||||
}}
|
||||
options={subSubArrayItem.values?.map((el) => {
|
||||
return { value: el };
|
||||
})}
|
||||
<SelectCore
|
||||
items={subSubArrayItem.values?.map((el) => String(el)) ?? []}
|
||||
value={subSubArrayItem.totalValue ?? subSubArrayItem.defaultValue}
|
||||
onChange={(value) => store.changeTotalSubValue(element.id, subIndex, value)}
|
||||
label={element.name}
|
||||
style={{ margin: 5 }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (subSubArrayItem.type.isEqualMany([InputType.NUMBER, InputType.STRING]))
|
||||
return (
|
||||
<div style={{ width: "200px" }}>
|
||||
<div>{subSubArrayItem.name}</div>
|
||||
<Input
|
||||
<div>
|
||||
<CoreInput
|
||||
style={{ margin: 5 }}
|
||||
onChange={(e) => {
|
||||
store.changeTotalSubValue(element.id, subIndex, e.target.value);
|
||||
store.changeTotalSubValue(element.id, subIndex, e);
|
||||
}}
|
||||
defaultValue={subSubArrayItem.defaultValue}
|
||||
value={subSubArrayItem.defaultValue}
|
||||
label={subSubArrayItem.name}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return <>Error</>;
|
||||
})}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</div>
|
||||
) : null}
|
||||
<div
|
||||
onClick={() => {
|
||||
store.open(element.id);
|
||||
}}
|
||||
>
|
||||
+
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (element.type.isEqualMany([InputType.NUMBER, InputType.STRING]))
|
||||
return (
|
||||
<div style={{ width: "200px" }}>
|
||||
<div>{element.name}</div>
|
||||
<Input
|
||||
<div>
|
||||
<CoreInput
|
||||
onChange={(e) => {
|
||||
store.changeTotalValue(element.id, e.target.value);
|
||||
store.changeTotalValue(element.id, e);
|
||||
}}
|
||||
defaultValue={element.defaultValue}
|
||||
value={element.defaultValue}
|
||||
label={element.name}
|
||||
style={{ margin: 10 }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return <>Error</>;
|
||||
})}
|
||||
<Button onClick={() => store.saveForm()}>SAVE FORM</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -4,16 +4,31 @@ import { Result } from "../../helper/result";
|
|||
export interface IIconsProps {
|
||||
type: string;
|
||||
style?: React.CSSProperties;
|
||||
onClick?: Function;
|
||||
}
|
||||
|
||||
export function Icon(props: IIconsProps) {
|
||||
const icon = getIconSvg(props.type);
|
||||
return icon.fold(
|
||||
(node) => {
|
||||
return <div style={props.style}>{node}</div>;
|
||||
return (
|
||||
<div
|
||||
onClick={() => {
|
||||
if (props.onClick) props.onClick();
|
||||
}}
|
||||
style={props.style}
|
||||
>
|
||||
{node}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<div style={props.style}>
|
||||
<div
|
||||
onClick={() => {
|
||||
if (props.onClick) props.onClick();
|
||||
}}
|
||||
style={props.style}
|
||||
>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
|
@ -28,6 +43,23 @@ export function Icon(props: IIconsProps) {
|
|||
}
|
||||
const getIconSvg = (type: string): Result<undefined, React.JSX.Element> => {
|
||||
switch (type) {
|
||||
case "DeleteCircle":
|
||||
return Result.ok(
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M10 0C4.47 0 0 4.47 0 10C0 15.53 4.47 20 10 20C15.53 20 20 15.53 20 10C20 4.47 15.53 0 10 0ZM10 18C5.59 18 2 14.41 2 10C2 5.59 5.59 2 10 2C14.41 2 18 5.59 18 10C18 14.41 14.41 18 10 18ZM10 8.59L13.59 5L15 6.41L11.41 10L15 13.59L13.59 15L10 11.41L6.41 15L5 13.59L8.59 10L5 6.41L6.41 5L10 8.59Z"
|
||||
fill="#49454F"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case "Check":
|
||||
return Result.ok(
|
||||
<svg width="12" height="10" viewBox="0 0 12 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 9.4L0 5.4L1.4 4L4 6.6L10.6 0L12 1.4L4 9.4Z" fill="white" />
|
||||
</svg>
|
||||
);
|
||||
case "PlusCircle":
|
||||
return Result.ok(
|
||||
<svg width="33" height="33" viewBox="0 0 33 33" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
|
|
38
ui/src/core/ui/input/input.tsx
Normal file
38
ui/src/core/ui/input/input.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { CoreText, CoreTextType } from "../text/text";
|
||||
|
||||
interface IInputProps {
|
||||
label: string;
|
||||
value?: string;
|
||||
onChange?: (value: string) => void;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export const CoreInput = (props: IInputProps) => {
|
||||
return (
|
||||
<div
|
||||
style={Object.assign(
|
||||
{
|
||||
backgroundColor: "rgba(230, 224, 233, 1)",
|
||||
height: 58,
|
||||
borderRadius: " 4px 4px 0px 0px",
|
||||
borderBottom: "solid 1px black",
|
||||
padding: "10px 10px 10px 10px",
|
||||
},
|
||||
props.style
|
||||
)}
|
||||
>
|
||||
<CoreText type={CoreTextType.small} text={props.label} />
|
||||
|
||||
<div
|
||||
onChange={(event) => {
|
||||
console.log(event);
|
||||
}}
|
||||
onInput={(e) => {
|
||||
if (props.onChange && e.currentTarget.textContent) props.onChange(e.currentTarget.textContent);
|
||||
}}
|
||||
style={{ fontSize: 16, fontFamily: "Roboto", color: "#1D1B20", height: 24 }}
|
||||
contentEditable="true"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
67
ui/src/core/ui/select/select.tsx
Normal file
67
ui/src/core/ui/select/select.tsx
Normal file
|
@ -0,0 +1,67 @@
|
|||
import React from "react";
|
||||
import { CoreText, CoreTextType } from "../text/text";
|
||||
|
||||
interface ISelectCoreProps {
|
||||
items: string[];
|
||||
value: string;
|
||||
label: string;
|
||||
onChange: (value: string) => void;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
export const SelectCore = (props: ISelectCoreProps) => {
|
||||
const ref = React.useRef<HTMLDivElement>(null);
|
||||
const [cursorIsCorses, setCursorIsCorses] = React.useState(false);
|
||||
const [value, setValue] = React.useState(props.value);
|
||||
React.useEffect(() => {
|
||||
ref.current?.addEventListener("mousemove", () => {
|
||||
setCursorIsCorses(true);
|
||||
});
|
||||
ref.current?.addEventListener("mouseleave", () => {
|
||||
setCursorIsCorses(false);
|
||||
});
|
||||
}, [ref, setCursorIsCorses]);
|
||||
|
||||
return (
|
||||
<div ref={ref} style={props.style}>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "rgba(230, 224, 233, 1)",
|
||||
height: 58,
|
||||
borderRadius: "4px 4px 0px 0px",
|
||||
borderBottom: "solid 1px black",
|
||||
padding: "10px 10px 10px 10px",
|
||||
}}
|
||||
>
|
||||
<CoreText type={CoreTextType.small} text={props.label} />
|
||||
<div style={{ fontSize: 16, fontFamily: "Roboto", color: "#1D1B20", height: 24 }}>{value}</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "rgba(243, 237, 247, 1)",
|
||||
boxShadow: "0px 1px 2px rgba(0, 0, 0, 0.3), 0px 2px 6px 2px rgba(0, 0, 0, 0.15)",
|
||||
borderRadius: 4,
|
||||
}}
|
||||
>
|
||||
{cursorIsCorses
|
||||
? props.items.map((el) => (
|
||||
<div
|
||||
onClick={() => {
|
||||
setValue(el);
|
||||
props.onChange(el);
|
||||
}}
|
||||
style={{
|
||||
height: 48,
|
||||
textAlign: "center",
|
||||
alignContent: "center",
|
||||
cursor: "pointer",
|
||||
borderBottom: "1px solid",
|
||||
}}
|
||||
>
|
||||
{el}
|
||||
</div>
|
||||
))
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
19
ui/src/core/ui/switch/switch.tsx
Normal file
19
ui/src/core/ui/switch/switch.tsx
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { Icon } from "../icons/icons";
|
||||
|
||||
interface ISwitchProps {
|
||||
isSelected: boolean;
|
||||
id: string;
|
||||
onChange: (status: boolean, id: string) => void;
|
||||
}
|
||||
export const CoreSwitch = (props: ISwitchProps) => {
|
||||
return (
|
||||
<div
|
||||
style={{ height: 40, width: 40, borderRadius: 2, alignContent: "center", cursor: "pointer" }}
|
||||
onClick={() => props.onChange(props.isSelected, props.id)}
|
||||
>
|
||||
<div style={{ backgroundColor: "rgba(104, 80, 164, 1)", width: 20, height: 20, textAlign: "center" }}>
|
||||
{props.isSelected ? <Icon type={"Check"} /> : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -4,6 +4,7 @@ export enum CoreTextType {
|
|||
header,
|
||||
medium,
|
||||
large,
|
||||
small,
|
||||
}
|
||||
|
||||
export interface ITextProps {
|
||||
|
@ -13,6 +14,22 @@ export interface ITextProps {
|
|||
}
|
||||
|
||||
export function CoreText(props: ITextProps) {
|
||||
if (props.type === CoreTextType.small) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
color: props.color ?? "rgba(73, 69, 79, 1)",
|
||||
fontSize: 12,
|
||||
fontFamily: "Roboto",
|
||||
fontWeight: 400,
|
||||
fontSizeAdjust: 14,
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
{props.text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (props.type === CoreTextType.large) {
|
||||
return (
|
||||
<div
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue