reorganization
parent
622eaa4696
commit
5b19a55f56
|
@ -30,7 +30,6 @@ import "../regimens/editor/interfaces";
|
|||
import "../regimens/interfaces";
|
||||
import "../resources/interfaces";
|
||||
import "../sequences/interfaces";
|
||||
import "../sequences/step_tiles/tile_move_absolute/interfaces";
|
||||
import "../tools/interfaces";
|
||||
|
||||
describe("interfaces", () => {
|
||||
|
|
|
@ -72,3 +72,21 @@ export interface WebcamFeed {
|
|||
updated_at?: string;
|
||||
created_at?: string;
|
||||
}
|
||||
|
||||
export interface Sensor {
|
||||
id?: number;
|
||||
pin: number | undefined;
|
||||
label: string;
|
||||
mode: number;
|
||||
}
|
||||
|
||||
export interface SensorReading {
|
||||
id?: number | undefined;
|
||||
x: number | undefined;
|
||||
y: number | undefined;
|
||||
z: number | undefined;
|
||||
value: number;
|
||||
mode: number;
|
||||
pin: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import {
|
|||
import { error, warning } from "farmbot-toastr";
|
||||
import {
|
||||
fakeResourceIndex
|
||||
} from "../../../sequences/step_tiles/tile_move_absolute/test_helpers";
|
||||
} from "../../../sequences/locals_list/test_helpers";
|
||||
import {
|
||||
PinBindingType, PinBindingSpecialAction
|
||||
} from "farmbot/dist/resources/api_resources";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from "react";
|
||||
import { t } from "i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Everything, Color } from "../../interfaces";
|
||||
import { Everything, ResourceColor } from "../../interfaces";
|
||||
import { initSave } from "../../api/crud";
|
||||
import {
|
||||
Row, Col, BlurableInput, ColorPicker
|
||||
|
@ -91,7 +91,7 @@ export class CreatePoints
|
|||
};
|
||||
}
|
||||
|
||||
changeColor = (color: Color) => {
|
||||
changeColor = (color: ResourceColor) => {
|
||||
this.setState({ color });
|
||||
if (this.props.currentPoint) {
|
||||
const { cx, cy, r } = this.props.currentPoint;
|
||||
|
@ -149,7 +149,7 @@ export class CreatePoints
|
|||
<Col xs={3}>
|
||||
<label>{t("color")}</label>
|
||||
<ColorPicker
|
||||
current={color as Color || "green"}
|
||||
current={color as ResourceColor || "green"}
|
||||
onChange={this.changeColor} />
|
||||
</Col>
|
||||
</Row>;
|
||||
|
|
|
@ -33,3 +33,9 @@ export interface FarmwareConfigMenuProps {
|
|||
}
|
||||
|
||||
export type Farmwares = Dictionary<FarmwareManifest | undefined>;
|
||||
|
||||
export interface FarmwareEnv {
|
||||
id?: number;
|
||||
key: string;
|
||||
value: string | number | boolean;
|
||||
}
|
||||
|
|
|
@ -2,38 +2,13 @@ import { AuthState } from "./auth/interfaces";
|
|||
import { ConfigState } from "./config/interfaces";
|
||||
import { BotState } from "./devices/interfaces";
|
||||
import { Color as FarmBotJsColor } from "farmbot";
|
||||
import { Point } from "farmbot/dist/resources/api_resources";
|
||||
import { DraggableState } from "./draggable/interfaces";
|
||||
import { PeripheralState } from "./controls/peripherals/interfaces";
|
||||
import { RestResources } from "./resources/interfaces";
|
||||
|
||||
/** Regimens and sequences may have a "color" which determines how it looks
|
||||
in the UI. Only certain colors are valid. */
|
||||
export type Color = FarmBotJsColor;
|
||||
|
||||
export interface Sensor {
|
||||
id?: number;
|
||||
pin: number | undefined;
|
||||
label: string;
|
||||
mode: number;
|
||||
}
|
||||
|
||||
export interface SensorReading {
|
||||
id?: number | undefined;
|
||||
x: number | undefined;
|
||||
y: number | undefined;
|
||||
z: number | undefined;
|
||||
value: number;
|
||||
mode: number;
|
||||
pin: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface FarmwareEnv {
|
||||
id?: number;
|
||||
key: string;
|
||||
value: string | number | boolean;
|
||||
}
|
||||
export type ResourceColor = FarmBotJsColor;
|
||||
|
||||
export interface Everything {
|
||||
config: ConfigState;
|
||||
|
@ -51,5 +26,3 @@ export interface Everything {
|
|||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
export type UnsafeError = any;
|
||||
|
||||
export type PointerTypeName = Point["pointer_type"];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Color } from "../interfaces";
|
||||
import { ResourceColor } from "../interfaces";
|
||||
import { Week } from "./bulk_scheduler/interfaces";
|
||||
import { AuthState } from "../auth/interfaces";
|
||||
import { BotState, ShouldDisplay } from "../devices/interfaces";
|
||||
|
@ -50,7 +50,7 @@ export interface Regimen {
|
|||
id?: number;
|
||||
/** Friendly identifier for humans to easily identify regimens. */
|
||||
name: string;
|
||||
color: Color;
|
||||
color: ResourceColor;
|
||||
regimen_items: RegimenItem[];
|
||||
}
|
||||
|
||||
|
|
|
@ -11,10 +11,10 @@ import {
|
|||
import { buildResourceIndex } from "../../__test_support__/resource_index_builder";
|
||||
import {
|
||||
sanitizeNodes
|
||||
} from "../../sequences/step_tiles/tile_move_absolute/variables_support";
|
||||
} from "../../sequences/locals_list/variables_support";
|
||||
import {
|
||||
formatPoint
|
||||
} from "../../sequences/step_tiles/tile_move_absolute/generate_list";
|
||||
} from "../../sequences/locals_list/location_form_list";
|
||||
|
||||
describe("determineDropdown", () => {
|
||||
it("Returns a label for `parameter_declarations`", () => {
|
||||
|
|
|
@ -13,7 +13,7 @@ import { TaggedResource, TaggedSequence } from "farmbot";
|
|||
import { ResourceIndex } from "./interfaces";
|
||||
import {
|
||||
sanitizeNodes
|
||||
} from "../sequences/step_tiles/tile_move_absolute/variables_support";
|
||||
} from "../sequences/locals_list/variables_support";
|
||||
import {
|
||||
selectAllFarmEvents,
|
||||
findByKindAndId,
|
||||
|
|
|
@ -10,7 +10,7 @@ import { findSlotByToolId, findToolById } from "./selectors_by_id";
|
|||
import { capitalize } from "lodash";
|
||||
import {
|
||||
formatPoint, safeEveryPointType, everyPointDDI
|
||||
} from "../sequences/step_tiles/tile_move_absolute/generate_list";
|
||||
} from "../sequences/locals_list/location_form_list";
|
||||
|
||||
export interface SequenceMeta {
|
||||
celeryNode: ScopeDeclarationBodyItem;
|
||||
|
|
|
@ -2,7 +2,7 @@ import { get, set } from "lodash";
|
|||
import { SequenceBodyItem, uuid } from "farmbot/dist";
|
||||
import {
|
||||
Traversable
|
||||
} from "../sequences/step_tiles/tile_move_absolute/variables_support";
|
||||
} from "../sequences/locals_list/variables_support";
|
||||
|
||||
/** HISTORICAL NOTES:
|
||||
* This file is the result of some very subtle bugs relating to dynamic
|
||||
|
|
|
@ -6,7 +6,7 @@ import { TaggedSequence, SpecialStatus } from "farmbot";
|
|||
import { TileMoveRelative } from "../step_tiles/tile_move_relative";
|
||||
import { TileReadPin } from "../step_tiles/tile_read_pin";
|
||||
import { TileWritePin } from "../step_tiles/tile_write_pin";
|
||||
import { sanitizeNodes } from "../step_tiles/tile_move_absolute/variables_support";
|
||||
import { sanitizeNodes } from "../locals_list/variables_support";
|
||||
|
||||
describe("<AllSteps/>", () => {
|
||||
const TEST_CASE: TaggedSequence = {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Color } from "../interfaces";
|
||||
import { ResourceColor } from "../interfaces";
|
||||
import {
|
||||
Sequence as CeleryScriptSequence,
|
||||
SequenceBodyItem,
|
||||
|
@ -70,7 +70,7 @@ export const NUMERIC_FIELDS = INT_NUMERIC_FIELDS.concat(FLOAT_NUMERIC_FIELDS);
|
|||
|
||||
export interface Sequence extends CeleryScriptSequence {
|
||||
id?: number;
|
||||
color: Color;
|
||||
color: ResourceColor;
|
||||
name: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import {
|
||||
localListCallback, manuallyEditAxis, AxisEditProps
|
||||
} from "../locals_list";
|
||||
import { localListCallback } from "../locals_list";
|
||||
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
|
||||
import { inputEvent } from "../../../__test_support__/fake_input_event";
|
||||
import { VariableDeclaration, ScopeDeclarationBodyItem } from "farmbot";
|
||||
import { AxisEditProps, manuallyEditAxis } from "../location_form";
|
||||
|
||||
describe("localListCallback", () => {
|
||||
it("handles a new local declaration", () => {
|
||||
|
|
|
@ -1,87 +1,16 @@
|
|||
import * as React from "react";
|
||||
import { LocationForm, LocalsList } from "../locals_list";
|
||||
import { LocalsList } from "../locals_list";
|
||||
import { VariableDeclaration, Coordinate } from "farmbot";
|
||||
import {
|
||||
fakeSequence
|
||||
} from "../../../__test_support__/fake_state/resources";
|
||||
import { shallow, mount } from "enzyme";
|
||||
import { shallow } from "enzyme";
|
||||
import {
|
||||
buildResourceIndex
|
||||
} from "../../../__test_support__/resource_index_builder";
|
||||
import { FBSelect, BlurableInput } from "../../../ui/index";
|
||||
import {
|
||||
LocationFormProps, LocalsListProps, PARENT, AllowedDeclaration
|
||||
} from "../locals_list_support";
|
||||
import { difference } from "lodash";
|
||||
import { LocalsListProps, AllowedDeclaration } from "../locals_list_support";
|
||||
import { VariableNameSet } from "../../../resources/interfaces";
|
||||
import { locationFormList } from "../../step_tiles/tile_move_absolute/generate_list";
|
||||
import { convertDDItoDeclaration } from "../handle_select";
|
||||
|
||||
describe("<LocationForm/>", () => {
|
||||
const fakeProps = (): LocationFormProps => ({
|
||||
variable: {
|
||||
celeryNode: {
|
||||
kind: "parameter_declaration",
|
||||
args: { label: "label", data_type: "coordinate" }
|
||||
},
|
||||
dropdown: { label: "label", value: 0 },
|
||||
vector: { x: 0, y: 0, z: 0 }
|
||||
},
|
||||
sequenceUuid: fakeSequence().uuid,
|
||||
resources: buildResourceIndex().index,
|
||||
onChange: jest.fn(),
|
||||
shouldDisplay: jest.fn(),
|
||||
allowedDeclarations: AllowedDeclaration.parameter,
|
||||
});
|
||||
|
||||
it("renders correct UI components", () => {
|
||||
const p = fakeProps();
|
||||
const el = shallow(<LocationForm {...p} />);
|
||||
const selects = el.find(FBSelect);
|
||||
const inputs = el.find(BlurableInput);
|
||||
|
||||
expect(selects.length).toBe(1);
|
||||
const select = selects.first().props();
|
||||
expect(select.allowEmpty).toBe(true);
|
||||
const choices = locationFormList(p.resources, [PARENT], true);
|
||||
const actualLabels = select.list.map(x => x.label).sort();
|
||||
const expectedLabels = choices.map(x => x.label).sort();
|
||||
const diff = difference(actualLabels, expectedLabels);
|
||||
expect(diff).toEqual([]);
|
||||
const choice = choices[1];
|
||||
select.onChange(choice);
|
||||
expect(p.onChange)
|
||||
.toHaveBeenCalledWith(convertDDItoDeclaration({ label: "label" })(choice));
|
||||
expect(inputs.length).toBe(3);
|
||||
});
|
||||
|
||||
it("uses local declaration data", () => {
|
||||
const p = fakeProps();
|
||||
p.declarations = [{
|
||||
kind: "variable_declaration",
|
||||
args: {
|
||||
label: "label", data_value: {
|
||||
kind: "identifier", args: { label: "new_var" }
|
||||
}
|
||||
}
|
||||
}];
|
||||
const wrapper = mount(<LocationForm {...p} />);
|
||||
expect(wrapper.text().toLowerCase()).toContain("new_var");
|
||||
});
|
||||
|
||||
it("shows parent in dropdown", () => {
|
||||
const p = fakeProps();
|
||||
p.shouldDisplay = () => true;
|
||||
const wrapper = shallow(<LocationForm {...p} />);
|
||||
expect(wrapper.find(FBSelect).first().props().list).toContain(PARENT);
|
||||
});
|
||||
|
||||
it("doesn't show parent in dropdown", () => {
|
||||
const p = fakeProps();
|
||||
const wrapper = shallow(<LocationForm {...p} />);
|
||||
expect(wrapper.find(FBSelect).first().props().list).not.toContain(PARENT);
|
||||
});
|
||||
});
|
||||
import { LocationForm } from "../location_form";
|
||||
|
||||
describe("<LocalsList/>", () => {
|
||||
const coordinate: Coordinate = {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { locationFormList, dropDownName } from "../generate_list";
|
||||
import { locationFormList, dropDownName } from "../location_form_list";
|
||||
import { fakeResourceIndex } from "../test_helpers";
|
||||
|
||||
describe("locationFormList()", () => {
|
|
@ -0,0 +1,82 @@
|
|||
import * as React from "react";
|
||||
import { LocationForm } from "../location_form";
|
||||
import {
|
||||
fakeSequence
|
||||
} from "../../../__test_support__/fake_state/resources";
|
||||
import { shallow, mount } from "enzyme";
|
||||
import {
|
||||
buildResourceIndex
|
||||
} from "../../../__test_support__/resource_index_builder";
|
||||
import { FBSelect, BlurableInput } from "../../../ui/index";
|
||||
import {
|
||||
LocationFormProps, PARENT, AllowedDeclaration
|
||||
} from "../locals_list_support";
|
||||
import { difference } from "lodash";
|
||||
import { locationFormList } from "../location_form_list";
|
||||
import { convertDDItoDeclaration } from "../handle_select";
|
||||
|
||||
describe("<LocationForm/>", () => {
|
||||
const fakeProps = (): LocationFormProps => ({
|
||||
variable: {
|
||||
celeryNode: {
|
||||
kind: "parameter_declaration",
|
||||
args: { label: "label", data_type: "coordinate" }
|
||||
},
|
||||
dropdown: { label: "label", value: 0 },
|
||||
vector: { x: 0, y: 0, z: 0 }
|
||||
},
|
||||
sequenceUuid: fakeSequence().uuid,
|
||||
resources: buildResourceIndex().index,
|
||||
onChange: jest.fn(),
|
||||
shouldDisplay: jest.fn(),
|
||||
allowedDeclarations: AllowedDeclaration.parameter,
|
||||
});
|
||||
|
||||
it("renders correct UI components", () => {
|
||||
const p = fakeProps();
|
||||
const el = shallow(<LocationForm {...p} />);
|
||||
const selects = el.find(FBSelect);
|
||||
const inputs = el.find(BlurableInput);
|
||||
|
||||
expect(selects.length).toBe(1);
|
||||
const select = selects.first().props();
|
||||
expect(select.allowEmpty).toBe(true);
|
||||
const choices = locationFormList(p.resources, [PARENT], true);
|
||||
const actualLabels = select.list.map(x => x.label).sort();
|
||||
const expectedLabels = choices.map(x => x.label).sort();
|
||||
const diff = difference(actualLabels, expectedLabels);
|
||||
expect(diff).toEqual([]);
|
||||
const choice = choices[1];
|
||||
select.onChange(choice);
|
||||
expect(p.onChange)
|
||||
.toHaveBeenCalledWith(convertDDItoDeclaration({ label: "label" })(choice));
|
||||
expect(inputs.length).toBe(3);
|
||||
});
|
||||
|
||||
it("uses local declaration data", () => {
|
||||
const p = fakeProps();
|
||||
p.declarations = [{
|
||||
kind: "variable_declaration",
|
||||
args: {
|
||||
label: "label", data_value: {
|
||||
kind: "identifier", args: { label: "new_var" }
|
||||
}
|
||||
}
|
||||
}];
|
||||
const wrapper = mount(<LocationForm {...p} />);
|
||||
expect(wrapper.text().toLowerCase()).toContain("new_var");
|
||||
});
|
||||
|
||||
it("shows parent in dropdown", () => {
|
||||
const p = fakeProps();
|
||||
p.shouldDisplay = () => true;
|
||||
const wrapper = shallow(<LocationForm {...p} />);
|
||||
expect(wrapper.find(FBSelect).first().props().list).toContain(PARENT);
|
||||
});
|
||||
|
||||
it("doesn't show parent in dropdown", () => {
|
||||
const p = fakeProps();
|
||||
const wrapper = shallow(<LocationForm {...p} />);
|
||||
expect(wrapper.find(FBSelect).first().props().list).not.toContain(PARENT);
|
||||
});
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
import { fakeSequence } from "../../../../__test_support__/fake_state/resources";
|
||||
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
|
||||
import { MoveAbsolute } from "farmbot";
|
||||
import { sanitizeNodes } from "../variables_support";
|
||||
import { get } from "lodash";
|
|
@ -1,128 +1,16 @@
|
|||
import * as React from "react";
|
||||
import { Row, Col, FBSelect, BlurableInput } from "../../ui";
|
||||
import { t } from "i18next";
|
||||
import { locationFormList } from "../step_tiles/tile_move_absolute/generate_list";
|
||||
import { addOrEditDeclaration } from "../locals_list/handle_select";
|
||||
import {
|
||||
convertDDItoDeclaration, addOrEditDeclaration
|
||||
} from "../locals_list/handle_select";
|
||||
import {
|
||||
LocationFormProps, LocalsListProps, PARENT, AllowedDeclaration,
|
||||
LocalsListProps, AllowedDeclaration
|
||||
} from "../locals_list/locals_list_support";
|
||||
import { defensiveClone, betterCompact } from "../../util/util";
|
||||
import {
|
||||
Xyz,
|
||||
TaggedSequence,
|
||||
ScopeDeclarationBodyItem,
|
||||
ParameterDeclaration,
|
||||
} from "farmbot";
|
||||
import { overwrite } from "../../api/crud";
|
||||
import {
|
||||
determineVector, determineDropdown, determineEditable, SequenceMeta
|
||||
} from "../../resources/sequence_meta";
|
||||
import { ResourceIndex, UUID } from "../../resources/interfaces";
|
||||
import { Feature } from "../../devices/interfaces";
|
||||
|
||||
/** For LocationForm coordinate input boxes. */
|
||||
export interface AxisEditProps {
|
||||
axis: Xyz;
|
||||
onChange: (sd: ScopeDeclarationBodyItem) => void;
|
||||
declaration: ScopeDeclarationBodyItem;
|
||||
}
|
||||
|
||||
/** Update a VariableDeclaration coordinate. */
|
||||
export const manuallyEditAxis = (props: AxisEditProps) =>
|
||||
(e: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
const { axis, onChange, declaration } = props;
|
||||
const num = parseFloat(e.currentTarget.value);
|
||||
if (declaration.kind === "variable_declaration" &&
|
||||
declaration.args.data_value.kind === "coordinate") {
|
||||
declaration.args.data_value.args[axis] = num;
|
||||
!isNaN(num) && onChange(declaration);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* If a declaration with a matching label exists in local `declarations`
|
||||
* (step body, etc.), use it instead of the one in scope declarations.
|
||||
*/
|
||||
const maybeUseStepData = ({ resources, declarations, variable, uuid }: {
|
||||
resources: ResourceIndex,
|
||||
declarations: ScopeDeclarationBodyItem[] | undefined,
|
||||
variable: SequenceMeta,
|
||||
uuid: UUID,
|
||||
}): SequenceMeta => {
|
||||
if (declarations) {
|
||||
const executeStepData = declarations
|
||||
.filter(v => v.args.label === variable.celeryNode.args.label)[0];
|
||||
if (executeStepData) {
|
||||
return {
|
||||
celeryNode: executeStepData,
|
||||
vector: determineVector(executeStepData, resources, uuid),
|
||||
dropdown: determineDropdown(executeStepData, resources),
|
||||
};
|
||||
}
|
||||
}
|
||||
return variable;
|
||||
};
|
||||
|
||||
/**
|
||||
* Form with an "import from" dropdown and coordinate display/input boxes.
|
||||
* Can be used to set a specific value, import a value, or declare a variable.
|
||||
*/
|
||||
export const LocationForm =
|
||||
(props: LocationFormProps) => {
|
||||
const {
|
||||
sequenceUuid, resources, onChange, declarations, variable,
|
||||
hideVariableLabel, locationDropdownKey, allowedDeclarations,
|
||||
disallowGroups
|
||||
} = props;
|
||||
const { celeryNode, dropdown, vector } =
|
||||
maybeUseStepData({
|
||||
resources, declarations, variable, uuid: sequenceUuid
|
||||
});
|
||||
/** For disabling coordinate input boxes when using external data. */
|
||||
const isDisabled = !determineEditable(celeryNode);
|
||||
const useIdentifier = allowedDeclarations === AllowedDeclaration.identifier;
|
||||
const variableListItems = (props.shouldDisplay(Feature.variables) &&
|
||||
allowedDeclarations !== AllowedDeclaration.variable) ? [PARENT] : [];
|
||||
const list = locationFormList(resources, variableListItems, !disallowGroups);
|
||||
/** Variable name. */
|
||||
const { label } = celeryNode.args;
|
||||
const declaration = defensiveClone(celeryNode);
|
||||
const axisPartialProps = { onChange, declaration };
|
||||
const formTitle = hideVariableLabel
|
||||
? t("Location")
|
||||
: `${label} (${t("Location")})`;
|
||||
return <div className="location-form">
|
||||
<Row>
|
||||
<Col xs={12}>
|
||||
<label>{formTitle}</label>
|
||||
<FBSelect
|
||||
key={locationDropdownKey}
|
||||
allowEmpty={true}
|
||||
list={list}
|
||||
selectedItem={dropdown}
|
||||
customNullLabel={t("Coordinate")}
|
||||
onChange={ddi =>
|
||||
onChange(convertDDItoDeclaration({ label, useIdentifier })(ddi))} />
|
||||
</Col>
|
||||
</Row>
|
||||
{vector &&
|
||||
<Row>
|
||||
{["x", "y", "z"].map((axis: Xyz) =>
|
||||
<Col xs={props.width || 4} key={axis}>
|
||||
<label>
|
||||
{t("{{axis}} (mm)", { axis })}
|
||||
</label>
|
||||
<BlurableInput type="number"
|
||||
disabled={isDisabled}
|
||||
onCommit={manuallyEditAxis({ ...axisPartialProps, axis })}
|
||||
name={`location-${axis}`}
|
||||
value={"" + vector[axis]} />
|
||||
</Col>)}
|
||||
</Row>}
|
||||
</div>;
|
||||
};
|
||||
import { LocationForm } from "./location_form";
|
||||
|
||||
interface LocalListCbProps {
|
||||
dispatch: Function;
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
import * as React from "react";
|
||||
import { Row, Col, FBSelect, BlurableInput } from "../../ui";
|
||||
import { t } from "i18next";
|
||||
import { locationFormList } from "./location_form_list";
|
||||
import { convertDDItoDeclaration } from "../locals_list/handle_select";
|
||||
import {
|
||||
LocationFormProps, PARENT, AllowedDeclaration,
|
||||
} from "../locals_list/locals_list_support";
|
||||
import { defensiveClone } from "../../util/util";
|
||||
import { Xyz, ScopeDeclarationBodyItem } from "farmbot";
|
||||
import {
|
||||
determineVector, determineDropdown, determineEditable, SequenceMeta
|
||||
} from "../../resources/sequence_meta";
|
||||
import { ResourceIndex, UUID } from "../../resources/interfaces";
|
||||
import { Feature } from "../../devices/interfaces";
|
||||
|
||||
/** For LocationForm coordinate input boxes. */
|
||||
export interface AxisEditProps {
|
||||
axis: Xyz;
|
||||
onChange: (sd: ScopeDeclarationBodyItem) => void;
|
||||
declaration: ScopeDeclarationBodyItem;
|
||||
}
|
||||
|
||||
/** Update a VariableDeclaration coordinate. */
|
||||
export const manuallyEditAxis = (props: AxisEditProps) =>
|
||||
(e: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
const { axis, onChange, declaration } = props;
|
||||
const num = parseFloat(e.currentTarget.value);
|
||||
if (declaration.kind === "variable_declaration" &&
|
||||
declaration.args.data_value.kind === "coordinate") {
|
||||
declaration.args.data_value.args[axis] = num;
|
||||
!isNaN(num) && onChange(declaration);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* If a declaration with a matching label exists in local `declarations`
|
||||
* (step body, etc.), use it instead of the one in scope declarations.
|
||||
*/
|
||||
const maybeUseStepData = ({ resources, declarations, variable, uuid }: {
|
||||
resources: ResourceIndex,
|
||||
declarations: ScopeDeclarationBodyItem[] | undefined,
|
||||
variable: SequenceMeta,
|
||||
uuid: UUID,
|
||||
}): SequenceMeta => {
|
||||
if (declarations) {
|
||||
const executeStepData = declarations
|
||||
.filter(v => v.args.label === variable.celeryNode.args.label)[0];
|
||||
if (executeStepData) {
|
||||
return {
|
||||
celeryNode: executeStepData,
|
||||
vector: determineVector(executeStepData, resources, uuid),
|
||||
dropdown: determineDropdown(executeStepData, resources),
|
||||
};
|
||||
}
|
||||
}
|
||||
return variable;
|
||||
};
|
||||
|
||||
/**
|
||||
* Form with an "import from" dropdown and coordinate display/input boxes.
|
||||
* Can be used to set a specific value, import a value, or declare a variable.
|
||||
*/
|
||||
export const LocationForm =
|
||||
(props: LocationFormProps) => {
|
||||
const {
|
||||
sequenceUuid, resources, onChange, declarations, variable,
|
||||
hideVariableLabel, locationDropdownKey, allowedDeclarations,
|
||||
disallowGroups
|
||||
} = props;
|
||||
const { celeryNode, dropdown, vector } =
|
||||
maybeUseStepData({
|
||||
resources, declarations, variable, uuid: sequenceUuid
|
||||
});
|
||||
/** For disabling coordinate input boxes when using external data. */
|
||||
const isDisabled = !determineEditable(celeryNode);
|
||||
const useIdentifier = allowedDeclarations === AllowedDeclaration.identifier;
|
||||
const variableListItems = (props.shouldDisplay(Feature.variables) &&
|
||||
allowedDeclarations !== AllowedDeclaration.variable) ? [PARENT] : [];
|
||||
const list = locationFormList(resources, variableListItems, !disallowGroups);
|
||||
/** Variable name. */
|
||||
const { label } = celeryNode.args;
|
||||
const declaration = defensiveClone(celeryNode);
|
||||
const axisPartialProps = { onChange, declaration };
|
||||
const formTitle = hideVariableLabel
|
||||
? t("Location")
|
||||
: `${label} (${t("Location")})`;
|
||||
return <div className="location-form">
|
||||
<Row>
|
||||
<Col xs={12}>
|
||||
<label>{formTitle}</label>
|
||||
<FBSelect
|
||||
key={locationDropdownKey}
|
||||
allowEmpty={true}
|
||||
list={list}
|
||||
selectedItem={dropdown}
|
||||
customNullLabel={t("Coordinate")}
|
||||
onChange={ddi =>
|
||||
onChange(convertDDItoDeclaration({ label, useIdentifier })(ddi))} />
|
||||
</Col>
|
||||
</Row>
|
||||
{vector &&
|
||||
<Row>
|
||||
{["x", "y", "z"].map((axis: Xyz) =>
|
||||
<Col xs={props.width || 4} key={axis}>
|
||||
<label>
|
||||
{t("{{axis}} (mm)", { axis })}
|
||||
</label>
|
||||
<BlurableInput type="number"
|
||||
disabled={isDisabled}
|
||||
onCommit={manuallyEditAxis({ ...axisPartialProps, axis })}
|
||||
name={`location-${axis}`}
|
||||
value={"" + vector[axis]} />
|
||||
</Col>)}
|
||||
</Row>}
|
||||
</div>;
|
||||
};
|
|
@ -1,19 +1,19 @@
|
|||
import { ResourceIndex } from "../../../resources/interfaces";
|
||||
import { ResourceIndex } from "../../resources/interfaces";
|
||||
import {
|
||||
selectAllToolSlotPointers,
|
||||
selectAllActivePoints
|
||||
} from "../../../resources/selectors";
|
||||
import { betterCompact } from "../../../util";
|
||||
import { PointerTypeName } from "../../../interfaces";
|
||||
} from "../../resources/selectors";
|
||||
import { betterCompact } from "../../util";
|
||||
import { TaggedTool, TaggedPoint } from "farmbot";
|
||||
import { DropDownItem } from "../../../ui/index";
|
||||
import { DropDownItem } from "../../ui";
|
||||
import { Vector3 } from "farmbot/dist";
|
||||
import { TOOL } from "./interfaces";
|
||||
import * as _ from "lodash";
|
||||
import { t } from "i18next";
|
||||
import { capitalize } from "lodash";
|
||||
import { joinKindAndId } from "../../../resources/reducer_support";
|
||||
import { joinKindAndId } from "../../resources/reducer_support";
|
||||
import { Point } from "farmbot/dist/resources/api_resources";
|
||||
|
||||
const TOOL: "Tool" = "Tool";
|
||||
type ToolAndLocation = { tool: TaggedTool, location: Vector3 };
|
||||
|
||||
/** Return tool and location for all tools currently in tool slots. */
|
||||
|
@ -32,6 +32,7 @@ export function activeTools(resources: ResourceIndex): ToolAndLocation[] {
|
|||
: undefined));
|
||||
}
|
||||
|
||||
type PointerTypeName = Point["pointer_type"];
|
||||
type DropdownHeadingId = PointerTypeName | typeof TOOL | "Other";
|
||||
|
||||
/** Location selection menu section names. */
|
|
@ -1,7 +1,7 @@
|
|||
import { buildResourceIndex } from "../../../__test_support__/resource_index_builder";
|
||||
import { ResourceIndex } from "../../../resources/interfaces";
|
||||
import { buildResourceIndex } from "../../__test_support__/resource_index_builder";
|
||||
import { ResourceIndex } from "../../resources/interfaces";
|
||||
import { TaggedResource } from "farmbot";
|
||||
import { newTaggedResource } from "../../../sync/actions";
|
||||
import { newTaggedResource } from "../../sync/actions";
|
||||
|
||||
export function fakeResourceIndex(): ResourceIndex {
|
||||
const fakeResources: TaggedResource[] = [
|
|
@ -9,7 +9,7 @@ import {
|
|||
import {
|
||||
SequenceResource as Sequence
|
||||
} from "farmbot/dist/resources/api_resources";
|
||||
import { maybeTagStep } from "../../../resources/sequence_tagging";
|
||||
import { maybeTagStep } from "../../resources/sequence_tagging";
|
||||
|
||||
// ======= TYPE DECLARATIONS =======
|
||||
/** Less strict version of CeleryScript args. It's traversable, or unknown. */
|
|
@ -1,4 +1,4 @@
|
|||
import { fakeResourceIndex } from "../../tile_move_absolute/test_helpers";
|
||||
import { fakeResourceIndex } from "../../../locals_list/test_helpers";
|
||||
import { resourceUpdate } from "../assertion_support";
|
||||
import { unpackStep, TOOL_MOUNT, DISMOUNTED } from "../unpack_step";
|
||||
import {
|
||||
|
|
|
@ -39,9 +39,12 @@ import { StepInputBox } from "../inputs/step_input_box";
|
|||
import {
|
||||
determineDropdown, determineVector, findVariableByName
|
||||
} from "../../resources/sequence_meta";
|
||||
import { LocationForm } from "../locals_list/locals_list";
|
||||
import { LocationForm } from "../locals_list/location_form";
|
||||
import { AllowedDeclaration } from "../locals_list/locals_list_support";
|
||||
|
||||
/** Union of all types found in a move_abs "args" attribute. */
|
||||
export type LocationData = MoveAbsolute["args"]["location"];
|
||||
|
||||
interface Args {
|
||||
location: Tool | Coordinate | Point | Identifier;
|
||||
speed: number;
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
import { MoveAbsolute } from "farmbot/dist";
|
||||
export const TOOL: "Tool" = "Tool";
|
||||
|
||||
export interface InputBoxProps {
|
||||
onCommit(e: React.SyntheticEvent<HTMLInputElement>): void;
|
||||
children?: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
/** Union of all types found in a move_abs "args" attribute. */
|
||||
export type LocationData = MoveAbsolute["args"]["location"];
|
|
@ -0,0 +1,29 @@
|
|||
import { parseClassNames } from "../util";
|
||||
|
||||
describe("parseClassNames", () => {
|
||||
it("parses class names correctly", () => {
|
||||
const base = "hello, base.";
|
||||
const results = parseClassNames({
|
||||
xs: 1,
|
||||
sm: 2,
|
||||
md: 3,
|
||||
lg: 4,
|
||||
xsOffset: 5,
|
||||
smOffset: 6,
|
||||
mdOffset: 7,
|
||||
lgOffset: 8,
|
||||
}, base);
|
||||
|
||||
[
|
||||
base,
|
||||
"col-xs-1",
|
||||
"col-sm-2",
|
||||
"col-md-3",
|
||||
"col-lg-4",
|
||||
"col-xs-offset-5",
|
||||
"col-sm-offset-6",
|
||||
"col-md-offset-7",
|
||||
"col-lg-offset-8",
|
||||
].map(string => expect(results).toContain(string));
|
||||
});
|
||||
});
|
|
@ -1,17 +1,17 @@
|
|||
import * as React from "react";
|
||||
import { Popover, Position } from "@blueprintjs/core";
|
||||
import { Saucer } from "../ui/index";
|
||||
import { Color } from "../interfaces";
|
||||
import { ResourceColor } from "../interfaces";
|
||||
import { colors } from "../util";
|
||||
|
||||
interface PickerProps {
|
||||
current: Color;
|
||||
onChange?: (color: Color) => void;
|
||||
current: ResourceColor;
|
||||
onChange?: (color: ResourceColor) => void;
|
||||
}
|
||||
|
||||
export class ColorPicker extends React.Component<PickerProps, {}> {
|
||||
|
||||
private renderColors(color: Color, key: number) {
|
||||
private renderColors(color: ResourceColor, key: number) {
|
||||
const isActive = color === this.props.current;
|
||||
const cb = this.props.onChange || function () { };
|
||||
return <div key={key} onClick={() => cb(color)} >
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import * as Util from "../util";
|
||||
import { times } from "lodash";
|
||||
import { parseClassNames } from "../../ui/util";
|
||||
|
||||
describe("util", () => {
|
||||
describe("safeStringFetch", () => {
|
||||
|
@ -149,35 +148,6 @@ describe("util", () => {
|
|||
|
||||
});
|
||||
|
||||
describe("parseClassNames", () => {
|
||||
it("parses class names correctly", () => {
|
||||
const base = "hello, base.";
|
||||
const results = parseClassNames({
|
||||
xs: 1,
|
||||
sm: 2,
|
||||
md: 3,
|
||||
lg: 4,
|
||||
xsOffset: 5,
|
||||
smOffset: 6,
|
||||
mdOffset: 7,
|
||||
lgOffset: 8,
|
||||
}, base);
|
||||
|
||||
[
|
||||
base,
|
||||
"col-xs-1",
|
||||
"col-sm-2",
|
||||
"col-md-3",
|
||||
"col-lg-4",
|
||||
"col-xs-offset-5",
|
||||
"col-sm-offset-6",
|
||||
"col-md-offset-7",
|
||||
"col-lg-offset-8",
|
||||
].map(string => expect(results).toContain(string));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("parseIntInput()", () => {
|
||||
it("parses int from number input", () => {
|
||||
expect(Util.parseIntInput("-1.1e+2")).toEqual(-110);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { t } from "i18next";
|
||||
import * as _ from "lodash";
|
||||
import { Color } from "../interfaces";
|
||||
import { ResourceColor } from "../interfaces";
|
||||
import { box } from "boxed_value";
|
||||
import {
|
||||
TaggedResource,
|
||||
|
@ -11,7 +11,7 @@ import {
|
|||
} from "farmbot";
|
||||
import { BotLocationData } from "../devices/interfaces";
|
||||
|
||||
export let colors: Array<Color> = [
|
||||
export let colors: Array<ResourceColor> = [
|
||||
"blue",
|
||||
"green",
|
||||
"yellow",
|
||||
|
@ -23,7 +23,7 @@ export let colors: Array<Color> = [
|
|||
];
|
||||
|
||||
/** Picks a color that is compliant with sequence / regimen color codes */
|
||||
export function randomColor(): Color {
|
||||
export function randomColor(): ResourceColor {
|
||||
return _.sample(colors) as typeof colors[0];
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue