@columenlabs/lumen-ui 0.1.34 → 0.1.35

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.
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { renameFormula } from '@columenlabs/lumen-ui-types/views';
2
+ import { FORMULA_FUNCTIONS, renameFormula } from '@columenlabs/lumen-ui-types/views';
3
3
  // Formula box with autocomplete: a formula reads fields by name, where they
4
4
  // come from and their key (`symbol.price`, `frame.earnings_next_week.eps`, a formula's bare key),
5
5
  // but people know fields by their labels (Price). Typing a word opens a LumenSelectorList under
@@ -8,20 +8,29 @@ import { renameFormula } from '@columenlabs/lumen-ui-types/views';
8
8
  import { useLayoutEffect, useMemo, useRef, useState } from 'react';
9
9
  import { LumenSelectorList, fuzzyScore } from './lumen-selector.js';
10
10
  import { Textarea } from './ui/textarea.js';
11
- /** The roll-ups a formula can call, offered beside the fields. */
11
+ /**
12
+ * The functions a formula can call, offered beside the fields: the two roll-ups, then the date,
13
+ * text and number functions, each with how to call it under its name.
14
+ */
12
15
  const FUNCTIONS = [
13
16
  {
14
17
  value: 'group_sum("")',
15
18
  label: 'group_sum',
16
- detail: 'a field summed over its group',
17
- group: 'Functions',
19
+ description: 'group_sum("field") · a field summed over its group',
20
+ group: 'Sums',
18
21
  },
19
22
  {
20
23
  value: 'total_sum("")',
21
24
  label: 'total_sum',
22
- detail: 'a field summed over every row',
23
- group: 'Functions',
25
+ description: 'total_sum("field") · a field summed over every row',
26
+ group: 'Sums',
24
27
  },
28
+ ...FORMULA_FUNCTIONS.map((f) => ({
29
+ value: `${f.name}()`,
30
+ label: f.name,
31
+ description: `${f.call} · ${f.detail}`,
32
+ group: `${f.group} functions`,
33
+ })),
25
34
  ];
26
35
  /** The name the caret is in (dots included), from its start to the caret. */
27
36
  function wordAt(text, caret) {
@@ -55,10 +64,11 @@ export function FormulaInput({ id, value, onChange, fields, invalid, placeholder
55
64
  if (caret === null)
56
65
  return;
57
66
  const end = wordEnd(value, caret);
58
- const fn = o.value.endsWith('("")');
67
+ const back = o.value.endsWith('("")') ? 2 : o.value.endsWith('()') ? 1 : 0;
59
68
  const next = value.slice(0, start) + o.value + value.slice(end);
60
- // A function's caret goes between its quotes, ready for the field it sums.
61
- moveTo.current = start + (fn ? o.value.length - 2 : o.value.length);
69
+ // A function's caret goes inside it: between a sum's quotes, ready for the field it sums, or
70
+ // between the brackets, ready for its arguments.
71
+ moveTo.current = start + o.value.length - back;
62
72
  onChange(next);
63
73
  };
64
74
  const track = () => setCaret(box.current?.selectionStart ?? null);
@@ -227,7 +227,7 @@ export function FormulaForm({ formula: f, issue, blank, fields, onChange, onRemo
227
227
  const to = guessField(name, fields);
228
228
  return to ? [{ name, to }] : [];
229
229
  });
230
- return (_jsxs("div", { className: row ? 'col-span-full grid gap-3 border-t bg-muted/30 px-3 py-3 md:pl-10' : 'grid gap-3 p-4', children: [_jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [_jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { htmlFor: `${id}-label`, children: "Label" }), _jsx(Input, { id: `${id}-label`, value: f.label, onChange: (e) => onChange({ label: e.target.value }) })] }), _jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { htmlFor: `${id}-key`, children: "Key" }), _jsx(Input, { id: `${id}-key`, className: "font-mono text-xs", value: f.key, onChange: (e) => onChange({ key: e.target.value }) })] })] }), _jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { htmlFor: `${id}-expr`, children: "Expression" }), _jsx(FormulaInput, { id: `${id}-expr`, invalid: !!issue, fields: fields, placeholder: 'Type a field: market_value / group_sum("market_value") * 100', value: f.expr, onChange: setExpr }), issue ? (_jsxs("p", { role: "alert", className: "flex flex-wrap items-center gap-1.5 text-xs text-destructive", children: [_jsx(CircleAlertIcon, { className: "size-3.5" }), fixes.length
230
+ return (_jsxs("div", { className: row ? 'col-span-full grid gap-3 border-t bg-muted/30 px-3 py-3 md:pl-10' : 'grid gap-3 p-4', children: [_jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [_jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { htmlFor: `${id}-label`, children: "Label" }), _jsx(Input, { id: `${id}-label`, value: f.label, onChange: (e) => onChange({ label: e.target.value }) })] }), _jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { htmlFor: `${id}-key`, children: "Key" }), _jsx(Input, { id: `${id}-key`, className: "font-mono text-xs", value: f.key, onChange: (e) => onChange({ key: e.target.value }) })] })] }), _jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { htmlFor: `${id}-expr`, children: "Expression" }), _jsx(FormulaInput, { id: `${id}-expr`, invalid: !!issue, fields: fields, placeholder: 'Type a field or a function: year(date), round(value / group_sum("value") * 100, 1)', value: f.expr, onChange: setExpr }), issue ? (_jsxs("p", { role: "alert", className: "flex flex-wrap items-center gap-1.5 text-xs text-destructive", children: [_jsx(CircleAlertIcon, { className: "size-3.5" }), fixes.length
231
231
  ? fixes.map(({ name, to }) => (_jsxs("span", { className: "flex items-center gap-1.5", children: ["No field ", name, ". Did you mean ", _jsx("code", { className: "font-mono", children: to.value }), " (", to.label, ")?", _jsxs(Button, { variant: "outline", size: "xs", onClick: () => setExpr(swapWord(f.expr, name, to.value)), children: [_jsx(WandSparklesIcon, {}), "Fix"] })] }, name)))
232
232
  : issue] })) : blank ? (_jsxs("p", { className: "flex items-center gap-1.5 text-xs text-warning", children: [_jsx(CircleAlertIcon, { className: "size-3.5" }), "Reads fine, but ", blank] })) : (_jsxs("p", { className: "flex items-center gap-1.5 text-xs text-gain", children: [_jsx(CheckIcon, { className: "size-3.5" }), "Reads fine"] }))] }), _jsxs("div", { className: "flex flex-wrap items-end gap-3", children: [_jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { children: "Format" }), _jsx(LumenSelector, { label: "Format", size: "sm", className: "w-40", options: [
233
233
  ...formatOptions(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@columenlabs/lumen-ui",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "description": "Lumen: shared React components (frame, views, widgets, charts, drawers).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -41,7 +41,7 @@
41
41
  "@codemirror/lang-javascript": "6.2.5",
42
42
  "@codemirror/lang-json": "6.0.2",
43
43
  "@codemirror/lang-sql": "6.10.0",
44
- "@columenlabs/lumen-ui-types": "0.1.34",
44
+ "@columenlabs/lumen-ui-types": "0.1.35",
45
45
  "@dnd-kit/core": "6.3.1",
46
46
  "@dnd-kit/sortable": "10.0.0",
47
47
  "@dnd-kit/utilities": "3.2.2",