@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,137 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from "@lapis-notes/ui";
|
|
3
|
+
import { tick } from "svelte";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
value,
|
|
7
|
+
multiline = false,
|
|
8
|
+
class: className,
|
|
9
|
+
onContentChange,
|
|
10
|
+
onCommit,
|
|
11
|
+
onNavigate,
|
|
12
|
+
}: {
|
|
13
|
+
value: string;
|
|
14
|
+
multiline?: boolean;
|
|
15
|
+
class?: string;
|
|
16
|
+
onContentChange: (value: string) => void;
|
|
17
|
+
onCommit?: () => void;
|
|
18
|
+
onNavigate?: (direction: "next" | "prev" | "down" | "up") => void;
|
|
19
|
+
} = $props();
|
|
20
|
+
|
|
21
|
+
let editing = $state(false);
|
|
22
|
+
let draft = $state("");
|
|
23
|
+
let editingMultiline = $state(false);
|
|
24
|
+
let inputEl = $state<HTMLInputElement | HTMLTextAreaElement | null>(null);
|
|
25
|
+
|
|
26
|
+
const useMultiline = $derived(
|
|
27
|
+
multiline || editingMultiline || draft.includes("\n"),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
$effect(() => {
|
|
31
|
+
if (!editing) {
|
|
32
|
+
draft = value;
|
|
33
|
+
editingMultiline = false;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
function startEditing() {
|
|
38
|
+
draft = value;
|
|
39
|
+
editingMultiline = multiline || value.includes("\n");
|
|
40
|
+
editing = true;
|
|
41
|
+
tick().then(() => inputEl?.focus());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function commit() {
|
|
45
|
+
if (!editing) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
editing = false;
|
|
49
|
+
editingMultiline = false;
|
|
50
|
+
if (draft !== value) {
|
|
51
|
+
onContentChange(draft);
|
|
52
|
+
}
|
|
53
|
+
onCommit?.();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function insertNewlineAtCursor(event: KeyboardEvent) {
|
|
57
|
+
const target = event.currentTarget as
|
|
58
|
+
| HTMLInputElement
|
|
59
|
+
| HTMLTextAreaElement;
|
|
60
|
+
const start = target.selectionStart ?? draft.length;
|
|
61
|
+
const end = target.selectionEnd ?? draft.length;
|
|
62
|
+
draft = `${draft.slice(0, start)}\n${draft.slice(end)}`;
|
|
63
|
+
editingMultiline = true;
|
|
64
|
+
const cursor = start + 1;
|
|
65
|
+
tick().then(() => {
|
|
66
|
+
inputEl?.focus();
|
|
67
|
+
if (inputEl instanceof HTMLTextAreaElement) {
|
|
68
|
+
inputEl.setSelectionRange(cursor, cursor);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
74
|
+
if (event.key === "Escape") {
|
|
75
|
+
editing = false;
|
|
76
|
+
editingMultiline = false;
|
|
77
|
+
draft = value;
|
|
78
|
+
(event.currentTarget as HTMLElement).blur();
|
|
79
|
+
event.preventDefault();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (event.key === "Enter" && event.shiftKey && !useMultiline) {
|
|
84
|
+
event.preventDefault();
|
|
85
|
+
insertNewlineAtCursor(event);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (event.key === "Enter" && !useMultiline) {
|
|
90
|
+
commit();
|
|
91
|
+
onNavigate?.("down");
|
|
92
|
+
event.preventDefault();
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (event.key === "Tab") {
|
|
97
|
+
commit();
|
|
98
|
+
onNavigate?.(event.shiftKey ? "prev" : "next");
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
</script>
|
|
103
|
+
|
|
104
|
+
{#if editing}
|
|
105
|
+
{#if useMultiline}
|
|
106
|
+
<textarea
|
|
107
|
+
bind:this={inputEl}
|
|
108
|
+
class={cn("csv-cell__input csv-cell__input--multiline", className)}
|
|
109
|
+
bind:value={draft}
|
|
110
|
+
onblur={commit}
|
|
111
|
+
onkeydown={handleKeydown}
|
|
112
|
+
></textarea>
|
|
113
|
+
{:else}
|
|
114
|
+
<input
|
|
115
|
+
bind:this={inputEl}
|
|
116
|
+
class={cn("csv-cell__input", className)}
|
|
117
|
+
type="text"
|
|
118
|
+
bind:value={draft}
|
|
119
|
+
onblur={commit}
|
|
120
|
+
onkeydown={handleKeydown}
|
|
121
|
+
/>
|
|
122
|
+
{/if}
|
|
123
|
+
{:else}
|
|
124
|
+
<button
|
|
125
|
+
type="button"
|
|
126
|
+
class={cn(
|
|
127
|
+
"csv-cell__display",
|
|
128
|
+
multiline && "csv-cell__display--multiline",
|
|
129
|
+
className,
|
|
130
|
+
)}
|
|
131
|
+
title={value}
|
|
132
|
+
onclick={startEditing}
|
|
133
|
+
ondblclick={startEditing}
|
|
134
|
+
>
|
|
135
|
+
{value || "\u00a0"}
|
|
136
|
+
</button>
|
|
137
|
+
{/if}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
value: string;
|
|
3
|
+
multiline?: boolean;
|
|
4
|
+
class?: string;
|
|
5
|
+
onContentChange: (value: string) => void;
|
|
6
|
+
onCommit?: () => void;
|
|
7
|
+
onNavigate?: (direction: "next" | "prev" | "down" | "up") => void;
|
|
8
|
+
};
|
|
9
|
+
declare const CsvCell: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
10
|
+
type CsvCell = ReturnType<typeof CsvCell>;
|
|
11
|
+
export default CsvCell;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { createDraggable, createDroppable } from "@dnd-kit/svelte";
|
|
3
|
+
import * as Table from "@lapismd/design-core/shadcn/table";
|
|
4
|
+
import { cn } from "@lapis-notes/ui";
|
|
5
|
+
import type { Snippet } from "svelte";
|
|
6
|
+
import type { Attachment } from "svelte/attachments";
|
|
7
|
+
import type { CsvTableDragSource } from "./csv-table-dnd";
|
|
8
|
+
import { dropIndicatorClasses } from "./csv-table-dnd";
|
|
9
|
+
|
|
10
|
+
type Props = {
|
|
11
|
+
colIndex: number;
|
|
12
|
+
class?: string;
|
|
13
|
+
dragSource: CsvTableDragSource | null;
|
|
14
|
+
dragOverIndex: number | null;
|
|
15
|
+
selectedClass?: string;
|
|
16
|
+
isDragSource?: boolean;
|
|
17
|
+
children: Snippet<[{ attachColGripHandle: Attachment }]>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
colIndex,
|
|
22
|
+
class: className,
|
|
23
|
+
dragSource,
|
|
24
|
+
dragOverIndex,
|
|
25
|
+
selectedClass,
|
|
26
|
+
isDragSource = false,
|
|
27
|
+
children,
|
|
28
|
+
}: Props = $props();
|
|
29
|
+
|
|
30
|
+
let headRef = $state<HTMLElement | null>(null);
|
|
31
|
+
|
|
32
|
+
const colDraggable = createDraggable({
|
|
33
|
+
get id() {
|
|
34
|
+
return `csv-col:${colIndex}`;
|
|
35
|
+
},
|
|
36
|
+
type: "csv-table-col",
|
|
37
|
+
get data() {
|
|
38
|
+
return { type: "csv-table-col" as const, index: colIndex };
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const colDroppable = createDroppable({
|
|
43
|
+
get id() {
|
|
44
|
+
return `csv-col-drop:head:${colIndex}`;
|
|
45
|
+
},
|
|
46
|
+
accept: "csv-table-col",
|
|
47
|
+
get data() {
|
|
48
|
+
return { type: "csv-table-col" as const, index: colIndex };
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
$effect(() => {
|
|
53
|
+
if (!headRef) return;
|
|
54
|
+
const cleanupDraggable = colDraggable.attach(headRef);
|
|
55
|
+
const cleanupDroppable = colDroppable.attach(headRef);
|
|
56
|
+
return () => {
|
|
57
|
+
cleanupDraggable();
|
|
58
|
+
cleanupDroppable();
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<Table.Head
|
|
64
|
+
bind:ref={headRef}
|
|
65
|
+
data-csv-col-index={colIndex}
|
|
66
|
+
class={cn(
|
|
67
|
+
className,
|
|
68
|
+
selectedClass,
|
|
69
|
+
isDragSource && "is-drag-source is-col-drag-source",
|
|
70
|
+
dropIndicatorClasses(dragSource, dragOverIndex, -1, colIndex),
|
|
71
|
+
)}
|
|
72
|
+
>
|
|
73
|
+
{@render children({
|
|
74
|
+
attachColGripHandle: colDraggable.attachHandle as Attachment,
|
|
75
|
+
})}
|
|
76
|
+
</Table.Head>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { Attachment } from "svelte/attachments";
|
|
3
|
+
import type { CsvTableDragSource } from "./csv-table-dnd";
|
|
4
|
+
type Props = {
|
|
5
|
+
colIndex: number;
|
|
6
|
+
class?: string;
|
|
7
|
+
dragSource: CsvTableDragSource | null;
|
|
8
|
+
dragOverIndex: number | null;
|
|
9
|
+
selectedClass?: string;
|
|
10
|
+
isDragSource?: boolean;
|
|
11
|
+
children: Snippet<[{
|
|
12
|
+
attachColGripHandle: Attachment;
|
|
13
|
+
}]>;
|
|
14
|
+
};
|
|
15
|
+
declare const CsvDataTableColumnHead: import("svelte").Component<Props, {}, "">;
|
|
16
|
+
type CsvDataTableColumnHead = ReturnType<typeof CsvDataTableColumnHead>;
|
|
17
|
+
export default CsvDataTableColumnHead;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import ArrowDownAZIcon from "@lucide/svelte/icons/arrow-down-a-z";
|
|
3
|
+
import ArrowDownZAIcon from "@lucide/svelte/icons/arrow-down-z-a";
|
|
4
|
+
import ArrowLeftIcon from "@lucide/svelte/icons/arrow-left";
|
|
5
|
+
import ArrowRightIcon from "@lucide/svelte/icons/arrow-right";
|
|
6
|
+
import ArrowUpDownIcon from "@lucide/svelte/icons/arrow-up-down";
|
|
7
|
+
import BetweenHorizontalEndIcon from "@lucide/svelte/icons/between-horizontal-end";
|
|
8
|
+
import BetweenHorizontalStartIcon from "@lucide/svelte/icons/between-horizontal-start";
|
|
9
|
+
import ChevronDownIcon from "@lucide/svelte/icons/chevron-down";
|
|
10
|
+
import ChevronUpIcon from "@lucide/svelte/icons/chevron-up";
|
|
11
|
+
import EllipsisVerticalIcon from "@lucide/svelte/icons/ellipsis-vertical";
|
|
12
|
+
import GripHorizontal from "@lucide/svelte/icons/grip-horizontal";
|
|
13
|
+
import Trash2Icon from "@lucide/svelte/icons/trash-2";
|
|
14
|
+
import { Button } from "@lapismd/design-core/shadcn/button";
|
|
15
|
+
import * as DropdownMenu from "@lapismd/design-core/shadcn/dropdown-menu";
|
|
16
|
+
import { cn } from "@lapis-notes/ui";
|
|
17
|
+
import type { Attachment } from "svelte/attachments";
|
|
18
|
+
import CsvCell from "./csv-cell.svelte";
|
|
19
|
+
|
|
20
|
+
type Props = {
|
|
21
|
+
title: string;
|
|
22
|
+
colIndex: number;
|
|
23
|
+
colCount: number;
|
|
24
|
+
isSynthetic?: boolean;
|
|
25
|
+
class?: string;
|
|
26
|
+
attachColGripHandle: Attachment;
|
|
27
|
+
onLabelChange?: (value: string) => void;
|
|
28
|
+
sortDirection?: "asc" | "desc" | null;
|
|
29
|
+
onToggleSort?: () => void;
|
|
30
|
+
onSortAsc?: () => void;
|
|
31
|
+
onSortDesc?: () => void;
|
|
32
|
+
onInsertLeft?: () => void;
|
|
33
|
+
onInsertRight?: () => void;
|
|
34
|
+
onMoveLeft?: () => void;
|
|
35
|
+
onMoveRight?: () => void;
|
|
36
|
+
onDelete?: () => void;
|
|
37
|
+
onSelectColumn?: () => void;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
let {
|
|
41
|
+
title,
|
|
42
|
+
colIndex,
|
|
43
|
+
colCount,
|
|
44
|
+
isSynthetic = false,
|
|
45
|
+
class: className,
|
|
46
|
+
attachColGripHandle,
|
|
47
|
+
onSelectColumn,
|
|
48
|
+
onLabelChange,
|
|
49
|
+
sortDirection = null,
|
|
50
|
+
onToggleSort,
|
|
51
|
+
onSortAsc,
|
|
52
|
+
onSortDesc,
|
|
53
|
+
onInsertLeft,
|
|
54
|
+
onInsertRight,
|
|
55
|
+
onMoveLeft,
|
|
56
|
+
onMoveRight,
|
|
57
|
+
onDelete,
|
|
58
|
+
}: Props = $props();
|
|
59
|
+
|
|
60
|
+
const canMoveLeft = $derived(colIndex > 0);
|
|
61
|
+
const canMoveRight = $derived(colIndex < colCount - 1);
|
|
62
|
+
const sortLabel = $derived(`Sort ${title || `column ${colIndex + 1}`}`);
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<div class={cn("csv-data-table__header", className)}>
|
|
66
|
+
<Button
|
|
67
|
+
variant="ghost"
|
|
68
|
+
size="xs"
|
|
69
|
+
data-grab-handle=""
|
|
70
|
+
class="csv-data-table__col-grip"
|
|
71
|
+
aria-label="Select column"
|
|
72
|
+
onclick={(event) => {
|
|
73
|
+
event.stopPropagation();
|
|
74
|
+
onSelectColumn?.();
|
|
75
|
+
}}
|
|
76
|
+
{@attach attachColGripHandle}
|
|
77
|
+
>
|
|
78
|
+
<GripHorizontal />
|
|
79
|
+
</Button>
|
|
80
|
+
<div class="csv-data-table__header-label">
|
|
81
|
+
<CsvCell
|
|
82
|
+
value={title}
|
|
83
|
+
class="csv-data-table__header-cell-editor"
|
|
84
|
+
onContentChange={(value) => onLabelChange?.(value)}
|
|
85
|
+
/>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="csv-data-table__header-actions">
|
|
88
|
+
<Button
|
|
89
|
+
variant="ghost"
|
|
90
|
+
size="icon"
|
|
91
|
+
class="csv-data-table__header-sort"
|
|
92
|
+
aria-label={sortLabel}
|
|
93
|
+
aria-pressed={sortDirection != null}
|
|
94
|
+
onclick={(event) => {
|
|
95
|
+
event.stopPropagation();
|
|
96
|
+
onToggleSort?.();
|
|
97
|
+
}}
|
|
98
|
+
>
|
|
99
|
+
{#if sortDirection === "asc"}
|
|
100
|
+
<ChevronUpIcon class="csv-data-table__header-icon" />
|
|
101
|
+
{:else if sortDirection === "desc"}
|
|
102
|
+
<ChevronDownIcon class="csv-data-table__header-icon" />
|
|
103
|
+
{:else}
|
|
104
|
+
<ArrowUpDownIcon class="csv-data-table__header-icon" />
|
|
105
|
+
{/if}
|
|
106
|
+
</Button>
|
|
107
|
+
<DropdownMenu.Root>
|
|
108
|
+
<DropdownMenu.Trigger>
|
|
109
|
+
{#snippet child({ props }: { props: Record<string, unknown> })}
|
|
110
|
+
<Button
|
|
111
|
+
{...props}
|
|
112
|
+
class="csv-data-table__header-menu"
|
|
113
|
+
size="icon"
|
|
114
|
+
variant="ghost"
|
|
115
|
+
aria-label="Column actions"
|
|
116
|
+
>
|
|
117
|
+
<EllipsisVerticalIcon class="csv-data-table__header-icon" />
|
|
118
|
+
</Button>
|
|
119
|
+
{/snippet}
|
|
120
|
+
</DropdownMenu.Trigger>
|
|
121
|
+
<DropdownMenu.Content align="start" class="csv-data-table__menu">
|
|
122
|
+
<DropdownMenu.Item onclick={() => onSortAsc?.()}>
|
|
123
|
+
<ArrowDownAZIcon class="csv-data-table__header-icon" />
|
|
124
|
+
Sort A–Z
|
|
125
|
+
</DropdownMenu.Item>
|
|
126
|
+
<DropdownMenu.Item onclick={() => onSortDesc?.()}>
|
|
127
|
+
<ArrowDownZAIcon class="csv-data-table__header-icon" />
|
|
128
|
+
Sort Z–A
|
|
129
|
+
</DropdownMenu.Item>
|
|
130
|
+
<DropdownMenu.Separator />
|
|
131
|
+
<DropdownMenu.Item onclick={() => onMoveLeft?.()} disabled={!canMoveLeft}>
|
|
132
|
+
<ArrowLeftIcon class="csv-data-table__header-icon" />
|
|
133
|
+
Move column left
|
|
134
|
+
</DropdownMenu.Item>
|
|
135
|
+
<DropdownMenu.Item
|
|
136
|
+
onclick={() => onMoveRight?.()}
|
|
137
|
+
disabled={!canMoveRight}
|
|
138
|
+
>
|
|
139
|
+
<ArrowRightIcon class="csv-data-table__header-icon" />
|
|
140
|
+
Move column right
|
|
141
|
+
</DropdownMenu.Item>
|
|
142
|
+
<DropdownMenu.Separator />
|
|
143
|
+
<DropdownMenu.Item onclick={() => onInsertLeft?.()}>
|
|
144
|
+
<BetweenHorizontalStartIcon class="csv-data-table__header-icon" />
|
|
145
|
+
Insert column left
|
|
146
|
+
</DropdownMenu.Item>
|
|
147
|
+
<DropdownMenu.Item onclick={() => onInsertRight?.()}>
|
|
148
|
+
<BetweenHorizontalEndIcon class="csv-data-table__header-icon" />
|
|
149
|
+
Insert column right
|
|
150
|
+
</DropdownMenu.Item>
|
|
151
|
+
<DropdownMenu.Separator />
|
|
152
|
+
<DropdownMenu.Item onclick={() => onDelete?.()}>
|
|
153
|
+
<Trash2Icon class="csv-data-table__header-icon" />
|
|
154
|
+
Delete column
|
|
155
|
+
</DropdownMenu.Item>
|
|
156
|
+
</DropdownMenu.Content>
|
|
157
|
+
</DropdownMenu.Root>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Attachment } from "svelte/attachments";
|
|
2
|
+
type Props = {
|
|
3
|
+
title: string;
|
|
4
|
+
colIndex: number;
|
|
5
|
+
colCount: number;
|
|
6
|
+
isSynthetic?: boolean;
|
|
7
|
+
class?: string;
|
|
8
|
+
attachColGripHandle: Attachment;
|
|
9
|
+
onLabelChange?: (value: string) => void;
|
|
10
|
+
sortDirection?: "asc" | "desc" | null;
|
|
11
|
+
onToggleSort?: () => void;
|
|
12
|
+
onSortAsc?: () => void;
|
|
13
|
+
onSortDesc?: () => void;
|
|
14
|
+
onInsertLeft?: () => void;
|
|
15
|
+
onInsertRight?: () => void;
|
|
16
|
+
onMoveLeft?: () => void;
|
|
17
|
+
onMoveRight?: () => void;
|
|
18
|
+
onDelete?: () => void;
|
|
19
|
+
onSelectColumn?: () => void;
|
|
20
|
+
};
|
|
21
|
+
declare const CsvDataTableColumnHeader: import("svelte").Component<Props, {}, "">;
|
|
22
|
+
type CsvDataTableColumnHeader = ReturnType<typeof CsvDataTableColumnHeader>;
|
|
23
|
+
export default CsvDataTableColumnHeader;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { createDroppable } from "@dnd-kit/svelte";
|
|
3
|
+
import * as Table from "@lapismd/design-core/shadcn/table";
|
|
4
|
+
import { cn } from "@lapis-notes/ui";
|
|
5
|
+
import type { Snippet } from "svelte";
|
|
6
|
+
import type { CsvTableDragSource } from "./csv-table-dnd";
|
|
7
|
+
import { dropIndicatorClasses } from "./csv-table-dnd";
|
|
8
|
+
|
|
9
|
+
type Props = {
|
|
10
|
+
rowIndex: number;
|
|
11
|
+
colIndex: number;
|
|
12
|
+
class?: string;
|
|
13
|
+
dragSource: CsvTableDragSource | null;
|
|
14
|
+
dragOverIndex: number | null;
|
|
15
|
+
selectedClass?: string;
|
|
16
|
+
isColDragSource?: boolean;
|
|
17
|
+
onmousedown?: (event: MouseEvent) => void;
|
|
18
|
+
onmouseenter?: (event: MouseEvent) => void;
|
|
19
|
+
oncontextmenu?: (event: MouseEvent) => void;
|
|
20
|
+
children: Snippet;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
let {
|
|
24
|
+
rowIndex,
|
|
25
|
+
colIndex,
|
|
26
|
+
class: className,
|
|
27
|
+
dragSource,
|
|
28
|
+
dragOverIndex,
|
|
29
|
+
selectedClass,
|
|
30
|
+
isColDragSource = false,
|
|
31
|
+
onmousedown,
|
|
32
|
+
onmouseenter,
|
|
33
|
+
oncontextmenu,
|
|
34
|
+
children,
|
|
35
|
+
}: Props = $props();
|
|
36
|
+
|
|
37
|
+
let cellRef = $state<HTMLElement | null>(null);
|
|
38
|
+
|
|
39
|
+
const rowDroppable = createDroppable({
|
|
40
|
+
get id() {
|
|
41
|
+
return `csv-row-drop:${rowIndex}:${colIndex}`;
|
|
42
|
+
},
|
|
43
|
+
accept: "csv-table-row",
|
|
44
|
+
get data() {
|
|
45
|
+
return { type: "csv-table-row" as const, index: rowIndex };
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
$effect(() => {
|
|
50
|
+
if (!cellRef) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
return rowDroppable.attach(cellRef);
|
|
54
|
+
});
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<Table.Cell
|
|
58
|
+
bind:ref={cellRef}
|
|
59
|
+
data-csv-col-index={colIndex}
|
|
60
|
+
{onmousedown}
|
|
61
|
+
{onmouseenter}
|
|
62
|
+
{oncontextmenu}
|
|
63
|
+
class={cn(
|
|
64
|
+
"csv-data-table__cell",
|
|
65
|
+
className,
|
|
66
|
+
selectedClass,
|
|
67
|
+
dropIndicatorClasses(dragSource, dragOverIndex, rowIndex, colIndex),
|
|
68
|
+
isColDragSource && "is-drag-source is-col-drag-source",
|
|
69
|
+
)}
|
|
70
|
+
>
|
|
71
|
+
{@render children()}
|
|
72
|
+
</Table.Cell>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { CsvTableDragSource } from "./csv-table-dnd";
|
|
3
|
+
type Props = {
|
|
4
|
+
rowIndex: number;
|
|
5
|
+
colIndex: number;
|
|
6
|
+
class?: string;
|
|
7
|
+
dragSource: CsvTableDragSource | null;
|
|
8
|
+
dragOverIndex: number | null;
|
|
9
|
+
selectedClass?: string;
|
|
10
|
+
isColDragSource?: boolean;
|
|
11
|
+
onmousedown?: (event: MouseEvent) => void;
|
|
12
|
+
onmouseenter?: (event: MouseEvent) => void;
|
|
13
|
+
oncontextmenu?: (event: MouseEvent) => void;
|
|
14
|
+
children: Snippet;
|
|
15
|
+
};
|
|
16
|
+
declare const CsvDataTableDataCell: import("svelte").Component<Props, {}, "">;
|
|
17
|
+
type CsvDataTableDataCell = ReturnType<typeof CsvDataTableDataCell>;
|
|
18
|
+
export default CsvDataTableDataCell;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { DragOverlay } from "@dnd-kit/svelte";
|
|
3
|
+
import type { Draggable } from "@dnd-kit/dom";
|
|
4
|
+
import { cn } from "@lapis-notes/ui";
|
|
5
|
+
import type { CsvTableDragData } from "./csv-table-dnd";
|
|
6
|
+
import type { CsvDragOverlayMetrics } from "./csv-table-drag-measure";
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
showRowNumbers: boolean;
|
|
10
|
+
headerLabels: string[];
|
|
11
|
+
metrics: CsvDragOverlayMetrics | null;
|
|
12
|
+
rowValuesForIndex: (rowIndex: number) => string[];
|
|
13
|
+
columnBodyValuesForIndex: (colIndex: number) => string[];
|
|
14
|
+
rainbowClass: (colIndex: number, kind: "head" | "cell") => string;
|
|
15
|
+
displayRowNumberForIndex?: (rowIndex: number) => string | number;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
showRowNumbers,
|
|
20
|
+
headerLabels,
|
|
21
|
+
metrics,
|
|
22
|
+
rowValuesForIndex,
|
|
23
|
+
columnBodyValuesForIndex,
|
|
24
|
+
rainbowClass,
|
|
25
|
+
displayRowNumberForIndex,
|
|
26
|
+
}: Props = $props();
|
|
27
|
+
|
|
28
|
+
function dragData(source: Draggable): CsvTableDragData | null {
|
|
29
|
+
const data = source.data as CsvTableDragData | undefined;
|
|
30
|
+
if (data?.type === "csv-table-col" || data?.type === "csv-table-row") {
|
|
31
|
+
return data;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function rowCellStyle(index: number): string | undefined {
|
|
37
|
+
const width = metrics?.segmentSizes[index];
|
|
38
|
+
if (!width) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
return `width:${width}px;min-width:${width}px;max-width:${width}px`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function columnCellStyle(index: number): string | undefined {
|
|
45
|
+
const height = metrics?.segmentSizes[index];
|
|
46
|
+
const width = metrics?.width;
|
|
47
|
+
if (!height || !width) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
return `width:${width}px;min-width:${width}px;max-width:${width}px;height:${height}px;min-height:${height}px;max-height:${height}px`;
|
|
51
|
+
}
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<DragOverlay>
|
|
55
|
+
{#snippet children(source)}
|
|
56
|
+
{@const data = dragData(source)}
|
|
57
|
+
{#if data?.type === "csv-table-row"}
|
|
58
|
+
{@const values = rowValuesForIndex(data.index)}
|
|
59
|
+
<div
|
|
60
|
+
class="csv-data-table__drag-overlay csv-data-table__drag-overlay--row"
|
|
61
|
+
style={metrics
|
|
62
|
+
? `width:${metrics.width}px;height:${metrics.height}px`
|
|
63
|
+
: undefined}
|
|
64
|
+
role="presentation"
|
|
65
|
+
>
|
|
66
|
+
{#if showRowNumbers}
|
|
67
|
+
<div
|
|
68
|
+
class="csv-data-table__drag-overlay-cell csv-data-table__drag-overlay-cell--gutter"
|
|
69
|
+
style={rowCellStyle(0)}
|
|
70
|
+
>
|
|
71
|
+
{displayRowNumberForIndex?.(data.index) ?? data.index + 1}
|
|
72
|
+
</div>
|
|
73
|
+
{/if}
|
|
74
|
+
{#each values as value, colIndex (colIndex)}
|
|
75
|
+
{@const segmentIndex = showRowNumbers ? colIndex + 1 : colIndex}
|
|
76
|
+
<div
|
|
77
|
+
class={cn(
|
|
78
|
+
"csv-data-table__drag-overlay-cell",
|
|
79
|
+
rainbowClass(colIndex, "cell"),
|
|
80
|
+
)}
|
|
81
|
+
style={rowCellStyle(segmentIndex)}
|
|
82
|
+
>
|
|
83
|
+
{value}
|
|
84
|
+
</div>
|
|
85
|
+
{/each}
|
|
86
|
+
</div>
|
|
87
|
+
{:else if data?.type === "csv-table-col"}
|
|
88
|
+
{@const bodyValues = columnBodyValuesForIndex(data.index)}
|
|
89
|
+
<div
|
|
90
|
+
class="csv-data-table__drag-overlay csv-data-table__drag-overlay--col"
|
|
91
|
+
style={metrics
|
|
92
|
+
? `width:${metrics.width}px;height:${metrics.height}px`
|
|
93
|
+
: undefined}
|
|
94
|
+
role="presentation"
|
|
95
|
+
>
|
|
96
|
+
<div
|
|
97
|
+
class={cn(
|
|
98
|
+
"csv-data-table__drag-overlay-cell csv-data-table__drag-overlay-cell--head csv-data-table__head",
|
|
99
|
+
rainbowClass(data.index, "head"),
|
|
100
|
+
)}
|
|
101
|
+
style={columnCellStyle(0)}
|
|
102
|
+
>
|
|
103
|
+
{headerLabels[data.index] ?? ""}
|
|
104
|
+
</div>
|
|
105
|
+
{#each bodyValues as value, rowIndex (rowIndex)}
|
|
106
|
+
<div
|
|
107
|
+
class={cn(
|
|
108
|
+
"csv-data-table__drag-overlay-cell",
|
|
109
|
+
rainbowClass(data.index, "cell"),
|
|
110
|
+
)}
|
|
111
|
+
style={columnCellStyle(rowIndex + 1)}
|
|
112
|
+
>
|
|
113
|
+
{value}
|
|
114
|
+
</div>
|
|
115
|
+
{/each}
|
|
116
|
+
</div>
|
|
117
|
+
{/if}
|
|
118
|
+
{/snippet}
|
|
119
|
+
</DragOverlay>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CsvDragOverlayMetrics } from "./csv-table-drag-measure";
|
|
2
|
+
type Props = {
|
|
3
|
+
showRowNumbers: boolean;
|
|
4
|
+
headerLabels: string[];
|
|
5
|
+
metrics: CsvDragOverlayMetrics | null;
|
|
6
|
+
rowValuesForIndex: (rowIndex: number) => string[];
|
|
7
|
+
columnBodyValuesForIndex: (colIndex: number) => string[];
|
|
8
|
+
rainbowClass: (colIndex: number, kind: "head" | "cell") => string;
|
|
9
|
+
displayRowNumberForIndex?: (rowIndex: number) => string | number;
|
|
10
|
+
};
|
|
11
|
+
declare const CsvDataTableDragOverlay: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type CsvDataTableDragOverlay = ReturnType<typeof CsvDataTableDragOverlay>;
|
|
13
|
+
export default CsvDataTableDragOverlay;
|