@marimo-team/islands 0.23.7-dev47 → 0.23.7-dev48

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 (27) hide show
  1. package/dist/{chat-ui-CufH8sfF.js → chat-ui-D8ZxPNTR.js} +3 -3
  2. package/dist/{code-visibility-C4KEMmUK.js → code-visibility-An0P9cL_.js} +1265 -935
  3. package/dist/{glide-data-editor-BK9s_dqy.js → glide-data-editor-DucgdjRo.js} +1 -1
  4. package/dist/{html-to-image-DxWM1HVj.js → html-to-image-DaPPaVDP.js} +1 -1
  5. package/dist/{input-Cc1Vvw9A.js → input-D4kjoQUB.js} +2 -0
  6. package/dist/main.js +8 -8
  7. package/dist/{process-output-DBYxXdrN.js → process-output-n0RJTxcC.js} +1 -1
  8. package/dist/{reveal-component-Dx7r_prC.js → reveal-component-B23qYh6r.js} +3 -3
  9. package/dist/style.css +1 -1
  10. package/package.json +1 -1
  11. package/src/components/data-table/__tests__/column-header.test.ts +3 -1
  12. package/src/components/data-table/__tests__/column-header.test.tsx +203 -0
  13. package/src/components/data-table/__tests__/filter-by-values-picker.test.tsx +112 -0
  14. package/src/components/data-table/__tests__/filter-pill-editor.test.tsx +175 -0
  15. package/src/components/data-table/__tests__/filters.test.ts +112 -36
  16. package/src/components/data-table/column-header.tsx +210 -157
  17. package/src/components/data-table/filter-by-values-picker.tsx +70 -9
  18. package/src/components/data-table/filter-pill-editor.tsx +289 -144
  19. package/src/components/data-table/filter-pills.tsx +49 -8
  20. package/src/components/data-table/filters.ts +131 -36
  21. package/src/components/data-table/header-items.tsx +8 -1
  22. package/src/components/data-table/operator-labels.ts +25 -0
  23. package/src/components/data-table/regex-input.tsx +61 -0
  24. package/src/components/ui/combobox.tsx +3 -2
  25. package/src/components/ui/number-field.tsx +2 -0
  26. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +24 -24
  27. package/src/plugins/impl/data-frames/schema.ts +4 -1
@@ -13,6 +13,7 @@ import { Button } from "../ui/button";
13
13
  import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
14
14
  import { FilterPillEditor } from "./filter-pill-editor";
15
15
  import type { ColumnFilterValue } from "./filters";
16
+ import { OPERATOR_LABELS } from "./operator-labels";
16
17
  import { stringifyUnknownValue } from "./utils";
17
18
 
