tools panel updates

pull/1730/head
gabrielburnworth 2020-03-13 14:09:46 -07:00
parent b1c2b36a37
commit ec878e0dae
21 changed files with 263 additions and 190 deletions

View File

@ -640,6 +640,9 @@
.add-tool-panel-content, .add-tool-panel-content,
.edit-tool-panel-content { .edit-tool-panel-content {
max-height: calc(100vh - 14rem);
overflow-y: auto;
overflow-x: hidden;
button { button {
display: block; display: block;
margin-left: auto; margin-left: auto;
@ -687,6 +690,7 @@
} }
} }
button { button {
margin-bottom: 2rem;
.fa-plus { .fa-plus {
margin-right: 0.5rem; margin-right: 0.5rem;
} }

View File

@ -26,9 +26,6 @@ const NO_TMC = ["arduino", "farmduino", "farmduino_k14"];
export const isTMCBoard = (firmwareHardware: FirmwareHardware | undefined) => export const isTMCBoard = (firmwareHardware: FirmwareHardware | undefined) =>
!firmwareHardware || !NO_TMC.includes(firmwareHardware); !firmwareHardware || !NO_TMC.includes(firmwareHardware);
export const isExpressBoard = (firmwareHardware: FirmwareHardware | undefined) =>
!!(firmwareHardware && EXPRESS_BOARDS.includes(firmwareHardware));
export const hasButtons = (firmwareHardware: FirmwareHardware | undefined) => export const hasButtons = (firmwareHardware: FirmwareHardware | undefined) =>
!firmwareHardware || !NO_BUTTONS.includes(firmwareHardware); !firmwareHardware || !NO_BUTTONS.includes(firmwareHardware);

View File

@ -6,6 +6,7 @@ import { Color } from "../../../../../ui/index";
import { import {
fakeMapTransformProps, fakeMapTransformProps,
} from "../../../../../__test_support__/map_transform_props"; } from "../../../../../__test_support__/map_transform_props";
import { svgMount } from "../../../../../__test_support__/svg_mount";
describe("<BotFigure/>", () => { describe("<BotFigure/>", () => {
const fakeProps = (): BotFigureProps => ({ const fakeProps = (): BotFigureProps => ({
@ -35,7 +36,7 @@ describe("<BotFigure/>", () => {
p.mapTransformProps.quadrant = quadrant; p.mapTransformProps.quadrant = quadrant;
p.mapTransformProps.xySwap = xySwap; p.mapTransformProps.xySwap = xySwap;
p.figureName = figureName; p.figureName = figureName;
const result = shallow<BotFigure>(<BotFigure {...p} />); const result = svgMount(<BotFigure {...p} />);
const expectedGantryProps = expect.objectContaining({ const expectedGantryProps = expect.objectContaining({
id: "gantry", id: "gantry",
@ -65,7 +66,7 @@ describe("<BotFigure/>", () => {
const p = fakeProps(); const p = fakeProps();
p.mapTransformProps.quadrant = 2; p.mapTransformProps.quadrant = 2;
p.position = { x: 100, y: 200, z: 0 }; p.position = { x: 100, y: 200, z: 0 };
const result = shallow<BotFigure>(<BotFigure {...p} />); const result = svgMount(<BotFigure {...p} />);
const gantry = result.find("#gantry"); const gantry = result.find("#gantry");
expect(gantry.length).toEqual(1); expect(gantry.length).toEqual(1);
expect(gantry.props().x).toEqual(90); expect(gantry.props().x).toEqual(90);
@ -77,7 +78,7 @@ describe("<BotFigure/>", () => {
it("changes color on e-stop", () => { it("changes color on e-stop", () => {
const p = fakeProps(); const p = fakeProps();
p.eStopStatus = true; p.eStopStatus = true;
const wrapper = shallow<BotFigure>(<BotFigure {...p} />); const wrapper = svgMount(<BotFigure {...p} />);
expect(wrapper.find("#gantry").props().fill).toEqual(Color.virtualRed); expect(wrapper.find("#gantry").props().fill).toEqual(Color.virtualRed);
}); });
@ -118,7 +119,7 @@ describe("<BotFigure/>", () => {
it("shows mounted tool", () => { it("shows mounted tool", () => {
const p = fakeProps(); const p = fakeProps();
p.mountedToolName = "Seeder"; p.mountedToolName = "Seeder";
const wrapper = shallow<BotFigure>(<BotFigure {...p} />); const wrapper = svgMount(<BotFigure {...p} />);
expect(wrapper.find("#UTM-wrapper").find("#mounted-tool").length) expect(wrapper.find("#UTM-wrapper").find("#mounted-tool").length)
.toEqual(1); .toEqual(1);
}); });

View File

@ -303,7 +303,6 @@ describe("<ToolSlotSVG />", () => {
const fakeProps = (): ToolSlotSVGProps => ({ const fakeProps = (): ToolSlotSVGProps => ({
toolSlot: fakeToolSlot(), toolSlot: fakeToolSlot(),
toolName: "seeder", toolName: "seeder",
renderRotation: false,
xySwap: false, xySwap: false,
quadrant: 2, quadrant: 2,
}); });

View File

@ -341,13 +341,12 @@ const SeedTrough = (props: ToolGraphicProps) => {
export interface ToolSlotSVGProps { export interface ToolSlotSVGProps {
toolSlot: TaggedToolSlotPointer; toolSlot: TaggedToolSlotPointer;
toolName: string | undefined; toolName: string | undefined;
renderRotation: boolean;
xySwap?: boolean; xySwap?: boolean;
quadrant?: BotOriginQuadrant; quadrant?: BotOriginQuadrant;
} }
export const ToolSlotSVG = (props: ToolSlotSVGProps) => { export const ToolSlotSVG = (props: ToolSlotSVGProps) => {
const xySwap = props.renderRotation ? !!props.xySwap : false; const xySwap = !!props.xySwap;
const toolProps = { const toolProps = {
x: 0, y: 0, x: 0, y: 0,
hovered: false, hovered: false,
@ -355,13 +354,11 @@ export const ToolSlotSVG = (props: ToolSlotSVGProps) => {
uuid: props.toolSlot.uuid, uuid: props.toolSlot.uuid,
xySwap, xySwap,
}; };
const pulloutDirection = props.renderRotation const pulloutDirection = props.toolSlot.body.pullout_direction
? props.toolSlot.body.pullout_direction || ToolPulloutDirection.POSITIVE_X;
: ToolPulloutDirection.POSITIVE_X; const quadrant = props.quadrant || 2;
const quadrant = props.renderRotation && props.quadrant ? props.quadrant : 2;
const viewBox = props.renderRotation ? "-25 0 50 1" : "-25 0 50 1";
return props.toolSlot.body.gantry_mounted return props.toolSlot.body.gantry_mounted
? <svg width="3rem" height="3rem" viewBox={viewBox}> ? <svg width="3rem" height="3rem" viewBox={"-25 0 50 1"}>
<GantryToolSlot x={0} y={0} xySwap={xySwap} /> <GantryToolSlot x={0} y={0} xySwap={xySwap} />
{props.toolSlot.body.tool_id && {props.toolSlot.body.tool_id &&
<Tool tool={reduceToolName(props.toolName)} toolProps={toolProps} />} <Tool tool={reduceToolName(props.toolName)} toolProps={toolProps} />}
@ -369,7 +366,7 @@ export const ToolSlotSVG = (props: ToolSlotSVGProps) => {
: <svg width="3rem" height="3rem" viewBox={`-50 0 100 1`}> : <svg width="3rem" height="3rem" viewBox={`-50 0 100 1`}>
{props.toolSlot.body.pullout_direction && {props.toolSlot.body.pullout_direction &&
<ToolbaySlot <ToolbaySlot
id={props.toolSlot.body.id} id={-(props.toolSlot.body.id || 1)}
x={0} x={0}
y={0} y={0}
pulloutDirection={pulloutDirection} pulloutDirection={pulloutDirection}

View File

@ -51,7 +51,7 @@ export const ToolSlotPoint = (props: TSPProps) => {
onClick={() => history.push(`/app/designer/tool-slots/${id}`)}> onClick={() => history.push(`/app/designer/tool-slots/${id}`)}>
{pullout_direction && !gantry_mounted && {pullout_direction && !gantry_mounted &&
<ToolbaySlot <ToolbaySlot
id={-(id || 1)} id={id}
x={qx} x={qx}
y={qy} y={qy}
pulloutDirection={pullout_direction} pulloutDirection={pullout_direction}

View File

@ -21,7 +21,8 @@ import { init, save, edit, destroy } from "../../../api/crud";
import { history } from "../../../history"; import { history } from "../../../history";
import { SpecialStatus } from "farmbot"; import { SpecialStatus } from "farmbot";
import { ToolPulloutDirection } from "farmbot/dist/resources/api_resources"; import { ToolPulloutDirection } from "farmbot/dist/resources/api_resources";
import { AddToolSlotProps, mapStateToPropsAdd } from "../map_to_props_add_edit"; import { mapStateToPropsAdd } from "../state_to_props";
import { AddToolSlotProps } from "../interfaces";
describe("<AddToolSlot />", () => { describe("<AddToolSlot />", () => {
const fakeProps = (): AddToolSlotProps => ({ const fakeProps = (): AddToolSlotProps => ({
@ -39,7 +40,7 @@ describe("<AddToolSlot />", () => {
it("renders", () => { it("renders", () => {
const wrapper = mount(<AddToolSlot {...fakeProps()} />); const wrapper = mount(<AddToolSlot {...fakeProps()} />);
["add new slot", "x (mm)", "y (mm)", "z (mm)", "tool or seed container", ["add new slot", "x (mm)", "y (mm)", "z (mm)", "tool or seed container",
"change direction", "gantry-mounted", "direction", "gantry-mounted",
].map(string => expect(wrapper.text().toLowerCase()).toContain(string)); ].map(string => expect(wrapper.text().toLowerCase()).toContain(string));
expect(init).toHaveBeenCalledWith("Point", { expect(init).toHaveBeenCalledWith("Point", {
pointer_type: "ToolSlot", name: "Slot", radius: 0, meta: {}, pointer_type: "ToolSlot", name: "Slot", radius: 0, meta: {},

View File

@ -4,14 +4,13 @@ jest.mock("../../../history", () => ({ history: { push: jest.fn() } }));
import * as React from "react"; import * as React from "react";
import { mount, shallow } from "enzyme"; import { mount, shallow } from "enzyme";
import { import { RawAddTool as AddTool, mapStateToProps } from "../add_tool";
RawAddTool as AddTool, AddToolProps, mapStateToProps,
} from "../add_tool";
import { fakeState } from "../../../__test_support__/fake_state"; import { fakeState } from "../../../__test_support__/fake_state";
import { SaveBtn } from "../../../ui"; import { SaveBtn } from "../../../ui";
import { initSave } from "../../../api/crud"; import { initSave } from "../../../api/crud";
import { history } from "../../../history"; import { history } from "../../../history";
import { FirmwareHardware } from "farmbot"; import { FirmwareHardware } from "farmbot";
import { AddToolProps } from "../interfaces";
describe("<AddTool />", () => { describe("<AddTool />", () => {
const fakeProps = (): AddToolProps => ({ const fakeProps = (): AddToolProps => ({
@ -90,6 +89,22 @@ describe("<AddTool />", () => {
wrapper.find("input").last().simulate("change"); wrapper.find("input").last().simulate("change");
expect(wrapper.state().toAdd).toEqual(["Seed Trough 2"]); expect(wrapper.state().toAdd).toEqual(["Seed Trough 2"]);
}); });
it("disables when all already added", () => {
const p = fakeProps();
p.firmwareHardware = "express_k10";
p.existingToolNames = ["Seed Trough 1", "Seed Trough 2"];
const wrapper = mount<AddTool>(<AddTool {...p} />);
expect(wrapper.find("button").last().hasClass("pseudo-disabled"))
.toBeTruthy();
});
it("hides when none firmware is selected", () => {
const p = fakeProps();
p.firmwareHardware = "none";
const wrapper = mount<AddTool>(<AddTool {...p} />);
expect(wrapper.find(".add-stock-tools").props().hidden).toBeTruthy();
});
}); });
describe("mapStateToProps()", () => { describe("mapStateToProps()", () => {

View File

@ -18,10 +18,9 @@ import {
buildResourceIndex, buildResourceIndex,
} from "../../../__test_support__/resource_index_builder"; } from "../../../__test_support__/resource_index_builder";
import { destroy, edit, save } from "../../../api/crud"; import { destroy, edit, save } from "../../../api/crud";
import { import { mapStateToPropsEdit } from "../state_to_props";
EditToolSlotProps, mapStateToPropsEdit,
} from "../map_to_props_add_edit";
import { SlotEditRows } from "../tool_slot_edit_components"; import { SlotEditRows } from "../tool_slot_edit_components";
import { EditToolSlotProps } from "../interfaces";
describe("<EditToolSlot />", () => { describe("<EditToolSlot />", () => {
const fakeProps = (): EditToolSlotProps => ({ const fakeProps = (): EditToolSlotProps => ({
@ -46,7 +45,7 @@ describe("<EditToolSlot />", () => {
p.findToolSlot = () => fakeToolSlot(); p.findToolSlot = () => fakeToolSlot();
const wrapper = mount(<EditToolSlot {...p} />); const wrapper = mount(<EditToolSlot {...p} />);
["edit slot", "x (mm)", "y (mm)", "z (mm)", "tool or seed container", ["edit slot", "x (mm)", "y (mm)", "z (mm)", "tool or seed container",
"change direction", "gantry-mounted", "direction", "gantry-mounted",
].map(string => expect(wrapper.text().toLowerCase()).toContain(string)); ].map(string => expect(wrapper.text().toLowerCase()).toContain(string));
}); });

View File

@ -13,9 +13,11 @@ jest.mock("../../../history", () => ({
import * as React from "react"; import * as React from "react";
import { mount, shallow } from "enzyme"; import { mount, shallow } from "enzyme";
import { import {
RawEditTool as EditTool, EditToolProps, mapStateToProps, isActive, RawEditTool as EditTool, mapStateToProps, isActive,
} from "../edit_tool"; } from "../edit_tool";
import { fakeTool, fakeToolSlot } from "../../../__test_support__/fake_state/resources"; import {
fakeTool, fakeToolSlot,
} from "../../../__test_support__/fake_state/resources";
import { fakeState } from "../../../__test_support__/fake_state"; import { fakeState } from "../../../__test_support__/fake_state";
import { import {
buildResourceIndex, fakeDevice, buildResourceIndex, fakeDevice,
@ -24,6 +26,7 @@ import { SaveBtn } from "../../../ui";
import { history } from "../../../history"; import { history } from "../../../history";
import { edit, destroy } from "../../../api/crud"; import { edit, destroy } from "../../../api/crud";
import { clickButton } from "../../../__test_support__/helpers"; import { clickButton } from "../../../__test_support__/helpers";
import { EditToolProps } from "../interfaces";
describe("<EditTool />", () => { describe("<EditTool />", () => {
beforeEach(() => { beforeEach(() => {

View File

@ -14,22 +14,20 @@ jest.mock("../../../device", () => ({ getDevice: () => mockDevice }));
import * as React from "react"; import * as React from "react";
import { mount, shallow } from "enzyme"; import { mount, shallow } from "enzyme";
import { import {
RawTools as Tools, ToolsProps, mapStateToProps, RawTools as Tools,
ToolSlotInventoryItem, ToolSlotInventoryItemProps, ToolSlotInventoryItem, ToolSlotInventoryItemProps,
} from "../index"; } from "../index";
import { import {
fakeTool, fakeToolSlot, fakeSensor, fakeTool, fakeToolSlot, fakeSensor,
} from "../../../__test_support__/fake_state/resources"; } from "../../../__test_support__/fake_state/resources";
import { history } from "../../../history"; import { history } from "../../../history";
import { fakeState } from "../../../__test_support__/fake_state"; import { fakeDevice } from "../../../__test_support__/resource_index_builder";
import {
buildResourceIndex, fakeDevice,
} from "../../../__test_support__/resource_index_builder";
import { bot } from "../../../__test_support__/fake_state/bot"; import { bot } from "../../../__test_support__/fake_state/bot";
import { error } from "../../../toast/toast"; import { error } from "../../../toast/toast";
import { Content, Actions } from "../../../constants"; import { Content, Actions } from "../../../constants";
import { edit, save } from "../../../api/crud"; import { edit, save } from "../../../api/crud";
import { ToolSelection } from "../tool_slot_edit_components"; import { ToolSelection } from "../tool_slot_edit_components";
import { ToolsProps } from "../interfaces";
describe("<Tools />", () => { describe("<Tools />", () => {
const fakeProps = (): ToolsProps => ({ const fakeProps = (): ToolsProps => ({
@ -43,6 +41,8 @@ describe("<Tools />", () => {
hoveredToolSlot: undefined, hoveredToolSlot: undefined,
firmwareHardware: undefined, firmwareHardware: undefined,
isActive: jest.fn(), isActive: jest.fn(),
xySwap: false,
quadrant: 2,
}); });
it("renders with no tools", () => { it("renders with no tools", () => {
@ -225,6 +225,8 @@ describe("<ToolSlotInventoryItem />", () => {
hovered: false, hovered: false,
dispatch: jest.fn(), dispatch: jest.fn(),
isActive: jest.fn(), isActive: jest.fn(),
xySwap: false,
quadrant: 2,
}); });
it("changes tool", () => { it("changes tool", () => {
@ -242,14 +244,3 @@ describe("<ToolSlotInventoryItem />", () => {
expect(e.stopPropagation).toHaveBeenCalled(); expect(e.stopPropagation).toHaveBeenCalled();
}); });
}); });
describe("mapStateToProps()", () => {
it("returns props", () => {
const state = fakeState();
const tool = fakeTool();
tool.body.id = 1;
state.resources = buildResourceIndex([tool, fakeDevice()]);
const props = mapStateToProps(state);
expect(props.findTool(tool.body.id)).toEqual(tool);
});
});

View File

@ -0,0 +1,31 @@
import { fakeState } from "../../../__test_support__/fake_state";
import {
fakeWebAppConfig, fakeTool,
} from "../../../__test_support__/fake_state/resources";
import {
buildResourceIndex, fakeDevice,
} from "../../../__test_support__/resource_index_builder";
import { mapStateToProps } from "../state_to_props";
describe("mapStateToProps()", () => {
it("returns props", () => {
const state = fakeState();
const webAppConfig = fakeWebAppConfig();
webAppConfig.body.bot_origin_quadrant = 1;
state.resources = buildResourceIndex([fakeDevice(), webAppConfig]);
const props = mapStateToProps(state);
expect(props.quadrant).toEqual(1);
});
it("returns props: incorrect quadrant", () => {
const state = fakeState();
const tool = fakeTool();
tool.body.id = 1;
const webAppConfig = fakeWebAppConfig();
webAppConfig.body.bot_origin_quadrant = 10;
state.resources = buildResourceIndex([tool, fakeDevice(), webAppConfig]);
const props = mapStateToProps(state);
expect(props.findTool(tool.body.id)).toEqual(tool);
expect(props.quadrant).toEqual(2);
});
});

View File

@ -7,8 +7,11 @@ import {
SlotLocationInputRow, SlotLocationInputRowProps, SlotLocationInputRow, SlotLocationInputRowProps,
ToolSelection, ToolSelectionProps, SlotEditRows, SlotEditRowsProps, ToolSelection, ToolSelectionProps, SlotEditRows, SlotEditRowsProps,
} from "../tool_slot_edit_components"; } from "../tool_slot_edit_components";
import { fakeTool, fakeToolSlot } from "../../../__test_support__/fake_state/resources"; import {
fakeTool, fakeToolSlot,
} from "../../../__test_support__/fake_state/resources";
import { FBSelect, NULL_CHOICE } from "../../../ui"; import { FBSelect, NULL_CHOICE } from "../../../ui";
import { ToolPulloutDirection } from "farmbot/dist/resources/api_resources";
describe("<GantryMountedInput />", () => { describe("<GantryMountedInput />", () => {
const fakeProps = (): GantryMountedInputProps => ({ const fakeProps = (): GantryMountedInputProps => ({
@ -35,9 +38,18 @@ describe("<SlotDirectionInputRow />", () => {
onChange: jest.fn(), onChange: jest.fn(),
}); });
it("renders", () => { it.each<[ToolPulloutDirection, string]>([
const wrapper = mount(<SlotDirectionInputRow {...fakeProps()} />); [ToolPulloutDirection.NONE, "fa-dot-circle-o"],
expect(wrapper.text().toLowerCase()).toContain("change direction"); [ToolPulloutDirection.POSITIVE_X, "fa-arrow-circle-right"],
[ToolPulloutDirection.NEGATIVE_X, "fa-arrow-circle-left"],
[ToolPulloutDirection.POSITIVE_Y, "fa-arrow-circle-up"],
[ToolPulloutDirection.NEGATIVE_Y, "fa-arrow-circle-down"],
])("renders: direction %s", (toolPulloutDirection, expected) => {
const p = fakeProps();
p.toolPulloutDirection = toolPulloutDirection;
const wrapper = mount(<SlotDirectionInputRow {...p} />);
expect(wrapper.text().toLowerCase()).toContain("direction");
expect(wrapper.find("i").first().hasClass(expected)).toBeTruthy();
}); });
it("changes value by click", () => { it("changes value by click", () => {
@ -47,6 +59,14 @@ describe("<SlotDirectionInputRow />", () => {
expect(p.onChange).toHaveBeenCalledWith({ pullout_direction: 1 }); expect(p.onChange).toHaveBeenCalledWith({ pullout_direction: 1 });
}); });
it("changes value by click: handles rollover", () => {
const p = fakeProps();
p.toolPulloutDirection = ToolPulloutDirection.NEGATIVE_Y;
const wrapper = shallow(<SlotDirectionInputRow {...p} />);
wrapper.find("i").first().simulate("click");
expect(p.onChange).toHaveBeenCalledWith({ pullout_direction: 0 });
});
it("changes value by selection", () => { it("changes value by selection", () => {
const p = fakeProps(); const p = fakeProps();
const wrapper = shallow(<SlotDirectionInputRow {...p} />); const wrapper = shallow(<SlotDirectionInputRow {...p} />);
@ -72,7 +92,8 @@ describe("<ToolSelection />", () => {
it("handles missing tool data", () => { it("handles missing tool data", () => {
const p = fakeProps(); const p = fakeProps();
p.filterSelectedTool = true; p.filterActiveTools = false;
p.filterSelectedTool = false;
const tool = fakeTool(); const tool = fakeTool();
tool.body.name = undefined; tool.body.name = undefined;
tool.body.id = undefined; tool.body.id = undefined;
@ -111,7 +132,7 @@ describe("<ToolInputRow />", () => {
tools: [], tools: [],
selectedTool: undefined, selectedTool: undefined,
onChange: jest.fn(), onChange: jest.fn(),
isExpress: false, noUTM: false,
isActive: jest.fn(), isActive: jest.fn(),
}); });
@ -129,7 +150,7 @@ describe("<ToolInputRow />", () => {
it("renders for express bots", () => { it("renders for express bots", () => {
const p = fakeProps(); const p = fakeProps();
p.isExpress = true; p.noUTM = true;
const wrapper = mount(<ToolInputRow {...p} />); const wrapper = mount(<ToolInputRow {...p} />);
expect(wrapper.text().toLowerCase()).toContain("seed container"); expect(wrapper.text().toLowerCase()).toContain("seed container");
}); });
@ -196,7 +217,7 @@ describe("<SlotEditRows />", () => {
tool: undefined, tool: undefined,
botPosition: { x: undefined, y: undefined, z: undefined }, botPosition: { x: undefined, y: undefined, z: undefined },
updateToolSlot: jest.fn(), updateToolSlot: jest.fn(),
isExpress: false, noUTM: false,
xySwap: false, xySwap: false,
quadrant: 2, quadrant: 2,
isActive: () => false, isActive: () => false,

View File

@ -6,7 +6,7 @@ import {
import { Everything } from "../../interfaces"; import { Everything } from "../../interfaces";
import { t } from "../../i18next_wrapper"; import { t } from "../../i18next_wrapper";
import { SaveBtn } from "../../ui"; import { SaveBtn } from "../../ui";
import { SpecialStatus, FirmwareHardware } from "farmbot"; import { SpecialStatus } from "farmbot";
import { initSave } from "../../api/crud"; import { initSave } from "../../api/crud";
import { Panel } from "../panel_header"; import { Panel } from "../panel_header";
import { history } from "../../history"; import { history } from "../../history";
@ -17,17 +17,7 @@ import {
} from "../../devices/components/firmware_hardware_support"; } from "../../devices/components/firmware_hardware_support";
import { getFbosConfig } from "../../resources/getters"; import { getFbosConfig } from "../../resources/getters";
import { ToolSVG } from "../map/layers/tool_slots/tool_graphics"; import { ToolSVG } from "../map/layers/tool_slots/tool_graphics";
import { AddToolProps, AddToolState } from "./interfaces";
export interface AddToolProps {
dispatch: Function;
existingToolNames: string[];
firmwareHardware: FirmwareHardware | undefined;
}
export interface AddToolState {
toolName: string;
toAdd: string[];
}
export const mapStateToProps = (props: Everything): AddToolProps => ({ export const mapStateToProps = (props: Everything): AddToolProps => ({
dispatch: props.dispatch, dispatch: props.dispatch,
@ -107,8 +97,10 @@ export class RawAddTool extends React.Component<AddToolProps, AddToolState> {
</div>; </div>;
} }
AddStockTools = () => AddStockTools = () => {
<div className="add-stock-tools"> const add = this.state.toAdd.filter(this.filterExisting);
return <div className="add-stock-tools"
hidden={this.props.firmwareHardware == "none"}>
<label>{t("stock names")}</label> <label>{t("stock names")}</label>
<ul> <ul>
{this.stockToolNames().map(n => {this.stockToolNames().map(n =>
@ -118,17 +110,17 @@ export class RawAddTool extends React.Component<AddToolProps, AddToolState> {
</li>)} </li>)}
</ul> </ul>
<button <button
className="fb-button green" className={`fb-button green ${add.length > 0 ? "" : "pseudo-disabled"}`}
title={t("add selected stock names")} title={add.length > 0 ? t("Add selected") : t("None to add")}
onClick={() => { onClick={() => {
this.state.toAdd.filter(this.filterExisting) add.map(n => this.newTool(n));
.map(n => this.newTool(n));
history.push("/app/designer/tools"); history.push("/app/designer/tools");
}}> }}>
<i className="fa fa-plus" /> <i className="fa fa-plus" />
{t("selected")} {t("selected")}
</button> </button>
</div> </div>;
}
render() { render() {
const panelName = "add-tool"; const panelName = "add-tool";

View File

@ -11,15 +11,9 @@ import { Panel } from "../panel_header";
import { ToolPulloutDirection } from "farmbot/dist/resources/api_resources"; import { ToolPulloutDirection } from "farmbot/dist/resources/api_resources";
import { history } from "../../history"; import { history } from "../../history";
import { SlotEditRows } from "./tool_slot_edit_components"; import { SlotEditRows } from "./tool_slot_edit_components";
import { UUID } from "../../resources/interfaces"; import { hasUTM } from "../../devices/components/firmware_hardware_support";
import { import { mapStateToPropsAdd } from "./state_to_props";
isExpressBoard, import { AddToolSlotState, AddToolSlotProps } from "./interfaces";
} from "../../devices/components/firmware_hardware_support";
import { AddToolSlotProps, mapStateToPropsAdd } from "./map_to_props_add_edit";
export interface AddToolSlotState {
uuid: UUID | undefined;
}
export class RawAddToolSlot export class RawAddToolSlot
extends React.Component<AddToolSlotProps, AddToolSlotState> { extends React.Component<AddToolSlotProps, AddToolSlotState> {
@ -30,7 +24,7 @@ export class RawAddToolSlot
pointer_type: "ToolSlot", name: t("Slot"), radius: 0, meta: {}, pointer_type: "ToolSlot", name: t("Slot"), radius: 0, meta: {},
x: 0, y: 0, z: 0, tool_id: undefined, x: 0, y: 0, z: 0, tool_id: undefined,
pullout_direction: ToolPulloutDirection.NONE, pullout_direction: ToolPulloutDirection.NONE,
gantry_mounted: isExpressBoard(this.props.firmwareHardware) ? true : false, gantry_mounted: !hasUTM(this.props.firmwareHardware) ? true : false,
}); });
this.setState({ uuid: action.payload.uuid }); this.setState({ uuid: action.payload.uuid });
this.props.dispatch(action); this.props.dispatch(action);
@ -74,7 +68,7 @@ export class RawAddToolSlot
<DesignerPanelContent panelName={panelName}> <DesignerPanelContent panelName={panelName}>
{this.toolSlot {this.toolSlot
? <SlotEditRows ? <SlotEditRows
isExpress={isExpressBoard(this.props.firmwareHardware)} noUTM={!hasUTM(this.props.firmwareHardware)}
toolSlot={this.toolSlot} toolSlot={this.toolSlot}
tools={this.props.tools} tools={this.props.tools}
tool={this.tool} tool={this.tool}

View File

@ -16,22 +16,12 @@ import { history } from "../../history";
import { Panel } from "../panel_header"; import { Panel } from "../panel_header";
import { ToolSVG } from "../map/layers/tool_slots/tool_graphics"; import { ToolSVG } from "../map/layers/tool_slots/tool_graphics";
import { error } from "../../toast/toast"; import { error } from "../../toast/toast";
import { EditToolProps, EditToolState } from "./interfaces";
export const isActive = (toolSlots: TaggedToolSlotPointer[]) => export const isActive = (toolSlots: TaggedToolSlotPointer[]) =>
(toolId: number | undefined) => (toolId: number | undefined) =>
!!(toolId && toolSlots.map(x => x.body.tool_id).includes(toolId)); !!(toolId && toolSlots.map(x => x.body.tool_id).includes(toolId));
export interface EditToolProps {
findTool(id: string): TaggedTool | undefined;
dispatch: Function;
mountedToolId: number | undefined;
isActive(id: number | undefined): boolean;
}
export interface EditToolState {
toolName: string;
}
export const mapStateToProps = (props: Everything): EditToolProps => ({ export const mapStateToProps = (props: Everything): EditToolProps => ({
findTool: (id: string) => findTool: (id: string) =>
maybeFindToolById(props.resources.index, parseInt(id)), maybeFindToolById(props.resources.index, parseInt(id)),

View File

@ -11,10 +11,9 @@ import { history } from "../../history";
import { Panel } from "../panel_header"; import { Panel } from "../panel_header";
import { SlotEditRows } from "./tool_slot_edit_components"; import { SlotEditRows } from "./tool_slot_edit_components";
import { moveAbs } from "../../devices/actions"; import { moveAbs } from "../../devices/actions";
import { import { hasUTM } from "../../devices/components/firmware_hardware_support";
isExpressBoard, import { mapStateToPropsEdit } from "./state_to_props";
} from "../../devices/components/firmware_hardware_support"; import { EditToolSlotProps } from "./interfaces";
import { EditToolSlotProps, mapStateToPropsEdit } from "./map_to_props_add_edit";
export class RawEditToolSlot extends React.Component<EditToolSlotProps> { export class RawEditToolSlot extends React.Component<EditToolSlotProps> {
@ -44,7 +43,7 @@ export class RawEditToolSlot extends React.Component<EditToolSlotProps> {
{toolSlot {toolSlot
? <div className={"edit-tool-slot-content-wrapper"}> ? <div className={"edit-tool-slot-content-wrapper"}>
<SlotEditRows <SlotEditRows
isExpress={isExpressBoard(this.props.firmwareHardware)} noUTM={!hasUTM(this.props.firmwareHardware)}
toolSlot={toolSlot} toolSlot={toolSlot}
tools={this.props.tools} tools={this.props.tools}
tool={this.tool} tool={this.tool}

View File

@ -3,21 +3,12 @@ import { connect } from "react-redux";
import { import {
DesignerPanel, DesignerPanelTop, DesignerPanelContent, DesignerPanel, DesignerPanelTop, DesignerPanelContent,
} from "../designer_panel"; } from "../designer_panel";
import { Everything } from "../../interfaces";
import { DesignerNavTabs, Panel, TAB_COLOR } from "../panel_header"; import { DesignerNavTabs, Panel, TAB_COLOR } from "../panel_header";
import { import {
EmptyStateWrapper, EmptyStateGraphic, EmptyStateWrapper, EmptyStateGraphic,
} from "../../ui/empty_state_wrapper"; } from "../../ui/empty_state_wrapper";
import { t } from "../../i18next_wrapper"; import { t } from "../../i18next_wrapper";
import { import { TaggedTool, TaggedToolSlotPointer, TaggedSensor } from "farmbot";
TaggedTool, TaggedToolSlotPointer, TaggedDevice, TaggedSensor,
FirmwareHardware,
} from "farmbot";
import {
selectAllTools, selectAllToolSlotPointers, getDeviceAccountSettings,
maybeFindToolById,
selectAllSensors,
} from "../../resources/selectors";
import { Content } from "../../constants"; import { Content } from "../../constants";
import { history } from "../../history"; import { history } from "../../history";
import { Row, Col, Help } from "../../ui"; import { Row, Col, Help } from "../../ui";
@ -26,47 +17,15 @@ import { Link } from "../../link";
import { edit, save } from "../../api/crud"; import { edit, save } from "../../api/crud";
import { readPin } from "../../devices/actions"; import { readPin } from "../../devices/actions";
import { isBotOnlineFromState } from "../../devices/must_be_online"; import { isBotOnlineFromState } from "../../devices/must_be_online";
import { BotState } from "../../devices/interfaces";
import { import {
setToolHover, ToolSlotSVG, ToolSVG, setToolHover, ToolSlotSVG, ToolSVG,
} from "../map/layers/tool_slots/tool_graphics"; } from "../map/layers/tool_slots/tool_graphics";
import { ToolSelection } from "./tool_slot_edit_components"; import { ToolSelection } from "./tool_slot_edit_components";
import { error } from "../../toast/toast"; import { error } from "../../toast/toast";
import { import { hasUTM } from "../../devices/components/firmware_hardware_support";
isExpressBoard, getFwHardwareValue, import { ToolsProps, ToolsState } from "./interfaces";
} from "../../devices/components/firmware_hardware_support"; import { mapStateToProps } from "./state_to_props";
import { getFbosConfig } from "../../resources/getters"; import { BotOriginQuadrant } from "../interfaces";
import { isActive } from "./edit_tool";
export interface ToolsProps {
tools: TaggedTool[];
toolSlots: TaggedToolSlotPointer[];
dispatch: Function;
findTool(id: number): TaggedTool | undefined;
device: TaggedDevice;
sensors: TaggedSensor[];
bot: BotState;
hoveredToolSlot: string | undefined;
firmwareHardware: FirmwareHardware | undefined;
isActive(id: number | undefined): boolean;
}
export interface ToolsState {
searchTerm: string;
}
export const mapStateToProps = (props: Everything): ToolsProps => ({
tools: selectAllTools(props.resources.index),
toolSlots: selectAllToolSlotPointers(props.resources.index),
dispatch: props.dispatch,
findTool: (id: number) => maybeFindToolById(props.resources.index, id),
device: getDeviceAccountSettings(props.resources.index),
sensors: selectAllSensors(props.resources.index),
bot: props.bot,
hoveredToolSlot: props.resources.consumers.farm_designer.hoveredToolSlot,
firmwareHardware: getFwHardwareValue(getFbosConfig(props.resources.index)),
isActive: isActive(selectAllToolSlotPointers(props.resources.index)),
});
const toolStatus = (value: number | undefined): string => { const toolStatus = (value: number | undefined): string => {
switch (value) { switch (value) {
@ -112,7 +71,7 @@ export class RawTools extends React.Component<ToolsProps, ToolsState> {
get botOnline() { return isBotOnlineFromState(this.props.bot); } get botOnline() { return isBotOnlineFromState(this.props.bot); }
get isExpress() { return isExpressBoard(this.props.firmwareHardware); } get noUTM() { return !hasUTM(this.props.firmwareHardware); }
MountedToolInfo = () => MountedToolInfo = () =>
<div className="mounted-tool"> <div className="mounted-tool">
@ -165,7 +124,9 @@ export class RawTools extends React.Component<ToolsProps, ToolsState> {
dispatch={this.props.dispatch} dispatch={this.props.dispatch}
toolSlot={toolSlot} toolSlot={toolSlot}
isActive={this.props.isActive} isActive={this.props.isActive}
tools={this.props.tools} />)} tools={this.props.tools}
xySwap={this.props.xySwap}
quadrant={this.props.quadrant} />)}
</div> </div>
Tools = () => Tools = () =>
@ -192,16 +153,16 @@ export class RawTools extends React.Component<ToolsProps, ToolsState> {
get strings() { get strings() {
return { return {
placeholder: this.isExpress placeholder: this.noUTM
? t("Search your seed containers...") ? t("Search your seed containers...")
: t("Search your tools..."), : t("Search your tools..."),
titleText: this.isExpress titleText: this.noUTM
? t("Add a seed container") ? t("Add a seed container")
: t("Add a tool or seed container"), : t("Add a tool or seed container"),
emptyStateText: this.isExpress emptyStateText: this.noUTM
? Content.NO_SEED_CONTAINERS ? Content.NO_SEED_CONTAINERS
: Content.NO_TOOLS, : Content.NO_TOOLS,
tools: this.isExpress tools: this.noUTM
? t("seed containers") ? t("seed containers")
: t("tools and seed containers"), : t("tools and seed containers"),
toolSlots: t("slots"), toolSlots: t("slots"),
@ -228,8 +189,7 @@ export class RawTools extends React.Component<ToolsProps, ToolsState> {
title={this.strings.titleText} title={this.strings.titleText}
text={this.strings.emptyStateText} text={this.strings.emptyStateText}
colorScheme={"tools"}> colorScheme={"tools"}>
{!this.isExpress && {!this.noUTM && <this.MountedToolInfo />}
<this.MountedToolInfo />}
<this.ToolSlots /> <this.ToolSlots />
<this.Tools /> <this.Tools />
</EmptyStateWrapper> </EmptyStateWrapper>
@ -244,6 +204,8 @@ export interface ToolSlotInventoryItemProps {
hovered: boolean; hovered: boolean;
dispatch: Function; dispatch: Function;
isActive(id: number | undefined): boolean; isActive(id: number | undefined): boolean;
xySwap: boolean;
quadrant: BotOriginQuadrant;
} }
export const ToolSlotInventoryItem = (props: ToolSlotInventoryItemProps) => { export const ToolSlotInventoryItem = (props: ToolSlotInventoryItemProps) => {
@ -260,7 +222,7 @@ export const ToolSlotInventoryItem = (props: ToolSlotInventoryItemProps) => {
<ToolSlotSVG <ToolSlotSVG
toolSlot={props.toolSlot} toolSlot={props.toolSlot}
toolName={tool_id ? toolName : "Empty"} toolName={tool_id ? toolName : "Empty"}
renderRotation={false} /> xySwap={props.xySwap} quadrant={props.quadrant} />
</Col> </Col>
<Col xs={6}> <Col xs={6}>
<div className={"tool-selection-wrapper"} <div className={"tool-selection-wrapper"}

View File

@ -0,0 +1,71 @@
import { UUID } from "../../resources/interfaces";
import {
FirmwareHardware, TaggedTool, TaggedToolSlotPointer,
TaggedDevice, TaggedSensor,
} from "farmbot";
import { BotOriginQuadrant } from "../interfaces";
import { BotState, BotPosition } from "../../devices/interfaces";
export interface AddToolSlotState {
uuid: UUID | undefined;
}
export interface AddToolProps {
dispatch: Function;
existingToolNames: string[];
firmwareHardware: FirmwareHardware | undefined;
}
export interface AddToolState {
toolName: string;
toAdd: string[];
}
export interface EditToolProps {
findTool(id: string): TaggedTool | undefined;
dispatch: Function;
mountedToolId: number | undefined;
isActive(id: number | undefined): boolean;
}
export interface EditToolState {
toolName: string;
}
export interface ToolsProps {
tools: TaggedTool[];
toolSlots: TaggedToolSlotPointer[];
dispatch: Function;
findTool(id: number): TaggedTool | undefined;
device: TaggedDevice;
sensors: TaggedSensor[];
bot: BotState;
hoveredToolSlot: string | undefined;
firmwareHardware: FirmwareHardware | undefined;
xySwap: boolean;
quadrant: BotOriginQuadrant;
isActive(id: number | undefined): boolean;
}
export interface ToolsState {
searchTerm: string;
}
export interface AddEditToolSlotPropsBase {
tools: TaggedTool[];
dispatch: Function;
botPosition: BotPosition;
findTool(id: number): TaggedTool | undefined;
firmwareHardware: FirmwareHardware | undefined;
xySwap: boolean;
quadrant: BotOriginQuadrant;
isActive(id: number | undefined): boolean;
}
export interface AddToolSlotProps extends AddEditToolSlotPropsBase {
findToolSlot(uuid: UUID | undefined): TaggedToolSlotPointer | undefined;
}
export interface EditToolSlotProps extends AddEditToolSlotPropsBase {
findToolSlot(id: string): TaggedToolSlotPointer | undefined;
}

View File

@ -1,10 +1,10 @@
import { Everything } from "../../interfaces"; import { Everything } from "../../interfaces";
import { TaggedTool, TaggedToolSlotPointer, FirmwareHardware } from "farmbot";
import { import {
selectAllTools, maybeFindToolById, maybeGetToolSlot, maybeFindToolSlotById, selectAllTools, maybeFindToolById, maybeGetToolSlot, maybeFindToolSlotById,
selectAllToolSlotPointers, selectAllToolSlotPointers,
getDeviceAccountSettings,
selectAllSensors,
} from "../../resources/selectors"; } from "../../resources/selectors";
import { BotPosition } from "../../devices/interfaces";
import { validBotLocationData } from "../../util"; import { validBotLocationData } from "../../util";
import { UUID } from "../../resources/interfaces"; import { UUID } from "../../resources/interfaces";
import { import {
@ -13,21 +13,35 @@ import {
import { getFbosConfig } from "../../resources/getters"; import { getFbosConfig } from "../../resources/getters";
import { getWebAppConfigValue } from "../../config_storage/actions"; import { getWebAppConfigValue } from "../../config_storage/actions";
import { BooleanSetting, NumericSetting } from "../../session_keys"; import { BooleanSetting, NumericSetting } from "../../session_keys";
import { BotOriginQuadrant, isBotOriginQuadrant } from "../interfaces"; import { isBotOriginQuadrant } from "../interfaces";
import { isActive } from "./edit_tool"; import { isActive } from "./edit_tool";
import {
AddEditToolSlotPropsBase, AddToolSlotProps, EditToolSlotProps, ToolsProps,
} from "./interfaces";
export interface AddEditToolSlotPropsBase { export const mapStateToProps = (props: Everything): ToolsProps => {
tools: TaggedTool[]; const getWebAppConfig = getWebAppConfigValue(() => props);
dispatch: Function; const xySwap = !!getWebAppConfig(BooleanSetting.xy_swap);
botPosition: BotPosition; const rawQuadrant = getWebAppConfig(NumericSetting.bot_origin_quadrant);
findTool(id: number): TaggedTool | undefined; const quadrant = isBotOriginQuadrant(rawQuadrant) ? rawQuadrant : 2;
firmwareHardware: FirmwareHardware | undefined; return {
xySwap: boolean; tools: selectAllTools(props.resources.index),
quadrant: BotOriginQuadrant; toolSlots: selectAllToolSlotPointers(props.resources.index),
isActive(id: number | undefined): boolean; dispatch: props.dispatch,
} findTool: (id: number) => maybeFindToolById(props.resources.index, id),
device: getDeviceAccountSettings(props.resources.index),
sensors: selectAllSensors(props.resources.index),
bot: props.bot,
hoveredToolSlot: props.resources.consumers.farm_designer.hoveredToolSlot,
firmwareHardware: getFwHardwareValue(getFbosConfig(props.resources.index)),
isActive: isActive(selectAllToolSlotPointers(props.resources.index)),
xySwap,
quadrant,
};
};
export const mapStateToPropsBase = (props: Everything): AddEditToolSlotPropsBase => { export const mapStateToPropsAddEditBase = (props: Everything):
AddEditToolSlotPropsBase => {
const getWebAppConfig = getWebAppConfigValue(() => props); const getWebAppConfig = getWebAppConfigValue(() => props);
const xySwap = !!getWebAppConfig(BooleanSetting.xy_swap); const xySwap = !!getWebAppConfig(BooleanSetting.xy_swap);
const rawQuadrant = getWebAppConfig(NumericSetting.bot_origin_quadrant); const rawQuadrant = getWebAppConfig(NumericSetting.bot_origin_quadrant);
@ -44,24 +58,16 @@ export const mapStateToPropsBase = (props: Everything): AddEditToolSlotPropsBase
}; };
}; };
export interface AddToolSlotProps extends AddEditToolSlotPropsBase {
findToolSlot(uuid: UUID | undefined): TaggedToolSlotPointer | undefined;
}
export const mapStateToPropsAdd = (props: Everything): AddToolSlotProps => { export const mapStateToPropsAdd = (props: Everything): AddToolSlotProps => {
const mapStateToProps = mapStateToPropsBase(props) as AddToolSlotProps; const stateToProps = mapStateToPropsAddEditBase(props) as AddToolSlotProps;
mapStateToProps.findToolSlot = (uuid: UUID | undefined) => stateToProps.findToolSlot = (uuid: UUID | undefined) =>
maybeGetToolSlot(props.resources.index, uuid); maybeGetToolSlot(props.resources.index, uuid);
return mapStateToProps; return stateToProps;
}; };
export interface EditToolSlotProps extends AddEditToolSlotPropsBase {
findToolSlot(id: string): TaggedToolSlotPointer | undefined;
}
export const mapStateToPropsEdit = (props: Everything): EditToolSlotProps => { export const mapStateToPropsEdit = (props: Everything): EditToolSlotProps => {
const mapStateToProps = mapStateToPropsBase(props) as EditToolSlotProps; const stateToProps = mapStateToPropsAddEditBase(props) as EditToolSlotProps;
mapStateToProps.findToolSlot = (id: string) => stateToProps.findToolSlot = (id: string) =>
maybeFindToolSlotById(props.resources.index, parseInt(id)); maybeFindToolSlotById(props.resources.index, parseInt(id));
return mapStateToProps; return stateToProps;
}; };

View File

@ -8,8 +8,8 @@ import { BotPosition } from "../../devices/interfaces";
import { ToolPulloutDirection } from "farmbot/dist/resources/api_resources"; import { ToolPulloutDirection } from "farmbot/dist/resources/api_resources";
import { Popover } from "@blueprintjs/core"; import { Popover } from "@blueprintjs/core";
import { ToolSlotSVG } from "../map/layers/tool_slots/tool_graphics"; import { ToolSlotSVG } from "../map/layers/tool_slots/tool_graphics";
import { BotOriginQuadrant } from "../interfaces";
import { isNumber } from "lodash"; import { isNumber } from "lodash";
import { BotOriginQuadrant } from "../interfaces";
export interface GantryMountedInputProps { export interface GantryMountedInputProps {
gantryMounted: boolean; gantryMounted: boolean;
@ -32,7 +32,7 @@ export interface SlotDirectionInputRowProps {
export const SlotDirectionInputRow = (props: SlotDirectionInputRowProps) => export const SlotDirectionInputRow = (props: SlotDirectionInputRowProps) =>
<fieldset className="tool-slot-direction-input"> <fieldset className="tool-slot-direction-input">
<label> <label>
{t("Change direction")} {t("Direction")}
</label> </label>
<i className={"direction-icon " <i className={"direction-icon "
+ directionIconClass(props.toolPulloutDirection)} + directionIconClass(props.toolPulloutDirection)}
@ -81,7 +81,7 @@ export interface ToolInputRowProps {
tools: TaggedTool[]; tools: TaggedTool[];
selectedTool: TaggedTool | undefined; selectedTool: TaggedTool | undefined;
onChange(update: { tool_id: number }): void; onChange(update: { tool_id: number }): void;
isExpress: boolean; noUTM: boolean;
isActive(id: number | undefined): boolean; isActive(id: number | undefined): boolean;
} }
@ -90,7 +90,7 @@ export const ToolInputRow = (props: ToolInputRowProps) =>
<Row> <Row>
<Col xs={12}> <Col xs={12}>
<label> <label>
{props.isExpress {props.noUTM
? t("Seed Container") ? t("Seed Container")
: t("Tool or Seed Container")} : t("Tool or Seed Container")}
</label> </label>
@ -155,7 +155,7 @@ export interface SlotEditRowsProps {
tool: TaggedTool | undefined; tool: TaggedTool | undefined;
botPosition: BotPosition; botPosition: BotPosition;
updateToolSlot(update: Partial<TaggedToolSlotPointer["body"]>): void; updateToolSlot(update: Partial<TaggedToolSlotPointer["body"]>): void;
isExpress: boolean; noUTM: boolean;
xySwap: boolean; xySwap: boolean;
quadrant: BotOriginQuadrant; quadrant: BotOriginQuadrant;
isActive(id: number | undefined): boolean; isActive(id: number | undefined): boolean;
@ -165,14 +165,14 @@ export const SlotEditRows = (props: SlotEditRowsProps) =>
<div className="tool-slot-edit-rows"> <div className="tool-slot-edit-rows">
<ToolSlotSVG toolSlot={props.toolSlot} <ToolSlotSVG toolSlot={props.toolSlot}
toolName={props.tool ? props.tool.body.name : "Empty"} toolName={props.tool ? props.tool.body.name : "Empty"}
renderRotation={true} xySwap={props.xySwap} quadrant={props.quadrant} /> xySwap={props.xySwap} quadrant={props.quadrant} />
<SlotLocationInputRow <SlotLocationInputRow
slotLocation={props.toolSlot.body} slotLocation={props.toolSlot.body}
gantryMounted={props.toolSlot.body.gantry_mounted} gantryMounted={props.toolSlot.body.gantry_mounted}
botPosition={props.botPosition} botPosition={props.botPosition}
onChange={props.updateToolSlot} /> onChange={props.updateToolSlot} />
<ToolInputRow <ToolInputRow
isExpress={props.isExpress} noUTM={props.noUTM}
tools={props.tools} tools={props.tools}
selectedTool={props.tool} selectedTool={props.tool}
isActive={props.isActive} isActive={props.isActive}
@ -181,7 +181,7 @@ export const SlotEditRows = (props: SlotEditRowsProps) =>
<SlotDirectionInputRow <SlotDirectionInputRow
toolPulloutDirection={props.toolSlot.body.pullout_direction} toolPulloutDirection={props.toolSlot.body.pullout_direction}
onChange={props.updateToolSlot} />} onChange={props.updateToolSlot} />}
{!props.isExpress && {!props.noUTM &&
<GantryMountedInput <GantryMountedInput
gantryMounted={props.toolSlot.body.gantry_mounted} gantryMounted={props.toolSlot.body.gantry_mounted}
onChange={props.updateToolSlot} />} onChange={props.updateToolSlot} />}