Farmbot-Web-App/frontend/farm_designer/tools/tool_slot_edit_components.tsx

232 lines
8.3 KiB
TypeScript

import React from "react";
import { t } from "../../i18next_wrapper";
import { Xyz, TaggedTool, TaggedToolSlotPointer } from "farmbot";
import {
Row, Col, BlurableInput, FBSelect, NULL_CHOICE, DropDownItem
} from "../../ui";
import { BotPosition } from "../../devices/interfaces";
import { ToolPulloutDirection } from "farmbot/dist/resources/api_resources";
import { Popover } from "@blueprintjs/core";
import { ToolSlotSVG } from "../map/layers/tool_slots/tool_graphics";
import { BotOriginQuadrant } from "../interfaces";
import { isNumber } from "lodash";
export interface GantryMountedInputProps {
gantryMounted: boolean;
onChange(update: { gantry_mounted: boolean }): void;
}
export const GantryMountedInput = (props: GantryMountedInputProps) =>
<fieldset className="gantry-mounted-input">
<label>{t("Gantry-mounted")}</label>
<input type="checkbox" name="gantry_mounted"
onChange={() => props.onChange({ gantry_mounted: !props.gantryMounted })}
checked={props.gantryMounted} />
</fieldset>;
export interface SlotDirectionInputRowProps {
toolPulloutDirection: ToolPulloutDirection;
onChange(update: { pullout_direction: ToolPulloutDirection }): void;
}
export const SlotDirectionInputRow = (props: SlotDirectionInputRowProps) =>
<fieldset className="tool-slot-direction-input">
<label>
{t("Change direction")}
</label>
<i className={"direction-icon "
+ directionIconClass(props.toolPulloutDirection)}
onClick={() => props.onChange({
pullout_direction: newSlotDirection(props.toolPulloutDirection)
})} />
<FBSelect
key={props.toolPulloutDirection}
list={DIRECTION_CHOICES}
selectedItem={DIRECTION_CHOICES_DDI[props.toolPulloutDirection]}
onChange={ddi => props.onChange({
pullout_direction: parseInt("" + ddi.value)
})} />
</fieldset>;
export interface ToolSelectionProps {
tools: TaggedTool[];
selectedTool: TaggedTool | undefined;
onChange(update: { tool_id: number }): void;
filterSelectedTool: boolean;
isActive(id: number | undefined): boolean;
filterActiveTools: boolean;
}
export const ToolSelection = (props: ToolSelectionProps) =>
<FBSelect
list={([NULL_CHOICE] as DropDownItem[]).concat(props.tools
.filter(tool => !props.filterSelectedTool
|| tool.body.id != props.selectedTool?.body.id)
.filter(tool => !props.filterActiveTools
|| !props.isActive(tool.body.id))
.map(tool => ({
label: tool.body.name || "untitled",
value: tool.body.id || 0,
}))
.filter(ddi => ddi.value > 0))}
selectedItem={props.selectedTool
? {
label: props.selectedTool.body.name || "untitled",
value: "" + props.selectedTool.body.id
} : NULL_CHOICE}
onChange={ddi =>
props.onChange({ tool_id: parseInt("" + ddi.value) })} />;
export interface ToolInputRowProps {
tools: TaggedTool[];
selectedTool: TaggedTool | undefined;
onChange(update: { tool_id: number }): void;
isExpress: boolean;
isActive(id: number | undefined): boolean;
}
export const ToolInputRow = (props: ToolInputRowProps) =>
<div className="tool-slot-tool-input">
<Row>
<Col xs={12}>
<label>
{props.isExpress
? t("Seed Container")
: t("Tool or Seed Container")}
</label>
<ToolSelection
tools={props.tools}
selectedTool={props.selectedTool}
onChange={props.onChange}
isActive={props.isActive}
filterSelectedTool={false}
filterActiveTools={true} />
</Col>
</Row>
</div>;
export interface SlotLocationInputRowProps {
slotLocation: Record<Xyz, number>;
gantryMounted: boolean;
onChange(update: Partial<Record<Xyz, number>>): void;
botPosition: BotPosition;
}
export const SlotLocationInputRow = (props: SlotLocationInputRowProps) =>
<div className="tool-slot-location-input">
<Row>
<Col xs={11} className="axis-inputs">
{["x", "y", "z"].map((axis: Xyz) =>
<Col xs={4} key={axis}>
<label>{t("{{axis}} (mm)", { axis })}</label>
{axis == "x" && props.gantryMounted
? <input disabled value={t("Gantry")} name={axis} />
: <BlurableInput
type="number"
value={props.slotLocation[axis]}
min={axis == "z" ? undefined : 0}
onCommit={e => props.onChange({
[axis]: parseFloat(e.currentTarget.value)
})} />}
</Col>)}
</Col>
<Col xs={1} className="use-current-location">
<Popover>
<i className="fa fa-question-circle help-icon" />
<div className="current-location-info">
<label>{t("Use current location")}</label>
<p>{positionButtonTitle(props.botPosition)}</p>
</div>
</Popover>
<button
className="blue fb-button"
title={positionButtonTitle(props.botPosition)}
onClick={() => positionIsDefined(props.botPosition) &&
props.onChange(props.botPosition)}>
<i className="fa fa-crosshairs" />
</button>
</Col>
</Row>
</div>;
export interface SlotEditRowsProps {
toolSlot: TaggedToolSlotPointer;
tools: TaggedTool[];
tool: TaggedTool | undefined;
botPosition: BotPosition;
updateToolSlot(update: Partial<TaggedToolSlotPointer["body"]>): void;
isExpress: boolean;
xySwap: boolean;
quadrant: BotOriginQuadrant;
isActive(id: number | undefined): boolean;
}
export const SlotEditRows = (props: SlotEditRowsProps) =>
<div className="tool-slot-edit-rows">
<ToolSlotSVG toolSlot={props.toolSlot}
toolName={props.tool ? props.tool.body.name : "Empty"}
renderRotation={true} xySwap={props.xySwap} quadrant={props.quadrant} />
<SlotLocationInputRow
slotLocation={props.toolSlot.body}
gantryMounted={props.toolSlot.body.gantry_mounted}
botPosition={props.botPosition}
onChange={props.updateToolSlot} />
<ToolInputRow
isExpress={props.isExpress}
tools={props.tools}
selectedTool={props.tool}
isActive={props.isActive}
onChange={props.updateToolSlot} />
{!props.toolSlot.body.gantry_mounted &&
<SlotDirectionInputRow
toolPulloutDirection={props.toolSlot.body.pullout_direction}
onChange={props.updateToolSlot} />}
{!props.isExpress &&
<GantryMountedInput
gantryMounted={props.toolSlot.body.gantry_mounted}
onChange={props.updateToolSlot} />}
</div>;
const directionIconClass = (slotDirection: ToolPulloutDirection) => {
switch (slotDirection) {
case ToolPulloutDirection.POSITIVE_X: return "fa fa-arrow-circle-right";
case ToolPulloutDirection.NEGATIVE_X: return "fa fa-arrow-circle-left";
case ToolPulloutDirection.POSITIVE_Y: return "fa fa-arrow-circle-up";
case ToolPulloutDirection.NEGATIVE_Y: return "fa fa-arrow-circle-down";
case ToolPulloutDirection.NONE: return "fa fa-dot-circle-o";
}
};
export const positionButtonTitle = (position: BotPosition): string =>
positionIsDefined(position)
? `(${position.x}, ${position.y}, ${position.z})`
: t("(unknown)");
export const newSlotDirection =
(old: ToolPulloutDirection | undefined): ToolPulloutDirection =>
isNumber(old) && old < 4 ? old + 1 : ToolPulloutDirection.NONE;
export const positionIsDefined = (position: BotPosition): boolean =>
isNumber(position.x) && isNumber(position.y) && isNumber(position.z);
export const DIRECTION_CHOICES_DDI: { [index: number]: DropDownItem } = {
[ToolPulloutDirection.NONE]:
{ label: t("None"), value: ToolPulloutDirection.NONE },
[ToolPulloutDirection.POSITIVE_X]:
{ label: t("Positive X"), value: ToolPulloutDirection.POSITIVE_X },
[ToolPulloutDirection.NEGATIVE_X]:
{ label: t("Negative X"), value: ToolPulloutDirection.NEGATIVE_X },
[ToolPulloutDirection.POSITIVE_Y]:
{ label: t("Positive Y"), value: ToolPulloutDirection.POSITIVE_Y },
[ToolPulloutDirection.NEGATIVE_Y]:
{ label: t("Negative Y"), value: ToolPulloutDirection.NEGATIVE_Y },
};
export const DIRECTION_CHOICES: DropDownItem[] = [
DIRECTION_CHOICES_DDI[ToolPulloutDirection.NONE],
DIRECTION_CHOICES_DDI[ToolPulloutDirection.POSITIVE_X],
DIRECTION_CHOICES_DDI[ToolPulloutDirection.NEGATIVE_X],
DIRECTION_CHOICES_DDI[ToolPulloutDirection.POSITIVE_Y],
DIRECTION_CHOICES_DDI[ToolPulloutDirection.NEGATIVE_Y],
];