import { Instance } from "./../ui/Instance"; import { StyledContainerConfig, ContainerBase } from "../ui/Container"; import { RenderingContext } from "../ui/RenderingContext"; import { Prop } from "../ui/Prop"; import { IRect, Rect } from "./util/Rect"; /** Typed context interface for SVG/layout-related context properties */ export interface SvgRenderingContext extends RenderingContext { parentRect?: Rect; inSvg?: boolean; addClipRect?: (rect: Rect) => string; } export interface BoundedObjectConfig extends StyledContainerConfig { /** * Anchor defines how child bounds are tied to the parent. Zero aligns with the top/left edge. * One aligns with the bottom/right edge. See Svg for more information. */ anchors?: Prop; /** Move boundaries specified by the offset. See Svg for more information. */ offset?: Prop; /** Apply margin to the given boundaries. See Svg for more information. */ margin?: Prop; /** Padding to be applied to the boundaries rectangle before passing to the children. */ padding?: Prop; } export interface BoundedObjectInstance extends Instance { parentRect?: any; } export class BoundedObject< Config extends BoundedObjectConfig = BoundedObjectConfig, InstanceType extends BoundedObjectInstance = BoundedObjectInstance, > extends ContainerBase { declare anchors: any; declare margin: any; declare offset: any; declare padding: any; declareData(...args: any[]) { return super.declareData( { anchors: undefined, offset: undefined, margin: undefined, padding: undefined, }, ...args, ); } prepareData(context: RenderingContext, instance: InstanceType) { super.prepareData(context, instance); var { data } = instance; data.anchors = Rect.convert(data.anchors); data.offset = Rect.convert(data.offset); data.margin = Rect.convertMargin(data.margin); data.padding = Rect.convertMargin(data.padding); } calculateBounds(context: SvgRenderingContext, instance: InstanceType) { var { data } = instance; return Rect.add(Rect.add(Rect.multiply(instance.parentRect, data.anchors), data.offset), data.margin); } prepareBounds(context: SvgRenderingContext, instance: InstanceType) { var { data } = instance; if ( instance.shouldUpdate || !instance.cached.parentRect || !instance.cached.parentRect.isEqual(context.parentRect) || !data.bounds ) { if (!context.parentRect) throw new Error("Parent bounds were not provided through the context."); instance.parentRect = context.parentRect; instance.cache("parentRect", context.parentRect); instance.markShouldUpdate(context); data.bounds = this.calculateBounds(context, instance); data.childrenBounds = Rect.add(data.bounds, data.padding); } } prepare(context: SvgRenderingContext, instance: InstanceType) { this.prepareBounds(context, instance); context.push("parentRect", instance.data.childrenBounds); } prepareCleanup(context: SvgRenderingContext, instance: InstanceType) { context.pop("parentRect"); } } BoundedObject.prototype.anchors = 0; BoundedObject.prototype.margin = 0; BoundedObject.prototype.offset = 0; BoundedObject.prototype.padding = 0; BoundedObject.prototype.styled = true;