import { type Pose } from "./project.ts"; import type { GroupInfo, MarkerInput } from "./types.ts"; /** The view as an element tells it: the Pose, the size of the box it is drawn in, and the CSS transform its picture carries between redraws. */ /** * The pose of an element's last drawing, with the size of its box and the CSS transform its picture carries between redraws. * * @example * ```ts * import type { OverlayPose } from "@johnmorrisdotca/chikyu-earth/markers"; * * const where = (pose: OverlayPose): string => `${pose.lon}, ${pose.lat} at ${pose.width} x ${pose.height}`; * console.log(typeof where); * ``` */ export interface OverlayPose extends Pose { width: number; height: number; transform: string; } /** What an element gives the markers: a box laid over its view, the pose of its last draw, and a way to hear the next ones. */ /** * What an element gives the markers (`element.overlay`): a box laid over its view, the pose of its last draw, and a way to hear the next ones. * * @example * ```ts * import type { OverlayHost } from "@johnmorrisdotca/chikyu-earth/markers"; * * const follow = (host: OverlayHost): (() => void) => host.subscribe((pose) => console.log(pose.radius)); * console.log(typeof follow); * ``` */ export interface OverlayHost { readonly overlayRoot: HTMLElement; readonly pose: OverlayPose | null; subscribe(listener: (pose: OverlayPose) => void): () => void; /** * Tells the element the boxes an overlay's marks and labels take now (an empty list when it is taken away), so the element's own names keep clear of them. `names` is the part of `boxes` that is the labels' (the marks' own boxes are the rest): the cities' names, which are * set at a mark's own place, keep clear of the labels and not of the marks. */ claim?(owner: object, boxes: readonly { left: number; top: number; width: number; height: number; }[], names?: readonly { left: number; top: number; width: number; height: number; }[]): void; /** The boxes the element's own places take now (names, rings, marks): the markers' labels are put clear of them, as the places come first. */ taken?(): readonly { left: number; top: number; width: number; height: number; }[]; } /** * What {@link attachMarkers} may be asked: the most labels, how close markers gather, and the default colour. * * @example * ```ts * import { attachMarkers, type MarkerOptions, type OverlayElement } from "@johnmorrisdotca/chikyu-earth/markers"; * * const options: MarkerOptions = { maxLabels: 12, clusterRadius: 20, color: "#ffcc00" }; * const mark = (element: OverlayElement) => attachMarkers(element, options); * console.log(typeof mark); * ``` */ export interface MarkerOptions { /** Most labels shown at once (24). */ maxLabels?: number; /** Gather markers closer than this many pixels into a cluster (28); 0 turns clustering off. */ clusterRadius?: number; /** The colour of a marker with none of its own. */ color?: string; } /** * The markers on one globe: add, update, remove and find them, show or hide groups, and detach. * * @example * ```ts no-run * import { attachMarkers, type Markers, type OverlayElement } from "@johnmorrisdotca/chikyu-earth/markers"; * * declare const earth: OverlayElement; * const markers: Markers = attachMarkers(earth); * markers.add({ id: "tokyo", lon: 139.69, lat: 35.69, label: "Tokyo", group: "Cities" }); * markers.toggleGroup("Cities"); * console.log(markers.size, markers.groups()); * ``` */ export interface Markers { add(marker: MarkerInput): void; addAll(markers: readonly MarkerInput[]): void; update(id: string, patch: Partial>): void; remove(id: string): void; clear(): void; get(id: string): MarkerInput | undefined; list(): MarkerInput[]; readonly size: number; groups(): GroupInfo[]; showGroup(name: string): void; hideGroup(name: string): void; /** Show it if hidden and hide it if shown (or set it with `force`). Returns whether it is shown. */ toggleGroup(name: string, force?: boolean): boolean; /** Markers and clusters drawn at the last render (for a page or a test). */ readonly stats: { markers: number; clusters: number; labels: number; renders: number; }; detach(): void; } /** * An element markers can be put on: any HTML element with an `overlay` (both `` and `` have one). * * @example * ```ts * import { attachMarkers, type OverlayElement } from "@johnmorrisdotca/chikyu-earth/markers"; * * const mark = (element: OverlayElement) => attachMarkers(element); * console.log(typeof mark); * ``` */ export type OverlayElement = HTMLElement & { overlay: OverlayHost; }; /** * Put markers on a globe, for either look: dots drawn in one pass on a canvas, a few labels as real buttons, crowded places gathered into clusters. It draws only when the view or the markers change, reads no layout to place a label, and shows no more labels than `maxLabels`. Hovering, clicking or focusing a marker fires `chikyu-marker-hover`, `chikyu-marker-click` and `chikyu-marker-focus` on the element, with your own `data` in the detail. * * @param element - `` or ``. * @param options - The most labels, the cluster radius and the default colour; each has a default. * @returns The markers: `add`, `addAll`, `update`, `remove`, `clear`, `groups`, `toggleGroup`, `stats` and `detach`. * * @example * ```ts no-run * import "@johnmorrisdotca/chikyu-earth/element"; * import { attachMarkers, type MarkerEventDetail } from "@johnmorrisdotca/chikyu-earth/markers"; * * const earth = document.querySelector("chikyu-earth")!; * const markers = attachMarkers(earth); * markers.add({ id: "tokyo", lon: 139.69, lat: 35.69, label: "Tokyo", group: "Cities" }); * earth.addEventListener("chikyu-marker-click", (event) => { * const { marker } = (event as CustomEvent).detail; * console.log("chose", marker?.label); * }); * ``` */ export declare function attachMarkers(element: OverlayElement, options?: MarkerOptions): Markers;