Highlight edited fields

This commit is contained in:
Tulir Asokan 2018-12-08 13:29:53 +02:00
parent 4f7eef6029
commit d09dd8ddc6
6 changed files with 56 additions and 36 deletions

View File

@ -34,28 +34,32 @@ export const PrefTable = ({ children, wrapperClass }) => {
) )
} }
export const PrefRow = ({ name, fullWidth = false, labelFor = undefined, children }) => ( export const PrefRow =
<div className={`entry ${fullWidth ? "full-width" : ""}`}> ({ name, fullWidth = false, labelFor = undefined, changed = false, children }) => (
<label htmlFor={labelFor}>{name}</label> <div className={`entry ${fullWidth ? "full-width" : ""} ${changed ? "changed" : ""}`}>
<div className="value">{children}</div> <label htmlFor={labelFor}>{name}</label>
</div> <div className="value">{children}</div>
) </div>
)
export const PrefInput = ({ rowName, fullWidth = false, ...args }) => ( export const PrefInput = ({ rowName, value, origValue, fullWidth = false, ...args }) => (
<PrefRow name={rowName} fullWidth={fullWidth} labelFor={rowName}> <PrefRow name={rowName} fullWidth={fullWidth} labelFor={rowName}
<input {...args} id={rowName}/> changed={origValue !== undefined && value !== origValue}>
<input {...args} value={value} id={rowName}/>
</PrefRow> </PrefRow>
) )
export const PrefSwitch = ({ rowName, fullWidth = false, ...args }) => ( export const PrefSwitch = ({ rowName, active, origActive, fullWidth = false, ...args }) => (
<PrefRow name={rowName} fullWidth={fullWidth} labelFor={rowName}> <PrefRow name={rowName} fullWidth={fullWidth} labelFor={rowName}
<Switch {...args} id={rowName}/> changed={origActive !== undefined && active !== origActive}>
<Switch {...args} active={active} id={rowName}/>
</PrefRow> </PrefRow>
) )
export const PrefSelect = ({ rowName, fullWidth = false, ...args }) => ( export const PrefSelect = ({ rowName, value, origValue, fullWidth = false, ...args }) => (
<PrefRow name={rowName} fullWidth={fullWidth} labelFor={rowName}> <PrefRow name={rowName} fullWidth={fullWidth} labelFor={rowName}
<Select className="select" {...args} id={rowName}/> changed={origValue !== undefined && value.id !== origValue}>
<Select className="select" {...args} id={rowName} value={value}/>
</PrefRow> </PrefRow>
) )

View File

