diff --git a/desktop/plugins/databases/DatabaseDetailSidebar.tsx b/desktop/plugins/databases/DatabaseDetailSidebar.tsx index 95bfe180e..530ae630b 100644 --- a/desktop/plugins/databases/DatabaseDetailSidebar.tsx +++ b/desktop/plugins/databases/DatabaseDetailSidebar.tsx @@ -7,9 +7,10 @@ * @format */ -import React, {useMemo} from 'react'; +import React, {useMemo, useState, useEffect, useReducer} from 'react'; import { Text, + Input, DetailSidebar, Panel, ManagedTable, @@ -17,14 +18,29 @@ import { TableBodyRow, ManagedDataInspector, Value, + valueToNullableString, renderValue, + Layout, + Button, + styled, + produce, } from 'flipper'; type DatabaseDetailSidebarProps = { columnLabels: Array; columnValues: Array; + onSave?: ((changes: {[key: string]: string | null}) => void) | undefined; }; +const EditTriggerSection = styled.div({ + display: 'flex', + justifyContent: 'flex-end', + width: '100%', + paddingTop: '3px', + paddingBottom: '3px', + paddingRight: '10px', +}); + function sidebarRows(labels: Array, values: Array): TableRows { return labels.map((label, idx) => buildSidebarRow(label, values[idx])); } @@ -51,6 +67,56 @@ function buildSidebarRow(key: string, val: Value): TableBodyRow { }; } +function sidebarEditableRows( + labels: Array, + values: Array, + rowDispatch: (action: RowAction) => void, +): TableRows { + return labels.map((label, idx) => + buildSidebarEditableRow( + label, + valueToNullableString(values[idx]), + (value: string | null) => rowDispatch({type: 'set', key: label, value}), + ), + ); +} + +function buildSidebarEditableRow( + key: string, + value: string | null, + onUpdateValue: (value: string | null) => void, +): TableBodyRow { + return { + columns: { + col: {value: {key}}, + val: { + value: , + }, + }, + key: key, + }; +} + +const EditField = React.memo( + (props: { + initialValue: string | null; + onUpdateValue: (value: string | null) => void; + }) => { + const {initialValue, onUpdateValue} = props; + const [value, setValue] = useState(initialValue); + useEffect(() => setValue(initialValue), [initialValue]); + return ( + { + setValue(e.target.value); + onUpdateValue(e.target.value); + }} + placeholder={value === null ? 'NULL' : undefined} + /> + ); + }, +); const cols = { col: { value: 'Column', @@ -66,14 +132,41 @@ const colSizes = { val: 'flex', }; +type RowState = {changes: {[key: string]: string | null}; updated: boolean}; +type RowAction = + | {type: 'set'; key: string; value: string | null} + | {type: 'reset'}; + +const rowStateReducer = produce((draftState: RowState, action: RowAction) => { + switch (action.type) { + case 'set': + draftState.changes[action.key] = action.value; + draftState.updated = true; + return; + case 'reset': + draftState.changes = {}; + draftState.updated = false; + return; + } +}); + export default React.memo(function DatabaseDetailSidebar( props: DatabaseDetailSidebarProps, ) { - const {columnLabels, columnValues} = props; - const rows = useMemo(() => sidebarRows(columnLabels, columnValues), [ - columnLabels, - columnValues, - ]); + const [editing, setEditing] = useState(false); + const [rowState, rowDispatch] = useReducer(rowStateReducer, { + changes: {}, + updated: false, + }); + const {columnLabels, columnValues, onSave} = props; + useEffect(() => rowDispatch({type: 'reset'}), [columnLabels, columnValues]); + const rows = useMemo( + () => + editing + ? sidebarEditableRows(columnLabels, columnValues, rowDispatch) + : sidebarRows(columnLabels, columnValues), + [columnLabels, columnValues, editing], + ); return ( - + + {onSave && ( + + {editing ? ( + <> + + + + ) : ( + + )} + + )} + + ); diff --git a/desktop/plugins/databases/index.tsx b/desktop/plugins/databases/index.tsx index 60243231c..7e70c991e 100644 --- a/desktop/plugins/databases/index.tsx +++ b/desktop/plugins/databases/index.tsx @@ -1127,6 +1127,7 @@ export default class DatabasesPlugin extends FlipperPlugin< )}