This commit is contained in:
IDONTSUDO 2024-09-10 19:58:09 +03:00
parent 25e57c1a56
commit 3b8d9e4298
41 changed files with 1571 additions and 543 deletions

View file

@ -4,9 +4,9 @@
"Ведите",
"дерево",
"Количество",
"модели",
"может",
"навык",
"модели",
"Новое",
"отрицательное",
"принимать",
@ -18,6 +18,7 @@
"Collada",
"Contolls",
"GLTF",
"grau",
"raycaster",
"skils",
"typedataset",

View file

@ -12,6 +12,7 @@ export const BehaviorTreeSchema = new Schema({
name: {
type: String,
},
description: { type: String },
scene: {
type: Schema.Types.Mixed,
default: [],
@ -21,7 +22,6 @@ export const BehaviorTreeSchema = new Schema({
},
unixTime: {
type: Number,
default: Date.now(),
},
isFinished: {
type: Boolean,

View file

@ -1,4 +1,4 @@
import { IsMongoId, IsString } from "class-validator";
import { IsMongoId, IsNumber, IsString } from "class-validator";
import { IBehaviorTreeModel } from "./behavior_tree_database_model";
export class BehaviorTreeValidationModel implements IBehaviorTreeModel {
@ -8,4 +8,6 @@ export class BehaviorTreeValidationModel implements IBehaviorTreeModel {
public project:string;
public skills:any;
public xml:string;
@IsNumber()
public unixTime: number
}

View file

@ -1,133 +1,183 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/logo.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Ubuntu:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,500;1,700&display=swap"
rel="stylesheet"
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap"
rel="stylesheet"
/>
<title>Robossembler</title>
</head>
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/logo.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>Robossembler </title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div style="width: 100%; height: 100%" id="root"></div>
</body>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div style="width: 100%; height: 100%" id="root"></div>
<style>
@import url("https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap");
</style>
<style>
[contenteditable]:focus {
outline: 0px solid transparent;
}
/* Absolute Center Spinner */
.loading {
}
</body>
<style>
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap')
</style>
<style>
[contenteditable]:focus {
outline: 0px solid transparent;
}
/* Absolute Center Spinner */
.loading {
}
/* Transparent Overlay */
.loading:before {
content: "";
display: block;
}
/* Transparent Overlay */
.loading:before {
content: '';
display: block;
}
/* :not(:required) hides these rules from IE9 and below */
.loading:not(:required) {
/* hide "loading..." text */
font: 0/0 a;
color: transparent;
text-shadow: none;
background-color: transparent;
border: 0;
}
/* :not(:required) hides these rules from IE9 and below */
.loading:not(:required) {
/* hide "loading..." text */
font: 0/0 a;
color: transparent;
text-shadow: none;
background-color: transparent;
border: 0;
}
.loading:not(:required):after {
margin-top: 14px;
content: "";
display: block;
font-size: 8px;
width: 8px;
height: 8px;
-webkit-animation: spinner 1500ms infinite linear;
-moz-animation: spinner 1500ms infinite linear;
-ms-animation: spinner 1500ms infinite linear;
-o-animation: spinner 1500ms infinite linear;
animation: spinner 1500ms infinite linear;
border-radius: 0.5em;
-webkit-box-shadow: white 1.5em 0 0 0, white 1.1em 1.1em 0 0, white 0 1.5em 0 0, white -1.1em 1.1em 0 0,
rgba(0, 0, 0, 0.5) -1.5em 0 0 0, rgba(0, 0, 0, 0.5) -1.1em -1.1em 0 0, white 0 -1.5em 0 0,
white 1.1em -1.1em 0 0;
box-shadow: white 1.5em 0 0 0, white 1.1em 1.1em 0 0, white 0 1.5em 0 0, white -1.1em 1.1em 0 0,
white -1.5em 0 0 0, white -1.1em -1.1em 0 0, white 0 -1.5em 0 0, white 1.1em -1.1em 0 0;
}
.loading:not(:required):after {
margin-top: 14px;
content: '';
display: block;
font-size: 8px;
width: 8px;
height: 8px;
-webkit-animation: spinner 1500ms infinite linear;
-moz-animation: spinner 1500ms infinite linear;
-ms-animation: spinner 1500ms infinite linear;
-o-animation: spinner 1500ms infinite linear;
animation: spinner 1500ms infinite linear;
border-radius: 0.5em;
-webkit-box-shadow: white 1.5em 0 0 0, white 1.1em 1.1em 0 0, white 0 1.5em 0 0, white -1.1em 1.1em 0 0, rgba(0, 0, 0, 0.5) -1.5em 0 0 0, rgba(0, 0, 0, 0.5) -1.1em -1.1em 0 0, white 0 -1.5em 0 0, white 1.1em -1.1em 0 0;
box-shadow: white 1.5em 0 0 0, white 1.1em 1.1em 0 0, white 0 1.5em 0 0, white -1.1em 1.1em 0 0, white -1.5em 0 0 0, white -1.1em -1.1em 0 0, white 0 -1.5em 0 0, white 1.1em -1.1em 0 0;
}
/* Animation */
/* Animation */
@-webkit-keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-moz-keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-o-keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-webkit-keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-moz-keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-o-keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
</style>
.dotted {
background-image: -webkit-repeating-radial-gradient(
center center,
rgba(147, 147, 147, 30%),
rgba(147, 147, 147, 30%) 2px,
transparent 2px,
transparent 100%
);
background-image: -moz-repeating-radial-gradient(
center center,
rgba(147, 147, 147, 30%),
rgba(147, 147, 147, 30%) 2px,
transparent 2px,
transparent 100%
);
background-image: -ms-repeating-radial-gradient(
center center,
rgba(147, 147, 147, 30%),
rgba(147, 147, 147, 30%) 2px,
transparent 2px,
transparent 100%
);
background-image: repeating-radial-gradient(
center center,
rgba(147, 147, 147, 30%),
rgba(147, 147, 147, 30%) 2px,
transparent 2px,
transparent 100%
);
-webkit-background-size: 20px 20px;
-moz-background-size: 20px 20px;
background-size: 20px 20px;
</html>
width: 100%;
height: 100%;
}
</style>
</html>

View file

@ -6,35 +6,7 @@ export const DateExtensions = () => {
}
if (Date.prototype.formatDate === undefined) {
Date.prototype.formatDate = function () {
const days = ["Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"];
const monthNames = [
"Января",
"Февраля",
"Марта",
"Апреля",
"Майя",
"Июня",
"Июля",
"Августа",
"Сентября",
"Октября",
"Ноября",
"Декабря",
];
return (
days[this.getDay()] +
", " +
this.getDate() +
", " +
monthNames[this.getMonth()] +
", " +
this.getFullYear() +
"г.," +
this.getHours() +
":" +
this.getMinutes()
);
return `${this.getFullYear()}.${this.getMonth()}.${this.getDay()} ${this.getHours()}:${this.getMinutes()}`;
};
}
};

View file

@ -37,7 +37,7 @@ export interface IDeviceDependency {
export interface IParam {
type: string;
dependency: Object;
dependency?: Object;
}
export class ParamViewModel implements IParam {
type: string;
@ -190,6 +190,9 @@ export class SkillModel extends ValidationModel implements ISkill {
super();
makeAutoObservable(this);
}
bgColor: string = `rgba(5, 26, 39, 1)`;
borderColor: string = "rgba(25, 130, 196, 1)";
@IsOptional()
@IsString()
sid?: string;
@ -239,13 +242,18 @@ export class Skills {
@IsArray()
@Type(() => SkillModel)
skills: SkillModel[];
static fromSkills = (skilsModel: SkillModel[]) => {
const skills = this.empty();
skills.skills = skilsModel;
return skills;
};
validation = (): Result<string[], void> => {
const errors: string[] = [];
this.skills.forEach((skill) => {
skill.BTAction.forEach((action) => {
if (action.param.isNotEmpty()) {
action.param.forEach((param) => {
if (Object.keys(param.dependency).isEmpty()) {
if (Object.keys(param?.dependency ?? {}).isEmpty()) {
errors.push(param.type);
}
});
@ -257,6 +265,16 @@ export class Skills {
}
return Result.ok(undefined);
};
getCssAtLabel = (label: string): React.CSSProperties =>
this.skills.reduce<React.CSSProperties>((acc, el) => {
el.BTAction.rFind<BtActionViewModel>((el) => el.name.isEqual(label)).map(() => {
acc = {
backgroundColor: el.bgColor,
border: `1px solid ${el.borderColor}`,
};
});
return acc;
}, {});
skillBySid = (sid: string) =>
SkillModel.isEmpty(
this.skills.reduce<SkillModel>((acc, el) => {
@ -270,7 +288,7 @@ export class Skills {
toSkillView = (): ISkillView[] =>
this.skills.map((el) => {
return {
name: el.Module.name,
name: el.SkillPackage.name,
children: el.BTAction.map((act) => {
return { name: act.name, uuid: v4() };
}),
@ -346,7 +364,7 @@ export class Skills {
skill.BTAction.map((action) => {
action.param.map((param) => {
if (param.type.isEqualR(skillType)) {
acc.push(param.dependency);
acc.push(param?.dependency ?? {});
}
return param;
});
@ -375,7 +393,7 @@ export class Skills {
skill.BTAction.forEach((action) => {
action.param.forEach((param) => {
if (param.type.isEqual(skillType)) {
acc = Object.keys(param.dependency).isNotEmpty();
acc = Object.keys(param?.dependency ?? {}).isNotEmpty();
}
});
});
@ -399,7 +417,6 @@ export class Skills {
return this.skills.filter((skill) => !skill.sid?.isEqual(sid));
}
updateSkill = (skill: SkillModel) => {
console.log(skill);
this.skills = this.skills.map((el) => {
if (el.sid?.isEqual(skill.sid ?? "")) {
el = skill;
@ -408,7 +425,13 @@ export class Skills {
});
};
skillHasForm = (label: string): boolean => {
// TODO:NEED IMPLEMENTS
return true;
};
getSkillWidthAtLabel = (label: string): number =>
this.getSkillParams(label).reduce((acc, element) => {
if (element.type.length > acc) {
acc = element.type.length;
}
return acc;
}, 0);
}

View file

@ -14,9 +14,8 @@ export class ValidationModel {
if (error.constraints) return Object.values(error.constraints).join(", ");
return "";
});
console.log(errors)
console.log(this)
return Result.error(message.join(","));
return Result.error(message.join(", \n"));
} else {
return Result.ok(this as unknown as T);
}

View file

@ -9,7 +9,10 @@ import { DataSetScreen, DatasetsScreenPath } from "../../features/dataset/datase
import { AssemblesScreen, AssemblesScreenPath } from "../../features/assembles/assembles_screen";
import { SimulationScreen, SimulationScreenPath } from "../../features/simulations/simulations_screen";
import { EstimateScreen, EstimateScreenPath } from "../../features/estimate/estimate_screen";
import { CalculationInstanceScreenPath, CalculationInstanceScreen } from "../../features/calculation_instance/presentation/calculation_instance_screen";
import {
CalculationInstanceScreenPath,
CalculationInstanceScreen,
} from "../../features/calculation_instance/presentation/calculation_instance_screen";
import { DetailsScreenPath, DetailsScreen } from "../../features/details/details_screen";
import { DigitalTwinsScreen, DigitalTwinsScreenPath } from "../../features/digital_twins/digital_twins_screen";
import { TopicsScreen, TopicsScreenPath } from "../../features/topics/topics_screen";
@ -17,7 +20,6 @@ import { SkillsScreen, SkillsScreenPath } from "../../features/skills/skills_scr
import { CalculationsTemplateScreenPath } from "../../features/calculations_template/calculations_template_screen";
const idURL = ":id";
export const router = createBrowserRouter([
{
path: AllProjectScreenPath,
@ -32,7 +34,11 @@ export const router = createBrowserRouter([
element: <SceneManger />,
},
{
path: BehaviorTreeBuilderPath,
path: BehaviorTreeBuilderPath(idURL),
element: <BehaviorTreeBuilderScreen />,
},
{
path: BehaviorTreeBuilderPath(),
element: <BehaviorTreeBuilderScreen />,
},
{

View file

@ -58,15 +58,15 @@ export class SimpleErrorState extends UiLoader {
}
export class ModalStore extends SimpleErrorState {
isModalOpen: boolean = false;
showModal = () => {
modalShow = () => {
this.isModalOpen = true;
};
handleOk = () => {
modalClickOk = () => {
this.isModalOpen = false;
};
handleCancel = () => {
modalCancel = () => {
this.isModalOpen = false;
};
}

View file

@ -0,0 +1,61 @@
import { makeAutoObservable } from "mobx";
export abstract class Theme {
abstract greenWhite: string;
abstract white: string;
abstract black: string;
abstract green: string;
abstract grau: string;
abstract grauLeft: string;
abstract redAccent: string;
abstract grau2: string;
abstract outlineVariantDark: string;
abstract outlineVariantLight: string;
abstract surfaceContainerLow: string;
abstract surfaceContainer: string;
abstract darkSurfaceVariant: string;
abstract outlineVariant: string;
abstract navBlue: string;
abstract grayLeft: string;
abstract onSurface: string;
abstract surfaceContainerHighest: string;
abstract darkSurface: string;
abstract fon: string;
abstract onSurfaceVariant: string;
abstract darkOnSurfaceVariant: string;
abstract error50: string;
}
export class BlackTheme implements Theme {
error50: string = "rgba(220, 54, 46, 1)";
darkOnSurfaceVariant: string = "rgba(202, 196, 208, 1)";
darkSurfaceVariant: string = "rgba(73, 69, 79, 1)";
navBlue: string = "rgba(202, 211, 216, 1)";
fon: string = "rgba(147, 147, 147, 0.4)";
darkSurface: string = "rgba(20, 18, 24, 1)";
surfaceContainerHighest: string = "rgba(54, 52, 59, 1)";
onSurface: string = "rgba(230, 224, 233, 1)";
grayLeft: string = "rgba(213, 220, 224, 1)";
surfaceContainer: string = "rgba(33, 31, 38, 1)";
surfaceContainerLow: string = "rgba(29, 27, 32, 1)";
outlineVariantLight: string = "rgba(202, 196, 208, 1)";
grau2: string = "rgba(147, 147, 147, 1)";
greenWhite: string = "rgba(233, 237, 201, 1)";
white: string = "rgba(255, 255, 255, 1)";
black: string = "rgba(0, 0, 0, 1)";
green: string = "rgba(56, 87, 72, 1)";
grau: string = "rgba(147, 147, 147, 1)";
grauLeft: string = "";
redAccent: string = "rgba(72, 23, 39, 1)";
outlineVariantDark: string = "rgba(73, 69, 79, 1)";
outlineVariant: string = "rgba(202, 196, 208, 1)";
onSurfaceVariant: string = "rgba(202, 196, 208, 1)";
}
export class ThemeStore {
theme: Theme = new BlackTheme();
constructor() {
makeAutoObservable(this);
}
changeTheme = (theme: Theme) => (this.theme = theme);
}

View file

@ -0,0 +1,54 @@
import { match } from "ts-pattern";
import { themeStore } from "../../..";
import { CoreText, CoreTextType } from "../text/text";
export enum ButtonV2Type {
default = "default",
}
export const ButtonV2 = ({
text,
textColor,
onClick,
style,
icon,
isFilled,
type,
}: {
text?: string;
textColor?: string;
onClick: () => void;
style?: React.CSSProperties;
icon?: React.ReactNode;
isFilled?: boolean;
type?: ButtonV2Type;
}) => {
return (
<div
style={{
cursor: "pointer",
backgroundColor: themeStore.theme.greenWhite,
color: textColor ?? themeStore.theme.black,
width: "max-content",
height: "max-content",
padding: match(type)
.with(ButtonV2Type.default, () => 15)
.otherwise(() => 5),
borderRadius: match(type)
.with(ButtonV2Type.default, () => 5)
.otherwise(() => 100),
display: "flex",
paddingRight: 10,
paddingLeft: 10,
}}
onClick={() => onClick()}
>
{icon}
<CoreText
color={textColor ?? themeStore.theme.black}
text={text}
type={match(type)
.with(ButtonV2Type.default, () => CoreTextType.smallv3)
.otherwise(() => CoreTextType.largeV2)}
/>
</div>
);
};

View file

@ -0,0 +1,88 @@
import { useRef, useEffect, useState } from "react";
import { themeStore } from "../../..";
import { ButtonV2 } from "../button/button_v2";
import { Icon } from "../icons/icons";
import { CoreText, CoreTextType } from "../text/text";
export const CoreCard: React.FC<{
clickDeleteIcon: Function;
clickGoToIcon: Function;
date: number;
descriptionBottom?: string;
descriptionTop: string;
descriptionMiddle?: string;
}> = ({ clickDeleteIcon, clickGoToIcon, descriptionBottom, descriptionTop, date, descriptionMiddle }) => {
const ref = useRef<HTMLDivElement>(null);
const [bgColor, setBgColor] = useState(themeStore.theme.surfaceContainerLow);
useEffect(() => {
ref.current?.addEventListener("mousemove", () => {
setBgColor(themeStore.theme.surfaceContainerHighest);
});
ref.current?.addEventListener("mouseleave", () => {
setBgColor(themeStore.theme.surfaceContainerLow);
});
}, []);
return (
<div
ref={ref}
style={{
backgroundColor: bgColor,
borderRadius: 12,
border: `1.5px solid ${themeStore.theme.outlineVariantDark}`,
padding: 10,
marginTop: 10,
marginRight: 10,
}}
>
<div
style={{ display: "flex", paddingTop: 10, paddingLeft: 20, paddingRight: 20, justifyContent: "space-between" }}
>
<div>
<CoreText type={CoreTextType.mediumV2} text={descriptionTop} color={themeStore.theme.white} />
<div style={{ height: 5 }} />
<CoreText
type={CoreTextType.mediumV2}
text={new Date().fromUnixDate(date).formatDate()}
color={themeStore.theme.outlineVariantLight}
/>
</div>
<div style={{ display: "flex" }}>
<ButtonV2 onClick={() => clickGoToIcon()} text="Перейти" />
<div style={{ width: 10 }} />
<Icon
width={10}
height={10}
type={"Bucket"}
onClick={() => clickDeleteIcon()}
style={{
border: `1px solid ${themeStore.theme.greenWhite}`,
height: 30,
width: 30,
textAlign: "center",
alignContent: "center",
borderRadius: 5,
cursor: "pointer",
}}
/>
</div>
</div>
<div
style={{
width: "calc(100% - 30px)",
height: 1,
backgroundColor: themeStore.theme.outlineVariantDark,
marginLeft: 15,
marginRight: 15,
}}
/>
<div style={{ paddingRight: 30, paddingLeft: 30 }}>
<div style={{ height: 20 }} />
<CoreText type={CoreTextType.largeV2} text={descriptionMiddle} color={themeStore.theme.white} />
<CoreText type={CoreTextType.mediumV2} text={descriptionBottom} color={themeStore.theme.white} />
<div style={{ height: 20 }} />
</div>
</div>
);
};

View file

@ -12,8 +12,6 @@ export const SelectDetail = observer((props: IFormBuilderComponentsProps<SelectD
React.useEffect(() => {
store.viewModel = new SelectDetailViewModel(props.dependency.details);
store.isLoading = false;
console.log(store.viewModel);
store.init();
}, []);

View file

@ -5,8 +5,6 @@ import { FormState } from "../../../../../store/base_store";
import { SelectDetailViewModel } from "../model/select_details_model";
export class SelectDetailStore extends FormState<SelectDetailViewModel, any> {
viewModel = SelectDetailViewModel.empty();
parts?: Parts[];
isLoading: boolean = true;
@ -16,7 +14,6 @@ export class SelectDetailStore extends FormState<SelectDetailViewModel, any> {
makeAutoObservable(this);
}
isSelected = (name: string) => {
console.log(this.viewModel.details);
if (this.viewModel.details)
for (const el of this.viewModel.details) {
if (el.name.isEqual(name)) {

View file

@ -54,12 +54,52 @@ const getIconSvg = (
switch (type) {
case "":
return Result.ok();
case "LeftIcon":
return Result.ok(
<svg width="10" height="12" viewBox="0 0 10 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd" clipRule="evenodd" d="M9.5 6L0.5 11.25L0.5 0.75L9.5 6Z" fill="#E6E0E9" />
</svg>
);
case "SettingGear":
return Result.ok(
<svg
width={width ? width : "14"}
height={height ? height : "14"}
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8.93089 12.2675C8.87255 12.6642 8.51089 12.9792 8.07922 12.9792H5.92089C5.48922 12.9792 5.12756 12.6642 5.07506 12.2383L4.91756 11.1358C4.76006 11.0542 4.60839 10.9667 4.45672 10.8675L3.40672 11.2875C2.99839 11.4392 2.54922 11.27 2.35089 10.9083L1.28339 9.05917C1.07922 8.67417 1.16672 8.21917 1.49339 7.96251L2.38589 7.26834C2.38006 7.18084 2.37422 7.09334 2.37422 7.00001C2.37422 6.91251 2.38006 6.81917 2.38589 6.73167L1.49922 6.03751C1.15506 5.77501 1.06756 5.30251 1.28339 4.94084L2.36256 3.08001C2.56089 2.71834 3.01006 2.55501 3.40672 2.71251L4.46256 3.13834C4.61422 3.03917 4.76589 2.95167 4.91756 2.87001L5.07506 1.75584C5.12756 1.34751 5.48922 1.02667 5.91505 1.02667H8.07339C8.50505 1.02667 8.86672 1.34167 8.91922 1.76751L9.07672 2.87001C9.23422 2.95167 9.38589 3.03917 9.53755 3.13834L10.5876 2.71834C11.0017 2.56667 11.4509 2.73584 11.6492 3.09751L12.7226 4.95251C12.9326 5.33751 12.8392 5.79251 12.5126 6.04917L11.6259 6.74334C11.6317 6.83084 11.6376 6.91834 11.6376 7.01167C11.6376 7.10501 11.6317 7.19251 11.6259 7.28001L12.5126 7.97417C12.8392 8.23667 12.9326 8.69167 12.7284 9.05917L11.6434 10.9375C11.4451 11.2992 10.9959 11.4625 10.5934 11.305L9.54339 10.885C9.39172 10.9842 9.24006 11.0717 9.08839 11.1533L8.93089 12.2675ZM6.19506 11.8125H7.80506L8.02089 10.325L8.33006 10.1967C8.58672 10.0917 8.84339 9.94001 9.11172 9.74167L9.37422 9.54334L10.7626 10.1033L11.5676 8.70334L10.3834 7.78167L10.4242 7.45501L10.426 7.4393C10.4429 7.29325 10.4592 7.15206 10.4592 7.00001C10.4592 6.84251 10.4417 6.69084 10.4242 6.54501L10.3834 6.21834L11.5676 5.29667L10.7567 3.89667L9.36256 4.45667L9.10005 4.25251C8.85505 4.06584 8.59256 3.91417 8.32422 3.80334L8.02089 3.67501L7.80506 2.18751H6.19506L5.97922 3.67501L5.67005 3.79751C5.41339 3.90834 5.15672 4.05417 4.88839 4.25834L4.62589 4.45084L3.23756 3.89667L2.42672 5.29084L3.61089 6.21251L3.57006 6.53917C3.55256 6.69084 3.53506 6.84834 3.53506 7.00001C3.53506 7.15167 3.54672 7.30917 3.57006 7.45501L3.61089 7.78167L2.42672 8.70334L3.23172 10.1033L4.62589 9.54334L4.88839 9.74751C5.13922 9.94001 5.39006 10.0858 5.66422 10.1967L5.97339 10.325L6.19506 11.8125ZM9.04172 7.00001C9.04172 8.12759 8.12764 9.04167 7.00006 9.04167C5.87247 9.04167 4.95839 8.12759 4.95839 7.00001C4.95839 5.87242 5.87247 4.95834 7.00006 4.95834C8.12764 4.95834 9.04172 5.87242 9.04172 7.00001Z"
fill="#CAC4D0"
/>
</svg>
);
case "Bucket":
return Result.ok(
<svg
width={width ?? "12"}
height={height ??"14"}
viewBox="0 0 12 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4.66699 6.33333V10.3333M7.33366 6.33333V10.3333M0.666992 3.66667H11.3337M10.667 3.66667L10.089 11.7613C10.065 12.0977 9.91452 12.4125 9.66773 12.6424C9.42095 12.8722 9.09623 13 8.75899 13H3.24166C2.90442 13 2.5797 12.8722 2.33292 12.6424C2.08613 12.4125 1.9356 12.0977 1.91166 11.7613L1.33366 3.66667H10.667ZM8.00033 3.66667V1.66667C8.00033 1.48986 7.93009 1.32029 7.80506 1.19526C7.68004 1.07024 7.51047 1 7.33366 1H4.66699C4.49018 1 4.32061 1.07024 4.19559 1.19526C4.07056 1.32029 4.00033 1.48986 4.00033 1.66667V3.66667H8.00033Z"
stroke={color ?? "#E9EDC9"}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
case "Error":
return Result.ok(
<svg
xmlns="http://www.w3.org/2000/svg"
width={width ? width : "20"}
height={height ? height : "20"}
width={width ?? "20"}
height={height ?? "20"}
viewBox="0 0 16 16"
fill="none"
>
@ -71,6 +111,173 @@ const getIconSvg = (
/>
</svg>
);
case "BottomSquare":
return Result.ok(
<svg width="21" height="28" viewBox="0 0 21 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M19.0909 8.61539H11.4545V2.15385C11.4545 1.58261 11.2534 1.03477 10.8954 0.630847C10.5374 0.226922 10.0518 0 9.54545 0H1.90909C1.40277 0 0.917184 0.226922 0.55916 0.630847C0.201136 1.03477 0 1.58261 0 2.15385V8.61539C0 9.18662 0.201136 9.73446 0.55916 10.1384C0.917184 10.5423 1.40277 10.7692 1.90909 10.7692H9.54545V17.2308H1.90909C1.40277 17.2308 0.917184 17.4577 0.55916 17.8616C0.201136 18.2655 0 18.8134 0 19.3846V25.8462C0 26.4174 0.201136 26.9652 0.55916 27.3692C0.917184 27.7731 1.40277 28 1.90909 28H9.54545C10.0518 28 10.5374 27.7731 10.8954 27.3692C11.2534 26.9652 11.4545 26.4174 11.4545 25.8462V19.3846H19.0909C19.5972 19.3846 20.0828 19.1577 20.4408 18.7538C20.7989 18.3498 21 17.802 21 17.2308V10.7692C21 10.198 20.7989 9.65016 20.4408 9.24623C20.0828 8.84231 19.5972 8.61539 19.0909 8.61539ZM9.54545 25.8462H1.90909V19.3846H9.54545V25.8462ZM9.54545 2.15385V8.61539H1.90909V2.15385H9.54545ZM19.0909 17.2308H11.4545V10.7692H19.0909V17.2308Z"
fill="black"
/>
</svg>
);
case "DashBoard":
return Result.ok(
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M7.25547 8.65138C7.25547 8.42934 7.16727 8.21639 7.01026 8.05938C6.85325 7.90238 6.64031 7.81417 6.41826 7.81417C6.19622 7.81417 5.98327 7.90238 5.82627 8.05938C5.66926 8.21639 5.58105 8.42934 5.58105 8.65138V18.6979C5.58105 18.9199 5.66926 19.1329 5.82627 19.2899C5.98327 19.4469 6.19622 19.5351 6.41826 19.5351C6.64031 19.5351 6.85325 19.4469 7.01026 19.2899C7.16727 19.1329 7.25547 18.9199 7.25547 18.6979V8.65138ZM11.9997 4.46533C12.2217 4.46533 12.4346 4.55354 12.5917 4.71054C12.7487 4.86755 12.8369 5.0805 12.8369 5.30254V18.6979C12.8369 18.9199 12.7487 19.1329 12.5917 19.2899C12.4346 19.4469 12.2217 19.5351 11.9997 19.5351C11.7776 19.5351 11.5647 19.4469 11.4077 19.2899C11.2507 19.1329 11.1625 18.9199 11.1625 18.6979V5.30254C11.1625 5.0805 11.2507 4.86755 11.4077 4.71054C11.5647 4.55354 11.7776 4.46533 11.9997 4.46533ZM18.4183 13.1165C18.4183 12.8945 18.3301 12.6815 18.1731 12.5245C18.016 12.3675 17.8031 12.2793 17.5811 12.2793C17.359 12.2793 17.1461 12.3675 16.9891 12.5245C16.8321 12.6815 16.7438 12.8945 16.7438 13.1165V18.6979C16.7438 18.9199 16.8321 19.1329 16.9891 19.2899C17.1461 19.4469 17.359 19.5351 17.5811 19.5351C17.8031 19.5351 18.016 19.4469 18.1731 19.2899C18.3301 19.1329 18.4183 18.9199 18.4183 18.6979V13.1165Z"
fill="black"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11.9364 0C9.35888 0 7.33953 6.65354e-08 5.76335 0.212093C4.15144 0.428651 2.87888 0.881861 1.87981 1.87981C0.880744 2.87888 0.428651 4.15144 0.212093 5.76446C6.65354e-08 7.33953 0 9.35888 0 11.9364V12.0636C0 14.6411 6.65354e-08 16.6605 0.212093 18.2367C0.428651 19.8486 0.881861 21.1211 1.87981 22.1202C2.87888 23.1193 4.15144 23.5713 5.76446 23.7879C7.33953 24 9.35888 24 11.9364 24H12.0636C14.6411 24 16.6605 24 18.2367 23.7879C19.8486 23.5713 21.1211 23.1181 22.1202 22.1202C23.1193 21.1211 23.5713 19.8486 23.7879 18.2355C24 16.6605 24 14.6411 24 12.0636V11.9364C24 9.35888 24 7.33953 23.7879 5.76335C23.5713 4.15144 23.1181 2.87888 22.1202 1.87981C21.1211 0.880744 19.8486 0.428651 18.2355 0.212093C16.6605 6.65354e-08 14.6411 0 12.0636 0H11.9364ZM3.06419 3.06419C3.70046 2.42791 4.56 2.06288 5.98772 1.87088C7.43888 1.67665 9.34549 1.67442 12 1.67442C14.6545 1.67442 16.5611 1.67665 18.0123 1.87088C19.44 2.06288 20.3007 2.42902 20.9369 3.06419C21.5721 3.70046 21.9371 4.56 22.1291 5.98772C22.3233 7.43888 22.3256 9.34549 22.3256 12C22.3256 14.6545 22.3233 16.5611 22.1291 18.0123C21.9371 19.44 21.571 20.3007 20.9358 20.9369C20.2995 21.5721 19.44 21.9371 18.0123 22.1291C16.5611 22.3233 14.6545 22.3256 12 22.3256C9.34549 22.3256 7.43888 22.3233 5.98772 22.1291C4.56 21.9371 3.69935 21.571 3.06307 20.9358C2.42791 20.2995 2.06288 19.44 1.87088 18.0123C1.67665 16.5611 1.67442 14.6545 1.67442 12C1.67442 9.34549 1.67665 7.43888 1.87088 5.98772C2.06288 4.56 2.42902 3.70046 3.06419 3.06419Z"
fill="black"
/>
</svg>
);
case "PlayComputer":
return Result.ok(
<svg width="26" height="24" viewBox="0 0 26 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M0.84375 0H22.7812L23.625 0.84375V10.1301C23.1025 9.7379 22.5362 9.40775 21.9375 9.14625V1.6875H1.6875V16.875H10.125C10.125 18.7006 10.7171 20.477 11.8125 21.9375H5.0625V20.25H10.125V18.5625H0.84375L0 17.7188V0.84375L0.84375 0Z"
fill="black"
/>
<path
d="M18.5624 10.125C19.1812 10.125 19.7791 10.2038 20.3563 10.3613C20.93 10.5199 21.4661 10.7488 21.9644 11.0481C22.4594 11.3462 22.9139 11.6977 23.3279 12.1027C23.7408 12.5077 24.0952 12.9622 24.3911 13.4662C24.8436 14.2447 25.1371 15.1053 25.2544 15.9981C25.3717 16.8909 25.3106 17.7981 25.0745 18.6671C24.917 19.2443 24.6886 19.7809 24.3894 20.277C24.0919 20.7709 23.738 21.2285 23.3347 21.6405C22.9297 22.0534 22.4752 22.4078 21.9712 22.7036C21.1927 23.1562 20.3321 23.4497 19.4393 23.567C18.5465 23.6843 17.6393 23.6231 16.7703 23.3871C16.2049 23.2333 15.6632 23.0028 15.1604 22.7019C14.6665 22.4045 14.209 22.0506 13.7969 21.6473C13.3826 21.2428 13.025 20.7842 12.7338 20.2838C12.2812 19.5053 11.9878 18.6447 11.8705 17.7519C11.7531 16.8591 11.8143 15.9519 12.0504 15.0829C12.2079 14.5057 12.4363 13.9691 12.7355 13.473C13.0336 12.978 13.3852 12.5235 13.7902 12.1095C14.1952 11.6966 14.6497 11.3422 15.1537 11.0464C16.189 10.4445 17.3649 10.1266 18.5624 10.125ZM21.9374 16.8547L16.8749 13.5V20.25L21.9374 16.8547Z"
fill="black"
/>
</svg>
);
case "Graph":
return Result.ok(
<svg width="26" height="24" viewBox="0 0 26 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M25 9.0799V2.1199C25 1.72226 24.6776 1.3999 24.28 1.3999L19.72 1.3999C19.3224 1.3999 19 1.72226 19 2.1199V9.0799C19 9.47755 19.3224 9.7999 19.72 9.7999L24.28 9.7999C24.6776 9.7999 25 9.47755 25 9.0799Z"
stroke="black"
strokeWidth="1.75"
/>
<path
d="M7 15.68L7 8.72C7 8.32236 6.67764 8 6.28 8L1.72 8C1.32236 8 1 8.32236 1 8.72L1 15.68C1 16.0776 1.32236 16.4 1.72 16.4H6.28C6.67764 16.4 7 16.0776 7 15.68Z"
stroke="black"
strokeWidth="1.75"
/>
<path
d="M25 22.2801V15.3201C25 14.9225 24.6776 14.6001 24.28 14.6001L19.72 14.6001C19.3224 14.6001 19 14.9225 19 15.3201V22.2801C19 22.6777 19.3224 23.0001 19.72 23.0001H24.28C24.6776 23.0001 25 22.6777 25 22.2801Z"
stroke="black"
strokeWidth="1.75"
/>
<path
d="M19 5.5998H14.8C14.1635 5.5998 13.553 5.85266 13.1029 6.30275C12.6529 6.75284 12.4 7.36328 12.4 7.9998V16.3998C12.4 17.0363 12.6529 17.6468 13.1029 18.0969C13.553 18.5469 14.1635 18.7998 14.8 18.7998H19M12.4 12.1998L7 12.1998"
stroke="black"
strokeWidth="1.75"
/>
</svg>
);
case "Storage":
return Result.ok(
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M25 5.28571C25 5.84852 24.6896 6.40582 24.0866 6.92579C23.4835 7.44575 22.5996 7.91821 21.4853 8.31617C20.371 8.71414 19.0481 9.02982 17.5922 9.2452C16.1363 9.46057 14.5759 9.57143 13 9.57143C9.8174 9.57143 6.76516 9.1199 4.51472 8.31617C2.26428 7.51244 1 6.42236 1 5.28571C1 4.72291 1.31039 4.16561 1.91345 3.64564C2.5165 3.12568 3.40042 2.65322 4.51472 2.25526C5.62902 1.85729 6.95189 1.54161 8.4078 1.32623C9.86371 1.11085 11.4241 1 13 1C14.5759 1 16.1363 1.11085 17.5922 1.32623C19.0481 1.54161 20.371 1.85729 21.4853 2.25526C22.5996 2.65322 23.4835 3.12568 24.0866 3.64564C24.6896 4.16561 25 4.72291 25 5.28571Z"
stroke="black"
strokeWidth="1.75"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M25 20.7144C25 21.4667 24.4455 22.2057 23.3923 22.8572C22.3391 23.5087 20.8242 24.0497 19 24.4259C17.1758 24.802 15.1064 25.0001 13 25.0001C10.8936 25.0001 8.82423 24.802 7 24.4259C5.17577 24.0497 3.66091 23.5087 2.60769 22.8572C1.55447 22.2057 1 21.4667 1 20.7144"
stroke="black"
strokeWidth="1.75"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M25 15.5713C25 16.3236 24.4455 17.0626 23.3923 17.7141C22.3391 18.3657 20.8242 18.9067 19 19.2828C17.1758 19.659 15.1064 19.857 13 19.857C10.8936 19.857 8.82423 19.659 7 19.2828C5.17577 18.9067 3.66092 18.3657 2.6077 17.7141C1.55448 17.0626 1 16.3236 1 15.5713"
stroke="black"
strokeWidth="1.75"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M25 10.4287C25 11.181 24.4455 11.9201 23.3923 12.5716C22.3391 13.2231 20.8242 13.7641 19 14.1402C17.1758 14.5164 15.1064 14.7144 13 14.7144C10.8936 14.7144 8.82423 14.5164 7 14.1402C5.17577 13.7641 3.66091 13.2231 2.60769 12.5716C1.55447 11.9201 1 11.181 1 10.4287"
stroke="black"
strokeWidth="1.75"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path d="M1 5.28564V20.7142" stroke="black" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M25 5.28564V20.7142"
stroke="black"
strokeWidth="1.75"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
case "RobotARM":
return Result.ok(
<svg width="27" height="25" viewBox="0 0 27 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M24.75 8.5H19.75L16 4.75L19.75 1H24.75M1.625 19.125C1.625 18.7935 1.7567 18.4755 1.99112 18.2411C2.22554 18.0067 2.54348 17.875 2.875 17.875H24.125C24.4565 17.875 24.7745 18.0067 25.0089 18.2411C25.2433 18.4755 25.375 18.7935 25.375 19.125V23.5H1.625V19.125Z"
stroke="black"
strokeWidth="1.75"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M3.5 7.25C4.88071 7.25 6 6.13071 6 4.75C6 3.36929 4.88071 2.25 3.5 2.25C2.11929 2.25 1 3.36929 1 4.75C1 6.13071 2.11929 7.25 3.5 7.25Z"
stroke="black"
strokeWidth="1.75"
/>
<path
d="M6 4.75H16M4.75 7.25L9.75 17.875"
stroke="black"
strokeWidth="1.75"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
case "Home":
return Result.ok(
<svg width="28" height="25" viewBox="0 0 28 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M1 13.8571L13.8571 1L26.7143 13.8571"
stroke="black"
strokeWidth="1.75"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M3.85742 11V22.4286C3.85742 22.8075 4.00793 23.1708 4.27584 23.4387C4.54375 23.7066 4.90711 23.8571 5.28599 23.8571H9.57171C9.95059 23.8571 10.314 23.7066 10.5819 23.4387C10.8498 23.1708 11.0003 22.8075 11.0003 22.4286V16.7143C11.0003 16.3354 11.1508 15.972 11.4187 15.7041C11.6866 15.4362 12.05 15.2857 12.4289 15.2857H15.286C15.6649 15.2857 16.0282 15.4362 16.2961 15.7041C16.5641 15.972 16.7146 16.3354 16.7146 16.7143V22.4286C16.7146 22.8075 16.8651 23.1708 17.133 23.4387C17.4009 23.7066 17.7643 23.8571 18.1431 23.8571H22.4289C22.8077 23.8571 23.1711 23.7066 23.439 23.4387C23.7069 23.1708 23.8574 22.8075 23.8574 22.4286V11"
stroke="black"
strokeWidth="1.75"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
case "ComposeSquares":
return Result.ok(
<svg width="27" height="27" viewBox="0 0 27 27" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M3.5 11C2.125 11 1 9.875 1 8.5V3.5C1 2.125 2.125 1 3.5 1H8.5C9.875 1 11 2.125 11 3.5M11 18.5C9.625 18.5 8.5 17.375 8.5 16V11C8.5 9.625 9.625 8.5 11 8.5H16C17.375 8.5 18.5 9.625 18.5 11"
stroke="black"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M23.5 16H18.5C17.1193 16 16 17.1193 16 18.5V23.5C16 24.8807 17.1193 26 18.5 26H23.5C24.8807 26 26 24.8807 26 23.5V18.5C26 17.1193 24.8807 16 23.5 16Z"
stroke="black"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
case "Log":
return Result.ok(
<svg
@ -466,6 +673,12 @@ const getIconSvg = (
<path d="M4 9.4L0 5.4L1.4 4L4 6.6L10.6 0L12 1.4L4 9.4Z" fill="white" />
</svg>
);
case "Plus":
return Result.ok(
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 6.75H6.75V12H5.25V6.75H0V5.25H5.25V0H6.75V5.25H12V6.75Z" fill="black" />
</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">
@ -482,7 +695,7 @@ const getIconSvg = (
fillRule="evenodd"
clipRule="evenodd"
d="M16.06 0.589883L17.41 1.93988C18.2 2.71988 18.2 3.98988 17.41 4.76988L4.18 17.9999H0V13.8199L10.4 3.40988L13.23 0.589883C14.01 -0.190117 15.28 -0.190117 16.06 0.589883ZM2 15.9999L3.41 16.0599L13.23 6.22988L11.82 4.81988L2 14.6399V15.9999Z"
fill="#31111D"
fill={color ?? "#49454F"}
/>
</svg>
);

View file

@ -0,0 +1,54 @@
import { themeStore } from "../../..";
import { Icon } from "../icons/icons";
import { CoreText, CoreTextType, FontType } from "../text/text";
interface InputV2Props {
label: string;
value?: string;
height?: number;
onChange?: (text: string) => void;
}
export const InputV2: React.FC<InputV2Props> = ({ label, height, value, onChange }) => {
return (
<div
style={{
display: "flex",
height: "max-content",
minHeight: 56,
justifyContent: "space-between",
backgroundColor: themeStore.theme.surfaceContainerHighest,
}}
>
<div style={{ paddingLeft: 10 }}>
<CoreText
style={{ paddingTop: 5 }}
type={CoreTextType.smallV2}
color={themeStore.theme.greenWhite}
fontType={FontType.ubuntu}
text={label}
/>
<CoreText
onChange={(text) => {
if (onChange) onChange(text);
}}
style={{
backgroundColor: themeStore.theme.surfaceContainerHighest,
paddingTop: 5,
borderRadius: 5,
}}
contentEditable={true}
type={CoreTextType.largeV2}
color={themeStore.theme.white}
fontType={FontType.ubuntu}
text={value}
/>
</div>
<Icon
style={{ alignContent: "center", paddingRight: 10, position: "relative", display: "flex", paddingTop: 17 }}
type="Pencil"
color={themeStore.theme.greenWhite}
/>
</div>
);
};

View file

@ -0,0 +1,49 @@
import React from "react";
import { themeStore } from "../../..";
import { CoreText, CoreTextType } from "../text/text";
import { InputV2 } from "../input/input_v2";
import { CoreButton } from "../button/button";
import { ButtonV2, ButtonV2Type } from "../button/button_v2";
interface ModalProps {
isOpen: boolean;
onClose: () => void;
children: React.ReactNode;
style?: React.CSSProperties;
}
export const CoreModal: React.FC<ModalProps> = ({ isOpen, onClose, children, style }) => {
if (!isOpen) return null;
return (
<div
style={{
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: themeStore.theme.fon,
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
onClick={() => onClose()}
>
<div
onClick={(event) => event.stopPropagation()}
style={{
backgroundColor: themeStore.theme.black,
border: `1px solid ${themeStore.theme.greenWhite}`,
padding: 20,
borderRadius: 5,
width: 400,
position: "relative",
}}
>
{children}
</div>
</div>
);
};

View file

@ -27,17 +27,19 @@ const Block = (props: IBlockProps) => {
style={
props.isActive
? {
textAlignLast: "center",
height: 32,
backgroundColor: "rgba(232, 222, 248, 1)",
marginLeft: 5,
marginRight: 5,
alignContent: "center",
borderRadius: 12,
}
textAlignLast: "center",
height: 32,
backgroundColor: "rgba(232, 222, 248, 1)",
marginLeft: 5,
marginRight: 5,
alignContent: "center",
borderRadius: 12,
}
: {
textAlignLast: "center", alignContent: "center", height: 32,
}
textAlignLast: "center",
alignContent: "center",
height: 32,
}
}
>
<Icon type={props.icon ?? ""} />
@ -57,7 +59,6 @@ export interface IMainPageProps {
error?: UiBaseError[];
}
export const MainPage = (props: IMainPageProps) => {
const blocksNames = [
{ name: "Детали", path: DetailsScreenPath, icon: "Setting" },
@ -65,7 +66,7 @@ export const MainPage = (props: IMainPageProps) => {
{ name: "Датасеты", path: DatasetsScreenPath, icon: "Datasets" },
{ name: "Сцена", path: SceneManagerPath, icon: "Scene" },
{ name: "Вычисления", path: SkillScreenPath, icon: "Layers" },
{ name: "Поведение", path: BehaviorTreeBuilderPath, icon: "Rocket" },
{ name: "Поведение", path: BehaviorTreeBuilderPath(), icon: "Rocket" },
{ name: "Симуляция", path: SimulationScreenPath, icon: "Simulation" },
{ name: "Оценка", path: EstimateScreenPath, icon: "Grade" },
];

View file

@ -0,0 +1,213 @@
import React, { useEffect } from "react";
import { themeStore } from "../../..";
import { Icon } from "../icons/icons";
import { observer } from "mobx-react-lite";
import { makeAutoObservable } from "mobx";
import { RefDiv } from "../ref/ref_div";
export enum CycleState {
isMoving = "isMoving",
stop = "stop",
}
function delay(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
class MainPageStore {
cycleState = CycleState.stop;
isFirstClick: boolean = true;
stepMs = { 1: 15, 2: 20, 3: 25, 4: 30, 5: 35, 6: 40, 7: 45, 8: 50 };
icons: {
icon: string;
isActive: boolean;
style: React.CSSProperties;
left: number;
top: number;
}[] = [
{ icon: "Home", isActive: true, style: {}, left: 0, top: 0 },
{ icon: "ComposeSquares", isActive: false, style: {}, left: 0, top: 0 },
{ icon: "BottomSquare", isActive: false, style: {}, left: 0, top: 0 },
{ icon: "RobotARM", isActive: false, style: {}, left: 0, top: 0 },
{ icon: "Storage", isActive: false, style: {}, left: 0, top: 0 },
{ icon: "Graph", isActive: false, style: {}, left: 0, top: 0 },
{ icon: "PlayComputer", isActive: false, style: {}, left: 0, top: 0 },
{ icon: "DashBoard", isActive: false, style: {}, left: 0, top: 0 },
];
paddingLeft: number = 0;
offsetLeftCircle: number = 0;
leftCircle = 0;
constructor() {
makeAutoObservable(this);
}
init = (paddingLeft: number) => {
this.leftCircle = Math.abs(this.offsetLeftCircle - this.getActive()!.left) - paddingLeft;
this.paddingLeft = paddingLeft;
};
getActive = () => this.icons.filter((el) => el.isActive).at(0);
getActiveIndex = () => this.icons.findIndex((el) => el.isActive);
selectIndex = async (i: number) => {
const prevActive = this.getActive();
const prevActiveIndex = this.getActiveIndex();
this.icons
.map((element) => {
element.isActive = false;
return element;
})
.map((element, index) =>
i.isEqualR(index).fold(
() => {
element.isActive = true;
},
() => element
)
);
const nextActive = this.getActive();
const nextActiveIndex = this.getActiveIndex();
this.cycleState = CycleState.isMoving;
let distance = 0;
distance = nextActive!.left - this.offsetLeftCircle;
const step = distance / 10;
for await (const num of Array.from({ length: 10 }, (_, i) => i + 1)) {
// @ts-ignore
await delay(this.stepMs[Math.abs(nextActiveIndex - prevActiveIndex)]);
nextActive!.top = nextActive!.top + 3;
if (!this.isFirstClick) prevActive!.top = prevActive!.top - 3;
let offset = 0;
if (num === 10) {
offset = 6;
}
this.leftCircle = this.leftCircle + step - offset;
}
this.isFirstClick = false;
};
updateLeftStyle = (left: number, index: number) => this.icons.replacePropIndex({ left: left }, index);
}
export const MainPageV2: React.FC<{ children: React.ReactNode; bgColor: string; style?: React.CSSProperties | void }> =
observer(({ children, bgColor, style }) => {
const [store] = React.useState(() => new MainPageStore());
useEffect(() => {
store.init(4);
}, []);
return (
<div
style={{
display: "flex",
flexDirection: "column",
backgroundColor: bgColor,
width: "100%",
height: "100%",
overflow: "auto",
}}
>
<div
style={{
alignSelf: "center",
width: 645,
height: 60,
backgroundColor: themeStore.theme.navBlue,
paddingBottom: 60,
borderRadius: 20,
zIndex: 20,
}}
>
<RefDiv
style={{
display: "flex",
width: "100%",
height: 60,
justifyContent: "space-evenly",
alignItems: "center",
}}
children={
<>
{store.icons.map((el, i) => (
<div key={i} style={{ zIndex: 25 }}>
{el.isActive === true ? (
<>
<RefDiv
style={{ position: "relative", top: 30 }}
property="offsetLeft"
onChange={(text) => store.updateLeftStyle(Number(text), i)}
>
<Icon
type={el.icon}
style={{
margin: 10,
zIndex: 25,
position: "relative",
}}
onClick={() => {
store.icons
.map((element) => {
element.isActive = false;
return element;
})
.map((element, index) =>
i.isEqualR(index).fold(
() => {
element.isActive = true;
},
() => element
)
);
}}
/>
</RefDiv>
</>
) : (
<>
<RefDiv
style={{ position: "relative", top: el.top, zIndex: 23 }}
property="offsetLeft"
onChange={(text) => store.updateLeftStyle(Number(text), i)}
>
<Icon type={el.icon} style={{ margin: 10 }} onClick={() => store.selectIndex(i)} />
</RefDiv>
</>
)}
</div>
))}
</>
}
/>
<div style={{ width: 0, height: 0 }}>
<RefDiv
property="offsetLeft"
onChange={(text) => {
store.offsetLeftCircle = Number(text);
}}
style={{
position: "relative",
top: -30,
left: store.leftCircle,
backgroundColor: bgColor,
width: 60,
height: 60,
borderRadius: "50%",
paddingTop: 10,
zIndex: 21,
}}
>
<div
style={{
position: "relative",
top: -5,
left: 5,
backgroundColor: themeStore.theme.greenWhite,
width: 50,
height: 50,
borderRadius: 200,
}}
></div>
</RefDiv>
</div>
</div>
<div style={Object.assign({ width: "100%" }, style)}>{children}</div>
</div>
);
});

View file

@ -21,7 +21,6 @@ export const PreviewPage = (props: IPreviewPageProps) => {
<CoreText text={props.largeText ?? ""} type={CoreTextType.big} />
<CoreText
onClick={() => {
console.log(200);
if (props.click) props.click();
}}
text={props.minText ?? ""}

View file

@ -0,0 +1,21 @@
import { useEffect, useRef } from "react";
export const RefDiv: React.FC<{
property?: string;
onChange?: (property: string) => void;
style?: React.CSSProperties;
children?: React.ReactNode;
}> = ({ property, onChange, style, children }) => {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
if (ref.current && onChange) {
// @ts-ignore
onChange(String(ref.current[property]));
}
} );
return (
<div ref={ref} style={style}>
{children}
</div>
);
};

View file

@ -0,0 +1,96 @@
import React from "react";
import { CoreText, CoreTextType } from "../text/text";
import { IStyle } from "../../model/style";
import { themeStore } from "../../..";
interface ISelectV2Props extends IStyle {
items: { name: string; value: string }[];
initialValue: string;
label: string;
onChange: (value: string) => void;
}
export const SelectV2: React.FC<ISelectV2Props> = ({ items, initialValue, label, onChange, style }) => {
const ref = React.useRef<HTMLDivElement>(null);
const [cursorIsCorses, setCursorIsCorses] = React.useState(false);
const [width, setWidth] = React.useState(0);
const [value, setValue] = React.useState(initialValue);
React.useEffect(() => {
ref.current?.addEventListener("mousemove", () => {
setCursorIsCorses(true);
});
ref.current?.addEventListener("mouseleave", () => {
setCursorIsCorses(false);
});
setWidth(Number(ref.current?.clientWidth));
}, [ref, setCursorIsCorses]);
return (
<div ref={ref} style={style}>
<div
style={{
backgroundColor: themeStore.theme.darkSurface,
height: 58,
borderRadius: "4px 4px 4px 4px",
border: `3px solid ${themeStore.theme.greenWhite}`,
padding: "10px 10px 10px 10px",
}}
>
<CoreText
type={CoreTextType.small}
color={themeStore.theme.greenWhite}
text={label}
style={{
position: "relative",
bottom: 20,
backgroundColor: themeStore.theme.darkSurface,
width: "min-content",
paddingLeft: 5,
paddingRight: 5,
}}
/>
<div
style={{
fontSize: 16,
fontFamily: "Roboto",
color: themeStore.theme.white,
height: 24,
position: "relative",
top: -13,
}}
>
{value}
</div>
</div>
<div
style={{
backgroundColor: themeStore.theme.surfaceContainer,
boxShadow: "0px 1px 2px rgba(0, 0, 0, 0.3), 0px 2px 6px 2px rgba(0, 0, 0, 0.15)",
borderRadius: 4,
}}
>
<div style={{ position: "absolute", width: width, backgroundColor: themeStore.theme.surfaceContainer }}>
{cursorIsCorses
? items.map((el, i) => (
<CoreText
text={el.name}
key={i}
type={CoreTextType.smallV2}
color={themeStore.theme.white}
onClick={() => {
setValue(el.name);
onChange(el.value);
}}
style={{
padding: 10,
alignContent: "center",
cursor: "pointer",
}}
/>
))
: null}
</div>
</div>
</div>
);
};

View file

@ -2,63 +2,106 @@ import * as React from "react";
import { IStyle } from "../../model/style";
export enum CoreTextType {
header = 'header',
medium = 'medium',
large = 'large',
small = 'small',
big = 'big'
header = "header",
medium = "medium",
mediumV2 = "mediumV2",
largeV2 = "largeV2",
large = "large",
small = "small",
smallV2 = "smallV2",
smallv3 = "smallv3",
big = "big",
}
export enum FontType {
ubuntu = "'Ubuntu'",
roboto = "'Roboto'",
}
export interface ITextProps extends IStyle {
text: string;
text?: string;
type: CoreTextType;
fontType?: FontType;
color?: string;
onClick?: Function;
onChange?: (text: string) => void;
contentEditable?: boolean;
}
const getStyle = (type: CoreTextType, color: string | undefined) => {
if (type.isEqual(CoreTextType.small)) return {
color: color ?? "rgba(73, 69, 79, 1)",
fontSize: 12,
fontFamily: "Roboto",
fontWeight: 400,
fontSizeAdjust: 14,
textOverflow: "ellipsis",
}
if (type.isEqual(CoreTextType.smallv3))
return {
fontWeight: "400",
fontSize: "16px;",
lineHeight: "19px",
fontStyle: "normal",
};
if (type.isEqual(CoreTextType.smallV2))
return {
fontStyle: "normal",
fontWeight: "400",
fontSize: 14,
color: color ?? "#1D1B20",
};
if (type.isEqual(CoreTextType.mediumV2))
return {
color: color ?? "rgba(73, 69, 79, 1)",
fontStyle: "normal",
fontWeight: "500",
fontSize: "16px",
lineHeight: "24px",
letterSpacing: "0.15px",
};
if (type.isEqual(CoreTextType.largeV2))
return {
color: color ?? "rgba(73, 69, 79, 1)",
if (type.isEqual(CoreTextType.large)) return {
color: color ?? "#1D1B20",
fontSize: 16,
fontFamily: "Roboto",
fontWeight: 400,
fontSizeAdjust: 14,
textOverflow: "ellipsis",
}
if (type.isEqual(CoreTextType.medium)) return {
color: color ?? "#1D1B20",
fontSize: 14,
fontFamily: "Roboto",
fontWeight: 400,
textOverflow: "ellipsis",
fontSizeAdjust: 14,
}
if (type.isEqual(CoreTextType.header)) return {
color: color ?? "#1D1B20",
fontSize: 20,
fontFamily: "Roboto",
fontWeight: 500,
textOverflow: "ellipsis",
fontSizeAdjust: 16,
}
if (type.isEqual(CoreTextType.big)) return {
color: color ?? "#1D1B20",
fontSize: 40,
fontFamily: "Roboto",
fontWeight: 500,
textOverflow: "ellipsis",
alignContent: "center",
fontWeight: 500,
fontSize: 14,
lineHeight: "20px",
};
if (type.isEqual(CoreTextType.small))
return {
color: color ?? "rgba(73, 69, 79, 1)",
fontSize: 12,
fontWeight: 400,
fontSizeAdjust: 14,
textOverflow: "ellipsis",
};
fontSizeAdjust: 16,
}
if (type.isEqual(CoreTextType.large))
return {
color: color ?? "#1D1B20",
fontSize: 16,
fontWeight: 400,
fontSizeAdjust: 14,
textOverflow: "ellipsis",
};
if (type.isEqual(CoreTextType.medium))
return {
color: color ?? "#1D1B20",
fontSize: 14,
fontWeight: 400,
textOverflow: "ellipsis",
fontSizeAdjust: 14,
};
if (type.isEqual(CoreTextType.header))
return {
color: color ?? "#1D1B20",
fontSize: 20,
fontWeight: 500,
textOverflow: "ellipsis",
fontSizeAdjust: 16,
};
if (type.isEqual(CoreTextType.big))
return {
color: color ?? "#1D1B20",
fontSize: 40,
fontWeight: 500,
textOverflow: "ellipsis",
fontSizeAdjust: 16,
};
return {
color: color ?? "rgba(73, 69, 79, 1)",
fontSize: 12,
@ -66,21 +109,37 @@ const getStyle = (type: CoreTextType, color: string | undefined) => {
fontWeight: 400,
fontSizeAdjust: 14,
textOverflow: "ellipsis",
};
};
const appendStyle = (
type: CoreTextType,
color: string | undefined,
style: React.CSSProperties | undefined,
fontType: FontType | undefined
) => {
let font = "Roboto";
if (fontType !== undefined) {
font = fontType;
}
}
const appendStyle = (type: CoreTextType, color: string | undefined, style: React.CSSProperties | undefined) => {
return Object.assign(getStyle(type, color), style)
}
return Object.assign(Object.assign(getStyle(type, color), style), { fontFamily: font });
};
export function CoreText(props: ITextProps) {
return (
<div
contentEditable={props.contentEditable}
onClick={() => {
if (props.onClick) props.onClick()
if (props.onClick) props.onClick();
}}
style={appendStyle(props.type, props.color, props.style, props.fontType)}
onInput={(event) => {
if (props.onChange) {
// @ts-expect-error
props.onChange(event.target.innerText);
}
}}
style={appendStyle(props.type, props.color, props.style)}
>
{props.text}
</div>
);
}

View file

@ -27,7 +27,7 @@ export const AllProjectScreen: React.FunctionComponent = observer(() => {
largeText={"Проекты"}
needBackButton={false}
minText="Создать новый проект?"
click={() => store.showModal()}
click={() => store.modalShow()}
isError={false}
isLoading={store.isLoading}
children={
@ -82,7 +82,7 @@ export const AllProjectScreen: React.FunctionComponent = observer(() => {
footer={null}
closable={false}
closeIcon={null}
onCancel={store.handleCancel}
onCancel={store.modalCancel}
>
<CoreText text="Новый проект" type={CoreTextType.big} />
<CoreInput label={"название проекта"} onChange={(text) => store.setDescriptionToNewProject(text)} />
@ -102,7 +102,7 @@ export const AllProjectScreen: React.FunctionComponent = observer(() => {
style={{ marginRight: 10 }}
filled={true}
/>
<CoreButton text="Отменить" onClick={() => store.handleCancel()} style={{ marginRight: 10 }} />
<CoreButton text="Отменить" onClick={() => store.modalCancel()} style={{ marginRight: 10 }} />
</div>
</Modal>
</>

View file

@ -1,5 +1,5 @@
import { Result } from "../../../core/helper/result";
import { Skills } from "../../../core/model/skill_model";
import { SkillModel, Skills } from "../../../core/model/skill_model";
import { HttpError, HttpMethod, CoreHttpRepository } from "../../../core/repository/core_http_repository";
import { BehaviorTreeModel } from "../model/behavior_tree_model";
import { BehaviorTreeViewModel } from "../model/behavior_tree_view_model";
@ -8,12 +8,12 @@ export class BehaviorTreeBuilderHttpRepository extends CoreHttpRepository {
featureApi = `/behavior/trees`;
getAllBtInstances = async () => this._jsonRequest<BehaviorTreeModel[]>(HttpMethod.GET, this.featureApi);
getBtSkills = async (): Promise<Result<HttpError, Skills>> => {
return (await this._jsonToClassInstanceRequest<Skills>(
getBtSkills = async (): Promise<Result<HttpError, SkillModel[]>> => {
return (await this._arrayJsonToClassInstanceRequest<SkillModel>(
HttpMethod.GET,
`${this.featureApi}/templates`,
Skills
)) as unknown as Promise<Result<HttpError, Skills>>;
`/skills`,
SkillModel
)) as unknown as Promise<Result<HttpError, SkillModel[]>>;
};
saveNewBt = async (model: BehaviorTreeViewModel) => this._jsonRequest(HttpMethod.POST, this.featureApi, model);
getBtById = async (id: string): Promise<Result<HttpError, BehaviorTreeModel>> =>
@ -22,7 +22,7 @@ export class BehaviorTreeBuilderHttpRepository extends CoreHttpRepository {
`${this.featureApi}/by_id?id=${id}`,
BehaviorTreeModel
) as unknown as Promise<Result<HttpError, BehaviorTreeModel>>;
deleteBt = (id: string) => this._jsonRequest(HttpMethod.DELETE, this.featureApi);
editBt = async (model: BehaviorTreeModel) => {
await this._jsonRequest(HttpMethod.POST, `${this.featureApi}/fill/tree`, model);
return await this._jsonRequest(HttpMethod.PUT, this.featureApi, model);

View file

@ -2,11 +2,14 @@ import { Type } from "class-transformer";
import { Result } from "../../../core/helper/result";
import { Skills } from "../../../core/model/skill_model";
import { NodeBehaviorTree } from "./node_behavior_tree";
import { IsOptional, IsString } from "class-validator";
import { IsNotEmpty, IsNumber, IsOptional, IsString } from "class-validator";
import { BehaviorTreeBuilderHttpRepository } from "../data/behavior_tree_builder_http_repository";
import { message } from "antd";
import { ValidationModel } from "../../../core/model/validation_model";
export class BehaviorTreeModel {
export class BehaviorTreeModel extends ValidationModel {
@IsNumber()
unixTime: number = Date.now();
@IsString()
public sceneId: string;
@IsOptional()
@ -14,10 +17,16 @@ export class BehaviorTreeModel {
public skills?: Skills;
public scene: NodeBehaviorTree[] = [];
public xml: string;
@IsString()
@IsNotEmpty()
public description: string;
@IsString()
@IsNotEmpty()
public name: string;
public project: string;
public _id: string;
constructor(skills: Skills, scene: NodeBehaviorTree[], xml: string, name: string, project: string, _id: string) {
super();
this.skills = skills;
this.scene = scene;
this.xml = xml;
@ -32,11 +41,9 @@ export class BehaviorTreeModel {
}
getSceneDependency = async (behaviorTreeBuilderHttpRepository: BehaviorTreeBuilderHttpRepository) =>
(await behaviorTreeBuilderHttpRepository.getSceneAsset(this.sceneId)).fold(
(s) => {
},
(s) => {},
(e) => {
message.error('Get Scene Dependency error')
message.error("Get Scene Dependency error");
}
);

View file

@ -1,18 +1,30 @@
import { IsNotEmpty, IsNumber, IsString } from "class-validator";
import { Result } from "../../../core/helper/result";
import { ValidationModel } from "../../../core/model/validation_model";
export class BehaviorTreeViewModel {
constructor(public name: string, public project: string, public sceneId: string) {}
static empty() {
return new BehaviorTreeViewModel("", "", "");
export class BehaviorTreeViewModel extends ValidationModel {
@IsNumber()
unixTime: number = Date.now();
@IsNotEmpty()
@IsString()
public name: string;
@IsNotEmpty()
@IsString()
public description: string;
@IsNotEmpty()
@IsString()
public project: string;
@IsNotEmpty()
@IsString()
public sceneId: string;
constructor(name: string, description: string, project: string, sceneId: string) {
super();
this.name = name;
this.description = description;
this.project = project;
this.sceneId = sceneId;
}
valid(): Result<string, BehaviorTreeViewModel> {
if (this.project.isEmpty()) {
return Result.error("project is empty");
}
if (this.name.isEmpty()) {
return Result.error("name is empty");
}
return Result.ok(this);
static empty() {
return new BehaviorTreeViewModel("", "", "", "");
}
}

View file

@ -4,76 +4,81 @@ import { ISkillView } from "../presentation/ui/skill_tree/skill_tree";
extensions();
export enum SystemPrimitive {
Sequence = 'Sequence',
Fallback = "Fallback",
ReactiveSequence = "ReactiveSequence",
SequenceWithMemory = 'SequenceWithMemory',
ReactiveFallback = "ReactiveFallback",
Decorator = "Decorator",
Inverter = "Inverter",
ForceSuccess = "ForceSuccess",
ForceFailure = "ForceFailure",
Repeat = "Repeat",
RetryUntilSuccessful = "RetryUntilSuccessful",
KeepRunningUntilFailure = "KeepRunningUntilFailure",
Delay = "Delay",
RunOnce = "RunOnce",
Sequence = "Sequence",
Fallback = "Fallback",
ReactiveSequence = "ReactiveSequence",
SequenceWithMemory = "SequenceWithMemory",
ReactiveFallback = "ReactiveFallback",
Decorator = "Decorator",
Inverter = "Inverter",
ForceSuccess = "ForceSuccess",
ForceFailure = "ForceFailure",
Repeat = "Repeat",
RetryUntilSuccessful = "RetryUntilSuccessful",
KeepRunningUntilFailure = "KeepRunningUntilFailure",
Delay = "Delay",
RunOnce = "RunOnce",
}
interface ISystemPrimitive {
label: string;
group: string;
css?: React.CSSProperties;
input: boolean;
output: boolean;
label: string;
group: string;
css?: React.CSSProperties;
input: boolean;
output: boolean;
}
export class PrimitiveViewModel {
primitives: ISystemPrimitive[] = [
{
label: SystemPrimitive.Inverter,
group: SystemPrimitive.Decorator,
input: true,
output: true,
css: {
border: "1px solid #003E61",
borderRadius: 33,
background: "#BDE8AE",
},
},
{
label: SystemPrimitive.ForceSuccess,
group: SystemPrimitive.Decorator,
input: true,
output: true,
css: {
border: "1px solid #003E61",
borderRadius: 33,
background: "#BDE8AE",
},
},
{ label: SystemPrimitive.ForceFailure, group: SystemPrimitive.Decorator, input: true, output: true },
{ label: SystemPrimitive.Repeat, group: SystemPrimitive.Decorator, input: true, output: true },
{ label: SystemPrimitive.RetryUntilSuccessful, group: SystemPrimitive.Decorator, input: true, output: true },
{ label: SystemPrimitive.KeepRunningUntilFailure, group: SystemPrimitive.Decorator, input: true, output: true },
{ label: SystemPrimitive.Delay, group: SystemPrimitive.Decorator, input: true, output: true },
{ label: SystemPrimitive.RunOnce, group: SystemPrimitive.Decorator, input: true, output: true },
{ label: SystemPrimitive.ReactiveSequence, group: SystemPrimitive.Sequence, input: true, output: true },
{ label: SystemPrimitive.Sequence, group: SystemPrimitive.Sequence, input: true, output: true },
{ label: SystemPrimitive.SequenceWithMemory, group: SystemPrimitive.Sequence, input: true, output: true },
{ label: SystemPrimitive.ReactiveFallback, group: SystemPrimitive.Fallback, input: true, output: true },
{ label: SystemPrimitive.Fallback, group: SystemPrimitive.Fallback, input: true, output: true },
];
getPrimitiveAtLabel = (name: string) => {
return this.primitives.find((el) => el.label.isEqual(name));
}
toSkillView = () => this.primitives.reduce<ISkillView[]>((acc, primitive) => {
acc.rFind<ISkillView>((el) => el.name.isEqual(primitive.group)).fold(() => {
acc.at(acc.findIndex((eeee) => eeee.name === primitive.group))?.children?.push({ name: primitive.label })
}, () => { acc.push({ name: primitive.group, children: [{ name: primitive.label }] }) })
return acc;
}, [])
}
primitives: ISystemPrimitive[] = [
{
label: SystemPrimitive.Inverter,
group: SystemPrimitive.Decorator,
input: true,
output: true,
css: {
border: "1px solid #003E61",
borderRadius: 33,
background: "#BDE8AE",
},
},
{
label: SystemPrimitive.ForceSuccess,
group: SystemPrimitive.Decorator,
input: true,
output: true,
css: {
border: `1px solid white`,
borderRadius: 5,
color: "white",
background: "rgba(106, 76, 147, 1)",
},
},
{ label: SystemPrimitive.ForceFailure, group: SystemPrimitive.Decorator, input: true, output: true },
{ label: SystemPrimitive.Repeat, group: SystemPrimitive.Decorator, input: true, output: true },
{ label: SystemPrimitive.RetryUntilSuccessful, group: SystemPrimitive.Decorator, input: true, output: true },
{ label: SystemPrimitive.KeepRunningUntilFailure, group: SystemPrimitive.Decorator, input: true, output: true },
{ label: SystemPrimitive.Delay, group: SystemPrimitive.Decorator, input: true, output: true },
{ label: SystemPrimitive.RunOnce, group: SystemPrimitive.Decorator, input: true, output: true },
{ label: SystemPrimitive.ReactiveSequence, group: SystemPrimitive.Sequence, input: true, output: true },
{ label: SystemPrimitive.Sequence, group: SystemPrimitive.Sequence, input: true, output: true },
{ label: SystemPrimitive.SequenceWithMemory, group: SystemPrimitive.Sequence, input: true, output: true },
{ label: SystemPrimitive.ReactiveFallback, group: SystemPrimitive.Fallback, input: true, output: true },
{ label: SystemPrimitive.Fallback, group: SystemPrimitive.Fallback, input: true, output: true },
];
getPrimitiveAtLabel = (name: string) => {
return this.primitives.find((el) => el.label.isEqual(name));
};
toSkillView = () =>
this.primitives.reduce<ISkillView[]>((acc, primitive) => {
acc
.rFind<ISkillView>((el) => el.name.isEqual(primitive.group))
.fold(
() => {
acc.at(acc.findIndex((element) => element.name === primitive.group))?.children?.push({ name: primitive.label });
},
() => {
acc.push({ name: primitive.group, children: [{ name: primitive.label }] });
}
);
return acc;
}, []);
}

View file

@ -8,12 +8,20 @@ import { CoreButton } from "../../../core/ui/button/button";
import { observer } from "mobx-react-lite";
import { match } from "ts-pattern";
import { Icon } from "../../../core/ui/icons/icons";
import { Drawer } from "antd";
import { Drawer, theme } from "antd";
import { CoreInput } from "../../../core/ui/input/input";
import { CoreText, CoreTextType } from "../../../core/ui/text/text";
import { useNavigate, useParams } from "react-router-dom";
import { IForms, forms } from "./ui/forms/forms";
import { CoreSelect } from "../../../core/ui/select/select";
import { ButtonV2, ButtonV2Type } from "../../../core/ui/button/button_v2";
import { CoreCard } from "../../../core/ui/card/card";
import { themeStore } from "../../..";
import { CoreModal } from "../../../core/ui/modal/modal";
import { InputV2 } from "../../../core/ui/input/input_v2";
import { SelectV2 } from "../../../core/ui/select/select_v2";
import { BehaviorTreeModel } from "../model/behavior_tree_model";
import { MainPageV2 } from "../../../core/ui/pages/main_page_v2";
export const behaviorTreeBuilderScreenPath = "behavior/tree/screen/path";
export interface DOMReact {
@ -28,8 +36,12 @@ export interface DOMReact {
}
export const behaviorTreeBuilderStore = new BehaviorTreeBuilderStore();
export const BehaviorTreeBuilderPath = "/behavior/tree/";
export const BehaviorTreeBuilderPath = (id?: string) => {
if (id) {
return `/behavior/tree/${id}`;
}
return `/behavior/tree/`;
};
export const BehaviorTreeBuilderScreen = observer(() => {
const navigate = useNavigate();
@ -53,101 +65,128 @@ export const BehaviorTreeBuilderScreen = observer(() => {
store.dispose();
};
}, [id, navigate, ref, store]);
return (
<MainPage
page={"Поведение"}
panelStyle={{ padding: 20 }}
maskLoader={store.isLoading}
panelChildren={
<MainPageV2
style={{ position: "absolute", height: "100%" }}
bgColor={themeStore.theme.black}
children={
<>
{match(store.type)
.with(StoreUIType.SelectBehaviorTree, () => (
.with(StoreUIType.ViewBehaviorTree, () => (
<>
<div
style={{
width: "100%",
height: "100%",
backgroundColor: "rgb(255, 217, 228)",
padding: 10,
textAlign: "center",
width: 300,
zIndex: 10,
position: "absolute",
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
backgroundColor: themeStore.theme.surfaceContainerLow,
}}
>
{store.btTreeModels?.map((el, index) => (
<div
style={{ display: "flex", justifyContent: "center", height: 30, alignItems: "center" }}
key={index}
>
<CoreText text={el.name} type={CoreTextType.medium} />
<div style={{ width: 10 }} />
<Icon
type={"code"}
style={{ height: "100%", placeContent: "center" }}
onClick={() => BehaviorTreeBuilderPath + navigate(el._id)}
<div style={{ margin: 20 }}>
<div style={{ height: 58, alignContent: "center" }}>
<CoreText
text="Поведение"
type={CoreTextType.mediumV2}
color={themeStore.theme.onSurfaceVariant}
style={{ marginLeft: 20 }}
/>
<div style={{ width: "100%", height: 1, backgroundColor: themeStore.theme.outlineVariant }} />
</div>
))}
<Icon type="PlusCircle" onClick={() => store.editDrawer(DrawerState.newBehaviorTree, true)} />
{store.skillTemplates ? <SkillTree skills={store.skillTree} dragEnd={store.dragEnd} /> : null}
</div>
<div style={{ width: 100, height: 40 }}>
<CoreButton onClick={() => store.onClickSaveBehaviorTree()} text="Сохранить" />
</div>
</div>
<div
ref={ref}
className="dotted"
style={{
width: "100%",
height: "100%",
backgroundSize: "20px 20px",
}}
/>
</>
))
.with(StoreUIType.ViewBehaviorTree, () => (
<div
style={{ height: "100%", display: "flex", flexDirection: "column", justifyContent: "space-between" }}
>
<div>
<div style={{ height: 58, alignContent: "center" }}>
<CoreText text="модели" type={CoreTextType.medium} />
</div>
{store.skillTemplates ? <SkillTree skills={store.skillTree} dragEnd={store.dragEnd} /> : null}
.with(StoreUIType.SelectBehaviorTree, () => (
<>
<div style={{ height: 20 }} />
<ButtonV2
icon={<Icon type={"Plus"} style={{ alignSelf: "center", marginLeft: 10, marginRight: 10 }} />}
text="СОЗДАТЬ ДЕРЕВО ПОВЕДЕНИЯ"
onClick={() => store.modalShow()}
/>
<div style={{ height: 50 }} />
<div style={{ display: "inline-grid", gridTemplateColumns: "1fr 1fr 1fr", width: "100%" }}>
{store.btTreeModels?.repeat(100).map((el, index) => (
<CoreCard
key={index}
clickDeleteIcon={() => store.deleteBt(el._id)}
clickGoToIcon={() => store.goToBt(el._id)}
descriptionMiddle={el.description}
descriptionTop={el.name}
date={el.unixTime}
/>
))}
</div>
<div style={{ width: 100, height: 40 }}>
<CoreButton onClick={() => store.onClickSaveBehaviorTree()} text="Сохранить" />
</div>
</div>
</>
))
.otherwise(() => (
<></>
))}
</>
}
bodyChildren={
<>
<div style={{ display: "flex", width: "100%" }}>
<div
ref={ref}
style={{
width: "100%",
height: window.innerHeight,
background: "white",
}}
/>
</div>
<Drawer
title={store.titleDrawer}
destroyOnClose={true}
onClose={() => store.editDrawer(DrawerState.newBehaviorTree, false)}
open={store.drawers.find((el) => el.name === DrawerState.newBehaviorTree)?.status}
>
<div style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", height: "100%" }}>
<div>
<CoreInput label="Имя дерева" onChange={(text) => store.updateForm({ name: text })} />
<CoreSelect
items={store.scenes?.map((el) => el.name) ?? []}
value={""}
label={"Сцена"}
onChange={(text) =>
store.updateForm({ sceneId: store.scenes?.filter((el) => el.name.isEqual(text)).at(0)?._id })
}
<CoreModal
isOpen={store.isModalOpen}
onClose={() => store.modalCancel()}
children={
<>
<div style={{ height: 30 }} />
<CoreText
text="Создание дерева поведения"
type={CoreTextType.header}
color={themeStore.theme.white}
style={{ textAlignLast: "center" }}
/>
</div>
<div style={{ display: "flex" }}>
<CoreButton text="Сохранить" filled={true} onClick={() => store.createNewBehaviorTree()} />
<div style={{ width: 10 }} />
<CoreButton text="Отмена" onClick={() => store.editDrawer(DrawerState.newBehaviorTree, false)} />
</div>
</div>
</Drawer>
<div style={{ height: 30 }} />
<div
style={{
height: 1,
width: "calc(100% - 30px)",
backgroundColor: themeStore.theme.outlineVariant,
}}
/>
<div style={{ height: 20 }} />
<InputV2 label={"Название"} onChange={(text) => store.updateForm({ name: text })} />
<div style={{ height: 20 }} />
<InputV2 label={"Описание"} onChange={(text) => store.updateForm({ description: text })} />
<div style={{ height: 20 }} />
<SelectV2
items={store.scenes?.map((el) => ({ name: el.name, value: el._id })) ?? []}
initialValue={""}
label={"Сцена"}
onChange={(value: string) => store.updateForm({ sceneId: value })}
/>
<div style={{ height: 30 }} />
<div
style={{
height: 1,
width: "calc(100% - 30px)",
backgroundColor: themeStore.theme.outlineVariant,
}}
/>
<div style={{ display: "flex", paddingTop: 20, justifyContent: "center" }}>
<ButtonV2 text="Сохранить" onClick={() => store.saveNewBt()} type={ButtonV2Type.default} />
<div style={{ width: 20 }} />
<ButtonV2 text="Отменить" onClick={() => store.modalCancel()} type={ButtonV2Type.default} />
</div>
</>
}
/>
<Drawer
title={store.titleDrawer}
destroyOnClose={true}

View file

@ -1,3 +1,4 @@
import React from "react";
import makeAutoObservable from "mobx-store-inheritance";
import { CoreError, UiDrawerFormState } from "../../../core/store/base_store";
import {
@ -18,14 +19,14 @@ import { message } from "antd";
import { NavigateFunction } from "react-router-dom";
import { BehaviorTreeViewModel } from "../model/behavior_tree_view_model";
import { UiBaseError } from "../../../core/model/ui_base_error";
import React from "react";
import { v4 } from "uuid";
import { behaviorTreeBuilderStore } from "./behavior_tree_builder_screen";
import { BehaviorTreeBuilderPath, behaviorTreeBuilderStore } from "./behavior_tree_builder_screen";
import clone from "just-clone";
import { BehaviorTreeModel } from "../model/behavior_tree_model";
import { PrimitiveViewModel, SystemPrimitive } from "../model/primitive_view_model";
import { SceneModel } from "../../scene_manager/model/scene_model";
import { SceneAsset } from "../../../core/model/scene_asset";
import { themeStore } from "../../..";
interface I2DArea {
x: number;
@ -59,15 +60,14 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
activeProject: string = "";
behaviorTreeBuilderHttpRepository = new BehaviorTreeBuilderHttpRepository();
canRun = true;
shiftIsPressed = false;
selected: string = "";
selectedSid: string = "";
deleteIsPressed = false;
nodeBehaviorTree: NodeBehaviorTree[] = [];
navigate?: NavigateFunction;
editor?: NodeEditor<Schemes>;
areaPlugin?: AreaPlugin<Schemes, AreaExtra>;
nodeUpdateObserver?: NodeRerenderObserver;
isModalOpen: boolean = false;
primitiveViewModel: PrimitiveViewModel;
skillTree: ISkillView = {
name: "",
@ -75,6 +75,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
{
name: "Действия",
children: [],
isSystem: true,
},
],
};
@ -82,10 +83,13 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
constructor() {
super(DrawerState);
makeAutoObservable(this);
this.type = StoreUIType.SelectBehaviorTree;
this.primitiveViewModel = new PrimitiveViewModel();
this.skillTree.children?.push({ name: "Примитивы BT", children: this.primitiveViewModel.toSkillView() });
this.skillTree.children?.push({
name: "Примитивы BT",
children: this.primitiveViewModel.toSkillView(),
isSystem: true,
});
}
syncScene = async (editor: NodeEditor<Schemes>, area: AreaPlugin<Schemes, AreaExtra>) => {
@ -140,14 +144,14 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
// eslint-disable-next-line array-callback-return
.map((el) => {
this.activeProject = el.id;
this.viewModel.project = this.activeProject;
});
(await this.behaviorTreeBuilderHttpRepository.getBtSkills()).fold(
(model) => {
this.skillTemplates = model;
this.skillTemplates = Skills.fromSkills(model);
this.skillTree.children = this.skillTree.children?.map((el) => {
if (el.name === "Действия") {
el.children = model.toSkillView();
el.children = this.skillTemplates.toSkillView();
}
return el;
});
@ -157,29 +161,10 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
);
await this.mapOk("btTreeModels", this.behaviorTreeBuilderHttpRepository.getAllBtInstances());
this.navigate = navigate;
document.addEventListener("keydown", (event) => {
const keyCode = event.keyCode || event.which;
if (keyCode === 16) {
this.shiftIsPressed = true;
}
if (keyCode === 8) {
this.deleteIsPressed = true;
}
});
document.addEventListener("keyup", (event) => {
const keyCode = event.keyCode || event.which;
if (keyCode === 16) {
this.shiftIsPressed = false;
}
if (keyCode === 8 || keyCode === 46) {
this.deleteIsPressed = false;
}
});
}
initParam = async (id?: string) => {
this.isLoading = true;
this.type = StoreUIType.ViewBehaviorTree;
if (id) {
(await this.behaviorTreeBuilderHttpRepository.getBtById(id)).fold(
async (model) => {
@ -197,7 +182,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
);
} else {
await this.mapOk("scenes", this.behaviorTreeBuilderHttpRepository.getAllScenes());
this.type = StoreUIType.SelectBehaviorTree;
// this.type = StoreUIType.SelectBehaviorTree;
}
};
dragZoneSetOffset(offsetTop: number, offsetWidth: number, width: number, height: number) {
@ -243,32 +228,30 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
validateBt() {}
createNewBehaviorTree = async () => {
this.viewModel.project = this.activeProject;
this.viewModel.valid().fold(
async (model) => {
await this.messageHttp(this.behaviorTreeBuilderHttpRepository.saveNewBt(model), {
successMessage: "Новое дерево создано",
});
this.mapOk("btTreeModels", this.behaviorTreeBuilderHttpRepository.getAllBtInstances());
},
async (error) => message.error(error)
);
// this.viewModel.valid().fold(
// async (model) => {
// await this.messageHttp(this.behaviorTreeBuilderHttpRepository.saveNewBt(model), {
// successMessage: "Новое дерево создано",
// });
// this.mapOk("btTreeModels", this.behaviorTreeBuilderHttpRepository.getAllBtInstances());
// },
// async (error) => message.error(error)
// );
};
setSelected = (label: string, selected: boolean, sid: string) => {
this.selectedSid = sid;
// this.selectedSid = sid;
// this.selected = label;
// if (!Object.keys(SystemPrimitive).includes(label) && this.skillTemplates.skillHasForm(label)) {
this.editDrawer(DrawerState.editThreadBehaviorTree, true);
this.selected = label;
if (
this.shiftIsPressed &&
!Object.keys(SystemPrimitive).includes(label) &&
this.skillTemplates.skillHasForm(label)
) {
this.editDrawer(DrawerState.editThreadBehaviorTree, selected);
this.selected = label;
}
if (this.deleteIsPressed) {
this.nodeBehaviorTree = this.nodeBehaviorTree.filter((el) => !el.id.isEqual(sid));
this.filledOutTemplates.skills = this.filledOutTemplates.deleteSid(sid);
this.nodeUpdateObserver?.emit({ type: UpdateEvent.DELETE, id: sid });
}
// }
};
deleteBtAction = (sid: string) => {
this.nodeBehaviorTree = this.nodeBehaviorTree.filter((el) => !el.id.isEqual(sid));
this.filledOutTemplates.skills = this.filledOutTemplates.deleteSid(sid);
this.nodeUpdateObserver?.emit({ type: UpdateEvent.DELETE, id: sid });
};
formUpdateDependency = (dependency: Object, formType: string) => {
this.filledOutTemplates?.skillBySid(this.selectedSid ?? "").fold(
@ -304,14 +287,36 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
<div style={{ width: "100%", display: "flex", flexDirection: "column", alignItems: "flex-start" }}>
{this.skillTemplates?.getSkillParams(label).map((el, index) => (
<div style={{ display: "flex", flexDirection: "row", width: "100%", marginBottom: 5 }} key={index}>
<div style={{ marginRight: 8, padding: 5 }}>IN</div>
<div style={{ display: "flex", width: "100%", backgroundColor: "rgba(255, 255, 255, 0.33)", padding: 5 }}>
<div
style={{
width: this.skillTemplates?.getSkillWidthAtLabel(label) * 10,
marginRight: 8,
padding: 5,
color: behaviorTreeBuilderStore.isFilledInput(el.type, sid)
? themeStore.theme.onSurface
: themeStore.theme.error50,
}}
>
{el.type}
{behaviorTreeBuilderStore.isFilledInput(el.type, sid) ? "" : <span style={{ color: "red" }}>*</span>}
</div>
<div
style={{
display: "flex",
width: 124,
backgroundColor: themeStore.theme.outlineVariantDark,
padding: 5,
borderRadius: "4px 4px 0px 0px",
borderBottom: `1px solid ${
behaviorTreeBuilderStore.isFilledInput(el.type, sid)
? themeStore.theme.onSurface
: themeStore.theme.error50
}`,
}}
></div>
</div>
))}
{this.skillTemplates?.getSkilsOut(label).map((el, index) => (
{/* {this.skillTemplates?.getSkilsOut(label).map((el, index) => (
<div
style={{ display: "flex", flexDirection: "row", width: "100%", marginBottom: 5, padding: 5 }}
key={index}
@ -321,7 +326,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
{el}
</div>
</div>
))}
))} */}
</div>
);
}
@ -342,10 +347,6 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
};
}
getStylesByLabelNode(label: string): React.CSSProperties | undefined {
const result = this.primitiveViewModel.getPrimitiveAtLabel(label);
if (result) {
return clone(result.css as Object);
}
if (label.isEqual(SystemPrimitive.Fallback)) {
return {
border: "1px solid #003E61",
@ -359,10 +360,29 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
background: "rgba(189, 232, 174, 1)",
};
}
return {
border: "1px solid rgba(0, 62, 97, 1)",
background: "rgba(153, 195, 234, 1)",
};
return this.filledOutTemplates.getCssAtLabel(label);
}
changeSceneViewModel = (text: string) => {};
modalShow = () => {
this.isModalOpen = true;
};
modalCancel = () => {
this.isModalOpen = false;
};
deleteBt = (id: string) => this.behaviorTreeBuilderHttpRepository.deleteBt(id);
saveNewBt = async () =>
(await this.viewModel.valid<BehaviorTreeViewModel>()).fold(
async (model) => {
await this.messageHttp(this.behaviorTreeBuilderHttpRepository.saveNewBt(model), {
successMessage: "Новое дерево создано",
});
await this.mapOk("btTreeModels", this.behaviorTreeBuilderHttpRepository.getAllBtInstances());
this.modalCancel();
},
async (error) => message.error(error)
);
goToBt = (id: string) => {
if (this.navigate) this.navigate(BehaviorTreeBuilderPath(id));
};
}

View file

@ -10,9 +10,4 @@
.background {
background: white;
/* background: url(https://sport.mail.ru/img/_main/subnav.png);
opacity: 1;
background-image:linear-gradient(black 3.2px, transparent 3.2px), linear-gradient(90deg, black 3.2px, transparent 3.2px), linear-gradient(black 1.6px, transparent 1.6px), linear-gradient(90deg, black 1.6px, #101010ed 1.6px);
background-size: 80px 80px, 80px 80px, 16px 16px, 16px 16px;
background-position: -3.2px -3.2px, -3.2px -3.2px, -1.6px -1.6px, -1.6px -1.6px; */
}

View file

@ -15,7 +15,7 @@ const Svg = styled.svg`
const Path = styled.path<{ styles?: (props: any) => any }>`
fill: none;
strokeWidth: 3px;
stroke: black;
stroke: white;
pointer-events: auto;
${(props) => props.styles && props.styles(props)}
`;

View file

@ -5,11 +5,11 @@ import styled from "styled-components";
const Styles = styled.div`
display: inline-block;
cursor: pointer;
border: 1px solid rgba(50, 50, 50, 1);
border: 1px solid rgb(255 255 255);
width: 10px;
height: 10px;
vertical-align: middle;
background: rgba(50, 50, 50, 1);
background: white;
z-index: 2;
box-sizing: border-box;
&:hover {

View file

@ -14,7 +14,6 @@ import {
UpdateEvent,
} from "../../../model/editor_view";
import { v4 } from "uuid";
import constructWithOptions from "styled-components/dist/constructors/constructWithOptions";
export type Schemes = GetSchemes<ClassicPreset.Node, ClassicPreset.Connection<ClassicPreset.Node, ClassicPreset.Node>>;
export type AreaExtra = ReactArea2D<Schemes>;
@ -126,7 +125,7 @@ export async function createEditor(container: HTMLElement) {
target: el.id,
});
}
nodeUpdateObserver.on(async (event) => {
if (event.type.isEqual(UpdateEvent.UPDATE)) {
areaContainer.update("node", event.id);

View file

@ -3,6 +3,8 @@ import { ClassicScheme, RenderEmit, Presets } from "rete-react-plugin";
import styled, { css } from "styled-components";
import { $nodewidth, $socketmargin, $socketsize } from "./vars";
import { behaviorTreeBuilderStore } from "../../../behavior_tree_builder_screen";
import { themeStore } from "../../../../../..";
import { Icon } from "../../../../../../core/ui/icons/icons";
const { RefSocket, RefControl } = Presets.classic;
type NodeExtraData = { width?: number; height?: number };
@ -15,16 +17,12 @@ export const NodeStyles = styled.div<NodeExtraData & { selected: boolean; styles
box-sizing: border-box;
width: ${(props) => (Number.isFinite(props.width) ? `${props.width}px` : `${$nodewidth}px`)};
height: ${(props) => (Number.isFinite(props.height) ? `${props.height}px` : "auto")};
color: white;
user-select: none;
&:hover {
background: #333;
}
${(props) =>
props.selected &&
css`
border-color: red;
`}
.title {
color: black;
font-family: sans-serif;
@ -102,17 +100,20 @@ export function SequenceNode<Scheme extends ClassicScheme>(props: Props<Scheme>)
sortByIndex(inputs);
sortByIndex(outputs);
sortByIndex(controls);
behaviorTreeBuilderStore.setSelected(label, selected, id);
const refSelect = React.useRef<HTMLDivElement>(null);
const refDelete = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
refSelect.current?.addEventListener("mouseup", () => behaviorTreeBuilderStore.setSelected(label, selected, id));
refDelete.current?.addEventListener("mouseup", () => behaviorTreeBuilderStore.deleteBtAction(id));
}, []);
return (
<>
<div style={{ width: "100%" }}>
<NodeStyles
selected={selected}
width={width}
style={behaviorTreeBuilderStore.getStylesByLabelNode(label)}
style={Object.assign({ width: "100%" }, behaviorTreeBuilderStore.getStylesByLabelNode(label))}
data-testid="node"
className="node"
id="node"
>
<div style={{ display: "inline-flex", width: "100%", justifyContent: "space-between" }}>
@ -147,14 +148,25 @@ export function SequenceNode<Scheme extends ClassicScheme>(props: Props<Scheme>)
)
)}
<div
style={{ marginTop: 20, marginBottom: 20 }}
onPointerDown={(e) => {}}
className="title"
data-testid="title"
>
<div>{label}</div>
<div>{behaviorTreeBuilderStore.getBodyNode(label,id)}</div>
<div style={{ marginTop: 20, marginBottom: 20 }} onPointerDown={(e) => {}} data-testid="title">
<div style={{ display: "flex", width: "100%", justifyContent: "space-between" }}>
<div style={{ paddingLeft: 5 }}>{label}</div>
<div style={{ display: "flex" }}>
<div ref={refSelect}>
<Icon type="SettingGear" />
</div>
<div style={{ width: 2 }} />
<div ref={refDelete}>
<Icon
type="Bucket"
color={themeStore.theme.onSurfaceVariant}
onClick={() => behaviorTreeBuilderStore.deleteBtAction(id)}
/>
</div>
</div>
</div>
<div>{behaviorTreeBuilderStore.getBodyNode(label, id)}</div>
</div>
{outputs.map(
([key, output]) =>
@ -179,6 +191,6 @@ export function SequenceNode<Scheme extends ClassicScheme>(props: Props<Scheme>)
{outputs.isEmpty() ? <div style={{ width: 10 }} /> : null}
</div>
</NodeStyles>
</>
</div>
);
}

View file

@ -7,7 +7,7 @@ export class TopicDependencyViewModel extends ValidationModel {
@IsNotEmpty()
@IsString()
type: string;
mode: StoreTopicType;
mode?: StoreTopicType;
constructor(type: string, mode: StoreTopicType) {
super();
makeAutoObservable(this);

View file

@ -15,13 +15,12 @@ export const TopicsForm = observer((props: IPropsForm<Partial<TopicDependencyVie
React.useEffect(() => {
store.init();
store.loadClassInstance(TopicDependencyViewModel, props.dependency as TopicDependencyViewModel);
console.log(store.viewModel);
}, [props]);
return (
<div>
<CoreText text={"Topics"} type={CoreTextType.header} style={{ padding: 10 }} />
{match(store.viewModel.mode)
{match(store.viewModel?.mode ?? "")
.with(StoreTopicType.btExecute, () => (
<>
<CoreText text={"Выберите точку размещения"} type={CoreTextType.header} />

View file

@ -4,10 +4,12 @@ import { IFlatMetadata } from "react-accessible-treeview/dist/TreeView/utils";
import { CallBackEventTarget } from "../../../../../core/extensions/extensions";
import "./styles.css";
import { CoreText, CoreTextType } from "../../../../../core/ui/text/text";
import { themeStore } from "../../../../..";
export interface ISkillView {
name: string;
children?: ISkillView[];
isSystem?: boolean;
id?: string;
interface?: string;
out?: string;
@ -54,26 +56,8 @@ export const RefListener = (props: IRefListerProps) => {
style={{ color: "black", alignItems: "center", height: "max-content", display: "flex" }}
draggable={props.isBranch ? undefined : "true"}
>
{props.isBranch ? (
<svg
style={{ marginRight: 5, width: 8 }}
width="12"
height="12"
viewBox="0 0 12 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6 12C9.31371 12 12 9.31371 12 6C12 2.68629 9.31371 0 6 0C2.68629 0 0 2.68629 0 6C0 9.31371 2.68629 12 6 12Z"
fill="#49454F"
/>
</svg>
) : (
""
)}
<CoreText text={props.element.name} type={CoreTextType.large} />
{props.isBranch ? undefined : ""}
<CoreText text={props.element.name} type={CoreTextType.large} color={themeStore.theme.darkOnSurfaceVariant} />
</div>
</div>
);

View file

@ -152,7 +152,7 @@ export const SkillsScreen = observer(() => {
onClick={() => store.addNewParam(index)}
/>
{el.param.map((param) => (
<div style={{ border: "1px solid", padding: 10, margin: 1 }} onClick={() => store.showModal()}>
<div style={{ border: "1px solid", padding: 10, margin: 1 }} onClick={() => store.modalShow()}>
<div>{param.type}</div>
<div> {JSON.stringify(param.dependency)}</div>
</div>

View file

@ -27,7 +27,7 @@ export class SkillsStore extends UiDrawerFormState<SkillModel, HttpError> {
init = async (navigate?: NavigateFunction | undefined) => {
this.mapOk("skills", this.skillsHttpRepository.getAllSkills());
};
showModal = () => {
modalShow = () => {
this.isModalOpen = true;
};
@ -40,7 +40,7 @@ export class SkillsStore extends UiDrawerFormState<SkillModel, HttpError> {
};
addNewParam = (index: number) => {
this.activeIndex = index;
this.showModal();
this.modalShow();
};
onChangeBtDependency = (dependency: Object) =>
this.viewModel.BTAction.at(this.activeIndex ?? 0)

View file

@ -6,6 +6,7 @@ import { SocketListener } from "./features/socket_listener/socket_listener";
import { RouterProvider } from "react-router-dom";
import { router } from "./core/routers/routers";
import { configure } from "mobx";
import { ThemeStore } from "./core/store/theme_store";
configure({
enforceActions: "never",
@ -14,7 +15,7 @@ configure({
extensions();
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
export const themeStore = new ThemeStore();
root.render(
<>
<SocketListener>
@ -22,4 +23,3 @@ root.render(
</SocketListener>
</>
);