Compare commits
8 Commits
Author | SHA1 | Date |
---|---|---|
|
412f25d32f | 4 years ago |
|
e8c043b08d | 4 years ago |
|
4b45610648 | 4 years ago |
|
ba566126d5 | 4 years ago |
|
9df02ccb48 | 4 years ago |
|
cda62f0a80 | 4 years ago |
|
df60e91bf3 | 4 years ago |
|
9a19bf3cc2 | 4 years ago |
31 changed files with 1234 additions and 699 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