@lapis-notes/csv 0.0.0-stage → 0.2.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 +22 -0
- package/LICENSE.md +4 -0
- package/README.md +19 -2
- package/dist/app.css +210 -0
- package/dist/csv-settings.d.ts +13 -0
- package/dist/csv-settings.js +28 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/lib/CsvSheet.svelte +58 -0
- package/dist/lib/CsvSheet.svelte.d.ts +11 -0
- package/dist/lib/CsvTable.svelte +58 -0
- package/dist/lib/CsvTable.svelte.d.ts +11 -0
- package/dist/lib/codemirror/csv-language-extensions.d.ts +3 -0
- package/dist/lib/codemirror/csv-language-extensions.js +11 -0
- package/dist/lib/codemirror/csv-language.d.ts +3 -0
- package/dist/lib/codemirror/csv-language.js +24 -0
- package/dist/lib/codemirror/csv-rainbow-theme.d.ts +3 -0
- package/dist/lib/codemirror/csv-rainbow-theme.js +15 -0
- package/dist/lib/codemirror/csv-source-extensions.d.ts +7 -0
- package/dist/lib/codemirror/csv-source-extensions.js +9 -0
- package/dist/lib/codemirror/csv.parser.d.ts +2 -0
- package/dist/lib/codemirror/csv.parser.js +16 -0
- package/dist/lib/codemirror/csv.parser.terms.d.ts +1 -0
- package/dist/lib/codemirror/csv.parser.terms.js +2 -0
- package/dist/lib/codemirror/syntax.grammar +36 -0
- package/dist/lib/components/csv-cell.svelte +137 -0
- package/dist/lib/components/csv-cell.svelte.d.ts +11 -0
- package/dist/lib/components/csv-data-table-column-head.svelte +76 -0
- package/dist/lib/components/csv-data-table-column-head.svelte.d.ts +17 -0
- package/dist/lib/components/csv-data-table-column-header.svelte +159 -0
- package/dist/lib/components/csv-data-table-column-header.svelte.d.ts +23 -0
- package/dist/lib/components/csv-data-table-data-cell.svelte +72 -0
- package/dist/lib/components/csv-data-table-data-cell.svelte.d.ts +18 -0
- package/dist/lib/components/csv-data-table-drag-overlay.svelte +119 -0
- package/dist/lib/components/csv-data-table-drag-overlay.svelte.d.ts +13 -0
- package/dist/lib/components/csv-data-table-row-actions.svelte +47 -0
- package/dist/lib/components/csv-data-table-row-actions.svelte.d.ts +8 -0
- package/dist/lib/components/csv-data-table-row-grip-cell.svelte +102 -0
- package/dist/lib/components/csv-data-table-row-grip-cell.svelte.d.ts +19 -0
- package/dist/lib/components/csv-data-table-row.svelte +38 -0
- package/dist/lib/components/csv-data-table-row.svelte.d.ts +11 -0
- package/dist/lib/components/csv-data-table.svelte +903 -0
- package/dist/lib/components/csv-data-table.svelte.d.ts +21 -0
- package/dist/lib/components/csv-find.svelte +382 -0
- package/dist/lib/components/csv-find.svelte.d.ts +24 -0
- package/dist/lib/components/csv-row-virtualizer.svelte.d.ts +9 -0
- package/dist/lib/components/csv-row-virtualizer.svelte.js +59 -0
- package/dist/lib/components/csv-source-view.svelte +127 -0
- package/dist/lib/components/csv-source-view.svelte.d.ts +10 -0
- package/dist/lib/components/csv-status-bar.svelte +101 -0
- package/dist/lib/components/csv-status-bar.svelte.d.ts +14 -0
- package/dist/lib/components/csv-table-dnd.d.ts +9 -0
- package/dist/lib/components/csv-table-dnd.js +12 -0
- package/dist/lib/components/csv-table-drag-measure.d.ts +7 -0
- package/dist/lib/components/csv-table-drag-measure.js +38 -0
- package/dist/lib/components/csv-table-selection.d.ts +28 -0
- package/dist/lib/components/csv-table-selection.js +178 -0
- package/dist/lib/components/csv-table-view.svelte +265 -0
- package/dist/lib/components/csv-table-view.svelte.d.ts +16 -0
- package/dist/lib/csv-plugin.d.ts +9 -0
- package/dist/lib/csv-plugin.js +107 -0
- package/dist/lib/csv-view-settings.d.ts +43 -0
- package/dist/lib/csv-view-settings.js +86 -0
- package/dist/lib/ids.d.ts +8 -0
- package/dist/lib/ids.js +19 -0
- package/dist/lib/model/csv-column-sort.d.ts +16 -0
- package/dist/lib/model/csv-column-sort.js +81 -0
- package/dist/lib/model/csv-grid.d.ts +31 -0
- package/dist/lib/model/csv-grid.js +161 -0
- package/dist/lib/model/csv-search.d.ts +38 -0
- package/dist/lib/model/csv-search.js +101 -0
- package/dist/lib/model/csv-table-model.d.ts +10 -0
- package/dist/lib/model/csv-table-model.js +18 -0
- package/dist/lib/model/parse-csv.d.ts +10 -0
- package/dist/lib/model/parse-csv.js +13 -0
- package/dist/lib/model/serialize-csv.d.ts +5 -0
- package/dist/lib/model/serialize-csv.js +10 -0
- package/dist/lib/sheet/csv-sheet-toolbar.svelte +244 -0
- package/dist/lib/sheet/csv-sheet-toolbar.svelte.d.ts +20 -0
- package/dist/lib/sheet/csv-sheet-view.svelte +444 -0
- package/dist/lib/sheet/csv-sheet-view.svelte.d.ts +18 -0
- package/dist/lib/sheet/csv-sheet.svelte +1121 -0
- package/dist/lib/sheet/csv-sheet.svelte.d.ts +37 -0
- package/dist/lib/sheet/sheet-glyph.svelte +57 -0
- package/dist/lib/sheet/sheet-glyph.svelte.d.ts +7 -0
- package/dist/lib/sheet/sheet-history.d.ts +14 -0
- package/dist/lib/sheet/sheet-history.js +33 -0
- package/dist/lib/sheet/sheet-layout.d.ts +121 -0
- package/dist/lib/sheet/sheet-layout.js +336 -0
- package/dist/lib/sheet/sheet-paint.d.ts +45 -0
- package/dist/lib/sheet/sheet-paint.js +274 -0
- package/dist/lib/sheet/sheet-theme.d.ts +2 -0
- package/dist/lib/sheet/sheet-theme.js +26 -0
- package/dist/lib/views/csv-view-mode.d.ts +3 -0
- package/dist/lib/views/csv-view-mode.js +6 -0
- package/dist/lib/views/csv-view.d.ts +24 -0
- package/dist/lib/views/csv-view.js +125 -0
- package/dist/styles/csv-find.css +313 -0
- package/dist/styles/csv-table.css +882 -0
- package/dist/styles.css +37 -0
- package/manifest.json +45 -0
- package/package.json +86 -3
- package/registry-content/overview.md +3 -0
- package/registry.json +322 -0
- package/styles.css +1 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { SHEET_CELL_PADDING, SHEET_GUTTER_WIDTH, SHEET_HEADER_HEIGHT, SHEET_LETTER_HEIGHT, SHEET_LINE_HEIGHT, SHEET_ROW_HEIGHT, SHEET_SORT_HIT, SHEET_WRAP_MAX_LINES, chromeHeight, columnEdges, columnLetter, visibleBodyRange, visibleColumnRange, wrapLines, } from "./sheet-layout";
|
|
2
|
+
function clipText(ctx, text, x, y, width, height, align = "left") {
|
|
3
|
+
if (width <= 1 || height <= 1)
|
|
4
|
+
return;
|
|
5
|
+
ctx.save();
|
|
6
|
+
ctx.beginPath();
|
|
7
|
+
ctx.rect(x, y, width, height);
|
|
8
|
+
ctx.clip();
|
|
9
|
+
ctx.textAlign = align;
|
|
10
|
+
ctx.textBaseline = "middle";
|
|
11
|
+
const inset = 8;
|
|
12
|
+
const textX = align === "center" ? x + width / 2 : x + inset;
|
|
13
|
+
ctx.fillText(text, textX, y + height / 2);
|
|
14
|
+
ctx.restore();
|
|
15
|
+
}
|
|
16
|
+
function paintCellText(ctx, text, x, y, width, height, wrap) {
|
|
17
|
+
if (!wrap) {
|
|
18
|
+
clipText(ctx, text, x, y, width, height);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (width <= 1 || height <= 1)
|
|
22
|
+
return;
|
|
23
|
+
const inner = Math.max(1, width - SHEET_CELL_PADDING);
|
|
24
|
+
const lines = wrapLines(text, inner, (value) => ctx.measureText(value).width).slice(0, SHEET_WRAP_MAX_LINES);
|
|
25
|
+
ctx.save();
|
|
26
|
+
ctx.beginPath();
|
|
27
|
+
ctx.rect(x, y, width, height);
|
|
28
|
+
ctx.clip();
|
|
29
|
+
ctx.textAlign = "left";
|
|
30
|
+
ctx.textBaseline = "top";
|
|
31
|
+
for (let line = 0; line < lines.length; line += 1) {
|
|
32
|
+
ctx.fillText(lines[line] ?? "", x + 8, y + 6 + line * SHEET_LINE_HEIGHT);
|
|
33
|
+
}
|
|
34
|
+
ctx.restore();
|
|
35
|
+
}
|
|
36
|
+
function hLine(ctx, x, y, width) {
|
|
37
|
+
if (width <= 0)
|
|
38
|
+
return;
|
|
39
|
+
ctx.fillRect(Math.round(x), Math.round(y), Math.round(width), 1);
|
|
40
|
+
}
|
|
41
|
+
function vLine(ctx, x, y, height) {
|
|
42
|
+
if (height <= 0)
|
|
43
|
+
return;
|
|
44
|
+
ctx.fillRect(Math.round(x), Math.round(y), 1, Math.round(height));
|
|
45
|
+
}
|
|
46
|
+
function columnColor(model, col) {
|
|
47
|
+
const colors = model.theme.columns;
|
|
48
|
+
return colors[col % Math.max(colors.length, 1)] || model.theme.text;
|
|
49
|
+
}
|
|
50
|
+
function headerFill(model, col) {
|
|
51
|
+
if (!model.colours)
|
|
52
|
+
return model.theme.header;
|
|
53
|
+
const tints = model.theme.headerTints;
|
|
54
|
+
return tints[col % Math.max(tints.length, 1)] || model.theme.header;
|
|
55
|
+
}
|
|
56
|
+
function fillSelection(ctx, model, x, y, width, height) {
|
|
57
|
+
ctx.fillStyle = model.theme.background;
|
|
58
|
+
ctx.fillRect(x, y, width, height);
|
|
59
|
+
ctx.fillStyle = model.theme.selection;
|
|
60
|
+
ctx.fillRect(x, y, width, height);
|
|
61
|
+
}
|
|
62
|
+
function columnSelected(model, col) {
|
|
63
|
+
const selection = model.selection;
|
|
64
|
+
if (!selection || model.body.length === 0)
|
|
65
|
+
return false;
|
|
66
|
+
return (selection.startIndex === 0 &&
|
|
67
|
+
selection.endIndex >= model.body.length - 1 &&
|
|
68
|
+
col >= selection.startCol &&
|
|
69
|
+
col <= selection.endCol);
|
|
70
|
+
}
|
|
71
|
+
function sheetSelected(model) {
|
|
72
|
+
const selection = model.selection;
|
|
73
|
+
if (!selection || model.body.length === 0 || model.widths.length === 0)
|
|
74
|
+
return false;
|
|
75
|
+
return (selection.startIndex === 0 &&
|
|
76
|
+
selection.endIndex >= model.body.length - 1 &&
|
|
77
|
+
selection.startCol === 0 &&
|
|
78
|
+
selection.endCol >= model.widths.length - 1);
|
|
79
|
+
}
|
|
80
|
+
function headerRowSelected(model) {
|
|
81
|
+
const selection = model.selection;
|
|
82
|
+
if (!selection || !model.headerRow || model.widths.length === 0)
|
|
83
|
+
return false;
|
|
84
|
+
return (selection.startIndex <= -1 &&
|
|
85
|
+
selection.endIndex >= -1 &&
|
|
86
|
+
selection.startCol === 0 &&
|
|
87
|
+
selection.endCol >= model.widths.length - 1);
|
|
88
|
+
}
|
|
89
|
+
function rowSelected(model, index) {
|
|
90
|
+
const selection = model.selection;
|
|
91
|
+
if (!selection || model.widths.length === 0)
|
|
92
|
+
return false;
|
|
93
|
+
return (index >= selection.startIndex &&
|
|
94
|
+
index <= selection.endIndex &&
|
|
95
|
+
selection.startCol === 0 &&
|
|
96
|
+
selection.endCol >= model.widths.length - 1);
|
|
97
|
+
}
|
|
98
|
+
export function paintSheet(canvas, model) {
|
|
99
|
+
const scale = globalThis.devicePixelRatio || 1;
|
|
100
|
+
const width = Math.max(1, Math.round(model.viewportWidth));
|
|
101
|
+
const height = Math.max(1, Math.round(model.viewportHeight));
|
|
102
|
+
if (canvas.width !== Math.round(width * scale) ||
|
|
103
|
+
canvas.height !== Math.round(height * scale)) {
|
|
104
|
+
canvas.width = Math.round(width * scale);
|
|
105
|
+
canvas.height = Math.round(height * scale);
|
|
106
|
+
}
|
|
107
|
+
canvas.style.width = `${width}px`;
|
|
108
|
+
canvas.style.height = `${height}px`;
|
|
109
|
+
const ctx = canvas.getContext("2d");
|
|
110
|
+
if (!ctx)
|
|
111
|
+
return;
|
|
112
|
+
ctx.reset();
|
|
113
|
+
ctx.setTransform(scale, 0, 0, scale, 0, 0);
|
|
114
|
+
const theme = model.theme;
|
|
115
|
+
ctx.clearRect(0, 0, width, height);
|
|
116
|
+
ctx.fillStyle = theme.background;
|
|
117
|
+
ctx.fillRect(0, 0, width, height);
|
|
118
|
+
ctx.font = `13px ${getComputedStyle(canvas).fontFamily || "monospace"}`;
|
|
119
|
+
const edges = columnEdges(model.widths);
|
|
120
|
+
const columns = visibleColumnRange({
|
|
121
|
+
scrollLeft: model.scrollLeft,
|
|
122
|
+
viewportWidth: width,
|
|
123
|
+
widths: model.widths,
|
|
124
|
+
});
|
|
125
|
+
const chrome = chromeHeight(model.headerRow);
|
|
126
|
+
const rows = visibleBodyRange({
|
|
127
|
+
scrollTop: model.scrollTop,
|
|
128
|
+
viewportHeight: height,
|
|
129
|
+
bodyCount: model.body.length,
|
|
130
|
+
offsets: model.rowOffsets,
|
|
131
|
+
});
|
|
132
|
+
const viewX = (documentX) => documentX - model.scrollLeft;
|
|
133
|
+
const rowBox = (index) => {
|
|
134
|
+
if (!model.rowOffsets) {
|
|
135
|
+
return {
|
|
136
|
+
y: chrome + index * SHEET_ROW_HEIGHT - model.scrollTop,
|
|
137
|
+
height: SHEET_ROW_HEIGHT,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const top = model.rowOffsets[index] ?? index * SHEET_ROW_HEIGHT;
|
|
141
|
+
const next = model.rowOffsets[index + 1] ?? top + SHEET_ROW_HEIGHT;
|
|
142
|
+
return {
|
|
143
|
+
y: chrome + top - model.scrollTop,
|
|
144
|
+
height: Math.max(1, next - top),
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
ctx.save();
|
|
148
|
+
ctx.beginPath();
|
|
149
|
+
ctx.rect(SHEET_GUTTER_WIDTH, chrome, Math.max(0, width - SHEET_GUTTER_WIDTH), Math.max(0, height - chrome));
|
|
150
|
+
ctx.clip();
|
|
151
|
+
for (let index = rows.start; index < rows.end; index += 1) {
|
|
152
|
+
const box = rowBox(index);
|
|
153
|
+
const row = model.body[index] ?? [];
|
|
154
|
+
for (let col = columns.start; col < columns.end; col += 1) {
|
|
155
|
+
const x = viewX(edges[col] ?? 0);
|
|
156
|
+
const columnWidth = model.widths[col] ?? 0;
|
|
157
|
+
const selected = model.selection != null &&
|
|
158
|
+
index >= model.selection.startIndex &&
|
|
159
|
+
index <= model.selection.endIndex &&
|
|
160
|
+
col >= model.selection.startCol &&
|
|
161
|
+
col <= model.selection.endCol;
|
|
162
|
+
ctx.fillStyle = selected ? theme.selection : theme.background;
|
|
163
|
+
ctx.fillRect(x, box.y, columnWidth, box.height);
|
|
164
|
+
ctx.fillStyle = model.colours ? columnColor(model, col) : theme.text;
|
|
165
|
+
paintCellText(ctx, row[col] ?? "", x, box.y, columnWidth, box.height, model.wrap);
|
|
166
|
+
ctx.fillStyle = theme.grid;
|
|
167
|
+
hLine(ctx, x, box.y + box.height - 1, columnWidth);
|
|
168
|
+
vLine(ctx, x + columnWidth - 1, box.y, box.height);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
ctx.restore();
|
|
172
|
+
ctx.save();
|
|
173
|
+
ctx.beginPath();
|
|
174
|
+
ctx.rect(SHEET_GUTTER_WIDTH, 0, Math.max(0, width - SHEET_GUTTER_WIDTH), chrome);
|
|
175
|
+
ctx.clip();
|
|
176
|
+
ctx.fillStyle = theme.header;
|
|
177
|
+
ctx.fillRect(SHEET_GUTTER_WIDTH, 0, width, chrome);
|
|
178
|
+
for (let col = columns.start; col < columns.end; col += 1) {
|
|
179
|
+
const x = viewX(edges[col] ?? 0);
|
|
180
|
+
const columnWidth = model.widths[col] ?? 0;
|
|
181
|
+
const picked = columnSelected(model, col);
|
|
182
|
+
const headerPicked = picked || headerRowSelected(model);
|
|
183
|
+
if (picked)
|
|
184
|
+
fillSelection(ctx, model, x, 0, columnWidth, SHEET_LETTER_HEIGHT);
|
|
185
|
+
else {
|
|
186
|
+
ctx.fillStyle = theme.header;
|
|
187
|
+
ctx.fillRect(x, 0, columnWidth, SHEET_LETTER_HEIGHT);
|
|
188
|
+
}
|
|
189
|
+
ctx.fillStyle = theme.muted;
|
|
190
|
+
clipText(ctx, columnLetter(col), x, 0, Math.max(0, columnWidth - SHEET_SORT_HIT), SHEET_LETTER_HEIGHT, "center");
|
|
191
|
+
if (model.headerRow) {
|
|
192
|
+
if (headerPicked) {
|
|
193
|
+
fillSelection(ctx, model, x, SHEET_LETTER_HEIGHT, columnWidth, SHEET_HEADER_HEIGHT);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
ctx.fillStyle = headerFill(model, col);
|
|
197
|
+
ctx.fillRect(x, SHEET_LETTER_HEIGHT, columnWidth, SHEET_HEADER_HEIGHT);
|
|
198
|
+
}
|
|
199
|
+
ctx.fillStyle = model.colours ? columnColor(model, col) : theme.text;
|
|
200
|
+
clipText(ctx, model.headerLabels[col] ?? "", x, SHEET_LETTER_HEIGHT, columnWidth, SHEET_HEADER_HEIGHT);
|
|
201
|
+
}
|
|
202
|
+
ctx.fillStyle = theme.grid;
|
|
203
|
+
vLine(ctx, x + columnWidth - 1, 0, chrome);
|
|
204
|
+
}
|
|
205
|
+
ctx.fillStyle = theme.grid;
|
|
206
|
+
hLine(ctx, SHEET_GUTTER_WIDTH, SHEET_LETTER_HEIGHT - 1, width);
|
|
207
|
+
if (model.headerRow)
|
|
208
|
+
hLine(ctx, SHEET_GUTTER_WIDTH, chrome - 1, width);
|
|
209
|
+
ctx.restore();
|
|
210
|
+
ctx.fillStyle = theme.gutter;
|
|
211
|
+
ctx.fillRect(0, 0, SHEET_GUTTER_WIDTH, height);
|
|
212
|
+
for (let index = rows.start; index < rows.end; index += 1) {
|
|
213
|
+
const box = rowBox(index);
|
|
214
|
+
const bottom = box.y + box.height;
|
|
215
|
+
if (bottom <= chrome)
|
|
216
|
+
continue;
|
|
217
|
+
const top = Math.max(box.y, chrome);
|
|
218
|
+
const picked = rowSelected(model, index);
|
|
219
|
+
ctx.fillStyle = picked ? theme.selection : theme.gutter;
|
|
220
|
+
ctx.fillRect(0, top, SHEET_GUTTER_WIDTH - 1, bottom - top);
|
|
221
|
+
ctx.fillStyle = theme.muted;
|
|
222
|
+
clipText(ctx, model.rowLabels[index] ?? String(index + 1), 0, box.y, SHEET_GUTTER_WIDTH - 1, box.height, "center");
|
|
223
|
+
ctx.fillStyle = theme.grid;
|
|
224
|
+
hLine(ctx, 0, box.y + box.height - 1, SHEET_GUTTER_WIDTH);
|
|
225
|
+
}
|
|
226
|
+
ctx.fillStyle = sheetSelected(model) ? theme.selection : theme.header;
|
|
227
|
+
ctx.fillRect(0, 0, SHEET_GUTTER_WIDTH, chrome);
|
|
228
|
+
if (model.headerRow) {
|
|
229
|
+
if (headerRowSelected(model)) {
|
|
230
|
+
fillSelection(ctx, model, 0, SHEET_LETTER_HEIGHT, SHEET_GUTTER_WIDTH - 1, SHEET_HEADER_HEIGHT);
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
ctx.fillStyle = theme.gutter;
|
|
234
|
+
ctx.fillRect(0, SHEET_LETTER_HEIGHT, SHEET_GUTTER_WIDTH - 1, SHEET_HEADER_HEIGHT);
|
|
235
|
+
}
|
|
236
|
+
ctx.fillStyle = theme.muted;
|
|
237
|
+
clipText(ctx, "1", 0, SHEET_LETTER_HEIGHT, SHEET_GUTTER_WIDTH - 1, SHEET_HEADER_HEIGHT, "center");
|
|
238
|
+
}
|
|
239
|
+
ctx.fillStyle = theme.grid;
|
|
240
|
+
hLine(ctx, 0, SHEET_LETTER_HEIGHT - 1, SHEET_GUTTER_WIDTH);
|
|
241
|
+
if (model.headerRow)
|
|
242
|
+
hLine(ctx, 0, chrome - 1, SHEET_GUTTER_WIDTH);
|
|
243
|
+
vLine(ctx, SHEET_GUTTER_WIDTH - 1, 0, height);
|
|
244
|
+
const outline = (index, col, header, color) => {
|
|
245
|
+
const top = header
|
|
246
|
+
? SHEET_LETTER_HEIGHT
|
|
247
|
+
: rowBox(index).y;
|
|
248
|
+
const x = viewX(edges[col] ?? 0);
|
|
249
|
+
const columnWidth = model.widths[col];
|
|
250
|
+
if (columnWidth == null)
|
|
251
|
+
return;
|
|
252
|
+
const boxWidth = columnWidth;
|
|
253
|
+
ctx.strokeStyle = color;
|
|
254
|
+
ctx.lineWidth = 2;
|
|
255
|
+
ctx.strokeRect(x + 1, top + 1, Math.max(0, boxWidth - 2), (header ? SHEET_HEADER_HEIGHT : rowBox(index).height) - 2);
|
|
256
|
+
ctx.lineWidth = 1;
|
|
257
|
+
};
|
|
258
|
+
const paintOutline = (cell, color) => {
|
|
259
|
+
if (cell.header) {
|
|
260
|
+
outline(cell.index, cell.col, true, color);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
ctx.save();
|
|
264
|
+
ctx.beginPath();
|
|
265
|
+
ctx.rect(SHEET_GUTTER_WIDTH, chrome, Math.max(0, width - SHEET_GUTTER_WIDTH), Math.max(0, height - chrome));
|
|
266
|
+
ctx.clip();
|
|
267
|
+
outline(cell.index, cell.col, false, color);
|
|
268
|
+
ctx.restore();
|
|
269
|
+
};
|
|
270
|
+
if (model.search)
|
|
271
|
+
paintOutline(model.search, theme.search);
|
|
272
|
+
if (model.active)
|
|
273
|
+
paintOutline(model.active, theme.accent);
|
|
274
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const THEME_ATTRIBUTES = [
|
|
2
|
+
"class",
|
|
3
|
+
"style",
|
|
4
|
+
"data-theme",
|
|
5
|
+
"data-mode",
|
|
6
|
+
"data-ui-theme",
|
|
7
|
+
];
|
|
8
|
+
/** Repaint when the document switches between light and dark. */
|
|
9
|
+
export function watchSheetTheme(node, onChange) {
|
|
10
|
+
const doc = node.ownerDocument;
|
|
11
|
+
const observer = new MutationObserver(onChange);
|
|
12
|
+
const options = { attributes: true, attributeFilter: THEME_ATTRIBUTES };
|
|
13
|
+
const seen = new Set();
|
|
14
|
+
for (let current = node; current; current = current.parentNode) {
|
|
15
|
+
if (seen.has(current) || !(current instanceof Element))
|
|
16
|
+
continue;
|
|
17
|
+
seen.add(current);
|
|
18
|
+
observer.observe(current, options);
|
|
19
|
+
}
|
|
20
|
+
const media = doc.defaultView?.matchMedia?.("(prefers-color-scheme: dark)");
|
|
21
|
+
media?.addEventListener("change", onChange);
|
|
22
|
+
return () => {
|
|
23
|
+
observer.disconnect();
|
|
24
|
+
media?.removeEventListener("change", onChange);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { FileView, type TFile, type WorkspaceLeaf } from "@lapis-notes/api";
|
|
2
|
+
import { csvViewMode, type CsvViewMode } from "./csv-view-mode";
|
|
3
|
+
export type { CsvViewMode };
|
|
4
|
+
export { csvViewMode };
|
|
5
|
+
export declare class CsvFileView extends FileView {
|
|
6
|
+
#private;
|
|
7
|
+
constructor(leaf: WorkspaceLeaf);
|
|
8
|
+
getViewType(): string;
|
|
9
|
+
getDisplayText(): string;
|
|
10
|
+
getIcon(): string;
|
|
11
|
+
canAcceptExtension(extension: string): boolean;
|
|
12
|
+
mode(): CsvViewMode;
|
|
13
|
+
showMode(mode: CsvViewMode): void;
|
|
14
|
+
toggleMode(): void;
|
|
15
|
+
showCanvas(): void;
|
|
16
|
+
onLoadFile(file: TFile): Promise<void>;
|
|
17
|
+
onUnloadFile(_file: TFile): Promise<void>;
|
|
18
|
+
onRename(file: TFile): Promise<void>;
|
|
19
|
+
onload(): void;
|
|
20
|
+
onunload(): void;
|
|
21
|
+
private prepareHost;
|
|
22
|
+
private mountSurface;
|
|
23
|
+
private unmountSurface;
|
|
24
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { FileView } from "@lapis-notes/api";
|
|
2
|
+
import { mount, unmount } from "svelte";
|
|
3
|
+
import CsvSourceView from "../components/csv-source-view.svelte";
|
|
4
|
+
import { readDefaultCsvView } from "../csv-view-settings";
|
|
5
|
+
import CsvSheet from "../CsvSheet.svelte";
|
|
6
|
+
import CsvTable from "../CsvTable.svelte";
|
|
7
|
+
import { CSV_EXTENSIONS, CSV_VIEW_TYPE } from "../ids";
|
|
8
|
+
import { csvViewMode } from "./csv-view-mode";
|
|
9
|
+
const tableExtensions = new Set(CSV_EXTENSIONS);
|
|
10
|
+
export { csvViewMode };
|
|
11
|
+
export class CsvFileView extends FileView {
|
|
12
|
+
#component = null;
|
|
13
|
+
constructor(leaf) {
|
|
14
|
+
super(leaf);
|
|
15
|
+
}
|
|
16
|
+
getViewType() {
|
|
17
|
+
return CSV_VIEW_TYPE;
|
|
18
|
+
}
|
|
19
|
+
getDisplayText() {
|
|
20
|
+
return this.file?.basename ?? "CSV";
|
|
21
|
+
}
|
|
22
|
+
getIcon() {
|
|
23
|
+
const mode = this.mode();
|
|
24
|
+
if (mode === "source")
|
|
25
|
+
return "file-code";
|
|
26
|
+
if (mode === "canvas")
|
|
27
|
+
return "grid-3x3";
|
|
28
|
+
return "table";
|
|
29
|
+
}
|
|
30
|
+
canAcceptExtension(extension) {
|
|
31
|
+
return tableExtensions.has(extension.toLowerCase());
|
|
32
|
+
}
|
|
33
|
+
mode() {
|
|
34
|
+
return csvViewMode(this.getState(), readDefaultCsvView(this.app));
|
|
35
|
+
}
|
|
36
|
+
showMode(mode) {
|
|
37
|
+
const file = this.file?.path ?? this.getState()["file"];
|
|
38
|
+
void this.leaf.setViewState({
|
|
39
|
+
type: CSV_VIEW_TYPE,
|
|
40
|
+
state: { ...this.getState(), file, mode },
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
toggleMode() {
|
|
44
|
+
this.showMode(this.mode() === "source" ? readDefaultCsvView(this.app) : "source");
|
|
45
|
+
}
|
|
46
|
+
showCanvas() {
|
|
47
|
+
this.showMode(this.mode() === "canvas" ? "table" : "canvas");
|
|
48
|
+
}
|
|
49
|
+
async onLoadFile(file) {
|
|
50
|
+
this.file = file;
|
|
51
|
+
this.mountSurface();
|
|
52
|
+
}
|
|
53
|
+
async onUnloadFile(_file) {
|
|
54
|
+
this.file = null;
|
|
55
|
+
this.unmountSurface();
|
|
56
|
+
}
|
|
57
|
+
async onRename(file) {
|
|
58
|
+
this.file = file;
|
|
59
|
+
this.mountSurface();
|
|
60
|
+
}
|
|
61
|
+
onload() {
|
|
62
|
+
this.prepareHost();
|
|
63
|
+
}
|
|
64
|
+
onunload() {
|
|
65
|
+
this.unmountSurface();
|
|
66
|
+
}
|
|
67
|
+
prepareHost() {
|
|
68
|
+
Object.assign(this.contentEl.style, {
|
|
69
|
+
display: "flex",
|
|
70
|
+
flexDirection: "column",
|
|
71
|
+
width: "100%",
|
|
72
|
+
height: "100%",
|
|
73
|
+
minWidth: "0",
|
|
74
|
+
minHeight: "0",
|
|
75
|
+
overflow: "hidden",
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
mountSurface() {
|
|
79
|
+
this.prepareHost();
|
|
80
|
+
this.unmountSurface();
|
|
81
|
+
const file = this.file;
|
|
82
|
+
if (!file)
|
|
83
|
+
return;
|
|
84
|
+
if (this.mode() === "source") {
|
|
85
|
+
this.#component = mount(CsvSourceView, {
|
|
86
|
+
target: this.contentEl,
|
|
87
|
+
props: {
|
|
88
|
+
app: this.app,
|
|
89
|
+
file,
|
|
90
|
+
onShowTable: () => this.showMode("table"),
|
|
91
|
+
onShowCanvas: () => this.showMode("canvas"),
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (this.mode() === "canvas") {
|
|
97
|
+
this.#component = mount(CsvSheet, {
|
|
98
|
+
target: this.contentEl,
|
|
99
|
+
props: {
|
|
100
|
+
app: this.app,
|
|
101
|
+
file,
|
|
102
|
+
onShowTable: () => this.showMode("table"),
|
|
103
|
+
onToggleSource: () => this.showMode("source"),
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
this.#component = mount(CsvTable, {
|
|
109
|
+
target: this.contentEl,
|
|
110
|
+
props: {
|
|
111
|
+
app: this.app,
|
|
112
|
+
file,
|
|
113
|
+
role: "file",
|
|
114
|
+
onToggleSource: () => this.showMode("source"),
|
|
115
|
+
onShowCanvas: () => this.showMode("canvas"),
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
unmountSurface() {
|
|
120
|
+
if (!this.#component)
|
|
121
|
+
return;
|
|
122
|
+
void unmount(this.#component);
|
|
123
|
+
this.#component = null;
|
|
124
|
+
}
|
|
125
|
+
}
|