import { Libs } from "../../utils/libs"; import { OptionHandle } from "../option-handle"; import type { EffectorInterface, DimensionObject, } from "../../types/services/effector.type"; import { ModelManager } from "../../core/model-manager"; import { EmptyState } from "./empty-state"; import { LoadingState } from "./loading-state"; import { MixedAdapter } from "../../adapter/mixed-adapter"; import type { RecyclerViewContract } from "../../types/core/base/recyclerview.type"; import { ResizeObserverService } from "../../services/resize-observer"; import { ElementMetrics } from "../../types/services/resize-observer.type"; import { MixedItem, VisibilityStats, } from "../../types/core/base/mixed-adapter.type"; import { SelectiveOptions } from "../../types/utils/selective.type"; import { ParentBinderMapLike, PopupLocaltion, PopupPosition, VirtualRecyclerOptions, } from "../../types/components/popup.type"; import { Lifecycle } from "../../core/base/lifecycle"; import { LifecycleState } from "../../types/core/base/lifecycle.type"; import { MountViewResult } from "src/ts/types/utils/libs.type"; /** * Popup panel that renders and manages the dropdown surface. * * Responsibilities: * - Build and attach the dropdown DOM structure * - Integrate state components (OptionHandle, LoadingState, EmptyState) * - Bind to ModelManager resources (adapter + recycler view) * - Handle virtual scrolling and infinite scroll * - Compute placement (top/bottom) and animate open/close/resize via Effector * - Keep "empty/not found" states in sync with adapter visibility stats * * Lifecycle: * - Created via constructor → `initialize()` (when args provided) → `init()` → `mount()` * - `open()` attaches and animates the panel * - `close()` collapses the panel * - `destroy()` fully tears down all resources * * @extends Lifecycle */ export class Popup extends Lifecycle { /** ModelManager reference used to provide adapter and recycler view resources */ private modelManager?: ModelManager< MixedItem, MixedAdapter >; /** Active configuration for the popup behavior and text labels */ private options?: SelectiveOptions; /** Indicates whether the popup DOM has been attached to the document body at least once */ private isCreated = false; /** Mixed adapter handling items/models and visibility stats */ public optionAdapter?: MixedAdapter; /** Root popup container (the floating panel) */ public node?: HTMLDivElement; /** Effector service used to measure/animate the popup */ private effSvc?: EffectorInterface; /** Resize observer to react to parent panel size changes */ private resizeObser?: ResizeObserverService; /** Binder map for parent elements (anchors to compute placement from) */ private parent?: ParentBinderMapLike; /** Header control exposing "Select All / Deselect All" actions */ public optionHandle?: OptionHandle; /** "Empty / Not found" feedback component */ public emptyState?: EmptyState; /** Loading indicator component */ public loadingState?: LoadingState; /** Virtualized recycler view for performant lists */ public recyclerView?: RecyclerViewContract; /** Container that holds the list of options */ private optionsContainer?: HTMLDivElement; /** Scroll handler used by infinite scroll */ private scrollListener?: () => Promise; /** Handle to defer hiding the loading indicator */ private hideLoadHandle?: ReturnType; /** Default virtual scroll configuration (tuned for typical option heights) */ private virtualScrollConfig = { /** Estimated item height in pixels (improves initial layout calculation) */ estimateItemHeight: 36, /** Number of extra items to render above/below the viewport */ overscan: 8, /** Whether the list contains items with dynamic (non-uniform) heights */ dynamicHeights: true, }; /** * Creates a Popup instance that manages the dropdown panel for a Select-like UI. * * If `select` and `options` are provided, the popup is initialized immediately. * * @param select - Source `