18
19
  interface Props<TData> {
@@ -209,19 +210,62 @@ function formatValue(
209
210
  }
210
211
 
211
212
  if (value.type === "number") {
212
- return formatMinMax(value.min, value.max);
213
+ switch (value.operator) {
214
+ case "between":
215
+ return {
216
+ operator: OPERATOR_LABELS.between.toLowerCase(),
217
+ value: `${value.min} - ${value.max}`,
218
+ };
219
+ case "==":
220
+ case "!=":
221
+ case ">":
222
+ case ">=":
223
+ case "<":
224
+ case "<=":
225
+ return { operator: value.operator, value: String(value.value) };
226
+ }
227
+ }
228
+ if (value.type === "text") {
229
+ switch (value.operator) {
230
+ case "in":
231
+ case "not_in": {
232
+ const items = value.values.map((v) => `"${v}"`);
233
+ return {
234
+ operator: value.operator === "in" ? "is in" : "not in",
235
+ value: `[${items.join(", ")}]`,
236
+ };
237
+ }
238
+ case "is_empty":
239
+ return { operator: "is empty" };
240
+ case "contains":
241
+ case "equals":
242
+ case "does_not_equal":
243
+ case "regex":
244
+ case "starts_with":
245
+ case "ends_with":
246
+ return {
247
+ operator: OPERATOR_LABELS[value.operator].toLowerCase(),
248
+ value: `"${value.text}"`,
249
+ };
250
+ }
213
251
  }
214
252
  if (value.type === "date") {
215
- return formatMinMax(value.min?.toISOString(), value.max?.toISOString());
253
+ return formatMinMaxLegacy(
254
+ value.min?.toISOString(),
255
+ value.max?.toISOString(),
256
+ );
216
257
  }
217
258
  if (value.type === "time") {
218
- return formatMinMax(
259
+ return formatMinMaxLegacy(
219
260
  value.min ? timeFormatter.format(value.min) : undefined,
220
261
  value.max ? timeFormatter.format(value.max) : undefined,
221
262
  );
222
263
  }
223
264
  if (value.type === "datetime") {
224
- return formatMinMax(value.min?.toISOString(), value.max?.toISOString());
265
+ return formatMinMaxLegacy(
266
+ value.min?.toISOString(),
267
+ value.max?.toISOString(),
268
+ );
225
269
  }
226
270
  if (value.type === "boolean") {
227
271
  return { operator: `is ${value.value ? "True" : "False"}` };
@@ -235,14 +279,11 @@ function formatValue(
235
279
  value: `[${stringifiedOptions.join(", ")}]`,
236
280
  };
237
281
  }
238
- if (value.type === "text") {
239
- return { operator: "contains", value: `"${value.text}"` };
240
- }
241
282
  logNever(value);
242
283
  return undefined;
243
284
  }
244
285
 
245
- function formatMinMax(
286
+ function formatMinMaxLegacy(
246
287
  min: string | number | undefined,
247
288
  max: string | number | undefined,
248
289
  ): FormattedFilter | undefined {
@@ -32,15 +32,66 @@ export type FilterType =
32
32
  | "select"
33
33
  | "boolean";
34
34
 
35
+ export const NULLISH_OPS = ["is_null", "is_not_null"] as const;
36
+ export const MEMBERSHIP_OPS = ["in", "not_in"] as const;
37
+ export const NUMBER_COMPARISON_OPS = [
38
+ "==",
39
+ "!=",
40
+ ">",
41
+ ">=",
42
+ "<",
43
+ "<=",
44
+ ] as const;
45
+ export const TEXT_SCALAR_OPS = [
46
+ "contains",
47
+ "equals",
48
+ "does_not_equal",
49
+ "regex",
50
+ "starts_with",
51
+ "ends_with",
52
+ ] as const;
53
+
54
+ export const NUMBER_OPS = [
55
+ "between",
56
+ ...NUMBER_COMPARISON_OPS,
57
+ ...NULLISH_OPS,
58
+ ] as const;
59
+ export const TEXT_OPS = [
60
+ ...TEXT_SCALAR_OPS,
61
+ ...MEMBERSHIP_OPS,
62
+ "is_empty",
63
+ ...NULLISH_OPS,
64
+ ] as const;
65
+
66
+ export type NullishOp = (typeof NULLISH_OPS)[number];
67
+ export type MembershipOp = (typeof MEMBERSHIP_OPS)[number];
68
+ export type NumberComparisonOp = (typeof NUMBER_COMPARISON_OPS)[number];
69
+ export type TextScalarOp = (typeof TEXT_SCALAR_OPS)[number];
70
+
71
+ interface NullishOpts {
72
+ operator: NullishOp;
73
+ }
74
+
75
+ type NumberFilterOpts =
76
+ | { operator: "between"; min: number; max: number }
77
+ | { operator: NumberComparisonOp; value: number }
78
+ | NullishOpts;
79
+
80
+ type TextFilterOpts =
81
+ | { operator: TextScalarOp; text: string }
82
+ | { operator: MembershipOp; values: string[] }
83
+ | { operator: "is_empty" }
84
+ | NullishOpts;
85
+
35
86
  // Filter is a factory function that creates a filter object
36
87
  export const Filter = {
37
- number(opts: { min?: number; max?: number; operator?: OperatorType }) {
88
+ number(opts: NumberFilterOpts) {
38
89
  return {
39
90
  type: "number",
40
91
  ...opts,
41
92
  } as const;
42
93
  },
43
- text(opts: { text?: string; operator: OperatorType }) {
94
+ text(opts: TextFilterOpts) {
44
95
  return {
45
96
  type: "text",
46
97
  ...opts,
@@ -84,6 +135,15 @@ export type ColumnFilterForType<T extends FilterType> = T extends FilterType
84
135
  ? Extract<ColumnFilterValue, { type: T }>
85
136
  : never;
86
137
 
138
+ function isNullishFilter(
139
+ filter: ColumnFilterValue,
140
+ ): filter is Extract<
141
+ ColumnFilterValue,
142
+ { operator: "is_null" | "is_not_null" }
143
+ > {
144
+ return filter.operator === "is_null" || filter.operator === "is_not_null";
145
+ }
146
+
87
147
  export function filterToFilterCondition(
88
148
  columnIdString: string,
89
149
  filter: ColumnFilterValue | undefined,
@@ -93,12 +153,11 @@ export function filterToFilterCondition(
93
153
  }
94
154
  const columnId = columnIdString as ColumnId;
95
155
 
96
- if (filter.operator === "is_null" || filter.operator === "is_not_null") {
156
+ if (isNullishFilter(filter)) {
97
157
  return [
98
158
  {
99
159
  column_id: columnId,
100
160
  operator: filter.operator,
101
- value: undefined,
102
161
  type: "condition",
103
162
  negate: false,
104
163
  },
@@ -106,38 +165,76 @@ export function filterToFilterCondition(
106
165
  }
107
166
 
108
167
  switch (filter.type) {
109
- case "number": {
110
- const conditions: FilterConditionType[] = [];
111
- if (filter.min !== undefined) {
112
- conditions.push({
113
- column_id: columnId,
114
- operator: ">=",
115
- value: filter.min,
116
- type: "condition",
117
- negate: false,
118
- });
119
- }
120
- if (filter.max !== undefined) {
121
- conditions.push({
122
- column_id: columnId,
123
- operator: "<=",
124
- value: filter.max,
125
- type: "condition",
126
- negate: false,
127
- });
168
+ case "number":
169
+ switch (filter.operator) {
170
+ case "between":
171
+ return [
172
+ {
173
+ column_id: columnId,
174
+ operator: "between",
175
+ value: { min: filter.min, max: filter.max },
176
+ type: "condition",
177
+ negate: false,
178
+ },
179
+ ];
180
+ case "==":
181
+ case "!=":
182
+ case ">":
183
+ case ">=":
184
+ case "<":
185
+ case "<=":
186
+ return [
187
+ {
188
+ column_id: columnId,
189
+ operator: filter.operator,
190
+ value: filter.value,
191
+ type: "condition",
192
+ negate: false,
193
+ },
194
+ ];
195
+ default:
196
+ assertNever(filter);
128
197
  }
129
- return conditions;
130
- }
131
198
  case "text":
132
- return [
133
- {
134
- column_id: columnId,
135
- operator: filter.operator,
136
- value: filter.text,
137
- type: "condition",
138
- negate: false,
139
- },
140
- ];
199
+ switch (filter.operator) {
200
+ case "contains":
201
+ case "equals":
202
+ case "does_not_equal":
203
+ case "regex":
204
+ case "starts_with":
205
+ case "ends_with":
206
+ return [
207
+ {
208
+ column_id: columnId,
209
+ operator: filter.operator,
210
+ value: filter.text,
211
+ type: "condition",
212
+ negate: false,
213
+ },
214
+ ];
215
+ case "in":
216
+ case "not_in":
217
+ return [
218
+ {
219
+ column_id: columnId,
220
+ operator: filter.operator,
221
+ value: filter.values,
222
+ type: "condition",
223
+ negate: false,
224
+ },
225
+ ];
226
+ case "is_empty":
227
+ return [
228
+ {
229
+ column_id: columnId,
230
+ operator: "is_empty",
231
+ type: "condition",
232
+ negate: false,
233
+ },
234
+ ];
235
+ default:
236
+ assertNever(filter);
237
+ }
141
238
  case "datetime": {
142
239
  const conditions: FilterConditionType[] = [];
143
240
  if (filter.min !== undefined) {
@@ -210,7 +307,6 @@ export function filterToFilterCondition(
210
307
  {
211
308
  column_id: columnId,
212
309
  operator: "is_true",
213
- value: undefined,
214
310
  type: "condition",
215
311
  negate: false,
216
312
  },
@@ -221,7 +317,6 @@ export function filterToFilterCondition(
221
317
  {
222
318
  column_id: columnId,
223
319
  operator: "is_false",
224
- value: undefined,
225
320
  type: "condition",
226
321
  negate: false,
227
322
  },
@@ -328,14 +328,21 @@ export const FilterButtons = ({
328
328
  onApply,
329
329
  onClear,
330
330
  clearButtonDisabled,
331
+ applyButtonDisabled,
331
332
  }: {
332
333
  onApply: () => void;
333
334
  onClear: () => void;
334
335
  clearButtonDisabled?: boolean;
336
+ applyButtonDisabled?: boolean;
335
337
  }) => {
336
338
  return (
337
339
  <div className="flex gap-2 px-2 justify-between">
338
- <Button variant="link" size="sm" onClick={onApply}>
340
+ <Button
341
+ variant="link"
342
+ size="sm"
343
+ onClick={onApply}
344
+ disabled={applyButtonDisabled}
345
+ >
339
346
  Apply
340
347
  </Button>
341
348
  <Button
@@ -0,0 +1,25 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import type { OperatorType } from "@/plugins/impl/data-frames/utils/operators";
3
+
4
+ export const OPERATOR_LABELS: Record<OperatorType | "between", string> = {
5
+ "==": "Equals",
6
+ "!=": "Doesn't equal",
7
+ ">": "Greater than",
8
+ ">=": "Greater than or equal",
9
+ "<": "Less than",
10
+ "<=": "Less than or equal",
11
+ between: "Between",
12
+ contains: "Contains",
13
+ equals: "Equals",
14
+ does_not_equal: "Doesn't equal",
15
+ starts_with: "Starts with",
16
+ ends_with: "Ends with",
17
+ regex: "Matches regex",
18
+ in: "Is in",
19
+ not_in: "Not in",
20
+ is_empty: "Is empty",
21
+ is_true: "Is true",
22
+ is_false: "Is false",
23
+ is_null: "Is null",
24
+ is_not_null: "Is not null",
25
+ };
@@ -0,0 +1,61 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import React from "react";
3
+ import { cn } from "@/utils/cn";
4
+ import { Input } from "../ui/input";
5
+
6
+ export interface RegexInputProps {
7
+ id?: string;
8
+ value: string;
9
+ onChange: (next: string) => void;
10
+ onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
11
+ placeholder?: string;
12
+ className?: string;
13
+ autoFocus?: boolean;
14
+ "aria-label"?: string;
15
+ }
16
+
17
+ export const RegexInput = React.forwardRef<HTMLInputElement, RegexInputProps>(
18
+ (
19
+ {
20
+ id,
21
+ value,
22
+ onChange,
23
+ onKeyDown,
24
+ placeholder = "pattern",
25
+ className,
26
+ autoFocus,
27
+ "aria-label": ariaLabel,
28
+ },
29
+ ref,
30
+ ) => (
31
+ <div
32
+ className={cn(
33
+ "flex items-stretch h-6 mb-1 rounded-sm border border-input bg-background shadow-xs-solid focus-within:shadow-md-solid focus-within:ring-1 focus-within:ring-ring focus-within:border-primary",
34
+ className,
35
+ )}
36
+ >
37
+ <Slash />
38
+ <Input
39
+ ref={ref}
40
+ id={id}
41
+ type="text"
42
+ value={value}
43
+ onChange={(e) => onChange(e.target.value)}
44
+ onKeyDown={onKeyDown}
45
+ placeholder={placeholder}
46
+ autoFocus={autoFocus}
47
+ aria-label={ariaLabel}
48
+ rootClassName="flex-1 min-w-0"
49
+ className="border-0 mb-0 h-full shadow-none! hover:shadow-none! focus-visible:shadow-none! focus-visible:ring-0 focus-visible:border-0 rounded-none bg-transparent"
50
+ />
51
+ <Slash />
52
+ </div>
53
+ ),
54
+ );
55
+ RegexInput.displayName = "RegexInput";
56
+
57
+ const Slash = () => (
58
+ <span className="px-1.5 flex items-center text-muted-foreground font-code text-sm select-none">
59
+ /
60
+ </span>
61
+ );
@@ -170,8 +170,9 @@ export const Combobox = <TValue,>({
170
170
  <div className={cn("relative")} {...rest}>
171
171
  <Popover open={open} onOpenChange={setOpen}>
172
172
  <PopoverTrigger asChild={true}>
173
- <div
173
+ <button
174
174
  id={id}
175
+ type="button"
175
176
  className={cn(
176
177
  "flex h-6 w-fit mb-1 shadow-xs-solid items-center justify-between rounded-sm border border-input bg-transparent px-2 text-sm font-prose ring-offset-background placeholder:text-muted-foreground hover:shadow-sm-solid focus:outline-hidden focus:ring-1 focus:ring-ring focus:border-primary focus:shadow-md-solid disabled:cursor-not-allowed disabled:opacity-50",
177
178
  className,
@@ -180,7 +181,7 @@ export const Combobox = <TValue,>({
180
181
  >
181
182
  <span className="truncate flex-1 min-w-0">{renderValue()}</span>
182
183
  <ChevronDownIcon className="ml-3 w-4 h-4 opacity-50 shrink-0" />
183
- </div>
184
+ </button>
184
185
  </PopoverTrigger>
185
186
  <PopoverContent
186
187
  className="w-full min-w-(--radix-popover-trigger-width) p-0"
@@ -62,6 +62,7 @@ export const NumberField = React.forwardRef<HTMLInputElement, NumberFieldProps>(
62
62
  slot="increment"
63
63
  isDisabled={props.isDisabled}
64
64
  variant={variant}
65
+ excludeFromTabOrder={true}
65
66
  >
66
67
  <ChevronUp
67
68
  aria-hidden={true}
@@ -73,6 +74,7 @@ export const NumberField = React.forwardRef<HTMLInputElement, NumberFieldProps>(
73
74
  slot="decrement"
74
75
  isDisabled={props.isDisabled}
75
76
  variant={variant}
77
+ excludeFromTabOrder={true}
76
78
  >
77
79
  <ChevronDown
78
80
  aria-hidden={true}
@@ -27,7 +27,7 @@ exports[`renderZodSchema > should render a form aggregate 1`] = `
27
27
  aria-invalid="false"
28
28
  class="relative"
29
29
  >
30
- <div
30
+ <button
31
31
  aria-controls="radix-_r_27_"
32
32
  aria-expanded="false"
33
33
  aria-haspopup="dialog"
@@ -58,7 +58,7 @@ exports[`renderZodSchema > should render a form aggregate 1`] = `
58
58
  d="m6 9 6 6 6-6"
59
59
  />
60
60
  </svg>
61
- </div>
61
+ </button>
62
62
  <div
63
63
  class="flex flex-col gap-1 items-start"
64
64
  />
@@ -82,7 +82,7 @@ exports[`renderZodSchema > should render a form aggregate 1`] = `
82
82
  aria-invalid="false"
83
83
  class="relative"
84
84
  >
85
- <div
85
+ <button
86
86
  aria-controls="radix-_r_29_"
87
87
  aria-expanded="false"
88
88
  aria-haspopup="dialog"
@@ -113,7 +113,7 @@ exports[`renderZodSchema > should render a form aggregate 1`] = `
113
113
  d="m6 9 6 6 6-6"
114
114
  />
115
115
  </svg>
116
- </div>
116
+ </button>
117
117
  <div
118
118
  class="flex flex-col gap-1 items-start"
119
119
  />
@@ -399,7 +399,7 @@ exports[`renderZodSchema > should render a form explode_columns 1`] = `
399
399
  aria-invalid="false"
400
400
  class="relative"
401
401
  >
402
- <div
402
+ <button
403
403
  aria-controls="radix-_r_2p_"
404
404
  aria-expanded="false"
405
405
  aria-haspopup="dialog"
@@ -430,7 +430,7 @@ exports[`renderZodSchema > should render a form explode_columns 1`] = `
430
430
  d="m6 9 6 6 6-6"
431
431
  />
432
432
  </svg>
433
- </div>
433
+ </button>
434
434
  <div
435
435
  class="flex flex-col gap-1 items-start"
436
436
  />
@@ -596,7 +596,7 @@ exports[`renderZodSchema > should render a form group_by 1`] = `
596
596
  aria-invalid="false"
597
597
  class="relative"
598
598
  >
599
- <div
599
+ <button
600
600
  aria-controls="radix-_r_1p_"
601
601
  aria-expanded="false"
602
602
  aria-haspopup="dialog"
@@ -627,7 +627,7 @@ exports[`renderZodSchema > should render a form group_by 1`] = `
627
627
  d="m6 9 6 6 6-6"
628
628
  />
629
629
  </svg>
630
- </div>
630
+ </button>
631
631
  <div
632
632
  class="flex flex-col gap-1 items-start"
633
633
  />
@@ -651,7 +651,7 @@ exports[`renderZodSchema > should render a form group_by 1`] = `
651
651
  aria-invalid="false"
652
652
  class="relative"
653
653
  >
654
- <div
654
+ <button
655
655
  aria-controls="radix-_r_1r_"
656
656
  aria-expanded="false"
657
657
  aria-haspopup="dialog"
@@ -682,7 +682,7 @@ exports[`renderZodSchema > should render a form group_by 1`] = `
682
682
  d="m6 9 6 6 6-6"
683
683
  />
684
684
  </svg>
685
- </div>
685
+ </button>
686
686
  <div
687
687
  class="flex flex-col gap-1 items-start"
688
688
  />
@@ -798,7 +798,7 @@ exports[`renderZodSchema > should render a form pivot 1`] = `
798
798
  aria-invalid="false"
799
799
  class="relative"
800
800
  >
801
- <div
801
+ <button
802
802
  aria-controls="radix-_r_3b_"
803
803
  aria-expanded="false"
804
804
  aria-haspopup="dialog"
@@ -829,7 +829,7 @@ exports[`renderZodSchema > should render a form pivot 1`] = `
829
829
  d="m6 9 6 6 6-6"
830
830
  />
831
831
  </svg>
832
- </div>
832
+ </button>
833
833
  <div
834
834
  class="flex flex-col gap-1 items-start"
835
835
  />
@@ -853,7 +853,7 @@ exports[`renderZodSchema > should render a form pivot 1`] = `
853
853
  aria-invalid="false"
854
854
  class="relative"
855
855
  >
856
- <div
856
+ <button
857
857
  aria-controls="radix-_r_3d_"
858
858
  aria-expanded="false"
859
859
  aria-haspopup="dialog"
@@ -884,7 +884,7 @@ exports[`renderZodSchema > should render a form pivot 1`] = `
884
884
  d="m6 9 6 6 6-6"
885
885
  />
886
886
  </svg>
887
- </div>
887
+ </button>
888
888
  <div
889
889
  class="flex flex-col gap-1 items-start"
890
890
  />
@@ -908,7 +908,7 @@ exports[`renderZodSchema > should render a form pivot 1`] = `
908
908
  aria-invalid="false"
909
909
  class="relative"
910
910
  >
911
- <div
911
+ <button
912
912
  aria-controls="radix-_r_3f_"
913
913
  aria-expanded="false"
914
914
  aria-haspopup="dialog"
@@ -939,7 +939,7 @@ exports[`renderZodSchema > should render a form pivot 1`] = `
939
939
  d="m6 9 6 6 6-6"
940
940
  />
941
941
  </svg>
942
- </div>
942
+ </button>
943
943
  <div
944
944
  class="flex flex-col gap-1 items-start"
945
945
  />
@@ -1298,7 +1298,7 @@ exports[`renderZodSchema > should render a form select_columns 1`] = `
1298
1298
  aria-invalid="false"
1299
1299
  class="relative"
1300
1300
  >
1301
- <div
1301
+ <button
1302
1302
  aria-controls="radix-_r_4_"
1303
1303
  aria-expanded="false"
1304
1304
  aria-haspopup="dialog"
@@ -1329,7 +1329,7 @@ exports[`renderZodSchema > should render a form select_columns 1`] = `
1329
1329
  d="m6 9 6 6 6-6"
1330
1330
  />
1331
1331
  </svg>
1332
- </div>
1332
+ </button>
1333
1333
  <div
1334
1334
  class="flex flex-col gap-1 items-start"
1335
1335
  />
@@ -1581,7 +1581,7 @@ exports[`renderZodSchema > should render a form unique 1`] = `
1581
1581
  aria-invalid="false"
1582
1582
  class="relative"
1583
1583
  >
1584
- <div
1584
+ <button
1585
1585
  aria-controls="radix-_r_32_"
1586
1586
  aria-expanded="false"
1587
1587
  aria-haspopup="dialog"
@@ -1612,7 +1612,7 @@ exports[`renderZodSchema > should render a form unique 1`] = `
1612
1612
  d="m6 9 6 6 6-6"
1613
1613
  />
1614
1614
  </svg>
1615
- </div>
1615
+ </button>
1616
1616
  <div
1617
1617
  class="flex flex-col gap-1 items-start"
1618
1618
  />
@@ -1740,7 +1740,7 @@ exports[`renders custom forms column_id_array 1`] = `
1740
1740
  aria-invalid="false"
1741
1741
  class="relative"
1742
1742
  >
1743
- <div
1743
+ <button
1744
1744
  aria-controls="radix-_r_41_"
1745
1745
  aria-expanded="false"
1746
1746
  aria-haspopup="dialog"
@@ -1771,7 +1771,7 @@ exports[`renders custom forms column_id_array 1`] = `
1771
1771
  d="m6 9 6 6 6-6"
1772
1772
  />
1773
1773
  </svg>
1774
- </div>
1774
+ </button>
1775
1775
  <div
1776
1776
  class="flex flex-col gap-1 items-start"
1777
1777
  />
@@ -1790,7 +1790,7 @@ exports[`renders custom forms column_id_dot_array 1`] = `
1790
1790
  aria-invalid="false"
1791
1791
  class="relative"
1792
1792
  >
1793
- <div
1793
+ <button
1794
1794
  aria-controls="radix-_r_43_"
1795
1795
  aria-expanded="false"
1796
1796
  aria-haspopup="dialog"
@@ -1821,7 +1821,7 @@ exports[`renders custom forms column_id_dot_array 1`] = `
1821
1821
  d="m6 9 6 6 6-6"
1822
1822
  />
1823
1823
  </svg>
1824
- </div>
1824
+ </button>
1825
1825
  <div
1826
1826
  class="flex flex-col gap-1 items-start"
1827
1827
  />
@@ -80,7 +80,10 @@ export const FilterConditionSchema = z
80
80
  .enum(Object.keys(ALL_OPERATORS) as [OperatorType, ...OperatorType[]])
81
81
  .describe(FieldOptions.of({ label: " " })),
82
82
  type: z.literal("condition").default("condition"),
83
- value: z.any().describe(FieldOptions.of({ label: "Value" })),
83
+ value: z
84
+ .any()
85
+ .optional()
86
+ .describe(FieldOptions.of({ label: "Value" })),
84
87
  negate: z.boolean().default(false),
85
88
  })
86
89
  .describe(FieldOptions.of({ direction: "row", special: "column_filter" }));