From b3f9432ae1bc53d4ebb91641e64469e5a543a151 Mon Sep 17 00:00:00 2001 From: yosilevy <37745463+yosilevy@users.noreply.github.com> Date: Thu, 28 Mar 2019 06:10:07 +0200 Subject: [PATCH] Tab fix in yaml-editor edit card (#3008) * Fixed tabs not working in yaml editor in edit card * Improved docs * Fixed comments * Added dependencies * Added typescript mapping * Fixed data type issue + removed depednency since it breaks the UI. Non final. * Added iron-overlay-behavior package * Added dependency * Update iron-overlay-behavior * Lint --- package.json | 4 +- .../lovelace/components/dialog/ha-dialog.ts | 26 ++++++ .../dialog/ha-iron-focusables-helper.js | 90 +++++++++++++++++++ .../lovelace/components/hui-yaml-editor.ts | 4 + .../editor/card-editor/hui-edit-card.ts | 13 +-- yarn.lock | 8 +- 6 files changed, 134 insertions(+), 11 deletions(-) create mode 100644 src/panels/lovelace/components/dialog/ha-dialog.ts create mode 100644 src/panels/lovelace/components/dialog/ha-iron-focusables-helper.js diff --git a/package.json b/package.json index dd9ac63620..d049b1ca4b 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "@polymer/iron-input": "^3.0.1", "@polymer/iron-label": "^3.0.1", "@polymer/iron-media-query": "^3.0.1", + "@polymer/iron-overlay-behavior": "^3.0.2", "@polymer/iron-pages": "^3.0.1", "@polymer/iron-resizable-behavior": "^3.0.1", "@polymer/neon-animation": "^3.0.1", @@ -167,7 +168,8 @@ "@webcomponents/webcomponentsjs": "^2.2.6", "@webcomponents/shadycss": "^1.9.0", "@vaadin/vaadin-overlay": "3.2.2", - "@vaadin/vaadin-lumo-styles": "1.3.0" + "@vaadin/vaadin-lumo-styles": "1.3.0", + "@polymer/iron-overlay-behavior": "^3.0.2" }, "main": "src/home-assistant.js", "husky": { diff --git a/src/panels/lovelace/components/dialog/ha-dialog.ts b/src/panels/lovelace/components/dialog/ha-dialog.ts new file mode 100644 index 0000000000..25be5b934e --- /dev/null +++ b/src/panels/lovelace/components/dialog/ha-dialog.ts @@ -0,0 +1,26 @@ +import "@polymer/paper-dialog/paper-dialog"; +import { mixinBehaviors } from "@polymer/polymer/lib/legacy/class"; +import { HaIronFocusablesHelper } from "./ha-iron-focusables-helper.js"; + +const paperDialogClass = customElements.get("paper-dialog"); + +// behavior that will override existing iron-overlay-behavior and call the fixed implementation +const haTabFixBehaviorImpl = { + get _focusableNodes() { + return HaIronFocusablesHelper.getTabbableNodes(this); + }, +}; + +// paper-dialog that uses the haTabFixBehaviorImpl behvaior +// export class HaPaperDialog extends paperDialogClass {} +export class HaPaperDialog extends mixinBehaviors( + [haTabFixBehaviorImpl], + paperDialogClass +) {} + +declare global { + interface HTMLElementTagNameMap { + "ha-paper-dialog": HaPaperDialog; + } +} +customElements.define("ha-paper-dialog", HaPaperDialog); diff --git a/src/panels/lovelace/components/dialog/ha-iron-focusables-helper.js b/src/panels/lovelace/components/dialog/ha-iron-focusables-helper.js new file mode 100644 index 0000000000..4ae78090e5 --- /dev/null +++ b/src/panels/lovelace/components/dialog/ha-iron-focusables-helper.js @@ -0,0 +1,90 @@ +/** +@license +Copyright (c) 2016 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at +http://polymer.github.io/LICENSE.txt The complete set of authors may be found at +http://polymer.github.io/AUTHORS.txt The complete set of contributors may be +found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as +part of the polymer project is also subject to an additional IP rights grant +found at http://polymer.github.io/PATENTS.txt +*/ +/* + Fixes issue with not using shadow dom properly in iron-overlay-behavior/icon-focusables-helper.js +*/ +import { dom } from "@polymer/polymer/lib/legacy/polymer.dom.js"; + +import { IronFocusablesHelper } from "@polymer/iron-overlay-behavior/iron-focusables-helper.js"; + +export const HaIronFocusablesHelper = { + /** + * Returns a sorted array of tabbable nodes, including the root node. + * It searches the tabbable nodes in the light and shadow dom of the chidren, + * sorting the result by tabindex. + * @param {!Node} node + * @return {!Array} + */ + getTabbableNodes: function(node) { + var result = []; + // If there is at least one element with tabindex > 0, we need to sort + // the final array by tabindex. + var needsSortByTabIndex = this._collectTabbableNodes(node, result); + if (needsSortByTabIndex) { + return IronFocusablesHelper._sortByTabIndex(result); + } + return result; + }, + + /** + * Searches for nodes that are tabbable and adds them to the `result` array. + * Returns if the `result` array needs to be sorted by tabindex. + * @param {!Node} node The starting point for the search; added to `result` + * if tabbable. + * @param {!Array} result + * @return {boolean} + * @private + */ + _collectTabbableNodes: function(node, result) { + // If not an element or not visible, no need to explore children. + if ( + node.nodeType !== Node.ELEMENT_NODE || + !IronFocusablesHelper._isVisible(node) + ) { + return false; + } + var element = /** @type {!HTMLElement} */ (node); + var tabIndex = IronFocusablesHelper._normalizedTabIndex(element); + var needsSort = tabIndex > 0; + if (tabIndex >= 0) { + result.push(element); + } + + // In ShadowDOM v1, tab order is affected by the order of distrubution. + // E.g. getTabbableNodes(#root) in ShadowDOM v1 should return [#A, #B]; + // in ShadowDOM v0 tab order is not affected by the distrubution order, + // in fact getTabbableNodes(#root) returns [#B, #A]. + //
+ // + // + // + // + // + // + //
+ // TODO(valdrin) support ShadowDOM v1 when upgrading to Polymer v2.0. + var children; + if (element.localName === "content" || element.localName === "slot") { + children = dom(element).getDistributedNodes(); + } else { + // ///////////////////////// + // Use shadow root if possible, will check for distributed nodes. + // THIS IS THE CHANGED LINE + children = dom(element.shadowRoot || element.root || element).children; + // ///////////////////////// + } + for (var i = 0; i < children.length; i++) { + // Ensure method is always invoked to collect tabbable children. + needsSort = this._collectTabbableNodes(children[i], result) || needsSort; + } + return needsSort; + }, +}; diff --git a/src/panels/lovelace/components/hui-yaml-editor.ts b/src/panels/lovelace/components/hui-yaml-editor.ts index d0efdf1eed..4f35f85d80 100644 --- a/src/panels/lovelace/components/hui-yaml-editor.ts +++ b/src/panels/lovelace/components/hui-yaml-editor.ts @@ -96,6 +96,10 @@ export class HuiYamlEditor extends HTMLElement { tabSize: 2, autofocus: true, viewportMargin: Infinity, + extraKeys: { + Tab: "indentMore", + "Shift-Tab": "indentLess", + }, gutters: this._hass && computeRTL(this._hass!) ? ["rtl-gutter", "CodeMirror-linenumbers"] diff --git a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts index d44b13f41b..93ce59eaf2 100644 --- a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts @@ -15,9 +15,10 @@ import { haStyleDialog } from "../../../../resources/styles"; import "@polymer/paper-spinner/paper-spinner"; import "@polymer/paper-dialog/paper-dialog"; +import "../../components/dialog/ha-dialog"; // This is not a duplicate import, one is for types, one is for element. // tslint:disable-next-line -import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog"; +import { HaPaperDialog } from "../../components/dialog/ha-dialog"; import "@material/mwc-button"; import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable"; import { HomeAssistant } from "../../../../types"; @@ -77,8 +78,8 @@ export class HuiEditCard extends LitElement { @property() private _errorMsg?: TemplateResult; - private get _dialog(): PaperDialogElement { - return this.shadowRoot!.querySelector("paper-dialog")!; + private get _dialog(): HaPaperDialog { + return this.shadowRoot!.querySelector("ha-paper-dialog")!; } private get _previewEl(): HuiCardPreview { @@ -147,7 +148,7 @@ export class HuiEditCard extends LitElement { } return html` - ` : ""} - + `; } @@ -218,7 +219,7 @@ export class HuiEditCard extends LitElement { private async _resizeDialog(): Promise { await this.updateComplete; - fireEvent(this._dialog, "iron-resize"); + fireEvent(this._dialog as HTMLElement, "iron-resize"); } private async _save(): Promise { diff --git a/yarn.lock b/yarn.lock index 954e3232a4..7ed5694ce9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1096,10 +1096,10 @@ dependencies: "@polymer/polymer" "^3.0.0" -"@polymer/iron-overlay-behavior@^3.0.0-pre.27": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@polymer/iron-overlay-behavior/-/iron-overlay-behavior-3.0.1.tgz#04746be26c7f0ac3b4e80ef363fcb478425dd677" - integrity sha512-7XDaPVmH90G/hiyWdwv0VG+E4B2xpiWGhql8ghHg7Ik1E4gNCZ7pdeTvjnawfycjrsod0ZezhKOFavYWAaXVxA== +"@polymer/iron-overlay-behavior@^3.0.0-pre.27", "@polymer/iron-overlay-behavior@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@polymer/iron-overlay-behavior/-/iron-overlay-behavior-3.0.2.tgz#6a12a3f4eab4721eb6978ed950be534c9c283983" + integrity sha512-j1qmt6mJHCwpe1mKOvqK5kcCUPQr5LSrlqpgRDbUuLgUfNJ/vGTipjrkBlfbEUagm5FEQdc1VLPLSQP6WVuP9g== dependencies: "@polymer/iron-a11y-keys-behavior" "^3.0.0-pre.26" "@polymer/iron-fit-behavior" "^3.0.0-pre.26"