11// Copyright (c) 2022 The Chromium Authors. All rights reserved.
22// Use of this source code is governed by a BSD-style license that can be
33// found in the LICENSE file.
4- /* eslint-disable rulesdir/no-imperative-dom-api */
54
5+ import type * as Common from '../../core/common/common.js' ;
66import * as i18n from '../../core/i18n/i18n.js' ;
77import * as SDK from '../../core/sdk/sdk.js' ;
88import type * as Protocol from '../../generated/protocol.js' ;
9+ import * as Lit from '../../third_party/lit/lit.js' ;
910import * as TreeOutline from '../../ui/components/tree_outline/tree_outline.js' ;
1011import * as UI from '../../ui/legacy/legacy.js' ;
1112import * as VisualLogging from '../../ui/visual_logging/visual_logging.js' ;
1213
1314import { ElementsPanel } from './ElementsPanel.js' ;
1415import layersWidgetStyles from './layersWidget.css.js' ;
1516
17+ const { render, html, Directives : { ref} } = Lit ;
18+
1619const UIStrings = {
1720 /**
1821 * @description Title of a section in the Element State Pane Widget of the Elements panel.
@@ -28,95 +31,122 @@ const UIStrings = {
2831const str_ = i18n . i18n . registerUIStrings ( 'panels/elements/LayersWidget.ts' , UIStrings ) ;
2932const i18nString = i18n . i18n . getLocalizedString . bind ( undefined , str_ ) ;
3033
31- let layersWidgetInstance : LayersWidget ;
34+ interface ViewInput {
35+ rootLayer : Protocol . CSS . CSSLayerData ;
36+ }
3237
33- export class LayersWidget extends UI . Widget . Widget {
34- private cssModel ?: SDK . CSSModel . CSSModel | null ;
35- private layerTreeComponent = new TreeOutline . TreeOutline . TreeOutline < string > ( ) ;
38+ interface ViewOutput {
39+ treeOutline : TreeOutline . TreeOutline . TreeOutline < string > | undefined ;
40+ }
3641
37- constructor ( ) {
38- super ( {
39- jslog : `${ VisualLogging . pane ( 'css-layers' ) } ` ,
40- useShadowDom : true ,
41- } ) ;
42- this . registerRequiredCSS ( layersWidgetStyles ) ;
42+ type View = ( input : ViewInput , output : ViewOutput , target : HTMLElement ) => void ;
4343
44- this . contentElement . className = 'styles-layers-pane' ;
45- UI . UIUtils . createTextChild ( this . contentElement . createChild ( 'div' ) , i18nString ( UIStrings . cssLayersTitle ) ) ;
44+ const DEFAULT_VIEW : View = ( input : ViewInput , output : ViewOutput , target : HTMLElement ) => {
45+ const makeTreeNode = ( parentId : string ) => ( layer : Protocol . CSS . CSSLayerData ) => {
46+ const subLayers = layer . subLayers ;
47+ const name = SDK . CSSModel . CSSModel . readableLayerName ( layer . name ) ;
48+ const treeNodeData = layer . order + ': ' + name ;
49+ const id = parentId ? parentId + '.' + name : name ;
50+ if ( ! subLayers ) {
51+ return { treeNodeData, id} ;
52+ }
53+ return {
54+ treeNodeData,
55+ id,
56+ children : async ( ) => subLayers . sort ( ( layer1 , layer2 ) => layer1 . order - layer2 . order ) . map ( makeTreeNode ( id ) ) ,
57+ } ;
58+ } ;
59+ const { defaultRenderer} = TreeOutline . TreeOutline ;
60+ const tree = [ makeTreeNode ( '' ) ( input . rootLayer ) ] ;
61+ const data : TreeOutline . TreeOutline . TreeOutlineData < string > = {
62+ defaultRenderer,
63+ tree,
64+ } ;
65+ const captureTreeOutline = ( e ?: Element ) : void => {
66+ output . treeOutline = e as typeof output . treeOutline ;
67+ } ;
68+ const template = html `
69+ < style > ${ layersWidgetStyles } </ style >
70+ < div class ="layers-widget ">
71+ < div class ="layers-widget-title "> ${ UIStrings . cssLayersTitle } </ div >
72+ < devtools-tree-outline ${ ref ( captureTreeOutline ) }
73+ .data =${ data } > </ devtools-tree-outline >
74+ </ div >
75+ ` ;
76+ render ( template , target ) ;
77+ } ;
4678
47- this . contentElement . appendChild ( this . layerTreeComponent ) ;
79+ let layersWidgetInstance : LayersWidget ;
4880
49- UI . Context . Context . instance ( ) . addFlavorChangeListener ( SDK . DOMModel . DOMNode , this . update , this ) ;
50- }
81+ export class LayersWidget extends UI . Widget . Widget {
82+ #node: SDK . DOMModel . DOMNode | null = null ;
83+ #view: View ;
84+ #layerToReveal: string | null = null ;
5185
52- private updateModel ( cssModel : SDK . CSSModel . CSSModel | null ) : void {
53- if ( this . cssModel === cssModel ) {
54- return ;
55- }
56- if ( this . cssModel ) {
57- this . cssModel . removeEventListener ( SDK . CSSModel . Events . StyleSheetChanged , this . update , this ) ;
58- }
59- this . cssModel = cssModel ;
60- if ( this . cssModel ) {
61- this . cssModel . addEventListener ( SDK . CSSModel . Events . StyleSheetChanged , this . update , this ) ;
62- }
86+ constructor ( view : View = DEFAULT_VIEW ) {
87+ super ( { jslog : `${ VisualLogging . pane ( 'css-layers' ) } ` } ) ;
88+ this . #view = view ;
6389 }
6490
65- override wasShown ( ) : Promise < void > {
91+ override wasShown ( ) : void {
6692 super . wasShown ( ) ;
67- return this . update ( ) ;
93+ UI . Context . Context . instance ( ) . addFlavorChangeListener ( SDK . DOMModel . DOMNode , this . #onDOMNodeChanged, this ) ;
94+ this . #onDOMNodeChanged( { data : UI . Context . Context . instance ( ) . flavor ( SDK . DOMModel . DOMNode ) } ) ;
6895 }
6996
70- async update ( ) : Promise < void > {
71- if ( ! this . isShowing ( ) ) {
97+ override wasHidden ( ) : void {
98+ UI . Context . Context . instance ( ) . addFlavorChangeListener ( SDK . DOMModel . DOMNode , this . #onDOMNodeChanged, this ) ;
99+ this . #onDOMNodeChanged( { data : null } ) ;
100+ super . wasHidden ( ) ;
101+ }
102+
103+ #onDOMNodeChanged( event : Common . EventTarget . EventTargetEvent < SDK . DOMModel . DOMNode | null > ) : void {
104+ const node = event . data ?. enclosingElementOrSelf ( ) ;
105+ if ( this . #node === node ) {
72106 return ;
73107 }
74-
75- let node = UI . Context . Context . instance ( ) . flavor ( SDK . DOMModel . DOMNode ) ;
76- if ( node ) {
77- node = node . enclosingElementOrSelf ( ) ;
108+ if ( this . #node) {
109+ this . #node. domModel ( ) . cssModel ( ) . removeEventListener (
110+ SDK . CSSModel . Events . StyleSheetChanged , this . requestUpdate , this ) ;
78111 }
79- if ( ! node ) {
80- // do something meaningful?
81- return ;
112+ this . #node = event . data ;
113+ if ( this . #node) {
114+ this . #node. domModel ( ) . cssModel ( ) . addEventListener (
115+ SDK . CSSModel . Events . StyleSheetChanged , this . requestUpdate , this ) ;
116+ }
117+ if ( this . isShowing ( ) ) {
118+ this . requestUpdate ( ) ;
82119 }
120+ }
83121
84- this . updateModel ( node . domModel ( ) . cssModel ( ) ) ;
85- if ( ! this . cssModel ) {
122+ override async performUpdate ( ) : Promise < void > {
123+ if ( ! this . #node ) {
86124 return ;
87125 }
88- const makeTreeNode = ( parentId : string ) => ( layer : Protocol . CSS . CSSLayerData ) => {
89- const subLayers = layer . subLayers ;
90- const name = SDK . CSSModel . CSSModel . readableLayerName ( layer . name ) ;
91- const treeNodeData = layer . order + ': ' + name ;
92- const id = parentId ? parentId + '.' + name : name ;
93- if ( ! subLayers ) {
94- return { treeNodeData, id} ;
95- }
96- return {
97- treeNodeData,
98- id,
99- children : ( ) =>
100- Promise . resolve ( subLayers . sort ( ( layer1 , layer2 ) => layer1 . order - layer2 . order ) . map ( makeTreeNode ( id ) ) ) ,
101- } ;
102- } ;
103- const rootLayer = await this . cssModel . getRootLayer ( node . id ) ;
104- this . layerTreeComponent . data = {
105- defaultRenderer : TreeOutline . TreeOutline . defaultRenderer ,
106- tree : [ makeTreeNode ( '' ) ( rootLayer ) ] ,
107- } ;
108126
109- // We only expand the first 5 user-defined layers to not make the
110- // view too overwhelming.
111- await this . layerTreeComponent . expandRecursively ( 5 ) ;
127+ const rootLayer = await this . #node. domModel ( ) . cssModel ( ) . getRootLayer ( this . #node. id ) ;
128+ const input = { rootLayer} ;
129+ const output : ViewOutput = { treeOutline : undefined } ;
130+ this . #view( input , output , this . contentElement ) ;
131+
132+ if ( output . treeOutline ) {
133+ // We only expand the first 5 user-defined layers to not make the
134+ // view too overwhelming.
135+ await output . treeOutline . expandRecursively ( 5 ) ;
136+ if ( this . #layerToReveal) {
137+ await output . treeOutline . expandToAndSelectTreeNodeId ( this . #layerToReveal) ;
138+ this . #layerToReveal = null ;
139+ }
140+ }
112141 }
113142
114143 async revealLayer ( layerName : string ) : Promise < void > {
115144 if ( ! this . isShowing ( ) ) {
116145 ElementsPanel . instance ( ) . showToolbarPane ( this , ButtonProvider . instance ( ) . item ( ) ) ;
117146 }
118- await this . update ( ) ;
119- return await this . layerTreeComponent . expandToAndSelectTreeNodeId ( 'implicit outer layer.' + layerName ) ;
147+ this . #layerToReveal = `implicit outer layer.${ layerName } ` ;
148+ this . requestUpdate ( ) ;
149+ await this . updateComplete ;
120150 }
121151
122152 static instance ( opts : {
0 commit comments