mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-24 20:55:49 +00:00
Organize JS better (#343)
This commit is contained in:
3
js/compatibility.js
Normal file
3
js/compatibility.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import objAssign from 'es6-object-assign';
|
||||
|
||||
objAssign.polyfill();
|
||||
37
js/core.js
Normal file
37
js/core.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import * as HAWS from 'home-assistant-js-websocket';
|
||||
|
||||
window.HAWS = HAWS;
|
||||
window.HASS_DEMO = __DEMO__;
|
||||
window.HASS_DEV = __DEV__;
|
||||
|
||||
const init = window.createHassConnection = function (password) {
|
||||
const proto = window.location.protocol === 'https:' ? 'wss' : 'ws';
|
||||
const url = `${proto}://${window.location.host}/api/websocket`;
|
||||
const options = {
|
||||
setupRetry: 10,
|
||||
};
|
||||
if (password !== undefined) {
|
||||
options.authToken = password;
|
||||
}
|
||||
|
||||
return HAWS.createConnection(url, options)
|
||||
.then(function (conn) {
|
||||
HAWS.subscribeEntities(conn);
|
||||
HAWS.subscribeConfig(conn);
|
||||
return conn;
|
||||
});
|
||||
};
|
||||
|
||||
if (window.noAuth) {
|
||||
window.hassConnection = init();
|
||||
} else if (window.localStorage.authToken) {
|
||||
window.hassConnection = init(window.localStorage.authToken);
|
||||
} else {
|
||||
window.hassConnection = null;
|
||||
}
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function () {
|
||||
navigator.serviceWorker.register('/service_worker.js');
|
||||
});
|
||||
}
|
||||
107
js/editor/automation.js
Normal file
107
js/editor/automation.js
Normal file
@@ -0,0 +1,107 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import Trigger from './trigger';
|
||||
import Script from './script';
|
||||
|
||||
export default class Automation extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.triggerChanged = this.triggerChanged.bind(this);
|
||||
this.actionChanged = this.actionChanged.bind(this);
|
||||
}
|
||||
|
||||
onChange(ev) {
|
||||
this.props.onChange({
|
||||
...this.props.automation,
|
||||
[ev.target.name]: ev.target.value,
|
||||
});
|
||||
}
|
||||
|
||||
triggerChanged(trigger) {
|
||||
this.props.onChange({
|
||||
...this.props.automation,
|
||||
trigger,
|
||||
});
|
||||
}
|
||||
|
||||
actionChanged(action) {
|
||||
this.props.onChange({
|
||||
...this.props.automation,
|
||||
action,
|
||||
});
|
||||
}
|
||||
|
||||
render({ automation, isWide }) {
|
||||
const { alias, trigger, condition, action } = automation;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ha-config-section is-wide={isWide}>
|
||||
<span slot='header'>{alias}</span>
|
||||
<span slot='introduction'>
|
||||
Use automations to bring your home alive.
|
||||
</span>
|
||||
<paper-card>
|
||||
<div class='card-content'>
|
||||
<paper-input
|
||||
label="Name"
|
||||
name="alias"
|
||||
value={alias}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
</paper-card>
|
||||
</ha-config-section>
|
||||
|
||||
<ha-config-section is-wide={isWide}>
|
||||
<span slot='header'>Triggers</span>
|
||||
<span slot='introduction'>
|
||||
Triggers are what starts the processing of an automation rule.
|
||||
It is possible to specify multiple triggers for the same rule.
|
||||
Once a trigger starts, Home Assistant will validate the conditions,
|
||||
if any, and call the action.
|
||||
<p><a href="https://home-assistant.io/docs/automation/trigger/" target="_blank">
|
||||
Learn more about triggers.
|
||||
</a></p>
|
||||
</span>
|
||||
<Trigger trigger={trigger} onChange={this.triggerChanged} />
|
||||
</ha-config-section>
|
||||
|
||||
{ condition &&
|
||||
<ha-config-section is-wide={isWide}>
|
||||
<span slot='header'>Conditions</span>
|
||||
<span slot='introduction'>
|
||||
Conditions are an optional part of an automation rule and can be used to prevent
|
||||
an action from happening when triggered. Conditions look very similar to triggers
|
||||
but are very different. A trigger will look at events happening in the system
|
||||
while a condition only looks at how the system looks right now. A trigger can
|
||||
observe that a switch is being turned on. A condition can only see if a switch
|
||||
is currently on or off.
|
||||
<p><a href="https://home-assistant.io/docs/scripts/conditions/" target="_blank">
|
||||
Learn more about conditions.
|
||||
</a></p>
|
||||
</span>
|
||||
<paper-card>
|
||||
<div class='card-content'>
|
||||
Conditions are not supported yet.
|
||||
<pre>{JSON.stringify(condition, null, 2)}</pre>
|
||||
</div>
|
||||
</paper-card>
|
||||
</ha-config-section>}
|
||||
|
||||
<ha-config-section is-wide={isWide}>
|
||||
<span slot='header'>Action</span>
|
||||
<span slot='introduction'>
|
||||
The actions are what Home Assistant will do when the automation is triggered.
|
||||
<p><a href="https://home-assistant.io/docs/scripts/" target="_blank">
|
||||
Learn more about actions.
|
||||
</a></p>
|
||||
</span>
|
||||
<Script script={action} onChange={this.actionChanged} />
|
||||
</ha-config-section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
10
js/editor/editor.js
Normal file
10
js/editor/editor.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import { h, render } from 'preact';
|
||||
import Automation from './automation';
|
||||
|
||||
window.AutomationEditor = function (mountEl, props, mergeEl) {
|
||||
return render(h(Automation, props), mountEl, mergeEl);
|
||||
};
|
||||
|
||||
window.unmountPreact = function (mountEl, mergeEl) {
|
||||
render(() => null, mountEl, mergeEl);
|
||||
};
|
||||
57
js/editor/json_textarea.js
Normal file
57
js/editor/json_textarea.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
|
||||
export default class JSONTextArea extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state.isValid = true;
|
||||
this.state.value = JSON.stringify(props.value || {}, null, 2);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
}
|
||||
|
||||
onChange(ev) {
|
||||
const value = ev.target.value;
|
||||
let parsed;
|
||||
let isValid;
|
||||
|
||||
try {
|
||||
parsed = JSON.parse(value);
|
||||
isValid = true;
|
||||
} catch (err) {
|
||||
// Invalid JSON
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
value,
|
||||
isValid,
|
||||
});
|
||||
if (isValid) {
|
||||
this.props.onChange(parsed);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps({ value }) {
|
||||
this.setState({
|
||||
value: JSON.stringify(value, null, 2),
|
||||
isValid: true,
|
||||
});
|
||||
}
|
||||
|
||||
render(props, { value, isValid }) {
|
||||
const style = {
|
||||
minWidth: 300,
|
||||
width: '100%',
|
||||
};
|
||||
if (!isValid) {
|
||||
style.border = '1px solid red';
|
||||
}
|
||||
return (
|
||||
<iron-autogrow-textarea
|
||||
value={value}
|
||||
style={style}
|
||||
onValue-Changed={this.onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
52
js/editor/script/call_service.js
Normal file
52
js/editor/script/call_service.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import JSONTextArea from '../json_textarea';
|
||||
|
||||
export default class CallServiceAction extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.serviceDataChanged = this.serviceDataChanged.bind(this);
|
||||
}
|
||||
|
||||
onChange(ev) {
|
||||
this.props.onChange(this.props.index, {
|
||||
...this.props.action,
|
||||
[ev.target.name]: ev.target.value
|
||||
});
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
serviceDataChanged(data) {
|
||||
this.props.onChange(this.props.index, {
|
||||
...this.props.action,
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
render({ action }) {
|
||||
const { alias, service, data } = action;
|
||||
return (
|
||||
<div>
|
||||
<paper-input
|
||||
label="Alias"
|
||||
name="alias"
|
||||
value={alias}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<paper-input
|
||||
label="Service"
|
||||
name="service"
|
||||
value={service}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Service Data<br />
|
||||
<JSONTextArea
|
||||
value={data}
|
||||
onChange={this.serviceDataChanged}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
50
js/editor/script/index.js
Normal file
50
js/editor/script/index.js
Normal file
@@ -0,0 +1,50 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import ScriptAction from './script_action';
|
||||
|
||||
export default class Script extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.addAction = this.addAction.bind(this);
|
||||
this.actionChanged = this.actionChanged.bind(this);
|
||||
}
|
||||
|
||||
addAction() {
|
||||
const script = this.props.script.concat({
|
||||
service: '',
|
||||
});
|
||||
|
||||
this.props.onChange(script);
|
||||
}
|
||||
|
||||
actionChanged(index, newValue) {
|
||||
const script = this.props.script.concat();
|
||||
|
||||
if (newValue === null) {
|
||||
script.splice(index, 1);
|
||||
} else {
|
||||
script[index] = newValue;
|
||||
}
|
||||
|
||||
this.props.onChange(script);
|
||||
}
|
||||
|
||||
render({ script }) {
|
||||
return (
|
||||
<div class="script">
|
||||
{script.map((act, idx) => (
|
||||
<ScriptAction
|
||||
index={idx}
|
||||
action={act}
|
||||
onChange={this.actionChanged}
|
||||
/>))}
|
||||
<paper-card>
|
||||
<div class='card-actions add-card'>
|
||||
<paper-button onTap={this.addAction}>Add action</paper-button>
|
||||
</div>
|
||||
</paper-card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
106
js/editor/script/script_action.js
Normal file
106
js/editor/script/script_action.js
Normal file
@@ -0,0 +1,106 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import CallService from './call_service';
|
||||
|
||||
function getType(action) {
|
||||
if ('service' in action) {
|
||||
return 'Call Service';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const TYPES = {
|
||||
'Call Service': CallService,
|
||||
Delay: null,
|
||||
'Templated Delay': null,
|
||||
Condition: null,
|
||||
'Fire Event': null,
|
||||
};
|
||||
|
||||
const OPTIONS = Object.keys(TYPES).sort();
|
||||
|
||||
export default class Action extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.typeChanged = this.typeChanged.bind(this);
|
||||
this.onDelete = this.onDelete.bind(this);
|
||||
}
|
||||
|
||||
typeChanged(ev) {
|
||||
const newType = ev.target.selectedItem.innerHTML;
|
||||
const oldType = getType(this.props.action);
|
||||
|
||||
if (oldType !== newType) {
|
||||
this.props.onChange(this.props.index, {
|
||||
platform: newType,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onDelete() {
|
||||
// eslint-disable-next-line
|
||||
if (confirm('Sure you want to delete?')) {
|
||||
this.props.onChange(this.props.index, null);
|
||||
}
|
||||
}
|
||||
|
||||
render({ index, action, onChange }) {
|
||||
const type = getType(action);
|
||||
const Comp = TYPES[type];
|
||||
const selected = OPTIONS.indexOf(type);
|
||||
let content;
|
||||
|
||||
if (Comp) {
|
||||
content = (
|
||||
<div>
|
||||
<paper-dropdown-menu-light label="Action Type" no-animations>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
selected={selected}
|
||||
oniron-select={this.typeChanged}
|
||||
>
|
||||
{OPTIONS.map(opt => <paper-item>{opt}</paper-item>)}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu-light>
|
||||
<Comp
|
||||
index={index}
|
||||
action={action}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
content = (
|
||||
<div>
|
||||
Unsupported action
|
||||
<pre>{JSON.stringify(action, null, 2)}</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<paper-card>
|
||||
<div class='card-menu'>
|
||||
<paper-menu-button
|
||||
no-animations
|
||||
horizontal-align="right"
|
||||
horizontal-offset="-5"
|
||||
vertical-offset="-5"
|
||||
>
|
||||
<paper-icon-button
|
||||
icon="mdi:dots-vertical"
|
||||
slot="dropdown-trigger"
|
||||
/>
|
||||
<paper-listbox slot="dropdown-content">
|
||||
<paper-item disabled>Duplicate</paper-item>
|
||||
<paper-item onTap={this.onDelete}>Delete</paper-item>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
</div>
|
||||
<div class='card-content'>{content}</div>
|
||||
</paper-card>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
41
js/editor/trigger/event.js
Normal file
41
js/editor/trigger/event.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import JSONTextArea from '../json_textarea';
|
||||
|
||||
import { onChange } from './util';
|
||||
|
||||
export default class EventTrigger extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChange.bind(this);
|
||||
this.eventDataChanged = this.eventDataChanged.bind(this);
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
eventDataChanged(event_data) {
|
||||
this.props.onChange(this.props.index, {
|
||||
...this.props.trigger,
|
||||
event_data,
|
||||
});
|
||||
}
|
||||
|
||||
render({ trigger }) {
|
||||
const { event_type, event_data } = trigger;
|
||||
return (
|
||||
<div>
|
||||
<paper-input
|
||||
label="Event Type"
|
||||
name="event_type"
|
||||
value={event_type}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Event Data
|
||||
<JSONTextArea
|
||||
value={event_data}
|
||||
onChange={this.eventDataChanged}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
50
js/editor/trigger/index.js
Normal file
50
js/editor/trigger/index.js
Normal file
@@ -0,0 +1,50 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import TriggerRow from './trigger_row';
|
||||
|
||||
export default class Trigger extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.addTrigger = this.addTrigger.bind(this);
|
||||
this.triggerChanged = this.triggerChanged.bind(this);
|
||||
}
|
||||
|
||||
addTrigger() {
|
||||
const trigger = this.props.trigger.concat({
|
||||
platform: 'event',
|
||||
});
|
||||
|
||||
this.props.onChange(trigger);
|
||||
}
|
||||
|
||||
triggerChanged(index, newValue) {
|
||||
const trigger = this.props.trigger.concat();
|
||||
|
||||
if (newValue === null) {
|
||||
trigger.splice(index, 1);
|
||||
} else {
|
||||
trigger[index] = newValue;
|
||||
}
|
||||
|
||||
this.props.onChange(trigger);
|
||||
}
|
||||
|
||||
render({ trigger }) {
|
||||
return (
|
||||
<div class="triggers">
|
||||
{trigger.map((trg, idx) => (
|
||||
<TriggerRow
|
||||
index={idx}
|
||||
trigger={trg}
|
||||
onChange={this.triggerChanged}
|
||||
/>))}
|
||||
<paper-card>
|
||||
<div class='card-actions add-card'>
|
||||
<paper-button onTap={this.addTrigger}>Add trigger</paper-button>
|
||||
</div>
|
||||
</paper-card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
45
js/editor/trigger/numeric_state.js
Normal file
45
js/editor/trigger/numeric_state.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChange } from './util';
|
||||
|
||||
export default class NumericStateTrigger extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChange.bind(this);
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
render({ trigger }) {
|
||||
const { value_template, entity_id, below, above } = trigger;
|
||||
return (
|
||||
<div>
|
||||
<paper-input
|
||||
label="Entity Id"
|
||||
name="entity_id"
|
||||
value={entity_id}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<paper-input
|
||||
label="Above"
|
||||
name="above"
|
||||
value={above}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<paper-input
|
||||
label="Below"
|
||||
name="below"
|
||||
value={below}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
Value template (optional)<br />
|
||||
<textarea
|
||||
name="value_template"
|
||||
value={value_template}
|
||||
style={{ width: '100%', height: 100 }}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
41
js/editor/trigger/state.js
Normal file
41
js/editor/trigger/state.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { onChange } from './util';
|
||||
|
||||
export default class StateTrigger extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = onChange.bind(this);
|
||||
}
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
render({ trigger }) {
|
||||
const { entity_id, to } = trigger;
|
||||
const trgFrom = trigger.from;
|
||||
const trgFor = trigger.for;
|
||||
return (
|
||||
<div>
|
||||
<paper-input
|
||||
label="Entity Id"
|
||||
name="entity_id"
|
||||
value={entity_id}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<paper-input
|
||||
label="From"
|
||||
name="from"
|
||||
value={trgFrom}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<paper-input
|
||||
label="To"
|
||||
name="to"
|
||||
value={to}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
{trgFor && <pre>For: {JSON.stringify(trgFor, null, 2)}</pre>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
103
js/editor/trigger/trigger_row.js
Normal file
103
js/editor/trigger/trigger_row.js
Normal file
@@ -0,0 +1,103 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import EventTrigger from './event';
|
||||
import StateTrigger from './state';
|
||||
import NumericStateTrigger from './numeric_state';
|
||||
|
||||
const TYPES = {
|
||||
event: EventTrigger,
|
||||
state: StateTrigger,
|
||||
homeassistant: null,
|
||||
mqtt: null,
|
||||
numeric_state: NumericStateTrigger,
|
||||
sun: null,
|
||||
template: null,
|
||||
time: null,
|
||||
zone: null,
|
||||
};
|
||||
|
||||
const OPTIONS = Object.keys(TYPES).sort();
|
||||
|
||||
export default class TriggerRow extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.typeChanged = this.typeChanged.bind(this);
|
||||
this.onDelete = this.onDelete.bind(this);
|
||||
}
|
||||
|
||||
typeChanged(ev) {
|
||||
const type = ev.target.selectedItem.innerHTML;
|
||||
|
||||
if (type !== this.props.trigger.platform) {
|
||||
this.props.onChange(this.props.index, {
|
||||
platform: type,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onDelete() {
|
||||
// eslint-disable-next-line
|
||||
if (confirm('Sure you want to delete?')) {
|
||||
this.props.onChange(this.props.index, null);
|
||||
}
|
||||
}
|
||||
|
||||
render({ index, trigger, onChange }) {
|
||||
const Comp = TYPES[trigger.platform];
|
||||
const selected = OPTIONS.indexOf(trigger.platform);
|
||||
|
||||
let content;
|
||||
|
||||
if (Comp) {
|
||||
content = (
|
||||
<div>
|
||||
<paper-dropdown-menu-light label="Trigger Type" no-animations>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
selected={selected}
|
||||
oniron-select={this.typeChanged}
|
||||
>
|
||||
{OPTIONS.map(opt => <paper-item>{opt}</paper-item>)}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu-light>
|
||||
<Comp
|
||||
index={index}
|
||||
trigger={trigger}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
content = (
|
||||
<div>
|
||||
Unsupported platform: {trigger.platform}
|
||||
<pre>{JSON.stringify(trigger, null, 2)}</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<paper-card>
|
||||
<div class='card-menu'>
|
||||
<paper-menu-button
|
||||
no-animations
|
||||
horizontal-align="right"
|
||||
horizontal-offset="-5"
|
||||
vertical-offset="-5"
|
||||
>
|
||||
<paper-icon-button
|
||||
icon="mdi:dots-vertical"
|
||||
slot="dropdown-trigger"
|
||||
/>
|
||||
<paper-listbox slot="dropdown-content">
|
||||
<paper-item disabled>Duplicate</paper-item>
|
||||
<paper-item onTap={this.onDelete}>Delete</paper-item>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
</div>
|
||||
<div class='card-content'>{content}</div>
|
||||
</paper-card>
|
||||
);
|
||||
}
|
||||
}
|
||||
11
js/editor/trigger/util.js
Normal file
11
js/editor/trigger/util.js
Normal file
@@ -0,0 +1,11 @@
|
||||
export function onChange(ev) {
|
||||
const trigger = { ...this.props.trigger };
|
||||
|
||||
if (ev.target.value) {
|
||||
trigger[ev.target.name] = ev.target.value;
|
||||
} else {
|
||||
delete trigger[ev.target.name];
|
||||
}
|
||||
|
||||
this.props.onChange(this.props.index, trigger);
|
||||
}
|
||||
3
js/editor/util.js
Normal file
3
js/editor/util.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export function validEntityId(entityId) {
|
||||
return /^(\w+)\.(\w+)$/.test(entityId);
|
||||
}
|
||||
Reference in New Issue
Block a user