@interactive-inc/docs-client 0.0.1 → 0.0.2

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 (87) hide show
  1. package/build/index.html +13 -0
  2. package/package.json +5 -2
  3. package/.tanstack/tmp/router-generator-Eq20Yx/50b11a0f488e61cf92364a7e585c1448 +0 -123
  4. package/.tanstack/tmp/router-generator-KQIYSa/aecb094e4891f9475b91de590abc8809 +0 -117
  5. package/.tanstack/tmp/router-generator-ekW2pI/aecb094e4891f9475b91de590abc8809 +0 -117
  6. package/app/components/archived-file-list-view.tsx +0 -99
  7. package/app/components/directory-file-list-view.tsx +0 -32
  8. package/app/components/directory-file-tree-node.tsx +0 -96
  9. package/app/components/directory-layout-sidebar.tsx +0 -181
  10. package/app/components/directory-page-view.tsx +0 -208
  11. package/app/components/directory-table-view.tsx +0 -383
  12. package/app/components/editable-table-cell/boolean-editable-cell.tsx +0 -44
  13. package/app/components/editable-table-cell/multi-boolean-editable-cell.tsx +0 -59
  14. package/app/components/editable-table-cell/multi-number-editable-cell.tsx +0 -62
  15. package/app/components/editable-table-cell/multi-relation-editable-cell.tsx +0 -19
  16. package/app/components/editable-table-cell/multi-select-number-editable-cell.tsx +0 -82
  17. package/app/components/editable-table-cell/multi-select-text-editable-cell.tsx +0 -82
  18. package/app/components/editable-table-cell/multi-text-editable-cell.tsx +0 -62
  19. package/app/components/editable-table-cell/number-editable-cell.tsx +0 -54
  20. package/app/components/editable-table-cell/relation-editable-cell.tsx +0 -20
  21. package/app/components/editable-table-cell/select-number-editable-cell.tsx +0 -43
  22. package/app/components/editable-table-cell/select-text-editable-cell.tsx +0 -33
  23. package/app/components/editable-table-cell/string-editable-cell.tsx +0 -42
  24. package/app/components/editable-table-cell.tsx +0 -129
  25. package/app/components/file-card.tsx +0 -120
  26. package/app/components/file-tree-icon.tsx +0 -26
  27. package/app/components/file-view/archive-restore-banner.tsx +0 -86
  28. package/app/components/file-view/csv-file-view.tsx +0 -50
  29. package/app/components/file-view/default-file-view.tsx +0 -11
  30. package/app/components/file-view/editable-csv-cell.tsx +0 -37
  31. package/app/components/file-view/editable-front-matter-view.tsx +0 -105
  32. package/app/components/file-view/file-csv-columns-view.tsx +0 -181
  33. package/app/components/file-view/file-csv-table.tsx +0 -142
  34. package/app/components/file-view/file-header.tsx +0 -115
  35. package/app/components/file-view/file-page-view.tsx +0 -165
  36. package/app/components/file-view/front-matter-input-field.tsx +0 -157
  37. package/app/components/file-view/front-matter-view.tsx +0 -30
  38. package/app/components/file-view/json-file-editor.tsx +0 -38
  39. package/app/components/file-view/markdown-file-view.tsx +0 -71
  40. package/app/components/file-view/multi-relation-select.tsx +0 -129
  41. package/app/components/file-view/render-front-matter-value.tsx +0 -57
  42. package/app/components/file-view/single-relation-select.tsx +0 -41
  43. package/app/components/file-view/sortable-csv-row.tsx +0 -57
  44. package/app/components/file-view/sortable-header.tsx +0 -30
  45. package/app/components/file-view/utils.ts +0 -8
  46. package/app/components/milestones-editor.tsx +0 -95
  47. package/app/components/page-header.tsx +0 -20
  48. package/app/components/project-view/feature-item.tsx +0 -139
  49. package/app/components/project-view/project-page-group.tsx +0 -125
  50. package/app/components/project-view/project-view.tsx +0 -230
  51. package/app/components/project-view/unlinked-features-section.tsx +0 -73
  52. package/app/components/providers.tsx +0 -21
  53. package/app/components/reload-button.tsx +0 -25
  54. package/app/components/sidebar-button.tsx +0 -13
  55. package/app/components/ui/alert.tsx +0 -66
  56. package/app/components/ui/badge.tsx +0 -46
  57. package/app/components/ui/button.tsx +0 -59
  58. package/app/components/ui/card.tsx +0 -92
  59. package/app/components/ui/dialog.tsx +0 -133
  60. package/app/components/ui/emoji-picker.tsx +0 -133
  61. package/app/components/ui/icons.tsx +0 -3
  62. package/app/components/ui/input.tsx +0 -21
  63. package/app/components/ui/select.tsx +0 -183
  64. package/app/components/ui/separator.tsx +0 -26
  65. package/app/components/ui/sheet.tsx +0 -137
  66. package/app/components/ui/sidebar.tsx +0 -724
  67. package/app/components/ui/simple-sidebar.tsx +0 -50
  68. package/app/components/ui/skeleton.tsx +0 -13
  69. package/app/components/ui/sonner.tsx +0 -23
  70. package/app/components/ui/table.tsx +0 -114
  71. package/app/components/ui/tabs.tsx +0 -53
  72. package/app/components/ui/textarea.tsx +0 -18
  73. package/app/components/ui/tooltip.tsx +0 -59
  74. package/app/components/vscode-button.tsx +0 -23
  75. package/app/hooks/use-directory-query.ts +0 -22
  76. package/app/hooks/use-file-properties-mutation.ts +0 -38
  77. package/app/hooks/use-file-query.ts +0 -21
  78. package/app/hooks/use-mobile.ts +0 -19
  79. package/app/main.css +0 -122
  80. package/app/main.tsx +0 -38
  81. package/app/routes/$.index.tsx +0 -25
  82. package/app/routes/$.tsx +0 -20
  83. package/app/routes/__root.tsx +0 -20
  84. package/app/routes/_index.tsx +0 -17
  85. package/app/routes/apps.client.$.tsx +0 -21
  86. package/components.json +0 -21
  87. package/tsconfig.index.json +0 -17
