@xenosystem/blocks 0.13.7 → 0.13.9

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.
@@ -6,15 +6,16 @@ import {
6
6
  quoteLiteral,
7
7
  renderFilterGroup
8
8
  } from "../chunk-DJJGZ6U4.js";
9
+ import {
10
+ ICONS
11
+ } from "../chunk-W4PTX2Y4.js";
9
12
  import {
10
13
  ChevronGlyph,
11
14
  DownloadGlyph,
12
15
  PlugGlyph,
13
- PlusGlyph,
14
16
  RefreshGlyph,
15
17
  SortAscGlyph,
16
- SortDescGlyph,
17
- XGlyph
18
+ SortDescGlyph
18
19
  } from "../chunk-K7YLLV2W.js";
19
20
  import "../chunk-2KG3PWR4.js";
20
21
 
@@ -197,8 +198,8 @@ function moveCell(from, delta, bounds) {
197
198
  }
198
199
 
199
200
  // src/data/table/numeric.ts
200
- function parseNumeric(text) {
201
- const trimmed = String(text).trim();
201
+ function parseNumeric(text2) {
202
+ const trimmed = String(text2).trim();
202
203
  if (trimmed.length === 0) return { ok: false, reason: "empty" };
203
204
  const match = /^[+-]?(\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/.exec(trimmed);
204
205
  if (!match) return { ok: false, reason: "nan" };
@@ -552,11 +553,11 @@ var TableController = class {
552
553
  return true;
553
554
  }
554
555
  /** Update the in-flight draft. Emits `editPreview` only when `livePreview` is on. */
555
- updateDraft(text) {
556
+ updateDraft(text2) {
556
557
  if (!this.editing) return;
557
- this.editing = { ...this.editing, draft: text };
558
+ this.editing = { ...this.editing, draft: text2 };
558
559
  if (this.livePreview) {
559
- const parsed = this.parseDraft(this.editing.columnId, text);
560
+ const parsed = this.parseDraft(this.editing.columnId, text2);
560
561
  if (parsed.ok) {
561
562
  this.host.emit("editPreview", this.buildEdit(this.editing, parsed.value));
562
563
  }
@@ -608,12 +609,12 @@ var TableController = class {
608
609
  };
609
610
  }
610
611
  /** Parse a draft according to the column's declared type. */
611
- parseDraft(columnId, text) {
612
+ parseDraft(columnId, text2) {
612
613
  const column = this.columnById(columnId);
613
- const trimmed = text.trim();
614
+ const trimmed = text2.trim();
614
615
  if (column?.type === "number" || column?.type === "integer" || column?.type === "duration") {
615
616
  if (trimmed === "") return { ok: true, value: null };
616
- const parsed = parseNumeric(text);
617
+ const parsed = parseNumeric(text2);
617
618
  if (!parsed.ok) return { ok: false, reason: parsed.reason };
618
619
  return { ok: true, value: normalizeNumeric(parsed.value, constraintsOfColumn(column)) };
619
620
  }
@@ -630,7 +631,7 @@ var TableController = class {
630
631
  value: trimmed === "" ? [] : trimmed.split(",").map((part) => part.trim()).filter(Boolean)
631
632
  };
632
633
  }
633
- return { ok: true, value: trimmed === "" ? null : text };
634
+ return { ok: true, value: trimmed === "" ? null : text2 };
634
635
  }
635
636
  rowById(rowId) {
636
637
  return this.result?.rows.find((r) => r.id === rowId);
@@ -3239,7 +3240,7 @@ function emptyFilterGroup() {
3239
3240
  function offerableFilterOps(column, sourceOps = []) {
3240
3241
  const common = ["eq", "ne", "isEmpty", "isNotEmpty", "in", "notIn"];
3241
3242
  const ordered = ["gt", "gte", "lt", "lte", "between"];
3242
- const text = ["contains", "notContains", "startsWith", "endsWith"];
3243
+ const text2 = ["contains", "notContains", "startsWith", "endsWith"];
3243
3244
  const temporal = ["relative"];
3244
3245
  let ops = [...common];
3245
3246
  switch (column?.type) {
@@ -3257,10 +3258,10 @@ function offerableFilterOps(column, sourceOps = []) {
3257
3258
  case "url":
3258
3259
  case "select":
3259
3260
  case "multiselect":
3260
- ops = [...ops, ...text];
3261
+ ops = [...ops, ...text2];
3261
3262
  break;
3262
3263
  default:
3263
- ops = [...ops, ...text, ...ordered];
3264
+ ops = [...ops, ...text2, ...ordered];
3264
3265
  }
3265
3266
  if (sourceOps.length === 0) return ops;
3266
3267
  return ops.filter((op) => sourceOps.includes(op));
@@ -3891,233 +3892,363 @@ var fieldsManifest = {
3891
3892
  import { useSyncExternalStore as useSyncExternalStore4 } from "react";
3892
3893
  import {
3893
3894
  Badge as Badge4,
3895
+ Column,
3894
3896
  EmptyState as EmptyState4,
3895
3897
  IconButton as IconButton3,
3898
+ Note,
3896
3899
  Row,
3897
3900
  RowList,
3898
3901
  ScrollArea as ScrollArea3,
3899
3902
  Section,
3900
3903
  SegmentedControl as SegmentedControl4,
3904
+ Select,
3901
3905
  StatusBar as StatusBar4,
3902
3906
  TextButton,
3903
3907
  Toolbar as Toolbar4,
3904
3908
  ToolbarGroup as ToolbarGroup3
3905
3909
  } from "@xenosystem/components/chrome";
3906
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
3907
- function FieldsPanelView({
3908
- controller,
3909
- showStats = true,
3910
- manualApply = false
3911
- }) {
3912
- const state = useSyncExternalStore4(controller.subscribe, controller.getState, controller.getState);
3913
- if (state.columns.length === 0) {
3914
- return /* @__PURE__ */ jsx4(
3915
- EmptyState4,
3910
+
3911
+ // src/data/fields/declaration.ts
3912
+ var COMBINATORS = [{ value: "and", label: "All" }, { value: "or", label: "Any" }];
3913
+ function fieldsViewState(c, options = {}) {
3914
+ const s = c.getState();
3915
+ const showStats = options.showStats ?? true;
3916
+ const manualApply = options.manualApply ?? false;
3917
+ const columnOf = (id) => s.columns.find((col) => col.id === id);
3918
+ const shelves = s.shelves.filter((shelf) => shelf.id !== "filters").map((shelf) => {
3919
+ const entries = s.entries[shelf.id] ?? [];
3920
+ return {
3921
+ id: shelf.id,
3922
+ label: shelf.label,
3923
+ count: entries.length,
3924
+ hasEntries: entries.length > 0,
3925
+ empty: entries.length === 0,
3926
+ hint: shelf.hint ?? `Drop a ${shelf.accepts.join(" or ")} here.`,
3927
+ entries: entries.map((entry) => {
3928
+ const column = columnOf(entry.columnId);
3929
+ return {
3930
+ shelfId: shelf.id,
3931
+ columnId: entry.columnId,
3932
+ label: entry.label ?? column?.name ?? entry.columnId,
3933
+ suggested: entry.suggested === true,
3934
+ aggregated: shelf.aggregated === true,
3935
+ fn: entry.fn ?? "count",
3936
+ aggregates: shelf.aggregated ? offerableAggregates(column, s.allowedAggregates).map((fn) => ({ value: fn, label: fn })) : [],
3937
+ sortable: !shelf.aggregated && shelf.id === "sorts",
3938
+ direction: entry.direction === "desc" ? "desc" : "asc",
3939
+ granularity: !shelf.aggregated && shelf.id !== "sorts" ? entry.granularity : void 0,
3940
+ removeLabel: `Remove ${column?.name ?? entry.columnId}`
3941
+ };
3942
+ })
3943
+ };
3944
+ });
3945
+ const filters = s.filters.filters.map((member, index) => {
3946
+ if (!isLeafFilter(member)) {
3947
+ return { key: `g${index}`, index, leaf: false, label: `(${member.combinator.toUpperCase()} group \u2014 ${member.filters.length})`, op: "", ops: [], removeLabel: "Remove group" };
3948
+ }
3949
+ const column = columnOf(member.columnId);
3950
+ return {
3951
+ key: `f${index}`,
3952
+ index,
3953
+ leaf: true,
3954
+ label: column?.name ?? member.columnId,
3955
+ op: member.op,
3956
+ ops: offerableFilterOps(column, s.allowedFilterOps).map((op) => ({ value: op, label: op })),
3957
+ removeLabel: "Remove filter"
3958
+ };
3959
+ });
3960
+ return {
3961
+ hasColumns: s.columns.length > 0,
3962
+ noColumns: s.columns.length === 0,
3963
+ sampleWarning: s.sampleWarning ?? void 0,
3964
+ sampled: !!s.sampleWarning,
3965
+ manualApply,
3966
+ applyDisabled: !s.dirty,
3967
+ columns: s.columns.map((column) => {
3968
+ const stat = showStats ? s.stats[column.id] : void 0;
3969
+ return { id: column.id, name: column.name, role: column.role ?? "dimension", focused: s.focusedColumnId === column.id, stat: stat ? String(stat.distinct ?? 0) : void 0, statTitle: stat ? `${stat.nulls ?? 0} empty` : void 0 };
3970
+ }),
3971
+ shelves,
3972
+ filters,
3973
+ filterCount: s.filters.filters.length,
3974
+ hasFilters: s.filters.filters.length > 0,
3975
+ noFilters: s.filters.filters.length === 0,
3976
+ combinator: s.filters.combinator,
3977
+ countLabel: `${s.columns.length} field${s.columns.length === 1 ? "" : "s"}`,
3978
+ dirtyLabel: s.dirty ? "unapplied" : void 0
3979
+ };
3980
+ }
3981
+ function applyFieldsUi(c, intent) {
3982
+ switch (intent.kind) {
3983
+ case "autoAssign":
3984
+ c.autoAssign();
3985
+ break;
3986
+ case "apply":
3987
+ c.apply(true);
3988
+ break;
3989
+ case "focus":
3990
+ c.focusColumn(intent.columnId);
3991
+ break;
3992
+ case "remove":
3993
+ c.removeFromShelf(intent.shelfId, intent.columnId);
3994
+ break;
3995
+ case "aggregate":
3996
+ c.setAggregate(intent.shelfId, intent.columnId, intent.fn);
3997
+ break;
3998
+ case "direction":
3999
+ c.toggleDirection(intent.shelfId, intent.columnId);
4000
+ break;
4001
+ case "addFilter": {
4002
+ const column = c.getState().columns[0];
4003
+ if (column) c.addFilter([], { columnId: column.id, op: "eq", value: null });
4004
+ break;
4005
+ }
4006
+ case "addGroup":
4007
+ c.addFilterGroup([]);
4008
+ break;
4009
+ case "filterOp":
4010
+ c.updateFilter([], intent.index, { op: intent.op });
4011
+ break;
4012
+ case "removeFilter":
4013
+ c.removeFilter([], intent.index);
4014
+ break;
4015
+ case "combinator":
4016
+ c.setCombinator([], intent.combinator);
4017
+ break;
4018
+ }
4019
+ }
4020
+ var fieldsViewManifest = {
4021
+ id: fieldsManifest.id,
4022
+ version: "1.0.0",
4023
+ title: "Fields",
4024
+ category: "data",
4025
+ layout: { slot: "side" },
4026
+ inputs: [{ id: "view", type: "object", label: "View state", description: "The FieldsViewModel the runtime derives from the controller on every draw." }],
4027
+ outputs: [{ id: "ui", type: "object", label: "UI intent", description: "{ kind, \u2026 } \u2014 applied by the runtime to the controller." }],
4028
+ commands: []
4029
+ };
4030
+ var EMPTY = { hasColumns: false, noColumns: true, sampled: false, manualApply: false, applyDisabled: true, columns: [], shelves: [], filters: [], filterCount: 0, hasFilters: false, noFilters: true, combinator: "and", countLabel: "0 fields" };
4031
+ var BEHAVIOURS = {
4032
+ "data/fieldsPanelView": (inputs) => inputs.view ?? EMPTY
4033
+ };
4034
+ var FIELDS_RUNTIME_UNITS = [];
4035
+ var text = (b) => ({ component: "text", props: { text: b } });
4036
+ var ui = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
4037
+ var removeButton = (label, on) => ({
4038
+ component: "chrome/IconButton",
4039
+ props: { size: { value: "sm" }, icon: { node: { component: "icons/Close", props: { size: { value: 11 } } } }, label },
4040
+ on: { onClick: on }
4041
+ });
4042
+ var FIELDS_DECLARATION = {
4043
+ format: "xeno.block@1",
4044
+ manifest: fieldsViewManifest,
4045
+ behaviour: "data/fieldsPanelView",
4046
+ composition: {
4047
+ component: "fragment",
4048
+ children: [
4049
+ { component: "chrome/EmptyState", when: { input: "view.noColumns" }, props: { title: { value: "No fields" }, hint: { value: "Connect a source \u2014 its columns appear here, ready to drop onto a shelf." } } },
3916
4050
  {
3917
- title: "No fields",
3918
- hint: "Connect a source \u2014 its columns appear here, ready to drop onto a shelf."
4051
+ component: "chrome/Column",
4052
+ when: { input: "view.hasColumns" },
4053
+ children: [
4054
+ {
4055
+ component: "chrome/Toolbar",
4056
+ props: {
4057
+ left: { node: { component: "chrome/Badge", when: { input: "view.sampled" }, props: { tone: { value: "warning" }, title: { input: "view.sampleWarning" } }, children: [text({ value: "sampled schema" })] } },
4058
+ right: {
4059
+ node: {
4060
+ component: "chrome/ToolbarGroup",
4061
+ props: { end: { value: true } },
4062
+ children: [
4063
+ { component: "chrome/TextButton", on: { onClick: ui("autoAssign") }, children: [text({ value: "Auto-assign" })] },
4064
+ { component: "chrome/TextButton", when: { input: "view.manualApply" }, props: { strong: { value: true }, disabled: { input: "view.applyDisabled" } }, on: { onClick: ui("apply") }, children: [text({ value: "Apply" })] }
4065
+ ]
4066
+ }
4067
+ }
4068
+ }
4069
+ },
4070
+ {
4071
+ component: "chrome/ScrollArea",
4072
+ children: [
4073
+ {
4074
+ component: "chrome/Section",
4075
+ props: { title: { value: "Fields" }, flush: { value: true } },
4076
+ children: [
4077
+ {
4078
+ component: "chrome/RowList",
4079
+ props: { role: { value: "listbox" }, "aria-label": { value: "Fields" } },
4080
+ children: [
4081
+ {
4082
+ component: "chrome/Row",
4083
+ each: { input: "view.columns", key: "id" },
4084
+ props: { label: { item: "name" }, meta: { item: "role" }, selected: { item: "focused" }, trailing: { node: { component: "chrome/Badge", when: { item: "stat" }, props: { title: { item: "statTitle" } }, children: [text({ item: "stat" })] } } },
4085
+ on: { onClick: ui("focus", { columnId: { item: "id" } }) }
4086
+ }
4087
+ ]
4088
+ }
4089
+ ]
4090
+ },
4091
+ {
4092
+ component: "chrome/Section",
4093
+ each: { input: "view.shelves", key: "id" },
4094
+ props: { title: { item: "label" }, flush: { value: true }, badge: { node: { component: "chrome/Badge", when: { item: "hasEntries" }, children: [text({ item: "count" })] } } },
4095
+ children: [
4096
+ { component: "chrome/Note", when: { item: "empty" }, children: [text({ item: "hint" })] },
4097
+ {
4098
+ component: "chrome/RowList",
4099
+ when: { item: "hasEntries" },
4100
+ children: [
4101
+ {
4102
+ component: "chrome/Row",
4103
+ each: { item: "entries", key: "columnId" },
4104
+ props: {
4105
+ label: { item: "label" },
4106
+ dimmed: { item: "suggested" },
4107
+ meta: {
4108
+ node: {
4109
+ component: "fragment",
4110
+ children: [
4111
+ { component: "chrome/Select", when: { item: "aggregated" }, props: { size: { value: "sm" }, label: { value: "Aggregate" }, options: { item: "aggregates" }, value: { item: "fn" } }, on: { onChange: ui("aggregate", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" }, fn: { arg: "" } }) } },
4112
+ { component: "chrome/TextButton", when: { item: "sortable" }, on: { onClick: ui("direction", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" } }) }, children: [text({ item: "direction" })] },
4113
+ { component: "chrome/Badge", when: { item: "granularity" }, children: [text({ item: "granularity" })] }
4114
+ ]
4115
+ }
4116
+ },
4117
+ trailing: { node: removeButton({ item: "removeLabel" }, ui("remove", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" } })) }
4118
+ }
4119
+ }
4120
+ ]
4121
+ }
4122
+ ]
4123
+ },
4124
+ {
4125
+ component: "chrome/Section",
4126
+ props: {
4127
+ title: { value: "Filters" },
4128
+ flush: { value: true },
4129
+ badge: { node: { component: "chrome/Badge", when: { input: "view.hasFilters" }, children: [text({ input: "view.filterCount" })] } },
4130
+ actions: { node: { component: "chrome/IconButton", props: { size: { value: "sm" }, icon: { node: { component: "icons/Plus", props: { size: { value: 11 } } } }, label: { value: "Add filter" } }, on: { onClick: ui("addFilter") } } }
4131
+ },
4132
+ children: [
4133
+ { component: "chrome/Note", when: { input: "view.noFilters" }, children: [text({ value: "No filters. Everything passes." })] },
4134
+ {
4135
+ component: "chrome/RowList",
4136
+ when: { input: "view.hasFilters" },
4137
+ children: [
4138
+ {
4139
+ component: "chrome/Row",
4140
+ each: { input: "view.filters", key: "key" },
4141
+ props: {
4142
+ label: { item: "label" },
4143
+ meta: { node: { component: "chrome/Select", when: { item: "leaf" }, props: { size: { value: "sm" }, label: { value: "Operator" }, options: { item: "ops" }, value: { item: "op" } }, on: { onChange: ui("filterOp", { index: { item: "index" }, op: { arg: "" } }) } } },
4144
+ trailing: { node: removeButton({ item: "removeLabel" }, ui("removeFilter", { index: { item: "index" } })) }
4145
+ }
4146
+ }
4147
+ ]
4148
+ },
4149
+ {
4150
+ component: "chrome/Toolbar",
4151
+ props: {
4152
+ divided: { value: false },
4153
+ left: { node: { component: "chrome/SegmentedControl", props: { size: { value: "sm" }, label: { value: "Combinator" }, options: { value: COMBINATORS }, value: { input: "view.combinator" } }, on: { onChange: ui("combinator", { combinator: { arg: "" } }) } } },
4154
+ right: { node: { component: "chrome/TextButton", on: { onClick: ui("addGroup") }, children: [text({ value: "+ group" })] } }
4155
+ }
4156
+ }
4157
+ ]
4158
+ }
4159
+ ]
4160
+ },
4161
+ { component: "chrome/StatusBar", props: { left: { input: "view.countLabel" }, right: { input: "view.dirtyLabel" } } }
4162
+ ]
3919
4163
  }
3920
- );
4164
+ ]
3921
4165
  }
3922
- return /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", height: "100%", minHeight: 0 }, children: [
4166
+ };
4167
+
4168
+ // src/data/fields/react/FieldsPanelView.tsx
4169
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
4170
+ var CloseGlyph = ICONS["icons/Close"];
4171
+ var PlusGlyph = ICONS["icons/Plus"];
4172
+ var COMBINATORS2 = [{ value: "and", label: "All" }, { value: "or", label: "Any" }];
4173
+ function FieldsPanelView({ controller, showStats, manualApply }) {
4174
+ useSyncExternalStore4(controller.subscribe, controller.getState, controller.getState);
4175
+ const view = fieldsViewState(controller, { showStats, manualApply });
4176
+ const ui2 = (intent) => applyFieldsUi(controller, intent);
4177
+ if (view.noColumns) {
4178
+ return /* @__PURE__ */ jsx4(EmptyState4, { title: "No fields", hint: "Connect a source \u2014 its columns appear here, ready to drop onto a shelf." });
4179
+ }
4180
+ return /* @__PURE__ */ jsxs4(Column, { children: [
3923
4181
  /* @__PURE__ */ jsx4(
3924
4182
  Toolbar4,
3925
4183
  {
3926
- left: state.sampleWarning ? (
3927
- // An inferred shape is a GUESS; building a query on a guessed column is how a
3928
- // dashboard breaks next Tuesday.
3929
- // Badge has no title prop, so the tooltip lives on a wrapper.
3930
- /* @__PURE__ */ jsx4("span", { title: state.sampleWarning, children: /* @__PURE__ */ jsx4(Badge4, { tone: "warning", children: "sampled schema" }) })
3931
- ) : void 0,
4184
+ left: view.sampled ? /* @__PURE__ */ jsx4(Badge4, { tone: "warning", title: view.sampleWarning, children: "sampled schema" }) : null,
3932
4185
  right: /* @__PURE__ */ jsxs4(ToolbarGroup3, { end: true, children: [
3933
- /* @__PURE__ */ jsx4(TextButton, { onClick: () => controller.autoAssign(), children: "Auto-assign" }),
3934
- manualApply ? /* @__PURE__ */ jsx4(TextButton, { strong: true, disabled: !state.dirty, onClick: () => controller.apply(true), children: "Apply" }) : null
4186
+ /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui2({ kind: "autoAssign" }), children: "Auto-assign" }),
4187
+ view.manualApply ? /* @__PURE__ */ jsx4(TextButton, { strong: true, disabled: view.applyDisabled, onClick: () => ui2({ kind: "apply" }), children: "Apply" }) : null
3935
4188
  ] })
3936
4189
  }
3937
4190
  ),
3938
4191
  /* @__PURE__ */ jsxs4(ScrollArea3, { children: [
3939
- /* @__PURE__ */ jsx4(Section, { title: "Fields", flush: true, children: /* @__PURE__ */ jsx4(RowList, { children: state.columns.map((column) => /* @__PURE__ */ jsx4(
4192
+ /* @__PURE__ */ jsx4(Section, { title: "Fields", flush: true, children: /* @__PURE__ */ jsx4(RowList, { role: "listbox", "aria-label": "Fields", children: view.columns.map((column) => /* @__PURE__ */ jsx4(
3940
4193
  Row,
3941
4194
  {
3942
4195
  label: column.name,
3943
- meta: column.role ?? "dimension",
3944
- selected: state.focusedColumnId === column.id,
3945
- onClick: () => controller.focusColumn(column.id),
3946
- trailing: showStats && state.stats[column.id] ? /* @__PURE__ */ jsx4("span", { title: `${state.stats[column.id]?.nulls ?? 0} empty`, children: /* @__PURE__ */ jsx4(Badge4, { children: String(state.stats[column.id]?.distinct ?? 0) }) }) : null
4196
+ meta: column.role,
4197
+ selected: column.focused,
4198
+ trailing: column.stat ? /* @__PURE__ */ jsx4(Badge4, { title: column.statTitle, children: column.stat }) : null,
4199
+ onClick: () => ui2({ kind: "focus", columnId: column.id })
3947
4200
  },
3948
4201
  column.id
3949
4202
  )) }) }),
3950
- state.shelves.filter((shelf) => shelf.id !== "filters").map((shelf) => {
3951
- const entries = state.entries[shelf.id] ?? [];
3952
- return /* @__PURE__ */ jsx4(
3953
- Section,
4203
+ view.shelves.map((shelf) => /* @__PURE__ */ jsxs4(Section, { title: shelf.label, flush: true, badge: shelf.hasEntries ? /* @__PURE__ */ jsx4(Badge4, { children: shelf.count }) : null, children: [
4204
+ shelf.empty ? /* @__PURE__ */ jsx4(Note, { children: shelf.hint }) : null,
4205
+ shelf.hasEntries ? /* @__PURE__ */ jsx4(RowList, { children: shelf.entries.map((entry) => /* @__PURE__ */ jsx4(
4206
+ Row,
3954
4207
  {
3955
- title: shelf.label,
3956
- badge: entries.length > 0 ? /* @__PURE__ */ jsx4(Badge4, { children: String(entries.length) }) : void 0,
3957
- flush: true,
3958
- children: entries.length === 0 ? /* @__PURE__ */ jsx4("div", { style: { padding: "4px 8px", fontSize: 10, opacity: 0.5 }, children: shelf.hint ?? `Drop a ${shelf.accepts.join(" or ")} here.` }) : /* @__PURE__ */ jsx4(RowList, { children: entries.map((entry) => {
3959
- const column = state.columns.find((c) => c.id === entry.columnId);
3960
- return /* @__PURE__ */ jsx4(
3961
- Row,
3962
- {
3963
- label: entry.label ?? column?.name ?? entry.columnId,
3964
- style: entry.suggested ? { opacity: 0.6 } : void 0,
3965
- meta: shelf.aggregated ? /* @__PURE__ */ jsx4(
3966
- AggregateSelect,
3967
- {
3968
- controller,
3969
- shelfId: shelf.id,
3970
- column,
3971
- value: entry.fn ?? "count",
3972
- allowed: state.allowedAggregates
3973
- }
3974
- ) : shelf.id === "sorts" ? /* @__PURE__ */ jsx4(
3975
- TextButton,
3976
- {
3977
- onClick: () => controller.toggleDirection(shelf.id, entry.columnId),
3978
- children: entry.direction === "desc" ? "desc" : "asc"
3979
- }
3980
- ) : entry.granularity ? /* @__PURE__ */ jsx4(Badge4, { children: entry.granularity }) : null,
3981
- trailing: /* @__PURE__ */ jsx4(
3982
- IconButton3,
3983
- {
3984
- size: "sm",
3985
- icon: /* @__PURE__ */ jsx4(XGlyph, {}),
3986
- label: `Remove ${column?.name ?? entry.columnId}`,
3987
- onClick: () => controller.removeFromShelf(shelf.id, entry.columnId)
3988
- }
3989
- )
3990
- },
3991
- entry.columnId
3992
- );
3993
- }) })
4208
+ label: entry.label,
4209
+ dimmed: entry.suggested,
4210
+ meta: /* @__PURE__ */ jsxs4(Fragment2, { children: [
4211
+ entry.aggregated ? /* @__PURE__ */ jsx4(Select, { size: "sm", label: "Aggregate", options: entry.aggregates, value: entry.fn, onChange: (fn) => ui2({ kind: "aggregate", shelfId: entry.shelfId, columnId: entry.columnId, fn }) }) : null,
4212
+ entry.sortable ? /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui2({ kind: "direction", shelfId: entry.shelfId, columnId: entry.columnId }), children: entry.direction }) : null,
4213
+ entry.granularity ? /* @__PURE__ */ jsx4(Badge4, { children: entry.granularity }) : null
4214
+ ] }),
4215
+ trailing: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(CloseGlyph, { size: 11 }), label: entry.removeLabel, onClick: () => ui2({ kind: "remove", shelfId: entry.shelfId, columnId: entry.columnId }) })
3994
4216
  },
3995
- shelf.id
3996
- );
3997
- }),
3998
- /* @__PURE__ */ jsx4(FilterBuilder, { controller })
3999
- ] }),
4000
- /* @__PURE__ */ jsx4(
4001
- StatusBar4,
4002
- {
4003
- left: `${state.columns.length} field${state.columns.length === 1 ? "" : "s"}`,
4004
- right: state.dirty ? "unapplied" : void 0
4005
- }
4006
- )
4007
- ] });
4008
- }
4009
- function AggregateSelect({
4010
- controller,
4011
- shelfId,
4012
- column,
4013
- value,
4014
- allowed
4015
- }) {
4016
- const options = offerableAggregates(column, allowed);
4017
- return /* @__PURE__ */ jsx4(
4018
- "select",
4019
- {
4020
- value,
4021
- onClick: (e) => e.stopPropagation(),
4022
- onChange: (e) => controller.setAggregate(shelfId, column?.id ?? "", e.target.value),
4023
- style: { font: "inherit", fontSize: 9, background: "transparent", color: "inherit", border: "none" },
4024
- children: options.map((fn) => /* @__PURE__ */ jsx4("option", { value: fn, children: fn }, fn))
4025
- }
4026
- );
4027
- }
4028
- function FilterBuilder({ controller }) {
4029
- const state = useSyncExternalStore4(controller.subscribe, controller.getState, controller.getState);
4030
- return /* @__PURE__ */ jsxs4(
4031
- Section,
4032
- {
4033
- title: "Filters",
4034
- badge: state.filters.filters.length > 0 ? /* @__PURE__ */ jsx4(Badge4, { children: String(state.filters.filters.length) }) : void 0,
4035
- actions: /* @__PURE__ */ jsx4(
4036
- IconButton3,
4217
+ entry.columnId
4218
+ )) }) : null
4219
+ ] }, shelf.id)),
4220
+ /* @__PURE__ */ jsxs4(
4221
+ Section,
4037
4222
  {
4038
- size: "sm",
4039
- icon: /* @__PURE__ */ jsx4(PlusGlyph, {}),
4040
- label: "Add filter",
4041
- onClick: () => {
4042
- const column = state.columns[0];
4043
- if (column) controller.addFilter([], { columnId: column.id, op: "eq", value: null });
4044
- }
4045
- }
4046
- ),
4047
- flush: true,
4048
- children: [
4049
- state.filters.filters.length === 0 ? /* @__PURE__ */ jsx4("div", { style: { padding: "4px 8px", fontSize: 10, opacity: 0.5 }, children: "No filters. Everything passes." }) : /* @__PURE__ */ jsx4(RowList, { children: state.filters.filters.map((member, index) => {
4050
- if (!isLeafFilter(member)) {
4051
- return /* @__PURE__ */ jsx4(
4223
+ title: "Filters",
4224
+ flush: true,
4225
+ badge: view.hasFilters ? /* @__PURE__ */ jsx4(Badge4, { children: view.filterCount }) : null,
4226
+ actions: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(PlusGlyph, { size: 11 }), label: "Add filter", onClick: () => ui2({ kind: "addFilter" }) }),
4227
+ children: [
4228
+ view.noFilters ? /* @__PURE__ */ jsx4(Note, { children: "No filters. Everything passes." }) : null,
4229
+ view.hasFilters ? /* @__PURE__ */ jsx4(RowList, { children: view.filters.map((filter) => /* @__PURE__ */ jsx4(
4052
4230
  Row,
4053
4231
  {
4054
- label: `(${member.combinator.toUpperCase()} group \u2014 ${member.filters.length})`,
4055
- trailing: /* @__PURE__ */ jsx4(
4056
- IconButton3,
4057
- {
4058
- size: "sm",
4059
- icon: /* @__PURE__ */ jsx4(XGlyph, {}),
4060
- label: "Remove group",
4061
- onClick: () => controller.removeFilter([], index)
4062
- }
4063
- )
4232
+ label: filter.label,
4233
+ meta: filter.leaf ? /* @__PURE__ */ jsx4(Select, { size: "sm", label: "Operator", options: filter.ops, value: filter.op, onChange: (op) => ui2({ kind: "filterOp", index: filter.index, op }) }) : null,
4234
+ trailing: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(CloseGlyph, { size: 11 }), label: filter.removeLabel, onClick: () => ui2({ kind: "removeFilter", index: filter.index }) })
4064
4235
  },
4065
- `g${index}`
4066
- );
4067
- }
4068
- const column = state.columns.find((c) => c.id === member.columnId);
4069
- return /* @__PURE__ */ jsx4(
4070
- Row,
4071
- {
4072
- label: column?.name ?? member.columnId,
4073
- meta: /* @__PURE__ */ jsx4(
4074
- "select",
4075
- {
4076
- value: member.op,
4077
- onClick: (e) => e.stopPropagation(),
4078
- onChange: (e) => controller.updateFilter([], index, { op: e.target.value }),
4079
- style: {
4080
- font: "inherit",
4081
- fontSize: 9,
4082
- background: "transparent",
4083
- color: "inherit",
4084
- border: "none"
4085
- },
4086
- children: offerableFilterOps(column, state.allowedFilterOps).map((op) => /* @__PURE__ */ jsx4("option", { value: op, children: op }, op))
4087
- }
4088
- ),
4089
- trailing: /* @__PURE__ */ jsx4(
4090
- IconButton3,
4091
- {
4092
- size: "sm",
4093
- icon: /* @__PURE__ */ jsx4(XGlyph, {}),
4094
- label: "Remove filter",
4095
- onClick: () => controller.removeFilter([], index)
4096
- }
4097
- )
4098
- },
4099
- `f${index}`
4100
- );
4101
- }) }),
4102
- /* @__PURE__ */ jsxs4("div", { style: { padding: "2px 8px", display: "flex", gap: 8 }, children: [
4103
- /* @__PURE__ */ jsx4(
4104
- SegmentedControl4,
4105
- {
4106
- size: "sm",
4107
- label: "Combinator",
4108
- options: [
4109
- { value: "and", label: "All" },
4110
- { value: "or", label: "Any" }
4111
- ],
4112
- value: state.filters.combinator,
4113
- onChange: (c) => controller.setCombinator([], c)
4114
- }
4115
- ),
4116
- /* @__PURE__ */ jsx4(TextButton, { onClick: () => controller.addFilterGroup([]), children: "+ group" })
4117
- ] })
4118
- ]
4119
- }
4120
- );
4236
+ filter.key
4237
+ )) }) : null,
4238
+ /* @__PURE__ */ jsx4(
4239
+ Toolbar4,
4240
+ {
4241
+ divided: false,
4242
+ left: /* @__PURE__ */ jsx4(SegmentedControl4, { size: "sm", label: "Combinator", options: COMBINATORS2, value: view.combinator, onChange: (combinator) => ui2({ kind: "combinator", combinator }) }),
4243
+ right: /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui2({ kind: "addGroup" }), children: "+ group" })
4244
+ }
4245
+ )
4246
+ ]
4247
+ }
4248
+ )
4249
+ ] }),
4250
+ /* @__PURE__ */ jsx4(StatusBar4, { left: view.countLabel, right: view.dirtyLabel })
4251
+ ] });
4121
4252
  }
4122
4253
 
4123
4254
  // src/data/fields/panel.ts
@@ -5245,10 +5376,10 @@ function parseJsonPath(path) {
5245
5376
  }
5246
5377
  return steps;
5247
5378
  }
5248
- function findBracketEnd(text, start) {
5379
+ function findBracketEnd(text2, start) {
5249
5380
  let quote = null;
5250
- for (let i = start + 1; i < text.length; i += 1) {
5251
- const char = text[i];
5381
+ for (let i = start + 1; i < text2.length; i += 1) {
5382
+ const char = text2[i];
5252
5383
  if (quote) {
5253
5384
  if (char === "\\") i += 1;
5254
5385
  else if (char === quote) quote = null;
@@ -5672,9 +5803,9 @@ function firstGraphqlError(errors) {
5672
5803
  }
5673
5804
 
5674
5805
  // src/data/connectors/executors/csv.ts
5675
- function sniffDelimiter(text) {
5806
+ function sniffDelimiter(text2) {
5676
5807
  const candidates = [",", ";", " ", "|"];
5677
- const line = firstLogicalLine(text);
5808
+ const line = firstLogicalLine(text2);
5678
5809
  let best = ",";
5679
5810
  let bestCount = 0;
5680
5811
  for (const candidate of candidates) {
@@ -5696,21 +5827,21 @@ function sniffDelimiter(text) {
5696
5827
  }
5697
5828
  return best;
5698
5829
  }
5699
- function firstLogicalLine(text) {
5830
+ function firstLogicalLine(text2) {
5700
5831
  let inQuotes = false;
5701
- for (let i = 0; i < text.length; i += 1) {
5702
- const char = text[i];
5832
+ for (let i = 0; i < text2.length; i += 1) {
5833
+ const char = text2[i];
5703
5834
  if (char === '"') {
5704
- if (inQuotes && text[i + 1] === '"') i += 1;
5835
+ if (inQuotes && text2[i + 1] === '"') i += 1;
5705
5836
  else inQuotes = !inQuotes;
5706
5837
  } else if ((char === "\n" || char === "\r") && !inQuotes) {
5707
- return text.slice(0, i);
5838
+ return text2.slice(0, i);
5708
5839
  }
5709
5840
  }
5710
- return text;
5841
+ return text2;
5711
5842
  }
5712
- function parseCsv(text, options = {}) {
5713
- const source = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
5843
+ function parseCsv(text2, options = {}) {
5844
+ const source = text2.charCodeAt(0) === 65279 ? text2.slice(1) : text2;
5714
5845
  const delimiter = options.delimiter && options.delimiter.length > 0 ? options.delimiter : sniffDelimiter(source);
5715
5846
  const quote = options.quote && options.quote.length > 0 ? options.quote : '"';
5716
5847
  const hasHeader = options.hasHeader ?? true;
@@ -5745,17 +5876,17 @@ function dedupe(names) {
5745
5876
  return count === 0 ? name : `${name}_${count + 1}`;
5746
5877
  });
5747
5878
  }
5748
- function scanRecords(text, delimiter, quote) {
5879
+ function scanRecords(text2, delimiter, quote) {
5749
5880
  const records = [];
5750
5881
  let fields = [];
5751
5882
  let field = "";
5752
5883
  let inQuotes = false;
5753
5884
  let i = 0;
5754
- while (i < text.length) {
5755
- const char = text[i];
5885
+ while (i < text2.length) {
5886
+ const char = text2[i];
5756
5887
  if (inQuotes) {
5757
5888
  if (char === quote) {
5758
- if (text[i + 1] === quote) {
5889
+ if (text2[i + 1] === quote) {
5759
5890
  field += quote;
5760
5891
  i += 2;
5761
5892
  continue;
@@ -5773,7 +5904,7 @@ function scanRecords(text, delimiter, quote) {
5773
5904
  i += 1;
5774
5905
  continue;
5775
5906
  }
5776
- if (text.startsWith(delimiter, i)) {
5907
+ if (text2.startsWith(delimiter, i)) {
5777
5908
  fields.push(field);
5778
5909
  field = "";
5779
5910
  i += delimiter.length;
@@ -5784,7 +5915,7 @@ function scanRecords(text, delimiter, quote) {
5784
5915
  records.push(fields);
5785
5916
  fields = [];
5786
5917
  field = "";
5787
- i += char === "\r" && text[i + 1] === "\n" ? 2 : 1;
5918
+ i += char === "\r" && text2[i + 1] === "\n" ? 2 : 1;
5788
5919
  continue;
5789
5920
  }
5790
5921
  field += char;
@@ -6935,7 +7066,10 @@ export {
6935
7066
  DEFAULT_RANGES,
6936
7067
  EMPTY_LAYOUT,
6937
7068
  EXPIRY_SKEW_MS,
7069
+ BEHAVIOURS as FIELDS_BEHAVIOURS,
7070
+ FIELDS_DECLARATION,
6938
7071
  FIELDS_PANEL_ID,
7072
+ FIELDS_RUNTIME_UNITS,
6939
7073
  FieldsController,
6940
7074
  FieldsPanelView,
6941
7075
  IMPLEMENTED_VIEW_MODES,
@@ -6950,6 +7084,7 @@ export {
6950
7084
  TABLE_PANEL_ID,
6951
7085
  TableController,
6952
7086
  TablePanelView,
7087
+ applyFieldsUi,
6953
7088
  applyPrecision,
6954
7089
  base64Url,
6955
7090
  breachSideOf,
@@ -7001,6 +7136,8 @@ export {
7001
7136
  facilitiesOf,
7002
7137
  fieldsManifest,
7003
7138
  fieldsPanel,
7139
+ fieldsViewManifest,
7140
+ fieldsViewState,
7004
7141
  firstGraphqlError,
7005
7142
  frozenWidth,
7006
7143
  getChartRenderer,