Scene: Consolidate layout props on a layout prop (formerly named size) (#60437)

* Initial prop rename changes

* Updates

* Rename layout to placement

* Fix

* Fixed test

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
This commit is contained in:
Torkel Ödegaard 2022-12-27 09:05:06 +01:00 committed by GitHub
parent 12c292fd44
commit 591c86e31d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 202 additions and 235 deletions

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.8
// +build go1.8
package xorm

View File

@ -143,9 +143,10 @@ func (session *Session) cacheUpdate(table *core.Table, tableName, sqlStr string,
// Update records, bean's non-empty fields are updated contents,
// condiBean' non-empty filds are conditions
// CAUTION:
// 1.bool will defaultly be updated content nor conditions
// You should call UseBool if you have bool to use.
// 2.float32 & float64 may be not inexact as conditions
//
// 1.bool will defaultly be updated content nor conditions
// You should call UseBool if you have bool to use.
// 2.float32 & float64 may be not inexact as conditions
func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int64, error) {
if session.isAutoClose {
defer session.Close()

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !windows && !nacl && !plan9
// +build !windows,!nacl,!plan9
package xorm

View File

@ -13,13 +13,13 @@ function setup() {
const store = configureStore();
const scene = new Scene({
title: 'Hello',
layout: new SceneFlexLayout({
body: new SceneFlexLayout({
children: [
new NestedScene({
title: 'Nested title',
canRemove: true,
canCollapse: true,
layout: new SceneFlexLayout({
body: new SceneFlexLayout({
children: [new SceneCanvasText({ text: 'SceneCanvasText' })],
}),
}),

View File

@ -13,7 +13,7 @@ interface NestedSceneState extends SceneLayoutChildState {
isCollapsed?: boolean;
canCollapse?: boolean;
canRemove?: boolean;
layout: SceneLayout;
body: SceneLayout;
actions?: SceneObject[];
}
@ -23,8 +23,8 @@ export class NestedScene extends SceneObjectBase<NestedSceneState> {
public onToggle = () => {
this.setState({
isCollapsed: !this.state.isCollapsed,
size: {
...this.state.size,
placement: {
...this.state.placement,
ySizing: this.state.isCollapsed ? 'fill' : 'content',
},
});
@ -42,7 +42,7 @@ export class NestedScene extends SceneObjectBase<NestedSceneState> {
}
export function NestedSceneRenderer({ model, isEditing }: SceneComponentProps<NestedScene>) {
const { title, isCollapsed, canCollapse, canRemove, layout, actions } = model.useState();
const { title, isCollapsed, canCollapse, canRemove, body, actions } = model.useState();
const styles = useStyles2(getStyles);
const toolbarActions = (actions ?? []).map((action) => <action.Component key={action.state.key} model={action} />);
@ -81,7 +81,7 @@ export function NestedSceneRenderer({ model, isEditing }: SceneComponentProps<Ne
</Stack>
<div className={styles.actions}>{toolbarActions}</div>
</div>
{!isCollapsed && <layout.Component model={layout} isEditing={isEditing} />}
{!isCollapsed && <body.Component model={body} isEditing={isEditing} />}
</div>
);
}

View File

@ -5,7 +5,7 @@ describe('Scene', () => {
it('Simple scene', () => {
const scene = new Scene({
title: 'Hello',
layout: new SceneFlexLayout({
body: new SceneFlexLayout({
children: [],
}),
});

View File

@ -12,7 +12,7 @@ import { UrlSyncManager } from '../services/UrlSyncManager';
interface SceneState extends SceneObjectStatePlain {
title: string;
layout: SceneObject;
body: SceneObject;
actions?: SceneObject[];
subMenu?: SceneObject;
isEditing?: boolean;
@ -39,7 +39,7 @@ export class EmbeddedScene extends Scene {
}
function EmbeddedSceneRenderer({ model }: SceneComponentProps<Scene>) {
const { layout, isEditing, subMenu } = model.useState();
const { body, isEditing, subMenu } = model.useState();
return (
<div
style={{
@ -53,13 +53,13 @@ function EmbeddedSceneRenderer({ model }: SceneComponentProps<Scene>) {
>
{subMenu && <subMenu.Component model={subMenu} />}
<div style={{ flexGrow: 1, display: 'flex', gap: '8px', overflow: 'auto' }}>
<layout.Component model={layout} isEditing={isEditing} />
<body.Component model={body} isEditing={isEditing} />
</div>
</div>
);
}
function SceneRenderer({ model }: SceneComponentProps<Scene>) {
const { title, layout, actions = [], isEditing, $editor, subMenu } = model.useState();
const { title, body, actions = [], isEditing, $editor, subMenu } = model.useState();
const toolbarActions = (actions ?? []).map((action) => <action.Component key={action.state.key} model={action} />);
@ -85,7 +85,7 @@ function SceneRenderer({ model }: SceneComponentProps<Scene>) {
<div style={{ flexGrow: 1, display: 'flex', flexDirection: 'column', gap: '8px' }}>
{subMenu && <subMenu.Component model={subMenu} />}
<div style={{ flexGrow: 1, display: 'flex', gap: '8px', overflow: 'auto' }}>
<layout.Component model={layout} isEditing={isEditing} />
<body.Component model={body} isEditing={isEditing} />
{$editor && <$editor.Component model={$editor} isEditing={isEditing} />}
</div>
</div>

View File

@ -14,14 +14,16 @@ import { SceneDragHandle } from '../SceneDragHandle';
import { VizPanel } from './VizPanel';
export function VizPanelRenderer({ model }: SceneComponentProps<VizPanel>) {
const { title, options, fieldConfig, pluginId, pluginLoadError, $data, ...state } = model.useState();
const { title, options, fieldConfig, pluginId, pluginLoadError, $data, placement } = model.useState();
const [ref, { width, height }] = useMeasure();
const plugin = model.getPlugin();
const { data } = sceneGraph.getData(model).useState();
const layout = sceneGraph.getLayout(model);
const parentLayout = sceneGraph.getLayout(model);
const isDraggable = layout.state.isDraggable ? state.isDraggable : false;
const dragHandle = <SceneDragHandle layoutKey={layout.state.key!} />;
// TODO: this should probably be parentLayout.isDraggingEnabled() ? placement?.isDraggable : false
// The current logic is not correct, just because parent layout itself is not draggable does not mean children are not
const isDraggable = parentLayout.state.placement?.isDraggable ? placement?.isDraggable : false;
const dragHandle = <SceneDragHandle layoutKey={parentLayout.state.key!} />;
const titleInterpolated = sceneGraph.interpolate(model, title);

View File

@ -3,7 +3,7 @@ import React, { CSSProperties } from 'react';
import { Field, RadioButtonGroup } from '@grafana/ui';
import { SceneObjectBase } from '../../core/SceneObjectBase';
import { SceneComponentProps, SceneLayoutChild, SceneLayoutState, SceneObjectSize } from '../../core/types';
import { SceneComponentProps, SceneLayoutChild, SceneLayoutState, SceneLayoutChildOptions } from '../../core/types';
export type FlexLayoutDirection = 'column' | 'row';
@ -43,47 +43,47 @@ function FlexLayoutChildComponent({
direction: FlexLayoutDirection;
isEditing?: boolean;
}) {
const { size } = item.useState();
const { placement } = item.useState();
return (
<div style={getItemStyles(direction, size)}>
<div style={getItemStyles(direction, placement)}>
<item.Component model={item} isEditing={isEditing} />
</div>
);
}
function getItemStyles(direction: FlexLayoutDirection, sizing: SceneObjectSize = {}) {
const { xSizing = 'fill', ySizing = 'fill' } = sizing;
function getItemStyles(direction: FlexLayoutDirection, layout: SceneLayoutChildOptions = {}) {
const { xSizing = 'fill', ySizing = 'fill' } = layout;
const style: CSSProperties = {
display: 'flex',
flexDirection: direction,
minWidth: sizing.minWidth,
minHeight: sizing.minHeight,
minWidth: layout.minWidth,
minHeight: layout.minHeight,
position: 'relative',
};
if (direction === 'column') {
if (sizing.height) {
style.height = sizing.height;
if (layout.height) {
style.height = layout.height;
} else {
style.flexGrow = ySizing === 'fill' ? 1 : 0;
}
if (sizing.width) {
style.width = sizing.width;
if (layout.width) {
style.width = layout.width;
} else {
style.alignSelf = xSizing === 'fill' ? 'stretch' : 'flex-start';
}
} else {
if (sizing.height) {
style.height = sizing.height;
if (layout.height) {
style.height = layout.height;
} else {
style.alignSelf = ySizing === 'fill' ? 'stretch' : 'flex-start';
}
if (sizing.width) {
style.width = sizing.width;
if (layout.width) {
style.width = layout.width;
} else {
style.flexGrow = xSizing === 'fill' ? 1 : 0;
}

View File

@ -34,10 +34,10 @@ describe('SceneGridLayout', () => {
it('should render all grid children', async () => {
const scene = new Scene({
title: 'Grid test',
layout: new SceneGridLayout({
body: new SceneGridLayout({
children: [
new TestObject({ size: { x: 0, y: 0, width: 12, height: 5 } }),
new TestObject({ size: { x: 0, y: 5, width: 12, height: 5 } }),
new TestObject({ placement: { x: 0, y: 0, width: 12, height: 5 } }),
new TestObject({ placement: { x: 0, y: 5, width: 12, height: 5 } }),
],
}),
});
@ -50,16 +50,16 @@ describe('SceneGridLayout', () => {
it('should not render children of a collapsed row', async () => {
const scene = new Scene({
title: 'Grid test',
layout: new SceneGridLayout({
body: new SceneGridLayout({
children: [
new TestObject({ key: 'a', size: { x: 0, y: 0, width: 12, height: 5 } }),
new TestObject({ key: 'b', size: { x: 0, y: 5, width: 12, height: 5 } }),
new TestObject({ key: 'a', placement: { x: 0, y: 0, width: 12, height: 5 } }),
new TestObject({ key: 'b', placement: { x: 0, y: 5, width: 12, height: 5 } }),
new SceneGridRow({
title: 'Row A',
key: 'Row A',
isCollapsed: true,
size: { y: 10 },
children: [new TestObject({ key: 'c', size: { x: 0, y: 11, width: 12, height: 5 } })],
placement: { y: 10 },
children: [new TestObject({ key: 'c', placement: { x: 0, y: 11, width: 12, height: 5 } })],
}),
],
}),
@ -73,16 +73,16 @@ describe('SceneGridLayout', () => {
it('should render children of an expanded row', async () => {
const scene = new Scene({
title: 'Grid test',
layout: new SceneGridLayout({
body: new SceneGridLayout({
children: [
new TestObject({ key: 'a', size: { x: 0, y: 0, width: 12, height: 5 } }),
new TestObject({ key: 'b', size: { x: 0, y: 5, width: 12, height: 5 } }),
new TestObject({ key: 'a', placement: { x: 0, y: 0, width: 12, height: 5 } }),
new TestObject({ key: 'b', placement: { x: 0, y: 5, width: 12, height: 5 } }),
new SceneGridRow({
title: 'Row A',
key: 'Row A',
isCollapsed: false,
size: { y: 10 },
children: [new TestObject({ key: 'c', size: { x: 0, y: 11, width: 12, height: 5 } })],
placement: { y: 10 },
children: [new TestObject({ key: 'c', placement: { x: 0, y: 11, width: 12, height: 5 } })],
}),
],
}),
@ -98,9 +98,9 @@ describe('SceneGridLayout', () => {
it('shoud update layout children placement and order ', () => {
const layout = new SceneGridLayout({
children: [
new TestObject({ key: 'a', size: { x: 0, y: 0, width: 1, height: 1 } }),
new TestObject({ key: 'b', size: { x: 1, y: 0, width: 1, height: 1 } }),
new TestObject({ key: 'c', size: { x: 0, y: 1, width: 1, height: 1 } }),
new TestObject({ key: 'a', placement: { x: 0, y: 0, width: 1, height: 1 } }),
new TestObject({ key: 'b', placement: { x: 1, y: 0, width: 1, height: 1 } }),
new TestObject({ key: 'c', placement: { x: 0, y: 1, width: 1, height: 1 } }),
],
});
layout.onDragStop(
@ -130,24 +130,24 @@ describe('SceneGridLayout', () => {
);
expect(layout.state.children[0].state.key).toEqual('b');
expect(layout.state.children[0].state.size).toEqual({ x: 0, y: 0, width: 1, height: 1 });
expect(layout.state.children[0].state.placement).toEqual({ x: 0, y: 0, width: 1, height: 1 });
expect(layout.state.children[1].state.key).toEqual('a');
expect(layout.state.children[1].state.size).toEqual({ x: 0, y: 1, width: 1, height: 1 });
expect(layout.state.children[1].state.placement).toEqual({ x: 0, y: 1, width: 1, height: 1 });
expect(layout.state.children[2].state.key).toEqual('c');
expect(layout.state.children[2].state.size).toEqual({ x: 0, y: 2, width: 1, height: 1 });
expect(layout.state.children[2].state.placement).toEqual({ x: 0, y: 2, width: 1, height: 1 });
});
});
describe('when using rows', () => {
it('should update objects relations when moving object out of a row', () => {
const rowAChild1 = new TestObject({ key: 'row-a-child1', size: { x: 0, y: 1, width: 1, height: 1 } });
const rowAChild2 = new TestObject({ key: 'row-a-child2', size: { x: 1, y: 1, width: 1, height: 1 } });
const rowAChild1 = new TestObject({ key: 'row-a-child1', placement: { x: 0, y: 1, width: 1, height: 1 } });
const rowAChild2 = new TestObject({ key: 'row-a-child2', placement: { x: 1, y: 1, width: 1, height: 1 } });
const sourceRow = new SceneGridRow({
title: 'Row A',
key: 'row-a',
children: [rowAChild1, rowAChild2],
size: { y: 0 },
placement: { y: 0 },
});
const layout = new SceneGridLayout({
@ -169,8 +169,8 @@ describe('SceneGridLayout', () => {
expect(updatedLayout[1]).not.toEqual(rowAChild1);
});
it('should update objects relations when moving objects between rows', () => {
const rowAChild1 = new TestObject({ key: 'row-a-child1', size: { x: 0, y: 0, width: 1, height: 1 } });
const rowAChild2 = new TestObject({ key: 'row-a-child2', size: { x: 1, y: 0, width: 1, height: 1 } });
const rowAChild1 = new TestObject({ key: 'row-a-child1', placement: { x: 0, y: 0, width: 1, height: 1 } });
const rowAChild2 = new TestObject({ key: 'row-a-child2', placement: { x: 1, y: 0, width: 1, height: 1 } });
const sourceRow = new SceneGridRow({
title: 'Row A',
@ -184,7 +184,7 @@ describe('SceneGridLayout', () => {
children: [],
});
const panelOutsideARow = new TestObject({ key: 'a', size: { x: 0, y: 0, width: 1, height: 1 } });
const panelOutsideARow = new TestObject({ key: 'a', placement: { x: 0, y: 0, width: 1, height: 1 } });
const layout = new SceneGridLayout({
children: [panelOutsideARow, sourceRow, targetRow],
});
@ -210,25 +210,25 @@ describe('SceneGridLayout', () => {
});
it('should update position of objects when row is expanded', () => {
const rowAChild1 = new TestObject({ key: 'row-a-child1', size: { x: 0, y: 1, width: 1, height: 1 } });
const rowAChild2 = new TestObject({ key: 'row-a-child2', size: { x: 1, y: 1, width: 1, height: 1 } });
const rowAChild1 = new TestObject({ key: 'row-a-child1', placement: { x: 0, y: 1, width: 1, height: 1 } });
const rowAChild2 = new TestObject({ key: 'row-a-child2', placement: { x: 1, y: 1, width: 1, height: 1 } });
const rowA = new SceneGridRow({
title: 'Row A',
key: 'row-a',
children: [rowAChild1, rowAChild2],
size: { y: 0 },
placement: { y: 0 },
isCollapsed: true,
});
const panelOutsideARow = new TestObject({ key: 'outsider', size: { x: 0, y: 1, width: 1, height: 1 } });
const panelOutsideARow = new TestObject({ key: 'outsider', placement: { x: 0, y: 1, width: 1, height: 1 } });
const rowBChild1 = new TestObject({ key: 'row-b-child1', size: { x: 0, y: 3, width: 1, height: 1 } });
const rowBChild1 = new TestObject({ key: 'row-b-child1', placement: { x: 0, y: 3, width: 1, height: 1 } });
const rowB = new SceneGridRow({
title: 'Row B',
key: 'row-b',
children: [rowBChild1],
size: { y: 2 },
placement: { y: 2 },
isCollapsed: false,
});
@ -238,9 +238,9 @@ describe('SceneGridLayout', () => {
layout.toggleRow(rowA);
expect(panelOutsideARow.state!.size!.y).toEqual(2);
expect(rowB.state!.size!.y).toEqual(3);
expect(rowBChild1.state!.size!.y).toEqual(4);
expect(panelOutsideARow.state!.placement!.y).toEqual(2);
expect(rowB.state!.placement!.y).toEqual(3);
expect(rowBChild1.state!.placement!.y).toEqual(4);
});
});
});

View File

@ -5,7 +5,7 @@ import AutoSizer from 'react-virtualized-auto-sizer';
import { DEFAULT_PANEL_SPAN, GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT } from 'app/core/constants';
import { SceneObjectBase } from '../../core/SceneObjectBase';
import { SceneComponentProps, SceneLayoutChild, SceneLayoutState, SceneObjectSize } from '../../core/types';
import { SceneComponentProps, SceneLayoutChild, SceneLayoutState, SceneLayoutChildOptions } from '../../core/types';
import { SceneGridRow } from './SceneGridRow';
@ -18,8 +18,11 @@ export class SceneGridLayout extends SceneObjectBase<SceneGridLayoutState> {
public constructor(state: SceneGridLayoutState) {
super({
isDraggable: true,
...state,
placement: {
isDraggable: true,
...state.placement,
},
children: sortChildrenByPosition(state.children),
});
}
@ -45,8 +48,8 @@ export class SceneGridLayout extends SceneObjectBase<SceneGridLayoutState> {
// Ok we are expanding row. We need to update row children y pos (incase they are incorrect) and push items below down
// Code copied from DashboardModel toggleRow()
const rowY = row.state.size?.y!;
const firstPanelYPos = rowChildren[0].state.size?.y ?? rowY;
const rowY = row.state.placement?.y!;
const firstPanelYPos = rowChildren[0].state.placement?.y ?? rowY;
const yDiff = firstPanelYPos - (rowY + 1);
// y max will represent the bottom y pos after all panels have been added
@ -55,12 +58,12 @@ export class SceneGridLayout extends SceneObjectBase<SceneGridLayoutState> {
for (const panel of rowChildren) {
// set the y gridPos if it wasn't already set
const newSize = { ...panel.state.size };
const newSize = { ...panel.state.placement };
newSize.y = newSize.y ?? rowY;
// make sure y is adjusted (in case row moved while collapsed)
newSize.y -= yDiff;
if (newSize.y > panel.state.size?.y!) {
panel.setState({ size: newSize });
if (newSize.y > panel.state.placement?.y!) {
panel.setState({ placement: newSize });
}
// update insert post and y max
yMax = Math.max(yMax, Number(newSize.y!) + Number(newSize.height!));
@ -70,13 +73,13 @@ export class SceneGridLayout extends SceneObjectBase<SceneGridLayoutState> {
// push panels below down
for (const child of this.state.children) {
if (child.state.size?.y! > rowY) {
if (child.state.placement?.y! > rowY) {
this.pushChildDown(child, pushDownAmount);
}
if (child instanceof SceneGridRow && child !== row) {
for (const rowChild of child.state.children) {
if (rowChild.state.size?.y! > rowY) {
if (rowChild.state.placement?.y! > rowY) {
this.pushChildDown(rowChild, pushDownAmount);
}
}
@ -105,10 +108,10 @@ export class SceneGridLayout extends SceneObjectBase<SceneGridLayoutState> {
height: item.h,
};
if (!isItemSizeEqual(child.state.size!, nextSize)) {
if (!isItemSizeEqual(child.state.placement!, nextSize)) {
child.setState({
size: {
...child.state.size,
placement: {
...child.state.placement,
...nextSize,
},
});
@ -142,8 +145,8 @@ export class SceneGridLayout extends SceneObjectBase<SceneGridLayoutState> {
public onResizeStop: ReactGridLayout.ItemCallback = (_, o, n) => {
const child = this.getSceneLayoutChild(n.i);
child.setState({
size: {
...child.state.size,
placement: {
...child.state.placement,
width: n.w,
height: n.h,
},
@ -152,9 +155,9 @@ export class SceneGridLayout extends SceneObjectBase<SceneGridLayoutState> {
private pushChildDown(child: SceneLayoutChild, amount: number) {
child.setState({
size: {
...child.state.size,
y: child.state.size?.y! + amount,
placement: {
...child.state.placement,
y: child.state.placement?.y! + amount,
},
});
}
@ -228,12 +231,12 @@ export class SceneGridLayout extends SceneObjectBase<SceneGridLayoutState> {
for (let i = 0; i < gridLayout.length; i++) {
const gridItem = gridLayout[i];
const child = this.getSceneLayoutChild(gridItem.i)!;
const childSize = child.state.size!;
const childSize = child.state.placement!;
if (childSize?.x !== gridItem.x || childSize?.y !== gridItem.y) {
child.setState({
size: {
...child.state.size,
placement: {
...child.state.placement,
x: gridItem.x,
y: gridItem.y,
},
@ -255,15 +258,15 @@ export class SceneGridLayout extends SceneObjectBase<SceneGridLayoutState> {
};
private toGridCell(child: SceneLayoutChild): ReactGridLayout.Layout {
const size = child.state.size!;
const size = child.state.placement!;
let x = size.x ?? 0;
let y = size.y ?? 0;
const w = Number.isInteger(Number(size.width)) ? Number(size.width) : DEFAULT_PANEL_SPAN;
const h = Number.isInteger(Number(size.height)) ? Number(size.height) : DEFAULT_PANEL_SPAN;
let isDraggable = Boolean(child.state.isDraggable);
let isResizable = Boolean(child.state.isResizable);
let isDraggable = Boolean(child.state.placement?.isDraggable);
let isResizable = Boolean(child.state.placement?.isResizable);
if (child instanceof SceneGridRow) {
isDraggable = child.state.isCollapsed ? true : false;
@ -363,24 +366,24 @@ function validateChildrenSize(children: SceneLayoutChild[]) {
if (
children.find(
(c) =>
!c.state.size ||
c.state.size.height === undefined ||
c.state.size.width === undefined ||
c.state.size.x === undefined ||
c.state.size.y === undefined
!c.state.placement ||
c.state.placement.height === undefined ||
c.state.placement.width === undefined ||
c.state.placement.x === undefined ||
c.state.placement.y === undefined
)
) {
throw new Error('All children must have a size specified');
}
}
function isItemSizeEqual(a: SceneObjectSize, b: SceneObjectSize) {
function isItemSizeEqual(a: SceneLayoutChildOptions, b: SceneLayoutChildOptions) {
return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
}
function sortChildrenByPosition(children: SceneLayoutChild[]) {
return [...children].sort((a, b) => {
return a.state.size?.y! - b.state.size?.y! || a.state.size?.x! - b.state.size?.x!;
return a.state.placement?.y! - b.state.placement?.y! || a.state.placement?.x! - b.state.placement?.x!;
});
}

View File

@ -27,12 +27,12 @@ export class SceneGridRow extends SceneObjectBase<SceneGridRowState> {
public constructor(state: SceneGridRowState) {
super({
isResizable: false,
isDraggable: true,
isCollapsible: true,
...state,
size: {
...state.size,
placement: {
isResizable: false,
isDraggable: true,
...state.placement,
x: 0,
height: 1,
width: GRID_COLUMN_COUNT,
@ -68,7 +68,7 @@ export class SceneGridRow extends SceneObjectBase<SceneGridRowState> {
export function SceneGridRowRenderer({ model }: SceneComponentProps<SceneGridRow>) {
const styles = useStyles2(getSceneGridRowStyles);
const { isCollapsible, isCollapsed, isDraggable, title } = model.useState();
const { isCollapsible, isCollapsed, title, placement } = model.useState();
const layout = sceneGraph.getLayout(model);
const dragHandle = <SceneDragHandle layoutKey={layout.state.key!} />;
@ -79,7 +79,7 @@ export function SceneGridRowRenderer({ model }: SceneComponentProps<SceneGridRow
{isCollapsible && <Icon name={isCollapsed ? 'angle-right' : 'angle-down'} />}
<span className={styles.rowTitle}>{title}</span>
</div>
{isDraggable && isCollapsed && <div>{dragHandle}</div>}
{placement?.isDraggable && isCollapsed && <div>{dragHandle}</div>}
</div>
</div>
);

View File

@ -13,22 +13,13 @@ export interface SceneObjectStatePlain {
$variables?: SceneVariables;
}
export interface SceneLayoutChildSize {
size?: SceneObjectSize;
export interface SceneLayoutChildState extends SceneObjectStatePlain {
placement?: SceneLayoutChildOptions;
}
export interface SceneLayoutChildInteractions {
isDraggable?: boolean;
isResizable?: boolean;
}
export interface SceneLayoutChildState
extends SceneObjectStatePlain,
SceneLayoutChildSize,
SceneLayoutChildInteractions {}
export type SceneObjectState = SceneObjectStatePlain | SceneLayoutState | SceneLayoutChildState;
export interface SceneObjectSize {
export interface SceneLayoutChildOptions {
width?: number | string;
height?: number | string;
xSizing?: 'fill' | 'content';
@ -37,6 +28,8 @@ export interface SceneObjectSize {
y?: number;
minWidth?: number | string;
minHeight?: number | string;
isDraggable?: boolean;
isResizable?: boolean;
}
export interface SceneComponentProps<T> {

View File

@ -13,7 +13,7 @@ import { UrlSyncManager } from '../services/UrlSyncManager';
interface DashboardSceneState extends SceneObjectStatePlain {
title: string;
uid: string;
layout: SceneLayout;
body: SceneLayout;
actions?: SceneObject[];
}
@ -43,7 +43,7 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
}
function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardScene>) {
const { title, layout, actions = [], uid } = model.useState();
const { title, body, actions = [], uid } = model.useState();
const toolbarActions = (actions ?? []).map((action) => <action.Component key={action.state.key} model={action} />);
@ -59,7 +59,7 @@ function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardScene>)
return (
<Page navId="scenes" pageNav={{ text: title }} layout={PageLayoutType.Canvas} toolbar={pageToolbar}>
<div style={{ flexGrow: 1, display: 'flex', gap: '8px', overflow: 'auto' }}>
<layout.Component model={layout} />
<body.Component model={body} />
</div>
</Page>
);

View File

@ -48,7 +48,7 @@ export class DashboardLoader extends StateManagerBase<DashboardLoaderState> {
const dashboard = new DashboardScene({
title: oldModel.title,
uid: oldModel.uid,
layout: new SceneGridLayout({
body: new SceneGridLayout({
children: this.buildSceneObjectsFromDashboard(oldModel),
}),
$timeRange: new SceneTimeRange(),
@ -81,7 +81,7 @@ export class DashboardLoader extends StateManagerBase<DashboardLoaderState> {
new SceneGridRow({
title: panel.title,
isCollapsed: true,
size: {
placement: {
y: panel.gridPos.y,
},
children: panel.panels ? panel.panels.map(createVizPanelFromPanelModel) : [],
@ -98,7 +98,7 @@ export class DashboardLoader extends StateManagerBase<DashboardLoaderState> {
panels.push(
new SceneGridRow({
title: currentRow!.title,
size: {
placement: {
y: currentRow.gridPos.y,
},
children: currentRowPanels,
@ -126,7 +126,7 @@ export class DashboardLoader extends StateManagerBase<DashboardLoaderState> {
panels.push(
new SceneGridRow({
title: currentRow!.title,
size: {
placement: {
y: currentRow.gridPos.y,
},
children: currentRowPanels,
@ -142,7 +142,7 @@ function createVizPanelFromPanelModel(panel: PanelModel) {
return new VizPanel({
title: panel.title,
pluginId: panel.type,
size: {
placement: {
x: panel.gridPos.x,
y: panel.gridPos.y,
width: panel.gridPos.w,

View File

@ -17,11 +17,11 @@ import { getQueryRunnerWithRandomWalkQuery } from './queries';
export function getFlexLayoutTest(standalone: boolean): Scene {
const state = {
title: 'Flex layout test',
layout: new SceneFlexLayout({
body: new SceneFlexLayout({
direction: 'row',
children: [
panelBuilders.newGraph({
size: { minWidth: '70%' },
placement: { minWidth: '70%' },
title: 'Dynamic height and width',
$data: getQueryRunnerWithRandomWalkQuery({}, { maxDataPointsFromWidth: true }),
}),
@ -44,14 +44,14 @@ export function getFlexLayoutTest(standalone: boolean): Scene {
title: 'Fill height',
}),
new SceneCanvasText({
size: { ySizing: 'content' },
placement: { ySizing: 'content' },
text: 'Size to content',
fontSize: 20,
align: 'center',
}),
panelBuilders.newGraph({
title: 'Fixed height',
size: { height: 300 },
placement: { height: 300 },
}),
],
}),
@ -75,13 +75,13 @@ export function getScenePanelRepeaterTest(standalone: boolean): Scene {
const state = {
title: 'Panel repeater test',
layout: new ScenePanelRepeater({
body: new ScenePanelRepeater({
layout: new SceneFlexLayout({
direction: 'column',
children: [
new SceneFlexLayout({
direction: 'row',
size: { minHeight: 200 },
placement: { minHeight: 200 },
children: [
new VizPanel({
pluginId: 'timeseries',
@ -91,7 +91,7 @@ export function getScenePanelRepeaterTest(standalone: boolean): Scene {
},
}),
new VizPanel({
size: { width: 300 },
placement: { width: 300 },
pluginId: 'stat',
fieldConfig: { defaults: { displayName: 'Last' }, overrides: [] },
options: {

View File

@ -11,42 +11,38 @@ import { getQueryRunnerWithRandomWalkQuery } from './queries';
export function getGridLayoutTest(standalone: boolean): Scene {
const state = {
title: 'Grid layout test',
layout: new SceneGridLayout({
body: new SceneGridLayout({
children: [
new VizPanel({
isResizable: true,
isDraggable: true,
pluginId: 'timeseries',
title: 'Draggable and resizable',
size: {
placement: {
x: 0,
y: 0,
width: 12,
height: 10,
isResizable: true,
isDraggable: true,
},
}),
new VizPanel({
pluginId: 'timeseries',
title: 'No drag and no resize',
isResizable: false,
isDraggable: false,
size: { x: 12, y: 0, width: 12, height: 10 },
placement: { x: 12, y: 0, width: 12, height: 10, isResizable: false, isDraggable: false },
}),
new SceneFlexLayout({
direction: 'column',
isDraggable: true,
isResizable: true,
size: { x: 6, y: 11, width: 12, height: 10 },
placement: { x: 6, y: 11, width: 12, height: 10, isDraggable: true, isResizable: true },
children: [
new VizPanel({
size: { ySizing: 'fill' },
placement: { ySizing: 'fill' },
pluginId: 'timeseries',
title: 'Child of flex layout',
}),
new VizPanel({
size: { ySizing: 'fill' },
placement: { ySizing: 'fill' },
pluginId: 'timeseries',
title: 'Child of flex layout',
}),

View File

@ -16,7 +16,7 @@ export function getGridWithMultipleTimeRanges(standalone: boolean): Scene {
const state = {
title: 'Grid with rows and different queries and time ranges',
layout: new SceneGridLayout({
body: new SceneGridLayout({
children: [
new SceneGridRow({
$timeRange: row1TimeRange,
@ -24,39 +24,35 @@ export function getGridWithMultipleTimeRanges(standalone: boolean): Scene {
title: 'Row A - has its own query, last year time range',
key: 'Row A',
isCollapsed: true,
size: { y: 0 },
placement: { y: 0 },
children: [
new VizPanel({
pluginId: 'timeseries',
title: 'Row A Child1',
key: 'Row A Child1',
isResizable: true,
isDraggable: true,
size: { x: 0, y: 1, width: 12, height: 5 },
placement: { x: 0, y: 1, width: 12, height: 5, isResizable: true, isDraggable: true },
}),
new VizPanel({
pluginId: 'timeseries',
title: 'Row A Child2',
key: 'Row A Child2',
isResizable: true,
isDraggable: true,
size: { x: 0, y: 5, width: 6, height: 5 },
placement: { x: 0, y: 5, width: 6, height: 5, isResizable: true, isDraggable: true },
}),
],
}),
new VizPanel({
$data: getQueryRunnerWithRandomWalkQuery(),
isResizable: true,
isDraggable: true,
pluginId: 'timeseries',
title: 'Outsider, has its own query',
key: 'Outsider-own-query',
size: {
placement: {
x: 0,
y: 12,
width: 6,
height: 10,
isResizable: true,
isDraggable: true,
},
}),
],

View File

@ -11,42 +11,38 @@ import { getQueryRunnerWithRandomWalkQuery } from './queries';
export function getMultipleGridLayoutTest(standalone: boolean): Scene {
const state = {
title: 'Multiple grid layouts test',
layout: new SceneFlexLayout({
body: new SceneFlexLayout({
children: [
new SceneGridLayout({
children: [
new VizPanel({
size: {
placement: {
x: 0,
y: 0,
width: 12,
height: 10,
isDraggable: true,
isResizable: true,
},
isDraggable: true,
isResizable: true,
pluginId: 'timeseries',
title: 'Dragabble and resizable',
}),
new VizPanel({
isResizable: false,
isDraggable: true,
size: { x: 12, y: 0, width: 12, height: 10 },
placement: { x: 12, y: 0, width: 12, height: 10, isResizable: false, isDraggable: true },
pluginId: 'timeseries',
title: 'Draggable only',
}),
new SceneFlexLayout({
isResizable: true,
isDraggable: true,
size: { x: 6, y: 11, width: 12, height: 10 },
placement: { x: 6, y: 11, width: 12, height: 10, isResizable: false, isDraggable: true },
direction: 'column',
children: [
new VizPanel({
size: { ySizing: 'fill' },
placement: { ySizing: 'fill' },
pluginId: 'timeseries',
title: 'Fill height',
}),
new VizPanel({
size: { ySizing: 'fill' },
placement: { ySizing: 'fill' },
pluginId: 'timeseries',
title: 'Fill height',
}),
@ -58,36 +54,32 @@ export function getMultipleGridLayoutTest(standalone: boolean): Scene {
new SceneGridLayout({
children: [
new VizPanel({
size: {
placement: {
x: 0,
y: 0,
width: 12,
height: 10,
isDraggable: true,
},
isDraggable: true,
pluginId: 'timeseries',
title: 'Fill height',
}),
new VizPanel({
isResizable: false,
isDraggable: true,
size: { x: 12, y: 0, width: 12, height: 10 },
placement: { x: 12, y: 0, width: 12, height: 10, isResizable: false, isDraggable: true },
pluginId: 'timeseries',
title: 'Fill height',
}),
new SceneFlexLayout({
size: { x: 6, y: 11, width: 12, height: 10 },
placement: { x: 6, y: 11, width: 12, height: 10 },
direction: 'column',
children: [
new VizPanel({
size: { ySizing: 'fill' },
isDraggable: true,
placement: { ySizing: 'fill', isDraggable: true },
pluginId: 'timeseries',
title: 'Fill height',
}),
new VizPanel({
isDraggable: true,
size: { ySizing: 'fill' },
placement: { ySizing: 'fill', isDraggable: true },
pluginId: 'timeseries',
title: 'Fill height',
}),

View File

@ -10,7 +10,7 @@ import { getQueryRunnerWithRandomWalkQuery } from './queries';
export function getGridWithMultipleData(standalone: boolean): Scene {
const state = {
title: 'Grid with rows and different queries',
layout: new SceneGridLayout({
body: new SceneGridLayout({
children: [
new SceneGridRow({
$timeRange: new SceneTimeRange(),
@ -18,23 +18,19 @@ export function getGridWithMultipleData(standalone: boolean): Scene {
title: 'Row A - has its own query',
key: 'Row A',
isCollapsed: true,
size: { y: 0 },
placement: { y: 0 },
children: [
new VizPanel({
pluginId: 'timeseries',
title: 'Row A Child1',
key: 'Row A Child1',
isResizable: true,
isDraggable: true,
size: { x: 0, y: 1, width: 12, height: 5 },
placement: { x: 0, y: 1, width: 12, height: 5, isResizable: true, isDraggable: true },
}),
new VizPanel({
pluginId: 'timeseries',
title: 'Row A Child2',
key: 'Row A Child2',
isResizable: true,
isDraggable: true,
size: { x: 0, y: 5, width: 6, height: 5 },
placement: { x: 0, y: 5, width: 6, height: 5, isResizable: true, isDraggable: true },
}),
],
}),
@ -42,52 +38,48 @@ export function getGridWithMultipleData(standalone: boolean): Scene {
title: 'Row B - uses global query',
key: 'Row B',
isCollapsed: true,
size: { y: 1 },
placement: { y: 1 },
children: [
new VizPanel({
pluginId: 'timeseries',
title: 'Row B Child1',
key: 'Row B Child1',
isResizable: false,
isDraggable: true,
size: { x: 0, y: 2, width: 12, height: 5 },
placement: { x: 0, y: 2, width: 12, height: 5, isResizable: false, isDraggable: true },
}),
new VizPanel({
$data: getQueryRunnerWithRandomWalkQuery({ seriesCount: 10 }),
pluginId: 'timeseries',
title: 'Row B Child2 with data',
key: 'Row B Child2',
isResizable: false,
isDraggable: true,
size: { x: 0, y: 7, width: 6, height: 5 },
placement: { x: 0, y: 7, width: 6, height: 5, isResizable: false, isDraggable: true },
}),
],
}),
new VizPanel({
$data: getQueryRunnerWithRandomWalkQuery({ seriesCount: 10 }),
isResizable: true,
isDraggable: true,
pluginId: 'timeseries',
title: 'Outsider, has its own query',
key: 'Outsider-own-query',
size: {
placement: {
x: 0,
y: 12,
width: 6,
height: 10,
isResizable: true,
isDraggable: true,
},
}),
new VizPanel({
isResizable: true,
isDraggable: true,
pluginId: 'timeseries',
title: 'Outsider, uses global query',
key: 'Outsider-global-query',
size: {
placement: {
x: 6,
y: 12,
width: 12,
height: 10,
isResizable: true,
isDraggable: true,
},
}),
],

View File

@ -9,29 +9,25 @@ import { getQueryRunnerWithRandomWalkQuery } from './queries';
export function getGridWithRowLayoutTest(standalone: boolean): Scene {
const state = {
title: 'Grid with row layout test',
layout: new SceneGridLayout({
body: new SceneGridLayout({
children: [
new SceneGridRow({
title: 'Row A',
key: 'Row A',
isCollapsed: true,
size: { y: 0 },
placement: { y: 0 },
children: [
new VizPanel({
pluginId: 'timeseries',
title: 'Row A Child1',
key: 'Row A Child1',
isResizable: true,
isDraggable: true,
size: { x: 0, y: 1, width: 12, height: 5 },
placement: { x: 0, y: 1, width: 12, height: 5, isResizable: true, isDraggable: true },
}),
new VizPanel({
pluginId: 'timeseries',
title: 'Row A Child2',
key: 'Row A Child2',
isResizable: true,
isDraggable: true,
size: { x: 0, y: 5, width: 6, height: 5 },
placement: { x: 0, y: 5, width: 6, height: 5, isResizable: true, isDraggable: true },
}),
],
}),
@ -39,37 +35,33 @@ export function getGridWithRowLayoutTest(standalone: boolean): Scene {
title: 'Row B',
key: 'Row B',
isCollapsed: true,
size: { y: 1 },
placement: { y: 1 },
children: [
new VizPanel({
pluginId: 'timeseries',
title: 'Row B Child1',
key: 'Row B Child1',
isResizable: false,
isDraggable: true,
size: { x: 0, y: 2, width: 12, height: 5 },
placement: { x: 0, y: 2, width: 12, height: 5, isResizable: false, isDraggable: true },
}),
new VizPanel({
pluginId: 'timeseries',
title: 'Row B Child2',
key: 'Row B Child2',
isResizable: false,
isDraggable: true,
size: { x: 0, y: 7, width: 6, height: 5 },
placement: { x: 0, y: 7, width: 6, height: 5, isResizable: false, isDraggable: true },
}),
],
}),
new VizPanel({
isResizable: true,
isDraggable: true,
pluginId: 'timeseries',
title: 'Outsider',
key: 'Outsider',
size: {
placement: {
x: 2,
y: 12,
width: 12,
height: 10,
isResizable: true,
isDraggable: true,
},
}),
],

View File

@ -16,7 +16,7 @@ export function getGridWithRowsTest(): Scene {
const row1 = new SceneGridRow({
title: 'Collapsible/draggable row with flex layout',
size: { x: 0, y: 0, height: 10 },
placement: { x: 0, y: 0, height: 10 },
children: [
new SceneFlexLayout({
direction: 'row',
@ -39,7 +39,7 @@ export function getGridWithRowsTest(): Scene {
});
const cell1 = new VizPanel({
size: {
placement: {
x: 0,
y: 10,
width: 12,
@ -50,15 +50,13 @@ export function getGridWithRowsTest(): Scene {
});
const cell2 = new VizPanel({
isResizable: false,
isDraggable: false,
size: { x: 12, y: 20, width: 12, height: 10 },
placement: { x: 12, y: 20, width: 12, height: 10, isResizable: false, isDraggable: false },
pluginId: 'timeseries',
title: 'No resize/no drag',
});
const row2 = new SceneGridRow({
size: { x: 12, y: 10, height: 10, width: 12 },
placement: { x: 12, y: 10, height: 10, width: 12 },
title: 'Row with a nested flex layout',
children: [
new SceneFlexLayout({
@ -77,7 +75,7 @@ export function getGridWithRowsTest(): Scene {
});
const scene = new Scene({
title: 'Grid rows test',
layout: new SceneGridLayout({
body: new SceneGridLayout({
children: [cell1, cell2, row1, row2],
}),
$editor: new SceneEditManager({}),

View File

@ -10,7 +10,7 @@ import { getQueryRunnerWithRandomWalkQuery } from './queries';
export function getNestedScene(standalone: boolean): Scene {
const state = {
title: 'Nested Scene demo',
layout: new SceneFlexLayout({
body: new SceneFlexLayout({
direction: 'column',
children: [
new VizPanel({
@ -34,7 +34,7 @@ export function getInnerScene(title: string) {
title: title,
canRemove: true,
canCollapse: true,
layout: new SceneFlexLayout({
body: new SceneFlexLayout({
direction: 'row',
children: [
new VizPanel({

View File

@ -45,13 +45,13 @@ export function getQueryVariableDemo(standalone: boolean): Scene {
}),
],
}),
layout: new SceneFlexLayout({
body: new SceneFlexLayout({
direction: 'row',
children: [
new SceneFlexLayout({
children: [
new SceneCanvasText({
size: { width: '40%' },
placement: { width: '40%' },
text: 'metric: ${metric}',
fontSize: 20,
align: 'center',

View File

@ -11,14 +11,14 @@ import { getQueryRunnerWithRandomWalkQuery } from './queries';
export function getSceneWithRows(standalone: boolean): Scene {
const state = {
title: 'Scene with rows',
layout: new SceneFlexLayout({
body: new SceneFlexLayout({
direction: 'column',
children: [
new NestedScene({
title: 'Overview',
canCollapse: true,
// size: { ySizing: 'content', xSizing: 'fill' },
layout: new SceneFlexLayout({
body: new SceneFlexLayout({
direction: 'row',
children: [
new VizPanel({
@ -36,7 +36,7 @@ export function getSceneWithRows(standalone: boolean): Scene {
title: 'More server details',
// size: { ySizing: 'content', xSizing: 'fill' },
canCollapse: true,
layout: new SceneFlexLayout({
body: new SceneFlexLayout({
direction: 'row',
children: [
new VizPanel({

View File

@ -9,7 +9,7 @@ import { getQueryRunnerWithRandomWalkQuery } from './queries';
export function getTransformationsDemo(standalone: boolean): Scene {
const state = {
title: 'Transformations demo',
layout: new SceneFlexLayout({
body: new SceneFlexLayout({
direction: 'row',
children: [
new SceneFlexLayout({

View File

@ -54,7 +54,7 @@ export function getVariablesDemo(standalone: boolean): Scene {
}),
],
}),
layout: new SceneFlexLayout({
body: new SceneFlexLayout({
direction: 'row',
children: [
new SceneFlexLayout({
@ -67,7 +67,7 @@ export function getVariablesDemo(standalone: boolean): Scene {
}),
}),
new SceneCanvasText({
size: { width: '40%' },
placement: { width: '40%' },
text: 'server: ${server} pod:${pod}',
fontSize: 20,
align: 'center',
@ -125,7 +125,7 @@ export function getVariablesDemoWithAll(): Scene {
}),
],
}),
layout: new SceneFlexLayout({
body: new SceneFlexLayout({
direction: 'row',
children: [
new SceneFlexLayout({
@ -138,7 +138,7 @@ export function getVariablesDemoWithAll(): Scene {
}),
}),
new SceneCanvasText({
size: { width: '40%' },
placement: { width: '40%' },
text: 'server: ${server} pod:${pod}',
fontSize: 20,
align: 'center',