@protobase/layout 0.22.6 → 0.22.8

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.
@@ -109,6 +109,7 @@ const blockSpecs = {
109
109
  resource: required("text"),
110
110
  filter: optional("text"),
111
111
  groupBy: required("text"),
112
+ splitBy: optional("text"),
112
113
  type: optional("chartType"),
113
114
  sum: optional("text"),
114
115
  average: optional("text"),
package/dist/blocks.d.ts CHANGED
@@ -98,6 +98,8 @@ export type ChartProps = Heading & {
98
98
  groupBy: string;
99
99
  /** Default `bar`; `line` is for a date `groupBy`, `donut` for one of values. */
100
100
  type?: ChartType;
101
+ /** With a date `groupBy`: a filterable field of values that splits each bucket, as stacked bars or a line per value, its 8 largest values and the rest as Other; an index must serve it. */
102
+ splitBy?: string;
101
103
  /** A numeric field each group adds up, instead of counting its records. */
102
104
  sum?: string;
103
105
  /** A numeric field each group averages, instead of counting its records. */
@@ -8,15 +8,24 @@ const ungroupable = ["json", "file"];
8
8
  const text = (value) => typeof value === "string" ? value : void 0;
9
9
  /** Whether a chart's `groupBy` is a date or timestamp field, which it buckets, rather than a field of values. */
10
10
  const isDatedGroup = (field) => field?.type === "date" || field?.type === "timestamp";
11
- /** The fields a chart reads: the one it groups by, and the one it adds up when it has `sum` or `average`. */
12
- const chartFields = (node) => [text(node.props.groupBy), text(node.props.sum) ?? text(node.props.average)].filter((name) => name !== void 0);
11
+ /** The fields a chart reads: the one it groups by, the one it splits by, and the one it adds up when it has `sum` or `average`. */
12
+ const chartFields = (node) => [
13
+ text(node.props.groupBy),
14
+ text(node.props.splitBy),
15
+ text(node.props.sum) ?? text(node.props.average)
16
+ ].filter((name) => name !== void 0);
13
17
  const fieldOf = (model, name) => Object.hasOwn(model.fields, name) ? model.fields[name] : void 0;
14
- const groupProblems = (model, name) => {
18
+ const groupProblems = (model, name, prop = "groupBy") => {
15
19
  const field = fieldOf(model, name);
16
- if (!field) return [`groupBy: unknown field "${name}" on ${model.name}`];
17
- if (field.sensitive) return [`groupBy: "${name}" is sensitive, so a chart cannot show its values`];
18
- if (ungroupable.includes(field.type)) return [`groupBy: "${name}" is a ${field.type} field, which has no values to group by`];
19
- return field.filterable ? [] : [`groupBy: "${name}" is not filterable, so the API refuses to group by it: mark "${name}" .filterable()`];
20
+ if (!field) return [`${prop}: unknown field "${name}" on ${model.name}`];
21
+ if (field.sensitive) return [`${prop}: "${name}" is sensitive, so a chart cannot show its values`];
22
+ if (ungroupable.includes(field.type)) return [`${prop}: "${name}" is a ${field.type} field, which has no values to group by`];
23
+ return field.filterable ? [] : [`${prop}: "${name}" is not filterable, so the API refuses to group by it: mark "${name}" .filterable()`];
24
+ };
25
+ const splitProblems = (model, name) => {
26
+ const problems = groupProblems(model, name, "splitBy");
27
+ if (problems.length > 0) return problems;
28
+ return isDatedGroup(fieldOf(model, name)) ? [`splitBy: "${name}" is a date field; split by a field of values`] : [];
20
29
  };
21
30
  const measureProblems = (model, prop, name) => {
22
31
  const field = fieldOf(model, name);
@@ -27,11 +36,19 @@ const measureProblems = (model, prop, name) => {
27
36
  const shapeProblems = (node, dated) => {
28
37
  const type = text(node.props.type) ?? "bar";
29
38
  const given = (names) => names.filter((name) => node.props[name] !== void 0);
30
- if (dated) return [...type === "donut" ? ["a donut shares out values, so it needs a groupBy of values, not dates"] : [], ...given(["limit"]).map((name) => `"${name}" is for a groupBy of values, not dates`)];
39
+ if (dated) return [
40
+ ...type === "donut" ? ["a donut shares out values, so it needs a groupBy of values, not dates"] : [],
41
+ ...type === "bar" && node.props.splitBy !== void 0 && node.props.average !== void 0 ? ["a split bar stacks its parts, and averages add up to none: use a line, or count or sum"] : [],
42
+ ...given(["limit"]).map((name) => `"${name}" is for a groupBy of values, not dates`)
43
+ ];
31
44
  return [
32
45
  ...type === "line" ? ["a line runs through time, so it needs a date or timestamp groupBy"] : [],
33
46
  ...type === "donut" && node.props.average !== void 0 ? ["a donut shares out a total, and averages add up to none: count or sum instead"] : [],
34
- ...given(["granularity", "range"]).map((name) => `"${name}" is for a date or timestamp groupBy`)
47
+ ...given([
48
+ "granularity",
49
+ "range",
50
+ "splitBy"
51
+ ]).map((name) => `"${name}" is for a date or timestamp groupBy`)
35
52
  ];
36
53
  };
37
54
  /** What a chart names that its resource does not allow: the field it groups by, the one it adds up, and its shape. */
@@ -39,9 +56,11 @@ const chartProblems = (node, model) => {
39
56
  const groupBy = String(node.props.groupBy);
40
57
  const sum = text(node.props.sum);
41
58
  const average = text(node.props.average);
59
+ const splitBy = text(node.props.splitBy);
42
60
  const grouped = groupProblems(model, groupBy);
43
61
  return [
44
62
  ...grouped,
63
+ ...splitBy !== void 0 ? splitProblems(model, splitBy) : [],
45
64
  ...sum !== void 0 ? measureProblems(model, "sum", sum) : [],
46
65
  ...average !== void 0 ? measureProblems(model, "average", average) : [],
47
66
  ...grouped.length === 0 ? shapeProblems(node, isDatedGroup(fieldOf(model, groupBy))) : []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/layout",
3
- "version": "0.22.6",
3
+ "version": "0.22.8",
4
4
  "description": "Composed pages for Protobase, written as JSX that compiles to plain data, with its JSX runtime.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -31,7 +31,7 @@
31
31
  "access": "public"
32
32
  },
33
33
  "dependencies": {
34
- "@protobase/schema": "0.22.6",
34
+ "@protobase/schema": "0.22.8",
35
35
  "aip-parsers": "^1.0.0"
36
36
  }
37
37
  }