Skip to content

Commit 693e722

Browse files
committed
feat(#77): Basic visual editor support
1 parent 10c0617 commit 693e722

File tree

5 files changed

+50
-231
lines changed

5 files changed

+50
-231
lines changed

src/editor.ts

Lines changed: 36 additions & 226 deletions
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,14 @@ import { HomeAssistant, fireEvent, LovelaceCardEditor, ActionConfig } from 'cust
44

55
import { SankeyChartConfig } from './types';
66
import { customElement, property, state } from 'lit/decorators';
7+
import { localize } from './localize/localize';
78

8-
const options = {
9-
required: {
10-
icon: 'tune',
11-
name: 'Required',
12-
secondary: 'Required options for this card to function',
13-
show: true,
14-
},
15-
actions: {
16-
icon: 'gesture-tap-hold',
17-
name: 'Actions',
18-
secondary: 'Perform actions based on tapping/clicking',
19-
show: false,
20-
options: {
21-
tap: {
22-
icon: 'gesture-tap',
23-
name: 'Tap',
24-
secondary: 'Set the action to perform on tap',
25-
show: false,
26-
},
27-
hold: {
28-
icon: 'gesture-tap-hold',
29-
name: 'Hold',
30-
secondary: 'Set the action to perform on hold',
31-
show: false,
32-
},
33-
double_tap: {
34-
icon: 'gesture-double-tap',
35-
name: 'Double Tap',
36-
secondary: 'Set the action to perform on double tap',
37-
show: false,
38-
},
39-
},
40-
},
41-
appearance: {
42-
icon: 'palette',
43-
name: 'Appearance',
44-
secondary: 'Customize the name, icon, etc',
45-
show: false,
46-
},
47-
};
9+
type MutateConfigFn = (config: SankeyChartConfig) => SankeyChartConfig;
4810

4911
@customElement('sankey-chart-editor')
5012
export class SankeyChartEditor extends LitElement implements LovelaceCardEditor {
5113
@property({ attribute: false }) public hass?: HomeAssistant;
5214
@state() private _config?: SankeyChartConfig;
53-
@state() private _toggle?: boolean;
5415
@state() private _helpers?: any;
5516
private _initialized = false;
5617

@@ -68,162 +29,45 @@ export class SankeyChartEditor extends LitElement implements LovelaceCardEditor
6829
return true;
6930
}
7031

71-
get _name(): string {
72-
return this._config?.name || '';
73-
}
74-
75-
get _entity(): string {
76-
return this._config?.entity || '';
77-
}
78-
79-
get _show_warning(): boolean {
80-
return this._config?.show_warning || false;
81-
}
82-
83-
get _show_error(): boolean {
84-
return this._config?.show_error || false;
85-
}
86-
87-
get _tap_action(): ActionConfig {
88-
return this._config?.tap_action || { action: 'more-info' };
89-
}
90-
91-
get _hold_action(): ActionConfig {
92-
return this._config?.hold_action || { action: 'none' };
93-
}
94-
95-
get _double_tap_action(): ActionConfig {
96-
return this._config?.double_tap_action || { action: 'none' };
97-
}
98-
9932
protected render(): TemplateResult | void {
10033
if (!this.hass || !this._helpers) {
10134
return html``;
10235
}
10336

10437
// The climate more-info has ha-switch and paper-dropdown-menu elements that are lazy loaded unless explicitly done here
105-
this._helpers.importMoreInfoControl('climate');
38+
// this._helpers.importMoreInfoControl('climate');
10639

10740
// You can restrict on domain type
108-
const entities = Object.keys(this.hass.states).filter(eid => eid.substr(0, eid.indexOf('.')) === 'sun');
41+
// const entities = Object.keys(this.hass.states).filter(eid => eid.substr(0, eid.indexOf('.')) === 'sun');
10942

11043
return html`
11144
<div class="card-config">
112-
<div class="option" @click=${this._toggleOption} .option=${'required'}>
113-
<div class="row">
114-
<ha-icon .icon=${`mdi:${options.required.icon}`}></ha-icon>
115-
<div class="title">${options.required.name}</div>
116-
</div>
117-
<div class="secondary">${options.required.secondary}</div>
118-
</div>
119-
${options.required.show
120-
? html`
121-
<div class="values">
122-
<paper-dropdown-menu
123-
label="Entity (Required)"
124-
@value-changed=${this._valueChanged}
125-
.configValue=${'entity'}
126-
>
127-
<paper-listbox slot="dropdown-content" .selected=${entities.indexOf(this._entity)}>
128-
${entities.map(entity => {
129-
return html`
130-
<paper-item>${entity}</paper-item>
131-
`;
132-
})}
133-
</paper-listbox>
134-
</paper-dropdown-menu>
135-
</div>
136-
`
137-
: ''}
138-
<div class="option" @click=${this._toggleOption} .option=${'actions'}>
139-
<div class="row">
140-
<ha-icon .icon=${`mdi:${options.actions.icon}`}></ha-icon>
141-
<div class="title">${options.actions.name}</div>
142-
</div>
143-
<div class="secondary">${options.actions.secondary}</div>
45+
<div class="values">
46+
<h3>${localize('editor.autoconfig')}</h3>
47+
<ha-formfield .label=${localize('editor.enable')}>
48+
<ha-switch
49+
.checked=${!!this._config?.autoconfig}
50+
.configValue=${(conf, val) => {
51+
const newConf = { ...conf };
52+
if (val && !conf.autoconfig) {
53+
newConf.autoconfig = { print_yaml: false };
54+
} else if (!val && conf.autoconfig) {
55+
delete newConf.autoconfig;
56+
}
57+
return newConf;
58+
}}
59+
@change=${this._valueChanged}
60+
></ha-switch>
61+
</ha-formfield>
62+
<ha-formfield .label=${localize('editor.print')}>
63+
<ha-switch
64+
.checked=${!!this._config?.autoconfig?.print_yaml}
65+
.configValue=${(conf, print_yaml) => ({ ...conf, autoconfig: { print_yaml } })}
66+
@change=${this._valueChanged}
67+
></ha-switch>
68+
</ha-formfield>
14469
</div>
145-
${options.actions.show
146-
? html`
147-
<div class="values">
148-
<div class="option" @click=${this._toggleAction} .option=${'tap'}>
149-
<div class="row">
150-
<ha-icon .icon=${`mdi:${options.actions.options.tap.icon}`}></ha-icon>
151-
<div class="title">${options.actions.options.tap.name}</div>
152-
</div>
153-
<div class="secondary">${options.actions.options.tap.secondary}</div>
154-
</div>
155-
${options.actions.options.tap.show
156-
? html`
157-
<div class="values">
158-
<paper-item>Action Editors Coming Soon</paper-item>
159-
</div>
160-
`
161-
: ''}
162-
<div class="option" @click=${this._toggleAction} .option=${'hold'}>
163-
<div class="row">
164-
<ha-icon .icon=${`mdi:${options.actions.options.hold.icon}`}></ha-icon>
165-
<div class="title">${options.actions.options.hold.name}</div>
166-
</div>
167-
<div class="secondary">${options.actions.options.hold.secondary}</div>
168-
</div>
169-
${options.actions.options.hold.show
170-
? html`
171-
<div class="values">
172-
<paper-item>Action Editors Coming Soon</paper-item>
173-
</div>
174-
`
175-
: ''}
176-
<div class="option" @click=${this._toggleAction} .option=${'double_tap'}>
177-
<div class="row">
178-
<ha-icon .icon=${`mdi:${options.actions.options.double_tap.icon}`}></ha-icon>
179-
<div class="title">${options.actions.options.double_tap.name}</div>
180-
</div>
181-
<div class="secondary">${options.actions.options.double_tap.secondary}</div>
182-
</div>
183-
${options.actions.options.double_tap.show
184-
? html`
185-
<div class="values">
186-
<paper-item>Action Editors Coming Soon</paper-item>
187-
</div>
188-
`
189-
: ''}
190-
</div>
191-
`
192-
: ''}
193-
<div class="option" @click=${this._toggleOption} .option=${'appearance'}>
194-
<div class="row">
195-
<ha-icon .icon=${`mdi:${options.appearance.icon}`}></ha-icon>
196-
<div class="title">${options.appearance.name}</div>
197-
</div>
198-
<div class="secondary">${options.appearance.secondary}</div>
199-
</div>
200-
${options.appearance.show
201-
? html`
202-
<div class="values">
203-
<paper-input
204-
label="Name (Optional)"
205-
.value=${this._name}
206-
.configValue=${'name'}
207-
@value-changed=${this._valueChanged}
208-
></paper-input>
209-
<br />
210-
<ha-formfield .label=${`Toggle warning ${this._show_warning ? 'off' : 'on'}`}>
211-
<ha-switch
212-
.checked=${this._show_warning !== false}
213-
.configValue=${'show_warning'}
214-
@change=${this._valueChanged}
215-
></ha-switch>
216-
</ha-formfield>
217-
<ha-formfield .label=${`Toggle error ${this._show_error ? 'off' : 'on'}`}>
218-
<ha-switch
219-
.checked=${this._show_error !== false}
220-
.configValue=${'show_error'}
221-
@change=${this._valueChanged}
222-
></ha-switch>
223-
</ha-formfield>
224-
</div>
225-
`
226-
: ''}
70+
<p>${localize('editor.yaml_disclaimer')}</p>
22771
</div>
22872
`;
22973
}
@@ -239,33 +83,15 @@ export class SankeyChartEditor extends LitElement implements LovelaceCardEditor
23983
this._helpers = await (window as any).loadCardHelpers();
24084
}
24185

