@lapis-notes/bases 0.1.0
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/CHANGELOG.md +6 -0
- package/LICENSE.md +8 -0
- package/README.md +20 -0
- package/dist/bases-plugin.d.ts +10 -0
- package/dist/bases-plugin.js +118 -0
- package/dist/bases-view/app-database-query-source.d.ts +30 -0
- package/dist/bases-view/app-database-query-source.js +248 -0
- package/dist/bases-view/bases.svelte.d.ts +569 -0
- package/dist/bases-view/bases.svelte.js +910 -0
- package/dist/bases-view/card-view/components/card-placeholder.svelte +68 -0
- package/dist/bases-view/card-view/components/card-placeholder.svelte.d.ts +7 -0
- package/dist/bases-view/card-view/components/card.svelte +289 -0
- package/dist/bases-view/card-view/components/card.svelte.d.ts +7 -0
- package/dist/bases-view/card-view/index.d.ts +11 -0
- package/dist/bases-view/card-view/index.js +55 -0
- package/dist/bases-view/cell-editor-options.d.ts +2 -0
- package/dist/bases-view/cell-editor-options.js +26 -0
- package/dist/bases-view/cell-editor.svelte +206 -0
- package/dist/bases-view/cell-editor.svelte.d.ts +12 -0
- package/dist/bases-view/cell.svelte +132 -0
- package/dist/bases-view/cell.svelte.d.ts +16 -0
- package/dist/bases-view/column-definition.d.ts +22 -0
- package/dist/bases-view/column-definition.js +149 -0
- package/dist/bases-view/columns.d.ts +15 -0
- package/dist/bases-view/columns.js +103 -0
- package/dist/bases-view/components/Tag.svelte +14 -0
- package/dist/bases-view/components/Tag.svelte.d.ts +8 -0
- package/dist/bases-view/components/autocomplete.svelte +45 -0
- package/dist/bases-view/components/autocomplete.svelte.d.ts +17 -0
- package/dist/bases-view/components/card-image.svelte +121 -0
- package/dist/bases-view/components/card-image.svelte.d.ts +11 -0
- package/dist/bases-view/components/column-visibility.svelte +388 -0
- package/dist/bases-view/components/column-visibility.svelte.d.ts +13 -0
- package/dist/bases-view/components/datetime.svelte +35 -0
- package/dist/bases-view/components/datetime.svelte.d.ts +7 -0
- package/dist/bases-view/components/file.svelte +31 -0
- package/dist/bases-view/components/file.svelte.d.ts +8 -0
- package/dist/bases-view/components/filters/filter-editor.svelte +102 -0
- package/dist/bases-view/components/filters/filter-editor.svelte.d.ts +10 -0
- package/dist/bases-view/components/filters/filter-group.svelte +142 -0
- package/dist/bases-view/components/filters/filter-group.svelte.d.ts +11 -0
- package/dist/bases-view/components/filters/filter-properties.svelte +77 -0
- package/dist/bases-view/components/filters/filter-properties.svelte.d.ts +8 -0
- package/dist/bases-view/components/filters/filter-root.svelte +112 -0
- package/dist/bases-view/components/filters/filter-root.svelte.d.ts +12 -0
- package/dist/bases-view/components/filters/filter-row.svelte +351 -0
- package/dist/bases-view/components/filters/filter-row.svelte.d.ts +11 -0
- package/dist/bases-view/components/group/group-root.svelte +120 -0
- package/dist/bases-view/components/group/group-root.svelte.d.ts +8 -0
- package/dist/bases-view/components/limit.svelte +100 -0
- package/dist/bases-view/components/limit.svelte.d.ts +12 -0
- package/dist/bases-view/components/query-editor.svelte +230 -0
- package/dist/bases-view/components/query-editor.svelte.d.ts +15 -0
- package/dist/bases-view/components/sort/sort-direction-labels.d.ts +9 -0
- package/dist/bases-view/components/sort/sort-direction-labels.js +25 -0
- package/dist/bases-view/components/sort/sort-root.svelte +239 -0
- package/dist/bases-view/components/sort/sort-root.svelte.d.ts +11 -0
- package/dist/bases-view/components/sort/sort-row.svelte +190 -0
- package/dist/bases-view/components/sort/sort-row.svelte.d.ts +15 -0
- package/dist/bases-view/components/summary-strip.svelte +56 -0
- package/dist/bases-view/components/summary-strip.svelte.d.ts +8 -0
- package/dist/bases-view/components/tag-chip.svelte +47 -0
- package/dist/bases-view/components/tag-chip.svelte.d.ts +12 -0
- package/dist/bases-view/components/views.svelte +178 -0
- package/dist/bases-view/components/views.svelte.d.ts +12 -0
- package/dist/bases-view/data-table-adapter/data-table.svelte.d.ts +45 -0
- package/dist/bases-view/data-table-adapter/data-table.svelte.js +112 -0
- package/dist/bases-view/data-table-adapter/flex-render.svelte +43 -0
- package/dist/bases-view/data-table-adapter/flex-render.svelte.d.ts +30 -0
- package/dist/bases-view/data-table-adapter/index.d.ts +3 -0
- package/dist/bases-view/data-table-adapter/index.js +3 -0
- package/dist/bases-view/data-table-adapter/render-helpers.d.ts +101 -0
- package/dist/bases-view/data-table-adapter/render-helpers.js +110 -0
- package/dist/bases-view/db.d.ts +31 -0
- package/dist/bases-view/db.js +174 -0
- package/dist/bases-view/document-core.d.ts +2 -0
- package/dist/bases-view/document-core.js +166 -0
- package/dist/bases-view/export-core.d.ts +4 -0
- package/dist/bases-view/export-core.js +31 -0
- package/dist/bases-view/file-fields-core.d.ts +31 -0
- package/dist/bases-view/file-fields-core.js +114 -0
- package/dist/bases-view/filter-parser.d.ts +27 -0
- package/dist/bases-view/filter-parser.js +315 -0
- package/dist/bases-view/functions-core.d.ts +66 -0
- package/dist/bases-view/functions-core.js +265 -0
- package/dist/bases-view/functions.d.ts +5 -0
- package/dist/bases-view/functions.js +107 -0
- package/dist/bases-view/fuzzy-search.d.ts +6 -0
- package/dist/bases-view/fuzzy-search.js +15 -0
- package/dist/bases-view/hooks/useResizeObserver.svelte.d.ts +7 -0
- package/dist/bases-view/hooks/useResizeObserver.svelte.js +26 -0
- package/dist/bases-view/index.d.ts +66 -0
- package/dist/bases-view/index.js +231 -0
- package/dist/bases-view/list-view/index.d.ts +10 -0
- package/dist/bases-view/list-view/index.js +17 -0
- package/dist/bases-view/list-view/list.svelte +156 -0
- package/dist/bases-view/list-view/list.svelte.d.ts +7 -0
- package/dist/bases-view/map-view/index.d.ts +10 -0
- package/dist/bases-view/map-view/index.js +17 -0
- package/dist/bases-view/map-view/view.svelte +28 -0
- package/dist/bases-view/map-view/view.svelte.d.ts +7 -0
- package/dist/bases-view/metadata-invalidation-core.d.ts +34 -0
- package/dist/bases-view/metadata-invalidation-core.js +162 -0
- package/dist/bases-view/metadata-type-info.d.ts +7 -0
- package/dist/bases-view/metadata-type-info.js +10 -0
- package/dist/bases-view/models.d.ts +90 -0
- package/dist/bases-view/models.js +133 -0
- package/dist/bases-view/query-result-core.d.ts +8 -0
- package/dist/bases-view/query-result-core.js +16 -0
- package/dist/bases-view/resource-url.d.ts +2 -0
- package/dist/bases-view/resource-url.js +21 -0
- package/dist/bases-view/search-core.d.ts +9 -0
- package/dist/bases-view/search-core.js +20 -0
- package/dist/bases-view/summary-core.d.ts +7 -0
- package/dist/bases-view/summary-core.js +17 -0
- package/dist/bases-view/table-view/components/div-table.svelte +464 -0
- package/dist/bases-view/table-view/components/div-table.svelte.d.ts +32 -0
- package/dist/bases-view/table-view/components/dnd-style.d.ts +3 -0
- package/dist/bases-view/table-view/components/dnd-style.js +6 -0
- package/dist/bases-view/table-view/components/sort-header.svelte +48 -0
- package/dist/bases-view/table-view/components/sort-header.svelte.d.ts +11 -0
- package/dist/bases-view/table-view/components/table-column-tracks.d.ts +14 -0
- package/dist/bases-view/table-view/components/table-column-tracks.js +27 -0
- package/dist/bases-view/table-view/components/table-header.svelte +146 -0
- package/dist/bases-view/table-view/components/table-header.svelte.d.ts +10 -0
- package/dist/bases-view/table-view/components/table-placeholder.svelte +46 -0
- package/dist/bases-view/table-view/components/table-placeholder.svelte.d.ts +30 -0
- package/dist/bases-view/table-view/components/table-virtualizer-core.d.ts +13 -0
- package/dist/bases-view/table-view/components/table-virtualizer-core.js +12 -0
- package/dist/bases-view/table-view/components/table.svelte +997 -0
- package/dist/bases-view/table-view/components/table.svelte.d.ts +32 -0
- package/dist/bases-view/table-view/index.svelte.d.ts +10 -0
- package/dist/bases-view/table-view/index.svelte.js +30 -0
- package/dist/bases-view/unknown-view/index.d.ts +10 -0
- package/dist/bases-view/unknown-view/index.js +19 -0
- package/dist/bases-view/unknown-view/view.svelte +11 -0
- package/dist/bases-view/unknown-view/view.svelte.d.ts +6 -0
- package/dist/bases-view/view-header.svelte +142 -0
- package/dist/bases-view/view-header.svelte.d.ts +28 -0
- package/dist/bases-view/view-registration-inventory.d.ts +22 -0
- package/dist/bases-view/view-registration-inventory.js +7 -0
- package/dist/bases-view/view-settings.svelte +350 -0
- package/dist/bases-view/view-settings.svelte.d.ts +9 -0
- package/dist/bases-view/view.svelte +192 -0
- package/dist/bases-view/view.svelte.d.ts +15 -0
- package/dist/bases-view-surface.svelte +31 -0
- package/dist/bases-view-surface.svelte.d.ts +14 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +8 -0
- package/dist/serialized-write-queue.d.ts +10 -0
- package/dist/serialized-write-queue.js +36 -0
- package/dist/styles.css +2164 -0
- package/manifest.json +33 -0
- package/package.json +90 -0
- package/styles.css +1 -0
|
@@ -0,0 +1,997 @@
|
|
|
1
|
+
<script lang="ts" generics="TData, TValue">
|
|
2
|
+
import {
|
|
3
|
+
getCoreRowModel,
|
|
4
|
+
getSortedRowModel,
|
|
5
|
+
type ColumnDef,
|
|
6
|
+
type ColumnOrderState,
|
|
7
|
+
type ColumnSizingInfoState,
|
|
8
|
+
type ColumnSizingState,
|
|
9
|
+
type Header,
|
|
10
|
+
type VisibilityState,
|
|
11
|
+
} from "@tanstack/table-core";
|
|
12
|
+
import TableHeader from "./table-header.svelte";
|
|
13
|
+
import { DragDropProvider } from "@dnd-kit/svelte";
|
|
14
|
+
import type { DragEndEvent } from "@dnd-kit/dom";
|
|
15
|
+
import { arrayMove } from "@dnd-kit/helpers";
|
|
16
|
+
import { createVirtualizer, type MetadataType } from "@lapis-notes/api";
|
|
17
|
+
import Cell from "../../cell.svelte";
|
|
18
|
+
import {
|
|
19
|
+
BasesEntry,
|
|
20
|
+
type BasesPropertyId,
|
|
21
|
+
type BasesView,
|
|
22
|
+
type QueryController,
|
|
23
|
+
} from "../../bases.svelte";
|
|
24
|
+
import {
|
|
25
|
+
createSvelteTable,
|
|
26
|
+
renderComponent,
|
|
27
|
+
} from "../../data-table-adapter";
|
|
28
|
+
import ChevronDown from "@lucide/svelte/icons/chevron-down";
|
|
29
|
+
import ChevronRight from "@lucide/svelte/icons/chevron-right";
|
|
30
|
+
import type { SortColumn } from "../../models";
|
|
31
|
+
import { getMetadataTypeInfo } from "../../metadata-type-info";
|
|
32
|
+
import {
|
|
33
|
+
formatSummaryLabel,
|
|
34
|
+
resolveConfiguredSummaries,
|
|
35
|
+
} from "../../summary-core";
|
|
36
|
+
import SortHeader from "./sort-header.svelte";
|
|
37
|
+
import { resolveVirtualTotalSize } from "./table-virtualizer-core";
|
|
38
|
+
import * as ScrollArea from "@lapismd/design-core/shadcn/scroll-area";
|
|
39
|
+
import { onDestroy, onMount, untrack } from "svelte";
|
|
40
|
+
import TablePlaceholder from "./table-placeholder.svelte";
|
|
41
|
+
import { useResizeObserver } from "../../hooks/useResizeObserver.svelte";
|
|
42
|
+
import { resolveTableColumnTracks } from "./table-column-tracks";
|
|
43
|
+
|
|
44
|
+
type DataTableProps = {
|
|
45
|
+
view: BasesView;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
type GroupConfig = {
|
|
49
|
+
property: BasesPropertyId;
|
|
50
|
+
direction: "ASC" | "DESC";
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
type TableDisplayItem =
|
|
54
|
+
| {
|
|
55
|
+
kind: "group";
|
|
56
|
+
key: string;
|
|
57
|
+
label: string;
|
|
58
|
+
value: string;
|
|
59
|
+
collapsed: boolean;
|
|
60
|
+
}
|
|
61
|
+
| {
|
|
62
|
+
kind: "row";
|
|
63
|
+
key: string;
|
|
64
|
+
row: BasesEntry;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
type GroupSection = {
|
|
68
|
+
key: string;
|
|
69
|
+
label: string;
|
|
70
|
+
value: string;
|
|
71
|
+
collapsed: boolean;
|
|
72
|
+
start: number;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
let { view }: DataTableProps = $props();
|
|
76
|
+
|
|
77
|
+
let queryResults = $derived(view.data);
|
|
78
|
+
|
|
79
|
+
let columnSizingInfo: ColumnSizingInfoState = $state({
|
|
80
|
+
columnSizingStart: [],
|
|
81
|
+
deltaOffset: null,
|
|
82
|
+
deltaPercentage: null,
|
|
83
|
+
isResizingColumn: false,
|
|
84
|
+
startOffset: null,
|
|
85
|
+
startSize: null,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
let types = $derived.by(() => {
|
|
89
|
+
return queryResults.properties
|
|
90
|
+
.filter((it) => it.startsWith("note."))
|
|
91
|
+
.reduce<Record<string, { type: MetadataType; icon: string }>>(
|
|
92
|
+
(acc, id) => {
|
|
93
|
+
const key = id.substring(5);
|
|
94
|
+
const { type, icon } = getMetadataTypeInfo(
|
|
95
|
+
key,
|
|
96
|
+
"text",
|
|
97
|
+
view.controller.app,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
acc[id] = { type, icon };
|
|
101
|
+
return acc;
|
|
102
|
+
},
|
|
103
|
+
{},
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const FILE_ICONS: Record<string, string> = {
|
|
108
|
+
file: "lucide-file",
|
|
109
|
+
"file.name": "lucide-text",
|
|
110
|
+
"file.basename": "lucide-text",
|
|
111
|
+
"file.fullname": "lucide-text",
|
|
112
|
+
"file.folder": "lucide-text",
|
|
113
|
+
"file.path": "lucide-text",
|
|
114
|
+
"file.ext": "lucide-text",
|
|
115
|
+
"file.ctime": "lucide-clock",
|
|
116
|
+
"file.mtime": "lucide-clock",
|
|
117
|
+
"file.size": "lucide-binary",
|
|
118
|
+
"file.tags": "lucide-tags",
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
function getIcon(id: BasesPropertyId) {
|
|
122
|
+
if (id.startsWith("formula.")) {
|
|
123
|
+
return "lucide-square-function";
|
|
124
|
+
} else if (id.startsWith("file.")) {
|
|
125
|
+
return FILE_ICONS[id] || "lucide-file";
|
|
126
|
+
}
|
|
127
|
+
return types[id]?.icon || "lucide-file";
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const MIN_COLUMN_WIDTH = 120;
|
|
131
|
+
const DEFAULT_COLUMN_WIDTH = 300;
|
|
132
|
+
const HEADER_PADDING_WIDTH = 96;
|
|
133
|
+
const CELL_PADDING_WIDTH = 24;
|
|
134
|
+
const GROUP_HEADER_HEIGHT = 44;
|
|
135
|
+
const ACTIVE_SEARCH_HEIGHT = 36;
|
|
136
|
+
|
|
137
|
+
export function estimateTextWidth(
|
|
138
|
+
text: string,
|
|
139
|
+
font: string = '20px ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
|
|
140
|
+
): number {
|
|
141
|
+
if (!text) return 0;
|
|
142
|
+
// Reuse a single offscreen canvas for performance
|
|
143
|
+
const canvas: HTMLCanvasElement =
|
|
144
|
+
(estimateTextWidth as any)._canvas ||
|
|
145
|
+
((estimateTextWidth as any)._canvas = document.createElement("canvas"));
|
|
146
|
+
const context = canvas.getContext("2d");
|
|
147
|
+
if (!context) throw new Error("2D context not available on this browser.");
|
|
148
|
+
|
|
149
|
+
context.font = font;
|
|
150
|
+
const metrics = context.measureText(text);
|
|
151
|
+
return metrics.width;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let columnSizing = $derived(
|
|
155
|
+
(view.config.get("columnSize") || {}) as ColumnSizingState,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
let intrinsicWidths: Record<string, number> = $derived.by(() => {
|
|
159
|
+
const sizes: Record<string, number> = {};
|
|
160
|
+
const order = view.config.getOrder();
|
|
161
|
+
|
|
162
|
+
for (const id of order) {
|
|
163
|
+
const label = view.config.getDisplayName(id as BasesPropertyId) || id;
|
|
164
|
+
sizes[id] = Math.min(
|
|
165
|
+
DEFAULT_COLUMN_WIDTH,
|
|
166
|
+
Math.max(
|
|
167
|
+
MIN_COLUMN_WIDTH,
|
|
168
|
+
Math.round(estimateTextWidth(label) + HEADER_PADDING_WIDTH),
|
|
169
|
+
),
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
for (const row of queryResults.data) {
|
|
174
|
+
for (const id of order) {
|
|
175
|
+
if (sizes[id] === DEFAULT_COLUMN_WIDTH) continue;
|
|
176
|
+
const value = row.getValue(id)?.value?.toString();
|
|
177
|
+
if (!value) continue;
|
|
178
|
+
const size = Math.round(estimateTextWidth(value) + CELL_PADDING_WIDTH);
|
|
179
|
+
sizes[id] = Math.min(
|
|
180
|
+
DEFAULT_COLUMN_WIDTH,
|
|
181
|
+
Math.max(sizes[id] || MIN_COLUMN_WIDTH, size, MIN_COLUMN_WIDTH),
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return sizes;
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
let containerSize = useResizeObserver();
|
|
190
|
+
let resolvedWidths: Record<string, number> = $derived.by(() => {
|
|
191
|
+
const order = view.config.getOrder();
|
|
192
|
+
const sizes = Object.fromEntries(
|
|
193
|
+
order.map((id) => [
|
|
194
|
+
id,
|
|
195
|
+
columnSizing[id] ?? intrinsicWidths[id] ?? DEFAULT_COLUMN_WIDTH,
|
|
196
|
+
]),
|
|
197
|
+
) as Record<string, number>;
|
|
198
|
+
|
|
199
|
+
if (!order.length) {
|
|
200
|
+
return sizes;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const availableWidth = containerSize.size.width;
|
|
204
|
+
const totalWidth = order.reduce(
|
|
205
|
+
(sum, id) => sum + (sizes[id] ?? DEFAULT_COLUMN_WIDTH),
|
|
206
|
+
0,
|
|
207
|
+
);
|
|
208
|
+
const extraWidth = Math.max(0, availableWidth - totalWidth);
|
|
209
|
+
|
|
210
|
+
if (extraWidth > 0) {
|
|
211
|
+
const lastColumnId = order[order.length - 1];
|
|
212
|
+
sizes[lastColumnId] =
|
|
213
|
+
(sizes[lastColumnId] ?? DEFAULT_COLUMN_WIDTH) + extraWidth;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return sizes;
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
let columns: Array<ColumnDef<BasesEntry, any>> = $derived.by(() => {
|
|
220
|
+
return view.allProperties.map((id) => {
|
|
221
|
+
return {
|
|
222
|
+
id: id,
|
|
223
|
+
accessorKey: id,
|
|
224
|
+
size: resolvedWidths[id] ?? DEFAULT_COLUMN_WIDTH,
|
|
225
|
+
accessorFn: (row) => {
|
|
226
|
+
return row.getValue(id);
|
|
227
|
+
},
|
|
228
|
+
header: ({ column }) => {
|
|
229
|
+
return renderComponent(SortHeader, {
|
|
230
|
+
text: view.config.getDisplayName(id),
|
|
231
|
+
icon: getIcon(id),
|
|
232
|
+
direction: column.getIsSorted(),
|
|
233
|
+
onclick: column.getToggleSortingHandler(),
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
let columnOrder = $derived(view.config.getOrder());
|
|
241
|
+
let summaries = $derived.by(() => {
|
|
242
|
+
return resolveConfiguredSummaries(
|
|
243
|
+
view.config.getOrder(),
|
|
244
|
+
view.controller.doc.summaries,
|
|
245
|
+
view.config.get("summaries") as Record<string, string> | undefined,
|
|
246
|
+
).map(({ propertyId, summaryKey }) => ({
|
|
247
|
+
propertyId,
|
|
248
|
+
label: formatSummaryLabel(summaryKey),
|
|
249
|
+
value: queryResults
|
|
250
|
+
.getSummaryValue(
|
|
251
|
+
view.controller,
|
|
252
|
+
queryResults.data,
|
|
253
|
+
propertyId as BasesPropertyId,
|
|
254
|
+
summaryKey,
|
|
255
|
+
)
|
|
256
|
+
.toString(),
|
|
257
|
+
}));
|
|
258
|
+
});
|
|
259
|
+
let summaryMap = $derived.by(() => {
|
|
260
|
+
return summaries.reduce<Record<string, { label: string; value: string }>>(
|
|
261
|
+
(acc, summary) => {
|
|
262
|
+
acc[summary.propertyId] = {
|
|
263
|
+
label: summary.label,
|
|
264
|
+
value: summary.value,
|
|
265
|
+
};
|
|
266
|
+
return acc;
|
|
267
|
+
},
|
|
268
|
+
{},
|
|
269
|
+
);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const table = createSvelteTable({
|
|
273
|
+
get data() {
|
|
274
|
+
return queryResults.data;
|
|
275
|
+
},
|
|
276
|
+
get columns() {
|
|
277
|
+
return columns;
|
|
278
|
+
},
|
|
279
|
+
defaultColumn: {
|
|
280
|
+
minSize: 30,
|
|
281
|
+
size: 300,
|
|
282
|
+
maxSize: 800,
|
|
283
|
+
},
|
|
284
|
+
manualPagination: true,
|
|
285
|
+
manualSorting: true,
|
|
286
|
+
columnResizeMode: "onChange",
|
|
287
|
+
enableSortingRemoval: true,
|
|
288
|
+
isMultiSortEvent: (e) => true,
|
|
289
|
+
getCoreRowModel: getCoreRowModel(),
|
|
290
|
+
getSortedRowModel: getSortedRowModel(),
|
|
291
|
+
onSortingChange: (updater) => {
|
|
292
|
+
let sorting = view.config.getSort().map((s) => ({
|
|
293
|
+
id: s.property as string,
|
|
294
|
+
desc: s.direction === "DESC",
|
|
295
|
+
}));
|
|
296
|
+
if (typeof updater === "function") {
|
|
297
|
+
sorting = updater(sorting);
|
|
298
|
+
} else {
|
|
299
|
+
sorting = updater;
|
|
300
|
+
}
|
|
301
|
+
view.config.set(
|
|
302
|
+
"sort",
|
|
303
|
+
sorting.map((s) => ({
|
|
304
|
+
property: s.id,
|
|
305
|
+
direction: s.desc ? "DESC" : "ASC",
|
|
306
|
+
})) as Array<SortColumn>,
|
|
307
|
+
);
|
|
308
|
+
},
|
|
309
|
+
onColumnVisibilityChange: (updater) => {
|
|
310
|
+
let columnVisibility: VisibilityState = columns.reduce<VisibilityState>(
|
|
311
|
+
(acc, value) => {
|
|
312
|
+
const id = value.id as BasesPropertyId;
|
|
313
|
+
if (id) {
|
|
314
|
+
acc[id] = view.config.getOrder().includes(id);
|
|
315
|
+
}
|
|
316
|
+
return acc;
|
|
317
|
+
},
|
|
318
|
+
{},
|
|
319
|
+
);
|
|
320
|
+
if (typeof updater === "function") {
|
|
321
|
+
columnVisibility = updater(columnVisibility);
|
|
322
|
+
} else {
|
|
323
|
+
columnVisibility = updater;
|
|
324
|
+
}
|
|
325
|
+
const order: string[] = [];
|
|
326
|
+
view.config.getOrder().forEach((id) => {
|
|
327
|
+
if (columnVisibility[id]) {
|
|
328
|
+
order.push(id);
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
const newColumns = Object.keys(columnVisibility).filter(
|
|
332
|
+
(k) => columnVisibility[k] && !order.includes(k),
|
|
333
|
+
);
|
|
334
|
+
view.config.set("order", [...order, ...newColumns]);
|
|
335
|
+
},
|
|
336
|
+
onColumnSizingInfoChange: (updater) => {
|
|
337
|
+
if (typeof updater === "function") {
|
|
338
|
+
columnSizingInfo = updater(columnSizingInfo);
|
|
339
|
+
} else {
|
|
340
|
+
columnSizingInfo = updater;
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
onColumnSizingChange: (updater) => {
|
|
344
|
+
let columnSizing: ColumnSizingState = {
|
|
345
|
+
...(view.config.get("columnSize") || {}),
|
|
346
|
+
};
|
|
347
|
+
if (typeof updater === "function") {
|
|
348
|
+
columnSizing = updater(columnSizing);
|
|
349
|
+
} else {
|
|
350
|
+
columnSizing = updater;
|
|
351
|
+
}
|
|
352
|
+
view.config.set("columnSize", columnSizing);
|
|
353
|
+
},
|
|
354
|
+
onColumnOrderChange: (updater) => {
|
|
355
|
+
let columnOrder: ColumnOrderState = [...view.config.getOrder()];
|
|
356
|
+
if (typeof updater === "function") {
|
|
357
|
+
columnOrder = updater(columnOrder);
|
|
358
|
+
} else {
|
|
359
|
+
columnOrder = updater;
|
|
360
|
+
}
|
|
361
|
+
view.config.set("order", columnOrder);
|
|
362
|
+
},
|
|
363
|
+
state: {
|
|
364
|
+
get sorting() {
|
|
365
|
+
return view.config
|
|
366
|
+
.getSort()
|
|
367
|
+
.map((s) => ({ id: s.property, desc: s.direction === "DESC" }));
|
|
368
|
+
},
|
|
369
|
+
get columnSizingInfo() {
|
|
370
|
+
return columnSizingInfo;
|
|
371
|
+
},
|
|
372
|
+
get columnSizing() {
|
|
373
|
+
return columnSizing;
|
|
374
|
+
},
|
|
375
|
+
get columnVisibility() {
|
|
376
|
+
return columns.reduce<VisibilityState>((acc, value) => {
|
|
377
|
+
const id = value.id as BasesPropertyId;
|
|
378
|
+
if (id) {
|
|
379
|
+
acc[id] = view.config.getOrder().includes(id);
|
|
380
|
+
}
|
|
381
|
+
return acc;
|
|
382
|
+
}, {});
|
|
383
|
+
},
|
|
384
|
+
get columnOrder() {
|
|
385
|
+
return columnOrder;
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
function handleDragEnd(event: DragEndEvent) {
|
|
391
|
+
const active = event.operation.source;
|
|
392
|
+
const over = event.operation.target;
|
|
393
|
+
if (active && over && active.id !== over.id) {
|
|
394
|
+
const oldIndex = view.config
|
|
395
|
+
.getOrder()
|
|
396
|
+
.indexOf(active.id as BasesPropertyId);
|
|
397
|
+
const newIndex = view.config
|
|
398
|
+
.getOrder()
|
|
399
|
+
.indexOf(over.id as BasesPropertyId);
|
|
400
|
+
view.config.set(
|
|
401
|
+
"order",
|
|
402
|
+
arrayMove(view.config.getOrder(), oldIndex, newIndex),
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const rowHeights: Record<string, number> = {
|
|
408
|
+
short: 30,
|
|
409
|
+
medium: 60,
|
|
410
|
+
tall: 120,
|
|
411
|
+
extra: 240,
|
|
412
|
+
};
|
|
413
|
+
let rowHeight: number = $derived(
|
|
414
|
+
rowHeights[view.config.get("rowHeight") as string] ?? 30,
|
|
415
|
+
);
|
|
416
|
+
let groupBy = $derived(view.config.get("groupBy") as GroupConfig | undefined);
|
|
417
|
+
let collapsedGroups = $state<Record<string, boolean>>({});
|
|
418
|
+
|
|
419
|
+
function getGroupKey(
|
|
420
|
+
group: { key?: { toString(): string } | null },
|
|
421
|
+
groupIndex: number,
|
|
422
|
+
) {
|
|
423
|
+
return `group_${groupIndex}_${group.key?.toString() ?? "ungrouped"}`;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function toggleGroupCollapse(groupKey: string) {
|
|
427
|
+
collapsedGroups = {
|
|
428
|
+
...collapsedGroups,
|
|
429
|
+
[groupKey]: !collapsedGroups[groupKey],
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
let groupedResults = $derived.by(() => {
|
|
434
|
+
const groups = [...queryResults.groupedData];
|
|
435
|
+
|
|
436
|
+
if (!groupBy?.property) {
|
|
437
|
+
return groups;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const direction = groupBy.direction === "DESC" ? -1 : 1;
|
|
441
|
+
|
|
442
|
+
return groups.sort((left, right) => {
|
|
443
|
+
const leftHasKey = left.hasKey();
|
|
444
|
+
const rightHasKey = right.hasKey();
|
|
445
|
+
|
|
446
|
+
if (leftHasKey && !rightHasKey) {
|
|
447
|
+
return -1;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (!leftHasKey && rightHasKey) {
|
|
451
|
+
return 1;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const leftValue = left.key?.toString() ?? "";
|
|
455
|
+
const rightValue = right.key?.toString() ?? "";
|
|
456
|
+
|
|
457
|
+
return (
|
|
458
|
+
direction *
|
|
459
|
+
leftValue.localeCompare(rightValue, undefined, {
|
|
460
|
+
numeric: true,
|
|
461
|
+
sensitivity: "base",
|
|
462
|
+
})
|
|
463
|
+
);
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
let displayItems = $derived.by(() => {
|
|
467
|
+
const items: TableDisplayItem[] = [];
|
|
468
|
+
const groupLabel = groupBy?.property
|
|
469
|
+
? view.config.getDisplayName(groupBy.property) || groupBy.property
|
|
470
|
+
: "";
|
|
471
|
+
|
|
472
|
+
for (const [groupIndex, group] of groupedResults.entries()) {
|
|
473
|
+
if (groupBy?.property) {
|
|
474
|
+
const groupKey = getGroupKey(group, groupIndex);
|
|
475
|
+
const collapsed = !!collapsedGroups[groupKey];
|
|
476
|
+
items.push({
|
|
477
|
+
kind: "group",
|
|
478
|
+
key: groupKey,
|
|
479
|
+
label: groupLabel,
|
|
480
|
+
value: group.hasKey() ? (group.key?.toString() ?? "") : "Ungrouped",
|
|
481
|
+
collapsed,
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
if (collapsed) {
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
for (const row of group.entries) {
|
|
490
|
+
items.push({
|
|
491
|
+
kind: "row",
|
|
492
|
+
key: row.id,
|
|
493
|
+
row,
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return items;
|
|
499
|
+
});
|
|
500
|
+
let activeSearchQuery = $derived(view.controller.searchQuery.trim());
|
|
501
|
+
let showStickySearch = $derived(
|
|
502
|
+
!!activeSearchQuery && !view.controller.searchPanelOpen,
|
|
503
|
+
);
|
|
504
|
+
let tableContainerRef: HTMLElement | null = $state(null);
|
|
505
|
+
let scrollTop = $state(0);
|
|
506
|
+
let headers = $derived(view.config.getOrder());
|
|
507
|
+
let headerMap = $derived.by(() => {
|
|
508
|
+
return table
|
|
509
|
+
.getFlatHeaders()
|
|
510
|
+
.reduce<Record<string, Header<BasesEntry, any>>>((acc, value) => {
|
|
511
|
+
acc[value.id] = value;
|
|
512
|
+
return acc;
|
|
513
|
+
}, {});
|
|
514
|
+
});
|
|
515
|
+
let columnTracks = $derived.by(() => {
|
|
516
|
+
columnSizing;
|
|
517
|
+
resolvedWidths;
|
|
518
|
+
const order = view.config.getOrder();
|
|
519
|
+
return resolveTableColumnTracks(order, (id) => {
|
|
520
|
+
return (
|
|
521
|
+
headerMap[id]?.column.getSize() ??
|
|
522
|
+
resolvedWidths[id] ??
|
|
523
|
+
DEFAULT_COLUMN_WIDTH
|
|
524
|
+
);
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
let columnTrackMap = $derived.by(() => {
|
|
528
|
+
return Object.fromEntries(
|
|
529
|
+
columnTracks.tracks.map((track) => [track.id, track]),
|
|
530
|
+
);
|
|
531
|
+
});
|
|
532
|
+
let displayItemCount = $derived(displayItems.length);
|
|
533
|
+
let displayItemMeasurementKey = $derived(
|
|
534
|
+
displayItems.map((item) => item.key).join("\u0000"),
|
|
535
|
+
);
|
|
536
|
+
let measuredItemSizes: Record<string, number> = $state({});
|
|
537
|
+
let virtualItemEls: Array<HTMLDivElement | null> = $state([]);
|
|
538
|
+
let rowVirtualizer = createVirtualizer({
|
|
539
|
+
count: untrack(() => displayItemCount),
|
|
540
|
+
getScrollElement: () => tableContainerRef,
|
|
541
|
+
estimateSize: (index) =>
|
|
542
|
+
untrack(() => {
|
|
543
|
+
const item = displayItems[index];
|
|
544
|
+
return item
|
|
545
|
+
? (measuredItemSizes[item.key] ??
|
|
546
|
+
(item.kind === "group" ? GROUP_HEADER_HEIGHT : rowHeight))
|
|
547
|
+
: rowHeight;
|
|
548
|
+
}),
|
|
549
|
+
overscan: 5,
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
$effect(() => {
|
|
553
|
+
if (rowVirtualizer.options.count !== displayItemCount) {
|
|
554
|
+
rowVirtualizer.setOptions({ count: displayItemCount });
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
let groupSections = $derived.by(() => {
|
|
559
|
+
rowVirtualizer.getTotalSize();
|
|
560
|
+
const sections: GroupSection[] = [];
|
|
561
|
+
let estimatedStart = 0;
|
|
562
|
+
|
|
563
|
+
for (const [index, item] of displayItems.entries()) {
|
|
564
|
+
const measurement = rowVirtualizer.measurementsCache[index];
|
|
565
|
+
if (item.kind === "group") {
|
|
566
|
+
sections.push({
|
|
567
|
+
key: item.key,
|
|
568
|
+
label: item.label,
|
|
569
|
+
value: item.value,
|
|
570
|
+
collapsed: item.collapsed,
|
|
571
|
+
start: measurement?.start ?? estimatedStart,
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
estimatedStart +=
|
|
576
|
+
measurement?.size ??
|
|
577
|
+
(item.kind === "group" ? GROUP_HEADER_HEIGHT : rowHeight);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
return sections;
|
|
581
|
+
});
|
|
582
|
+
let stickyGroup = $derived.by(() => {
|
|
583
|
+
if (!groupBy?.property || !groupSections.length) {
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
let activeIndex = 0;
|
|
588
|
+
|
|
589
|
+
for (let index = 0; index < groupSections.length; index += 1) {
|
|
590
|
+
if (groupSections[index].start <= scrollTop) {
|
|
591
|
+
activeIndex = index;
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
const active = groupSections[activeIndex];
|
|
599
|
+
const next = groupSections[activeIndex + 1];
|
|
600
|
+
let offset = 0;
|
|
601
|
+
|
|
602
|
+
if (next) {
|
|
603
|
+
offset = Math.min(0, next.start - scrollTop - GROUP_HEADER_HEIGHT);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
return {
|
|
607
|
+
...active,
|
|
608
|
+
offset,
|
|
609
|
+
visible: scrollTop > active.start,
|
|
610
|
+
};
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
let columnVirtualizer = createVirtualizer({
|
|
614
|
+
horizontal: true,
|
|
615
|
+
get count() {
|
|
616
|
+
return view.config.getOrder().length;
|
|
617
|
+
},
|
|
618
|
+
getScrollElement: () => tableContainerRef,
|
|
619
|
+
estimateSize: (i) => {
|
|
620
|
+
return columnTracks.tracks[i]?.width ?? DEFAULT_COLUMN_WIDTH;
|
|
621
|
+
},
|
|
622
|
+
overscan: 3,
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
$effect(() => {
|
|
626
|
+
columnVirtualizer.setOptions({ count: view.config.getOrder().length });
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
$effect(() => {
|
|
630
|
+
if (!tableContainerRef) {
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
rowVirtualizer.setOptions({
|
|
635
|
+
count: displayItems.length,
|
|
636
|
+
getScrollElement: () => tableContainerRef,
|
|
637
|
+
});
|
|
638
|
+
columnVirtualizer.setOptions({
|
|
639
|
+
count: view.config.getOrder().length,
|
|
640
|
+
getScrollElement: () => tableContainerRef,
|
|
641
|
+
});
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
$effect(() => {
|
|
645
|
+
const viewport = tableContainerRef;
|
|
646
|
+
if (!viewport) {
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
containerSize.ref = viewport;
|
|
651
|
+
const updateScrollTop = () => {
|
|
652
|
+
scrollTop = viewport.scrollTop;
|
|
653
|
+
};
|
|
654
|
+
updateScrollTop();
|
|
655
|
+
viewport.addEventListener("scroll", updateScrollTop, { passive: true });
|
|
656
|
+
|
|
657
|
+
return () => viewport.removeEventListener("scroll", updateScrollTop);
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
$effect(() => {
|
|
661
|
+
columnTracks;
|
|
662
|
+
columnSizingInfo;
|
|
663
|
+
containerSize.size.width;
|
|
664
|
+
view.config.getOrder();
|
|
665
|
+
columnVirtualizer.measure();
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
let measuredDisplayItemKey = "";
|
|
669
|
+
let measuredRowHeight = 0;
|
|
670
|
+
let measurementFrame = 0;
|
|
671
|
+
|
|
672
|
+
$effect(() => {
|
|
673
|
+
if (
|
|
674
|
+
measuredDisplayItemKey === displayItemMeasurementKey &&
|
|
675
|
+
measuredRowHeight === rowHeight
|
|
676
|
+
) {
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
measuredDisplayItemKey = displayItemMeasurementKey;
|
|
681
|
+
const rowHeightChanged = measuredRowHeight !== rowHeight;
|
|
682
|
+
measuredRowHeight = rowHeight;
|
|
683
|
+
if (rowHeightChanged) {
|
|
684
|
+
measuredItemSizes = {};
|
|
685
|
+
}
|
|
686
|
+
virtualItemEls.length = displayItemCount;
|
|
687
|
+
rowVirtualizer.measure();
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
$effect(() => {
|
|
691
|
+
const _ = [
|
|
692
|
+
displayItems,
|
|
693
|
+
columnTracks,
|
|
694
|
+
columnSizingInfo.isResizingColumn,
|
|
695
|
+
rowHeight,
|
|
696
|
+
];
|
|
697
|
+
const renderedElements = virtualItemEls.slice();
|
|
698
|
+
cancelAnimationFrame(measurementFrame);
|
|
699
|
+
measurementFrame = requestAnimationFrame(() => {
|
|
700
|
+
const nextSizes = { ...measuredItemSizes };
|
|
701
|
+
let sizesChanged = false;
|
|
702
|
+
|
|
703
|
+
renderedElements.forEach((element, index) => {
|
|
704
|
+
if (element?.isConnected) {
|
|
705
|
+
const item = displayItems[index];
|
|
706
|
+
const height = element.getBoundingClientRect().height;
|
|
707
|
+
if (item && Math.abs((nextSizes[item.key] ?? 0) - height) > 0.25) {
|
|
708
|
+
nextSizes[item.key] = height;
|
|
709
|
+
sizesChanged = true;
|
|
710
|
+
}
|
|
711
|
+
rowVirtualizer.resizeItem(
|
|
712
|
+
index,
|
|
713
|
+
height,
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
if (sizesChanged) {
|
|
719
|
+
measuredItemSizes = nextSizes;
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
onDestroy(() => cancelAnimationFrame(measurementFrame));
|
|
725
|
+
|
|
726
|
+
type RenderedVirtualRow = ReturnType<
|
|
727
|
+
typeof rowVirtualizer.getVirtualItems
|
|
728
|
+
>[number];
|
|
729
|
+
|
|
730
|
+
let virtualRows = $derived.by(() => {
|
|
731
|
+
displayItems.length;
|
|
732
|
+
rowHeight;
|
|
733
|
+
scrollTop;
|
|
734
|
+
return rowVirtualizer.getVirtualItems();
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
let visibleVirtualRows = $derived.by(() => {
|
|
738
|
+
return virtualRows.reduce<
|
|
739
|
+
Array<{ virtualRow: RenderedVirtualRow; item: TableDisplayItem }>
|
|
740
|
+
>((entries, virtualRow) => {
|
|
741
|
+
const item = displayItems[virtualRow.index];
|
|
742
|
+
if (!item) {
|
|
743
|
+
return entries;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
entries.push({ virtualRow, item });
|
|
747
|
+
return entries;
|
|
748
|
+
}, []);
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
let lastDataRowIndex = $derived.by(() => {
|
|
752
|
+
for (let index = displayItems.length - 1; index >= 0; index -= 1) {
|
|
753
|
+
if (displayItems[index]?.kind === "row") {
|
|
754
|
+
return index;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
return -1;
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
let virtualTotalSize = $derived.by(() => {
|
|
762
|
+
rowHeight;
|
|
763
|
+
scrollTop;
|
|
764
|
+
return resolveVirtualTotalSize(
|
|
765
|
+
displayItems.length,
|
|
766
|
+
rowVirtualizer.getTotalSize(),
|
|
767
|
+
);
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
let previousDisplayItemCount = $state(0);
|
|
771
|
+
|
|
772
|
+
$effect(() => {
|
|
773
|
+
const count = displayItems.length;
|
|
774
|
+
|
|
775
|
+
if (
|
|
776
|
+
count < previousDisplayItemCount &&
|
|
777
|
+
tableContainerRef &&
|
|
778
|
+
tableContainerRef.scrollTop > 0
|
|
779
|
+
) {
|
|
780
|
+
tableContainerRef.scrollTop = 0;
|
|
781
|
+
scrollTop = 0;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
previousDisplayItemCount = count;
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
let show = $state(false);
|
|
788
|
+
onMount(() => {
|
|
789
|
+
setTimeout(() => {
|
|
790
|
+
show = true;
|
|
791
|
+
});
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
let tableWidth = $derived.by(() => {
|
|
795
|
+
return Math.max(containerSize.size.width, columnTracks.totalWidth);
|
|
796
|
+
});
|
|
797
|
+
</script>
|
|
798
|
+
|
|
799
|
+
<DragDropProvider onDragEnd={handleDragEnd}>
|
|
800
|
+
<div
|
|
801
|
+
class="bases-table-view bases-style-h-full-668b21 bases-style-w-full-6da6a3 bases-style-flex-grow-95a3df"
|
|
802
|
+
data-layout="table"
|
|
803
|
+
data-ui-component="bases-table-view"
|
|
804
|
+
data-ui-part="scroll-area-shell"
|
|
805
|
+
>
|
|
806
|
+
<ScrollArea.Root
|
|
807
|
+
class="bases-table__scroll-area"
|
|
808
|
+
orientation="both"
|
|
809
|
+
type="always"
|
|
810
|
+
bind:viewportRef={tableContainerRef}
|
|
811
|
+
scrollbarXClasses="bases-table__scrollbar bases-table__scrollbar--horizontal"
|
|
812
|
+
scrollbarYClasses="bases-table__scrollbar bases-table__scrollbar--vertical"
|
|
813
|
+
>
|
|
814
|
+
<div
|
|
815
|
+
class="bases-table-container bases-table relative bases-style-min-w-full-a1e7a8 bases-style-pb-100px-03c580 bases-style-text-sm-fc7473"
|
|
816
|
+
style={`--ui-bases-table-row-height: ${rowHeight}px; width: ${tableWidth}px; height: ${virtualTotalSize + 2 * rowHeight}px;`}
|
|
817
|
+
>
|
|
818
|
+
<div
|
|
819
|
+
class="bases-table__head bases-thead sticky bases-style-top-0-216740 bases-style-h-9-e7a768"
|
|
820
|
+
data-ui-part="header"
|
|
821
|
+
>
|
|
822
|
+
<div
|
|
823
|
+
class="bases-tr relative bases-style-flex-60fbb7 bases-style-h-9-e7a768 bases-style-min-w-full-a1e7a8 bases-style-flex-row-a6e886"
|
|
824
|
+
style={`width: ${tableWidth}px;`}
|
|
825
|
+
>
|
|
826
|
+
{#each columnVirtualizer.getVirtualItems() as virtualColumn (virtualColumn.key)}
|
|
827
|
+
{@const track = columnTracks.tracks[virtualColumn.index]}
|
|
828
|
+
{#if track}
|
|
829
|
+
<div
|
|
830
|
+
data-index={virtualColumn.index}
|
|
831
|
+
data-column-id={track.id}
|
|
832
|
+
class="bases-table__header-cell absolute bases-style-top-0-216740 bases-style-left-0-c78fac"
|
|
833
|
+
style={`inset-inline-start: ${track.startCss}; width: ${track.widthCss}`}
|
|
834
|
+
>
|
|
835
|
+
<TableHeader
|
|
836
|
+
header={headerMap[track.id]}
|
|
837
|
+
icon={getIcon(track.id as BasesPropertyId)}
|
|
838
|
+
text={view.config.getDisplayName(track.id as BasesPropertyId)}
|
|
839
|
+
width={track.width}
|
|
840
|
+
/>
|
|
841
|
+
</div>
|
|
842
|
+
{/if}
|
|
843
|
+
{/each}
|
|
844
|
+
</div>
|
|
845
|
+
</div>
|
|
846
|
+
{#if showStickySearch}
|
|
847
|
+
<div
|
|
848
|
+
class="bases-table__sticky-search sticky bases-style-z-96-7882ad bases-style-flex-60fbb7 bases-style-min-w-full-a1e7a8 bases-style-items-center-3960ff bases-style-gap-2-77a2a2 bases-style-border-b-65fdba bases-style-px-3-0e17f2 bases-style-py-2-03b4dd bases-style-text-sm-fc7473 backdrop-blur-sm"
|
|
849
|
+
style={`top: 36px; width: ${tableWidth}px; min-height: ${ACTIVE_SEARCH_HEIGHT}px;`}
|
|
850
|
+
>
|
|
851
|
+
<span class="bases-table__group-label">Search</span>
|
|
852
|
+
<span class="bases-style-font-medium-2689f3">{activeSearchQuery}</span>
|
|
853
|
+
</div>
|
|
854
|
+
{/if}
|
|
855
|
+
{#if stickyGroup?.visible}
|
|
856
|
+
<div
|
|
857
|
+
class="bases-table__group sticky bases-style-z-95-0002d5 bases-style-flex-60fbb7 bases-style-min-w-full-a1e7a8 bases-style-items-center-3960ff bases-style-border-t-b950dd bases-style-border-b-65fdba bases-style-px-3-0e17f2 bases-style-py-2-03b4dd bases-style-text-sm-fc7473"
|
|
858
|
+
style={`top: ${36 + (showStickySearch ? ACTIVE_SEARCH_HEIGHT : 0)}px; width: ${tableWidth}px; transform: translateY(${stickyGroup.offset}px); min-height: ${GROUP_HEADER_HEIGHT}px;`}
|
|
859
|
+
>
|
|
860
|
+
<button
|
|
861
|
+
type="button"
|
|
862
|
+
class="bases-style-flex-60fbb7 bases-style-w-full-6da6a3 bases-style-items-center-3960ff bases-style-gap-2-77a2a2 bases-style-text-left-2eba0d"
|
|
863
|
+
aria-expanded={!stickyGroup.collapsed}
|
|
864
|
+
onclick={() => toggleGroupCollapse(stickyGroup.key)}
|
|
865
|
+
>
|
|
866
|
+
{#if stickyGroup.collapsed}
|
|
867
|
+
<ChevronRight
|
|
868
|
+
class="bases-table__group-label bases-style-size-4-f7b5fa bases-style-shrink-0-012fbd"
|
|
869
|
+
/>
|
|
870
|
+
{:else}
|
|
871
|
+
<ChevronDown class="bases-table__group-label bases-style-size-4-f7b5fa bases-style-shrink-0-012fbd" />
|
|
872
|
+
{/if}
|
|
873
|
+
<span class="bases-table__group-label">{stickyGroup.label}</span>
|
|
874
|
+
<span class="bases-style-font-semibold-e83a70">{stickyGroup.value}</span>
|
|
875
|
+
</button>
|
|
876
|
+
</div>
|
|
877
|
+
{/if}
|
|
878
|
+
<div
|
|
879
|
+
class="bases-tbody absolute"
|
|
880
|
+
style={`height: ${virtualTotalSize}px;`}
|
|
881
|
+
>
|
|
882
|
+
{#if show}
|
|
883
|
+
{#each visibleVirtualRows as entry (entry.item.key)}
|
|
884
|
+
{@const virtualRow = entry.virtualRow}
|
|
885
|
+
{@const item = entry.item}
|
|
886
|
+
{#if item?.kind === "group"}
|
|
887
|
+
<div
|
|
888
|
+
data-index={virtualRow.index}
|
|
889
|
+
bind:this={
|
|
890
|
+
() => {
|
|
891
|
+
virtualItemEls[virtualRow.index] ||= null;
|
|
892
|
+
return virtualItemEls[virtualRow.index];
|
|
893
|
+
},
|
|
894
|
+
(value) => {
|
|
895
|
+
virtualItemEls[virtualRow.index] = value;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
class="bases-table__group absolute bases-style-top-0-216740 bases-style-left-0-c78fac bases-style-flex-60fbb7 bases-style-min-w-full-a1e7a8 bases-style-items-center-3960ff bases-style-border-t-b950dd bases-style-border-b-65fdba bases-style-px-3-0e17f2 bases-style-py-2-03b4dd bases-style-text-sm-fc7473"
|
|
899
|
+
style={`width: ${tableWidth}px; top: ${virtualRow.start}px; min-height: ${GROUP_HEADER_HEIGHT}px;`}
|
|
900
|
+
>
|
|
901
|
+
<button
|
|
902
|
+
type="button"
|
|
903
|
+
class="bases-style-flex-60fbb7 bases-style-w-full-6da6a3 bases-style-items-center-3960ff bases-style-gap-2-77a2a2 bases-style-text-left-2eba0d"
|
|
904
|
+
aria-expanded={!item.collapsed}
|
|
905
|
+
onclick={() => toggleGroupCollapse(item.key)}
|
|
906
|
+
>
|
|
907
|
+
{#if item.collapsed}
|
|
908
|
+
<ChevronRight
|
|
909
|
+
class="bases-table__group-label bases-style-size-4-f7b5fa bases-style-shrink-0-012fbd"
|
|
910
|
+
/>
|
|
911
|
+
{:else}
|
|
912
|
+
<ChevronDown
|
|
913
|
+
class="bases-table__group-label bases-style-size-4-f7b5fa bases-style-shrink-0-012fbd"
|
|
914
|
+
/>
|
|
915
|
+
{/if}
|
|
916
|
+
<span class="bases-table__group-label">{item.label}</span>
|
|
917
|
+
<span class="bases-style-font-semibold-e83a70">{item.value}</span>
|
|
918
|
+
</button>
|
|
919
|
+
</div>
|
|
920
|
+
{:else if item.kind === "row"}
|
|
921
|
+
{@const row = item.row}
|
|
922
|
+
<div
|
|
923
|
+
data-index={virtualRow.index}
|
|
924
|
+
bind:this={
|
|
925
|
+
() => {
|
|
926
|
+
virtualItemEls[virtualRow.index] ||= null;
|
|
927
|
+
return virtualItemEls[virtualRow.index];
|
|
928
|
+
},
|
|
929
|
+
(value) => {
|
|
930
|
+
virtualItemEls[virtualRow.index] = value;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
class="bases-table__row bases-tr absolute bases-style-top-0-216740 bases-style-left-0-c78fac bases-style-flex-60fbb7 bases-style-min-w-full-a1e7a8 bases-style-flex-row-a6e886 bases-style-transition-colors-ceb69a"
|
|
934
|
+
style={`width: ${tableWidth}px; top: ${virtualRow.start}px; min-height: var(--ui-bases-table-row-height);`}
|
|
935
|
+
data-ui-part="row"
|
|
936
|
+
data-last-row={virtualRow.index === lastDataRowIndex}
|
|
937
|
+
>
|
|
938
|
+
{#each headers as id, idx (`${id}_${virtualRow.key}_${idx}`)}
|
|
939
|
+
{@const track = columnTrackMap[id]}
|
|
940
|
+
<div
|
|
941
|
+
data-column-id={id}
|
|
942
|
+
class="bases-table__cell bases-td bases-style-flex-none-81e443 bases-style-overflow-hidden-2cd02d bases-style-text-nowrap-621d3b bases-style-overflow-ellipsis-5b2ef5 bases-style-whitespace-nowrap-e82ae8"
|
|
943
|
+
style={`flex: 0 0 auto; width: ${track?.widthCss ?? "0px"}; min-height: var(--ui-bases-table-row-height)`}
|
|
944
|
+
>
|
|
945
|
+
<div
|
|
946
|
+
class="bases-table__cell-inner bases-style-h-full-668b21 bases-style-w-full-6da6a3 bases-style-px-2-d5eab2 bases-style-py-1-660d2e"
|
|
947
|
+
>
|
|
948
|
+
<Cell
|
|
949
|
+
app={view.controller.app}
|
|
950
|
+
name={id}
|
|
951
|
+
file={row.file}
|
|
952
|
+
readOnly={view.controller.readOnly}
|
|
953
|
+
type={types[id]?.type}
|
|
954
|
+
value={row.getValue(id as BasesPropertyId)}
|
|
955
|
+
class="bases-style-z-1-0bcb04 bases-style-h-full-668b21 bases-style-w-full-6da6a3 bases-style-min-w-full-a1e7a8 bases-style-overflow-hidden-2cd02d bases-style-text-nowrap-621d3b bases-style-overflow-ellipsis-5b2ef5"
|
|
956
|
+
/>
|
|
957
|
+
</div>
|
|
958
|
+
</div>
|
|
959
|
+
{/each}
|
|
960
|
+
</div>
|
|
961
|
+
{/if}
|
|
962
|
+
{/each}
|
|
963
|
+
{/if}
|
|
964
|
+
</div>
|
|
965
|
+
{#if summaries.length}
|
|
966
|
+
<div
|
|
967
|
+
class="bases-table__footer bases-tfoot sticky bases-style-bottom-0-189f03 bases-style-z-90-3d4b39 bases-style-border-t-b950dd backdrop-blur-sm"
|
|
968
|
+
>
|
|
969
|
+
<div
|
|
970
|
+
class="bases-tr relative bases-style-flex-60fbb7 bases-style-min-w-full-a1e7a8 bases-style-flex-row-a6e886"
|
|
971
|
+
style={`width: ${tableWidth}px;`}
|
|
972
|
+
>
|
|
973
|
+
{#each headers as id, idx (`${id}_summary_${idx}`)}
|
|
974
|
+
{@const summary = summaryMap[id]}
|
|
975
|
+
{@const track = columnTrackMap[id]}
|
|
976
|
+
<div
|
|
977
|
+
data-column-id={id}
|
|
978
|
+
class="bases-table__summary-cell bases-td bases-style-min-h-11-0e5b24 bases-style-flex-none-81e443 bases-style-items-center-3960ff bases-style-overflow-hidden-2cd02d bases-style-px-2-d5eab2 bases-style-py-1-660d2e bases-style-text-xs-359090"
|
|
979
|
+
style={`flex: 0 0 auto; width: ${track?.widthCss ?? "0px"}`}
|
|
980
|
+
>
|
|
981
|
+
{#if summary}
|
|
982
|
+
<div class="bases-style-min-w-0-7e0b7c">
|
|
983
|
+
<div class="bases-table__summary-label truncate">
|
|
984
|
+
{summary.label}
|
|
985
|
+
</div>
|
|
986
|
+
<div class="truncate bases-style-font-medium-2689f3">{summary.value}</div>
|
|
987
|
+
</div>
|
|
988
|
+
{/if}
|
|
989
|
+
</div>
|
|
990
|
+
{/each}
|
|
991
|
+
</div>
|
|
992
|
+
</div>
|
|
993
|
+
{/if}
|
|
994
|
+
</div>
|
|
995
|
+
</ScrollArea.Root>
|
|
996
|
+
</div>
|
|
997
|
+
</DragDropProvider>
|