11import {
22 ArrowUturnLeftIcon ,
3- CodeBracketIcon ,
43 PencilSquareIcon ,
54 PlusIcon ,
5+ VariableIcon ,
66 ViewColumnsIcon ,
7+ XMarkIcon ,
78} from "@heroicons/react/20/solid" ;
89import { GripVerticalIcon } from "lucide-react" ;
910import { useMemo , useState } from "react" ;
@@ -16,9 +17,9 @@ import { useOptimisticLocation } from "~/hooks/useOptimisticLocation";
1617import { useSearchParams } from "~/hooks/useSearchParam" ;
1718import { cn } from "~/utils/cn" ;
1819import {
19- availableStandardColumns ,
2020 encodeColumnLayout ,
2121 resolveColumnLayout ,
22+ type LayoutColumn ,
2223 type ResolvedColumn ,
2324 type RunColumnRuntime ,
2425 type SmartColumnDef ,
@@ -54,54 +55,55 @@ export function RunsDisplayOptions() {
5455 [ cols . join ( " " ) , sc . join ( " " ) , runtime . isManagedCloud , runtime . isDevelopment ]
5556 ) ;
5657
57- const available = availableStandardColumns ( runtime ) ;
58- const visibleStandardCount = layout . visible . filter ( ( c ) => c . kind === "standard" ) . length ;
58+ const totalCount = layout . ordered . filter ( ( o ) => o . col . kind === "standard" ) . length ;
59+ const shownCount = layout . ordered . filter ( ( o ) => o . col . kind === "standard" && ! o . hidden ) . length ;
5960
60- const applyVisible = ( nextVisible : ResolvedColumn [ ] ) => {
61- const encoded = encodeColumnLayout ( nextVisible , runtime ) ;
61+ const applyLayout = ( next : LayoutColumn [ ] ) => {
62+ const encoded = encodeColumnLayout ( next , runtime ) ;
6263 replace ( {
6364 cols : encoded . cols . length > 0 ? encoded . cols : undefined ,
6465 sc : encoded . sc . length > 0 ? encoded . sc : undefined ,
6566 } ) ;
6667 } ;
6768
68- const hideStandard = ( id : string ) => {
69- applyVisible ( layout . visible . filter ( ( c ) => ! ( c . kind === "standard" && c . def . id === id ) ) ) ;
70- } ;
71-
72- const showStandard = ( id : string ) => {
73- const def = available . find ( ( c ) => c . id === id ) ;
74- if ( ! def ) return ;
75- applyVisible ( [ ...layout . visible , { kind : "standard" , def } ] ) ;
69+ const toggleHidden = ( key : string ) => {
70+ applyLayout (
71+ layout . ordered . map ( ( o ) => ( keyFor ( o . col ) === key ? { ...o , hidden : ! o . hidden } : o ) )
72+ ) ;
7673 } ;
7774
7875 const removeSmart = ( index : number ) => {
79- applyVisible ( layout . visible . filter ( ( c ) => ! ( c . kind === "smart" && c . index === index ) ) ) ;
76+ applyLayout ( layout . ordered . filter ( ( o ) => ! ( o . col . kind === "smart" && o . col . index === index ) ) ) ;
8077 } ;
8178
8279 const submitSmart = ( def : SmartColumnDef ) => {
8380 if ( editing ) {
84- applyVisible (
85- layout . visible . map ( ( c ) =>
86- c . kind === "smart" && c . index === editing . index ? { ...c , def } : c
81+ applyLayout (
82+ layout . ordered . map ( ( o ) =>
83+ o . col . kind === "smart" && o . col . index === editing . index
84+ ? { ...o , col : { ...o . col , def } }
85+ : o
8786 )
8887 ) ;
8988 } else {
90- applyVisible ( [ ...layout . visible , { kind : "smart" , index : layout . smartColumns . length , def } ] ) ;
89+ applyLayout ( [
90+ ...layout . ordered ,
91+ { col : { kind : "smart" , index : layout . smartColumns . length , def } , hidden : false } ,
92+ ] ) ;
9193 }
9294 } ;
9395
9496 const reset = ( ) => replace ( { cols : undefined , sc : undefined } ) ;
9597
9698 const reorder = ( fromKey : string , toKey : string ) => {
9799 if ( fromKey === toKey ) return ;
98- const arr = [ ...layout . visible ] ;
99- const from = arr . findIndex ( ( c ) => keyFor ( c ) === fromKey ) ;
100- const to = arr . findIndex ( ( c ) => keyFor ( c ) === toKey ) ;
100+ const arr = [ ...layout . ordered ] ;
101+ const from = arr . findIndex ( ( o ) => keyFor ( o . col ) === fromKey ) ;
102+ const to = arr . findIndex ( ( o ) => keyFor ( o . col ) === toKey ) ;
101103 if ( from < 0 || to < 0 ) return ;
102104 const [ moved ] = arr . splice ( from , 1 ) ;
103105 arr . splice ( to , 0 , moved ) ;
104- applyVisible ( arr ) ;
106+ applyLayout ( arr ) ;
105107 } ;
106108
107109 const endDrag = ( ) => {
@@ -121,49 +123,37 @@ export function RunsDisplayOptions() {
121123 < div className = "flex items-center justify-between px-3 py-2" >
122124 < span className = "text-xs font-medium text-text-dimmed" > Columns</ span >
123125 < span className = "text-xs text-text-dimmed" >
124- { visibleStandardCount } of { available . length }
126+ { shownCount } of { totalCount }
125127 </ span >
126128 </ div >
127129 < div className = "max-h-80 overflow-y-auto border-y border-grid-dimmed" >
128- { layout . visible . map ( ( col ) => (
129- < ColumnRow
130- key = { keyFor ( col ) }
131- col = { col }
132- checked
133- draggable
134- locked = { col . kind === "standard" && ! ! col . def . locked }
135- dragging = { dragKey === keyFor ( col ) }
136- isOver = { overKey === keyFor ( col ) && dragKey !== keyFor ( col ) }
137- onDragStart = { ( ) => setDragKey ( keyFor ( col ) ) }
138- onDragEnter = { ( ) => setOverKey ( keyFor ( col ) ) }
139- onDragEnd = { endDrag }
140- onDrop = { ( ) => {
141- if ( dragKey ) reorder ( dragKey , keyFor ( col ) ) ;
142- endDrag ( ) ;
143- } }
144- onToggle = { ( ) => {
145- if ( col . kind === "smart" ) removeSmart ( col . index ) ;
146- else if ( ! col . def . locked ) hideStandard ( col . def . id ) ;
147- } }
148- onEdit = {
149- col . kind === "smart"
150- ? ( ) => setEditing ( { index : col . index , def : col . def } )
151- : undefined
152- }
153- />
154- ) ) }
155- { layout . hiddenStandard . map ( ( def ) => (
156- < ColumnRow
157- key = { `std:${ def . id } ` }
158- col = { { kind : "standard" , def } }
159- checked = { false }
160- draggable = { false }
161- locked = { false }
162- dragging = { false }
163- isOver = { false }
164- onToggle = { ( ) => showStandard ( def . id ) }
165- />
166- ) ) }
130+ { layout . ordered . map ( ( { col, hidden } ) => {
131+ const key = keyFor ( col ) ;
132+ return (
133+ < ColumnRow
134+ key = { key }
135+ col = { col }
136+ checked = { ! hidden }
137+ locked = { col . kind === "standard" && ! ! col . def . locked }
138+ dragging = { dragKey === key }
139+ isOver = { overKey === key && dragKey !== key }
140+ onDragStart = { ( ) => setDragKey ( key ) }
141+ onDragEnter = { ( ) => setOverKey ( key ) }
142+ onDragEnd = { endDrag }
143+ onDrop = { ( ) => {
144+ if ( dragKey ) reorder ( dragKey , key ) ;
145+ endDrag ( ) ;
146+ } }
147+ onToggle = { ( ) => toggleHidden ( key ) }
148+ onEdit = {
149+ col . kind === "smart"
150+ ? ( ) => setEditing ( { index : col . index , def : col . def } )
151+ : undefined
152+ }
153+ onRemove = { col . kind === "smart" ? ( ) => removeSmart ( col . index ) : undefined }
154+ />
155+ ) ;
156+ } ) }
167157 </ div >
168158 < div className = "flex flex-col p-1" >
169159 < button
@@ -205,29 +195,29 @@ export function RunsDisplayOptions() {
205195function ColumnRow ( {
206196 col,
207197 checked,
208- draggable,
209198 locked,
210199 dragging,
211200 isOver,
212201 onToggle,
213202 onEdit,
203+ onRemove,
214204 onDragStart,
215205 onDragEnter,
216206 onDragEnd,
217207 onDrop,
218208} : {
219209 col : ResolvedColumn ;
220210 checked : boolean ;
221- draggable : boolean ;
222211 locked : boolean ;
223212 dragging : boolean ;
224213 isOver : boolean ;
225214 onToggle : ( ) => void ;
226215 onEdit ?: ( ) => void ;
227- onDragStart ?: ( ) => void ;
228- onDragEnter ?: ( ) => void ;
229- onDragEnd ?: ( ) => void ;
230- onDrop ?: ( ) => void ;
216+ onRemove ?: ( ) => void ;
217+ onDragStart : ( ) => void ;
218+ onDragEnter : ( ) => void ;
219+ onDragEnd : ( ) => void ;
220+ onDrop : ( ) => void ;
231221} ) {
232222 const isSmart = col . kind === "smart" ;
233223
@@ -237,18 +227,16 @@ function ColumnRow({
237227 "relative flex h-8 items-center gap-2 px-3 transition-colors hover:bg-charcoal-750" ,
238228 dragging && "opacity-40"
239229 ) }
240- draggable = { draggable }
230+ draggable
241231 onDragStart = { onDragStart }
242232 onDragEnter = { onDragEnter }
243233 onDragEnd = { onDragEnd }
244- onDragOver = { ( e ) => {
245- if ( draggable ) e . preventDefault ( ) ;
246- } }
234+ onDragOver = { ( e ) => e . preventDefault ( ) }
247235 onDrop = { onDrop }
248236 >
249237 { isOver && < div className = "absolute inset-x-0 top-0 h-0.5 bg-blue-500" /> }
250238 { locked ? < Checkbox checked disabled /> : < Checkbox checked = { checked } onChange = { onToggle } /> }
251- { isSmart && < CodeBracketIcon className = "size-4 flex-none text-text-dimmed" /> }
239+ { isSmart && < VariableIcon className = "size-4 flex-none text-text-dimmed" /> }
252240 < span
253241 className = { cn ( "flex-1 truncate text-sm" , checked ? "text-text-bright" : "text-text-dimmed" ) }
254242 >
@@ -264,11 +252,17 @@ function ColumnRow({
264252 < PencilSquareIcon className = "size-3.5" />
265253 </ button >
266254 ) }
267- { draggable ? (
268- < GripVerticalIcon className = "size-4 cursor-grab text-text-dimmed active:cursor-grabbing" />
269- ) : (
270- < div className = "size-4" />
255+ { onRemove && (
256+ < button
257+ type = "button"
258+ onClick = { onRemove }
259+ aria-label = { `Remove ${ col . def . label } ` }
260+ className = "flex size-5 items-center justify-center rounded text-text-dimmed transition-colors hover:text-error focus-custom"
261+ >
262+ < XMarkIcon className = "size-3.5" />
263+ </ button >
271264 ) }
265+ < GripVerticalIcon className = "size-4 cursor-grab text-text-dimmed active:cursor-grabbing" />
272266 </ div >
273267 ) ;
274268}
0 commit comments