242-
private _toggleAction(ev): void {
243-
this._toggleThing(ev, options.actions.options);
244-
}
245-
246-
private _toggleOption(ev): void {
247-
this._toggleThing(ev, options);
248-
}
249-
250-
private _toggleThing(ev, optionList): void {
251-
const show = !optionList[ev.target.option].show;
252-
for (const [key] of Object.entries(optionList)) {
253-
optionList[key].show = false;
254-
}
255-
optionList[ev.target.option].show = show;
256-
this._toggle = !this._toggle;
257-
}
258-
25986
private _valueChanged(ev): void {
26087
if (!this._config || !this.hass) {
26188
return;
26289
}
26390
const target = ev.target;
264-
if (this[`_${target.configValue}`] === target.value) {
265-
return;
266-
}
26791
if (target.configValue) {
268-
if (target.value === '') {
92+
if (typeof target.configValue === 'function') {
93+
this._config = target.configValue(this._config, target.checked !== undefined ? target.checked : target.value);
94+
} else if (target.value === '') {
26995
const tmpConfig = { ...this._config };
27096
delete tmpConfig[target.configValue];
27197
this._config = tmpConfig;
@@ -281,29 +107,13 @@ export class SankeyChartEditor extends LitElement implements LovelaceCardEditor
281107

282108
static get styles(): CSSResultGroup {
283109
return css`
284-
.option {
285-
padding: 4px 0px;
286-
cursor: pointer;
287-
}
288-
.row {
289-
display: flex;
290-
margin-bottom: -14px;
291-
pointer-events: none;
292-
}
293-
.title {
294-
padding-left: 16px;
295-
margin-top: -6px;
296-
pointer-events: none;
297-
}
298-
.secondary {
299-
padding-left: 40px;
300-
color: var(--secondary-text-color);
301-
pointer-events: none;
110+
.card-config {
111+
padding: 16px;
112+
background: var(--secondary-background-color);
302113
}
303114
.values {
304-
padding-left: 16px;
305-
background: var(--secondary-background-color);
306115
display: grid;
116+
margin-bottom: 20px;
307117
}
308118
ha-formfield {
309119
padding-bottom: 8px;

src/editor.ts.old

Whitespace-only changes.

src/ha-sankey-chart.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
} from './energy';
2020
import { until } from 'lit/directives/until';
2121
import { getEntitiesByArea, HomeAssistantReal } from './hass';
22+
import { LovelaceCardEditor } from 'custom-card-helpers';
2223

2324
/* eslint no-console: 0 */
2425
console.info(
@@ -40,12 +41,13 @@ const ENERGY_DATA_TIMEOUT = 10000;
4041

4142
@customElement('sankey-chart')
4243
export class SankeyChart extends SubscribeMixin(LitElement) {
43-
// public static async getConfigElement(): Promise<LovelaceCardEditor> {
44-
// return document.createElement('sankey-chart-editor');
45-
// }
44+
public static async getConfigElement(): Promise<LovelaceCardEditor> {
45+
await import('./editor');
46+
return document.createElement('sankey-chart-editor');
47+
}
4648

4749
public static getStubConfig(): Record<string, unknown> {
48-
return {};
50+
return { autoconfig: { print_yaml: false } };
4951
}
5052

5153
// https://lit.dev/docs/components/properties/

src/localize/languages/en.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,11 @@
66
"entity_not_found": "Entity state not found",
77
"missing_child": "Missing child entity",
88
"loading": "Loading..."
9+
},
10+
"editor": {
11+
"yaml_disclaimer": "Please use yaml mode for the other options",
12+
"autoconfig": "Autoconfig",
13+
"enable": "Enable",
14+
"print": "Print auto generated config yaml"
915
}
1016
}

src/localize/languages/nl.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
"entity_not_found": "Entiteit niet gevonden",
77
"missing_child": "Ontbrekende onderliggende entiteit",
88
"loading": "Laden..."
9-
}
9+
},
10+
"editor": {}
1011
}

0 commit comments

Comments
 (0)