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