chrome-devtools-frontend 1.0.945329 → 1.0.945579
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/gni/devtools_grd_files.gni +1 -0
- package/config/gni/devtools_image_files.gni +1 -0
- package/front_end/Images/src/circled_exclamation_icon.svg +3 -0
- package/front_end/core/sdk/CSSMetadata.ts +0 -1
- package/front_end/generated/protocol.d.ts +0 -4
- package/front_end/models/emulation/EmulatedDevices.ts +2 -4
- package/front_end/panels/application/BackForwardCacheView.ts +8 -1
- package/front_end/panels/elements/StyleEditorWidget.ts +13 -2
- package/front_end/panels/elements/StylePropertyTreeElement.ts +8 -12
- package/front_end/panels/elements/StylesSidebarPane.ts +35 -9
- package/front_end/ui/components/adorners/Adorner.ts +14 -14
- package/front_end/ui/components/buttons/Button.ts +116 -42
- package/front_end/ui/components/data_grid/DataGrid.ts +122 -122
- package/front_end/ui/components/data_grid/DataGridController.ts +42 -42
- package/front_end/ui/components/diff_view/DiffView.ts +4 -4
- package/front_end/ui/components/docs/button/basic.html +3 -0
- package/front_end/ui/components/docs/button/basic.ts +16 -0
- package/front_end/ui/components/expandable_list/ExpandableList.ts +11 -11
- package/front_end/ui/components/icon_button/Icon.ts +24 -21
- package/front_end/ui/components/icon_button/IconButton.ts +31 -31
- package/front_end/ui/components/issue_counter/IssueCounter.ts +52 -52
- package/front_end/ui/components/issue_counter/IssueLinkIcon.ts +42 -42
- package/front_end/ui/components/linear_memory_inspector/LinearMemoryInspector.ts +67 -67
- package/front_end/ui/components/linear_memory_inspector/LinearMemoryInspectorController.ts +22 -22
- package/front_end/ui/components/linear_memory_inspector/LinearMemoryInspectorPane.ts +36 -36
- package/front_end/ui/components/linear_memory_inspector/LinearMemoryNavigator.ts +19 -19
- package/front_end/ui/components/linear_memory_inspector/LinearMemoryValueInterpreter.ts +25 -25
- package/front_end/ui/components/linear_memory_inspector/LinearMemoryViewer.ts +52 -52
- package/front_end/ui/components/linear_memory_inspector/ValueInterpreterDisplay.ts +21 -21
- package/front_end/ui/components/linear_memory_inspector/ValueInterpreterSettings.ts +6 -6
- package/front_end/ui/components/markdown_view/MarkdownImage.ts +14 -14
- package/front_end/ui/components/markdown_view/MarkdownLink.ts +8 -8
- package/front_end/ui/components/markdown_view/MarkdownView.ts +6 -6
- package/front_end/ui/components/render_coordinator/RenderCoordinator.ts +33 -33
- package/front_end/ui/components/report_view/ReportView.ts +18 -18
- package/front_end/ui/components/request_link_icon/RequestLinkIcon.ts +53 -53
- package/front_end/ui/components/settings/SettingCheckbox.ts +15 -15
- package/front_end/ui/components/survey_link/SurveyLink.ts +28 -28
- package/front_end/ui/components/text_editor/TextEditor.ts +51 -51
- package/front_end/ui/components/text_editor/javascript.ts +6 -6
- package/front_end/ui/components/text_prompt/TextPrompt.ts +19 -19
- package/front_end/ui/components/tree_outline/TreeOutline.ts +56 -56
- package/front_end/ui/legacy/Infobar.ts +9 -0
- package/front_end/ui/legacy/tabbedPane.css +1 -1
- package/package.json +1 -1
|
@@ -45,6 +45,7 @@ grd_files_release_sources = [
|
|
|
45
45
|
"front_end/Images/chromeRight.avif",
|
|
46
46
|
"front_end/Images/chromeSelect.svg",
|
|
47
47
|
"front_end/Images/chromeSelectDark.svg",
|
|
48
|
+
"front_end/Images/circled_exclamation_icon.svg",
|
|
48
49
|
"front_end/Images/close-icon.svg",
|
|
49
50
|
"front_end/Images/copy_icon.svg",
|
|
50
51
|
"front_end/Images/cssoverview_icons_2x.avif",
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="16" height="16" viewBox="0 0 4.233 4.233" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M7.25 10.25h1.5v1.5h-1.5zm0-6h1.5v4.5h-1.5ZM7.992.5C3.853.5.5 3.86.5 8c0 4.14 3.353 7.5 7.492 7.5 4.148 0 7.508-3.36 7.508-7.5 0-4.14-3.36-7.5-7.508-7.5ZM8 14c-3.315 0-6-2.685-6-6s2.685-6 6-6 6 2.685 6 6-2.685 6-6 6z" transform="scale(.26458)" style="fill:#000"/>
|
|
3
|
+
</svg>
|
|
@@ -10442,10 +10442,6 @@ declare namespace Protocol {
|
|
|
10442
10442
|
* The fantasy font-family.
|
|
10443
10443
|
*/
|
|
10444
10444
|
fantasy?: string;
|
|
10445
|
-
/**
|
|
10446
|
-
* The pictograph font-family.
|
|
10447
|
-
*/
|
|
10448
|
-
pictograph?: string;
|
|
10449
10445
|
}
|
|
10450
10446
|
|
|
10451
10447
|
/**
|
|
@@ -502,14 +502,12 @@ export class EmulatedDevicesList extends Common.ObjectWrapper.ObjectWrapper<Even
|
|
|
502
502
|
constructor() {
|
|
503
503
|
super();
|
|
504
504
|
|
|
505
|
-
this.#standardSetting = Common.Settings.Settings.instance().createSetting(
|
|
506
|
-
'standardEmulatedDeviceList', [], Common.Settings.SettingStorageType.Synced);
|
|
505
|
+
this.#standardSetting = Common.Settings.Settings.instance().createSetting('standardEmulatedDeviceList', []);
|
|
507
506
|
this.#standardInternal = new Set();
|
|
508
507
|
this.listFromJSONV1(this.#standardSetting.get(), this.#standardInternal);
|
|
509
508
|
this.updateStandardDevices();
|
|
510
509
|
|
|
511
|
-
this.#customSetting = Common.Settings.Settings.instance().createSetting(
|
|
512
|
-
'customEmulatedDeviceList', [], Common.Settings.SettingStorageType.Synced);
|
|
510
|
+
this.#customSetting = Common.Settings.Settings.instance().createSetting('customEmulatedDeviceList', []);
|
|
513
511
|
this.#customInternal = new Set();
|
|
514
512
|
if (!this.listFromJSONV1(this.#customSetting.get(), this.#customInternal)) {
|
|
515
513
|
this.saveCustomDevices();
|
|
@@ -262,7 +262,14 @@ export class BackForwardCacheView extends UI.ThrottledWidget.ThrottledWidget {
|
|
|
262
262
|
|
|
263
263
|
private renderReason(explanation: Protocol.Page.BackForwardCacheNotRestoredExplanation): LitHtml.TemplateResult {
|
|
264
264
|
return LitHtml.html`
|
|
265
|
-
<li
|
|
265
|
+
<li>
|
|
266
|
+
<${IconButton.Icon.Icon.litTagName} class="inline-icon" .data=${{
|
|
267
|
+
iconName: 'circled_exclamation_icon',
|
|
268
|
+
color: 'orange',
|
|
269
|
+
width: '16px',
|
|
270
|
+
height: '16px',
|
|
271
|
+
} as IconButton.Icon.IconData}></${IconButton.Icon.Icon.litTagName}>
|
|
272
|
+
${explanation.reason} : ${
|
|
266
273
|
(explanation.reason in NotRestoredReasonDescription) ?
|
|
267
274
|
LitHtml.html`${NotRestoredReasonDescription[explanation.reason].name()}` :
|
|
268
275
|
LitHtml.nothing} </li>
|
|
@@ -30,6 +30,8 @@ export class StyleEditorWidget extends UI.Widget.VBox {
|
|
|
30
30
|
private section?: StylePropertiesSection;
|
|
31
31
|
private editorContainer: HTMLElement;
|
|
32
32
|
|
|
33
|
+
#triggerKey: string|undefined;
|
|
34
|
+
|
|
33
35
|
constructor() {
|
|
34
36
|
super(true);
|
|
35
37
|
this.contentElement.tabIndex = 0;
|
|
@@ -71,6 +73,14 @@ export class StyleEditorWidget extends UI.Widget.VBox {
|
|
|
71
73
|
this.editor?.addEventListener('propertydeselected', this.onPropertyDeselected);
|
|
72
74
|
}
|
|
73
75
|
|
|
76
|
+
setTriggerKey(value: string): void {
|
|
77
|
+
this.#triggerKey = value;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getTriggerKey(): string|undefined {
|
|
81
|
+
return this.#triggerKey;
|
|
82
|
+
}
|
|
83
|
+
|
|
74
84
|
unbindContext(): void {
|
|
75
85
|
this.pane = undefined;
|
|
76
86
|
this.section = undefined;
|
|
@@ -105,8 +115,8 @@ export class StyleEditorWidget extends UI.Widget.VBox {
|
|
|
105
115
|
}
|
|
106
116
|
|
|
107
117
|
static createTriggerButton(
|
|
108
|
-
pane: StylesSidebarPane, section: StylePropertiesSection, editorClass: {new(): Editor},
|
|
109
|
-
|
|
118
|
+
pane: StylesSidebarPane, section: StylePropertiesSection, editorClass: {new(): Editor}, buttonTitle: string,
|
|
119
|
+
triggerKey: string): HTMLElement {
|
|
110
120
|
const triggerButton = createButton(buttonTitle);
|
|
111
121
|
|
|
112
122
|
triggerButton.onclick = async(event): Promise<void> => {
|
|
@@ -115,6 +125,7 @@ export class StyleEditorWidget extends UI.Widget.VBox {
|
|
|
115
125
|
const widget = StyleEditorWidget.instance();
|
|
116
126
|
widget.setEditor(editorClass);
|
|
117
127
|
widget.bindContext(pane, section);
|
|
128
|
+
widget.setTriggerKey(triggerKey);
|
|
118
129
|
await widget.render();
|
|
119
130
|
const scrollerElement = triggerButton.enclosingNodeOrSelfWithClass('style-panes-wrapper');
|
|
120
131
|
const onScroll = (): void => {
|
|
@@ -668,21 +668,17 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
668
668
|
const section = this.section();
|
|
669
669
|
if (this.valueElement && section && section.editable && this.property.name === 'display') {
|
|
670
670
|
const propertyValue = this.property.trimmedValueWithoutImportant();
|
|
671
|
-
|
|
671
|
+
const isFlex = propertyValue === 'flex' || propertyValue === 'inline-flex';
|
|
672
|
+
const isGrid = propertyValue === 'grid' || propertyValue === 'inline-grid';
|
|
673
|
+
if (isFlex || isGrid) {
|
|
674
|
+
const key = `${section.getSectionIdx()}_${section.nextEditorTriggerButtonIdx}`;
|
|
672
675
|
const button = StyleEditorWidget.createTriggerButton(
|
|
673
|
-
this.parentPaneInternal, section, FlexboxEditor,
|
|
676
|
+
this.parentPaneInternal, section, isFlex ? FlexboxEditor : GridEditor,
|
|
677
|
+
isFlex ? i18nString(UIStrings.flexboxEditorButton) : i18nString(UIStrings.gridEditorButton), key);
|
|
678
|
+
section.nextEditorTriggerButtonIdx++;
|
|
674
679
|
this.listItemElement.appendChild(button);
|
|
675
680
|
const helper = this.parentPaneInternal.swatchPopoverHelper();
|
|
676
|
-
if (helper.isShowing(StyleEditorWidget.instance())) {
|
|
677
|
-
helper.setAnchorElement(button);
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
if (propertyValue === 'grid' || propertyValue === 'inline-grid') {
|
|
681
|
-
const button = StyleEditorWidget.createTriggerButton(
|
|
682
|
-
this.parentPaneInternal, section, GridEditor, i18nString(UIStrings.gridEditorButton));
|
|
683
|
-
this.listItemElement.appendChild(button);
|
|
684
|
-
const helper = this.parentPaneInternal.swatchPopoverHelper();
|
|
685
|
-
if (helper.isShowing(StyleEditorWidget.instance())) {
|
|
681
|
+
if (helper.isShowing(StyleEditorWidget.instance()) && StyleEditorWidget.instance().getTriggerKey() === key) {
|
|
686
682
|
helper.setAnchorElement(button);
|
|
687
683
|
}
|
|
688
684
|
}
|
|
@@ -832,6 +832,7 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
|
832
832
|
this.idleCallbackManager = new IdleCallbackManager();
|
|
833
833
|
|
|
834
834
|
const blocks = [new SectionBlock(null)];
|
|
835
|
+
let sectionIdx = 0;
|
|
835
836
|
let lastParentNode: SDK.DOMModel.DOMNode|null = null;
|
|
836
837
|
for (const style of matchedStyles.nodeStyles()) {
|
|
837
838
|
const parentNode = matchedStyles.isInherited(style) ? matchedStyles.nodeForStyle(style) : null;
|
|
@@ -844,7 +845,8 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
|
844
845
|
const lastBlock = blocks[blocks.length - 1];
|
|
845
846
|
if (lastBlock) {
|
|
846
847
|
this.idleCallbackManager.schedule(() => {
|
|
847
|
-
const section = new StylePropertiesSection(this, matchedStyles, style);
|
|
848
|
+
const section = new StylePropertiesSection(this, matchedStyles, style, sectionIdx);
|
|
849
|
+
sectionIdx++;
|
|
848
850
|
lastBlock.sections.push(section);
|
|
849
851
|
});
|
|
850
852
|
}
|
|
@@ -860,7 +862,8 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
|
860
862
|
const block = SectionBlock.createPseudoTypeBlock(pseudoType);
|
|
861
863
|
for (const style of matchedStyles.pseudoStyles(pseudoType)) {
|
|
862
864
|
this.idleCallbackManager.schedule(() => {
|
|
863
|
-
const section = new StylePropertiesSection(this, matchedStyles, style);
|
|
865
|
+
const section = new StylePropertiesSection(this, matchedStyles, style, sectionIdx);
|
|
866
|
+
sectionIdx++;
|
|
864
867
|
block.sections.push(section);
|
|
865
868
|
});
|
|
866
869
|
}
|
|
@@ -871,7 +874,8 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
|
871
874
|
const block = SectionBlock.createKeyframesBlock(keyframesRule.name().text);
|
|
872
875
|
for (const keyframe of keyframesRule.keyframes()) {
|
|
873
876
|
this.idleCallbackManager.schedule(() => {
|
|
874
|
-
block.sections.push(new KeyframePropertiesSection(this, matchedStyles, keyframe.style));
|
|
877
|
+
block.sections.push(new KeyframePropertiesSection(this, matchedStyles, keyframe.style, sectionIdx));
|
|
878
|
+
sectionIdx++;
|
|
875
879
|
});
|
|
876
880
|
}
|
|
877
881
|
blocks.push(block);
|
|
@@ -917,7 +921,7 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
|
917
921
|
const node = this.node();
|
|
918
922
|
const blankSection = new BlankStylePropertiesSection(
|
|
919
923
|
this, insertAfterSection.matchedStyles, node ? node.simpleSelector() : '', styleSheetId, ruleLocation,
|
|
920
|
-
insertAfterSection.style());
|
|
924
|
+
insertAfterSection.style(), 0);
|
|
921
925
|
|
|
922
926
|
this.sectionsContainer.insertBefore(blankSection.element, insertAfterSection.element.nextSibling);
|
|
923
927
|
|
|
@@ -929,6 +933,13 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
|
929
933
|
block.sections.splice(index + 1, 0, blankSection);
|
|
930
934
|
blankSection.startEditingSelector();
|
|
931
935
|
}
|
|
936
|
+
let sectionIdx = 0;
|
|
937
|
+
for (const block of this.sectionBlocks) {
|
|
938
|
+
for (const section of block.sections) {
|
|
939
|
+
section.setSectionIdx(sectionIdx);
|
|
940
|
+
sectionIdx++;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
932
943
|
}
|
|
933
944
|
|
|
934
945
|
removeSection(section: StylePropertiesSection): void {
|
|
@@ -1208,10 +1219,15 @@ export class StylePropertiesSection {
|
|
|
1208
1219
|
|
|
1209
1220
|
private queryListElement: HTMLElement;
|
|
1210
1221
|
|
|
1222
|
+
// Used to identify buttons that trigger a flexbox or grid editor.
|
|
1223
|
+
nextEditorTriggerButtonIdx = 1;
|
|
1224
|
+
private sectionIdx = 0;
|
|
1225
|
+
|
|
1211
1226
|
constructor(
|
|
1212
1227
|
parentPane: StylesSidebarPane, matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles,
|
|
1213
|
-
style: SDK.CSSStyleDeclaration.CSSStyleDeclaration) {
|
|
1228
|
+
style: SDK.CSSStyleDeclaration.CSSStyleDeclaration, sectionIdx: number) {
|
|
1214
1229
|
this.parentPane = parentPane;
|
|
1230
|
+
this.sectionIdx = sectionIdx;
|
|
1215
1231
|
this.styleInternal = style;
|
|
1216
1232
|
this.matchedStyles = matchedStyles;
|
|
1217
1233
|
this.editable = Boolean(style.styleSheetId && style.range);
|
|
@@ -1344,6 +1360,15 @@ export class StylePropertiesSection {
|
|
|
1344
1360
|
this.onpopulate();
|
|
1345
1361
|
}
|
|
1346
1362
|
|
|
1363
|
+
setSectionIdx(sectionIdx: number): void {
|
|
1364
|
+
this.sectionIdx = sectionIdx;
|
|
1365
|
+
this.onpopulate();
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
getSectionIdx(): number {
|
|
1369
|
+
return this.sectionIdx;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1347
1372
|
registerFontProperty(treeElement: StylePropertyTreeElement): void {
|
|
1348
1373
|
if (this.fontEditorSectionManager) {
|
|
1349
1374
|
this.fontEditorSectionManager.registerFontProperty(treeElement);
|
|
@@ -1935,6 +1960,7 @@ export class StylePropertiesSection {
|
|
|
1935
1960
|
|
|
1936
1961
|
onpopulate(): void {
|
|
1937
1962
|
this.parentPane.setActiveProperty(null);
|
|
1963
|
+
this.nextEditorTriggerButtonIdx = 1;
|
|
1938
1964
|
this.propertiesTreeOutline.removeChildren();
|
|
1939
1965
|
const style = this.styleInternal;
|
|
1940
1966
|
let count = 0;
|
|
@@ -2457,10 +2483,10 @@ export class BlankStylePropertiesSection extends StylePropertiesSection {
|
|
|
2457
2483
|
constructor(
|
|
2458
2484
|
stylesPane: StylesSidebarPane, matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles, defaultSelectorText: string,
|
|
2459
2485
|
styleSheetId: Protocol.CSS.StyleSheetId, ruleLocation: TextUtils.TextRange.TextRange,
|
|
2460
|
-
insertAfterStyle: SDK.CSSStyleDeclaration.CSSStyleDeclaration) {
|
|
2486
|
+
insertAfterStyle: SDK.CSSStyleDeclaration.CSSStyleDeclaration, sectionIdx: number) {
|
|
2461
2487
|
const cssModel = (stylesPane.cssModel() as SDK.CSSModel.CSSModel);
|
|
2462
2488
|
const rule = SDK.CSSRule.CSSStyleRule.createDummyRule(cssModel, defaultSelectorText);
|
|
2463
|
-
super(stylesPane, matchedStyles, rule.style);
|
|
2489
|
+
super(stylesPane, matchedStyles, rule.style, sectionIdx);
|
|
2464
2490
|
this.normal = false;
|
|
2465
2491
|
this.ruleLocation = ruleLocation;
|
|
2466
2492
|
this.styleSheetId = styleSheetId;
|
|
@@ -2564,8 +2590,8 @@ export class BlankStylePropertiesSection extends StylePropertiesSection {
|
|
|
2564
2590
|
export class KeyframePropertiesSection extends StylePropertiesSection {
|
|
2565
2591
|
constructor(
|
|
2566
2592
|
stylesPane: StylesSidebarPane, matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles,
|
|
2567
|
-
style: SDK.CSSStyleDeclaration.CSSStyleDeclaration) {
|
|
2568
|
-
super(stylesPane, matchedStyles, style);
|
|
2593
|
+
style: SDK.CSSStyleDeclaration.CSSStyleDeclaration, sectionIdx: number) {
|
|
2594
|
+
super(stylesPane, matchedStyles, style, sectionIdx);
|
|
2569
2595
|
this.selectorElement.className = 'keyframe-key';
|
|
2570
2596
|
}
|
|
2571
2597
|
|
|
@@ -19,18 +19,18 @@ export class Adorner extends HTMLElement {
|
|
|
19
19
|
static readonly litTagName = LitHtml.literal`devtools-adorner`;
|
|
20
20
|
name = '';
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
readonly #shadow = this.attachShadow({mode: 'open'});
|
|
23
|
+
#isToggle = false;
|
|
24
|
+
#ariaLabelDefault?: string;
|
|
25
|
+
#ariaLabelActive?: string;
|
|
26
|
+
#content?: HTMLElement;
|
|
27
27
|
|
|
28
28
|
set data(data: AdornerData) {
|
|
29
29
|
this.name = data.name;
|
|
30
30
|
data.content.slot = 'content';
|
|
31
|
-
this
|
|
31
|
+
this.#content?.remove();
|
|
32
32
|
this.append(data.content);
|
|
33
|
-
this
|
|
33
|
+
this.#content = data.content;
|
|
34
34
|
this.render();
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -38,7 +38,7 @@ export class Adorner extends HTMLElement {
|
|
|
38
38
|
if (!this.getAttribute('aria-label')) {
|
|
39
39
|
this.setAttribute('aria-label', this.name);
|
|
40
40
|
}
|
|
41
|
-
this
|
|
41
|
+
this.#shadow.adoptedStyleSheets = [adornerStyles];
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
isActive(): boolean {
|
|
@@ -50,12 +50,12 @@ export class Adorner extends HTMLElement {
|
|
|
50
50
|
* an active state; pass `false` to force-set an inactive state.
|
|
51
51
|
*/
|
|
52
52
|
toggle(forceActiveState?: boolean): void {
|
|
53
|
-
if (!this
|
|
53
|
+
if (!this.#isToggle) {
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
56
|
const shouldBecomeActive = forceActiveState === undefined ? !this.isActive() : forceActiveState;
|
|
57
57
|
this.setAttribute('aria-pressed', Boolean(shouldBecomeActive).toString());
|
|
58
|
-
this.setAttribute('aria-label', (shouldBecomeActive ? this
|
|
58
|
+
this.setAttribute('aria-label', (shouldBecomeActive ? this.#ariaLabelActive : this.#ariaLabelDefault) || this.name);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
show(): void {
|
|
@@ -78,9 +78,9 @@ export class Adorner extends HTMLElement {
|
|
|
78
78
|
}): void {
|
|
79
79
|
const {isToggle = false, shouldPropagateOnKeydown = false, ariaLabelDefault, ariaLabelActive} = options;
|
|
80
80
|
|
|
81
|
-
this
|
|
82
|
-
this
|
|
83
|
-
this
|
|
81
|
+
this.#isToggle = isToggle;
|
|
82
|
+
this.#ariaLabelDefault = ariaLabelDefault;
|
|
83
|
+
this.#ariaLabelActive = ariaLabelActive;
|
|
84
84
|
this.setAttribute('aria-label', ariaLabelDefault);
|
|
85
85
|
|
|
86
86
|
if (isToggle) {
|
|
@@ -111,7 +111,7 @@ export class Adorner extends HTMLElement {
|
|
|
111
111
|
// clang-format off
|
|
112
112
|
render(html`
|
|
113
113
|
<slot name="content"></slot>
|
|
114
|
-
`, this
|
|
114
|
+
`, this.#shadow, {
|
|
115
115
|
host: this,
|
|
116
116
|
});
|
|
117
117
|
}
|
|
@@ -25,12 +25,16 @@ export const enum Size {
|
|
|
25
25
|
MEDIUM = 'MEDIUM',
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
type ButtonType = 'button'|'submit'|'reset';
|
|
29
|
+
|
|
28
30
|
interface ButtonState {
|
|
29
31
|
iconUrl?: string;
|
|
30
32
|
variant?: Variant;
|
|
31
33
|
size?: Size;
|
|
32
34
|
disabled: boolean;
|
|
33
35
|
active: boolean;
|
|
36
|
+
type: ButtonType;
|
|
37
|
+
value?: string;
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
export type ButtonData = {
|
|
@@ -39,30 +43,46 @@ export type ButtonData = {
|
|
|
39
43
|
size?: Size,
|
|
40
44
|
disabled?: boolean,
|
|
41
45
|
active?: boolean,
|
|
46
|
+
type?: ButtonType,
|
|
47
|
+
value?: string,
|
|
42
48
|
}|{
|
|
43
49
|
variant: Variant.PRIMARY | Variant.SECONDARY,
|
|
44
50
|
iconUrl?: string,
|
|
45
51
|
size?: Size,
|
|
46
52
|
disabled?: boolean,
|
|
47
53
|
active?: boolean,
|
|
54
|
+
type?: ButtonType,
|
|
55
|
+
value?: string,
|
|
48
56
|
};
|
|
49
57
|
|
|
58
|
+
interface ButtonElementInternals extends ElementInternals {
|
|
59
|
+
readonly form?: HTMLFormElement;
|
|
60
|
+
readonly validity: ValidityState;
|
|
61
|
+
readonly willValidate: boolean;
|
|
62
|
+
readonly validationMessage: string;
|
|
63
|
+
checkValidity(): void;
|
|
64
|
+
reportValidity(): void;
|
|
65
|
+
}
|
|
66
|
+
|
|
50
67
|
export class Button extends HTMLElement {
|
|
68
|
+
static formAssociated = true;
|
|
51
69
|
static readonly litTagName = LitHtml.literal`devtools-button`;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
70
|
+
readonly #shadow = this.attachShadow({mode: 'open', delegatesFocus: true});
|
|
71
|
+
readonly #boundRender = this.render.bind(this);
|
|
72
|
+
readonly #boundOnClick = this.onClick.bind(this);
|
|
73
|
+
readonly #props: ButtonState = {
|
|
56
74
|
size: Size.MEDIUM,
|
|
57
75
|
disabled: false,
|
|
58
76
|
active: false,
|
|
77
|
+
type: 'button',
|
|
59
78
|
};
|
|
60
|
-
|
|
79
|
+
#isEmpty = true;
|
|
80
|
+
#internals = this.attachInternals() as ButtonElementInternals;
|
|
61
81
|
|
|
62
82
|
constructor() {
|
|
63
83
|
super();
|
|
64
84
|
this.setAttribute('role', 'presentation');
|
|
65
|
-
this.addEventListener('click', this
|
|
85
|
+
this.addEventListener('click', this.#boundOnClick, true);
|
|
66
86
|
}
|
|
67
87
|
|
|
68
88
|
/**
|
|
@@ -70,104 +90,158 @@ export class Button extends HTMLElement {
|
|
|
70
90
|
* for increased type-safety.
|
|
71
91
|
*/
|
|
72
92
|
set data(data: ButtonData) {
|
|
73
|
-
this
|
|
74
|
-
this
|
|
75
|
-
this
|
|
76
|
-
this
|
|
93
|
+
this.#props.variant = data.variant;
|
|
94
|
+
this.#props.iconUrl = data.iconUrl;
|
|
95
|
+
this.#props.size = data.size || Size.MEDIUM;
|
|
96
|
+
this.#props.active = Boolean(data.active);
|
|
97
|
+
this.#props.type = data.type || 'button';
|
|
77
98
|
this.setDisabledProperty(data.disabled || false);
|
|
78
|
-
ComponentHelpers.ScheduledRender.scheduleRender(this, this
|
|
99
|
+
ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
|
|
79
100
|
}
|
|
80
101
|
|
|
81
102
|
set iconUrl(iconUrl: string|undefined) {
|
|
82
|
-
this
|
|
83
|
-
ComponentHelpers.ScheduledRender.scheduleRender(this, this
|
|
103
|
+
this.#props.iconUrl = iconUrl;
|
|
104
|
+
ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
|
|
84
105
|
}
|
|
85
106
|
|
|
86
107
|
set variant(variant: Variant) {
|
|
87
|
-
this
|
|
88
|
-
ComponentHelpers.ScheduledRender.scheduleRender(this, this
|
|
108
|
+
this.#props.variant = variant;
|
|
109
|
+
ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
|
|
89
110
|
}
|
|
90
111
|
|
|
91
112
|
set size(size: Size) {
|
|
92
|
-
this
|
|
93
|
-
ComponentHelpers.ScheduledRender.scheduleRender(this, this
|
|
113
|
+
this.#props.size = size;
|
|
114
|
+
ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
set type(type: ButtonType) {
|
|
118
|
+
this.#props.type = type;
|
|
119
|
+
ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
|
|
94
120
|
}
|
|
95
121
|
|
|
96
122
|
set disabled(disabled: boolean) {
|
|
97
123
|
this.setDisabledProperty(disabled);
|
|
98
|
-
ComponentHelpers.ScheduledRender.scheduleRender(this, this
|
|
124
|
+
ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
|
|
99
125
|
}
|
|
100
126
|
|
|
101
127
|
set active(active: boolean) {
|
|
102
|
-
this
|
|
103
|
-
ComponentHelpers.ScheduledRender.scheduleRender(this, this
|
|
128
|
+
this.#props.active = active;
|
|
129
|
+
ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
|
|
104
130
|
}
|
|
105
131
|
|
|
106
132
|
private setDisabledProperty(disabled: boolean): void {
|
|
107
|
-
this
|
|
133
|
+
this.#props.disabled = disabled;
|
|
108
134
|
this.toggleAttribute('disabled', disabled);
|
|
109
135
|
}
|
|
110
136
|
|
|
111
137
|
focus(): void {
|
|
112
|
-
this
|
|
138
|
+
this.#shadow.querySelector('button')?.focus();
|
|
113
139
|
}
|
|
114
140
|
|
|
115
141
|
connectedCallback(): void {
|
|
116
|
-
this
|
|
117
|
-
ComponentHelpers.ScheduledRender.scheduleRender(this, this
|
|
142
|
+
this.#shadow.adoptedStyleSheets = [buttonStyles];
|
|
143
|
+
ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
|
|
118
144
|
}
|
|
119
145
|
|
|
120
146
|
private onClick(event: Event): void {
|
|
121
|
-
if (this
|
|
147
|
+
if (this.#props.disabled) {
|
|
122
148
|
event.stopPropagation();
|
|
123
149
|
event.preventDefault();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (this.form && this.#props.type === 'submit') {
|
|
153
|
+
event.preventDefault();
|
|
154
|
+
this.form.dispatchEvent(new SubmitEvent('submit', {
|
|
155
|
+
submitter: this,
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
if (this.form && this.#props.type === 'reset') {
|
|
159
|
+
event.preventDefault();
|
|
160
|
+
this.form.reset();
|
|
124
161
|
}
|
|
125
162
|
}
|
|
126
163
|
|
|
127
164
|
private onSlotChange(event: Event): void {
|
|
128
165
|
const slot = event.target as HTMLSlotElement | undefined;
|
|
129
166
|
const nodes = slot?.assignedNodes();
|
|
130
|
-
this
|
|
131
|
-
ComponentHelpers.ScheduledRender.scheduleRender(this, this
|
|
167
|
+
this.#isEmpty = !nodes || !Boolean(nodes.length);
|
|
168
|
+
ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
|
|
132
169
|
}
|
|
133
170
|
|
|
134
171
|
private render(): void {
|
|
135
|
-
if (!this
|
|
172
|
+
if (!this.#props.variant) {
|
|
136
173
|
throw new Error('Button requires a variant to be defined');
|
|
137
174
|
}
|
|
138
|
-
if (this
|
|
139
|
-
if (!this
|
|
175
|
+
if (this.#props.variant === Variant.TOOLBAR) {
|
|
176
|
+
if (!this.#props.iconUrl) {
|
|
140
177
|
throw new Error('Toolbar button requires an icon');
|
|
141
178
|
}
|
|
142
|
-
if (!this
|
|
179
|
+
if (!this.#isEmpty) {
|
|
143
180
|
throw new Error('Tooblar button does not accept children');
|
|
144
181
|
}
|
|
145
182
|
}
|
|
146
183
|
const classes = {
|
|
147
|
-
primary: this
|
|
148
|
-
secondary: this
|
|
149
|
-
toolbar: this
|
|
150
|
-
'text-with-icon': Boolean(this
|
|
151
|
-
'only-icon': Boolean(this
|
|
152
|
-
small: Boolean(this
|
|
153
|
-
active: this
|
|
184
|
+
primary: this.#props.variant === Variant.PRIMARY,
|
|
185
|
+
secondary: this.#props.variant === Variant.SECONDARY,
|
|
186
|
+
toolbar: this.#props.variant === Variant.TOOLBAR,
|
|
187
|
+
'text-with-icon': Boolean(this.#props.iconUrl) && !this.#isEmpty,
|
|
188
|
+
'only-icon': Boolean(this.#props.iconUrl) && this.#isEmpty,
|
|
189
|
+
small: Boolean(this.#props.size === Size.SMALL),
|
|
190
|
+
active: this.#props.active,
|
|
154
191
|
};
|
|
155
192
|
// clang-format off
|
|
156
193
|
LitHtml.render(
|
|
157
194
|
LitHtml.html`
|
|
158
|
-
<button .disabled=${this
|
|
159
|
-
${this
|
|
195
|
+
<button .disabled=${this.#props.disabled} class=${LitHtml.Directives.classMap(classes)}>
|
|
196
|
+
${this.#props.iconUrl ? LitHtml.html`<${IconButton.Icon.Icon.litTagName}
|
|
160
197
|
.data=${{
|
|
161
|
-
iconPath: this
|
|
198
|
+
iconPath: this.#props.iconUrl,
|
|
162
199
|
color: 'var(--color-background)',
|
|
163
200
|
} as IconButton.Icon.IconData}
|
|
164
201
|
>
|
|
165
202
|
</${IconButton.Icon.Icon.litTagName}>` : ''}
|
|
166
203
|
<slot @slotchange=${this.onSlotChange}></slot>
|
|
167
204
|
</button>
|
|
168
|
-
`, this
|
|
205
|
+
`, this.#shadow, {host: this});
|
|
169
206
|
// clang-format on
|
|
170
207
|
}
|
|
208
|
+
|
|
209
|
+
// Based on https://web.dev/more-capable-form-controls/ to make custom elements form-friendly.
|
|
210
|
+
// Form controls usually expose a "value" property.
|
|
211
|
+
get value(): string {
|
|
212
|
+
return this.#props.value || '';
|
|
213
|
+
}
|
|
214
|
+
set value(value: string) {
|
|
215
|
+
this.#props.value = value;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// The following properties and methods aren't strictly required,
|
|
219
|
+
// but browser-level form controls provide them. Providing them helps
|
|
220
|
+
// ensure consistency with browser-provided controls.
|
|
221
|
+
get form(): HTMLFormElement|undefined {
|
|
222
|
+
return this.#internals.form;
|
|
223
|
+
}
|
|
224
|
+
get name(): string|null {
|
|
225
|
+
return this.getAttribute('name');
|
|
226
|
+
}
|
|
227
|
+
get type(): ButtonType {
|
|
228
|
+
return this.#props.type;
|
|
229
|
+
}
|
|
230
|
+
get validity(): ValidityState {
|
|
231
|
+
return this.#internals.validity;
|
|
232
|
+
}
|
|
233
|
+
get validationMessage(): string {
|
|
234
|
+
return this.#internals.validationMessage;
|
|
235
|
+
}
|
|
236
|
+
get willValidate(): boolean {
|
|
237
|
+
return this.#internals.willValidate;
|
|
238
|
+
}
|
|
239
|
+
checkValidity(): void {
|
|
240
|
+
return this.#internals.checkValidity();
|
|
241
|
+
}
|
|
242
|
+
reportValidity(): void {
|
|
243
|
+
return this.#internals.reportValidity();
|
|
244
|
+
}
|
|
171
245
|
}
|
|
172
246
|
|
|
173
247
|
ComponentHelpers.CustomElements.defineComponent('devtools-button', Button);
|