@lapis-notes/csv 0.0.0-stage → 0.2.1

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.
Files changed (105) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/LICENSE.md +4 -0
  3. package/README.md +19 -2
  4. package/dist/app.css +210 -0
  5. package/dist/csv-settings.d.ts +13 -0
  6. package/dist/csv-settings.js +28 -0
  7. package/dist/index.d.ts +7 -0
  8. package/dist/index.js +7 -0
  9. package/dist/lib/CsvSheet.svelte +58 -0
  10. package/dist/lib/CsvSheet.svelte.d.ts +11 -0
  11. package/dist/lib/CsvTable.svelte +58 -0
  12. package/dist/lib/CsvTable.svelte.d.ts +11 -0
  13. package/dist/lib/codemirror/csv-language-extensions.d.ts +3 -0
  14. package/dist/lib/codemirror/csv-language-extensions.js +11 -0
  15. package/dist/lib/codemirror/csv-language.d.ts +3 -0
  16. package/dist/lib/codemirror/csv-language.js +24 -0
  17. package/dist/lib/codemirror/csv-rainbow-theme.d.ts +3 -0
  18. package/dist/lib/codemirror/csv-rainbow-theme.js +15 -0
  19. package/dist/lib/codemirror/csv-source-extensions.d.ts +7 -0
  20. package/dist/lib/codemirror/csv-source-extensions.js +9 -0
  21. package/dist/lib/codemirror/csv.parser.d.ts +2 -0
  22. package/dist/lib/codemirror/csv.parser.js +16 -0
  23. package/dist/lib/codemirror/csv.parser.terms.d.ts +1 -0
  24. package/dist/lib/codemirror/csv.parser.terms.js +2 -0
  25. package/dist/lib/codemirror/syntax.grammar +36 -0
  26. package/dist/lib/components/csv-cell.svelte +137 -0
  27. package/dist/lib/components/csv-cell.svelte.d.ts +11 -0
  28. package/dist/lib/components/csv-data-table-column-head.svelte +76 -0
  29. package/dist/lib/components/csv-data-table-column-head.svelte.d.ts +17 -0
  30. package/dist/lib/components/csv-data-table-column-header.svelte +159 -0
  31. package/dist/lib/components/csv-data-table-column-header.svelte.d.ts +23 -0
  32. package/dist/lib/components/csv-data-table-data-cell.svelte +72 -0
  33. package/dist/lib/components/csv-data-table-data-cell.svelte.d.ts +18 -0
  34. package/dist/lib/components/csv-data-table-drag-overlay.svelte +119 -0
  35. package/dist/lib/components/csv-data-table-drag-overlay.svelte.d.ts +13 -0
  36. package/dist/lib/components/csv-data-table-row-actions.svelte +47 -0
  37. package/dist/lib/components/csv-data-table-row-actions.svelte.d.ts +8 -0
  38. package/dist/lib/components/csv-data-table-row-grip-cell.svelte +102 -0
  39. package/dist/lib/components/csv-data-table-row-grip-cell.svelte.d.ts +19 -0
  40. package/dist/lib/components/csv-data-table-row.svelte +38 -0
  41. package/dist/lib/components/csv-data-table-row.svelte.d.ts +11 -0
  42. package/dist/lib/components/csv-data-table.svelte +903 -0
  43. package/dist/lib/components/csv-data-table.svelte.d.ts +21 -0
  44. package/dist/lib/components/csv-find.svelte +382 -0
  45. package/dist/lib/components/csv-find.svelte.d.ts +24 -0
  46. package/dist/lib/components/csv-row-virtualizer.svelte.d.ts +9 -0
  47. package/dist/lib/components/csv-row-virtualizer.svelte.js +59 -0
  48. package/dist/lib/components/csv-source-view.svelte +127 -0
  49. package/dist/lib/components/csv-source-view.svelte.d.ts +10 -0
  50. package/dist/lib/components/csv-status-bar.svelte +101 -0
  51. package/dist/lib/components/csv-status-bar.svelte.d.ts +14 -0
  52. package/dist/lib/components/csv-table-dnd.d.ts +9 -0
  53. package/dist/lib/components/csv-table-dnd.js +12 -0
  54. package/dist/lib/components/csv-table-drag-measure.d.ts +7 -0
  55. package/dist/lib/components/csv-table-drag-measure.js +38 -0
  56. package/dist/lib/components/csv-table-selection.d.ts +28 -0
  57. package/dist/lib/components/csv-table-selection.js +178 -0
  58. package/dist/lib/components/csv-table-view.svelte +265 -0
  59. package/dist/lib/components/csv-table-view.svelte.d.ts +16 -0
  60. package/dist/lib/csv-plugin.d.ts +9 -0
  61. package/dist/lib/csv-plugin.js +107 -0
  62. package/dist/lib/csv-view-settings.d.ts +43 -0
  63. package/dist/lib/csv-view-settings.js +86 -0
  64. package/dist/lib/ids.d.ts +8 -0
  65. package/dist/lib/ids.js +19 -0
  66. package/dist/lib/model/csv-column-sort.d.ts +16 -0
  67. package/dist/lib/model/csv-column-sort.js +81 -0
  68. package/dist/lib/model/csv-grid.d.ts +31 -0
  69. package/dist/lib/model/csv-grid.js +161 -0
  70. package/dist/lib/model/csv-search.d.ts +38 -0
  71. package/dist/lib/model/csv-search.js +101 -0
  72. package/dist/lib/model/csv-table-model.d.ts +10 -0
  73. package/dist/lib/model/csv-table-model.js +18 -0
  74. package/dist/lib/model/parse-csv.d.ts +10 -0
  75. package/dist/lib/model/parse-csv.js +13 -0
  76. package/dist/lib/model/serialize-csv.d.ts +5 -0
  77. package/dist/lib/model/serialize-csv.js +10 -0
  78. package/dist/lib/sheet/csv-sheet-toolbar.svelte +244 -0
  79. package/dist/lib/sheet/csv-sheet-toolbar.svelte.d.ts +20 -0
  80. package/dist/lib/sheet/csv-sheet-view.svelte +444 -0
  81. package/dist/lib/sheet/csv-sheet-view.svelte.d.ts +18 -0
  82. package/dist/lib/sheet/csv-sheet.svelte +1121 -0
  83. package/dist/lib/sheet/csv-sheet.svelte.d.ts +37 -0
  84. package/dist/lib/sheet/sheet-glyph.svelte +57 -0
  85. package/dist/lib/sheet/sheet-glyph.svelte.d.ts +7 -0
  86. package/dist/lib/sheet/sheet-history.d.ts +14 -0
  87. package/dist/lib/sheet/sheet-history.js +33 -0
  88. package/dist/lib/sheet/sheet-layout.d.ts +121 -0
  89. package/dist/lib/sheet/sheet-layout.js +336 -0
  90. package/dist/lib/sheet/sheet-paint.d.ts +45 -0
  91. package/dist/lib/sheet/sheet-paint.js +274 -0
  92. package/dist/lib/sheet/sheet-theme.d.ts +2 -0
  93. package/dist/lib/sheet/sheet-theme.js +26 -0
  94. package/dist/lib/views/csv-view-mode.d.ts +3 -0
  95. package/dist/lib/views/csv-view-mode.js +6 -0
  96. package/dist/lib/views/csv-view.d.ts +24 -0
  97. package/dist/lib/views/csv-view.js +125 -0
  98. package/dist/styles/csv-find.css +313 -0
  99. package/dist/styles/csv-table.css +882 -0
  100. package/dist/styles.css +37 -0
  101. package/manifest.json +45 -0
  102. package/package.json +86 -3
  103. package/registry-content/overview.md +3 -0
  104. package/registry.json +322 -0
  105. 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;