Skip to content

Commit

Permalink
Added Calculator Inputs to documentation, changed 'fields' to 'inputs…
Browse files Browse the repository at this point in the history
…' for consisency
  • Loading branch information
OAGr committed Oct 22, 2023
1 parent ad6a925 commit c635acc
Show file tree
Hide file tree
Showing 10 changed files with 102 additions and 68 deletions.
6 changes: 6 additions & 0 deletions .changeset/curvy-monkeys-deliver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@quri/squiggle-lang": patch
"@quri/squiggle-components": patch
---

Breaking: Changed Calculator fields attribute, to inputs, which now requires Input objects. These allow for multiple types of inputs
12 changes: 6 additions & 6 deletions packages/components/src/components/Calculator/asyncActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ function modifyCode(
calculator: SqCalculator,
code: string
): string {
const input = calculator.fields.find((row) => row.name === name);
const input = calculator.inputs.find((row) => row.name === name);
if (!input) {
throw new Error("Invalid input name.");
}
return alterCodeForSquiggleRun(input, code);
}
// Gets all field codes in the State. Runs them all, runs the function, and updates all these values in the state.
// Gets all input codes in the State. Runs them all, runs the function, and updates all these values in the state.
export async function processAllFieldCodes({
dispatch,
path,
Expand All @@ -93,10 +93,10 @@ export async function processAllFieldCodes({
environment: Env;
}) {
let _state = state;
for (const name of state.fieldNames) {
const field = state.fields[name];
for (const name of state.inputNames) {
const input = state.inputs[name];
const valueResult = await runSquiggleCode(
modifyCode(name, calculator, field.code),
modifyCode(name, calculator, input.code),
environment
);
const _action: CalculatorAction = {
Expand All @@ -109,7 +109,7 @@ export async function processAllFieldCodes({
updateFnValue({ path, state: _state, calculator, environment, dispatch });
}

// Takes an updated field code, runs it, and runs the function.
// Takes an updated input code, runs it, and runs the function.
export async function updateAndProcessFieldCode({
dispatch,
path,
Expand Down
28 changes: 14 additions & 14 deletions packages/components/src/components/Calculator/calculatorReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export type ResultValue = {
};

export type CalculatorState = {
fieldNames: string[];
fields: Record<string, FieldValue>;
inputNames: string[];
inputs: Record<string, FieldValue>;
fn: ResultValue;
hash: string;
};
Expand All @@ -31,7 +31,7 @@ export function hasSameCalculator(state: CalculatorState, calc: SqCalculator) {
}

export function allFields(state: CalculatorState): FieldValue[] {
return state.fieldNames.map((name) => state.fields[name]);
return state.inputNames.map((name) => state.inputs[name]);
}

export function allFieldResults(
Expand All @@ -47,16 +47,16 @@ export function allFieldValuesAreValid(state: CalculatorState): boolean {
export function initialCalculatorState(
calculator: SqCalculator
): CalculatorState {
const fields: Record<string, FieldValue> = {};
calculator.fields.forEach((row) => {
fields[row.name] = {
const inputs: Record<string, FieldValue> = {};
calculator.inputs.forEach((row) => {
inputs[row.name] = {
name: row.name,
code: defaultAsString(row),
};
});
return {
fieldNames: calculator.fields.map((row) => row.name),
fields,
inputNames: calculator.inputs.map((row) => row.name),
inputs,
fn: {},
hash: calculator.hashString,
};
Expand Down Expand Up @@ -89,23 +89,23 @@ export const calculatorReducer = (
action: CalculatorAction
): CalculatorState => {
const modifyField = (name: string, newField: FieldValue) => {
const newFields = { ...state.fields, [name]: newField };
return { ...state, fields: newFields };
const newFields = { ...state.inputs, [name]: newField };
return { ...state, inputs: newFields };
};
switch (action.type) {
case "RESET": {
return action.payload.state;
}
case "SET_FIELD_CODE": {
const { name, code } = action.payload;
const field = state.fields[name];
const newField = { ...field, code, value: undefined };
const input = state.inputs[name];
const newField = { ...input, code, value: undefined };
return modifyField(name, newField);
}
case "SET_FIELD_VALUE": {
const { name, value } = action.payload;
const field = state.fields[name];
const newField = { ...field, value };
const input = state.inputs[name];
const newField = { ...input, value };
return modifyField(name, newField);
}
case "SET_FUNCTION_VALUE": {
Expand Down
12 changes: 6 additions & 6 deletions packages/components/src/components/Calculator/calculatorUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const CalculatorUI: FC<UIProps> = ({
calculatorState,
onChange,
}) => {
const fieldShowSettings: PlaygroundSettings = {
const inputShowSettings: PlaygroundSettings = {
...settings,
distributionChartSettings: {
...settings.distributionChartSettings,
Expand Down Expand Up @@ -85,11 +85,11 @@ export const CalculatorUI: FC<UIProps> = ({
)}

<div className="py-3 px-5 border-b border-slate-200 bg-gray-50 space-y-3">
{calculator.fields.map((row) => {
{calculator.inputs.map((row) => {
const { name, description } = row;
const field = calculatorState.fields[name];
if (field) {
const { value, code } = field;
const input = calculatorState.inputs[name];
if (input) {
const { value, code } = input;
const result = value;
const resultHasInterestingError =
result && !result.ok && code !== "";
Expand Down Expand Up @@ -176,7 +176,7 @@ export const CalculatorUI: FC<UIProps> = ({
<div>
{result &&
resultHasInterestingError &&
showSqValue(renderValue, result, fieldShowSettings)}
showSqValue(renderValue, result, inputShowSettings)}
{!result && (
<div className="text-sm text-gray-500">No result</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export const Basic: Story = {
fn: f,
title: "My Calculator",
description: a,
fields: [
Input.checkbox({name: "VariableCheckbox", description: "This is a long name", default: true}),
inputs: [
Input.checkbox({name: "VariableCheckbox", description: "This is a long name", default: false}),
Input.textArea({name: "Variable2", description: "This is a long name", default: "2 to 40"}),
Input.text({name: "Variable1", description: "This is a very long description This is a very long description This is a very long description This is a very long description This is a very long description", default: 1}),
Input.select({name: "Variable3", default: "alice", options: ["alice", "charles", "bob", "bill", "maven", "billy", "samantha", "becky"]})
Expand Down
23 changes: 10 additions & 13 deletions packages/components/src/stories/SquigglePlayground.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -234,25 +234,22 @@ bar = 123
export const Calculator: Story = {
name: "Calculator",
args: {
defaultCode: `f(a, b, c) = [a + b, a, c]
a = "## My favorite calculator\nA longer description of the calculator goes here...\n"
defaultCode: `f(a, b, c, d) = [a,b,c,d]
a = "A longer description of the calculator goes here...\n"
Calculator.make(
{
fn: f,
title: "My Calculator",
description: a,
fields: [
{
name: "Variable 1",
default: "1",
description: "This is a short description of the first variable input",
},
{ name: "Variable2", default: "2 to 40" },
{ name: "Some array", default: "[3,3,5,2,2]" },
inputs: [
Input.checkbox({name: "VariableCheckbox", description: "This is a long name", default: false}),
Input.textArea({name: "Variable2", description: "This is a long name", default: "2 to 40"}),
Input.text({name: "Variable1", description: "This is a very long description This is a very long description This is a very long description This is a very long description This is a very long description", default: 1}),
Input.select({name: "Variable3", default: "alice", options: ["alice", "charles", "bob", "bill", "maven", "billy", "samantha", "becky"]})
],
}
) `,
)
`,
height: 800,
},
};
6 changes: 3 additions & 3 deletions packages/squiggle-lang/src/fr/calculator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ export const library = [
["fn", frLambda],
["title", frOptional(frString)],
["description", frOptional(frString)],
["fields", frArray(frInput)]
["inputs", frArray(frInput)]
),
],
([{ fn, title, description, fields }]) => {
([{ fn, title, description, inputs }]) => {
const calc = vCalculator({
fn,
title: title || undefined,
description: description || undefined,
fields: fields,
inputs: inputs,
});
const error = calc.getError();
if (error) {
Expand Down
6 changes: 3 additions & 3 deletions packages/squiggle-lang/src/public/SqValue/SqCalculator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ export class SqCalculator {
// It's obviously not perfect - it doesn't capture changes within the calculator function, but this would be much more complicated.

get hashString(): string {
const rowData = JSON.stringify(this._value.fields);
const rowData = JSON.stringify(this._value.inputs);
const paramData = this._value.fn.toString() || "";
return rowData + paramData + this._value.description;
}

get fields(): SqInput[] {
return this._value.fields.map(wrapInput);
get inputs(): SqInput[] {
return this._value.inputs.map(wrapInput);
}
}
10 changes: 5 additions & 5 deletions packages/squiggle-lang/src/value/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ export const vTableChart = (v: TableChart) => new VTableChart(v);

export type Calculator = {
fn: Lambda;
fields: Input[];
inputs: Input[];
description?: string;
title?: string;
};
Expand All @@ -504,19 +504,19 @@ class VCalculator extends BaseValue {

constructor(public value: Calculator) {
super();
if (!value.fn.parameterCounts().includes(value.fields.length)) {
if (!value.fn.parameterCounts().includes(value.inputs.length)) {
this.setError(
`Calculator function needs ${value.fn.parameterCountString()} parameters, but ${
value.fields.length
value.inputs.length
} fields were provided.`
);
}

if (value.fields.some((x) => x.name === "")) {
if (value.inputs.some((x) => x.name === "")) {
this.setError(`Calculator field names can't be empty.`);
}

const fieldNames = value.fields.map((f) => f.name);
const fieldNames = value.inputs.map((f) => f.name);
const uniqueNames = new Set(fieldNames);
if (fieldNames.length !== uniqueNames.size) {
this.setError(`Duplicate calculator field names found.`);
Expand Down
63 changes: 47 additions & 16 deletions packages/website/src/pages/docs/Api/Calculator.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,63 @@ Calculator.make: ({
fn: ...arguments => any,
title?: string,
description?: string,
fields: list<{
name: string,
default?: string | number,
description?: string
}>
fields: list<input>
}) => calculator
```

``Calculator.make`` takes in a function, a description, and a list of fields. The function should take in the same number of arguments as the number of fields, and the arguments should be of the same type as the default value of the field.

Examples:
Calculators require a list of Inputs to be passed in. Inputs are created using the ``Input`` module. The ``Input`` module has a few different functions for creating different types of inputs.

Example:

<SquiggleEditor
defaultCode={`Calculator.make(
{
fn: {|a, b|a + b},
title: "Sum()"
description: "This takes in two arguments, and outputs the sum of those two arguments.",
fields: [
{
fn: {|a, b,c,d| [a,b,c,d]},
title: "Concat()",
description: "This function takes in 4 arguments, then displays them",
inputs: [
Input.text({
name: "First Param",
default: "10 to 11",
default: "10 to 13",
description: "Must be a number or distribution",
},
{ name: "Second Param", default: "3" },
],
}),
Input.textArea({ name: "Second Param", default: "[4,5,2,3,4,5,3,3,2,2,2,3,3,4,45,5,5,2,1]" }),
Input.select({ name: "Third Param", default: "Option 1", options: ["Option 1", "Option 2", "Option 3"] }),
Input.checkbox({ name: "Fourth Param", default: false})
]
}
)`}
/>
/>

## Inputs

Inputs are now only used for creating calculators, as shown above. They are created using the ``Input`` module.

```
Input.text: ({
name: string,
description?: string,
default?: string,
}) => input
Input.textArea: ({
name: string,
description?: string,
default?: string,
}) => input
Input.checkbox: ({
name: string,
description?: string,
default?: boolean,
}) => input
Input.select: ({
name: string,
description?: string,
default?: string,
options: string[]
}) => input
```

0 comments on commit c635acc

Please sign in to comment.