@tscircuit/runframe 0.0.11 → 0.0.12

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 (44) hide show
  1. package/README.md +31 -1
  2. package/package.json +10 -2
  3. package/.github/workflows/bun-formatcheck.yml +0 -26
  4. package/.github/workflows/bun-pver-release.yml +0 -25
  5. package/.github/workflows/bun-typecheck.yml +0 -26
  6. package/biome.json +0 -57
  7. package/bun.lockb +0 -0
  8. package/cosmos.config.json +0 -3
  9. package/cosmos.decorator.tsx +0 -3
  10. package/examples/resistor.fixture.tsx +0 -12
  11. package/examples/runframe-with-api-1.fixture.tsx +0 -41
  12. package/examples/runframe1-basic.fixture.tsx +0 -19
  13. package/index.html +0 -13
  14. package/lib/components/BomTable.tsx +0 -69
  15. package/lib/components/CircuitJsonPreview.tsx +0 -348
  16. package/lib/components/CircuitJsonTableViewer/CircuitJsonTableViewer.tsx +0 -315
  17. package/lib/components/CircuitJsonTableViewer/ClickableText.tsx +0 -20
  18. package/lib/components/CircuitJsonTableViewer/HeaderCell.tsx +0 -26
  19. package/lib/components/CircuitJsonTableViewer/Modal.tsx +0 -38
  20. package/lib/components/ErrorFallback.tsx +0 -25
  21. package/lib/components/ErrorTabContent.tsx +0 -86
  22. package/lib/components/PcbViewerWithContainerHeight.tsx +0 -47
  23. package/lib/components/PreviewEmptyState.tsx +0 -14
  24. package/lib/components/RunFrame.tsx +0 -68
  25. package/lib/components/RunFrameWithApi/index.tsx +0 -44
  26. package/lib/components/RunFrameWithApi/standalone.tsx +0 -6
  27. package/lib/components/RunFrameWithApi/store.ts +0 -122
  28. package/lib/components/RunFrameWithApi/types.ts +0 -43
  29. package/lib/components/ui/button.tsx +0 -57
  30. package/lib/components/ui/dropdown-menu.tsx +0 -203
  31. package/lib/components/ui/tabs.tsx +0 -53
  32. package/lib/dev/render-to-circuit-json.ts +0 -7
  33. package/lib/hooks/use-styles.ts +0 -17
  34. package/lib/preview.ts +0 -1
  35. package/lib/runner.ts +0 -2
  36. package/lib/utils/index.ts +0 -6
  37. package/lib/utils/pcbManualEditEventHandler.ts +0 -156
  38. package/postcss.config.js +0 -8
  39. package/renovate.json +0 -15
  40. package/scripts/build-css.ts +0 -26
  41. package/src/main.tsx +0 -13
  42. package/tailwind.config.js +0 -3
  43. package/tsconfig.json +0 -45
  44. package/vite.config.ts +0 -72