@@ -1,86 +0,0 @@
1
- import { useMutation } from "@tanstack/react-query"
2
- import { useNavigate } from "@tanstack/react-router"
3
- import { useState } from "react"
4
- import { Button } from "@/components/ui/button"
5
- import { Card } from "@/components/ui/card"
6
- import { apiClient } from "@/lib/api-client"
7
- import { normalizePath } from "@/lib/path-utils"
8
-
9
- type Props = {
10
- filePath: string
11
- onRestore?: () => void
12
- }
13
-
14
- export function ArchiveRestoreBanner(props: Props) {
15
- const navigate = useNavigate()
16
-
17
- const [isRestoring, setIsRestoring] = useState(false)
18
-
19
- const restoreMutation = useMutation({
20
- async mutationFn() {
21
- setIsRestoring(true)
22
-
23
- // パスから docs/ プレフィックスを削除
24
- const normalizedPath = normalizePath(props.filePath)
25
-
26
- // ファイルを復元(アーカイブから移動)
27
- const response = await apiClient.api.files[":path{.+}"].$put({
28
- param: {
29
- path: normalizedPath,
30
- },
31
- json: {
32
- title: null,
33
- properties: null,
34
- content: null,
35
- description: null,
36
- isArchived: false, // アーカイブを解除
37
- },
38
- })
39
-
40
- return response.json()
41
- },
42
- onSuccess: async (_data) => {
43
- // 復元後のパスを構築(_ディレクトリを除外)
44
- const pathSegments = props.filePath.split("/")
45
- const fileName = pathSegments.pop()
46
- const restoredPath = pathSegments
47
- .filter((segment) => segment !== "_")
48
- .concat(fileName || "")
49
- .join("/")
50
-
51
- // コールバックを実行
52
- if (props.onRestore) {
53
- props.onRestore()
54
- }
55
-
56
- // 復元後のファイルパスにリダイレクト
57
- navigate({ to: `/${restoredPath}` })
58
- },
59
- onError: (error) => {
60
- console.error("復元エラー:", error)
61
- alert("ファイルの復元に失敗しました")
62
- },
63
- onSettled: () => {
64
- setIsRestoring(false)
65
- },
66
- })
67
-
68
- return (
69
- <Card className="p-2">
70
- <div className="flex items-center justify-between gap-2">
71
- <div className="flex items-center gap-x-2">
72
- <span className="text-muted-foreground text-sm">
73
- このファイルはアーカイブされています
74
- </span>
75
- </div>
76
- <Button
77
- size="sm"
78
- onClick={() => restoreMutation.mutate()}
79
- disabled={isRestoring || restoreMutation.isPending}
80
- >
81
- {isRestoring ? "復元中..." : "復元する"}
82
- </Button>
83
- </div>
84
- </Card>
85
- )
86
- }
@@ -1,50 +0,0 @@
1
- import { useState } from "react"
2
- import { FileCsvColumns } from "@/components/file-view/file-csv-columns-view"
3
- import { FileCsvTable } from "@/components/file-view/file-csv-table"
4
- import { FileHeader } from "@/components/file-view/file-header"
5
- import { Button } from "@/components/ui/button"
6
-
7
- export type Props = {
8
- filePath: string
9
- fileData: {
10
- path: string
11
- title: string | null
12
- }
13
- cwd: string
14
- content: string
15
- onChange(content: string): void
16
- onReload: () => void
17
- isLoading: boolean
18
- }
19
-
20
- export function CsvFileView(props: Props) {
21
- const [isColumnEditMode, setIsColumnEditMode] = useState(false)
22
-
23
- const toggleColumnEditMode = () => {
24
- setIsColumnEditMode(!isColumnEditMode)
25
- }
26
-
27
- return (
28
- <div className="space-y-2">
29
- <FileHeader
30
- filePath={props.filePath}
31
- fileData={props.fileData}
32
- cwd={props.cwd}
33
- onReload={props.onReload}
34
- isLoading={props.isLoading}
35
- >
36
- <Button
37
- onClick={toggleColumnEditMode}
38
- variant={isColumnEditMode ? "default" : "secondary"}
39
- >
40
- {"カラム"}
41
- </Button>
42
- </FileHeader>
43
- {isColumnEditMode ? (
44
- <FileCsvColumns content={props.content} onChange={props.onChange} />
45
- ) : (
46
- <FileCsvTable content={props.content} onChange={props.onChange} />
47
- )}
48
- </div>
49
- )
50
- }
@@ -1,11 +0,0 @@
1
- type Props = {
2
- content: string
3
- }
4
-
5
- export function DefaultFileViewer(props: Props) {
6
- return (
7
- <div className="rounded bg-gray-800 p-4 text-white">
8
- <pre className="whitespace-pre-wrap">{props.content}</pre>
9
- </div>
10
- )
11
- }
@@ -1,37 +0,0 @@
1
- import { useRef, useState } from "react"
2
- import { TableCell } from "@/components/ui/table"
3
-
4
- type Props = {
5
- content: string
6
- rowIndex: number
7
- columnIndex: number
8
- onCellChange: (rowIndex: number, columnIndex: number, value: string) => void
9
- }
10
-
11
- export function EditableCsvCell(props: Props) {
12
- const [value, setValue] = useState(props.content)
13
- const cellRef = useRef<HTMLTableCellElement>(null)
14
-
15
- function handleBlur() {
16
- if (value !== props.content) {
17
- props.onCellChange(props.rowIndex, props.columnIndex, value)
18
- }
19
- }
20
-
21
- function handleInput(event: React.FormEvent<HTMLTableCellElement>) {
22
- const newValue = event.currentTarget.textContent || ""
23
- setValue(newValue)
24
- }
25
-
26
- return (
27
- <TableCell
28
- ref={cellRef}
29
- contentEditable
30
- suppressContentEditableWarning
31
- onBlur={handleBlur}
32
- onInput={handleInput}
33
- >
34
- {props.content}
35
- </TableCell>
36
- )
37
- }
@@ -1,105 +0,0 @@
1
- import { Card } from "@/components/ui/card"
2
- import type { DocRelation, DocSchemaRecord, DocFileMdFrontMatter } from "@/lib/types"
3
- import { cn } from "@/lib/utils"
4
- import { FrontMatterInputField } from "./front-matter-input-field"
5
-
6
- type Props = {
7
- frontMatter: DocFileMdFrontMatter | null
8
- onUpdate?: (key: string, value: unknown) => void
9
- schema?: DocSchemaRecord
10
- relations?: DocRelation[]
11
- }
12
-
13
- /**
14
- * Front-matterを編集可能に表示するコンポーネント
15
- */
16
- export function EditableFrontMatterView(props: Props) {
17
- const frontMatter = props.frontMatter
18
- // front-matterが空の場合は表示しない
19
- if (!frontMatter || Object.keys(frontMatter).length === 0) {
20
- return null
21
- }
22
-
23
- const handleValueChange = (key: string, newValue: string) => {
24
- if (!props.onUpdate) {
25
- console.warn("onUpdate is not provided to EditableFrontMatterView")
26
- return
27
- }
28
-
29
- const originalValue = frontMatter[key]
30
-
31
- // 元の型に応じて値を変換
32
- let convertedValue: unknown = newValue
33
-
34
- if (typeof originalValue === "boolean") {
35
- convertedValue = newValue === "true"
36
- } else if (typeof originalValue === "number") {
37
- const numValue = Number(newValue)
38
- convertedValue = Number.isNaN(numValue) ? originalValue : numValue
39
- } else if (Array.isArray(originalValue)) {
40
- // 配列の場合はカンマ区切りで分割
41
- const items = newValue
42
- .split(",")
43
- .map((v) => v.trim())
44
- .filter((v) => v !== "")
45
- if (originalValue.length > 0 && typeof originalValue[0] === "number") {
46
- convertedValue = items
47
- .map((v) => Number(v))
48
- .filter((v) => !Number.isNaN(v))
49
- } else if (
50
- originalValue.length > 0 &&
51
- typeof originalValue[0] === "boolean"
52
- ) {
53
- convertedValue = items.map((v) => v === "true")
54
- } else {
55
- convertedValue = items
56
- }
57
- } else if (originalValue === null && Array.isArray(newValue)) {
58
- // null値だが新しい値が配列の場合(配列リレーションなど)
59
- convertedValue = newValue
60
- }
61
-
62
- props.onUpdate(key, convertedValue)
63
- }
64
-
65
- const handleBlur = (key: string, value: string) => {
66
- handleValueChange(key, value)
67
- }
68
-
69
- return (
70
- <Card className="gap-4 rounded-md p-2">
71
- <div className="grid grid-cols-1 gap-3 md:grid-cols-2">
72
- {Object.entries(frontMatter).map(([key, value]) => {
73
- const schemaField = props.schema?.[key]
74
- const relationData = props.relations?.find((rel) => {
75
- return (
76
- rel.path ===
77
- (schemaField && "path" in schemaField ? schemaField.path : null)
78
- )
79
- })
80
-
81
- // multi-系のフィールドは2カラムスペースを使用
82
- const isMultiField = schemaField?.type?.startsWith("multi-")
83
- const columnSpan = isMultiField ? "md:col-span-2" : ""
84
-
85
- return (
86
- <div key={key} className={cn("flex flex-col gap-1", columnSpan)}>
87
- <span className="font-medium text-sm">{key}</span>
88
- <div className="flex-1">
89
- <FrontMatterInputField
90
- fieldKey={key}
91
- value={value}
92
- originalValue={frontMatter[key]}
93
- onValueChange={handleValueChange}
94
- onBlur={handleBlur}
95
- schemaField={schemaField}
96
- relationOptions={relationData?.files}
97
- />
98
- </div>
99
- </div>
100
- )
101
- })}
102
- </div>
103
- </Card>
104
- )
105
- }
@@ -1,181 +0,0 @@
1
- import { ArrowDown, ArrowUp, Check, Plus, XCircle } from "lucide-react"
2
- import { type FormEvent, useEffect, useState } from "react"
3
- import { Button } from "@/components/ui/button"
4
- import { Card } from "@/components/ui/card"
5
- import { Input } from "@/components/ui/input"
6
- import { OpenCsv } from "@/lib/open-csv/open-csv"
7
- import { cn } from "@/lib/utils"
8
-
9
- type Props = {
10
- content: string
11
- onChange(content: string): void
12
- }
13
-
14
- export function FileCsvColumns(props: Props) {
15
- const [csvInstance, setCsvInstance] = useState<OpenCsv>(
16
- new OpenCsv(props.content),
17
- )
18
-
19
- const [editingHeader, setEditingHeader] = useState<{
20
- index: number
21
- value: string
22
- } | null>(null)
23
-
24
- const [newColumnName, setNewColumnName] = useState("")
25
-
26
- // propsの変更を検知してCSVインスタンスを更新
27
- useEffect(() => {
28
- setCsvInstance(new OpenCsv(props.content))
29
- }, [props.content])
30
-
31
- // カラム名の編集モードを開始
32
- const startEditingHeader = (index: number, headerName: string) => {
33
- setEditingHeader({ index, value: headerName })
34
- }
35
-
36
- // カラム名の編集を確定
37
- const confirmHeaderEdit = () => {
38
- if (editingHeader && editingHeader.value.trim() !== "") {
39
- const newCsvInstance = csvInstance.renameColumn(
40
- editingHeader.index,
41
- editingHeader.value,
42
- )
43
- updateCsvAndNotify(newCsvInstance)
44
- setEditingHeader(null)
45
- }
46
- }
47
-
48
- // カラムを削除
49
- const removeColumn = (index: number) => {
50
- const newCsvInstance = csvInstance.removeColumn(index)
51
- updateCsvAndNotify(newCsvInstance)
52
- }
53
-
54
- // カラムを上に移動
55
- const moveColumnUp = (index: number) => {
56
- if (index > 0) {
57
- const newCsvInstance = csvInstance.moveColumn(index, index - 1)
58
- updateCsvAndNotify(newCsvInstance)
59
- }
60
- }
61
-
62
- // カラムを下に移動
63
- const moveColumnDown = (index: number) => {
64
- if (index < csvInstance.headers.length - 1) {
65
- const newCsvInstance = csvInstance.moveColumn(index, index + 1)
66
- updateCsvAndNotify(newCsvInstance)
67
- }
68
- }
69
-
70
- // 新しいカラムを追加
71
- const addNewColumn = (event?: FormEvent) => {
72
- if (event) {
73
- event.preventDefault()
74
- }
75
-
76
- if (newColumnName.trim() !== "") {
77
- const newCsvInstance = csvInstance.addColumn(newColumnName)
78
- updateCsvAndNotify(newCsvInstance)
79
- setNewColumnName("")
80
- }
81
- }
82
-
83
- // CSVインスタンスを更新し、親コンポーネントに通知
84
- const updateCsvAndNotify = (newCsvInstance: OpenCsv) => {
85
- setCsvInstance(newCsvInstance)
86
- if (props.onChange) {
87
- props.onChange(newCsvInstance.toString())
88
- }
89
- }
90
-
91
- return (
92
- <Card className="h-full max-h-fit max-w-none gap-0 rounded-md p-0">
93
- <div className="flex items-center justify-end border-b p-2">
94
- <form className="flex items-center space-x-2" onSubmit={addNewColumn}>
95
- <Input
96
- className="h-9 w-40"
97
- onChange={(e) => setNewColumnName(e.target.value)}
98
- placeholder="新しいカラム名"
99
- value={newColumnName}
100
- />
101
- <Button
102
- disabled={newColumnName.trim() === ""}
103
- size="sm"
104
- type="submit"
105
- >
106
- <Plus className="mr-1 h-4 w-4" />
107
- {"追加"}
108
- </Button>
109
- </form>
110
- </div>
111
- <div className="flex flex-col gap-2 p-2">
112
- {csvInstance.headers.map((header, headerIndex) => (
113
- <Card
114
- className={"relative gap-0 p-2"}
115
- key={`column-${header}-${headerIndex.toFixed()}`}
116
- >
117
- <div className="flex items-center space-x-2">
118
- <Input
119
- autoFocus={editingHeader?.index === headerIndex}
120
- className="h-8"
121
- onChange={(e) =>
122
- editingHeader?.index === headerIndex
123
- ? setEditingHeader({
124
- ...editingHeader,
125
- value: e.target.value,
126
- })
127
- : startEditingHeader(headerIndex, e.target.value)
128
- }
129
- value={
130
- editingHeader?.index === headerIndex
131
- ? editingHeader.value
132
- : header
133
- }
134
- />
135
- {editingHeader?.index === headerIndex && (
136
- <Button
137
- className="h-8 w-8"
138
- onClick={confirmHeaderEdit}
139
- size="icon"
140
- variant="ghost"
141
- >
142
- <Check className="h-4 w-4" />
143
- </Button>
144
- )}
145
- <Button
146
- className="h-7 w-7"
147
- disabled={headerIndex === 0}
148
- onClick={() => moveColumnUp(headerIndex)}
149
- size="icon"
150
- variant="outline"
151
- >
152
- <ArrowUp className="h-3 w-3" />
153
- </Button>
154
- <Button
155
- className="h-7 w-7"
156
- disabled={headerIndex === csvInstance.headers.length - 1}
157
- onClick={() => moveColumnDown(headerIndex)}
158
- size="icon"
159
- variant="outline"
160
- >
161
- <ArrowDown className="h-3 w-3" />
162
- </Button>
163
- <Button
164
- className={cn(
165
- "h-7 w-7",
166
- csvInstance.headers.length <= 1 && "opacity-50",
167
- )}
168
- disabled={csvInstance.headers.length <= 1}
169
- onClick={() => removeColumn(headerIndex)}
170
- size="icon"
171
- variant="outline"
172
- >
173
- <XCircle className="h-3 w-3" />
174
- </Button>
175
- </div>
176
- </Card>
177
- ))}
178
- </div>
179
- </Card>
180
- )
181
- }
@@ -1,142 +0,0 @@
1
- import type { DragEndEvent } from "@dnd-kit/core"
2
- import {
3
- closestCenter,
4
- DndContext,
5
- KeyboardSensor,
6
- PointerSensor,
7
- useSensor,
8
- useSensors,
9
- } from "@dnd-kit/core"
10
- import { restrictToVerticalAxis } from "@dnd-kit/modifiers"
11
- import {
12
- arrayMove,
13
- SortableContext,
14
- verticalListSortingStrategy,
15
- } from "@dnd-kit/sortable"
16
- import { useState } from "react"
17
- import { Card } from "@/components/ui/card"
18
- import {
19
- Table,
20
- TableBody,
21
- TableHead,
22
- TableHeader,
23
- TableRow,
24
- } from "@/components/ui/table"
25
- import { OpenCsv } from "@/lib/open-csv/open-csv"
26
- import { SortableCsvRow } from "./sortable-csv-row"
27
-
28
- type Props = {
29
- content: string
30
- onChange(content: string): void
31
- }
32
-
33
- export function FileCsvTable(props: Props) {
34
- const [csvData, setCsvData] = useState(new OpenCsv(props.content))
35
-
36
- const sensors = useSensors(
37
- useSensor(PointerSensor, {
38
- activationConstraint: {
39
- distance: 5,
40
- },
41
- }),
42
- useSensor(KeyboardSensor),
43
- )
44
-
45
- function handleDragEnd(event: DragEndEvent) {
46
- const active = event.active
47
- const over = event.over
48
-
49
- if (over && active.id !== over.id) {
50
- const activeId = String(active.id)
51
- const overId = String(over.id)
52
-
53
- const activeSplit = activeId.split("-")
54
- const overSplit = overId.split("-")
55
-
56
- if (activeSplit.length < 2 || overSplit.length < 2) {
57
- return
58
- }
59
-
60
- const activeIndex = activeSplit[1]
61
- const overIndex = overSplit[1]
62
-
63
- if (activeIndex === undefined || overIndex === undefined) {
64
- return
65
- }
66
-
67
- const activeRowIndex = Number.parseInt(activeIndex, 10) + 1 // +1 for header row
68
- const overRowIndex = Number.parseInt(overIndex, 10) + 1
69
-
70
- const newRecords = arrayMove(
71
- csvData.records,
72
- activeRowIndex - 1,
73
- overRowIndex - 1,
74
- )
75
-
76
- // Create a new CSV string to initialize a new OpenCsv instance
77
- const headers = csvData.headers
78
- if (headers.length > 0) {
79
- const newData = [headers, ...newRecords.map((row) => [...row])]
80
- const newCsvString = newData.map((row) => row.join(",")).join("\n")
81
- setCsvData(new OpenCsv(newCsvString))
82
-
83
- // 親コンポーネントに変更を通知
84
- props.onChange(newCsvString)
85
- }
86
- }
87
- } // Create unique and stable ids for each row
88
- const rowIds = csvData.records.map((_, idx) => `row-${idx}`)
89
-
90
- function handleCellUpdate(
91
- rowIndex: number,
92
- columnIndex: number,
93
- value: string,
94
- ) {
95
- const newCsvData = csvData.updateCell(rowIndex + 1, columnIndex, value) // +1 for header row
96
- const newCsvString = newCsvData.toString()
97
- setCsvData(newCsvData)
98
- props.onChange(newCsvString)
99
- }
100
-
101
- return (
102
- <Card className="h-full max-h-fit max-w-none overflow-hidden rounded-md p-0">
103
- <Table className="w-full border-collapse">
104
- <TableHeader>
105
- <TableRow>
106
- <TableHead />
107
- {csvData.headers.map((header: string, index: number) => {
108
- const headerKey = `header-${index}-${header}`
109
- return <TableHead key={headerKey}>{header}</TableHead>
110
- })}
111
- </TableRow>
112
- </TableHeader>
113
- <DndContext
114
- sensors={sensors}
115
- collisionDetection={closestCenter}
116
- onDragEnd={handleDragEnd}
117
- modifiers={[restrictToVerticalAxis]}
118
- >
119
- <TableBody>
120
- <SortableContext
121
- items={rowIds}
122
- strategy={verticalListSortingStrategy}
123
- >
124
- {csvData.records.map((row: string[], rowIndex: number) => {
125
- const rowId = `row-${rowIndex}`
126
- return (
127
- <SortableCsvRow
128
- key={rowId}
129
- id={rowId}
130
- row={row}
131
- rowIndex={rowIndex}
132
- onCellUpdate={handleCellUpdate}
133
- />
134
- )
135
- })}
136
- </SortableContext>
137
- </TableBody>
138
- </DndContext>
139
- </Table>
140
- </Card>
141
- )
142
- }