@@ -11,10 +11,12 @@ import {
1111 mdiMagnify ,
1212 mdiPencil ,
1313 mdiPlus ,
14+ mdiRedo ,
1415 mdiRefresh ,
1516 mdiRobot ,
1617 mdiShape ,
1718 mdiSofa ,
19+ mdiUndo ,
1820 mdiViewDashboard ,
1921} from "@mdi/js" ;
2022import type { CSSResultGroup , PropertyValues , TemplateResult } from "lit" ;
@@ -50,7 +52,10 @@ import "../../components/ha-tab-group-tab";
5052import "../../components/ha-tooltip" ;
5153import { createAreaRegistryEntry } from "../../data/area_registry" ;
5254import type { LovelacePanelConfig } from "../../data/lovelace" ;
53- import type { LovelaceConfig } from "../../data/lovelace/config/types" ;
55+ import type {
56+ LovelaceConfig ,
57+ LovelaceRawConfig ,
58+ } from "../../data/lovelace/config/types" ;
5459import { isStrategyDashboard } from "../../data/lovelace/config/types" ;
5560import type { LovelaceViewConfig } from "../../data/lovelace/config/view" ;
5661import {
@@ -92,6 +97,7 @@ import "./views/hui-view";
9297import type { HUIView } from "./views/hui-view" ;
9398import "./views/hui-view-background" ;
9499import "./views/hui-view-container" ;
100+ import { UndoRedoController } from "../../common/controllers/undo-redo-controller" ;
95101
96102interface ActionItem {
97103 icon : string ;
@@ -113,6 +119,11 @@ interface SubActionItem {
113119 visible : boolean | undefined ;
114120}
115121
122+ interface UndoStackItem {
123+ location : string ;
124+ config : LovelaceRawConfig ;
125+ }
126+
116127@customElement ( "hui-root" )
117128class HUIRoot extends LitElement {
118129 @property ( { attribute : false } ) public panel ?: PanelInfo < LovelacePanelConfig > ;
@@ -130,12 +141,22 @@ class HUIRoot extends LitElement {
130141
131142 @state ( ) private _curView ?: number | "hass-unused-entities" ;
132143
144+ private _configChangedByUndo = false ;
145+
133146 private _viewCache ?: Record < string , HUIView > ;
134147
135148 private _viewScrollPositions : Record < string , number > = { } ;
136149
137150 private _restoreScroll = false ;
138151
152+ private _undoRedoController = new UndoRedoController < UndoStackItem > ( this , {
153+ apply : ( config ) => this . _applyUndoRedo ( config ) ,
154+ currentConfig : ( ) => ( {
155+ location : this . route ! . path ,
156+ config : this . lovelace ! . rawConfig ,
157+ } ) ,
158+ } ) ;
159+
139160 private _debouncedConfigChanged : ( ) => void ;
140161
141162 private _conversation = memoizeOne ( ( _components ) =>
@@ -157,7 +178,29 @@ class HUIRoot extends LitElement {
157178 const result : TemplateResult [ ] = [ ] ;
158179 if ( this . _editMode ) {
159180 result . push (
160- html `<ha- butto n
181+ html `<ha- icon- butto n
182+ slot= "toolbar-icon"
183+ .path = ${ mdiUndo }
184+ @click = ${ this . _undo }
185+ .disabled = ${ ! this . _undoRedoController . canUndo }
186+ id= "button-undo"
187+ >
188+ </ ha- icon- butto n>
189+ <ha- to oltip placement= "bottom" for = "button-undo" >
190+ ${ this . hass . localize ( "ui.common.undo" ) }
191+ </ ha- to oltip>
192+ <ha- icon- butto n
193+ slot= "toolbar-icon"
194+ .path = ${ mdiRedo }
195+ @click = ${ this . _redo }
196+ .disabled = ${ ! this . _undoRedoController . canRedo }
197+ id= "button-redo"
198+ >
199+ </ ha- icon- butto n>
200+ <ha- to oltip placement= "bottom" for = "button-redo" >
201+ ${ this . hass . localize ( "ui.common.redo" ) }
202+ </ ha- to oltip>
203+ <ha- butto n
161204 appearance= "filled"
162205 size = "small"
163206 class = "exit-edit-mode"
@@ -645,6 +688,27 @@ class HUIRoot extends LitElement {
645688 window . history . scrollRestoration = "auto" ;
646689 }
647690
691+ protected willUpdate ( changedProperties : PropertyValues ) : void {
692+ if ( changedProperties . has ( "lovelace" ) ) {
693+ const oldLovelace = changedProperties . get ( "lovelace" ) as
694+ | Lovelace
695+ | undefined ;
696+
697+ if (
698+ oldLovelace &&
699+ this . lovelace ! . rawConfig !== oldLovelace ! . rawConfig &&
700+ ! this . _configChangedByUndo
701+ ) {
702+ this . _undoRedoController . commit ( {
703+ location : this . route ! . path ,
704+ config : oldLovelace . rawConfig ,
705+ } ) ;
706+ } else {
707+ this . _configChangedByUndo = false ;
708+ }
709+ }
710+ }
711+
648712 protected updated ( changedProperties : PropertyValues ) : void {
649713 super . updated ( changedProperties ) ;
650714
@@ -1029,6 +1093,7 @@ class HUIRoot extends LitElement {
10291093
10301094 private _editModeDisable ( ) : void {
10311095 this . lovelace ! . setEditMode ( false ) ;
1096+ this . _undoRedoController . reset ( ) ;
10321097 }
10331098
10341099 private async _editDashboard ( ) {
@@ -1207,6 +1272,36 @@ class HUIRoot extends LitElement {
12071272 showShortcutsDialog ( this ) ;
12081273 }
12091274
1275+ private async _applyUndoRedo ( item : UndoStackItem ) {
1276+ this . _configChangedByUndo = true ;
1277+ try {
1278+ await this . lovelace ! . saveConfig ( item . config ) ;
1279+ } catch ( err : any ) {
1280+ this . _configChangedByUndo = false ;
1281+ showToast ( this , {
1282+ message : this . hass . localize (
1283+ "ui.panel.lovelace.editor.undo_redo_failed_to_apply_changes" ,
1284+ {
1285+ error : err . message ,
1286+ }
1287+ ) ,
1288+ duration : 4000 ,
1289+ dismissable : true ,
1290+ } ) ;
1291+ return ;
1292+ }
1293+
1294+ this . _navigateToView ( item . location ) ;
1295+ }
1296+
1297+ private _undo ( ) {
1298+ this . _undoRedoController . undo ( ) ;
1299+ }
1300+
1301+ private _redo ( ) {
1302+ this . _undoRedoController . redo ( ) ;
1303+ }
1304+
12101305 static get styles ( ) : CSSResultGroup {
12111306 return [
12121307 haStyle ,
0 commit comments