import { nu as NuSpotInfo } from '../view/SpotInfo'; import type { Bubble } from './Bubble'; import * as Direction from './Direction'; import { AnchorBoxBounds, boundsRestriction } from './LayoutBounds'; import type { AnchorBox, AnchorElement, AnchorLayout } from './LayoutTypes'; import { Placement, getPlacement } from './Placement'; /* Layouts for things that overlay over the anchor element/box. These are designed to mirror the `Layout` logic. As an example `Layout.north` will appear horizontally centered above the anchor, whereas `LayoutInset.north` will appear horizontally centered overlapping the top of the anchor. */ const labelPrefix = 'layout-inset'; // returns left edge of anchor - used to display element to the left, left edge against the anchor const westEdgeX = (anchor: AnchorBox): number => anchor.x; // returns middle of anchor minus half the element width - used to horizontally centre element to the anchor const middleX = (anchor: AnchorBox, element: AnchorElement): number => anchor.x + (anchor.width / 2) - (element.width / 2); // returns right edge of anchor minus element width - used to display element to the right, right edge against the anchor const eastEdgeX = (anchor: AnchorBox, element: AnchorElement): number => anchor.x + anchor.width - element.width; // returns top edge - used to display element to the top, top edge against the anchor const northY = (anchor: AnchorBox): number => anchor.y; // returns bottom edge minus element height - used to display element at the bottom, bottom edge against the anchor const southY = (anchor: AnchorBox, element: AnchorElement): number => anchor.y + anchor.height - element.height; // returns centre of anchor minus half the element height - used to vertically centre element to the anchor const centreY = (anchor: AnchorBox, element: AnchorElement): number => anchor.y + (anchor.height / 2) - (element.height / 2); // positions element relative to the bottom right of the anchor const southwest: AnchorLayout = (anchor: AnchorBox, element: AnchorElement, bubbles: Bubble) => NuSpotInfo( eastEdgeX(anchor, element), southY(anchor, element), bubbles.insetSouthwest(), Direction.northwest(), Placement.Southwest, boundsRestriction(anchor, { right: AnchorBoxBounds.RightEdge, bottom: AnchorBoxBounds.BottomEdge }), labelPrefix ); // positions element relative to the bottom left of the anchor const southeast: AnchorLayout = (anchor: AnchorBox, element: AnchorElement, bubbles: Bubble) => NuSpotInfo( westEdgeX(anchor), southY(anchor, element), bubbles.insetSoutheast(), Direction.northeast(), Placement.Southeast, boundsRestriction(anchor, { left: AnchorBoxBounds.LeftEdge, bottom: AnchorBoxBounds.BottomEdge }), labelPrefix ); // positions element relative to the top right of the anchor const northwest: AnchorLayout = (anchor: AnchorBox, element: AnchorElement, bubbles: Bubble) => NuSpotInfo( eastEdgeX(anchor, element), northY(anchor), bubbles.insetNorthwest(), Direction.southwest(), Placement.Northwest, boundsRestriction(anchor, { right: AnchorBoxBounds.RightEdge, top: AnchorBoxBounds.TopEdge }), labelPrefix ); // positions element relative to the top left of the anchor const northeast: AnchorLayout = (anchor: AnchorBox, element: AnchorElement, bubbles: Bubble) => NuSpotInfo( westEdgeX(anchor), northY(anchor), bubbles.insetNortheast(), Direction.southeast(), Placement.Northeast, boundsRestriction(anchor, { left: AnchorBoxBounds.LeftEdge, top: AnchorBoxBounds.TopEdge }), labelPrefix ); // positions element relative to the top of the anchor, horizontally centered const north: AnchorLayout = (anchor: AnchorBox, element: AnchorElement, bubbles: Bubble) => NuSpotInfo( middleX(anchor, element), northY(anchor), bubbles.insetNorth(), Direction.south(), Placement.North, boundsRestriction(anchor, { top: AnchorBoxBounds.TopEdge }), labelPrefix ); // positions element relative to the bottom of the anchor, horizontally centered const south: AnchorLayout = (anchor: AnchorBox, element: AnchorElement, bubbles: Bubble) => NuSpotInfo( middleX(anchor, element), southY(anchor, element), bubbles.insetSouth(), Direction.north(), Placement.South, boundsRestriction(anchor, { bottom: AnchorBoxBounds.BottomEdge }), labelPrefix ); // positions element with the right edge against the anchor, vertically centered const east: AnchorLayout = (anchor: AnchorBox, element: AnchorElement, bubbles: Bubble) => NuSpotInfo( eastEdgeX(anchor, element), centreY(anchor, element), bubbles.insetEast(), Direction.west(), Placement.East, boundsRestriction(anchor, { right: AnchorBoxBounds.RightEdge }), labelPrefix ); // positions element with the left each against the anchor, vertically centered const west: AnchorLayout = (anchor: AnchorBox, element: AnchorElement, bubbles: Bubble) => NuSpotInfo( westEdgeX(anchor), centreY(anchor, element), bubbles.insetWest(), Direction.east(), Placement.West, boundsRestriction(anchor, { left: AnchorBoxBounds.LeftEdge }), labelPrefix ); const all = (): AnchorLayout[] => [ southeast, southwest, northeast, northwest, south, north, east, west ]; const allRtl = (): AnchorLayout[] => [ southwest, southeast, northwest, northeast, south, north, east, west ]; const lookupPreserveLayout = (lastPlacement: Placement) => { switch (lastPlacement) { case Placement.North: return north; case Placement.Northeast: return northeast; case Placement.Northwest: return northwest; case Placement.South: return south; case Placement.Southeast: return southeast; case Placement.Southwest: return southwest; case Placement.East: return east; case Placement.West: return west; } }; const preserve: AnchorLayout = (anchor, element, bubbles, placee, bounds) => { const layout = getPlacement(placee).map(lookupPreserveLayout).getOr(north); return layout(anchor, element, bubbles, placee, bounds); }; const lookupFlippedLayout = (lastPlacement: Placement) => { switch (lastPlacement) { case Placement.North: return south; case Placement.Northeast: return southeast; case Placement.Northwest: return southwest; case Placement.South: return north; case Placement.Southeast: return northeast; case Placement.Southwest: return northwest; case Placement.East: return west; case Placement.West: return east; } }; const flip: AnchorLayout = (anchor, element, bubbles, placee, bounds) => { const layout = getPlacement(placee).map(lookupFlippedLayout).getOr(north); return layout(anchor, element, bubbles, placee, bounds); }; export { southeast, northeast, southwest, northwest, south, north, east, west, all, allRtl, preserve, flip };