parent
df60e91bf3
commit
cda62f0a80
8 changed files with 347 additions and 324 deletions
@ -0,0 +1,118 @@ |
||||
import React, { useState } from 'react'; |
||||
import { Button, Dialog, DialogActions, Divider, Typography, Box, TextField, IconButton } from "@material-ui/core"; |
||||
import { ExternalLinksEditor } from './ExternalLinksEditor'; |
||||
import UndoIcon from '@material-ui/icons/Undo'; |
||||
import { ResourceType } from '../../api/api'; |
||||
let _ = require('lodash') |
||||
|
||||
export enum EditablePropertyType { |
||||
Text = 0, |
||||
} |
||||
|
||||
export interface EditableProperty { |
||||
metadataKey: string, |
||||
title: string, |
||||
type: EditablePropertyType |
||||
} |
||||
|
||||
function EditTextProperty(props: { |
||||
title: string, |
||||
originalValue: string, |
||||
currentValue: string, |
||||
onChange: (v: string) => void |
||||
}) { |
||||
return <Box display="flex" alignItems="center" width="100%"> |
||||
<TextField |
||||
// Here we "abuse" the label to show the original title.
|
||||
// emptying the text box means going back to the original.
|
||||
variant="outlined" |
||||
value={props.currentValue} |
||||
label={props.title} |
||||
helperText={(props.currentValue != props.originalValue) && |
||||
"Current: " + props.originalValue || undefined} |
||||
error={(props.currentValue != props.originalValue)} |
||||
onChange={(e: any) => { |
||||
props.onChange((e.target.value == "") ? |
||||
props.originalValue : e.target.value) |
||||
}} |
||||
fullWidth={true} |
||||
/> |
||||
{props.currentValue != props.originalValue && <IconButton |
||||
onClick={() => { |
||||
props.onChange(props.originalValue) |
||||
}} |
||||
><UndoIcon /></IconButton>} |
||||
</Box> |
||||
} |
||||
|
||||
function PropertyEditor(props: { |
||||
originalMetadata: any, |
||||
currentMetadata: any, |
||||
onChange: (metadata: any) => void, |
||||
editableProperties: EditableProperty[] |
||||
}) { |
||||
return <Box display="flex" width="100%"> |
||||
{props.editableProperties.map( |
||||
(p: EditableProperty) => { |
||||
if (p.type == EditablePropertyType.Text) { |
||||
return <EditTextProperty |
||||
title={p.title} |
||||
originalValue={props.originalMetadata[p.metadataKey]} |
||||
currentValue={props.currentMetadata[p.metadataKey]} |
||||
onChange={(v: string) => props.onChange({ ...props.currentMetadata, [p.metadataKey]: v })} |
||||
/> |
||||
} |
||||
return undefined; |
||||
} |
||||
)} |
||||
</Box > |
||||
} |
||||
|
||||
export default function EditItemDialog(props: { |
||||
open: boolean, |
||||
onClose: () => void, |
||||
onSubmit: (v: any) => void, |
||||
id: number, |
||||
metadata: any, |
||||
defaultExternalLinksQuery: string, |
||||
editableProperties: EditableProperty[], |
||||
resourceType: ResourceType, |
||||
editStoreLinks: boolean, |
||||
}) { |
||||
let [editingMetadata, setEditingMetadata] = useState<any>(props.metadata); |
||||
|
||||
return <Dialog |
||||
maxWidth="lg" |
||||
fullWidth |
||||
open={props.open} |
||||
onClose={props.onClose} |
||||
disableBackdropClick={true}> |
||||
<Typography variant="h5">Properties</Typography> |
||||
<PropertyEditor |
||||
originalMetadata={props.metadata} |
||||
currentMetadata={editingMetadata} |
||||
onChange={setEditingMetadata} |
||||
editableProperties={props.editableProperties} |
||||
/> |
||||
{props.editStoreLinks && <><Divider /> |
||||
<Typography variant="h5">External Links</Typography> |
||||
<ExternalLinksEditor |
||||
metadata={editingMetadata} |
||||
original={props.metadata} |
||||
onChange={(v: any) => setEditingMetadata(v)} |
||||
defaultQuery={props.defaultExternalLinksQuery} |
||||
resourceType={props.resourceType} |
||||
/></>} |
||||
<Divider /> |
||||
{!_.isEqual(editingMetadata, props.metadata) && <DialogActions> |
||||
<Button variant="contained" color="secondary" |
||||
onClick={() => { |
||||
props.onSubmit(editingMetadata); |
||||
props.onClose(); |
||||
}}>Save all changes</Button> |
||||
<Button variant="outlined" |
||||
onClick={() => setEditingMetadata(props.metadata)}>Discard changes</Button> |
||||
</DialogActions>} |
||||
</Dialog> |
||||
|
||||
} |
@ -1,93 +0,0 @@ |
||||
import React, { useState } from 'react'; |
||||
import { Box, IconButton, TextField } from '@material-ui/core'; |
||||
import EditIcon from '@material-ui/icons/Edit'; |
||||
import CheckIcon from '@material-ui/icons/Check'; |
||||
import UndoIcon from '@material-ui/icons/Undo'; |
||||
import { useTheme } from '@material-ui/core/styles'; |
||||
|
||||
// This component is an editable text. It shows up as normal text,
|
||||
// but will display an edit icon on hover. When clicked, this
|
||||
// enables a text input to make a new suggestion.
|
||||
// The text can show a striked-through version of the old text,
|
||||
// with the new value next to it and an undo button.
|
||||
|
||||
export interface IProps { |
||||
defaultValue: string, |
||||
changedValue: string | null, // Null == not changed
|
||||
editingValue: string | null, // Null == not editing
|
||||
editingLabel: string, |
||||
onChangeEditingValue: (v: string | null) => void, |
||||
onChangeChangedValue: (v: string | null) => void, |
||||
} |
||||
|
||||
export default function EditableText(props: IProps) { |
||||
let editingValue = props.editingValue; |
||||
let defaultValue = props.defaultValue; |
||||
let changedValue = props.changedValue; |
||||
let onChangeEditingValue = props.onChangeEditingValue; |
||||
let onChangeChangedValue = props.onChangeChangedValue; |
||||
let editing = editingValue !== null; |
||||
let editingLabel = props.editingLabel; |
||||
|
||||
const theme = useTheme(); |
||||
|
||||
const [hovering, setHovering] = useState<Boolean>(false); |
||||
|
||||
const editButton = <Box |
||||
visibility={(hovering && !editing) ? "visible" : "hidden"}> |
||||
<IconButton |
||||
onClick={() => onChangeEditingValue(changedValue || defaultValue)} |
||||
> |
||||
<EditIcon /> |
||||
</IconButton> |
||||
</Box> |
||||
|
||||
const discardChangesButton = <Box |
||||
visibility={(hovering && !editing) ? "visible" : "hidden"}> |
||||
<IconButton |
||||
onClick={() => { |
||||
onChangeChangedValue(null); |
||||
onChangeEditingValue(null); |
||||
}} |
||||
> |
||||
<UndoIcon /> |
||||
</IconButton> |
||||
</Box> |
||||
|
||||
if (editing) { |
||||
return <Box display="flex" alignItems="center"> |
||||
<TextField |
||||
variant="outlined" |
||||
value={editingValue || ""} |
||||
label={editingLabel} |
||||
inputProps={{ style: { fontSize: '2rem' } }} |
||||
onChange={(e: any) => onChangeEditingValue(e.target.value)} |
||||
/> |
||||
<IconButton |
||||
onClick={() => { |
||||
onChangeChangedValue(editingValue === defaultValue ? null : editingValue); |
||||
onChangeEditingValue(null); |
||||
}} |
||||
><CheckIcon /></IconButton> |
||||
</Box> |
||||
} else if (changedValue) { |
||||
return <Box |
||||
onMouseEnter={() => setHovering(true)} |
||||
onMouseLeave={() => setHovering(false)} |
||||
display="flex" |
||||
alignItems="center" |
||||
> |
||||
<del style={{ color: theme.palette.text.secondary }}>{defaultValue}</del>→ |
||||
{changedValue} |
||||
{editButton} |
||||
{discardChangesButton} |
||||
</Box> |
||||
} |
||||
|
||||
return <Box |
||||
onMouseEnter={() => setHovering(true)} |
||||
onMouseLeave={() => setHovering(false)} |
||||
display="flex" |
||||
alignItems="center" |
||||
>{defaultValue}{editButton}</Box>; |
||||
} |
Loading…
Reference in new issue