forked from eclipse-cdt-cloud/vscode-peripheral-inspector
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Propagate value updates back to the data provider and tracker - Simplify column declaration and provide dedicated 'edit' property -- Always render the expander on the first column - Provide edit renderer for text value changes Refactor: - Convert some utility functions to React components - Convert thenable to promises Closes eclipse-cdt-cloud#16 Add more edit controls for specific types - Enumeration Dropdown for fields with enumeration values - Boolean Checkbox edit for non-enums of width 1 Closes eclipse-cdt-cloud#22 Consider monospace font for values Closes eclipse-cdt-cloud#45
- Loading branch information
1 parent
14c74a1
commit f56ee0e
Showing
19 changed files
with
606 additions
and
265 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import React from 'react'; | ||
import { CDTTreeTableActionColumn, CDTTreeItem, CDTTreeTableActionColumnCommand, CDTTreeItemResource } from '../../types'; | ||
import { CommandDefinition } from '../../../../common'; | ||
|
||
export interface ActionCellProps<T extends CDTTreeItemResource> { | ||
column: CDTTreeTableActionColumn; | ||
record: CDTTreeItem<T>; | ||
actions: CDTTreeTableActionColumnCommand[]; | ||
onAction?: (event: React.UIEvent, command: CommandDefinition, value: unknown, record: CDTTreeItem<T>) => void; | ||
} | ||
|
||
const ActionCell = <T extends CDTTreeItemResource>({ record, actions, onAction }: ActionCellProps<T>) => { | ||
return ( | ||
<div className="tree-actions"> | ||
{actions.map((action) => { | ||
const handleAction = (e: React.MouseEvent | React.KeyboardEvent) => onAction?.(e, action, action.value, record); | ||
return ( | ||
<i | ||
key={action.commandId} | ||
title={action.title} | ||
className={`codicon codicon-${action.icon}`} | ||
onClick={handleAction} | ||
role="button" | ||
tabIndex={0} | ||
onKeyDown={(e) => e.key === 'Enter' && handleAction(e)} | ||
/> | ||
); | ||
})} | ||
</div> | ||
); | ||
}; | ||
|
||
export default ActionCell; |
157 changes: 157 additions & 0 deletions
157
src/components/tree/components/cells/EditableStringCell.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
import './editable-string-cell.css'; | ||
|
||
import React, { useState, useRef, useEffect, useCallback } from 'react'; | ||
import { Input, Checkbox, Select } from 'antd'; | ||
import type { CheckboxChangeEvent } from 'antd/lib/checkbox'; | ||
import LabelCell from './LabelCell'; | ||
import { CDTTreeTableStringColumn, CDTTreeItem, EditableEnumData, CDTTreeItemResource } from '../../types'; | ||
|
||
interface EditableLabelCellProps<T extends CDTTreeItemResource> { | ||
column: CDTTreeTableStringColumn; | ||
record: CDTTreeItem<T>; | ||
editing: boolean; | ||
onSubmit: (newValue: string) => void; | ||
onCancel: () => void; | ||
} | ||
|
||
const EditableLabelCell = <T extends CDTTreeItemResource>({ | ||
column, | ||
record, | ||
editing, | ||
onSubmit, | ||
onCancel | ||
}: EditableLabelCellProps<T>) => { | ||
const [editMode, setEditMode] = useState(editing); | ||
const [value, setValue] = useState(column.label); | ||
const containerRef = useRef<HTMLDivElement>(null); | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const editorRef = useRef<any>(null); | ||
|
||
// Focus the editor when entering edit mode. | ||
useEffect(() => { | ||
if (editMode && editorRef.current) { | ||
editorRef.current.focus(); | ||
} | ||
}, [editMode]); | ||
|
||
const commitEdit = useCallback((newValue: string = value) => { | ||
setValue(newValue); | ||
onSubmit(newValue); | ||
setEditMode(false); | ||
}, [onSubmit, value]); | ||
|
||
const cancelEdit = useCallback(() => { | ||
setValue(column.label); | ||
onCancel(); | ||
setEditMode(false); | ||
}, [column.label]); | ||
|
||
// Cancel the edit only if focus leaves the entire container. | ||
const handleBlur = useCallback(() => { | ||
setTimeout(() => { | ||
if ( | ||
containerRef.current && | ||
document.activeElement && | ||
!containerRef.current.contains(document.activeElement) | ||
) { | ||
cancelEdit(); | ||
} | ||
}, 0); | ||
}, [column.label]); | ||
|
||
const handleKeyDown = useCallback( | ||
(e: React.KeyboardEvent) => { | ||
if (e.key === 'Escape') { | ||
cancelEdit(); | ||
} | ||
}, | ||
[column.label] | ||
); | ||
|
||
// Consume the double-click event so no other handler is triggered. | ||
const handleDoubleClick = useCallback((e: React.MouseEvent) => { | ||
e.preventDefault(); | ||
e.stopPropagation(); | ||
if (column.edit) { | ||
setEditMode(true); | ||
} | ||
}, [column]); | ||
|
||
useEffect(() => { | ||
if (editMode || editing) { | ||
editorRef.current && editorRef.current.focus(); | ||
} | ||
}, [editMode, editing]); | ||
|
||
if (editMode || editing) { | ||
return ( | ||
<div className='edit-field-container' ref={containerRef}> | ||
{(() => { | ||
switch (column.edit?.type) { | ||
case 'text': | ||
return ( | ||
<Input | ||
ref={editorRef} | ||
className={'text-field-cell'} | ||
value={value} | ||
onChange={e => setValue(e.target.value)} | ||
onPressEnter={e => commitEdit(e.currentTarget.value)} | ||
onBlur={handleBlur} | ||
onKeyDown={handleKeyDown} | ||
/> | ||
); | ||
case 'boolean': { | ||
const checked = value === '1'; | ||
return ( | ||
<Checkbox | ||
ref={editorRef} | ||
checked={checked} | ||
onChange={(e: CheckboxChangeEvent) => commitEdit(e.target.checked ? '1' : '0')} | ||
onBlur={handleBlur} | ||
onKeyDown={handleKeyDown} | ||
/> | ||
); | ||
} | ||
case 'enum': { | ||
const enumEdit = column.edit as EditableEnumData; | ||
return ( | ||
<Select | ||
ref={editorRef} | ||
className={'enum-field-cell'} | ||
placeholder={column.label} | ||
value={value} | ||
onChange={(newValue) => commitEdit(newValue)} | ||
onBlur={handleBlur} | ||
onKeyDown={handleKeyDown} | ||
> | ||
{enumEdit.options.map((opt) => { | ||
const label = opt.value + (opt.detail ? `: ${opt.detail}` : ''); | ||
return ( | ||
<Select.Option key={opt.value} value={opt.value}> | ||
{label} | ||
</Select.Option> | ||
); | ||
})} | ||
</Select> | ||
); | ||
} | ||
default: | ||
return null; | ||
} | ||
})()} | ||
</div> | ||
); | ||
} | ||
|
||
return ( | ||
<div | ||
className='editable-string-cell' | ||
onDoubleClick={handleDoubleClick} | ||
style={{ cursor: column.edit ? 'pointer' : 'default' }} | ||
> | ||
<LabelCell record={record} column={column} /> | ||
</div> | ||
); | ||
}; | ||
|
||
export default React.memo(EditableLabelCell); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import React from 'react'; | ||
import classNames from 'classnames'; | ||
import { CDTTreeTableStringColumn, CDTTreeItem, CDTTreeItemResource } from '../../types'; | ||
import { createLabelWithTooltip, createHighlightedText } from '../utils'; | ||
|
||
export interface LabelCellProps<T extends CDTTreeItemResource> { | ||
column: CDTTreeTableStringColumn; | ||
record: CDTTreeItem<T>; | ||
} | ||
|
||
const LabelCell = <T extends CDTTreeItemResource>({ column }: LabelCellProps<T>) => { | ||
const icon = column.icon && <i className={classNames('cell-icon', column.icon)} />; | ||
|
||
const content = column.tooltip | ||
? createLabelWithTooltip(<span>{createHighlightedText(column.label, column.highlight)}</span>, column.tooltip) | ||
: createHighlightedText(column.label, column.highlight); | ||
|
||
return ( | ||
<div className="tree-cell ant-table-cell-ellipsis" tabIndex={0}> | ||
{icon} | ||
{content} | ||
</div> | ||
); | ||
}; | ||
|
||
export default React.memo(LabelCell); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import React, { useCallback } from 'react'; | ||
import { CDTTreeItem, CDTTreeItemResource, CDTTreeTableStringColumn } from '../../types'; | ||
import EditableStringCell from './EditableStringCell'; | ||
import LabelCell from './LabelCell'; | ||
|
||
interface StringCellProps<T extends CDTTreeItemResource> { | ||
column: CDTTreeTableStringColumn; | ||
record: CDTTreeItem<T>; | ||
editing?: boolean; | ||
onSubmit?: (record: CDTTreeItem<T>, newValue: string) => void; | ||
onCancel?: (record: CDTTreeItem<T>) => void; | ||
} | ||
|
||
const StringCell = <T extends CDTTreeItemResource>({ column, record, editing = false, onSubmit, onCancel }: StringCellProps<T>) => { | ||
const handleSubmit = useCallback( | ||
(newValue: string) => onSubmit?.(record, newValue), | ||
[record, onSubmit] | ||
); | ||
|
||
const handleCancel = useCallback( | ||
() => onCancel?.(record), | ||
[record, onCancel] | ||
); | ||
|
||
return column.edit && onSubmit | ||
? <EditableStringCell record={record} column={column} onSubmit={handleSubmit} onCancel={handleCancel} editing={editing} /> | ||
: <LabelCell record={record} column={column} />; | ||
}; | ||
|
||
export default StringCell; |
79 changes: 79 additions & 0 deletions
79
src/components/tree/components/cells/editable-string-cell.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/********************************************************************* | ||
* Copyright (c) 2024 Arm Limited and others | ||
* | ||
* This program and the accompanying materials are made | ||
* available under the terms of the Eclipse Public License 2.0 | ||
* which is available at https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
*********************************************************************/ | ||
|
||
.ant-input.text-field-cell { | ||
background: var(--ant-color-bg-container); | ||
color: var(--ant-color-text); | ||
font-family: var(--ant-font-family-code); | ||
font-size: var(--ant-table-cell-font-size); | ||
line-height: var(--ant-line-height); | ||
padding: 1px; | ||
padding-left: 3px; | ||
border-radius: 0; | ||
border-style: dotted; | ||
} | ||
|
||
.css-var-r0.ant-select-css-var { | ||
--ant-control-height: auto; | ||
--ant-color-text-placeholder: var(--vscode-sideBar-foreground); | ||
--ant-select-show-arrow-padding-inline-end: 2em; | ||
--ant-border-radius-sm: 0; | ||
--ant-border-radius-lg: 0; | ||
--ant-select-option-active-bg: var(--vscode-list-hoverBackground); | ||
--ant-select-option-font-size: 12px; | ||
--ant-select-option-selected-bg: var(--vscode-list-inactiveSelectionBackground); | ||
--ant-select-option-selected-color: var(--vscode-sideBar-foreground); | ||
--ant-select-option-height: auto; | ||
--ant-select-option-padding: 3px 6px; | ||
} | ||
|
||
.enum-field-cell.ant-select-outlined:not(.ant-select-customize-input) .ant-select-selector { | ||
background: var(--ant-color-bg-container); | ||
color: var(--ant-color-text); | ||
font-family: var(--ant-font-family-code); | ||
font-size: var(--ant-table-cell-font-size); | ||
line-height: var(--ant-line-height); | ||
padding: 1px; | ||
padding-left: 3px; | ||
border-radius: 0; | ||
border-style: dotted; | ||
} | ||
|
||
.ant-select-dropdown { | ||
background: var(--ant-color-bg-container); | ||
color: var(--ant-color-text); | ||
font-family: var(--ant-font-family-code); | ||
font-size: var(--ant-table-cell-font-size); | ||
line-height: var(--ant-line-height); | ||
padding: 1px; | ||
padding-left: 3px; | ||
} | ||
|
||
.enum-field-cell.ant-select-outlined:not(.ant-select-customize-input) .ant-select-arrow { | ||
color: var(--ant-color-text); | ||
} | ||
|
||
.editable-string-cell:hover { | ||
text-decoration: underline dotted var(--vscode-foreground); | ||
text-underline-offset: 4px; | ||
} | ||
|
||
.ant-table-cell.value { | ||
font-family: var(--ant-font-family-code); | ||
} | ||
|
||
.edit-field-container { | ||
display: flex; | ||
flex-grow: 1; | ||
} | ||
|
||
.edit-field-container>* { | ||
flex-grow: 1; | ||
} |
Oops, something went wrong.