123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- import { cloneDeep } from 'lodash';
- import React from 'react';
- import { canvasElementRegistry, CanvasFrameOptions } from 'app/features/canvas';
- import { notFoundItem } from 'app/features/canvas/elements/notFound';
- import { DimensionContext } from 'app/features/dimensions';
- import { LayerActionID } from 'app/plugins/panel/canvas/types';
- import { CanvasElementItem } from '../element';
- import { HorizontalConstraint, Placement, VerticalConstraint } from '../types';
- import { ElementState } from './element';
- import { RootElement } from './root';
- import { Scene } from './scene';
- export const frameItemDummy: CanvasElementItem = {
- id: 'frame',
- name: 'Frame',
- description: 'Frame',
- getNewOptions: () => ({
- config: {},
- }),
- // eslint-disable-next-line react/display-name
- display: () => {
- return <div>FRAME!</div>;
- },
- };
- export class FrameState extends ElementState {
- elements: ElementState[] = [];
- scene: Scene;
- constructor(public options: CanvasFrameOptions, scene: Scene, public parent?: FrameState) {
- super(frameItemDummy, options, parent);
- this.scene = scene;
- // mutate options object
- let { elements } = this.options;
- if (!elements) {
- this.options.elements = elements = [];
- }
- for (const c of elements) {
- if (c.type === 'frame') {
- this.elements.push(new FrameState(c as CanvasFrameOptions, scene, this));
- } else {
- const item = canvasElementRegistry.getIfExists(c.type) ?? notFoundItem;
- this.elements.push(new ElementState(item, c, this));
- }
- }
- }
- isRoot(): this is RootElement {
- return false;
- }
- updateData(ctx: DimensionContext) {
- super.updateData(ctx);
- for (const elem of this.elements) {
- elem.updateData(ctx);
- }
- }
- // used in the layer editor
- reorder(startIndex: number, endIndex: number) {
- const result = Array.from(this.elements);
- const [removed] = result.splice(startIndex, 1);
- result.splice(endIndex, 0, removed);
- this.elements = result;
- this.reinitializeMoveable();
- }
- doMove(child: ElementState, action: LayerActionID) {
- const vals = this.elements.filter((v) => v !== child);
- if (action === LayerActionID.MoveBottom) {
- vals.unshift(child);
- } else {
- vals.push(child);
- }
- this.elements = vals;
- this.scene.save();
- this.reinitializeMoveable();
- }
- reinitializeMoveable() {
- // Need to first clear current selection and then re-init moveable with slight delay
- this.scene.clearCurrentSelection();
- setTimeout(() => this.scene.initMoveable(true, this.scene.isEditingEnabled));
- }
- // ??? or should this be on the element directly?
- // are actions scoped to layers?
- doAction = (action: LayerActionID, element: ElementState, updateName = true, shiftItemsOnDuplicate = true) => {
- switch (action) {
- case LayerActionID.Delete:
- this.elements = this.elements.filter((e) => e !== element);
- this.scene.byName.delete(element.options.name);
- this.scene.save();
- this.reinitializeMoveable();
- break;
- case LayerActionID.Duplicate:
- if (element.item.id === 'frame') {
- console.log('Can not duplicate frames (yet)', action, element);
- return;
- }
- const opts = cloneDeep(element.options);
- if (shiftItemsOnDuplicate) {
- const { constraint, placement: oldPlacement } = element.options;
- const { vertical, horizontal } = constraint ?? {};
- const placement = oldPlacement ?? ({} as Placement);
- switch (vertical) {
- case VerticalConstraint.Top:
- case VerticalConstraint.TopBottom:
- if (placement.top == null) {
- placement.top = 25;
- } else {
- placement.top += 10;
- }
- break;
- case VerticalConstraint.Bottom:
- if (placement.bottom == null) {
- placement.bottom = 100;
- } else {
- placement.bottom -= 10;
- }
- break;
- }
- switch (horizontal) {
- case HorizontalConstraint.Left:
- case HorizontalConstraint.LeftRight:
- if (placement.left == null) {
- placement.left = 50;
- } else {
- placement.left += 10;
- }
- break;
- case HorizontalConstraint.Right:
- if (placement.right == null) {
- placement.right = 50;
- } else {
- placement.right -= 10;
- }
- break;
- }
- opts.placement = placement;
- }
- const copy = new ElementState(element.item, opts, this);
- copy.updateData(this.scene.context);
- if (updateName) {
- copy.options.name = this.scene.getNextElementName();
- }
- this.elements.push(copy);
- this.scene.byName.set(copy.options.name, copy);
- this.scene.save();
- this.reinitializeMoveable();
- break;
- case LayerActionID.MoveTop:
- case LayerActionID.MoveBottom:
- element.parent?.doMove(element, action);
- break;
- default:
- console.log('DO action', action, element);
- return;
- }
- };
- render() {
- return (
- <div key={this.UID} ref={this.initElement} style={{ overflow: 'hidden' }}>
- {this.elements.map((v) => v.render())}
- </div>
- );
- }
- /** Recursively visit all nodes */
- visit(visitor: (v: ElementState) => void) {
- super.visit(visitor);
- for (const e of this.elements) {
- visitor(e);
- }
- }
- getSaveModel() {
- return {
- ...this.options,
- elements: this.elements.map((v) => v.getSaveModel()),
- };
- }
- }
|