@ -34,7 +34,7 @@ class BaseMainView extends Component {
} }
get isNew() { get isNew() {
return !this.props.entry return !this.props.entry.id
} }
inputChange = event => { inputChange = event => {

View File

@ -157,25 +157,31 @@ class Client extends BaseMainView {
renderPreferences = () => ( renderPreferences = () => (
<PrefTable> <PrefTable>
<PrefInput rowName="User ID" type="text" disabled={!this.isNew} fullWidth={true} <PrefInput rowName="User ID" type="text" disabled={!this.isNew} fullWidth={true}
name={this.isNew ? "id" : ""} value={this.state.id} className="id" name={this.isNew ? "id" : ""} className="id"
value={this.state.id} origValue={this.props.entry.id}
placeholder="@fancybot:example.com" onChange={this.inputChange}/> placeholder="@fancybot:example.com" onChange={this.inputChange}/>
<PrefInput rowName="Homeserver" type="text" name="homeserver" <PrefInput rowName="Homeserver" type="text" name="homeserver"
value={this.state.homeserver} placeholder="https://example.com" value={this.state.homeserver} origValue={this.props.entry.homeserver}
onChange={this.inputChange}/> placeholder="https://example.com" onChange={this.inputChange}/>
<PrefInput rowName="Access token" type="text" name="access_token" <PrefInput rowName="Access token" type="text" name="access_token"
value={this.state.access_token} onChange={this.inputChange} value={this.state.access_token} origValue={this.props.entry.access_token}
placeholder="MDAxYWxvY2F0aW9uIG1hdHJpeC5sb2NhbAowMDEzaWRlbnRpZmllc"/> placeholder="MDAxYWxvY2F0aW9uIG1hdHJpeC5sb2NhbAowMDEzaWRlbnRpZmllc"
<PrefInput rowName="Display name" type="text" name="displayname"
value={this.state.displayname} placeholder="My fancy bot"
onChange={this.inputChange}/> onChange={this.inputChange}/>
<PrefInput rowName="Display name" type="text" name="displayname"
value={this.state.displayname} origValue={this.props.entry.displayname}
placeholder="My fancy bot" onChange={this.inputChange}/>
<PrefInput rowName="Avatar URL" type="text" name="avatar_url" <PrefInput rowName="Avatar URL" type="text" name="avatar_url"
value={this.state.avatar_url} onChange={this.inputChange} value={this.state.avatar_url} origValue={this.props.entry.avatar_url}
placeholder="mxc://example.com/mbmwyoTvPhEQPiCskcUsppko"/> placeholder="mxc://example.com/mbmwyoTvPhEQPiCskcUsppko"
<PrefSwitch rowName="Sync" active={this.state.sync} onChange={this.inputChange}/>
<PrefSwitch rowName="Sync"
active={this.state.sync} origActive={this.props.entry.sync}
onToggle={sync => this.setState({ sync })}/> onToggle={sync => this.setState({ sync })}/>
<PrefSwitch rowName="Autojoin" active={this.state.autojoin} <PrefSwitch rowName="Autojoin"
active={this.state.autojoin} origActive={this.props.entry.autojoin}
onToggle={autojoin => this.setState({ autojoin })}/> onToggle={autojoin => this.setState({ autojoin })}/>
<PrefSwitch rowName="Enabled" active={this.state.enabled} <PrefSwitch rowName="Enabled"
active={this.state.enabled} origActive={this.props.entry.enabled}
onToggle={enabled => this.setState({ onToggle={enabled => this.setState({
enabled, enabled,
started: enabled && this.state.started, started: enabled && this.state.started,

View File

@ -138,15 +138,18 @@ class Instance extends BaseMainView {
<PrefInput rowName="ID" type="text" name="id" value={this.state.id} <PrefInput rowName="ID" type="text" name="id" value={this.state.id}
placeholder="fancybotinstance" onChange={this.inputChange} placeholder="fancybotinstance" onChange={this.inputChange}
disabled={!this.isNew} fullWidth={true} className="id"/> disabled={!this.isNew} fullWidth={true} className="id"/>
<PrefSwitch rowName="Enabled" active={this.state.enabled} <PrefSwitch rowName="Enabled"
active={this.state.enabled} origActive={this.props.entry.enabled}
onToggle={enabled => this.setState({ enabled })}/> onToggle={enabled => this.setState({ enabled })}/>
<PrefSwitch rowName="Running" active={this.state.started} <PrefSwitch rowName="Running"
active={this.state.started} origActive={this.props.entry.started}
onToggle={started => this.setState({ started })}/> onToggle={started => this.setState({ started })}/>
<PrefSelect rowName="Primary user" options={this.clientOptions} <PrefSelect rowName="Primary user" options={this.clientOptions}
isSearchable={false} value={this.selectedClientEntry} isSearchable={false} value={this.selectedClientEntry}
origValue={this.props.entry.primary_user}
onChange={({ id }) => this.setState({ primary_user: id })}/> onChange={({ id }) => this.setState({ primary_user: id })}/>
<PrefSelect rowName="Type" options={this.typeOptions} isSearchable={false} <PrefSelect rowName="Type" options={this.typeOptions} isSearchable={false}
value={this.selectedPluginEntry} value={this.selectedPluginEntry} origValue={this.props.entry.type}
onChange={({ id }) => this.setState({ type: id })}/> onChange={({ id }) => this.setState({ type: id })}/>
</PrefTable> </PrefTable>
{!this.isNew && {!this.isNew &&

View File

@ -191,11 +191,13 @@ class Dashboard extends Component {
<Route path="/" exact render={() => <Home openLog={this.openLog}/>}/> <Route path="/" exact render={() => <Home openLog={this.openLog}/>}/>
<Route path="/new/instance" render={() => <Route path="/new/instance" render={() =>
<Instance onChange={newEntry => this.add("instances", newEntry)} <Instance onChange={newEntry => this.add("instances", newEntry)}
ctx={this.state}/>}/> entry={{}} ctx={this.state}/>}/>
<Route path="/new/client" render={() => <Client <Route path="/new/client" render={() =>
onChange={newEntry => this.add("clients", newEntry)}/>}/> <Client entry={{}} onChange={newEntry =>
<Route path="/new/plugin" render={() => <Plugin this.add("clients", newEntry)}/>}/>
onChange={newEntry => this.add("plugins", newEntry)}/>}/> <Route path="/new/plugin" render={() =>
<Plugin entry={{}} onChange={newEntry =>
this.add("plugins", newEntry)}/>}/>
<Route path="/instance/:id" render={({ match }) => <Route path="/instance/:id" render={({ match }) =>
this.renderView("instances", Instance, match.params.id)}/> this.renderView("instances", Instance, match.params.id)}/>
<Route path="/client/:id" render={({ match }) => <Route path="/client/:id" render={({ match }) =>

View File

@ -32,6 +32,11 @@
&.full-width &.full-width
width: 100% width: 100%
&.changed > label
font-weight: bold
&:after
content: "*"
> label, > .value > label, > .value
display: block display: block
width: 100% width: 100%