@aglyn/plugins-logic 1.0.0-beta.246 → 1.0.0-beta.249

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/package.json +8 -8
  2. package/src/lib/components/formula-builder-dialog.component.d.ts +33 -0
  3. package/src/lib/components/formula-builder-dialog.component.js +331 -0
  4. package/src/lib/components/formula-builder-dialog.component.js.map +1 -0
  5. package/src/lib/components/formula-builder.component.d.ts +30 -0
  6. package/src/lib/components/formula-builder.component.js +62 -0
  7. package/src/lib/components/formula-builder.component.js.map +1 -0
  8. package/src/lib/components/host-functions-card.component.js +167 -62
  9. package/src/lib/components/host-functions-card.component.js.map +1 -1
  10. package/src/lib/components/host-variables-card.component.js +14 -9
  11. package/src/lib/components/host-variables-card.component.js.map +1 -1
  12. package/src/lib/components/parameter-choices-editor.component.d.ts +31 -0
  13. package/src/lib/components/parameter-choices-editor.component.js +124 -0
  14. package/src/lib/components/parameter-choices-editor.component.js.map +1 -0
  15. package/src/lib/components/structured-value-editor.component.d.ts +30 -0
  16. package/src/lib/components/structured-value-editor.component.js +189 -0
  17. package/src/lib/components/structured-value-editor.component.js.map +1 -0
  18. package/src/lib/model/formula-names.d.ts +41 -0
  19. package/src/lib/model/formula-names.js +82 -0
  20. package/src/lib/model/formula-names.js.map +1 -0
  21. package/src/lib/model/formula.d.ts +72 -0
  22. package/src/lib/model/formula.js +264 -0
  23. package/src/lib/model/formula.js.map +1 -0
  24. package/src/lib/model/parameter-options.d.ts +16 -0
  25. package/src/lib/model/parameter-options.js +31 -0
  26. package/src/lib/model/parameter-options.js.map +1 -1
  27. package/src/lib/model/structured-value.d.ts +51 -0
  28. package/src/lib/model/structured-value.js +135 -0
  29. package/src/lib/model/structured-value.js.map +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglyn/plugins-logic",
