mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Canvas: Position inline editor default via panel dimensions and add context menu option (#51471)
Co-authored-by: nmarrs <nathanielmarrs@gmail.com>
This commit is contained in:
parent
63366615bb
commit
dec3c3a5b1
@ -63,6 +63,8 @@ export class Scene {
|
||||
|
||||
isPanelEditing = locationService.getSearchObject().editPanel !== undefined;
|
||||
|
||||
inlineEditingCallback?: () => void;
|
||||
|
||||
constructor(cfg: CanvasFrameOptions, enableEditing: boolean, public onSave: (cfg: CanvasFrameOptions) => void) {
|
||||
this.root = this.load(cfg, enableEditing);
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useObservable } from 'react-use';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
import { ContextMenu, MenuItem } from '@grafana/ui';
|
||||
|
||||
import { Scene } from '../../../features/canvas/runtime/scene';
|
||||
|
||||
import { activePanelSubject } from './CanvasPanel';
|
||||
import { LayerActionID } from './types';
|
||||
|
||||
type Props = {
|
||||
@ -18,6 +20,8 @@ type AnchorPoint = {
|
||||
};
|
||||
|
||||
export const CanvasContextMenu = ({ scene }: Props) => {
|
||||
const activePanel = useObservable(activePanelSubject);
|
||||
const inlineEditorOpen = activePanel?.panel.state.openInlineEdit;
|
||||
const [isMenuVisible, setIsMenuVisible] = useState<boolean>(false);
|
||||
const [anchorPoint, setAnchorPoint] = useState<AnchorPoint>({ x: 0, y: 0 });
|
||||
|
||||
@ -87,6 +91,20 @@ export const CanvasContextMenu = ({ scene }: Props) => {
|
||||
}}
|
||||
className={styles.menuItem}
|
||||
/>
|
||||
<MenuItem
|
||||
label={inlineEditorOpen ? 'Close Editor' : 'Open Editor'}
|
||||
onClick={() => {
|
||||
if (scene.inlineEditingCallback) {
|
||||
if (inlineEditorOpen) {
|
||||
activePanel.panel.inlineEditButtonClose();
|
||||
} else {
|
||||
scene.inlineEditingCallback();
|
||||
}
|
||||
}
|
||||
closeContextMenu();
|
||||
}}
|
||||
className={styles.menuItem}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -57,6 +57,7 @@ export class CanvasPanel extends Component<Props, State> {
|
||||
this.scene = new Scene(this.props.options.root, this.props.options.inlineEditing, this.onUpdateScene);
|
||||
this.scene.updateSize(props.width, props.height);
|
||||
this.scene.updateData(props.data);
|
||||
this.scene.inlineEditingCallback = this.inlineEditButtonClick;
|
||||
|
||||
this.subs.add(
|
||||
this.props.eventBus.subscribe(PanelEditEnteredEvent, (evt) => {
|
||||
@ -192,7 +193,9 @@ export class CanvasPanel extends Component<Props, State> {
|
||||
};
|
||||
|
||||
renderInlineEdit = () => {
|
||||
return <InlineEdit onClose={() => this.inlineEditButtonClose()} />;
|
||||
return (
|
||||
<InlineEdit onClose={() => this.inlineEditButtonClose()} id={this.props.id} scene={activeCanvasPanel!.scene} />
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
|
@ -1,29 +1,35 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React, { SyntheticEvent, useRef, useState } from 'react';
|
||||
import React, { SyntheticEvent, useEffect, useRef, useState } from 'react';
|
||||
import Draggable from 'react-draggable';
|
||||
import { Resizable, ResizeCallbackData } from 'react-resizable';
|
||||
|
||||
import { Dimensions2D, GrafanaTheme2 } from '@grafana/data';
|
||||
import { IconButton, Portal, useStyles2 } from '@grafana/ui';
|
||||
import store from 'app/core/store';
|
||||
import { Scene } from 'app/features/canvas/runtime/scene';
|
||||
|
||||
import { InlineEditBody } from './InlineEditBody';
|
||||
|
||||
type Props = {
|
||||
onClose?: () => void;
|
||||
id: number;
|
||||
scene: Scene;
|
||||
};
|
||||
|
||||
const OFFSET_X = 70;
|
||||
const OFFSET_X = 10;
|
||||
const OFFSET_Y = 32;
|
||||
|
||||
export const InlineEdit = ({ onClose }: Props) => {
|
||||
const btnInlineEdit = document.querySelector('[data-btninlineedit]')!.getBoundingClientRect();
|
||||
export const InlineEdit = ({ onClose, id, scene }: Props) => {
|
||||
const root = scene.root.div!.getBoundingClientRect();
|
||||
const windowHeight = window.innerHeight;
|
||||
const windowWidth = window.innerWidth;
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const styles = useStyles2(getStyles);
|
||||
const inlineEditKey = 'inlineEditPanel';
|
||||
const inlineEditKey = 'inlineEditPanel' + id.toString();
|
||||
|
||||
const defaultMeasurements = { width: 350, height: 400 };
|
||||
const defaultX = btnInlineEdit.x + OFFSET_X;
|
||||
const defaultY = btnInlineEdit.y - defaultMeasurements.height;
|
||||
const defaultX = root.x + root.width - defaultMeasurements.width - OFFSET_X;
|
||||
const defaultY = root.y + OFFSET_Y;
|
||||
|
||||
const savedPlacement = store.getObject(inlineEditKey, {
|
||||
x: defaultX,
|
||||
@ -34,6 +40,18 @@ export const InlineEdit = ({ onClose }: Props) => {
|
||||
const [measurements, setMeasurements] = useState<Dimensions2D>({ width: savedPlacement.w, height: savedPlacement.h });
|
||||
const [placement, setPlacement] = useState({ x: savedPlacement.x, y: savedPlacement.y });
|
||||
|
||||
// Checks that placement is within browser window
|
||||
useEffect(() => {
|
||||
const minX = windowWidth - measurements.width - OFFSET_X;
|
||||
const minY = windowHeight - measurements.height - OFFSET_Y;
|
||||
if (minX < placement.x && minX > 0) {
|
||||
setPlacement({ ...placement, x: minX });
|
||||
}
|
||||
if (minY < placement.y && minY > 0) {
|
||||
setPlacement({ ...placement, y: minY });
|
||||
}
|
||||
}, [windowHeight, windowWidth, placement, measurements]);
|
||||
|
||||
const onDragStop = (event: any, dragElement: any) => {
|
||||
let x = dragElement.x < 0 ? 0 : dragElement.x;
|
||||
let y = dragElement.y < 0 ? 0 : dragElement.y;
|
||||
|
Loading…
Reference in New Issue
Block a user