Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"query-selector-shadow-dom": "^1.0.1"
},
"devDependencies": {
"@types/query-selector-shadow-dom": "^1.0.4",
"custom-card-helpers": "^1.9.0",
"esbuild": "^0.20.1",
"lit": "^3.1.2"
Expand Down
131 changes: 67 additions & 64 deletions src/combined-card-editor.ts
Original file line number Diff line number Diff line change
@@ -1,88 +1,91 @@
import { html, LitElement } from "lit";
import { HomeAssistant, LovelaceCardConfig, LovelaceCardEditor, LovelaceConfig } from 'custom-card-helpers';
import { NAME, EDITOR_NAME, LOG_EDITOR as LOG } from './utils';

class CombinedCardEditor extends LitElement implements LovelaceCardEditor {
private _hass?: HomeAssistant;
private _lovelace?: LovelaceConfig;
private _stackCardEditor?;
private _config = {};

private _setEditorConfig(config: LovelaceCardConfig) {
// @ts-ignore
if (this._stackCardEditor) {
this._stackCardEditor.setConfig({
type: 'vertical-stack',
cards: config.cards || []
});
import { LOG_EDITOR as LOG } from './utils';

export const editorFactory = (NAME: string) => {
class CombinedCardEditor extends LitElement implements LovelaceCardEditor {
private _hass?: HomeAssistant;
private _lovelace?: LovelaceConfig;
private _stackCardEditor?;
private _config = {};

private _setEditorConfig(config: LovelaceCardConfig) {
// @ts-ignore
if (this._stackCardEditor) {
this._stackCardEditor.setConfig({
type: 'vertical-stack',
cards: config.cards || []
});
}
}
}

setConfig(config: LovelaceCardConfig): void {
this._config = {
// I think this won't allow removing the hidden values
// ...this._config,
...config
};
setConfig(config: LovelaceCardConfig): void {
this._config = {
// I think this won't allow removing the hidden values
// ...this._config,
...config
};

this._setEditorConfig(this._config as LovelaceCardConfig);
}
this._setEditorConfig(this._config as LovelaceCardConfig);
}

configChanged(newConfig: LovelaceCardConfig): void {
const event = new Event('config-changed', {
bubbles: true,
composed: true
});
configChanged(newConfig: LovelaceCardConfig): void {
const event = new Event('config-changed', {
bubbles: true,
composed: true
});

// @ts-ignore
event.detail = { config: newConfig };
// @ts-ignore
event.detail = { config: newConfig };

this.dispatchEvent(event);
}
this.dispatchEvent(event);
}

protected render() {
LOG('render', this._stackCardEditor);
protected render() {
LOG('render', this._stackCardEditor);

if (this._hass) {
this._stackCardEditor.hass = this._hass;
}
if (this._hass) {
this._stackCardEditor.hass = this._hass;
}

if (this._lovelace) {
this._stackCardEditor.lovelace = this._lovelace;
}
if (this._lovelace) {
this._stackCardEditor.lovelace = this._lovelace;
}

this._stackCardEditor.addEventListener('config-changed', ev => {
ev.stopPropagation();
this._stackCardEditor.addEventListener('config-changed', ev => {
ev.stopPropagation();

this.configChanged({
...this._config,
...ev.detail.config,
type: `custom:${NAME}`
this.configChanged({
...this._config,
...ev.detail.config,
type: `custom:${NAME}`
});
});
});

return html`<div>${this._stackCardEditor}</div>`;
}
return html`<div>${this._stackCardEditor}</div>`;
}

set hass(hass: HomeAssistant) {
this._hass = hass;
set hass(hass: HomeAssistant) {
this._hass = hass;

if (this._stackCardEditor) {
this._stackCardEditor.hass = hass;
if (this._stackCardEditor) {
this._stackCardEditor.hass = hass;
}
}
}

set lovelace(ll: LovelaceConfig) {
this._lovelace = ll;
set lovelace(ll: LovelaceConfig) {
this._lovelace = ll;

if (this._stackCardEditor) {
this._stackCardEditor.lovelace = ll;
if (this._stackCardEditor) {
this._stackCardEditor.lovelace = ll;
}
}
}

set cardEditor(editor) {
this._stackCardEditor = editor;
set cardEditor(editor) {
this._stackCardEditor = editor;
}
}
}

customElements.define(EDITOR_NAME, CombinedCardEditor);
return CombinedCardEditor;
};

18 changes: 15 additions & 3 deletions src/combined-card.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import { css, CSSResultGroup, html, LitElement } from "lit";
import { state } from "lit/decorators.js";
import { css, CSSResultGroup, html, LitElement } from 'lit';
import { state } from 'lit/decorators.js';
import { HomeAssistant, LovelaceCardConfig, LovelaceCard } from 'custom-card-helpers';
import { NAME, EDITOR_NAME, HELPERS, LOG, loadStackEditor, sleep } from './utils';
import { HELPERS, LOG, loadStackEditor, sleep } from './utils';

import { editorFactory } from "./combined-card-editor";

const NAME = 'combined-card';
const EDITOR_NAME = `${NAME}-editor`;

export const card = {
type: NAME,
name: "Catdad: Combined Card",
description: "Combine a stack of cards into a single seamless card",
};

const getRandomId = (): string => Math.random().toString(36).slice(2);

Expand Down Expand Up @@ -229,3 +240,4 @@ class CombinedCard extends LitElement implements LovelaceCard {
}

customElements.define(NAME, CombinedCard);
customElements.define(EDITOR_NAME, editorFactory(NAME));
147 changes: 147 additions & 0 deletions src/kiosk-card.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { css, CSSResultGroup, html, LitElement } from "lit";
import { state } from "lit/decorators.js";
import { querySelectorDeep } from "query-selector-shadow-dom";
import { HomeAssistant, LovelaceCardConfig, LovelaceCard } from 'custom-card-helpers';
import { LOG } from './utils';

const NAME = 'kiosk-card';
const EDITOR_NAME = `${NAME}-editor`;

export const card = {
type: NAME,
name: 'Catdad: Kiosk Card',
description: 'Hide the navigation UI for the dashboard where this card is rendered'
};

class KioskCard extends LitElement implements LovelaceCard {
@state() private _config?: LovelaceCardConfig;
@state() private _editMode: boolean = false;

private _hass?: HomeAssistant;

set hass(hass: HomeAssistant) {
this._hass = hass;
}

set editMode(editMode: boolean) {
this._editMode = editMode;

if (editMode) {
this.disable();
} else {
this.enable();
}
}

public async getCardSize(): Promise<number> {
return 4;
}

public setConfig(config: LovelaceCardConfig): void {
this._config = Object.assign({}, KioskCard.getStubConfig(), config);
}

private enable(): void {
try {
const header = querySelectorDeep('ha-panel-lovelace .header');
const view = querySelectorDeep('ha-panel-lovelace hui-view-container');
const thisCard = querySelectorDeep('.catdad-kiosk-card');

// LOG('kiosk mode got elements:', { header, view, thisCard });

// when this card is not being rendered, it should not apply kiosk mode
if (!thisCard) {
return;
}

if (!header || !view) {
throw new Error('could not find necessary elements to apply kiosk mode');
}

if (this._editMode) {
header.style.removeProperty('display');
view.style.removeProperty('padding-top');
} else {
header.style.display = 'none';
view.style.paddingTop = '0px';
}
} catch (e) {
LOG('failed to connect kiosk mode', e);
}
}

private disable(): void {
try {
const header = querySelectorDeep('ha-panel-lovelace .header');
const view = querySelectorDeep('ha-panel-lovelace hui-view-container');

// LOG('kiosk mode got elements:', { header, view });

if (!header || !view) {
throw new Error('could not find necessary elements to disconnect kiosk mode');
}

header.style.removeProperty('display');
view.style.removeProperty('padding-top');
} catch (e) {
LOG('failed to disconnect kiosk mode', e);
}
}

connectedCallback(): void {
super.connectedCallback()
// LOG('Kiosk card connected', this._editMode);
this.enable();
}

disconnectedCallback(): void {
super.disconnectedCallback();
// LOG('Kiosk card disconnected');
this.disable();
}

protected render() {
const styles = [
'padding: var(--spacing, 12px)',
'display: flex',
'align-items: center',
'justify-content: center',
];

return html`
<ha-card style=${`${this._editMode ? '' : 'display: none'}`}>
<div class="catdad-kiosk-card" style=${styles.join(';')}>Kiosk mode card</div>
</ha-card>
`;
}

static get styles(): CSSResultGroup {
return css`
ha-card {
overflow: hidden;
}
`;
}

public static getConfigElement() {
return document.createElement(EDITOR_NAME);
}

static getStubConfig() {
return {
type: `custom:${NAME}`,
};
}
}

class KioskCardEditor extends LitElement {
setConfig() {}
configChanged() {}

render() {
return html`<div>This card has no options!</div>`;
}
}

customElements.define(NAME, KioskCard);
customElements.define(EDITOR_NAME, KioskCardEditor);
12 changes: 3 additions & 9 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { NAME } from './utils';

import "./combined-card";
import "./combined-card-editor";
import { card as combinedCard } from "./combined-card";
import { card as kioskCard } from "./kiosk-card";

// Note: this is what adds the card to the UI card selector
(window as any).customCards = (window as any).customCards || [];
(window as any).customCards.push({
type: NAME,
name: "Combined Card",
description: "Combine a stack of cards into a single seamless card",
});
(window as any).customCards.push(combinedCard, kioskCard);