1- import React , { useCallback , useEffect , useMemo } from 'react' ;
1+ import React , { useCallback , useEffect , useMemo , useState } from 'react' ;
22import {
33 PluginType ,
44 type WidgetMiddlewarePlugin ,
55 type WidgetMiddlewareComponentProps ,
66 type WidgetMiddlewarePanelProps ,
77} from '@deephaven/plugin' ;
88import { type dh } from '@deephaven/jsapi-types' ;
9- import { Button } from '@deephaven/components' ;
10- import { vsHistory , vsTrash } from '@deephaven/icons' ;
9+ import {
10+ Button ,
11+ ConfirmationDialog ,
12+ DialogTrigger ,
13+ SpectrumButton ,
14+ Text ,
15+ } from '@deephaven/components' ;
16+ import { vsEdit , vsHistory , vsTrash } from '@deephaven/icons' ;
1117import { usePersistentState } from '@deephaven/dashboard' ;
1218import { type MoveOperation } from '@deephaven/grid' ;
1319import {
@@ -32,6 +38,8 @@ interface DehydratedStateSnapshot {
3238 id : string ;
3339 /** When the snapshot was taken (ISO string for JSON serialization) */
3440 timestamp : string ;
41+ /** User-defined name for the snapshot (optional) */
42+ name ?: string ;
3543 /** Quick filters state (dehydrated) */
3644 quickFilters : readonly DehydratedQuickFilter [ ] ;
3745 /** Advanced filters state (dehydrated) */
@@ -226,6 +234,78 @@ function TableHistoryPanel(_props: TableOptionPanelProps): JSX.Element {
226234 setState ( { snapshots : [ ] } ) ;
227235 } , [ setState ] ) ;
228236
237+ /**
238+ * Reset the table to its initial state.
239+ * Clears all filters, sorts, search, select distinct, custom columns,
240+ * and column re-ordering, restoring the table to its default view.
241+ */
242+ const handleResetTable = useCallback ( ( ) => {
243+ // Clear select distinct columns first (triggers handler that resets other state)
244+ dispatch ( {
245+ type : 'SET_SELECT_DISTINCT_COLUMNS' ,
246+ columns : [ ] ,
247+ } ) ;
248+
249+ // Clear custom columns
250+ dispatch ( {
251+ type : 'SET_CUSTOM_COLUMNS' ,
252+ columns : [ ] ,
253+ } ) ;
254+
255+ // Clear all filters
256+ dispatch ( {
257+ type : 'SET_QUICK_FILTERS' ,
258+ filters : new Map ( ) ,
259+ } ) ;
260+ dispatch ( {
261+ type : 'SET_ADVANCED_FILTERS' ,
262+ filters : new Map ( ) ,
263+ } ) ;
264+
265+ // Clear search
266+ dispatch ( {
267+ type : 'SET_CROSS_COLUMN_SEARCH' ,
268+ searchValue : '' ,
269+ selectedSearchColumns : [ ] ,
270+ invertSearchColumns : false ,
271+ } ) ;
272+
273+ // Clear sorts and reverse
274+ dispatch ( { type : 'SET_SORTS' , sorts : [ ] } ) ;
275+ dispatch ( { type : 'SET_REVERSE' , reverse : false } ) ;
276+
277+ // Reset column order
278+ dispatch ( {
279+ type : 'SET_MOVED_COLUMNS' ,
280+ columns : [ ] ,
281+ } ) ;
282+ } , [ dispatch ] ) ;
283+
284+ // State for inline editing of snapshot names
285+ const [ editingId , setEditingId ] = useState < string | null > ( null ) ;
286+ const [ editValue , setEditValue ] = useState ( '' ) ;
287+
288+ const handleStartEdit = useCallback ( ( snapshot : DehydratedStateSnapshot ) => {
289+ setEditingId ( snapshot . id ) ;
290+ setEditValue ( snapshot . name ?? '' ) ;
291+ } , [ ] ) ;
292+
293+ const handleSaveEdit = useCallback ( ( ) => {
294+ if ( editingId == null ) return ;
295+ setState ( prev => ( {
296+ snapshots : prev . snapshots . map ( s =>
297+ s . id === editingId ? { ...s , name : editValue . trim ( ) || undefined } : s
298+ ) ,
299+ } ) ) ;
300+ setEditingId ( null ) ;
301+ setEditValue ( '' ) ;
302+ } , [ editingId , editValue , setState ] ) ;
303+
304+ const handleCancelEdit = useCallback ( ( ) => {
305+ setEditingId ( null ) ;
306+ setEditValue ( '' ) ;
307+ } , [ ] ) ;
308+
229309 // Compute what has changed since the last saved snapshot
230310 const changedProperties = useMemo ( ( ) => {
231311 const lastSnapshot = snapshots [ snapshots . length - 1 ] ;
@@ -333,37 +413,103 @@ function TableHistoryPanel(_props: TableOptionPanelProps): JSX.Element {
333413 { snapshots . length > 0 && (
334414 < >
335415 < div className = "mt-3" >
336- < h6 className = "text-muted mb-2" > Saved Snapshots</ h6 >
416+ < h6 className = "text-muted mb-2" > Saved Snapshots (newest first) </ h6 >
337417 < ul className = "list-unstyled" >
338- { snapshots . map ( snapshot => (
418+ { [ ... snapshots ] . reverse ( ) . map ( snapshot => (
339419 < li
340420 key = { snapshot . id }
341421 className = "d-flex align-items-center justify-content-between py-1"
342422 >
343- < button
344- type = "button"
345- className = "btn btn-link p-0 text-start"
346- onClick = { ( ) => handleRestoreSnapshot ( snapshot ) }
347- title = "Click to restore this snapshot"
348- >
349- { formatTimestamp ( snapshot . timestamp ) }
350- </ button >
351- < Button
352- kind = "ghost"
353- icon = { vsTrash }
354- tooltip = "Delete"
355- aria-label = "Delete snapshot"
356- onClick = { ( ) => handleDeleteSnapshot ( snapshot . id ) }
357- />
423+ { editingId === snapshot . id ? (
424+ < div className = "d-flex align-items-center gap-1 flex-grow-1" >
425+ < input
426+ type = "text"
427+ className = "form-control form-control-sm"
428+ value = { editValue }
429+ onChange = { e => setEditValue ( e . target . value ) }
430+ onKeyDown = { e => {
431+ if ( e . key === 'Enter' ) handleSaveEdit ( ) ;
432+ if ( e . key === 'Escape' ) handleCancelEdit ( ) ;
433+ } }
434+ placeholder = { formatTimestamp ( snapshot . timestamp ) }
435+ autoFocus
436+ />
437+ < Button
438+ kind = "primary"
439+ onClick = { handleSaveEdit }
440+ style = { { padding : '2px 8px' , fontSize : '12px' } }
441+ >
442+ Save
443+ </ Button >
444+ < Button
445+ kind = "secondary"
446+ onClick = { handleCancelEdit }
447+ style = { { padding : '2px 8px' , fontSize : '12px' } }
448+ >
449+ Cancel
450+ </ Button >
451+ </ div >
452+ ) : (
453+ < >
454+ < button
455+ type = "button"
456+ className = "btn btn-link p-0 text-start"
457+ onClick = { ( ) => handleRestoreSnapshot ( snapshot ) }
458+ title = "Click to restore this snapshot"
459+ >
460+ { snapshot . name || formatTimestamp ( snapshot . timestamp ) }
461+ { snapshot . name && (
462+ < span className = "text-muted small ms-1" >
463+ ({ formatTimestamp ( snapshot . timestamp ) } )
464+ </ span >
465+ ) }
466+ </ button >
467+ < div className = "d-flex align-items-center" >
468+ < Button
469+ kind = "ghost"
470+ icon = { vsEdit }
471+ tooltip = "Rename"
472+ aria-label = "Rename snapshot"
473+ onClick = { ( ) => handleStartEdit ( snapshot ) }
474+ />
475+ < Button
476+ kind = "ghost"
477+ icon = { vsTrash }
478+ tooltip = "Delete"
479+ aria-label = "Delete snapshot"
480+ onClick = { ( ) => handleDeleteSnapshot ( snapshot . id ) }
481+ />
482+ </ div >
483+ </ >
484+ ) }
358485 </ li >
359486 ) ) }
360487 </ ul >
361488 </ div >
362489
363490 < div className = "mt-2" >
364- < Button kind = "secondary" onClick = { handleClearAll } >
365- Clear All Snapshots
366- </ Button >
491+ < DialogTrigger >
492+ < SpectrumButton variant = "secondary" >
493+ Clear All Snapshots
494+ </ SpectrumButton >
495+ { ( close : ( ) => void ) => (
496+ < ConfirmationDialog
497+ heading = "Clear All Snapshots"
498+ confirmationButtonLabel = "Clear All"
499+ onCancel = { close }
500+ onConfirm = { ( ) => {
501+ close ( ) ;
502+ handleClearAll ( ) ;
503+ } }
504+ >
505+ < Text >
506+ Are you sure you want to clear all { snapshots . length } { ' ' }
507+ snapshot{ snapshots . length === 1 ? '' : 's' } ? This action
508+ cannot be undone.
509+ </ Text >
510+ </ ConfirmationDialog >
511+ ) }
512+ </ DialogTrigger >
367513 </ div >
368514 </ >
369515 ) }
@@ -372,6 +518,32 @@ function TableHistoryPanel(_props: TableOptionPanelProps): JSX.Element {
372518 Save the current table state (filters, sorts, search) and restore it
373519 later by clicking on a timestamp.
374520 </ p >
521+
522+ < div className = "mt-4 pt-3 border-top" >
523+ < DialogTrigger >
524+ < SpectrumButton variant = "secondary" > Reset Table</ SpectrumButton >
525+ { ( close : ( ) => void ) => (
526+ < ConfirmationDialog
527+ heading = "Reset Table"
528+ confirmationButtonLabel = "Reset"
529+ onCancel = { close }
530+ onConfirm = { ( ) => {
531+ close ( ) ;
532+ handleResetTable ( ) ;
533+ } }
534+ >
535+ < Text >
536+ Reset the table to its initial state? This will clear all
537+ filters, sorts, search, and column re-ordering.
538+ </ Text >
539+ </ ConfirmationDialog >
540+ ) }
541+ </ DialogTrigger >
542+ < p className = "text-muted small mt-2 mb-0" >
543+ Clear all filters, sorts, search, custom columns, and column
544+ re-ordering to restore the table to its default state.
545+ </ p >
546+ </ div >
375547 </ div >
376548 ) ;
377549}
0 commit comments