blob: 0891afc48bb2de14c02d00d3864b35d357b9047d [file] [log] [blame]
// Copyright 2025 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.
/**
* @fileoverview A library to identify and templatize manually constructed Toolbars.
*/
import type {TSESTree} from '@typescript-eslint/utils';
import {isIdentifier, isIdentifierChain, isMemberExpression} from './ast.ts';
import {DomFragment} from './dom-fragment.ts';
type Node = TSESTree.Node;
export const toolbar = {
create(context) {
const sourceCode = context.getSourceCode();
return {
getEvent(event: Node): string |
null {
switch (sourceCode.getText(event)) {
case 'UI.Toolbar.ToolbarInput.Event.TEXT_CHANGED':
return 'change';
case 'UI.Toolbar.ToolbarInput.Event.ENTER_PRESSED':
return 'submit';
case 'UI.Toolbar.ToolbarButton.Events.CLICK':
return 'click';
default:
return null;
}
},
methodCall(property: Node, firstArg: Node, _secondArg: Node, domFragment: DomFragment, _call: Node): boolean {
if (isIdentifier(property, 'appendToolbarItem')) {
domFragment.appendChild(firstArg, sourceCode);
return true;
}
return false;
},
NewExpression(node) {
const toolbarItem =
isMemberExpression(node.callee, n => isIdentifierChain(n, ['UI', 'Toolbar']), n => n.type === 'Identifier');
if (!toolbarItem) {
return;
}
if (isIdentifier(toolbarItem, ['ToolbarFilter', 'ToolbarInput'])) {
const domFragment = DomFragment.getOrCreate(node, sourceCode);
domFragment.tagName = 'devtools-toolbar-input';
const type = isIdentifier(toolbarItem, 'ToolbarFilter') ? 'filter' : 'text';
domFragment.attributes.push({
key: 'type',
value: type,
});
const args = [...node.arguments];
const placeholder = args.shift();
if (placeholder && !isIdentifier(placeholder, 'undefined')) {
domFragment.attributes.push({
key: 'placeholder',
value: placeholder,
});
}
if (type === 'text') {
const accesiblePlaceholder = args.shift();
if (accesiblePlaceholder && !isIdentifier(accesiblePlaceholder, 'undefined')) {
domFragment.attributes.push({
key: 'aria-label',
value: accesiblePlaceholder,
});
}
}
const flexGrow = args.shift();
if (flexGrow && !isIdentifier(flexGrow, 'undefined')) {
domFragment.style.push({
key: 'flex-grow',
value: flexGrow,
});
}
const flexShrink = args.shift();
if (flexShrink && !isIdentifier(flexShrink, 'undefined')) {
domFragment.style.push({
key: 'flex-shrink',
value: flexShrink,
});
}
const title = args.shift();
if (title && !isIdentifier(title, 'undefined')) {
domFragment.attributes.push({
key: 'title',
value: title,
});
}
const completions = args.shift();
if (completions && !isIdentifier(completions, 'undefined')) {
domFragment.attributes.push({
key: 'list',
value: 'completions',
});
const dataList = domFragment.appendChild(completions, sourceCode);
dataList.tagName = 'datalist';
dataList.attributes.push({
key: 'id',
value: 'completions',
});
dataList.textContent = completions;
}
args.shift(); // dynamicCompletions is not supported
const jslogContext = args.shift();
if (jslogContext && !isIdentifier(jslogContext, 'undefined')) {
domFragment.attributes.push({
key: 'id',
value: jslogContext,
});
}
}
if (isIdentifier(toolbarItem, 'ToolbarButton')) {
const domFragment = DomFragment.getOrCreate(node, sourceCode);
domFragment.tagName = 'devtools-button';
const title = node.arguments[0];
domFragment.bindings.push({
key: 'variant',
value: '${Buttons.Button.Variant.TOOLBAR}',
});
if (title && !isIdentifier(title, 'undefined')) {
domFragment.attributes.push({
key: 'title',
value: title,
});
}
const glyph = node.arguments[1];
if (glyph && !isIdentifier(glyph, 'undefined')) {
domFragment.bindings.push({
key: 'iconName',
value: glyph,
});
}
const text = node.arguments[2];
if (text && !isIdentifier(text, 'undefined')) {
domFragment.textContent = text;
}
const jslogContext = node.arguments[3];
if (jslogContext && !isIdentifier(jslogContext, 'undefined')) {
domFragment.bindings.push({
key: 'jslogContext',
value: jslogContext,
});
}
}
if (isIdentifier(toolbarItem, ['ToolbarCheckbox', 'ToolbarSettingCheckbox'])) {
const domFragment = DomFragment.getOrCreate(node, sourceCode);
domFragment.tagName = 'devtools-checkbox';
const title = node.arguments[0];
if (title && !isIdentifier(title, 'undefined')) {
let text: Node|string = title;
if (isIdentifier(toolbarItem, 'ToolbarSettingCheckbox')) {
domFragment.directives.push({
name: 'bindToSetting',
arguments: [title],
});
const alternateTitle = node.arguments[2];
if (alternateTitle && !isIdentifier(alternateTitle, 'undefined')) {
text = alternateTitle;
} else {
text = '${' + sourceCode.getText(title) + '.title()}';
}
}
domFragment.textContent = text;
}
const tooltip = node.arguments[1];
if (tooltip && !isIdentifier(tooltip, 'undefined')) {
domFragment.attributes.push({
key: 'title',
value: tooltip,
});
}
if (isIdentifier(toolbarItem, 'ToolbarCheckbox')) {
const listener = node.arguments[2];
if (listener && !isIdentifier(listener, 'undefined')) {
domFragment.eventListeners.push({
key: 'click',
value: listener,
});
}
const jslogContext = node.arguments[3];
if (jslogContext && !isIdentifier(jslogContext, 'undefined')) {
domFragment.bindings.push({
key: 'jslogContext',
value: jslogContext,
});
}
}
}
}
};
}
};