@marimo-team/islands 0.23.2-dev2 → 0.23.2-dev20
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/_basePickBy-C-mod5Dp.js +34 -0
- package/dist/{_baseUniq-C87CckHL.js → _baseUniq-Be_p_Ty6.js} +2 -2
- package/dist/{architecture-7HQA4BMR-BHdkAMvZ.js → architecture-7HQA4BMR-kNyKQXbB.js} +2 -2
- package/dist/{architectureDiagram-VXUJARFQ-B3YQo9At.js → architectureDiagram-VXUJARFQ-Dx_Dniiw.js} +11 -11
- package/dist/{blockDiagram-VD42YOAC-CpQ3TKEN.js → blockDiagram-VD42YOAC-D3hGPvEt.js} +4 -4
- package/dist/{c4Diagram-YG6GDRKO-CZSU4uqU.js → c4Diagram-YG6GDRKO-CtY1WMbV.js} +1 -1
- package/dist/{chat-ui-CNHw9Osh.js → chat-ui-DIVMRPO5.js} +2 -2
- package/dist/{chunk-4F5CHEZ2-D5mClyDv.js → chunk-4F5CHEZ2-oWcaQSBe.js} +1 -1
- package/dist/{chunk-B2363JML-Br0eA2T3.js → chunk-B2363JML-72CRxZbk.js} +1 -1
- package/dist/{chunk-B4BG7PRW-4BjV11Br.js → chunk-B4BG7PRW-ChYfc4rf.js} +2 -2
- package/dist/{chunk-DI55MBZ5-DITY3EyP.js → chunk-DI55MBZ5-CYNE3N2j.js} +2 -2
- package/dist/{chunk-FRFDVMJY-DnEvEFRR.js → chunk-FRFDVMJY-Dgl-7l0K.js} +1 -1
- package/dist/{chunk-JA3XYJ7Z-BcPEfxk_.js → chunk-JA3XYJ7Z-B2BoMdpr.js} +1 -1
- package/dist/{chunk-JZLCHNYA-2bnLL3xL.js → chunk-JZLCHNYA-CkHD9mQU.js} +2 -2
- package/dist/{chunk-N4CR4FBY-CpZSuGSU.js → chunk-N4CR4FBY-DDeXUk3y.js} +4 -4
- package/dist/{chunk-PL6DKKU2-DnId6G-x.js → chunk-PL6DKKU2-CpBHhdj8.js} +1 -1
- package/dist/{chunk-QXUST7PY-Ch6F5Obl.js → chunk-QXUST7PY-BnSZbSK7.js} +3 -3
- package/dist/{chunk-S3R3BYOJ-B0UOFJwq.js → chunk-S3R3BYOJ-DVdRer7T.js} +1 -1
- package/dist/{chunk-SJTYNZTY-BsBZnJUj.js → chunk-SJTYNZTY-DPOwAZc-.js} +1 -1
- package/dist/{chunk-TCCFYFTB-Clbl-fTg.js → chunk-TCCFYFTB-BdE6BTq1.js} +6 -6
- package/dist/{chunk-TQ3KTPDO-CFkSQ30e.js → chunk-TQ3KTPDO-BCXCq8f2.js} +1 -1
- package/dist/{chunk-UMXZTB3W-D-A834Bq.js → chunk-UMXZTB3W-C5Hu2atA.js} +1 -1
- package/dist/{classDiagram-v2-WZHVMYZB-DrmbGANl.js → classDiagram-2ON5EDUG-sUXB0Obe.js} +6 -6
- package/dist/{classDiagram-2ON5EDUG-C8-zE3Zv.js → classDiagram-v2-WZHVMYZB-JeF9-idj.js} +6 -6
- package/dist/{clone-DZFQCtFJ.js → clone-B48LSK6I.js} +1 -1
- package/dist/{dagre-6UL2VRFP-OMItEBnY.js → dagre-6UL2VRFP-Bs_DhCUk.js} +9 -9
- package/dist/{dagre-QVd-lCXU.js → dagre-BLW2E2fh.js} +19 -8
- package/dist/{diagram-PSM6KHXK-CkKbohWI.js → diagram-PSM6KHXK-VB3japmQ.js} +10 -10
- package/dist/{diagram-QEK2KX5R-DjUMpVcx.js → diagram-QEK2KX5R-B8nm2JL9.js} +10 -10
- package/dist/{diagram-S2PKOQOG-b-c0d-wZ.js → diagram-S2PKOQOG-D6PR_2iv.js} +10 -10
- package/dist/{erDiagram-Q2GNP2WA-CDhLaOZ1.js → erDiagram-Q2GNP2WA-gjAse7Jb.js} +5 -5
- package/dist/{flowDiagram-NV44I4VS-BDi4O4CL.js → flowDiagram-NV44I4VS-CQTSZWcI.js} +5 -5
- package/dist/{ganttDiagram-JELNMOA3-BpZE6kVp.js → ganttDiagram-JELNMOA3-aktqk_om.js} +1 -1
- package/dist/{gitGraph-G5XIXVHT-B_c6xFJv.js → gitGraph-G5XIXVHT-Cy06nzLg.js} +2 -2
- package/dist/{gitGraphDiagram-V2S2FVAM-iQnXzbPM.js → gitGraphDiagram-V2S2FVAM-C1ntKO33.js} +10 -10
- package/dist/{graphlib-BV1_gi0C.js → graphlib-Cr691-na.js} +3 -3
- package/dist/{hasIn-DnfJcYpY.js → hasIn-BDDmuo1w.js} +1 -1
- package/dist/{info-VBDWY6EO-BTyzxmhr.js → info-VBDWY6EO-BIO6A8nW.js} +2 -2
- package/dist/{infoDiagram-HS3SLOUP-OYrX6uO3.js → infoDiagram-HS3SLOUP-CtfUf0g_.js} +9 -9
- package/dist/{kanban-definition-3W4ZIXB7-DHEAKdZt.js → kanban-definition-3W4ZIXB7-C5FK4v7x.js} +3 -3
- package/dist/main.js +264 -130
- package/dist/{mermaid-BbhZNQeB.js → mermaid-CcM8GHeT.js} +29 -29
- package/dist/{mermaid-parser.core-ntCgyx0x.js → mermaid-parser.core-fZdPSYor.js} +8 -8
- package/dist/min-DAIOAwWK.js +102 -0
- package/dist/{mindmap-definition-VGOIOE7T-CxEUZZvY.js → mindmap-definition-VGOIOE7T-BvrQf8XZ.js} +5 -5
- package/dist/{packet-DYOGHKS2-BhvnpoGi.js → packet-DYOGHKS2-DDx1z7B-.js} +2 -2
- package/dist/pick-DfX21dj2.js +18 -0
- package/dist/{pie-VRWISCQL-dILuA3iG.js → pie-VRWISCQL-BgRtyDMT.js} +2 -2
- package/dist/{pieDiagram-ADFJNKIX-U3LrUqAS.js → pieDiagram-ADFJNKIX-DAhjFwJD.js} +10 -10
- package/dist/{process-output-Bekznt_B.js → process-output-H_7QTreh.js} +2133 -2119
- package/dist/{radar-ZZBFDIW7-DwFrOJDj.js → radar-ZZBFDIW7-xwh47Yzn.js} +2 -2
- package/dist/{requirementDiagram-UZGBJVZJ-D0zpQnKC.js → requirementDiagram-UZGBJVZJ-B3nnp0VG.js} +5 -5
- package/dist/{sequenceDiagram-WL72ISMW-D1BJxLjH.js → sequenceDiagram-WL72ISMW-D2mpRRG2.js} +1 -1
- package/dist/{stateDiagram-FKZM4ZOC-B1S8jGMn.js → stateDiagram-FKZM4ZOC-QD9Wuca0.js} +8 -8
- package/dist/{stateDiagram-v2-4FDKWEC3-BH5ozUbc.js → stateDiagram-v2-4FDKWEC3-DnUhJ525.js} +6 -6
- package/dist/{treemap-GDKQZRPO-bx2ngsgN.js → treemap-GDKQZRPO-5ZsmvXgc.js} +2 -2
- package/dist/{xychartDiagram-PRI3JC2R-CuAZiqHS.js → xychartDiagram-PRI3JC2R-BMsB7VdF.js} +2 -2
- package/package.json +2 -2
- package/src/components/data-table/TableBottomBar.tsx +5 -1
- package/src/components/data-table/__tests__/filters.test.ts +304 -0
- package/src/components/data-table/__tests__/pagination.test.tsx +46 -132
- package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
- package/src/components/data-table/filters.ts +87 -33
- package/src/components/data-table/pagination.tsx +189 -76
- package/src/components/data-table/types.ts +0 -4
- package/src/components/editor/Output.tsx +1 -1
- package/src/components/editor/cell/code/cell-editor.tsx +1 -0
- 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/core/codemirror/language/languages/python.ts +9 -9
- package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +8 -1
- package/src/core/codemirror/lsp/federated-lsp.ts +2 -2
- package/src/core/codemirror/lsp/notebook-lsp.ts +2 -2
- package/src/core/codemirror/lsp/utils.ts +21 -6
- 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/_basePickBy-Sow3pJjS.js +0 -41
- package/dist/min-Ds3gG0Ff.js +0 -96
- package/dist/range-fJeId9Ri.js +0 -30
- /package/dist/{isEmpty-B7FX9wKt.js → isEmpty-D3lf6gH3.js} +0 -0
- /package/dist/{memoize-CSTI9eOX.js → memoize-DEvRzlwP.js} +0 -0
|
@@ -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
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use no memo";
|
|
3
3
|
|
|
4
4
|
import type { Table } from "@tanstack/react-table";
|
|
5
|
-
import {
|
|
5
|
+
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
6
6
|
import {
|
|
7
7
|
ChevronDown,
|
|
8
8
|
ChevronLeft,
|
|
@@ -37,9 +37,6 @@ import { cn } from "@/utils/cn";
|
|
|
37
37
|
import { Events } from "@/utils/events";
|
|
38
38
|
import { prettyNumber } from "@/utils/numbers";
|
|
39
39
|
import { PluralWord } from "@/utils/pluralize";
|
|
40
|
-
import type { PageRange } from "./types";
|
|
41
|
-
|
|
42
|
-
const MAX_PAGES_BEFORE_CLAMPING = 100;
|
|
43
40
|
|
|
44
41
|
interface DataTablePaginationProps<TData> {
|
|
45
42
|
table: Table<TData>;
|
|
@@ -189,6 +186,81 @@ export const DataTablePagination = <TData,>({
|
|
|
189
186
|
);
|
|
190
187
|
};
|
|
191
188
|
|
|
189
|
+
const PAGE_ITEM_HEIGHT = 32;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Compute contiguous ranges of page numbers whose string starts with `prefix`,
|
|
193
|
+
* without scanning every page. O(log10(totalPages)).
|
|
194
|
+
*
|
|
195
|
+
* For prefix "5", totalPages=500: [[5,5], [50,59], [500,500]]
|
|
196
|
+
*/
|
|
197
|
+
export function matchingPageRanges(
|
|
198
|
+
prefix: string,
|
|
199
|
+
totalPages: number,
|
|
200
|
+
): [number, number][] {
|
|
201
|
+
const n = Number.parseInt(prefix, 10);
|
|
202
|
+
if (Number.isNaN(n) || n <= 0 || String(n) !== prefix) {
|
|
203
|
+
return [];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const ranges: [number, number][] = [];
|
|
207
|
+
let power = 1;
|
|
208
|
+
while (n * power <= totalPages) {
|
|
209
|
+
const start = n * power;
|
|
210
|
+
const end = Math.min((n + 1) * power - 1, totalPages);
|
|
211
|
+
ranges.push([start, end]);
|
|
212
|
+
power *= 10;
|
|
213
|
+
}
|
|
214
|
+
return ranges;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
interface PageMapping {
|
|
218
|
+
count: number;
|
|
219
|
+
pageAtIndex: (index: number) => number;
|
|
220
|
+
indexOfPage: (page: number) => number;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function createPageMapping(search: string, totalPages: number): PageMapping {
|
|
224
|
+
if (search === "") {
|
|
225
|
+
return {
|
|
226
|
+
count: totalPages,
|
|
227
|
+
pageAtIndex: (i) => i + 1,
|
|
228
|
+
indexOfPage: (p) => p - 1,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const ranges = matchingPageRanges(search, totalPages);
|
|
233
|
+
let count = 0;
|
|
234
|
+
for (const [s, e] of ranges) {
|
|
235
|
+
count += e - s + 1;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
count,
|
|
240
|
+
pageAtIndex: (i) => {
|
|
241
|
+
let offset = 0;
|
|
242
|
+
for (const [start, end] of ranges) {
|
|
243
|
+
const size = end - start + 1;
|
|
244
|
+
if (i < offset + size) {
|
|
245
|
+
return start + (i - offset);
|
|
246
|
+
}
|
|
247
|
+
offset += size;
|
|
248
|
+
}
|
|
249
|
+
return -1;
|
|
250
|
+
},
|
|
251
|
+
indexOfPage: (p) => {
|
|
252
|
+
let offset = 0;
|
|
253
|
+
for (const [start, end] of ranges) {
|
|
254
|
+
if (p >= start && p <= end) {
|
|
255
|
+
return offset + (p - start);
|
|
256
|
+
}
|
|
257
|
+
offset += end - start + 1;
|
|
258
|
+
}
|
|
259
|
+
return -1;
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
192
264
|
export const PageSelector = ({
|
|
193
265
|
currentPage,
|
|
194
266
|
totalPages,
|
|
@@ -199,19 +271,31 @@ export const PageSelector = ({
|
|
|
199
271
|
onPageChange: (page: number) => void;
|
|
200
272
|
}) => {
|
|
201
273
|
const [open, setOpen] = React.useState(false);
|
|
274
|
+
const [search, setSearch] = React.useState("");
|
|
202
275
|
|
|
203
|
-
const
|
|
204
|
-
() =>
|
|
205
|
-
[
|
|
276
|
+
const mapping = React.useMemo(
|
|
277
|
+
() => createPageMapping(search, totalPages),
|
|
278
|
+
[search, totalPages],
|
|
206
279
|
);
|
|
207
280
|
|
|
208
281
|
const handleSelect = (page: number) => {
|
|
209
282
|
onPageChange(page - 1);
|
|
283
|
+
setSearch("");
|
|
210
284
|
setOpen(false);
|
|
211
285
|
};
|
|
212
286
|
|
|
287
|
+
const listHeight = Math.min(mapping.count * PAGE_ITEM_HEIGHT, 240);
|
|
288
|
+
|
|
213
289
|
return (
|
|
214
|
-
<Popover
|
|
290
|
+
<Popover
|
|
291
|
+
open={totalPages > 1 ? open : false}
|
|
292
|
+
onOpenChange={(next) => {
|
|
293
|
+
setOpen(next);
|
|
294
|
+
if (!next) {
|
|
295
|
+
setSearch("");
|
|
296
|
+
}
|
|
297
|
+
}}
|
|
298
|
+
>
|
|
215
299
|
<PopoverTrigger asChild={true} disabled={totalPages <= 1}>
|
|
216
300
|
<button
|
|
217
301
|
type="button"
|
|
@@ -229,18 +313,15 @@ export const PageSelector = ({
|
|
|
229
313
|
</button>
|
|
230
314
|
</PopoverTrigger>
|
|
231
315
|
<PopoverContent className="w-36 p-0" align="center" sideOffset={6}>
|
|
232
|
-
<Command
|
|
233
|
-
shouldFilter={true}
|
|
234
|
-
filter={(value, search) => {
|
|
235
|
-
return value.startsWith(search) ? 1 : 0;
|
|
236
|
-
}}
|
|
237
|
-
>
|
|
316
|
+
<Command shouldFilter={false} value={String(currentPage)}>
|
|
238
317
|
<CommandInput
|
|
239
318
|
placeholder={`Page (1–${totalPages})`}
|
|
240
319
|
rootClassName="px-2 h-8"
|
|
241
320
|
className="text-xs h-8"
|
|
242
321
|
autoFocus={true}
|
|
243
322
|
icon={null}
|
|
323
|
+
value={search}
|
|
324
|
+
onValueChange={setSearch}
|
|
244
325
|
onKeyDown={(e) => {
|
|
245
326
|
// Allow navigation/editing keys, block non-numeric input
|
|
246
327
|
const allowed = [
|
|
@@ -248,6 +329,8 @@ export const PageSelector = ({
|
|
|
248
329
|
"Delete",
|
|
249
330
|
"ArrowLeft",
|
|
250
331
|
"ArrowRight",
|
|
332
|
+
"ArrowUp",
|
|
333
|
+
"ArrowDown",
|
|
251
334
|
"Tab",
|
|
252
335
|
"Enter",
|
|
253
336
|
"Escape",
|
|
@@ -257,27 +340,19 @@ export const PageSelector = ({
|
|
|
257
340
|
}
|
|
258
341
|
}}
|
|
259
342
|
/>
|
|
260
|
-
<CommandList className="max-h-60">
|
|
261
|
-
{
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
onMouseDown={Events.preventFocus}
|
|
273
|
-
>
|
|
274
|
-
{item.page}
|
|
275
|
-
</CommandItem>
|
|
276
|
-
),
|
|
343
|
+
<CommandList className="max-h-60 overflow-hidden">
|
|
344
|
+
{mapping.count === 0 ? (
|
|
345
|
+
<CommandEmpty className="py-2 text-center text-xs text-muted-foreground">
|
|
346
|
+
No matching page
|
|
347
|
+
</CommandEmpty>
|
|
348
|
+
) : (
|
|
349
|
+
<VirtualizedPageList
|
|
350
|
+
mapping={mapping}
|
|
351
|
+
currentPage={currentPage}
|
|
352
|
+
listHeight={listHeight}
|
|
353
|
+
onSelect={handleSelect}
|
|
354
|
+
/>
|
|
277
355
|
)}
|
|
278
|
-
<CommandEmpty className="py-2 text-center text-xs text-muted-foreground">
|
|
279
|
-
No matching page
|
|
280
|
-
</CommandEmpty>
|
|
281
356
|
</CommandList>
|
|
282
357
|
</Command>
|
|
283
358
|
</PopoverContent>
|
|
@@ -285,57 +360,95 @@ export const PageSelector = ({
|
|
|
285
360
|
);
|
|
286
361
|
};
|
|
287
362
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const
|
|
300
|
-
for (let i = 0; i < count; i++) {
|
|
301
|
-
items.push({ type: "page", page: start + i });
|
|
302
|
-
}
|
|
303
|
-
};
|
|
304
|
-
|
|
305
|
-
addPages(1, 10);
|
|
306
|
-
items.push({ type: "ellipsis", key: "e1" });
|
|
307
|
-
|
|
308
|
-
if (currentPage > 10 && currentPage <= middle - 5) {
|
|
309
|
-
items.push(
|
|
310
|
-
{ type: "page", page: currentPage },
|
|
311
|
-
{ type: "ellipsis", key: "e1b" },
|
|
312
|
-
);
|
|
313
|
-
}
|
|
363
|
+
const VirtualizedPageList = ({
|
|
364
|
+
mapping,
|
|
365
|
+
currentPage,
|
|
366
|
+
listHeight,
|
|
367
|
+
onSelect,
|
|
368
|
+
}: {
|
|
369
|
+
mapping: PageMapping;
|
|
370
|
+
currentPage: number;
|
|
371
|
+
listHeight: number;
|
|
372
|
+
onSelect: (page: number) => void;
|
|
373
|
+
}) => {
|
|
374
|
+
const parentRef = React.useRef<HTMLDivElement>(null);
|
|
314
375
|
|
|
315
|
-
|
|
316
|
-
items.push({ type: "ellipsis", key: "e2" });
|
|
376
|
+
const currentIndex = mapping.indexOfPage(currentPage);
|
|
317
377
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
378
|
+
const virtualizer = useVirtualizer({
|
|
379
|
+
count: mapping.count,
|
|
380
|
+
getScrollElement: () => parentRef.current,
|
|
381
|
+
estimateSize: () => PAGE_ITEM_HEIGHT,
|
|
382
|
+
overscan: 10,
|
|
383
|
+
initialOffset:
|
|
384
|
+
currentIndex > 0
|
|
385
|
+
? Math.max(0, currentIndex * PAGE_ITEM_HEIGHT - listHeight / 2)
|
|
386
|
+
: 0,
|
|
387
|
+
});
|
|
324
388
|
|
|
325
|
-
|
|
389
|
+
// Scroll to top when filtered results change (user is searching)
|
|
390
|
+
const prevCount = React.useRef(mapping.count);
|
|
391
|
+
React.useEffect(() => {
|
|
392
|
+
if (mapping.count !== prevCount.current) {
|
|
393
|
+
virtualizer.scrollToIndex(0);
|
|
394
|
+
prevCount.current = mapping.count;
|
|
395
|
+
}
|
|
396
|
+
}, [mapping.count, virtualizer]);
|
|
326
397
|
|
|
327
|
-
return
|
|
328
|
-
}
|
|
398
|
+
return (
|
|
399
|
+
<div ref={parentRef} style={{ height: listHeight, overflow: "auto" }}>
|
|
400
|
+
<div
|
|
401
|
+
style={{
|
|
402
|
+
height: virtualizer.getTotalSize(),
|
|
403
|
+
width: "100%",
|
|
404
|
+
position: "relative",
|
|
405
|
+
}}
|
|
406
|
+
>
|
|
407
|
+
{virtualizer.getVirtualItems().map((virtualItem) => {
|
|
408
|
+
const page = mapping.pageAtIndex(virtualItem.index);
|
|
409
|
+
return (
|
|
410
|
+
<CommandItem
|
|
411
|
+
key={page}
|
|
412
|
+
value={String(page)}
|
|
413
|
+
data-testid="page-option"
|
|
414
|
+
aria-selected={page === currentPage}
|
|
415
|
+
className={cn(
|
|
416
|
+
"text-xs cursor-pointer",
|
|
417
|
+
page === currentPage && "font-semibold bg-accent",
|
|
418
|
+
)}
|
|
419
|
+
style={{
|
|
420
|
+
position: "absolute",
|
|
421
|
+
top: 0,
|
|
422
|
+
left: 0,
|
|
423
|
+
width: "100%",
|
|
424
|
+
height: virtualItem.size,
|
|
425
|
+
transform: `translateY(${virtualItem.start}px)`,
|
|
426
|
+
}}
|
|
427
|
+
onSelect={() => onSelect(page)}
|
|
428
|
+
onMouseDown={Events.preventFocus}
|
|
429
|
+
>
|
|
430
|
+
{page}
|
|
431
|
+
</CommandItem>
|
|
432
|
+
);
|
|
433
|
+
})}
|
|
434
|
+
</div>
|
|
435
|
+
</div>
|
|
436
|
+
);
|
|
437
|
+
};
|
|
329
438
|
|
|
330
439
|
export function prettifyRowCount(rowCount: number, locale: string): string {
|
|
331
440
|
return `${prettyNumber(rowCount, locale)} ${new PluralWord("row").pluralize(rowCount)}`;
|
|
332
441
|
}
|
|
333
442
|
|
|
334
|
-
export const prettifyRowColumnCount = (
|
|
335
|
-
numRows
|
|
336
|
-
totalColumns
|
|
337
|
-
locale
|
|
338
|
-
|
|
443
|
+
export const prettifyRowColumnCount = ({
|
|
444
|
+
numRows,
|
|
445
|
+
totalColumns,
|
|
446
|
+
locale,
|
|
447
|
+
}: {
|
|
448
|
+
numRows: number | "too_many";
|
|
449
|
+
totalColumns: number;
|
|
450
|
+
locale: string;
|
|
451
|
+
}): string => {
|
|
339
452
|
const rowsLabel =
|
|
340
453
|
numRows === "too_many" ? "Unknown" : prettifyRowCount(numRows, locale);
|
|
341
454
|
const columnsLabel = `${prettyNumber(totalColumns, locale)} ${new PluralWord("column").pluralize(totalColumns)}`;
|
|
@@ -105,7 +105,3 @@ export function extractTimezone(dtype: string | undefined): string | undefined {
|
|
|
105
105
|
const match = /^datetime(?:64)?\[[^,]+,([^,]+)]$/.exec(dtype);
|
|
106
106
|
return match?.[1]?.trim();
|
|
107
107
|
}
|
|
108
|
-
|
|
109
|
-
export type PageRange =
|
|
110
|
-
| { type: "page"; page: number }
|
|
111
|
-
| { type: "ellipsis"; key: string };
|
|
@@ -263,7 +263,7 @@ const MimeBundleOutputRenderer: React.FC<{
|
|
|
263
263
|
const { mode } = useAtomValue(viewStateAtom);
|
|
264
264
|
const appView = mode === "present" || mode === "read";
|
|
265
265
|
|
|
266
|
-
// Extract metadata if present (e.g.,
|
|
266
|
+
// Extract metadata if present (e.g., to maintain a constant display size regardless of DPI/PPI)
|
|
267
267
|
const metadata = mimebundle[METADATA_KEY];
|
|
268
268
|
|
|
269
269
|
// Filter out metadata from the mime entries and type narrow
|
|
@@ -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);
|