@@ -1,348 +0,0 @@
1
- import {
2
- Tabs,
3
- TabsContent,
4
- TabsList,
5
- TabsTrigger,
6
- } from "lib/components/ui/tabs"
7
- import { cn } from "lib/utils"
8
- import { applyPcbEditEvents } from "lib/utils/pcbManualEditEventHandler"
9
- import { CadViewer } from "@tscircuit/3d-viewer"
10
- import { PCBViewer } from "@tscircuit/pcb-viewer"
11
- import { useEffect, useState } from "react"
12
- import { ErrorFallback } from "./ErrorFallback"
13
- import { ErrorBoundary } from "react-error-boundary"
14
- import { ErrorTabContent } from "./ErrorTabContent"
15
- import { SchematicViewer } from "@tscircuit/schematic-viewer"
16
- import PreviewEmptyState from "./PreviewEmptyState"
17
- import { CircuitJsonTableViewer } from "./CircuitJsonTableViewer/CircuitJsonTableViewer"
18
- import { BomTable } from "./BomTable"
19
- import {
20
- CheckIcon,
21
- EllipsisIcon,
22
- EllipsisVerticalIcon,
23
- FullscreenIcon,
24
- MinimizeIcon,
25
- } from "lucide-react"
26
- import {
27
- DropdownMenu,
28
- DropdownMenuContent,
29
- DropdownMenuItem,
30
- DropdownMenuTrigger,
31
- } from "./ui/dropdown-menu"
32
- import { Button } from "./ui/button"
33
- import { PcbViewerWithContainerHeight } from "./PcbViewerWithContainerHeight"
34
- import { useStyles } from "lib/hooks/use-styles"
35
-
36
- export interface PreviewContentProps {
37
- code?: string
38
- readOnly?: boolean
39
- onRunClicked?: () => void
40
- tsxRunTriggerCount?: number
41
- errorMessage?: string | null
42
- circuitJson: any
43
- circuitJsonKey?: string
44
- className?: string
45
- showCodeTab?: boolean
46
- codeTabContent?: React.ReactNode
47
- showJsonTab?: boolean
48
- showImportAndFormatButtons?: boolean
49
- headerClassName?: string
50
- leftHeaderContent?: React.ReactNode
51
- isRunningCode?: boolean
52
- isStreaming?: boolean
53
- onToggleFullScreen?: () => void
54
- isFullScreen?: boolean
55
- onCodeChange?: (code: string) => void
56
- onDtsChange?: (dts: string) => void
57
- manualEditsFileContent?: string
58
- hasCodeChangedSinceLastRun?: boolean
59
- onManualEditsFileContentChange?: (newmanualEditsFileContent: string) => void
60
- }
61
-
62
- export const CircuitJsonPreview = ({
63
- code,
64
- onRunClicked = () => {},
65
- tsxRunTriggerCount,
66
- errorMessage,
67
- circuitJsonKey = "",
68
- circuitJson,
69
- showCodeTab = false,
70
- codeTabContent,
71
- showJsonTab = true,
72
- showImportAndFormatButtons = true,
73
- className,
74
- headerClassName,
75
- leftHeaderContent,
76
- readOnly,
77
- isStreaming,
78
- onToggleFullScreen,
79
- isFullScreen,
80
- isRunningCode,
81
- hasCodeChangedSinceLastRun,
82
- onCodeChange,
83
- onDtsChange,
84
- manualEditsFileContent,
85
- onManualEditsFileContentChange,
86
- }: PreviewContentProps) => {
87
- useStyles()
88
- const [activeTab, setActiveTab] = useState(showCodeTab ? "code" : "pcb")
89
-
90
- useEffect(() => {
91
- if (errorMessage) {
92
- setActiveTab("error")
93
- }
94
- }, [errorMessage])
95
-
96
- useEffect(() => {
97
- if (activeTab === "code" && circuitJson && !errorMessage) {
98
- setActiveTab("pcb")
99
- }
100
- }, [circuitJson])
101
-
102
- return (
103
- <div className={cn("flex flex-col relative", className)}>
104
- <div className="md:sticky md:top-2">
105
- <Tabs
106
- value={activeTab}
107
- onValueChange={setActiveTab}
108
- className="flex-grow flex flex-col p-2"
109
- >
110
- <div className={cn("flex items-center gap-2", headerClassName)}>
111
- {leftHeaderContent}
112
- {leftHeaderContent && <div className="flex-grow" />}
113
- {/* <RunButton
114
- onClick={() => triggerRunTsx()}
115
- disabled={!hasCodeChangedSinceLastRun && tsxRunTriggerCount !== 0}
116
- isRunningCode={isRunningCode}
117
- /> */}
118
- {!leftHeaderContent && <div className="flex-grow" />}
119
- <TabsList>
120
- {showCodeTab && <TabsTrigger value="code">Code</TabsTrigger>}
121
- <TabsTrigger value="pcb" className="whitespace-nowrap">
122
- {circuitJson && (
123
- <span
124
- className={cn(
125
- "inline-flex items-center justify-center w-2 h-2 mr-1 text-xs font-bold text-white rounded-full",
126
- !hasCodeChangedSinceLastRun
127
- ? "bg-blue-500"
128
- : "bg-gray-500",
129
- )}
130
- />
131
- )}
132
- PCB
133
- </TabsTrigger>
134
- <TabsTrigger value="schematic" className="whitespace-nowrap">
135
- {circuitJson && (
136
- <span
137
- className={cn(
138
- "inline-flex items-center justify-center w-2 h-2 mr-1 text-xs font-bold text-white rounded-full",
139
- !hasCodeChangedSinceLastRun
140
- ? "bg-blue-500"
141
- : "bg-gray-500",
142
- )}
143
- />
144
- )}
145
- Schematic
146
- </TabsTrigger>
147
- <TabsTrigger value="cad">
148
- {circuitJson && (
149
- <span
150
- className={cn(
151
- "inline-flex items-center justify-center w-2 h-2 mr-1 text-xs font-bold text-white rounded-full",
152
- !hasCodeChangedSinceLastRun
153
- ? "bg-blue-500"
154
- : "bg-gray-500",
155
- )}
156
- />
157
- )}
158
- 3D
159
- </TabsTrigger>
160
- <DropdownMenu>
161
- <DropdownMenuTrigger asChild>
162
- <div className="whitespace-nowrap p-2 mr-1 cursor-pointer relative">
163
- <EllipsisIcon className="w-4 h-4" />
164
- {errorMessage && (
165
- <span className="inline-flex absolute top-[6px] right-[4px] items-center justify-center w-1 h-1 ml-2 text-[8px] font-bold text-white bg-red-500 rounded-full" />
166
- )}
167
- </div>
168
- </DropdownMenuTrigger>
169
- <DropdownMenuContent className="*:text-xs">
170
- <DropdownMenuItem
171
- onSelect={() => setActiveTab("error")}
172
- className="flex"
173
- >
174
- <CheckIcon
175
- className={cn(
176
- "w-3 h-3 mr-2",
177
- activeTab !== "error" && "invisible",
178
- )}
179
- />
180
- <div className="flex-grow">Errors</div>
181
- {errorMessage && (
182
- <span className="inline-flex items-center justify-center w-3 h-3 ml-2 text-[8px] font-bold text-white bg-red-500 rounded-full">
183
- 1
184
- </span>
185
- )}
186
- </DropdownMenuItem>
187
- <DropdownMenuItem onSelect={() => setActiveTab("bom")}>
188
- <CheckIcon
189
- className={cn(
190
- "w-3 h-3 mr-2",
191
- activeTab !== "bom" && "invisible",
192
- )}
193
- />
194
- Bill of Materials
195
- </DropdownMenuItem>
196
- <DropdownMenuItem
197
- onSelect={() => setActiveTab("circuitjson")}
198
- >
199
- <CheckIcon
200
- className={cn(
201
- "w-3 h-3 mr-2",
202
- activeTab !== "circuitjson" && "invisible",
203
- )}
204
- />
205
- JSON
206
- </DropdownMenuItem>
207
- </DropdownMenuContent>
208
- </DropdownMenu>
209
- </TabsList>
210
- {onToggleFullScreen && (
211
- <Button onClick={onToggleFullScreen} variant="ghost">
212
- {isFullScreen ? (
213
- <MinimizeIcon size={16} />
214
- ) : (
215
- <FullscreenIcon size={16} />
216
- )}
217
- </Button>
218
- )}
219
- </div>
220
- {showCodeTab && (
221
- <TabsContent value="code" className="flex-grow overflow-hidden">
222
- <div className="h-full">{codeTabContent}</div>
223
- </TabsContent>
224
- )}
225
-
226
- <TabsContent value="pcb">
227
- <div
228
- className={cn(
229
- "overflow-hidden",
230
- isFullScreen ? "h-[calc(100vh-96px)]" : "h-[620px]",
231
- )}
232
- >
233
- <ErrorBoundary fallback={<div>Error loading PCB viewer</div>}>
234
- {circuitJson ? (
235
- <PcbViewerWithContainerHeight
236
- key={circuitJsonKey}
237
- soup={circuitJson}
238
- containerClassName={cn(
239
- "h-full w-full",
240
- isFullScreen
241
- ? "min-h-[calc(100vh-240px)]"
242
- : "min-h-[620px]",
243
- )}
244
- // onEditEventsChanged={(editEvents) => {
245
- // if (editEvents.some((editEvent) => editEvent.in_progress))
246
- // return
247
- // // Update state with new edit events
248
- // const newManualEditsFileContent = applyPcbEditEvents({
249
- // editEvents,
250
- // circuitJson,
251
- // manualEditsFileContent,
252
- // })
253
- // onManualEditsFileContentChange?.(
254
- // JSON.stringify(newManualEditsFileContent, null, 2),
255
- // )
256
- // }}
257
- />
258
- ) : (
259
- <PreviewEmptyState onRunClicked={onRunClicked} />
260
- )}
261
- </ErrorBoundary>
262
- </div>
263
- </TabsContent>
264
-
265
- <TabsContent value="schematic">
266
- <div
267
- className={cn(
268
- "overflow-auto",
269
- isFullScreen ? "h-[calc(100vh-96px)]" : "h-[620px]",
270
- )}
271
- >
272
- <ErrorBoundary fallback={<div>Error loading Schematic</div>}>
273
- {circuitJson ? (
274
- <SchematicViewer
275
- circuitJson={circuitJson}
276
- containerStyle={{
277
- height: "100%",
278
- }}
279
- />
280
- ) : (
281
- <PreviewEmptyState onRunClicked={onRunClicked} />
282
- )}
283
- </ErrorBoundary>
284
- </div>
285
- </TabsContent>
286
-
287
- <TabsContent value="cad">
288
- <div
289
- className={cn(
290
- "overflow-auto",
291
- isFullScreen ? "h-[calc(100vh-96px)]" : "h-[620px]",
292
- )}
293
- >
294
- <ErrorBoundary FallbackComponent={ErrorFallback}>
295
- {circuitJson ? (
296
- <CadViewer soup={circuitJson as any} />
297
- ) : (
298
- <PreviewEmptyState onRunClicked={onRunClicked} />
299
- )}
300
- </ErrorBoundary>
301
- </div>
302
- </TabsContent>
303
-
304
- <TabsContent value="bom">
305
- <div
306
- className={cn(
307
- "overflow-auto",
308
- isFullScreen ? "h-[calc(100vh-96px)]" : "h-[620px]",
309
- )}
310
- >
311
- <ErrorBoundary fallback={<div>Error loading BOM</div>}>
312
- {circuitJson ? (
313
- <BomTable circuitJson={circuitJson} />
314
- ) : (
315
- <PreviewEmptyState onRunClicked={onRunClicked} />
316
- )}
317
- </ErrorBoundary>
318
- </div>
319
- </TabsContent>
320
-
321
- <TabsContent value="circuitjson">
322
- <div
323
- className={cn(
324
- "overflow-auto",
325
- isFullScreen ? "h-[calc(100vh-96px)]" : "h-[620px]",
326
- )}
327
- >
328
- <ErrorBoundary fallback={<div>Error loading JSON viewer</div>}>
329
- {circuitJson ? (
330
- <CircuitJsonTableViewer elements={circuitJson as any} />
331
- ) : (
332
- <PreviewEmptyState onRunClicked={onRunClicked} />
333
- )}
334
- </ErrorBoundary>
335
- </div>
336
- </TabsContent>
337
- <TabsContent value="error">
338
- {circuitJson || errorMessage ? (
339
- <ErrorTabContent code={code} errorMessage={errorMessage} />
340
- ) : (
341
- <PreviewEmptyState onRunClicked={onRunClicked} />
342
- )}
343
- </TabsContent>
344
- </Tabs>
345
- </div>
346
- </div>
347
- )
348
- }
@@ -1,315 +0,0 @@
1
- import { useReducer, useState } from "react"
2
- import { ClickableText } from "./ClickableText"
3
- import { HeaderCell } from "./HeaderCell"
4
- import Modal from "./Modal"
5
-
6
- type Filters = {
7
- component_type_filter?:
8
- | "any"
9
- | "source"
10
- | "source/pcb"
11
- | "source/schematic"
12
- | string
13
- id_search?: string
14
- name_search?: string
15
- selector_search?: string
16
- focused_id?: string
17
- }
18
-
19
- type CommonProps = { name?: string; type: string }
20
-
21
- type ModalState =
22
- | { open: false; element?: never }
23
- | { open: true; element: Element; title: string }
24
-
25
- interface Element {
26
- [key: string]: any
27
- type: string
28
- name?: string
29
- }
30
-
31
- interface ProcessedElement extends CommonProps {
32
- primary_id: string
33
- other_ids: { [key: string]: string }
34
- selector_path?: string
35
- _og_elm: Element
36
- }
37
-
38
- interface Column {
39
- key: string
40
- name: string
41
- renderCell?: (row: ProcessedElement) => React.ReactNode
42
- renderHeaderCell?: (col: Column) => React.ReactNode
43
- }
44
-
45
- export const CircuitJsonTableViewer: React.FC<{ elements: Element[] }> = ({
46
- elements,
47
- }) => {
48
- const [modal, setModal] = useState<ModalState>({ open: false })
49
- const [filters, setFilter] = useReducer(
50
- (s: Filters, a: Filters) => ({
51
- ...s,
52
- ...a,
53
- }),
54
- {},
55
- )
56
-
57
- const element_types = [...new Set(elements.map((e) => e.type))]
58
-
59
- // Process elements to separate primary and non-primary ids
60
- const elements2: ProcessedElement[] = elements.map((e) => {
61
- const primary_id = e[`${e.type}_id`]
62
-
63
- const other_ids = Object.fromEntries(
64
- Object.entries(e).filter(([k]) => {
65
- if (k === `${e.type}_id`) return false
66
- if (!k.endsWith("_id")) return false
67
- return true
68
- }),
69
- ) as { [key: string]: string }
70
-
71
- const other_props: CommonProps = Object.fromEntries(
72
- Object.entries(e).filter(([k]) => !k.endsWith("_id")),
73
- ) as CommonProps
74
-
75
- return {
76
- primary_id,
77
- other_ids,
78
- ...other_props,
79
- _og_elm: e,
80
- }
81
- })
82
-
83
- const elements3 = elements2.map((e) => {
84
- let selector_path = ""
85
-
86
- const getSelectorPath = (e2: ProcessedElement): string => {
87
- const parent_key = Object.keys(e2.other_ids).find((k) =>
88
- k.startsWith("source_"),
89
- )
90
- if (!parent_key) return `.${e2.name}`
91
- const parent_type = parent_key.slice(0, -3) // trim "_id"
92
-
93
- const parent = elements2.find(
94
- (p) =>
95
- p.type === parent_type && p.primary_id === e2.other_ids[parent_key],
96
- )
97
-
98
- if (!parent) return `??? > .${e2.name}`
99
-
100
- if (!("name" in parent)) return `#${parent.primary_id} > .${e2.name}`
101
-
102
- return `${getSelectorPath(parent)} > .${e2.name}`
103
- }
104
-
105
- if ("name" in e) {
106
- selector_path = getSelectorPath(e)
107
- }
108
-
109
- return {
110
- ...e,
111
- selector_path,
112
- }
113
- })
114
-
115
- const columns: Column[] = [
116
- {
117
- key: "primary_id",
118
- name: "primary_id",
119
- renderCell: (row: ProcessedElement) => (
120
- <div className="flex items-center">
121
- <ClickableText
122
- text={row.primary_id}
123
- onClick={() =>
124
- setFilter({
125
- focused_id: row.primary_id,
126
- id_search: undefined,
127
- selector_search: undefined,
128
- })
129
- }
130
- />
131
- <span className="flex-grow" />
132
- <ClickableText
133
- text="(JSON)"
134
- onClick={() =>
135
- setModal({
136
- open: true,
137
- element: row._og_elm,
138
- title: row.primary_id,
139
- })
140
- }
141
- />
142
- </div>
143
- ),
144
- renderHeaderCell: (col: Column) => (
145
- <HeaderCell
146
- column={col}
147
- onTextChange={(v) => setFilter({ id_search: v })}
148
- field={
149
- !filters.focused_id
150
- ? undefined
151
- : () => (
152
- <div>
153
- Focus:{" "}
154
- <span className="underline">{filters.focused_id}</span>
155
- <ClickableText
156
- text="(unfocus)"
157
- onClick={() => setFilter({ focused_id: undefined })}
158
- />
159
- </div>
160
- )
161
- }
162
- />
163
- ),
164
- },
165
- {
166
- key: "type",
167
- name: "type",
168
- renderHeaderCell: (col: Column) => (
169
- <HeaderCell
170
- column={col}
171
- field={() => (
172
- <select
173
- onChange={(e) =>
174
- setFilter({ component_type_filter: e.target.value })
175
- }
176
- className="border rounded p-1 w-full"
177
- >
178
- <option key="any" value="any">
179
- any
180
- </option>
181
- <option key="source" value="source">
182
- source
183
- </option>
184
- <option key="source/pcb" value="source/pcb">
185
- source/pcb
186
- </option>
187
- <option key="source/schematic" value="source/schematic">
188
- source/schematic
189
- </option>
190
- {element_types.map((type) => (
191
- <option key={type} value={type}>
192
- {type}
193
- </option>
194
- ))}
195
- </select>
196
- )}
197
- />
198
- ),
199
- },
200
- {
201
- key: "name",
202
- name: "name",
203
- renderHeaderCell: (col: Column) => (
204
- <HeaderCell
205
- column={col}
206
- onTextChange={(t) => setFilter({ name_search: t })}
207
- />
208
- ),
209
- },
210
- {
211
- key: "selector_path",
212
- name: "selector_path",
213
- renderHeaderCell: (col: Column) => (
214
- <HeaderCell
215
- column={col}
216
- onTextChange={(t) => setFilter({ selector_search: t })}
217
- />
218
- ),
219
- },
220
- {
221
- key: "other_ids",
222
- name: "other_ids",
223
- renderCell: (row: ProcessedElement) => (
224
- <div className="space-x-2">
225
- {Object.entries(row.other_ids).map(([other_id, v]) => (
226
- <ClickableText
227
- key={v}
228
- text={v}
229
- onClick={() => setFilter({ focused_id: v })}
230
- />
231
- ))}
232
- </div>
233
- ),
234
- },
235
- ]
236
-
237
- const elements4 = elements3
238
- .filter((e) => {
239
- if (!filters.name_search) return true
240
- return e.name?.toLowerCase()?.includes(filters.name_search.toLowerCase())
241
- })
242
- .filter((e) => {
243
- if (!filters.component_type_filter) return true
244
- if (filters.component_type_filter === "any") return true
245
- if (filters.component_type_filter === "source") {
246
- return e.type.startsWith("source_")
247
- }
248
- if (filters.component_type_filter === "source/pcb") {
249
- return e.type.startsWith("source_") || e.type.startsWith("pcb_")
250
- }
251
- if (filters.component_type_filter === "source/schematic") {
252
- return e.type.startsWith("source_") || e.type.startsWith("schematic_")
253
- }
254
- return e.type?.includes(filters.component_type_filter)
255
- })
256
- .filter((e) => {
257
- if (!filters.selector_search) return true
258
- const parts = filters.selector_search
259
- .split(" ")
260
- .filter((p) => p.length > 0)
261
- return parts.every((part) => e.selector_path?.includes(part))
262
- })
263
- .filter((e) => {
264
- if (!filters.id_search) return true
265
- return e.primary_id?.includes(filters.id_search)
266
- })
267
- .filter((e) => {
268
- if (!filters.focused_id) return true
269
- if (e.primary_id === filters.focused_id) return true
270
- if (Object.values(e.other_ids).includes(filters.focused_id)) return true
271
- return false
272
- })
273
-
274
- return (
275
- <div className="font-mono text-xs">
276
- <div className="overflow-x-auto">
277
- <table className="table-auto w-full text-left">
278
- <thead>
279
- <tr>
280
- {columns.map((col) => (
281
- <th key={col.key} className="px-4 py-2 border-b">
282
- {col.renderHeaderCell ? col.renderHeaderCell(col) : col.name}
283
- </th>
284
- ))}
285
- </tr>
286
- </thead>
287
- <tbody>
288
- {elements4.map((row, rowIndex) => (
289
- <tr key={rowIndex} className="hover:bg-gray-100">
290
- {columns.map((col) => (
291
- <td key={col.key} className="px-4 py-2 border-b">
292
- {col.renderCell
293
- ? col.renderCell(row)
294
- : (row as any)[col.key]}
295
- </td>
296
- ))}
297
- </tr>
298
- ))}
299
- </tbody>
300
- </table>
301
- </div>
302
- <Modal
303
- open={modal.open}
304
- onClose={() => setModal({ open: false })}
305
- title={modal.open ? modal.title : ""}
306
- >
307
- <div className="bg-gray-800 p-3 text-white rounded">
308
- <pre className="whitespace-pre-wrap">
309
- {modal.open ? JSON.stringify(modal.element, null, 2) : ""}
310
- </pre>
311
- </div>
312
- </Modal>
313
- </div>
314
- )
315
- }
@@ -1,20 +0,0 @@
1
- import type React from "react"
2
-
3
- interface ClickableTextProps {
4
- text: string
5
- onClick: () => void
6
- }
7
-
8
- export const ClickableText: React.FC<ClickableTextProps> = ({
9
- text,
10
- onClick,
11
- }) => {
12
- return (
13
- <span
14
- className="cursor-pointer underline text-blue-300 mx-2"
15
- onClick={onClick}
16
- >
17
- {text}
18
- </span>
19
- )
20
- }
@@ -1,26 +0,0 @@
1
- import type React from "react"
2
-
3
- interface HeaderCellProps {
4
- column: { name: string }
5
- field?: () => React.ReactNode
6
- onTextChange?: (value: string) => void
7
- }
8
-
9
- export const HeaderCell: React.FC<HeaderCellProps> = (p) => {
10
- return (
11
- <div className="leading-5">
12
- <div className="py-2 font-bold">{p.column.name}</div>
13
- <div>
14
- {p.field?.() ?? (
15
- <input
16
- type="text"
17
- className="border rounded p-1 w-full"
18
- onChange={(e) => {
19
- p.onTextChange?.(e.target.value)
20
- }}
21
- />
22
- )}
23
- </div>
24
- </div>
25
- )
26
- }