blob: bcfaf0fe7463a8f315d601a6fa60dbb3b803829e [file] [log] [blame]
// Copyright (c) 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as i18n from '../i18n/i18n.js';
import * as SDK from '../sdk/sdk.js';
import * as UI from '../ui/ui.js';
import {ElementsPanel} from './ElementsPanel.js';
export const UIStrings = {
/**
*@description Text of a DOM element in Element State Pane Widget of the Elements panel
*/
forceElementState: 'Force element state',
/**
*@description Text in Element State Pane Widget of the Elements panel
*/
toggleElementState: 'Toggle Element State',
/**
*@description Text in Element State Pane Widget of the Elements panel
*/
hov: ':hov',
};
const str_ = i18n.i18n.registerUIStrings('elements/ElementStatePaneWidget.js', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
export class ElementStatePaneWidget extends UI.Widget.Widget {
constructor() {
super(true);
this.registerRequiredCSS('elements/elementStatePaneWidget.css', {enableLegacyPatching: true});
this.contentElement.className = 'styles-element-state-pane';
UI.UIUtils.createTextChild(this.contentElement.createChild('div'), i18nString(UIStrings.forceElementState));
const table = document.createElement('table');
table.classList.add('source-code');
UI.ARIAUtils.markAsPresentation(table);
/** @type {!Array<!HTMLInputElement>} */
const inputs = [];
this._inputs = inputs;
/** @type {!WeakMap<!HTMLInputElement,string>} */
this._inputStates = new WeakMap();
/**
* @param {!MouseEvent} event
*/
const clickListener = event => {
const node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
if (!node || !(event.target instanceof HTMLInputElement)) {
return;
}
const state = this._inputStates.get(event.target);
if (!state) {
return;
}
node.domModel().cssModel().forcePseudoState(node, state, event.target.checked);
};
/**
* @param {string} state
* @return {!Element}
*/
const createCheckbox = state => {
const td = document.createElement('td');
const label = UI.UIUtils.CheckboxLabel.create(':' + state);
const input = label.checkboxElement;
this._inputStates.set(input, state);
input.addEventListener('click', /** @type {!EventListener} */ (clickListener), false);
inputs.push(input);
td.appendChild(label);
return td;
};
let tr = table.createChild('tr');
tr.appendChild(createCheckbox('active'));
tr.appendChild(createCheckbox('hover'));
tr = table.createChild('tr');
tr.appendChild(createCheckbox('focus'));
tr.appendChild(createCheckbox('visited'));
tr = table.createChild('tr');
tr.appendChild(createCheckbox('focus-within'));
tr.appendChild(createCheckbox('focus-visible'));
tr = table.createChild('tr');
tr.appendChild(createCheckbox('target'));
this.contentElement.appendChild(table);
UI.Context.Context.instance().addFlavorChangeListener(SDK.DOMModel.DOMNode, this._update, this);
}
/**
* @param {?SDK.CSSModel.CSSModel} cssModel
*/
_updateModel(cssModel) {
if (this._cssModel === cssModel) {
return;
}
if (this._cssModel) {
this._cssModel.removeEventListener(SDK.CSSModel.Events.PseudoStateForced, this._update, this);
}
/** @type {?SDK.CSSModel.CSSModel} */
this._cssModel = cssModel;
if (this._cssModel) {
this._cssModel.addEventListener(SDK.CSSModel.Events.PseudoStateForced, this._update, this);
}
}
/**
* @override
*/
wasShown() {
this._update();
}
_update() {
if (!this.isShowing()) {
return;
}
let node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
if (node) {
node = node.enclosingElementOrSelf();
}
this._updateModel(node ? node.domModel().cssModel() : null);
if (node) {
const nodePseudoState = node.domModel().cssModel().pseudoState(node);
for (const input of this._inputs) {
input.disabled = Boolean(node.pseudoType());
const state = this._inputStates.get(input);
input.checked = nodePseudoState && state !== undefined ? nodePseudoState.indexOf(state) >= 0 : false;
}
} else {
for (const input of this._inputs) {
input.disabled = true;
input.checked = false;
}
}
}
}
/** @type {!ButtonProvider} */
let buttonProviderInstance;
/**
* @implements {UI.Toolbar.Provider}
*/
export class ButtonProvider {
/** @private */
constructor() {
this._button = new UI.Toolbar.ToolbarToggle(i18nString(UIStrings.toggleElementState), '');
this._button.setText(i18nString(UIStrings.hov));
this._button.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this._clicked, this);
this._button.element.classList.add('monospace');
this._view = new ElementStatePaneWidget();
}
/**
* @param {{forceNew: ?boolean}} opts
*/
static instance(opts = {forceNew: null}) {
const {forceNew} = opts;
if (!buttonProviderInstance || forceNew) {
buttonProviderInstance = new ButtonProvider();
}
return buttonProviderInstance;
}
_clicked() {
ElementsPanel.instance().showToolbarPane(!this._view.isShowing() ? this._view : null, this._button);
}
/**
* @override
* @return {!UI.Toolbar.ToolbarItem}
*/
item() {
return this._button;
}
}