@marimo-team/islands 0.23.2-dev12 → 0.23.2-dev14
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.
- package/dist/main.js +129 -64
- package/package.json +1 -1
- package/src/components/data-table/__tests__/filters.test.ts +304 -0
- package/src/components/data-table/filters.ts +87 -33
- package/src/core/codemirror/__tests__/__snapshots__/setup.test.ts.snap +4 -14
- package/src/core/codemirror/cells/extensions.ts +0 -4
- package/src/core/codemirror/keymaps/keymaps.ts +69 -2
- package/src/plugins/impl/DataTablePlugin.tsx +7 -20
- package/src/plugins/impl/data-frames/DataFramePlugin.tsx +4 -4
- package/src/plugins/impl/data-frames/schema.ts +41 -9
- package/src/plugins/impl/data-frames/utils/operators.ts +2 -0
package/dist/main.js
CHANGED
|
@@ -35,7 +35,7 @@ import { a as Plus, i as Trash, n as ErrorBoundary, o as Pencil, r as require_pr
|
|
|
35
35
|
import { n as clsx_default } from "./clsx-CwTY0BxM.js";
|
|
36
36
|
import { t as require_react_dom } from "./react-dom-D5FDLRUB.js";
|
|
37
37
|
import { t as require_jsx_runtime } from "./jsx-runtime-9hcJiI23.js";
|
|
38
|
-
import { $ as StyleNamespace, A as looseObject, B as union, C as any, F as optional, G as parse$3, I as record, J as prettifyError, K as parseAsync, N as number, P as object, Q as isInVscodeExtension, R as string, S as _null, T as boolean, U as ZodError, V as unknown, X as withFullScreenAsRoot, Y as MAX_HEIGHT_OFFSET, Z as withSmartCollisionBoundary, _ as ZodString, _t as Primitive$1, a as ZodIssueCode, b as _enum, c as ZodBoolean, d as ZodDiscriminatedUnion, dt as Presence, f as ZodEnum, ft as useControllableState, g as ZodOptional, gt as createContextScope, h as ZodObject, i as string$1, j as nan, k as literal, l as ZodDate, lt as useCallbackRef, m as ZodNumber, mt as composeEventHandlers$1, n as date, nt as __awaiter, o as ZodAny, p as ZodLiteral, pt as useLayoutEffect2, q as $ZodError, r as number$1, s as ZodArray, st as DismissableLayer, t as zod_default, u as ZodDefault, ut as useId$12, v as ZodType, vt as dispatchDiscreteCustomEvent, w as array, x as _instanceof, y as ZodUnion, z as tuple } from "./zod-W5ZEjzaE.js";
|
|
38
|
+
import { $ as StyleNamespace, A as looseObject, B as union, C as any, F as optional, G as parse$3, I as record, J as prettifyError, K as parseAsync, N as number, O as lazy$2, P as object, Q as isInVscodeExtension, R as string, S as _null, T as boolean, U as ZodError, V as unknown, X as withFullScreenAsRoot, Y as MAX_HEIGHT_OFFSET, Z as withSmartCollisionBoundary, _ as ZodString, _t as Primitive$1, a as ZodIssueCode, b as _enum, c as ZodBoolean, d as ZodDiscriminatedUnion, dt as Presence, f as ZodEnum, ft as useControllableState, g as ZodOptional, gt as createContextScope, h as ZodObject, i as string$1, j as nan, k as literal, l as ZodDate, lt as useCallbackRef, m as ZodNumber, mt as composeEventHandlers$1, n as date, nt as __awaiter, o as ZodAny, p as ZodLiteral, pt as useLayoutEffect2, q as $ZodError, r as number$1, s as ZodArray, st as DismissableLayer, t as zod_default, u as ZodDefault, ut as useId$12, v as ZodType, vt as dispatchDiscreteCustomEvent, w as array, x as _instanceof, y as ZodUnion, z as tuple } from "./zod-W5ZEjzaE.js";
|
|
39
39
|
import { a as Content$2, c as Root$6, i as prettyError, n as ErrorBanner, o as Overlay, r as CellNotInitializedError, s as Portal$1, t as Banner } from "./error-banner-B_ioHva3.js";
|
|
40
40
|
import { a as TooltipRoot, i as TooltipProvider, n as TooltipContent, o as TooltipTrigger, r as TooltipPortal, t as Tooltip } from "./tooltip-DwNnFsxZ.js";
|
|
41
41
|
import { n as minimalSetup, r as _extends$5, t as esm_default, u as CopyClipboardIcon } from "./esm-CDHI9cuO.js";
|
|
@@ -23789,10 +23789,10 @@ Database schema: ${c}`), (_a4 = r2.aiFix) == null ? void 0 : _a4.setAiCompletion
|
|
|
23789
23789
|
else if (c.some(_temp2$24)) _ = "An internal error occurred";
|
|
23790
23790
|
else if (c.some(_temp3$16)) _ = "Ancestor prevented from running", y = "default", S = "text-secondary-foreground";
|
|
23791
23791
|
else if (c.some(_temp4$9)) _ = "Ancestor stopped", y = "default", S = "text-secondary-foreground";
|
|
23792
|
-
else if (c.some(_temp5$
|
|
23792
|
+
else if (c.some(_temp5$5)) _ = "SQL error";
|
|
23793
23793
|
else {
|
|
23794
23794
|
let e2;
|
|
23795
|
-
r[0] === c ? e2 = r[1] : (e2 = c.find(_temp6$
|
|
23795
|
+
r[0] === c ? e2 = r[1] : (e2 = c.find(_temp6$5), r[0] = c, r[1] = e2);
|
|
23796
23796
|
let d2 = e2;
|
|
23797
23797
|
d2 && "exception_type" in d2 && (_ = d2.exception_type);
|
|
23798
23798
|
}
|
|
@@ -24164,10 +24164,10 @@ Database schema: ${c}`), (_a4 = r2.aiFix) == null ? void 0 : _a4.setAiCompletion
|
|
|
24164
24164
|
function _temp4$9(e) {
|
|
24165
24165
|
return e.type === "ancestor-stopped";
|
|
24166
24166
|
}
|
|
24167
|
-
function _temp5$
|
|
24167
|
+
function _temp5$5(e) {
|
|
24168
24168
|
return e.type === "sql-error";
|
|
24169
24169
|
}
|
|
24170
|
-
function _temp6$
|
|
24170
|
+
function _temp6$5(e) {
|
|
24171
24171
|
return e.type === "exception";
|
|
24172
24172
|
}
|
|
24173
24173
|
function _temp7$2(e) {
|
|
@@ -25715,40 +25715,56 @@ Database schema: ${c}`), (_a4 = r2.aiFix) == null ? void 0 : _a4.setAiCompletion
|
|
|
25715
25715
|
function filterToFilterCondition(e, r) {
|
|
25716
25716
|
if (!r) return [];
|
|
25717
25717
|
let c = e;
|
|
25718
|
-
if (r.operator === "is_null" || r.operator === "is_not_null") return
|
|
25719
|
-
|
|
25720
|
-
|
|
25721
|
-
|
|
25722
|
-
|
|
25718
|
+
if (r.operator === "is_null" || r.operator === "is_not_null") return [
|
|
25719
|
+
{
|
|
25720
|
+
column_id: c,
|
|
25721
|
+
operator: r.operator,
|
|
25722
|
+
value: void 0,
|
|
25723
|
+
type: "condition",
|
|
25724
|
+
negate: false
|
|
25725
|
+
}
|
|
25726
|
+
];
|
|
25723
25727
|
switch (r.type) {
|
|
25724
25728
|
case "number": {
|
|
25725
25729
|
let e2 = [];
|
|
25726
25730
|
return r.min !== void 0 && e2.push({
|
|
25727
25731
|
column_id: c,
|
|
25728
25732
|
operator: ">=",
|
|
25729
|
-
value: r.min
|
|
25733
|
+
value: r.min,
|
|
25734
|
+
type: "condition",
|
|
25735
|
+
negate: false
|
|
25730
25736
|
}), r.max !== void 0 && e2.push({
|
|
25731
25737
|
column_id: c,
|
|
25732
25738
|
operator: "<=",
|
|
25733
|
-
value: r.max
|
|
25739
|
+
value: r.max,
|
|
25740
|
+
type: "condition",
|
|
25741
|
+
negate: false
|
|
25734
25742
|
}), e2;
|
|
25735
25743
|
}
|
|
25736
25744
|
case "text":
|
|
25737
|
-
return
|
|
25738
|
-
|
|
25739
|
-
|
|
25740
|
-
|
|
25741
|
-
|
|
25745
|
+
return [
|
|
25746
|
+
{
|
|
25747
|
+
column_id: c,
|
|
25748
|
+
operator: r.operator,
|
|
25749
|
+
value: r.text,
|
|
25750
|
+
type: "condition",
|
|
25751
|
+
negate: false
|
|
25752
|
+
}
|
|
25753
|
+
];
|
|
25742
25754
|
case "datetime": {
|
|
25743
25755
|
let e2 = [];
|
|
25744
25756
|
return r.min !== void 0 && e2.push({
|
|
25745
25757
|
column_id: c,
|
|
25746
25758
|
operator: ">=",
|
|
25747
|
-
value: r.min.toISOString()
|
|
25759
|
+
value: r.min.toISOString(),
|
|
25760
|
+
type: "condition",
|
|
25761
|
+
negate: false
|
|
25748
25762
|
}), r.max !== void 0 && e2.push({
|
|
25749
25763
|
column_id: c,
|
|
25750
25764
|
operator: "<=",
|
|
25751
|
-
value: r.max.toISOString()
|
|
25765
|
+
value: r.max.toISOString(),
|
|
25766
|
+
type: "condition",
|
|
25767
|
+
negate: false
|
|
25752
25768
|
}), e2;
|
|
25753
25769
|
}
|
|
25754
25770
|
case "date": {
|
|
@@ -25756,11 +25772,15 @@ Database schema: ${c}`), (_a4 = r2.aiFix) == null ? void 0 : _a4.setAiCompletion
|
|
|
25756
25772
|
return r.min !== void 0 && e2.push({
|
|
25757
25773
|
column_id: c,
|
|
25758
25774
|
operator: ">=",
|
|
25759
|
-
value: r.min.toISOString()
|
|
25775
|
+
value: r.min.toISOString(),
|
|
25776
|
+
type: "condition",
|
|
25777
|
+
negate: false
|
|
25760
25778
|
}), r.max !== void 0 && e2.push({
|
|
25761
25779
|
column_id: c,
|
|
25762
25780
|
operator: "<=",
|
|
25763
|
-
value: r.max.toISOString()
|
|
25781
|
+
value: r.max.toISOString(),
|
|
25782
|
+
type: "condition",
|
|
25783
|
+
negate: false
|
|
25764
25784
|
}), e2;
|
|
25765
25785
|
}
|
|
25766
25786
|
case "time": {
|
|
@@ -25768,37 +25788,61 @@ Database schema: ${c}`), (_a4 = r2.aiFix) == null ? void 0 : _a4.setAiCompletion
|
|
|
25768
25788
|
return r.min !== void 0 && e2.push({
|
|
25769
25789
|
column_id: c,
|
|
25770
25790
|
operator: ">=",
|
|
25771
|
-
value: r.min.toISOString()
|
|
25791
|
+
value: r.min.toISOString(),
|
|
25792
|
+
type: "condition",
|
|
25793
|
+
negate: false
|
|
25772
25794
|
}), r.max !== void 0 && e2.push({
|
|
25773
25795
|
column_id: c,
|
|
25774
25796
|
operator: "<=",
|
|
25775
|
-
value: r.max.toISOString()
|
|
25797
|
+
value: r.max.toISOString(),
|
|
25798
|
+
type: "condition",
|
|
25799
|
+
negate: false
|
|
25776
25800
|
}), e2;
|
|
25777
25801
|
}
|
|
25778
25802
|
case "boolean":
|
|
25779
|
-
return r.value ?
|
|
25780
|
-
|
|
25781
|
-
|
|
25782
|
-
|
|
25783
|
-
|
|
25784
|
-
|
|
25785
|
-
|
|
25786
|
-
|
|
25787
|
-
|
|
25803
|
+
return r.value ? [
|
|
25804
|
+
{
|
|
25805
|
+
column_id: c,
|
|
25806
|
+
operator: "is_true",
|
|
25807
|
+
value: void 0,
|
|
25808
|
+
type: "condition",
|
|
25809
|
+
negate: false
|
|
25810
|
+
}
|
|
25811
|
+
] : r.value ? [] : [
|
|
25812
|
+
{
|
|
25813
|
+
column_id: c,
|
|
25814
|
+
operator: "is_false",
|
|
25815
|
+
value: void 0,
|
|
25816
|
+
type: "condition",
|
|
25817
|
+
negate: false
|
|
25818
|
+
}
|
|
25819
|
+
];
|
|
25788
25820
|
case "select": {
|
|
25789
25821
|
let e2 = r.operator;
|
|
25790
25822
|
return r.operator !== "in" && r.operator !== "not_in" && (Logger.warn("Invalid operator for select filter", {
|
|
25791
25823
|
operator: r.operator
|
|
25792
|
-
}), e2 = "in"),
|
|
25793
|
-
|
|
25794
|
-
|
|
25795
|
-
|
|
25796
|
-
|
|
25824
|
+
}), e2 = "in"), [
|
|
25825
|
+
{
|
|
25826
|
+
column_id: c,
|
|
25827
|
+
operator: e2,
|
|
25828
|
+
value: r.options,
|
|
25829
|
+
type: "condition",
|
|
25830
|
+
negate: false
|
|
25831
|
+
}
|
|
25832
|
+
];
|
|
25797
25833
|
}
|
|
25798
25834
|
default:
|
|
25799
25835
|
assertNever(r);
|
|
25800
25836
|
}
|
|
25801
25837
|
}
|
|
25838
|
+
function filtersToFilterGroup(e) {
|
|
25839
|
+
return {
|
|
25840
|
+
type: "group",
|
|
25841
|
+
operator: "and",
|
|
25842
|
+
children: e.flatMap((e2) => filterToFilterCondition(e2.id, e2.value)),
|
|
25843
|
+
negate: false
|
|
25844
|
+
};
|
|
25845
|
+
}
|
|
25802
25846
|
function functionalUpdate(e, r) {
|
|
25803
25847
|
return typeof e == "function" ? e(r) : e;
|
|
25804
25848
|
}
|
|
@@ -43129,7 +43173,7 @@ ${c}
|
|
|
43129
43173
|
r[0] !== c || r[1] !== f ? (h = () => {
|
|
43130
43174
|
f.setValue(c, "");
|
|
43131
43175
|
}, r[0] = c, r[1] = f, r[2] = h) : h = r[2];
|
|
43132
|
-
let _ = h, v = _temp5$
|
|
43176
|
+
let _ = h, v = _temp5$4, y;
|
|
43133
43177
|
r[3] !== _ || r[4] !== d ? (y = (e2) => {
|
|
43134
43178
|
let { field: r2 } = e2;
|
|
43135
43179
|
return (0, import_jsx_runtime.jsxs)(FormItem, {
|
|
@@ -43250,7 +43294,7 @@ ${c}
|
|
|
43250
43294
|
render: S
|
|
43251
43295
|
}), r[5] = c, r[6] = y.control, r[7] = S, r[8] = w) : w = r[8], w;
|
|
43252
43296
|
}, AggregationSelect = (e) => {
|
|
43253
|
-
let r = (0, import_compiler_runtime$80.c)(12), { fieldName: c, selectedDataType: d, binFieldName: f, defaultAggregation: h } = e, _ = useFormContext(), v = d === "string" ? STRING_AGGREGATION_FNS : AGGREGATION_FNS$1, { chartType: y } = useChartFormContext(), S = y !== ChartType.HEATMAP, w = _temp6$
|
|
43297
|
+
let r = (0, import_compiler_runtime$80.c)(12), { fieldName: c, selectedDataType: d, binFieldName: f, defaultAggregation: h } = e, _ = useFormContext(), v = d === "string" ? STRING_AGGREGATION_FNS : AGGREGATION_FNS$1, { chartType: y } = useChartFormContext(), S = y !== ChartType.HEATMAP, w = _temp6$4, E = _temp7$1, O;
|
|
43254
43298
|
r[0] !== f || r[1] !== _ ? (O = (e2) => {
|
|
43255
43299
|
let { value: r2, previousValue: c2, onChange: d2 } = e2;
|
|
43256
43300
|
r2 === "bin" ? _.setValue(f, true) : c2 === "bin" && _.setValue(f, false), d2(r2);
|
|
@@ -43404,7 +43448,7 @@ ${c}
|
|
|
43404
43448
|
function _temp4$7(e) {
|
|
43405
43449
|
return e;
|
|
43406
43450
|
}
|
|
43407
|
-
function _temp5$
|
|
43451
|
+
function _temp5$4(e) {
|
|
43408
43452
|
let [r, c] = TIME_UNIT_DESCRIPTIONS[e];
|
|
43409
43453
|
return (0, import_jsx_runtime.jsx)(SelectItem, {
|
|
43410
43454
|
value: e,
|
|
@@ -43416,7 +43460,7 @@ ${c}
|
|
|
43416
43460
|
children: r
|
|
43417
43461
|
}, e);
|
|
43418
43462
|
}
|
|
43419
|
-
function _temp6$
|
|
43463
|
+
function _temp6$4(e) {
|
|
43420
43464
|
return (0, import_jsx_runtime.jsx)("span", {
|
|
43421
43465
|
className: "text-xs text-muted-foreground pr-10",
|
|
43422
43466
|
children: e
|
|
@@ -45609,6 +45653,12 @@ ${c}
|
|
|
45609
45653
|
"<=": [
|
|
45610
45654
|
e
|
|
45611
45655
|
],
|
|
45656
|
+
between: [
|
|
45657
|
+
object({
|
|
45658
|
+
min: e,
|
|
45659
|
+
max: e
|
|
45660
|
+
})
|
|
45661
|
+
],
|
|
45612
45662
|
is_null: [],
|
|
45613
45663
|
is_not_null: []
|
|
45614
45664
|
});
|
|
@@ -45637,6 +45687,7 @@ ${c}
|
|
|
45637
45687
|
not_in: [
|
|
45638
45688
|
Schema.stringMultiColumnValues
|
|
45639
45689
|
],
|
|
45690
|
+
is_empty: [],
|
|
45640
45691
|
is_null: [],
|
|
45641
45692
|
is_not_null: []
|
|
45642
45693
|
}, ALL_OPERATORS = {
|
|
@@ -45753,17 +45804,30 @@ ${c}
|
|
|
45753
45804
|
special: "radio_group"
|
|
45754
45805
|
})).default("last")
|
|
45755
45806
|
});
|
|
45756
|
-
const
|
|
45807
|
+
const FilterConditionSchema = object({
|
|
45757
45808
|
column_id,
|
|
45758
45809
|
operator: _enum(Object.keys(ALL_OPERATORS)).describe(FieldOptions.of({
|
|
45759
45810
|
label: " "
|
|
45760
45811
|
})),
|
|
45812
|
+
type: literal("condition").default("condition"),
|
|
45761
45813
|
value: any().describe(FieldOptions.of({
|
|
45762
45814
|
label: "Value"
|
|
45763
|
-
}))
|
|
45815
|
+
})),
|
|
45816
|
+
negate: boolean().default(false)
|
|
45764
45817
|
}).describe(FieldOptions.of({
|
|
45765
45818
|
direction: "row",
|
|
45766
45819
|
special: "column_filter"
|
|
45820
|
+
})), FilterGroupSchema = lazy$2(() => object({
|
|
45821
|
+
type: literal("group").default("group"),
|
|
45822
|
+
operator: _enum([
|
|
45823
|
+
"and",
|
|
45824
|
+
"or"
|
|
45825
|
+
]).default("and"),
|
|
45826
|
+
children: array(union([
|
|
45827
|
+
FilterConditionSchema,
|
|
45828
|
+
FilterGroupSchema
|
|
45829
|
+
])).default([]),
|
|
45830
|
+
negate: boolean().default(false)
|
|
45767
45831
|
}));
|
|
45768
45832
|
var FilterRowsTransformSchema = object({
|
|
45769
45833
|
type: literal("filter_rows"),
|
|
@@ -45773,16 +45837,23 @@ ${c}
|
|
|
45773
45837
|
]).default("keep_rows").describe(FieldOptions.of({
|
|
45774
45838
|
special: "radio_group"
|
|
45775
45839
|
})),
|
|
45776
|
-
where: array(
|
|
45840
|
+
where: array(FilterConditionSchema).min(1).describe(FieldOptions.of({
|
|
45777
45841
|
label: "Value",
|
|
45778
45842
|
minLength: 1
|
|
45779
|
-
})).
|
|
45843
|
+
})).default(() => [
|
|
45780
45844
|
{
|
|
45781
45845
|
column_id: "",
|
|
45782
45846
|
operator: "==",
|
|
45783
|
-
value: ""
|
|
45784
|
-
|
|
45785
|
-
|
|
45847
|
+
value: "",
|
|
45848
|
+
type: "condition",
|
|
45849
|
+
negate: false
|
|
45850
|
+
}
|
|
45851
|
+
]).transform((e) => ({
|
|
45852
|
+
type: "group",
|
|
45853
|
+
operator: "and",
|
|
45854
|
+
children: e.filter((e2) => isConditionValueValid(e2.operator, e2.value)),
|
|
45855
|
+
negate: false
|
|
45856
|
+
}))
|
|
45786
45857
|
}), GroupByTransformSchema = object({
|
|
45787
45858
|
type: literal("group_by"),
|
|
45788
45859
|
column_ids: array(column_id.describe(FieldOptions.of({
|
|
@@ -46008,7 +46079,7 @@ ${c}
|
|
|
46008
46079
|
descending: boolean()
|
|
46009
46080
|
})).optional(),
|
|
46010
46081
|
query: string().optional(),
|
|
46011
|
-
filters:
|
|
46082
|
+
filters: FilterGroupSchema.optional(),
|
|
46012
46083
|
page_number: number(),
|
|
46013
46084
|
page_size: number(),
|
|
46014
46085
|
max_columns: number().nullable().optional()
|
|
@@ -46166,9 +46237,9 @@ ${c}
|
|
|
46166
46237
|
query: M,
|
|
46167
46238
|
page_number: E.pageIndex,
|
|
46168
46239
|
page_size: E.pageSize,
|
|
46169
|
-
filters: Y7
|
|
46240
|
+
filters: filtersToFilterGroup(Y7)
|
|
46170
46241
|
});
|
|
46171
|
-
if (S2) w2.catch(
|
|
46242
|
+
if (S2) w2.catch(_temp2$15);
|
|
46172
46243
|
else {
|
|
46173
46244
|
let e2 = await w2;
|
|
46174
46245
|
r2 = e2.data, c2 = e2.raw_data, d2 = e2.total_rows, h = e2.cell_styles || {}, _2 = e2.cell_hover_texts || {};
|
|
@@ -46202,15 +46273,15 @@ ${c}
|
|
|
46202
46273
|
rows: await loadTableData((await f({
|
|
46203
46274
|
page_number: e2,
|
|
46204
46275
|
page_size: 1,
|
|
46205
|
-
sort: y.length > 0 ? y.map(
|
|
46276
|
+
sort: y.length > 0 ? y.map(_temp3$10) : void 0,
|
|
46206
46277
|
query: M,
|
|
46207
|
-
filters: Y7
|
|
46278
|
+
filters: filtersToFilterGroup(Y7),
|
|
46208
46279
|
max_columns: null
|
|
46209
46280
|
})).data)
|
|
46210
46281
|
}), r[48] = Y7, r[49] = f, r[50] = M, r[51] = y, r[52] = l9) : l9 = r[52];
|
|
46211
46282
|
let u9 = l9, d9;
|
|
46212
46283
|
r[53] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel") ? (d9 = () => {
|
|
46213
|
-
O(
|
|
46284
|
+
O(_temp4$6);
|
|
46214
46285
|
}, r[53] = d9) : d9 = r[53];
|
|
46215
46286
|
let f9 = a9 == null ? void 0 : a9.totalRows, p9;
|
|
46216
46287
|
r[54] === f9 ? p9 = r[55] : (p9 = [
|
|
@@ -46512,21 +46583,15 @@ ${c}
|
|
|
46512
46583
|
};
|
|
46513
46584
|
}
|
|
46514
46585
|
function _temp2$15(e) {
|
|
46515
|
-
return filterToFilterCondition(e.id, e.value);
|
|
46516
|
-
}
|
|
46517
|
-
function _temp3$10(e) {
|
|
46518
46586
|
Logger.error(e);
|
|
46519
46587
|
}
|
|
46520
|
-
function
|
|
46588
|
+
function _temp3$10(e) {
|
|
46521
46589
|
return {
|
|
46522
46590
|
by: e.id,
|
|
46523
46591
|
descending: e.desc
|
|
46524
46592
|
};
|
|
46525
46593
|
}
|
|
46526
|
-
function
|
|
46527
|
-
return filterToFilterCondition(e.id, e.value);
|
|
46528
|
-
}
|
|
46529
|
-
function _temp6$4(e) {
|
|
46594
|
+
function _temp4$6(e) {
|
|
46530
46595
|
return e.pageIndex === 0 ? e : {
|
|
46531
46596
|
...e,
|
|
46532
46597
|
pageIndex: 0
|
|
@@ -49109,7 +49174,7 @@ ${c}
|
|
|
49109
49174
|
descending: boolean()
|
|
49110
49175
|
})).optional(),
|
|
49111
49176
|
query: string().optional(),
|
|
49112
|
-
filters:
|
|
49177
|
+
filters: FilterGroupSchema.optional(),
|
|
49113
49178
|
page_number: number(),
|
|
49114
49179
|
page_size: number()
|
|
49115
49180
|
})).output(object({
|
|
@@ -68770,7 +68835,7 @@ ${c}
|
|
|
68770
68835
|
return Logger.warn("Failed to get version from mount config"), null;
|
|
68771
68836
|
}
|
|
68772
68837
|
}
|
|
68773
|
-
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.23.2-
|
|
68838
|
+
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.23.2-dev14"), showCodeInRunModeAtom = atom(true);
|
|
68774
68839
|
atom(null);
|
|
68775
68840
|
var VIRTUAL_FILE_REGEX = /\/@file\/([^\s"&'/]+)\.([\dA-Za-z]+)/g, VirtualFileTracker = class e {
|
|
68776
68841
|
constructor() {
|
package/package.json
CHANGED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
filterToFilterCondition,
|
|
5
|
+
filtersToFilterGroup,
|
|
6
|
+
Filter,
|
|
7
|
+
} from "../filters";
|
|
8
|
+
import {
|
|
9
|
+
FilterConditionSchema,
|
|
10
|
+
FilterGroupSchema,
|
|
11
|
+
} from "@/plugins/impl/data-frames/schema";
|
|
12
|
+
|
|
13
|
+
describe("filterToFilterCondition", () => {
|
|
14
|
+
it("returns empty array for undefined filter", () => {
|
|
15
|
+
expect(filterToFilterCondition("col", undefined)).toEqual([]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("handles is_null filter", () => {
|
|
19
|
+
const result = filterToFilterCondition(
|
|
20
|
+
"col",
|
|
21
|
+
Filter.number({ operator: "is_null" }),
|
|
22
|
+
);
|
|
23
|
+
expect(result).toEqual([
|
|
24
|
+
{
|
|
25
|
+
column_id: "col",
|
|
26
|
+
operator: "is_null",
|
|
27
|
+
value: undefined,
|
|
28
|
+
type: "condition",
|
|
29
|
+
negate: false,
|
|
30
|
+
},
|
|
31
|
+
]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("handles is_not_null filter", () => {
|
|
35
|
+
const result = filterToFilterCondition(
|
|
36
|
+
"col",
|
|
37
|
+
Filter.number({ operator: "is_not_null" }),
|
|
38
|
+
);
|
|
39
|
+
expect(result).toEqual([
|
|
40
|
+
{
|
|
41
|
+
column_id: "col",
|
|
42
|
+
operator: "is_not_null",
|
|
43
|
+
value: undefined,
|
|
44
|
+
type: "condition",
|
|
45
|
+
negate: false,
|
|
46
|
+
},
|
|
47
|
+
]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("handles number filter with min only", () => {
|
|
51
|
+
const result = filterToFilterCondition("age", Filter.number({ min: 18 }));
|
|
52
|
+
expect(result).toHaveLength(1);
|
|
53
|
+
expect(result[0]).toMatchObject({
|
|
54
|
+
column_id: "age",
|
|
55
|
+
operator: ">=",
|
|
56
|
+
value: 18,
|
|
57
|
+
type: "condition",
|
|
58
|
+
negate: false,
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("handles number filter with max only", () => {
|
|
63
|
+
const result = filterToFilterCondition("age", Filter.number({ max: 65 }));
|
|
64
|
+
expect(result).toHaveLength(1);
|
|
65
|
+
expect(result[0]).toMatchObject({
|
|
66
|
+
column_id: "age",
|
|
67
|
+
operator: "<=",
|
|
68
|
+
value: 65,
|
|
69
|
+
type: "condition",
|
|
70
|
+
negate: false,
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("handles number filter with min and max", () => {
|
|
75
|
+
const result = filterToFilterCondition(
|
|
76
|
+
"age",
|
|
77
|
+
Filter.number({ min: 18, max: 65 }),
|
|
78
|
+
);
|
|
79
|
+
expect(result).toHaveLength(2);
|
|
80
|
+
expect(result[0]).toMatchObject({ operator: ">=", value: 18 });
|
|
81
|
+
expect(result[1]).toMatchObject({ operator: "<=", value: 65 });
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("handles text filter", () => {
|
|
85
|
+
const result = filterToFilterCondition(
|
|
86
|
+
"name",
|
|
87
|
+
Filter.text({ text: "foo", operator: "contains" }),
|
|
88
|
+
);
|
|
89
|
+
expect(result).toEqual([
|
|
90
|
+
{
|
|
91
|
+
column_id: "name",
|
|
92
|
+
operator: "contains",
|
|
93
|
+
value: "foo",
|
|
94
|
+
type: "condition",
|
|
95
|
+
negate: false,
|
|
96
|
+
},
|
|
97
|
+
]);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("handles boolean true filter", () => {
|
|
101
|
+
const result = filterToFilterCondition(
|
|
102
|
+
"active",
|
|
103
|
+
Filter.boolean({ value: true }),
|
|
104
|
+
);
|
|
105
|
+
expect(result).toEqual([
|
|
106
|
+
{
|
|
107
|
+
column_id: "active",
|
|
108
|
+
operator: "is_true",
|
|
109
|
+
value: undefined,
|
|
110
|
+
type: "condition",
|
|
111
|
+
negate: false,
|
|
112
|
+
},
|
|
113
|
+
]);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("handles boolean false filter", () => {
|
|
117
|
+
const result = filterToFilterCondition(
|
|
118
|
+
"active",
|
|
119
|
+
Filter.boolean({ value: false }),
|
|
120
|
+
);
|
|
121
|
+
expect(result).toEqual([
|
|
122
|
+
{
|
|
123
|
+
column_id: "active",
|
|
124
|
+
operator: "is_false",
|
|
125
|
+
value: undefined,
|
|
126
|
+
type: "condition",
|
|
127
|
+
negate: false,
|
|
128
|
+
},
|
|
129
|
+
]);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("handles select in filter", () => {
|
|
133
|
+
const result = filterToFilterCondition(
|
|
134
|
+
"status",
|
|
135
|
+
Filter.select({ options: ["a", "b"], operator: "in" }),
|
|
136
|
+
);
|
|
137
|
+
expect(result).toEqual([
|
|
138
|
+
{
|
|
139
|
+
column_id: "status",
|
|
140
|
+
operator: "in",
|
|
141
|
+
value: ["a", "b"],
|
|
142
|
+
type: "condition",
|
|
143
|
+
negate: false,
|
|
144
|
+
},
|
|
145
|
+
]);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("handles date filter with min and max", () => {
|
|
149
|
+
const min = new Date("2024-01-01");
|
|
150
|
+
const max = new Date("2024-12-31");
|
|
151
|
+
const result = filterToFilterCondition(
|
|
152
|
+
"created",
|
|
153
|
+
Filter.date({ min, max }),
|
|
154
|
+
);
|
|
155
|
+
expect(result).toHaveLength(2);
|
|
156
|
+
expect(result[0]).toMatchObject({
|
|
157
|
+
operator: ">=",
|
|
158
|
+
value: min.toISOString(),
|
|
159
|
+
});
|
|
160
|
+
expect(result[1]).toMatchObject({
|
|
161
|
+
operator: "<=",
|
|
162
|
+
value: max.toISOString(),
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("every condition has type and negate fields", () => {
|
|
167
|
+
const result = filterToFilterCondition(
|
|
168
|
+
"col",
|
|
169
|
+
Filter.number({ min: 1, max: 10 }),
|
|
170
|
+
);
|
|
171
|
+
for (const condition of result) {
|
|
172
|
+
expect(condition).toHaveProperty("type", "condition");
|
|
173
|
+
expect(condition).toHaveProperty("negate", false);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
describe("filtersToFilterGroup", () => {
|
|
179
|
+
it("returns empty AND group for no filters", () => {
|
|
180
|
+
const result = filtersToFilterGroup([]);
|
|
181
|
+
expect(result).toEqual({
|
|
182
|
+
type: "group",
|
|
183
|
+
operator: "and",
|
|
184
|
+
children: [],
|
|
185
|
+
negate: false,
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("wraps single filter in AND group", () => {
|
|
190
|
+
const result = filtersToFilterGroup([
|
|
191
|
+
{ id: "age", value: Filter.number({ min: 18 }) },
|
|
192
|
+
]);
|
|
193
|
+
expect(result.type).toBe("group");
|
|
194
|
+
expect(result.operator).toBe("and");
|
|
195
|
+
expect(result.negate).toBe(false);
|
|
196
|
+
expect(result.children).toHaveLength(1);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("wraps multiple filters in AND group", () => {
|
|
200
|
+
const result = filtersToFilterGroup([
|
|
201
|
+
{ id: "age", value: Filter.number({ min: 18 }) },
|
|
202
|
+
{ id: "name", value: Filter.text({ text: "foo", operator: "contains" }) },
|
|
203
|
+
]);
|
|
204
|
+
expect(result.children).toHaveLength(2);
|
|
205
|
+
expect(result.operator).toBe("and");
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("flattens multi-condition filters", () => {
|
|
209
|
+
const result = filtersToFilterGroup([
|
|
210
|
+
{ id: "age", value: Filter.number({ min: 18, max: 65 }) },
|
|
211
|
+
]);
|
|
212
|
+
// min + max = 2 conditions
|
|
213
|
+
expect(result.children).toHaveLength(2);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
describe("schema validation", () => {
|
|
218
|
+
it("FilterConditionSchema accepts valid condition", () => {
|
|
219
|
+
const result = FilterConditionSchema.safeParse({
|
|
220
|
+
column_id: "age",
|
|
221
|
+
operator: ">=",
|
|
222
|
+
value: 18,
|
|
223
|
+
});
|
|
224
|
+
expect(result.success).toBe(true);
|
|
225
|
+
if (result.success) {
|
|
226
|
+
expect(result.data.type).toBe("condition");
|
|
227
|
+
expect(result.data.negate).toBe(false);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("FilterConditionSchema defaults type and negate", () => {
|
|
232
|
+
const result = FilterConditionSchema.safeParse({
|
|
233
|
+
column_id: "age",
|
|
234
|
+
operator: "==",
|
|
235
|
+
value: 5,
|
|
236
|
+
});
|
|
237
|
+
expect(result.success).toBe(true);
|
|
238
|
+
if (result.success) {
|
|
239
|
+
expect(result.data.type).toBe("condition");
|
|
240
|
+
expect(result.data.negate).toBe(false);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("FilterConditionSchema accepts negate=true", () => {
|
|
245
|
+
const result = FilterConditionSchema.safeParse({
|
|
246
|
+
column_id: "age",
|
|
247
|
+
operator: "==",
|
|
248
|
+
value: 5,
|
|
249
|
+
negate: true,
|
|
250
|
+
});
|
|
251
|
+
expect(result.success).toBe(true);
|
|
252
|
+
if (result.success) {
|
|
253
|
+
expect(result.data.negate).toBe(true);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it("FilterGroupSchema accepts valid group", () => {
|
|
258
|
+
const result = FilterGroupSchema.safeParse({
|
|
259
|
+
type: "group",
|
|
260
|
+
operator: "and",
|
|
261
|
+
children: [{ column_id: "age", operator: ">=", value: 18 }],
|
|
262
|
+
});
|
|
263
|
+
expect(result.success).toBe(true);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("FilterGroupSchema accepts nested groups", () => {
|
|
267
|
+
const result = FilterGroupSchema.safeParse({
|
|
268
|
+
type: "group",
|
|
269
|
+
operator: "or",
|
|
270
|
+
children: [
|
|
271
|
+
{
|
|
272
|
+
type: "group",
|
|
273
|
+
operator: "and",
|
|
274
|
+
children: [
|
|
275
|
+
{ column_id: "a", operator: "==", value: 1 },
|
|
276
|
+
{ column_id: "b", operator: ">", value: 2 },
|
|
277
|
+
],
|
|
278
|
+
},
|
|
279
|
+
{ column_id: "c", operator: "==", value: 3 },
|
|
280
|
+
],
|
|
281
|
+
});
|
|
282
|
+
expect(result.success).toBe(true);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it("FilterGroupSchema rejects invalid operator", () => {
|
|
286
|
+
const result = FilterGroupSchema.safeParse({
|
|
287
|
+
type: "group",
|
|
288
|
+
operator: "xor",
|
|
289
|
+
children: [],
|
|
290
|
+
});
|
|
291
|
+
expect(result.success).toBe(false);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it("FilterGroupSchema defaults fields", () => {
|
|
295
|
+
const result = FilterGroupSchema.safeParse({});
|
|
296
|
+
expect(result.success).toBe(true);
|
|
297
|
+
if (result.success) {
|
|
298
|
+
expect(result.data.type).toBe("group");
|
|
299
|
+
expect(result.data.operator).toBe("and");
|
|
300
|
+
expect(result.data.children).toEqual([]);
|
|
301
|
+
expect(result.data.negate).toBe(false);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
});
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
"use no memo";
|
|
3
3
|
|
|
4
|
-
import type { RowData } from "@tanstack/react-table";
|
|
4
|
+
import type { ColumnFiltersState, RowData } from "@tanstack/react-table";
|
|
5
5
|
import type { DataType } from "@/core/kernel/messages";
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
FilterConditionType,
|
|
8
|
+
FilterGroupType,
|
|
9
|
+
} from "@/plugins/impl/data-frames/schema";
|
|
7
10
|
import type { ColumnId } from "@/plugins/impl/data-frames/types";
|
|
8
11
|
import type { OperatorType } from "@/plugins/impl/data-frames/utils/operators";
|
|
9
12
|
import { assertNever } from "@/utils/assertNever";
|
|
@@ -84,28 +87,34 @@ export type ColumnFilterForType<T extends FilterType> = T extends FilterType
|
|
|
84
87
|
export function filterToFilterCondition(
|
|
85
88
|
columnIdString: string,
|
|
86
89
|
filter: ColumnFilterValue | undefined,
|
|
87
|
-
):
|
|
90
|
+
): FilterConditionType[] {
|
|
88
91
|
if (!filter) {
|
|
89
92
|
return [];
|
|
90
93
|
}
|
|
91
94
|
const columnId = columnIdString as ColumnId;
|
|
92
95
|
|
|
93
96
|
if (filter.operator === "is_null" || filter.operator === "is_not_null") {
|
|
94
|
-
return
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
return [
|
|
98
|
+
{
|
|
99
|
+
column_id: columnId,
|
|
100
|
+
operator: filter.operator,
|
|
101
|
+
value: undefined,
|
|
102
|
+
type: "condition",
|
|
103
|
+
negate: false,
|
|
104
|
+
},
|
|
105
|
+
];
|
|
99
106
|
}
|
|
100
107
|
|
|
101
108
|
switch (filter.type) {
|
|
102
109
|
case "number": {
|
|
103
|
-
const conditions:
|
|
110
|
+
const conditions: FilterConditionType[] = [];
|
|
104
111
|
if (filter.min !== undefined) {
|
|
105
112
|
conditions.push({
|
|
106
113
|
column_id: columnId,
|
|
107
114
|
operator: ">=",
|
|
108
115
|
value: filter.min,
|
|
116
|
+
type: "condition",
|
|
117
|
+
negate: false,
|
|
109
118
|
});
|
|
110
119
|
}
|
|
111
120
|
if (filter.max !== undefined) {
|
|
@@ -113,23 +122,31 @@ export function filterToFilterCondition(
|
|
|
113
122
|
column_id: columnId,
|
|
114
123
|
operator: "<=",
|
|
115
124
|
value: filter.max,
|
|
125
|
+
type: "condition",
|
|
126
|
+
negate: false,
|
|
116
127
|
});
|
|
117
128
|
}
|
|
118
129
|
return conditions;
|
|
119
130
|
}
|
|
120
131
|
case "text":
|
|
121
|
-
return
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
132
|
+
return [
|
|
133
|
+
{
|
|
134
|
+
column_id: columnId,
|
|
135
|
+
operator: filter.operator,
|
|
136
|
+
value: filter.text,
|
|
137
|
+
type: "condition",
|
|
138
|
+
negate: false,
|
|
139
|
+
},
|
|
140
|
+
];
|
|
126
141
|
case "datetime": {
|
|
127
|
-
const conditions:
|
|
142
|
+
const conditions: FilterConditionType[] = [];
|
|
128
143
|
if (filter.min !== undefined) {
|
|
129
144
|
conditions.push({
|
|
130
145
|
column_id: columnId,
|
|
131
146
|
operator: ">=",
|
|
132
147
|
value: filter.min.toISOString(),
|
|
148
|
+
type: "condition",
|
|
149
|
+
negate: false,
|
|
133
150
|
});
|
|
134
151
|
}
|
|
135
152
|
if (filter.max !== undefined) {
|
|
@@ -137,17 +154,21 @@ export function filterToFilterCondition(
|
|
|
137
154
|
column_id: columnId,
|
|
138
155
|
operator: "<=",
|
|
139
156
|
value: filter.max.toISOString(),
|
|
157
|
+
type: "condition",
|
|
158
|
+
negate: false,
|
|
140
159
|
});
|
|
141
160
|
}
|
|
142
161
|
return conditions;
|
|
143
162
|
}
|
|
144
163
|
case "date": {
|
|
145
|
-
const conditions:
|
|
164
|
+
const conditions: FilterConditionType[] = [];
|
|
146
165
|
if (filter.min !== undefined) {
|
|
147
166
|
conditions.push({
|
|
148
167
|
column_id: columnId,
|
|
149
168
|
operator: ">=",
|
|
150
169
|
value: filter.min.toISOString(),
|
|
170
|
+
type: "condition",
|
|
171
|
+
negate: false,
|
|
151
172
|
});
|
|
152
173
|
}
|
|
153
174
|
if (filter.max !== undefined) {
|
|
@@ -155,17 +176,21 @@ export function filterToFilterCondition(
|
|
|
155
176
|
column_id: columnId,
|
|
156
177
|
operator: "<=",
|
|
157
178
|
value: filter.max.toISOString(),
|
|
179
|
+
type: "condition",
|
|
180
|
+
negate: false,
|
|
158
181
|
});
|
|
159
182
|
}
|
|
160
183
|
return conditions;
|
|
161
184
|
}
|
|
162
185
|
case "time": {
|
|
163
|
-
const conditions:
|
|
186
|
+
const conditions: FilterConditionType[] = [];
|
|
164
187
|
if (filter.min !== undefined) {
|
|
165
188
|
conditions.push({
|
|
166
189
|
column_id: columnId,
|
|
167
190
|
operator: ">=",
|
|
168
191
|
value: filter.min.toISOString(),
|
|
192
|
+
type: "condition",
|
|
193
|
+
negate: false,
|
|
169
194
|
});
|
|
170
195
|
}
|
|
171
196
|
if (filter.max !== undefined) {
|
|
@@ -173,24 +198,34 @@ export function filterToFilterCondition(
|
|
|
173
198
|
column_id: columnId,
|
|
174
199
|
operator: "<=",
|
|
175
200
|
value: filter.max.toISOString(),
|
|
201
|
+
type: "condition",
|
|
202
|
+
negate: false,
|
|
176
203
|
});
|
|
177
204
|
}
|
|
178
205
|
return conditions;
|
|
179
206
|
}
|
|
180
207
|
case "boolean":
|
|
181
208
|
if (filter.value) {
|
|
182
|
-
return
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
209
|
+
return [
|
|
210
|
+
{
|
|
211
|
+
column_id: columnId,
|
|
212
|
+
operator: "is_true",
|
|
213
|
+
value: undefined,
|
|
214
|
+
type: "condition",
|
|
215
|
+
negate: false,
|
|
216
|
+
},
|
|
217
|
+
];
|
|
187
218
|
}
|
|
188
219
|
if (!filter.value) {
|
|
189
|
-
return
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
220
|
+
return [
|
|
221
|
+
{
|
|
222
|
+
column_id: columnId,
|
|
223
|
+
operator: "is_false",
|
|
224
|
+
value: undefined,
|
|
225
|
+
type: "condition",
|
|
226
|
+
negate: false,
|
|
227
|
+
},
|
|
228
|
+
];
|
|
194
229
|
}
|
|
195
230
|
|
|
196
231
|
return [];
|
|
@@ -200,16 +235,35 @@ export function filterToFilterCondition(
|
|
|
200
235
|
Logger.warn("Invalid operator for select filter", {
|
|
201
236
|
operator: filter.operator,
|
|
202
237
|
});
|
|
203
|
-
operator = "in";
|
|
238
|
+
operator = "in";
|
|
204
239
|
}
|
|
205
|
-
return
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
240
|
+
return [
|
|
241
|
+
{
|
|
242
|
+
column_id: columnId,
|
|
243
|
+
operator,
|
|
244
|
+
value: filter.options,
|
|
245
|
+
type: "condition",
|
|
246
|
+
negate: false,
|
|
247
|
+
},
|
|
248
|
+
];
|
|
210
249
|
}
|
|
211
250
|
|
|
212
251
|
default:
|
|
213
252
|
assertNever(filter);
|
|
214
253
|
}
|
|
215
254
|
}
|
|
255
|
+
|
|
256
|
+
export function filtersToFilterGroup(
|
|
257
|
+
columnFilters: ColumnFiltersState,
|
|
258
|
+
): FilterGroupType {
|
|
259
|
+
const conditions = columnFilters.flatMap((filter) =>
|
|
260
|
+
filterToFilterCondition(filter.id, filter.value as ColumnFilterValue),
|
|
261
|
+
);
|
|
262
|
+
// To maintain existing behavior "and" all the conditions
|
|
263
|
+
return {
|
|
264
|
+
type: "group",
|
|
265
|
+
operator: "and",
|
|
266
|
+
children: conditions,
|
|
267
|
+
negate: false,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
@@ -9,9 +9,7 @@ exports[`snapshot all duplicate keymaps > default keymaps 2`] = `
|
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
"key": "ArrowDown",
|
|
12
|
-
"preventDefault": true,
|
|
13
12
|
"run": "run",
|
|
14
|
-
"stopPropagation": true,
|
|
15
13
|
},
|
|
16
14
|
{
|
|
17
15
|
"key": "ArrowDown",
|
|
@@ -27,9 +25,7 @@ exports[`snapshot all duplicate keymaps > default keymaps 2`] = `
|
|
|
27
25
|
},
|
|
28
26
|
{
|
|
29
27
|
"key": "ArrowUp",
|
|
30
|
-
"preventDefault": true,
|
|
31
28
|
"run": "run",
|
|
32
|
-
"stopPropagation": true,
|
|
33
29
|
},
|
|
34
30
|
{
|
|
35
31
|
"key": "ArrowUp",
|
|
@@ -118,15 +114,12 @@ exports[`snapshot all duplicate keymaps > vim keymaps 2`] = `
|
|
|
118
114
|
},
|
|
119
115
|
{
|
|
120
116
|
"key": "ArrowDown",
|
|
121
|
-
"preventDefault": true,
|
|
122
117
|
"run": "run",
|
|
123
|
-
"stopPropagation": true,
|
|
124
118
|
},
|
|
125
119
|
{
|
|
126
120
|
"key": "ArrowDown",
|
|
127
|
-
"
|
|
128
|
-
"
|
|
129
|
-
"shift": "selectLineDown",
|
|
121
|
+
"run": "<no name>",
|
|
122
|
+
"shift": "<no name>",
|
|
130
123
|
},
|
|
131
124
|
],
|
|
132
125
|
"ArrowUp": [
|
|
@@ -136,15 +129,12 @@ exports[`snapshot all duplicate keymaps > vim keymaps 2`] = `
|
|
|
136
129
|
},
|
|
137
130
|
{
|
|
138
131
|
"key": "ArrowUp",
|
|
139
|
-
"preventDefault": true,
|
|
140
132
|
"run": "run",
|
|
141
|
-
"stopPropagation": true,
|
|
142
133
|
},
|
|
143
134
|
{
|
|
144
135
|
"key": "ArrowUp",
|
|
145
|
-
"
|
|
146
|
-
"
|
|
147
|
-
"shift": "selectLineUp",
|
|
136
|
+
"run": "<no name>",
|
|
137
|
+
"shift": "<no name>",
|
|
148
138
|
},
|
|
149
139
|
],
|
|
150
140
|
"Backspace": [
|
|
@@ -169,8 +169,6 @@ function cellKeymaps({
|
|
|
169
169
|
},
|
|
170
170
|
{
|
|
171
171
|
key: "ArrowUp",
|
|
172
|
-
preventDefault: true,
|
|
173
|
-
stopPropagation: true,
|
|
174
172
|
run: (ev) => {
|
|
175
173
|
// Skip if we are in the middle of an autocompletion
|
|
176
174
|
const hasAutocomplete = completionStatus(ev.state);
|
|
@@ -188,8 +186,6 @@ function cellKeymaps({
|
|
|
188
186
|
},
|
|
189
187
|
{
|
|
190
188
|
key: "ArrowDown",
|
|
191
|
-
preventDefault: true,
|
|
192
|
-
stopPropagation: true,
|
|
193
189
|
run: (ev) => {
|
|
194
190
|
// Skip if we are in the middle of an autocompletion
|
|
195
191
|
const hasAutocomplete = completionStatus(ev.state);
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
cursorCharLeft,
|
|
5
|
+
cursorCharRight,
|
|
6
|
+
cursorLineDown,
|
|
7
|
+
cursorLineUp,
|
|
4
8
|
insertNewlineAndIndent,
|
|
5
9
|
defaultKeymap as originalDefaultKeymap,
|
|
10
|
+
selectCharLeft,
|
|
11
|
+
selectCharRight,
|
|
12
|
+
selectLineDown,
|
|
13
|
+
selectLineUp,
|
|
6
14
|
toggleBlockComment,
|
|
7
15
|
toggleComment,
|
|
8
16
|
} from "@codemirror/commands";
|
|
@@ -13,7 +21,7 @@ import {
|
|
|
13
21
|
type KeyBinding,
|
|
14
22
|
keymap,
|
|
15
23
|
} from "@codemirror/view";
|
|
16
|
-
import { getCM, vim } from "@replit/codemirror-vim";
|
|
24
|
+
import { type CodeMirror, getCM, vim } from "@replit/codemirror-vim";
|
|
17
25
|
import type { KeymapConfig } from "@/core/config/config-schema";
|
|
18
26
|
import type { HotkeyProvider } from "@/core/hotkeys/hotkeys";
|
|
19
27
|
import { logNever } from "@/utils/assertNever";
|
|
@@ -62,6 +70,12 @@ export function keymapBundle(
|
|
|
62
70
|
},
|
|
63
71
|
),
|
|
64
72
|
),
|
|
73
|
+
// Arrow keys: use CodeMirror's cursor movement except in vim visual
|
|
74
|
+
// mode, where vim must handle them to maintain selection.
|
|
75
|
+
// The original cursorLineUp/Down bindings from the default keymap are
|
|
76
|
+
// filtered out (see defaultVimKeymap) because their preventDefault
|
|
77
|
+
// flag blocks vim's handler even when their run function returns false.
|
|
78
|
+
keymap.of(vimVisualModeArrowKeyBindings()),
|
|
65
79
|
// Base vim mode
|
|
66
80
|
vim({ status: false }),
|
|
67
81
|
// Custom vim keymaps for cell navigation
|
|
@@ -101,12 +115,22 @@ const overrideKeymap = (keymap: HotkeyProvider): readonly KeyBinding[] => {
|
|
|
101
115
|
};
|
|
102
116
|
|
|
103
117
|
const defaultVimKeymap = once(() => {
|
|
104
|
-
const toRemove = new Set([
|
|
118
|
+
const toRemove = new Set([
|
|
119
|
+
"Enter",
|
|
120
|
+
"Ctrl-v",
|
|
121
|
+
"ArrowUp",
|
|
122
|
+
"ArrowDown",
|
|
123
|
+
"ArrowLeft",
|
|
124
|
+
"ArrowRight",
|
|
125
|
+
]);
|
|
105
126
|
// Remove conflicting keys from the keymap
|
|
106
127
|
// Enter (<CR>) adds a new line
|
|
107
128
|
// - it should just go to the next line
|
|
108
129
|
// Ctrl-v goes to the bottom of the cell
|
|
109
130
|
// - should enter blockwise visual mode
|
|
131
|
+
// ArrowUp/ArrowDown (cursorLineUp/Down) always handle the event and have
|
|
132
|
+
// preventDefault, which blocks vim's handler from processing arrow keys.
|
|
133
|
+
// Replaced with visual-mode-aware wrappers in keymapBundle.
|
|
110
134
|
return defaultKeymap().filter(
|
|
111
135
|
(k) => !toRemove.has(k.key || k.mac || k.linux || k.win || ""),
|
|
112
136
|
);
|
|
@@ -155,6 +179,49 @@ function doubleCharacterListener(
|
|
|
155
179
|
]);
|
|
156
180
|
}
|
|
157
181
|
|
|
182
|
+
function isInVimVisualMode(cm: CodeMirror | undefined | null): boolean {
|
|
183
|
+
return cm?.state.vim?.visualMode === true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* In vim visual mode, arrow keys must be handled by vim to maintain selection.
|
|
188
|
+
* Wrap each arrow key's run and shift so they defer to vim in visual mode,
|
|
189
|
+
* but use CodeMirror's cursor commands in all other modes.
|
|
190
|
+
*/
|
|
191
|
+
function vimVisualModeArrowKeyBindings(): KeyBinding[] {
|
|
192
|
+
const wrap =
|
|
193
|
+
(cmd: Command): Command =>
|
|
194
|
+
(view) => {
|
|
195
|
+
if (isInVimVisualMode(getCM(view))) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
return cmd(view);
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
return [
|
|
202
|
+
{
|
|
203
|
+
key: "ArrowDown",
|
|
204
|
+
run: wrap(cursorLineDown),
|
|
205
|
+
shift: wrap(selectLineDown),
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
key: "ArrowUp",
|
|
209
|
+
run: wrap(cursorLineUp),
|
|
210
|
+
shift: wrap(selectLineUp),
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
key: "ArrowLeft",
|
|
214
|
+
run: wrap(cursorCharLeft),
|
|
215
|
+
shift: wrap(selectCharLeft),
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
key: "ArrowRight",
|
|
219
|
+
run: wrap(cursorCharRight),
|
|
220
|
+
shift: wrap(selectCharRight),
|
|
221
|
+
},
|
|
222
|
+
];
|
|
223
|
+
}
|
|
224
|
+
|
|
158
225
|
export const visibleForTesting = {
|
|
159
226
|
defaultKeymap,
|
|
160
227
|
defaultVimKeymap,
|
|
@@ -28,10 +28,7 @@ import { TablePanel } from "@/components/data-table/charts/charts";
|
|
|
28
28
|
import { hasChart } from "@/components/data-table/charts/storage";
|
|
29
29
|
import { ColumnChartSpecModel } from "@/components/data-table/column-summary/chart-spec-model";
|
|
30
30
|
import { ColumnChartContext } from "@/components/data-table/column-summary/column-summary";
|
|
31
|
-
import {
|
|
32
|
-
type ColumnFilterValue,
|
|
33
|
-
filterToFilterCondition,
|
|
34
|
-
} from "@/components/data-table/filters";
|
|
31
|
+
import { filtersToFilterGroup } from "@/components/data-table/filters";
|
|
35
32
|
import { usePanelOwnership } from "@/components/data-table/hooks/use-panel-ownership";
|
|
36
33
|
import { LoadingTable } from "@/components/data-table/loading-table";
|
|
37
34
|
import {
|
|
@@ -86,8 +83,8 @@ import { rpc } from "../core/rpc";
|
|
|
86
83
|
import { Banner } from "./common/error-banner";
|
|
87
84
|
import { Labeled } from "./common/labeled";
|
|
88
85
|
import {
|
|
89
|
-
|
|
90
|
-
type
|
|
86
|
+
FilterGroupSchema,
|
|
87
|
+
type FilterGroupType,
|
|
91
88
|
columnToFieldTypesSchema,
|
|
92
89
|
} from "./data-frames/schema";
|
|
93
90
|
|
|
@@ -213,7 +210,7 @@ type DataTableFunctions = {
|
|
|
213
210
|
descending: boolean;
|
|
214
211
|
}[];
|
|
215
212
|
query?: string;
|
|
216
|
-
filters?:
|
|
213
|
+
filters?: FilterGroupType;
|
|
217
214
|
page_number: number;
|
|
218
215
|
page_size: number;
|
|
219
216
|
max_columns?: number | null;
|
|
@@ -312,7 +309,7 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
|
|
|
312
309
|
)
|
|
313
310
|
.optional(),
|
|
314
311
|
query: z.string().optional(),
|
|
315
|
-
filters:
|
|
312
|
+
filters: FilterGroupSchema.optional(),
|
|
316
313
|
page_number: z.number(),
|
|
317
314
|
page_size: z.number(),
|
|
318
315
|
max_columns: z.number().nullable().optional(),
|
|
@@ -578,12 +575,7 @@ export const LoadingDataTableComponent = memo(
|
|
|
578
575
|
query: searchQuery,
|
|
579
576
|
page_number: paginationState.pageIndex,
|
|
580
577
|
page_size: paginationState.pageSize,
|
|
581
|
-
filters: filters
|
|
582
|
-
return filterToFilterCondition(
|
|
583
|
-
filter.id,
|
|
584
|
-
filter.value as ColumnFilterValue,
|
|
585
|
-
);
|
|
586
|
-
}),
|
|
578
|
+
filters: filtersToFilterGroup(filters),
|
|
587
579
|
});
|
|
588
580
|
|
|
589
581
|
if (canShowInitialPage) {
|
|
@@ -641,12 +633,7 @@ export const LoadingDataTableComponent = memo(
|
|
|
641
633
|
page_size: 1,
|
|
642
634
|
sort: sortArgs,
|
|
643
635
|
query: searchQuery,
|
|
644
|
-
filters: filters
|
|
645
|
-
return filterToFilterCondition(
|
|
646
|
-
filter.id,
|
|
647
|
-
filter.value as ColumnFilterValue,
|
|
648
|
-
);
|
|
649
|
-
}),
|
|
636
|
+
filters: filtersToFilterGroup(filters),
|
|
650
637
|
// Do not clamp number of columns since we are viewing a single row
|
|
651
638
|
max_columns: null,
|
|
652
639
|
});
|
|
@@ -29,8 +29,8 @@ import { LoadingDataTableComponent, TableProviders } from "../DataTablePlugin";
|
|
|
29
29
|
import type { DataType } from "../vega/vega-loader";
|
|
30
30
|
import { TransformPanel, type TransformPanelHandle } from "./panel";
|
|
31
31
|
import {
|
|
32
|
-
|
|
33
|
-
type
|
|
32
|
+
FilterGroupSchema,
|
|
33
|
+
type FilterGroupType,
|
|
34
34
|
columnToFieldTypesSchema,
|
|
35
35
|
type Transformations,
|
|
36
36
|
} from "./schema";
|
|
@@ -75,7 +75,7 @@ type PluginFunctions = {
|
|
|
75
75
|
descending: boolean;
|
|
76
76
|
}[];
|
|
77
77
|
query?: string;
|
|
78
|
-
filters?:
|
|
78
|
+
filters?: FilterGroupType;
|
|
79
79
|
page_number: number;
|
|
80
80
|
page_size: number;
|
|
81
81
|
}) => Promise<{
|
|
@@ -138,7 +138,7 @@ export const DataFramePlugin = createPlugin<S>("marimo-dataframe")
|
|
|
138
138
|
)
|
|
139
139
|
.optional(),
|
|
140
140
|
query: z.string().optional(),
|
|
141
|
-
filters:
|
|
141
|
+
filters: FilterGroupSchema.optional(),
|
|
142
142
|
page_number: z.number(),
|
|
143
143
|
page_size: z.number(),
|
|
144
144
|
}),
|
|
@@ -73,16 +73,36 @@ const SortColumnTransformSchema = z.object({
|
|
|
73
73
|
.default("last"),
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
-
export const
|
|
76
|
+
export const FilterConditionSchema = z
|
|
77
77
|
.object({
|
|
78
78
|
column_id: column_id,
|
|
79
79
|
operator: z
|
|
80
80
|
.enum(Object.keys(ALL_OPERATORS) as [OperatorType, ...OperatorType[]])
|
|
81
81
|
.describe(FieldOptions.of({ label: " " })),
|
|
82
|
+
type: z.literal("condition").default("condition"),
|
|
82
83
|
value: z.any().describe(FieldOptions.of({ label: "Value" })),
|
|
84
|
+
negate: z.boolean().default(false),
|
|
83
85
|
})
|
|
84
86
|
.describe(FieldOptions.of({ direction: "row", special: "column_filter" }));
|
|
85
|
-
export type
|
|
87
|
+
export type FilterConditionType = z.infer<typeof FilterConditionSchema>;
|
|
88
|
+
|
|
89
|
+
export interface FilterGroupType {
|
|
90
|
+
type: "group";
|
|
91
|
+
operator: "and" | "or";
|
|
92
|
+
children: (FilterConditionType | FilterGroupType)[];
|
|
93
|
+
negate: boolean;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const FilterGroupSchema: z.ZodType<FilterGroupType> = z.lazy(() =>
|
|
97
|
+
z.object({
|
|
98
|
+
type: z.literal("group").default("group"),
|
|
99
|
+
operator: z.enum(["and", "or"]).default("and"),
|
|
100
|
+
children: z
|
|
101
|
+
.array(z.union([FilterConditionSchema, FilterGroupSchema]))
|
|
102
|
+
.default([]),
|
|
103
|
+
negate: z.boolean().default(false),
|
|
104
|
+
}),
|
|
105
|
+
);
|
|
86
106
|
|
|
87
107
|
const FilterRowsTransformSchema = z.object({
|
|
88
108
|
type: z.literal("filter_rows"),
|
|
@@ -91,17 +111,29 @@ const FilterRowsTransformSchema = z.object({
|
|
|
91
111
|
.default("keep_rows")
|
|
92
112
|
.describe(FieldOptions.of({ special: "radio_group" })),
|
|
93
113
|
where: z
|
|
94
|
-
.array(
|
|
114
|
+
.array(FilterConditionSchema)
|
|
95
115
|
.min(1)
|
|
96
116
|
.describe(FieldOptions.of({ label: "Value", minLength: 1 }))
|
|
97
|
-
.
|
|
98
|
-
|
|
117
|
+
.default(() => [
|
|
118
|
+
{
|
|
119
|
+
column_id: "" as ColumnId,
|
|
120
|
+
operator: "==" as const,
|
|
121
|
+
value: "",
|
|
122
|
+
type: "condition" as const,
|
|
123
|
+
negate: false,
|
|
124
|
+
},
|
|
125
|
+
])
|
|
126
|
+
.transform((value): FilterGroupType => {
|
|
127
|
+
const validConditions = value.filter((condition) => {
|
|
99
128
|
return isConditionValueValid(condition.operator, condition.value);
|
|
100
129
|
});
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
130
|
+
return {
|
|
131
|
+
type: "group",
|
|
132
|
+
operator: "and",
|
|
133
|
+
children: validConditions,
|
|
134
|
+
negate: false,
|
|
135
|
+
};
|
|
136
|
+
}),
|
|
105
137
|
});
|
|
106
138
|
|
|
107
139
|
const GroupByTransformSchema = z
|
|
@@ -41,6 +41,7 @@ const createComparisonOperators = (schema: z.ZodType) => ({
|
|
|
41
41
|
">=": [schema],
|
|
42
42
|
"<": [schema],
|
|
43
43
|
"<=": [schema],
|
|
44
|
+
between: [z.object({ min: schema, max: schema })],
|
|
44
45
|
is_null: [],
|
|
45
46
|
is_not_null: [],
|
|
46
47
|
});
|
|
@@ -59,6 +60,7 @@ export const STRING_OPERATORS = {
|
|
|
59
60
|
ends_with: [Schema.string],
|
|
60
61
|
in: [Schema.stringMultiColumnValues],
|
|
61
62
|
not_in: [Schema.stringMultiColumnValues],
|
|
63
|
+
is_empty: [],
|
|
62
64
|
is_null: [],
|
|
63
65
|
is_not_null: [],
|
|
64
66
|
};
|