diff --git a/src/components/figures/figure/figure.ts b/src/components/figures/figure/figure.ts index 997f46e8ef..6cc35573cb 100644 --- a/src/components/figures/figure/figure.ts +++ b/src/components/figures/figure/figure.ts @@ -6,6 +6,8 @@ import { SELECTION_BORDER_COLOR, } from "../../../constants"; import { figureRegistry } from "../../../registries/index"; +import { Store, useStore } from "../../../store_engine"; +import { DOMFocusableElementStore } from "../../../stores/DOM_focus_store"; import { CSSProperties, DOMCoordinates, @@ -115,7 +117,6 @@ css/*SCSS*/ ` interface Props { figure: Figure; style: string; - onFigureDeleted: () => void; onMouseDown: (ev: MouseEvent) => void; onClickAnchor(dirX: ResizeDirection, dirY: ResizeDirection, ev: MouseEvent): void; } @@ -125,17 +126,16 @@ export class FigureComponent extends Component { static props = { figure: Object, style: { type: String, optional: true }, - onFigureDeleted: { type: Function, optional: true }, onMouseDown: { type: Function, optional: true }, onClickAnchor: { type: Function, optional: true }, }; static components = { Menu }; static defaultProps = { - onFigureDeleted: () => {}, onMouseDown: () => {}, onClickAnchor: () => {}, }; + private DOMFocusableElementStore!: Store; private menuState: MenuState = useState({ isOpen: false, position: null, menuItems: [] }); private figureRef = useRef("figure"); @@ -196,6 +196,7 @@ export class FigureComponent extends Component { } setup() { + this.DOMFocusableElementStore = useStore(DOMFocusableElementStore); const borderWidth = figureRegistry.get(this.props.figure.tag).borderWidth; this.borderWidth = borderWidth !== undefined ? borderWidth : BORDER_WIDTH; useEffect( @@ -216,7 +217,7 @@ export class FigureComponent extends Component { ); onWillUnmount(() => { - this.props.onFigureDeleted(); + this.onFigureDeleted(); }); } @@ -239,7 +240,7 @@ export class FigureComponent extends Component { sheetId: this.env.model.getters.getActiveSheetId(), id: figure.id, }); - this.props.onFigureDeleted(); + this.onFigureDeleted(); ev.preventDefault(); ev.stopPropagation(); break; @@ -304,6 +305,12 @@ export class FigureComponent extends Component { this.menuState.position = position; this.menuState.menuItems = figureRegistry .get(this.props.figure.tag) - .menuBuilder(this.props.figure.id, this.props.onFigureDeleted, this.env); + .menuBuilder(this.props.figure.id, this.onFigureDeleted.bind(this), this.env); + } + + private onFigureDeleted() { + if (document.activeElement === this.figureRef.el) { + this.DOMFocusableElementStore.focus(); + } } } diff --git a/src/components/figures/figure/figure.xml b/src/components/figures/figure/figure.xml index 009379e9cb..cfccd58828 100644 --- a/src/components/figures/figure/figure.xml +++ b/src/components/figures/figure/figure.xml @@ -14,7 +14,6 @@
diff --git a/src/components/figures/figure_chart/figure_chart.ts b/src/components/figures/figure_chart/figure_chart.ts index 7b629f393d..f8aaadbfbd 100644 --- a/src/components/figures/figure_chart/figure_chart.ts +++ b/src/components/figures/figure_chart/figure_chart.ts @@ -18,14 +18,12 @@ interface Props { // props figure is currently necessary scorecards, we need the chart dimension at render to avoid having to force the // style by hand in the useEffect() figure: Figure; - onFigureDeleted: () => void; } export class ChartFigure extends Component { static template = "o-spreadsheet-ChartFigure"; static props = { figure: Object, - onFigureDeleted: Function, }; static components = {}; diff --git a/src/components/figures/figure_container/figure_container.ts b/src/components/figures/figure_container/figure_container.ts index 8c97e8bee4..d501580250 100644 --- a/src/components/figures/figure_container/figure_container.ts +++ b/src/components/figures/figure_container/figure_container.ts @@ -22,9 +22,7 @@ import { FigureComponent } from "../figure/figure"; type ContainerType = "topLeft" | "topRight" | "bottomLeft" | "bottomRight" | "dnd"; -interface Props { - onFigureDeleted: () => void; -} +interface Props {} interface Container { type: ContainerType; @@ -127,9 +125,7 @@ css/*SCSS*/ ` */ export class FiguresContainer extends Component { static template = "o-spreadsheet-FiguresContainer"; - static props = { - onFigureDeleted: Function, - }; + static props = {}; static components = { FigureComponent }; dnd = useState({ diff --git a/src/components/figures/figure_container/figure_container.xml b/src/components/figures/figure_container/figure_container.xml index 2cff25b0c1..6e2a56d086 100644 --- a/src/components/figures/figure_container/figure_container.xml +++ b/src/components/figures/figure_container/figure_container.xml @@ -11,7 +11,6 @@ t-att-style="container.inverseViewportStyle"> void; } export class ImageFigure extends Component { static template = "o-spreadsheet-ImageFigure"; static props = { figure: Object, - onFigureDeleted: Function, }; static components = {}; diff --git a/src/components/grid/grid.xml b/src/components/grid/grid.xml index 577124b601..2db6484a2e 100644 --- a/src/components/grid/grid.xml +++ b/src/components/grid/grid.xml @@ -16,7 +16,6 @@ onGridResized.bind="onGridResized" onGridMoved.bind="moveCanvas" gridOverlayDimensions="gridOverlayDimensions" - onFigureDeleted.bind="focusDefaultElement" /> void; -} +interface Props {} export class GridAddRowsFooter extends Component { static template = "o-spreadsheet-GridAddRowsFooter"; - static props = { - focusGrid: Function, - }; + static props = {}; static components = { ValidationMessages }; + + private DOMFocusableElementStore!: Store; + inputRef = useRef("inputRef"); state = useState({ inputValue: "100", @@ -37,6 +38,7 @@ export class GridAddRowsFooter extends Component { }); setup() { + this.DOMFocusableElementStore = useStore(DOMFocusableElementStore); useExternalListener(window, "click", this.onExternalClick, { capture: true }); } @@ -58,7 +60,7 @@ export class GridAddRowsFooter extends Component { onKeydown(ev: KeyboardEvent) { if (ev.key.toUpperCase() === "ESCAPE") { - this.props.focusGrid(); + this.focusDefaultElement(); } else if (ev.key.toUpperCase() === "ENTER") { this.onConfirm(); } @@ -85,7 +87,7 @@ export class GridAddRowsFooter extends Component { quantity, dimension: "ROW", }); - this.props.focusGrid(); + this.focusDefaultElement(); // After adding new rows, scroll down to the new last row const { scrollX } = this.env.model.getters.getActiveSheetDOMScrollInfo(); @@ -103,6 +105,12 @@ export class GridAddRowsFooter extends Component { if (this.inputRef.el !== document.activeElement || ev.target === this.inputRef.el) { return; } - this.props.focusGrid(); + this.focusDefaultElement(); + } + + private focusDefaultElement() { + if (document.activeElement === this.inputRef.el) { + this.DOMFocusableElementStore.focus(); + } } } diff --git a/src/components/grid_overlay/grid_overlay.ts b/src/components/grid_overlay/grid_overlay.ts index 63426dfa13..30cf63cf0e 100644 --- a/src/components/grid_overlay/grid_overlay.ts +++ b/src/components/grid_overlay/grid_overlay.ts @@ -129,7 +129,6 @@ interface Props { onGridResized: (dimension: Rect) => void; onGridMoved: (deltaX: Pixel, deltaY: Pixel) => void; gridOverlayDimensions: string; - onFigureDeleted: () => void; } export class GridOverlay extends Component { @@ -140,7 +139,6 @@ export class GridOverlay extends Component { onCellClicked: { type: Function, optional: true }, onCellRightClicked: { type: Function, optional: true }, onGridResized: { type: Function, optional: true }, - onFigureDeleted: { type: Function, optional: true }, onGridMoved: Function, gridOverlayDimensions: String, }; @@ -156,7 +154,6 @@ export class GridOverlay extends Component { onCellClicked: () => {}, onCellRightClicked: () => {}, onGridResized: () => {}, - onFigureDeleted: () => {}, }; private gridOverlay: Ref = useRef("gridOverlay"); private gridOverlayRect = useAbsoluteBoundingRect(this.gridOverlay); diff --git a/src/components/grid_overlay/grid_overlay.xml b/src/components/grid_overlay/grid_overlay.xml index 16a364037c..308fb4c71a 100644 --- a/src/components/grid_overlay/grid_overlay.xml +++ b/src/components/grid_overlay/grid_overlay.xml @@ -8,13 +8,12 @@ t-on-pointerdown="onMouseDown" t-on-dblclick.self="onDoubleClick" t-on-contextmenu.stop.prevent="onContextMenu"> - +
diff --git a/tests/figures/figure_component.test.ts b/tests/figures/figure_component.test.ts index aa96276482..343d36f630 100644 --- a/tests/figures/figure_component.test.ts +++ b/tests/figures/figure_component.test.ts @@ -714,6 +714,23 @@ describe("figures", () => { expect(bottomRightContainerStyle.height).toEqual(`${height - 2 * DEFAULT_CELL_HEIGHT}px`); }); + test("Deleting a figure does not change the DOM focus if the figure was not focused", async () => { + createFigure(model); + await nextTick(); + env.openSidePanel("FindAndReplace"); + await nextTick(); + + const panelInput = fixture.querySelector(".o-sidePanel input"); + panelInput?.focus(); + expect(document.activeElement).toBe(panelInput); + + const figureId = model.getters.getFigures(sheetId)[0].id; + model.dispatch("DELETE_FIGURE", { sheetId, id: figureId }); + await nextTick(); + + expect(document.activeElement).toBe(panelInput); + }); + describe("Figure drag & drop snap", () => { describe("Move figure", () => { test.each([