Compare commits
No commits in common. 'master' and 'editsong' have entirely different histories.
31 changed files with 699 additions and 1234 deletions
@ -1,118 +0,0 @@ |
|||||||
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> |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,93 @@ |
|||||||
|
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