3
- "version": "1.0.0-beta.246",
3
+ "version": "1.0.0-beta.249",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://aglyn.com",
6
6
  "repository": {
@@ -25,13 +25,13 @@
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "dependencies": {
28
- "@aglyn/aglyn": "1.0.0-beta.246",
29
- "@aglyn/shared-data-mdi": "1.0.0-beta.246",
30
- "@aglyn/shared-ui-jsx": "1.0.0-beta.246",
31
- "@aglyn/shared-ui-snackstack": "1.0.0-beta.246",
32
- "@aglyn/shared-util-timestamp": "1.0.0-beta.246",
33
- "@aglyn/tenant-data-admin": "1.0.0-beta.246",
34
- "@aglyn/tenant-feature-instance": "1.0.0-beta.246",
28
+ "@aglyn/aglyn": "1.0.0-beta.249",
29
+ "@aglyn/shared-data-mdi": "1.0.0-beta.249",
30
+ "@aglyn/shared-ui-jsx": "1.0.0-beta.249",
31
+ "@aglyn/shared-ui-snackstack": "1.0.0-beta.249",
32
+ "@aglyn/shared-util-timestamp": "1.0.0-beta.249",
33
+ "@aglyn/tenant-data-admin": "1.0.0-beta.249",
34
+ "@aglyn/tenant-feature-instance": "1.0.0-beta.249",
35
35
  "@swc/helpers": "0.5.23"
36
36
  },
37
37
  "peerDependencies": {
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { FormulaName } from '../model/formula-names';
18
+ export interface FormulaBuilderDialogProps {
19
+ /** What the box holds now; offered as a value so formulas can be layered. */
20
+ current: string;
21
+ /** Parameters, local variables and site variables, grouped. */
22
+ names: readonly FormulaName[];
23
+ /** Receives the expression the builder wrote. */
24
+ onUse: (expression: string) => void;
25
+ onClose: () => void;
26
+ }
27
+ /**
28
+ * The "Build formula" dialog (AGL-3757): pick what to work out — add,
29
+ * smallest of, format as money — and the values it works on, and the box
30
+ * gets the expression that does it. Nothing is typed but numbers and text.
31
+ * Its own chunk, loaded the first time an ƒx button is pressed.
32
+ */
33
+ export default function FormulaBuilderDialog(props: FormulaBuilderDialogProps): import("react").JSX.Element;
@@ -0,0 +1,331 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ 'use client';
17
+ import { _ as _extends } from "@swc/helpers/_/_extends";
18
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
19
+ import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, ListSubheader, MenuItem, Stack, TextField, Typography } from "@mui/material";
20
+ import { useState } from "react";
21
+ import { buildFormula, FORMULA_OPERATION_ORDER, FORMULA_OPERATIONS, formulaDraftFor, formulaOperandLabel } from "../model/formula.js";
22
+ const SOURCE_LABELS = {
23
+ name: 'A value',
24
+ number: 'A number',
25
+ text: 'Some text',
26
+ current: 'What is there now'
27
+ };
28
+ const SEPARATORS = [
29
+ {
30
+ value: ' ',
31
+ label: 'A space'
32
+ },
33
+ {
34
+ value: ', ',
35
+ label: 'A comma'
36
+ },
37
+ {
38
+ value: ' - ',
39
+ label: 'A dash'
40
+ },
41
+ {
42
+ value: '',
43
+ label: 'Nothing'
44
+ }
45
+ ];
46
+ const SYMBOLS = [
47
+ '$',
48
+ '€',
49
+ '£',
50
+ '¥',
51
+ ''
52
+ ];
53
+ function NameSelect(props) {
54
+ const { label, value, names, onChange } = props;
55
+ const items = [];
56
+ let group = null;
57
+ for (const option of names){
58
+ if (option.group !== group) {
59
+ group = option.group;
60
+ items.push(/*#__PURE__*/ _jsx(ListSubheader, {
61
+ children: group
62
+ }, `group:${group}`));
63
+ }
64
+ items.push(/*#__PURE__*/ _jsx(MenuItem, {
65
+ value: option.value,
66
+ children: option.label
67
+ }, option.value));
68
+ }
69
+ return /*#__PURE__*/ _jsx(TextField, {
70
+ select: true,
71
+ label: label,
72
+ value: names.some((option)=>option.value === value) ? value : '',
73
+ onChange: (event)=>onChange(event.target.value),
74
+ size: "small",
75
+ sx: {
76
+ flex: 1
77
+ },
78
+ children: items.length ? items : /*#__PURE__*/ _jsx(MenuItem, {
79
+ disabled: true,
80
+ children: 'Nothing to pick yet'
81
+ })
82
+ });
83
+ }
84
+ function OperandRow(props) {
85
+ const { label, operand, names, hasCurrent, onChange, onRemove } = props;
86
+ const sources = Object.keys(SOURCE_LABELS).filter((source)=>source !== 'current' || hasCurrent);
87
+ return /*#__PURE__*/ _jsxs(Stack, {
88
+ direction: "row",
89
+ spacing: 1,
90
+ sx: {
91
+ alignItems: 'center'
92
+ },
93
+ children: [
94
+ /*#__PURE__*/ _jsx(TextField, {
95
+ select: true,
96
+ label: label,
97
+ value: operand.source,
98
+ onChange: (event)=>{
99
+ const source = event.target.value;
100
+ onChange(source === 'name' ? {
101
+ source,
102
+ name: ''
103
+ } : source === 'current' ? {
104
+ source
105
+ } : {
106
+ source,
107
+ value: ''
108
+ });
109
+ },
110
+ size: "small",
111
+ sx: {
112
+ width: 170
113
+ },
114
+ children: sources.map((source)=>/*#__PURE__*/ _jsx(MenuItem, {
115
+ value: source,
116
+ children: SOURCE_LABELS[source]
117
+ }, source))
118
+ }),
119
+ operand.source === 'name' ? /*#__PURE__*/ _jsx(NameSelect, {
120
+ label: "Which",
121
+ value: operand.name,
122
+ names: names,
123
+ onChange: (name)=>onChange({
124
+ source: 'name',
125
+ name
126
+ })
127
+ }) : operand.source === 'number' ? /*#__PURE__*/ _jsx(TextField, {
128
+ label: "Number",
129
+ value: operand.value,
130
+ onChange: (event)=>onChange({
131
+ source: 'number',
132
+ value: event.target.value.replace(/[^0-9.-]/g, '')
133
+ }),
134
+ slotProps: {
135
+ htmlInput: {
136
+ inputMode: 'decimal'
137
+ }
138
+ },
139
+ size: "small",
140
+ sx: {
141
+ flex: 1
142
+ }
143
+ }) : operand.source === 'text' ? /*#__PURE__*/ _jsx(TextField, {
144
+ label: "Text",
145
+ value: operand.value,
146
+ onChange: (event)=>onChange({
147
+ source: 'text',
148
+ value: event.target.value
149
+ }),
150
+ size: "small",
151
+ sx: {
152
+ flex: 1
153
+ }
154
+ }) : /*#__PURE__*/ _jsx(Typography, {
155
+ variant: "body2",
156
+ color: "text.secondary",
157
+ sx: {
158
+ flex: 1
159
+ },
160
+ children: 'The formula already in the box'
161
+ }),
162
+ onRemove ? /*#__PURE__*/ _jsx(IconButton, {
163
+ size: "small",
164
+ "aria-label": `Remove ${label}`,
165
+ onClick: onRemove,
166
+ children: '×'
167
+ }) : null
168
+ ]
169
+ });
170
+ }
171
+ /**
172
+ * The "Build formula" dialog (AGL-3757): pick what to work out — add,
173
+ * smallest of, format as money — and the values it works on, and the box
174
+ * gets the expression that does it. Nothing is typed but numbers and text.
175
+ * Its own chunk, loaded the first time an ƒx button is pressed.
176
+ */ export default function FormulaBuilderDialog(props) {
177
+ var _draft_symbol, _draft_places, _draft_separator;
178
+ const { current, names, onUse, onClose } = props;
179
+ const [draft, setDraft] = useState(()=>formulaDraftFor(current.trim() ? 'add' : 'value', undefined, current));
180
+ const spec = FORMULA_OPERATIONS[draft.operation];
181
+ const expression = buildFormula(draft, current);
182
+ const setOperand = (index, operand)=>setDraft((previous)=>_extends({}, previous, {
183
+ operands: previous.operands.map((item, at)=>at === index ? operand : item)
184
+ }));
185
+ return /*#__PURE__*/ _jsxs(Dialog, {
186
+ open: true,
187
+ onClose: onClose,
188
+ fullWidth: true,
189
+ maxWidth: "sm",
190
+ children: [
191
+ /*#__PURE__*/ _jsx(DialogTitle, {
192
+ children: 'Build formula'
193
+ }),
194
+ /*#__PURE__*/ _jsx(DialogContent, {
195
+ children: /*#__PURE__*/ _jsxs(Stack, {
196
+ spacing: 2,
197
+ sx: {
198
+ pt: 1
199
+ },
200
+ children: [
201
+ /*#__PURE__*/ _jsx(TextField, {
202
+ select: true,
203
+ label: "Work out",
204
+ value: draft.operation,
205
+ onChange: (event)=>setDraft(formulaDraftFor(event.target.value, draft, current)),
206
+ size: "small",
207
+ children: FORMULA_OPERATION_ORDER.map((operation)=>/*#__PURE__*/ _jsx(MenuItem, {
208
+ value: operation,
209
+ children: FORMULA_OPERATIONS[operation].label
210
+ }, operation))
211
+ }),
212
+ draft.operands.map((operand, index)=>/*#__PURE__*/ _jsx(OperandRow, {
213
+ label: formulaOperandLabel(draft.operation, index),
214
+ operand: operand,
215
+ names: names,
216
+ hasCurrent: Boolean(current.trim()),
217
+ onChange: (next)=>setOperand(index, next),
218
+ onRemove: spec.variadic && index >= spec.minOperands ? ()=>setDraft(_extends({}, draft, {
219
+ operands: draft.operands.filter((_, at)=>at !== index)
220
+ })) : undefined
221
+ }, index)),
222
+ spec.variadic ? /*#__PURE__*/ _jsx(Button, {
223
+ size: "small",
224
+ sx: {
225
+ alignSelf: 'flex-start'
226
+ },
227
+ onClick: ()=>setDraft(_extends({}, draft, {
228
+ operands: [
229
+ ...draft.operands,
230
+ draft.operation === 'join' ? {
231
+ source: 'text',
232
+ value: ''
233
+ } : {
234
+ source: 'name',
235
+ name: ''
236
+ }
237
+ ]
238
+ })),
239
+ children: 'Add another'
240
+ }) : null,
241
+ /*#__PURE__*/ _jsxs(Stack, {
242
+ direction: "row",
243
+ spacing: 1,
244
+ children: [
245
+ draft.operation === 'currency' ? /*#__PURE__*/ _jsx(TextField, {
246
+ select: true,
247
+ label: "Sign",
248
+ value: (_draft_symbol = draft.symbol) != null ? _draft_symbol : '$',
249
+ onChange: (event)=>setDraft(_extends({}, draft, {
250
+ symbol: event.target.value
251
+ })),
252
+ size: "small",
253
+ sx: {
254
+ width: 110
255
+ },
256
+ children: SYMBOLS.map((symbol)=>/*#__PURE__*/ _jsx(MenuItem, {
257
+ value: symbol,
258
+ children: symbol || 'None'
259
+ }, symbol || 'none'))
260
+ }) : null,
261
+ spec.places ? /*#__PURE__*/ _jsx(TextField, {
262
+ select: true,
263
+ label: "Decimal places",
264
+ value: (_draft_places = draft.places) != null ? _draft_places : draft.operation === 'currency' ? 2 : 0,
265
+ onChange: (event)=>setDraft(_extends({}, draft, {
266
+ places: Number(event.target.value)
267
+ })),
268
+ size: "small",
269
+ sx: {
270
+ width: 150
271
+ },
272
+ children: [
273
+ 0,
274
+ 1,
275
+ 2,
276
+ 3,
277
+ 4
278
+ ].map((places)=>/*#__PURE__*/ _jsx(MenuItem, {
279
+ value: places,
280
+ children: String(places)
281
+ }, places))
282
+ }) : null,
283
+ draft.operation === 'join' ? /*#__PURE__*/ _jsx(TextField, {
284
+ select: true,
285
+ label: "Between each",
286
+ value: (_draft_separator = draft.separator) != null ? _draft_separator : ' ',
287
+ onChange: (event)=>setDraft(_extends({}, draft, {
288
+ separator: event.target.value
289
+ })),
290
+ size: "small",
291
+ sx: {
292
+ width: 160
293
+ },
294
+ children: SEPARATORS.map((separator)=>/*#__PURE__*/ _jsx(MenuItem, {
295
+ value: separator.value,
296
+ children: separator.label
297
+ }, separator.label))
298
+ }) : null
299
+ ]
300
+ }),
301
+ /*#__PURE__*/ _jsx(Typography, {
302
+ variant: "caption",
303
+ color: "text.secondary",
304
+ children: expression ? `The box will read: ${expression}` : 'Choose every value to finish the formula.'
305
+ })
306
+ ]
307
+ })
308
+ }),
309
+ /*#__PURE__*/ _jsxs(DialogActions, {
310
+ children: [
311
+ /*#__PURE__*/ _jsx(Button, {
312
+ onClick: onClose,
313
+ children: 'Cancel'
314
+ }),
315
+ /*#__PURE__*/ _jsx(Button, {
316
+ variant: "contained",
317
+ disabled: !expression,
318
+ onClick: ()=>{
319
+ if (!expression) return;
320
+ onUse(expression);
321
+ onClose();
322
+ },
323
+ children: 'Use formula'
324
+ })
325
+ ]
326
+ })
327
+ ]
328
+ });
329
+ }
330
+
331
+ //# sourceMappingURL=formula-builder-dialog.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/logic/src/lib/components/formula-builder-dialog.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n IconButton,\n ListSubheader,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { type ReactNode, useState } from 'react'\nimport {\n buildFormula,\n FORMULA_OPERATION_ORDER,\n FORMULA_OPERATIONS,\n type FormulaDraft,\n type FormulaOperand,\n type FormulaOperation,\n formulaDraftFor,\n formulaOperandLabel,\n} from '../model/formula'\nimport type { FormulaName } from '../model/formula-names'\n\nconst SOURCE_LABELS: Record<FormulaOperand['source'], string> = {\n name: 'A value',\n number: 'A number',\n text: 'Some text',\n current: 'What is there now',\n}\n\nconst SEPARATORS: { value: string; label: string }[] = [\n { value: ' ', label: 'A space' },\n { value: ', ', label: 'A comma' },\n { value: ' - ', label: 'A dash' },\n { value: '', label: 'Nothing' },\n]\n\nconst SYMBOLS = ['$', '€', '£', '¥', '']\n\nfunction NameSelect(props: {\n label: string\n value: string\n names: readonly FormulaName[]\n onChange: (value: string) => void\n}) {\n const { label, value, names, onChange } = props\n const items: ReactNode[] = []\n let group: string | null = null\n for (const option of names) {\n if (option.group !== group) {\n group = option.group\n items.push(<ListSubheader key={`group:${group}`}>{group}</ListSubheader>)\n }\n items.push(\n <MenuItem key={option.value} value={option.value}>\n {option.label}\n </MenuItem>,\n )\n }\n return (\n <TextField\n select\n label={label}\n value={names.some((option) => option.value === value) ? value : ''}\n onChange={(event) => onChange(event.target.value)}\n size=\"small\"\n sx={{ flex: 1 }}\n >\n {items.length ? items : <MenuItem disabled>{'Nothing to pick yet'}</MenuItem>}\n </TextField>\n )\n}\n\nfunction OperandRow(props: {\n label: string\n operand: FormulaOperand\n names: readonly FormulaName[]\n hasCurrent: boolean\n onChange: (operand: FormulaOperand) => void\n onRemove?: () => void\n}) {\n const { label, operand, names, hasCurrent, onChange, onRemove } = props\n const sources = (Object.keys(SOURCE_LABELS) as FormulaOperand['source'][]).filter(\n (source) => source !== 'current' || hasCurrent,\n )\n return (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <TextField\n select\n label={label}\n value={operand.source}\n onChange={(event) => {\n const source = event.target.value as FormulaOperand['source']\n onChange(\n source === 'name'\n ? { source, name: '' }\n : source === 'current'\n ? { source }\n : { source, value: '' },\n )\n }}\n size=\"small\"\n sx={{ width: 170 }}\n >\n {sources.map((source) => (\n <MenuItem key={source} value={source}>\n {SOURCE_LABELS[source]}\n </MenuItem>\n ))}\n </TextField>\n {operand.source === 'name' ? (\n <NameSelect\n label=\"Which\"\n value={operand.name}\n names={names}\n onChange={(name) => onChange({ source: 'name', name })}\n />\n ) : operand.source === 'number' ? (\n <TextField\n label=\"Number\"\n value={operand.value}\n onChange={(event) =>\n onChange({\n source: 'number',\n value: event.target.value.replace(/[^0-9.-]/g, ''),\n })\n }\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n size=\"small\"\n sx={{ flex: 1 }}\n />\n ) : operand.source === 'text' ? (\n <TextField\n label=\"Text\"\n value={operand.value}\n onChange={(event) =>\n onChange({ source: 'text', value: event.target.value })\n }\n size=\"small\"\n sx={{ flex: 1 }}\n />\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ flex: 1 }}>\n {'The formula already in the box'}\n </Typography>\n )}\n {onRemove ? (\n <IconButton size=\"small\" aria-label={`Remove ${label}`} onClick={onRemove}>\n {'×'}\n </IconButton>\n ) : null}\n </Stack>\n )\n}\n\nexport interface FormulaBuilderDialogProps {\n /** What the box holds now; offered as a value so formulas can be layered. */\n current: string\n /** Parameters, local variables and site variables, grouped. */\n names: readonly FormulaName[]\n /** Receives the expression the builder wrote. */\n onUse: (expression: string) => void\n onClose: () => void\n}\n\n/**\n * The \"Build formula\" dialog (AGL-3757): pick what to work out — add,\n * smallest of, format as money — and the values it works on, and the box\n * gets the expression that does it. Nothing is typed but numbers and text.\n * Its own chunk, loaded the first time an ƒx button is pressed.\n */\nexport default function FormulaBuilderDialog(props: FormulaBuilderDialogProps) {\n const { current, names, onUse, onClose } = props\n const [draft, setDraft] = useState<FormulaDraft>(() =>\n formulaDraftFor(current.trim() ? 'add' : 'value', undefined, current),\n )\n const spec = FORMULA_OPERATIONS[draft.operation]\n const expression = buildFormula(draft, current)\n const setOperand = (index: number, operand: FormulaOperand) =>\n setDraft((previous) => ({\n ...previous,\n operands: previous.operands.map((item, at) => (at === index ? operand : item)),\n }))\n return (\n <Dialog open onClose={onClose} fullWidth maxWidth=\"sm\">\n <DialogTitle>{'Build formula'}</DialogTitle>\n <DialogContent>\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField\n select\n label=\"Work out\"\n value={draft.operation}\n onChange={(event) =>\n setDraft(\n formulaDraftFor(event.target.value as FormulaOperation, draft, current),\n )\n }\n size=\"small\"\n >\n {FORMULA_OPERATION_ORDER.map((operation) => (\n <MenuItem key={operation} value={operation}>\n {FORMULA_OPERATIONS[operation].label}\n </MenuItem>\n ))}\n </TextField>\n {draft.operands.map((operand, index) => (\n <OperandRow\n key={index}\n label={formulaOperandLabel(draft.operation, index)}\n operand={operand}\n names={names}\n hasCurrent={Boolean(current.trim())}\n onChange={(next) => setOperand(index, next)}\n onRemove={\n spec.variadic && index >= spec.minOperands\n ? () =>\n setDraft({\n ...draft,\n operands: draft.operands.filter((_, at) => at !== index),\n })\n : undefined\n }\n />\n ))}\n {spec.variadic ? (\n <Button\n size=\"small\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() =>\n setDraft({\n ...draft,\n operands: [\n ...draft.operands,\n draft.operation === 'join'\n ? { source: 'text', value: '' }\n : { source: 'name', name: '' },\n ],\n })\n }\n >\n {'Add another'}\n </Button>\n ) : null}\n <Stack direction=\"row\" spacing={1}>\n {draft.operation === 'currency' ? (\n <TextField\n select\n label=\"Sign\"\n value={draft.symbol ?? '$'}\n onChange={(event) => setDraft({ ...draft, symbol: event.target.value })}\n size=\"small\"\n sx={{ width: 110 }}\n >\n {SYMBOLS.map((symbol) => (\n <MenuItem key={symbol || 'none'} value={symbol}>\n {symbol || 'None'}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n {spec.places ? (\n <TextField\n select\n label=\"Decimal places\"\n value={draft.places ?? (draft.operation === 'currency' ? 2 : 0)}\n onChange={(event) =>\n setDraft({ ...draft, places: Number(event.target.value) })\n }\n size=\"small\"\n sx={{ width: 150 }}\n >\n {[0, 1, 2, 3, 4].map((places) => (\n <MenuItem key={places} value={places}>\n {String(places)}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n {draft.operation === 'join' ? (\n <TextField\n select\n label=\"Between each\"\n value={draft.separator ?? ' '}\n onChange={(event) => setDraft({ ...draft, separator: event.target.value })}\n size=\"small\"\n sx={{ width: 160 }}\n >\n {SEPARATORS.map((separator) => (\n <MenuItem key={separator.label} value={separator.value}>\n {separator.label}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n </Stack>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {expression\n ? `The box will read: ${expression}`\n : 'Choose every value to finish the formula.'}\n </Typography>\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n disabled={!expression}\n onClick={() => {\n if (!expression) return\n onUse(expression)\n onClose()\n }}\n >\n {'Use formula'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n}\n"],"names":["Button","Dialog","DialogActions","DialogContent","DialogTitle","IconButton","ListSubheader","MenuItem","Stack","TextField","Typography","useState","buildFormula","FORMULA_OPERATION_ORDER","FORMULA_OPERATIONS","formulaDraftFor","formulaOperandLabel","SOURCE_LABELS","name","number","text","current","SEPARATORS","value","label","SYMBOLS","NameSelect","props","names","onChange","items","group","option","push","select","some","event","target","size","sx","flex","length","disabled","OperandRow","operand","hasCurrent","onRemove","sources","Object","keys","filter","source","direction","spacing","alignItems","width","map","replace","slotProps","htmlInput","inputMode","variant","color","aria-label","onClick","FormulaBuilderDialog","draft","onUse","onClose","setDraft","trim","undefined","spec","operation","expression","setOperand","index","previous","operands","item","at","open","fullWidth","maxWidth","pt","Boolean","next","variadic","minOperands","_","alignSelf","symbol","places","Number","String","separator"],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { FormulaName } from '../model/formula-names';
18
+ export interface FormulaBuilderButtonProps {
19
+ /** What the box holds now; offered as a value so formulas can be layered. */
20
+ current: string;
21
+ /** Parameters, local variables and site variables, grouped. */
22
+ names: readonly FormulaName[];
23
+ /** Receives the expression the builder wrote. */
24
+ onUse: (expression: string) => void;
25
+ /** Called as the dialog opens — where a caller loads names it reads lazily. */
26
+ onOpen?: () => void;
27
+ }
28
+ /** The ƒx "Build formula" button beside an expression box (AGL-3757). */
29
+ export declare function FormulaBuilderButton(props: FormulaBuilderButtonProps): import("react").JSX.Element;
30
+ export default FormulaBuilderButton;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ 'use client';
17
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
18
+ import { IconButton, Tooltip, Typography } from "@mui/material";
19
+ import { lazy, Suspense, useState } from "react";
20
+ // The dialog is its own chunk: a function editor that never presses ƒx
21
+ // never downloads it.
22
+ const FormulaBuilderDialog = /*#__PURE__*/ lazy(()=>import("./formula-builder-dialog.component.js"));
23
+ /** The ƒx "Build formula" button beside an expression box (AGL-3757). */ export function FormulaBuilderButton(props) {
24
+ const { current, names, onUse, onOpen } = props;
25
+ const [open, setOpen] = useState(false);
26
+ return /*#__PURE__*/ _jsxs(_Fragment, {
27
+ children: [
28
+ /*#__PURE__*/ _jsx(Tooltip, {
29
+ title: "Build formula",
30
+ children: /*#__PURE__*/ _jsx(IconButton, {
31
+ size: "small",
32
+ "aria-label": "Build formula",
33
+ onClick: ()=>{
34
+ onOpen == null ? void 0 : onOpen();
35
+ setOpen(true);
36
+ },
37
+ children: /*#__PURE__*/ _jsx(Typography, {
38
+ component: "span",
39
+ sx: {
40
+ fontStyle: 'italic',
41
+ fontWeight: 600,
42
+ lineHeight: 1
43
+ },
44
+ children: 'ƒx'
45
+ })
46
+ })
47
+ }),
48
+ open ? /*#__PURE__*/ _jsx(Suspense, {
49
+ fallback: null,
50
+ children: /*#__PURE__*/ _jsx(FormulaBuilderDialog, {
51
+ current: current,
52
+ names: names,
53
+ onUse: onUse,
54
+ onClose: ()=>setOpen(false)
55
+ })
56
+ }) : null
57
+ ]
58
+ });
59
+ }
60
+ export default FormulaBuilderButton;
61
+
62
+ //# sourceMappingURL=formula-builder.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/logic/src/lib/components/formula-builder.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { IconButton, Tooltip, Typography } from '@mui/material'\nimport { lazy, Suspense, useState } from 'react'\nimport type { FormulaName } from '../model/formula-names'\n\n// The dialog is its own chunk: a function editor that never presses ƒx\n// never downloads it.\nconst FormulaBuilderDialog = lazy(() => import('./formula-builder-dialog.component'))\n\nexport interface FormulaBuilderButtonProps {\n /** What the box holds now; offered as a value so formulas can be layered. */\n current: string\n /** Parameters, local variables and site variables, grouped. */\n names: readonly FormulaName[]\n /** Receives the expression the builder wrote. */\n onUse: (expression: string) => void\n /** Called as the dialog opens — where a caller loads names it reads lazily. */\n onOpen?: () => void\n}\n\n/** The ƒx \"Build formula\" button beside an expression box (AGL-3757). */\nexport function FormulaBuilderButton(props: FormulaBuilderButtonProps) {\n const { current, names, onUse, onOpen } = props\n const [open, setOpen] = useState(false)\n return (\n <>\n <Tooltip title=\"Build formula\">\n <IconButton\n size=\"small\"\n aria-label=\"Build formula\"\n onClick={() => {\n onOpen?.()\n setOpen(true)\n }}\n >\n <Typography\n component=\"span\"\n sx={{ fontStyle: 'italic', fontWeight: 600, lineHeight: 1 }}\n >\n {'ƒx'}\n </Typography>\n </IconButton>\n </Tooltip>\n {open ? (\n <Suspense fallback={null}>\n <FormulaBuilderDialog\n current={current}\n names={names}\n onUse={onUse}\n onClose={() => setOpen(false)}\n />\n </Suspense>\n ) : null}\n </>\n )\n}\n\nexport default FormulaBuilderButton\n"],"names":["IconButton","Tooltip","Typography","lazy","Suspense","useState","FormulaBuilderDialog","FormulaBuilderButton","props","current","names","onUse","onOpen","open","setOpen","title","size","aria-label","onClick","component","sx","fontStyle","fontWeight","lineHeight","fallback","onClose"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;AAEA,SAASA,UAAU,EAAEC,OAAO,EAAEC,UAAU,QAAQ,gBAAe;AAC/D,SAASC,IAAI,EAAEC,QAAQ,EAAEC,QAAQ,QAAQ,QAAO;AAGhD,uEAAuE;AACvE,sBAAsB;AACtB,MAAMC,qCAAuBH,KAAK,IAAM,MAAM,CAAC;AAa/C,uEAAuE,GACvE,OAAO,SAASI,qBAAqBC,KAAgC;IACnE,MAAM,EAAEC,OAAO,EAAEC,KAAK,EAAEC,KAAK,EAAEC,MAAM,EAAE,GAAGJ;IAC1C,MAAM,CAACK,MAAMC,QAAQ,GAAGT,SAAS;IACjC,qBACE;;0BACE,KAACJ;gBAAQc,OAAM;0BACb,cAAA,KAACf;oBACCgB,MAAK;oBACLC,cAAW;oBACXC,SAAS;wBACPN,0BAAAA;wBACAE,QAAQ;oBACV;8BAEA,cAAA,KAACZ;wBACCiB,WAAU;wBACVC,IAAI;4BAAEC,WAAW;4BAAUC,YAAY;4BAAKC,YAAY;wBAAE;kCAEzD;;;;YAINV,qBACC,KAACT;gBAASoB,UAAU;0BAClB,cAAA,KAAClB;oBACCG,SAASA;oBACTC,OAAOA;oBACPC,OAAOA;oBACPc,SAAS,IAAMX,QAAQ;;iBAGzB;;;AAGV;AAEA,eAAeP,qBAAoB"}