๐ŸŽฏ Selective UI โ€” ARCHITECTURE

Version 1.3 | MIT License | CRV-UI Engine

"Render less. Control more."

๐Ÿ“ 1. CRV-UI ENGINE โ€” OVERALL ARCHITECTURE

graph TB subgraph PUBLIC_API A["SelectiveUIGlobal GLOBAL_SEUI singleton"] B[bind] C[find] D[destroy] E[rebind] F[effector] G[registerPlugin] H[unregisterPlugin] A --> B A --> C A --> D A --> E A --> F A --> G A --> H end subgraph CORE_ORCHESTRATOR SEL["Selective Lifecycle FSM"] EAO["ElementAdditionObserver MutationObserver auto-rebind"] BQ["bindedQueries Map selector -> options"] PLG["Plugin registry id -> SelectivePlugin"] SEL --> EAO SEL --> BQ SEL --> PLG end subgraph COMPONENT_LAYER SB["SelectBox UI coordinator"] PH[PlaceHolder] DIR[Directive] SRCH[SearchBox] POP[Popup] ACC[AccessoryBox] EFF[Effector] REF[Refresher] SB --> PH SB --> DIR SB --> SRCH SB --> POP SB --> ACC SB --> EFF SB --> REF end subgraph MODEL_ADAPTER_RENDER MM["ModelManager DOM -> Models"] MA["MixedAdapter Group + Option"] RV["VirtualRecyclerView"] GV[GroupView] OV[OptionView] MM --> MA MA --> RV RV --> GV RV --> OV end subgraph SERVICES SC["SearchController"] SO["SelectObserver"] DO["DatasetObserver"] RSZO["ResizeObserver"] end subgraph UTILITIES LIBS["Libs utilities"] CBS["CallbackScheduler"] IEVT["iEvents"] FW["Fenwick Tree"] end B --> SEL C --> SEL D --> SEL E --> SEL SEL --> SB SB --> MM SB --> SC SB --> SO SB --> DO RV --> RSZO RV --> FW SEL --> LIBS MM --> CBS SEL --> IEVT style A fill:#667eea,stroke:#333,stroke-width:3px,color:#fff style SEL fill:#764ba2,stroke:#333,stroke-width:2px,color:#fff style SB fill:#f093fb,stroke:#333,stroke-width:2px,color:#fff style MM fill:#4facfe,stroke:#333,stroke-width:2px,color:#fff style RV fill:#43e97b,stroke:#333,stroke-width:2px,color:#fff style FW fill:#fa709a,stroke:#333,stroke-width:2px,color:#fff

๐Ÿ“Œ CRV-UI Engine:

โ€ข Contract-Driven: All communication occurs through TypeScript interfaces โ€” views do not depend on concrete implementations

โ€ข Retained DOM: DOM nodes are created once and updated incrementally, without Virtual DOM or diffing

โ€ข Virtualized Rendering: Fenwick Tree + windowing for 10k+ items with O(log n) height updates

โ€ข GLOBAL_SEUI singleton: The new version uses globalThis.GLOBAL_SEUI to prevent duplicate script tag loading ()

๐Ÿ†• v1.3 Changes so with v1.2:

โ€ข Public API currently routed through globalThis.GLOBAL_SEUI singleton instead of module-level SECLASS

โ€ข Added registerPlugin / unregisterPlugin to the Public API

โ€ข Official FSM Lifecycle: NEW โ†’ INITIALIZED โ†’ MOUNTED โ†’ UPDATED โ†’ DESTROYED

โ€ข SelectBox now composes VirtualRecyclerView (virtual scroll) or RecyclerView

โ€ข Fenwick Tree replaces the previous simple scroll calculation

๐Ÿ—๏ธ 2. CLASS DIAGRAM (Core Hierarchy)

classDiagram class Lifecycle { #state: LifecycleState -hooks: Map~HookName, Set~Callback~~ +init() +mount() +update() +destroy() +on(hook, fn) this +off(hook, fn) this +is(state) boolean +getState() LifecycleState #emit(hook, prevState) } class Selective { -EAObserver: ElementAdditionObserver -bindedQueries: Map~string, SelectiveOptions~ -plugins: Map~string, SelectivePlugin~ +bind(query, options) +find(query) SelectiveActionApi +destroy(target) +rebind(query, options) +Observer() +registerPlugin(plugin) +unregisterPlugin(id) +getPlugin(id) -applySelectBox(el, options) boolean -destroyAll() -destroyByQuery(query) -destroyElement(el) -buildGetSetAction(...) -buildFuntionAction(...) } class SelectBox { -element: HTMLSelectElement -selective: Selective +container: ContainerRuntime -modelManager: ModelManager -searchController: SearchController -selectObserver: SelectObserver -datasetObserver: DatasetObserver +mount() +deInit() +destroy() +on(hook, fn) +getAction() SelectBoxAction } class ModelManager { -privModelList: MixedItem[] -privAdapterHandle: TAdapter -privRecyclerViewHandle: RecyclerViewContract -options: SelectiveOptions +setupAdapter(cls) +setupRecyclerView(cls) +createModelResources(data) Models +load(viewEl, adapterOpt, recyclerOpt) +replace(modelData) +updateModel(modelData) +refresh(isUpdate) +triggerChanging(event) Promise +triggerChanged(event) Promise +getResources() +skipEvent(value) } class Adapter { +items: TItem[] +adapterKey: string +isSkipEvent: boolean +recyclerView: any +onViewHolder(item, viewer, pos) +onPropChanging(prop, cb) +onPropChanged(prop, cb) +changeProp(prop) Promise +changingProp(prop) Promise +setItems(items) Promise +syncFromSource(items) Promise +viewHolder(parent, item) TViewer +updateRecyclerView(parent) +updateData(items) } class MixedAdapter { +isMultiple: boolean +options: SelectiveOptions +groups: GroupModel[] +flatOptions: OptionModel[] -currentHighlightIndex: number -selectedItemSingle: OptionModel +getSelectedItems() OptionModel[] +highlightNext() +highlightPrev() +onVisibilityChanged(cb) +checkAll() +uncheckAll() } class RecyclerView { +viewElement: HTMLDivElement +adapter: TAdapter +setAdapter(adapter) +clear() +render() +refresh(isUpdate) +destroy() } class VirtualRecyclerView { -opts: VirtualOptions -PadTop: HTMLDivElement -ItemsHost: HTMLDivElement -PadBottom: HTMLDivElement -fenwick: Fenwick -heightCache: number[] -created: Map~int, HTMLElement~ +configure(opts) +setAdapter(adapter) +refresh(isUpdate) +ensureRendered(index, opt) +scrollToIndex(index) +suspend() +resume() +refreshItem() +dispose() -updateWindowInternal() -mountRange(start, end) -unmountOutside(start, end) } class Fenwick { -bit: number[] -stackNum: number +initialize(n) +reset(n) +add(i, delta) +sum(i) number +rangeSum(l, r) number +buildFrom(arr) +lowerBoundPrefix(target) number } class Model { +targetElement: TTarget +options: TOptions +view: TView +position: number +isInit: boolean +isRemoved: boolean +value: string +updateTarget(el) +destroy() } class Effector { +element: HTMLElement +isAnimating: boolean +setElement(query) +expand(config) +collapse(config) +resize(config) +swipe(config) +cancel() } Lifecycle <|-- Selective Lifecycle <|-- SelectBox Lifecycle <|-- ModelManager Lifecycle <|-- Adapter Lifecycle <|-- RecyclerView Lifecycle <|-- Model Lifecycle <|-- Fenwick Adapter <|-- MixedAdapter RecyclerView <|-- VirtualRecyclerView Selective --> SelectBox : creates SelectBox --> ModelManager : owns ModelManager --> MixedAdapter : instantiates ModelManager --> RecyclerView : or VirtualRecyclerView VirtualRecyclerView --> Fenwick : uses SelectBox --> Effector : uses

๐Ÿ“Œ Actual hierarchy:

โ€ข Lifecycle is the common base class for all components โ€” strict FSM with Setโ€‘based hooks

โ€ข Selective is the main orchestrator: manages bindings, plugins, and the ElementAdditionObserver

โ€ข SelectBox is the root UI component: composes all components and services

โ€ข ModelManager headless: DOM โ†’ GroupModel/OptionModel โ†’ Adapter โ†’ RecyclerView

โ€ข MixedAdapter xแปญ lรฝ heterogeneous list (group + option), navigation, visibility, selection

โ€ข VirtualRecyclerView + Fenwick: virtualization vแป›i dynamic heights, adaptive estimator

๐Ÿ”„ 3. LIFECYCLE FSM (Finite State Machine)

stateDiagram-v2 [*] --> NEW: Constructor NEW --> INITIALIZED: init() note right of INITIALIZED bindedQueries = new Map() plugins = new Map() hooks containers created end note INITIALIZED --> MOUNTED: mount() note right of MOUNTED First successful bind() Active bound elements exist Ready for interactions end note MOUNTED --> UPDATED: update() UPDATED --> UPDATED: update() repeatable note right of UPDATED Triggered by: - bind() when already mounted - destroy() partial teardown - rebind() re-registration - Observer() new element detected end note MOUNTED --> DESTROYED: destroyAll() UPDATED --> DESTROYED: destroyAll() INITIALIZED --> DESTROYED: destroyAll() note right of DESTROYED bindedQueries cleared plugins cleared EAObserver disconnected All hooks cleared end note DESTROYED --> [*]

๐Ÿ“Œ FSM Guards (Idempotency):

โ€ข init(): noโ€‘op if the state is not NEW

โ€ข mount(): only transitions INITIALIZED โ†’ MOUNTED; otherwise a noโ€‘op

โ€ข update(): allowed in MOUNTED and UPDATED states; always emits onUpdate

โ€ข destroy(): idempotent if already DESTROYED; clears all hooks

โ€ข Hook exceptions are caught by handleHookError() โ€” without breaking the lifecycle flow

โš™๏ธ 4. BINDING FLOW (bind โ†’ applySelectBox โ†’ mount)

sequenceDiagram participant User participant API as GLOBAL_SEUI participant SEL as Selective participant SB as SelectBox participant MM as ModelManager participant MA as MixedAdapter participant RV as VirtualRecyclerView participant CBS as CallbackScheduler User->>API: bind(".my-select", options) API->>SEL: bind(query, options) SEL->>SEL: mergeConfig(defaults, options) SEL->>SEL: bindedQueries.set(query, merged) SEL->>SEL: getElements(query) -> HTMLSelectElement[] loop For each select SEL->>SEL: applySelectBox(element, options) SEL->>SEL: generate SEID values SEL->>SEL: buildConfig merge dataset SEL->>SB: new SelectBox SB->>SB: onMount wireMouseUp SEL->>SB: mount SB->>MM: new ModelManager MM->>MM: setupAdapter MixedAdapter MM->>MM: setupRecyclerView VirtualRecyclerView MM->>MM: createModelResources MM->>MA: new MixedAdapter MM->>RV: new VirtualRecyclerView RV->>MA: setAdapter MA->>RV: set recyclerView reference RV->>RV: build scaffold RV->>RV: attachScrollListener and ResizeObserver RV->>RV: refresh initial render SB-->>SEL: getAction ActionAPI SEL->>SEL: save bindMap end SEL->>SEL: mount if first bind SEL->>CBS: run doneToken CBS-->>User: on.load callbacks SEL-->>User: binding complete

๐Ÿ“Œ Binding Flow Details:

โ€ข SEID: unique 8-character ID per instance โ€” used for seui-{SEID}-optionlist, seui-{SEID}-placeholder

โ€ข buildConfig: merges dataset attributes into options (medium priority)

โ€ข onMount hook: wires mouseup on the view to trigger toggle()

โ€ข VirtualRecyclerView: scaffold 3 nodes (PadTop, ItemsHost, PadBottom) + ResizeObserver

โ€ข on.load callbacks: invoked asynchronously via CallbackScheduler after all elements are processed

๐Ÿ“Š 5. DATA FLOW: DOM โ†’ Models โ†’ Adapter โ†’ Views

graph LR subgraph INPUT_SOURCES A["HTML select option / optgroup"] B["setData or reload JSON Array"] C["AJAX Response fetch + dataTransform"] end subgraph MODEL_MANAGER D["createModelResources DOM to GroupModel / OptionModel"] D --> E["updateModel Diff + reconcile in-place"] E --> F["replace Full dataset swap"] end subgraph MODELS G["GroupModel label items visible"] H["OptionModel value text selected visible highlighted"] G --> H end subgraph MIXED_ADAPTER I["flatOptions flattened navigation index"] J["groups group model refs"] K["Visibility aggregation visibleCount totalCount"] L["Selection tracking selectedItem"] end subgraph RECYCLER_RENDERING M["VirtualRecyclerView windowing start-end"] M --> N["Fenwick Tree O(log n) prefix sums"] N --> O["mountRange start end visible window only"] O --> P["GroupView OptionView DOM nodes"] end subgraph SEARCH_CONTROLLER Q["SearchController filter + AJAX"] Q --> R["updateModel visibility flags"] R --> S["adapter.onVisibilityChanged refreshItem"] end A --> D B --> F C --> Q D --> G D --> H G --> I H --> I I --> M Q --> R S --> M style A fill:#e3f2fd,stroke:#1976d2 style B fill:#e3f2fd,stroke:#1976d2 style C fill:#e3f2fd,stroke:#1976d2 style M fill:#43e97b,stroke:#333,stroke-width:2px style N fill:#fa709a,stroke:#333,stroke-width:2px,color:#fff style Q fill:#fff3e0,stroke:#f57c00

๐Ÿ“Œ New Data Flow compared to v1.2:

โ€ข ModelManager replaces direct DOM manipulation โ€” headless, test-friendly

โ€ข updateModel(): The diff strategy preserves model instances (keyed by value::text), avoiding destruction/recreation

โ€ข MixedAdapter.flatOptions[]: flattened index for keyboard navigation independent of group structure

โ€ข onVisibilityChanged: when search filter changes โ†’ VirtualRecyclerView.refreshItem() โ†’ rebuild Fenwick

โ€ข Fenwick Tree: dynamic heights with adaptive estimator (running average of measured items)

๐Ÿš€ 6. VIRTUAL SCROLL + FENWICK TREE

graph TB subgraph INITIALIZATION A[setAdapter called] --> B["Build scaffold nodes PadTop / ItemsHost / PadBottom"] B --> C["Probe index 0 height seed estimateItemHeight"] C --> D["buildFrom arr Fenwick O(n log n)"] D --> E["scheduleUpdateWindow requestAnimationFrame"] end subgraph SCROLL_EVENTS F["scroll event passive listener"] --> G["scheduleUpdateWindow rAF debounced"] H["ResizeObserver ItemsHost mutations"] --> I["measureVisibleAndUpdate rAF debounced"] end subgraph WINDOW_UPDATE G --> J[updateWindowInternal] J --> K["containerTopInScroll relative offset"] K --> L["findFirstVisibleIndex fenwick lowerBoundPrefix"] L --> M["Compute start end overscan estimate"] M --> N{Window changed} N -->|No| O[Skip update] N -->|Yes| P["mountRange start end"] P --> Q["unmountOutside start end"] Q --> R["measureVisibleAndUpdate updateHeightAt log n"] R --> S["Update PadTop Update PadBottom"] S --> T["Anchor correction prevent scroll jump"] end subgraph FENWICK U["add i delta log n update"] V["sum i log n prefix"] W["lowerBoundPrefix log n search"] X["rangeSum l r log n"] Y["buildFrom arr n log n"] end subgraph SUSPEND_RESUME Z["suspend cancel rAF and scroll listener disconnect ResizeObserver"] AA["resume reattach and scheduleUpdateWindow"] end I --> R U -.-> V V -.-> W style A fill:#667eea,stroke:#333,stroke-width:2px,color:#fff style J fill:#764ba2,stroke:#333,stroke-width:2px,color:#fff style P fill:#43e97b,stroke:#333,stroke-width:2px style U fill:#fa709a,stroke:#333,stroke-width:2px,color:#fff style Z fill:#ffd89b,stroke:#333,stroke-width:2px

๐Ÿ“Œ Advanced Virtual Scroll compared to v1.2:

โ€ข Fenwick Tree: simple formula replacement โ€” supports dynamic/variable heights

โ€ข Adaptive estimator: uses running average of measured items instead of fixed estimateItemHeight

โ€ข Anchor correction: maintains scroll position stability when heights change after measurement

โ€ข Suspend/Resume: batch operations can pause virtualization to avoid feedback loops

โ€ข Re-entrancy guard: the updating flag prevents recursive calls to updateWindowInternal

๐ŸŽฏ Fenwick Tree Formulas:

  • offsetTopOf(i) = fenwick.sum(i) โ†’ prefix sum of heights[0..i-1]
  • findFirstVisible(scrollTop) = fenwick.lowerBoundPrefix(scrollTop)
  • overscanPx = opts.overscan ร— getEstimate()
  • adaptiveEstimate = measuredSum / measuredCount (khi measuredCount > 0)
  • totalHeight(count) = fenwick.sum(count)
  • windowHeight(s,e) = fenwick.rangeSum(s+1, e+1)

๐ŸŽจ 7. UI COMPONENT STRUCTURE

graph TD A[.seui-container
SelectBox root wrapper] --> B[.seui-view
PlaceHolder + Directive] A --> C[.seui-panel / Popup
dropdown container] B --> D[.seui-placeholder
selected value display] B --> E[.seui-directive
arrow + caret indicator] B --> F[.seui-accessorybox
multi-select chips] D --> D1[Text / Image
selected display] F --> F1[Chip per selected item] F1 --> F2[Remove button per chip] C --> G[.seui-option-handle
SearchBox + SelectAll] C --> H[.seui-optionlist
RecyclerView host] C --> I[.seui-empty-state
EmptyState component] C --> J[.seui-loading-state
LoadingState component] G --> G1[.seui-search
search input + clear btn] G --> G2[.seui-selectall
Select All / Deselect All] H --> K[VirtualRecyclerView scaffold] K --> K1[seui-virtual-pad-top] K --> K2[seui-virtual-items
ItemsHost] K --> K3[seui-virtual-pad-bottom] K2 --> L[.seui-group
GroupView] K2 --> M[.seui-option
OptionView] L --> L1[Group label] L --> L2[Nested .seui-option items] M --> M1[Checkbox / Radio] M --> M2[Image optional] M --> M3[Label text] M --> M4[highlighted / selected states] style A fill:#667eea,stroke:#333,stroke-width:3px,color:#fff style B fill:#f093fb,stroke:#333,stroke-width:2px style C fill:#4facfe,stroke:#333,stroke-width:2px style K fill:#43e97b,stroke:#333,stroke-width:2px style K2 fill:#fa709a,stroke:#333,stroke-width:2px,color:#fff

๐Ÿ“Œ Component Actual hierarchy:

โ€ข seui-container: SelectBox root โ€” wraps native <select> (hidden)

โ€ข PlaceHolder: Display the selected value, image, or "no selection" text

โ€ข Directive: arrow/caret indicator, not seui-arrow anymore

โ€ข AccessoryBox: chips display for multi-select (separate component)

โ€ข VirtualRecyclerView scaffold: PadTop + ItemsHost + PadBottom โ€” not in v1.2

โ€ข EmptyState / LoadingState: separate into popup sub-components

๐Ÿ” 8. SEARCH CONTROLLER + AJAX FLOW

sequenceDiagram participant User participant SB as SearchBox participant SC as SearchController participant MM as ModelManager participant MA as MixedAdapter participant RV as VirtualRecyclerView participant Server User->>SB: Type in search input SB->>SC: search(query) SC->>SC: Check ajax config alt AJAX configured SC->>MA: changingProp("select") - pre-change SC->>MM: Show LoadingState SC->>Server: fetch(url + params) Server-->>SC: JSON response SC->>SC: dataTransform(response) โ†’ normalized data SC->>MM: replace(normalizedData) MM->>MA: syncFromSource(models) MA->>MA: setItems(items) โ†’ changingProp + changeProp MA->>RV: onPropChanged("items") โ†’ render() RV->>RV: refresh(false) โ†’ full rebuild SC->>MA: changeProp("selected") - post-change else No AJAX โ€” local filter SC->>MM: updateModel with filtered data MM->>MA: updateData(newModels) MA->>MA: Update visibility flags on OptionModels MA->>MA: onVisibilityChanged callbacks MA->>RV: refreshItem() - hard reset window RV->>RV: suspend โ†’ resetState โ†’ rebuildFenwick โ†’ resume end RV-->>User: Updated dropdown display User->>SB: Press Enter / Arrow keys SB->>SC: Emit navigation event SC->>MA: highlightNext() / highlightPrev() MA->>MA: Update currentHighlightIndex MA->>RV: ensureRendered(index, scrollIntoView) RV->>RV: mountIndexOnce + scrollToIndex

๐Ÿ“Œ Search / AJAX detailed:

โ€ข Local filter: update OptionModel.visible โ†’ onVisibilityChanged โ†’ VirtualRecyclerView.refreshItem()

โ€ข refreshItem(): suspend + resetState + rebuildFenwick + resume โ€” hard reset completely

โ€ข AJAX: dataTransform(response) normalize to standard format before inserting into ModelManager

โ€ข Keyboard navigation: MixedAdapter.flatOptions[] flat index โ†’ ensureRendered() ensures item is mounted

โšก 9. EVENT SYSTEM + PLUGIN SYSTEM

graph TB subgraph CALLBACK_SCHEDULER A[CallbackScheduler] A --> B["executeStored Map key -> callback array"] A --> C["timerRunner Map key -> debounce timer"] B --> D["Callback 1 ... Callback N"] C --> E["setTimeout debounce"] end subgraph USER_EVENT_REGISTRATION F["on.load callback"] G["on.beforeShow / on.show"] H["on.beforeChange / on.change"] I["on.beforeClose / on.close"] J["on.search"] end subgraph IEVENTS_FLOW K["iEvents buildEventToken"] K --> L["token.isContinue"] K --> M["callback.stopPropagation"] K --> N["callback.cancel"] L --> O["Stop iteration across elements"] end subgraph ADAPTER_PROPERTY_PIPELINE P["changingProp propName pre-change subscribers"] Q["changeProp propName post-change subscribers"] P --> R["select property before user selection"] Q --> S["selected property after user selection"] Q --> T["selected internal"] Q --> U["items after setItems or sync"] end subgraph LIFECYCLE_HOOKS V["Lifecycle hooks onInit onMount onUpdate onDestroy"] V --> W["Set callback per hook"] W --> X["Deduplicated by reference"] X --> Y["Insertion order execution"] Y --> Z["handleHookError catches exceptions"] end subgraph PLUGIN_HOOKS AA["SelectivePlugin interface"] AA --> AB["onBind context"] AA --> AC["onOpen context"] AA --> AD["onClose context"] AA --> AE["onChange context"] AA --> AF["init destroy onDestroy"] AB --> AG["PluginContext selectBox options adapter recycler viewTags actions"] end F --> A G --> A H --> A I --> A J --> A style A fill:#fa709a,stroke:#333,stroke-width:2px,color:#fff style K fill:#43e97b,stroke:#333,stroke-width:2px style P fill:#667eea,stroke:#333,stroke-width:2px,color:#fff style V fill:#764ba2,stroke:#333,stroke-width:2px,color:#fff style AA fill:#4facfe,stroke:#333,stroke-width:2px

๐Ÿ“Œ Actual Event System:

โ€ข CallbackScheduler: debounce per-key โ€” each callback has its own timer

โ€ข iEvents + EventToken: flow control is like DOM events โ€” stopPropagation stops iteration through elements

โ€ข Adapter Property Pipeline: 2-phase (changingProp / changeProp) for "select", "selected", "items"

โ€ข Plugin System (new in v1.3): SelectivePlugin interface with hooks onBind/onOpen/onClose/onChange

โ€ข PluginContext: passes selectBox, adapter, recycler, viewTags, actions to every plugin hook

๐Ÿงน 10. MEMORY MANAGEMENT & CLEANUP

graph TB subgraph "BINDING PHASE" A[bind called] --> B[applySelectBox
generates SEID] B --> C[Mount DOM wrapper
hide native select] C --> D[Wire events via Lifecycle hooks] D --> E[setBinderMap element โ†’ BinderMap] E --> F[getBindedCommand.push query] end subgraph "OBSERVER AUTO-BIND" G[ElementAdditionObserver
MutationObserver on body] --> H{New select added?} H -->|Matches query| I[applySelectBox
auto-bind new element] H -->|No match| J[Skip] I --> K[Selective.update] end subgraph "PARTIAL DESTROY" L[destroy query] --> M[destroyByQuery] M --> N[For each element
destroyElement] N --> O[Disconnect EAObserver temporarily] O --> P[selectBox.deInit
sub-component teardown] P --> Q[DOM unwrap
restore native select] Q --> R[removeBinderMap] R --> S[Reconnect EAObserver
if bindings remain] S --> T[selectBox.destroy
lifecycle teardown] T --> U[Selective.update] end subgraph "GLOBAL DESTROY" V[destroy null] --> W[destroyAll] W --> X[destroyByQuery ร— all queries] X --> Y[bindedQueries.clear] Y --> Z[plugins forEach โ†’ destroy + onDestroy] Z --> AA[EAObserver.disconnect] AA --> AB[super.destroy โ†’ DESTROYED state] end subgraph "VIRTUAL RECYCLER CLEANUP" AC[VirtualRecyclerView.destroy] --> AD[cancelFrames rAF] AD --> AE[removeScrollListener] AE --> AF[resizeObs.disconnect] AF --> AG[created.forEach remove] AG --> AH[PadTop/ItemsHost/PadBottom.remove] AH --> AI[super.destroy] end style A fill:#43e97b,stroke:#333,stroke-width:2px style L fill:#fa709a,stroke:#333,stroke-width:2px,color:#fff style V fill:#f44336,stroke:#333,stroke-width:2px,color:#fff style G fill:#667eea,stroke:#333,stroke-width:2px,color:#fff style AC fill:#ff9800,stroke:#333,stroke-width:2px

๐Ÿ“Œ Detailed Memory Management:

โ€ข EAObserver disconnect: Pause in destroyElement to avoid re-bind when the DOM changes

โ€ข deInit() vs destroy(): deInit is SelectBox-specific cleanup, destroy is Lifecycle teardown

โ€ข Plugin teardown: calls both plugin.destroy() and plugin.onDestroy()

โ€ข VirtualRecyclerView: cancel rAF, remove scroll listener, disconnect ResizeObserver, remove scaffold nodes

โ€ข BinderMap: removed from WeakMap after destroy โ€” GC can reclaim references

โš™๏ธ 11. CONFIGURATION HIERARCHY

graph TB A[iStorage.defaultConfig
Priority 3 - Lowest] --> D[Libs.mergeConfig] B[element.dataset data-*
Priority 2 - Medium
buildConfig] --> D C[bind options arg
Priority 1 - Highest] --> D D --> E[Final SelectiveOptions] subgraph "SelectiveOptions = DefaultConfig + IDs" F[SEID unique 8-char] G[SEID_LIST seui-SEID-optionlist] H[SEID_HOLDER seui-SEID-placeholder] end E --> F E --> G E --> H subgraph "DefaultConfig Categories" I[Visual
width, height, panelHeight
imageMode, imagePosition] J[Behavior
multiple, searchable, autoclose
disabled, readonly, visible] K[Selection
selectall, maxSelected
keepSelected] L[Text / i18n
placeholder, textLoading
textNotFound, textSelectAll] M[Performance
virtualScroll, animationtime
delaysearchtime, overscan
estimateItemHeight] N[AJAX
ajax.url, ajax.method
ajax.headers, ajax.data
ajax.dataTransform] O[Events / on.*
load, beforeShow, show
beforeChange, change
beforeClose, close, search] P[Plugin hooks
passed via PluginContext] end E --> I E --> J E --> K E --> L E --> M E --> N E --> O style A fill:#e3f2fd,stroke:#1976d2 style B fill:#fff3e0,stroke:#f57c00 style C fill:#e8f5e9,stroke:#388e3c style E fill:#667eea,stroke:#333,stroke-width:3px,color:#fff

๐Ÿ“Œ Config Priority (high โ†’ low):

1. bind() options โ€” highest priority, direct API call

2. data-* attributes โ€” HTML element dataset, medium

3. defaultConfig โ€” fallback defaults from iStorage

โ€ข SEID, SEID_LIST, SEID_HOLDER: generated at bind time, not in defaultConfig

โ€ข VirtualScroll options: overscan, estimateItemHeight, dynamicHeights, adaptiveEstimate is new

๐Ÿ“ฆ 12. MODULE SYSTEM & BUILD OUTPUT

graph LR subgraph SOURCE A["src/ts/index.ts ESM entry"] B["src/ts/global.ts UMD global entry"] end subgraph BUILD_ROLLUP C[Tree Shaking] D[Minification] E[CSS extraction] F[Source maps] end subgraph OUTPUT_DIST G["selective-ui.esm.js"] GM["selective-ui.esm.min.js"] H["selective-ui.umd.js"] NM["selective-ui.min.js"] I["selective-ui.css"] O["selective-ui.min.css"] end subgraph USAGE subgraph ESM_ENV J["ES Module import bind from 'selective-ui'"] end subgraph BROWSER_ENV K["Browser Global window.SelectiveUI"] M["GLOBAL_SEUI globalThis.GLOBAL_SEUI"] end subgraph STYLE L["CSS link selective-ui.min.css"] end end A --> C B --> C C --> G C --> GM C --> H C --> NM D --> GM D --> NM E --> I E --> O F --> G F --> H G --> J GM --> J H --> K NM --> K NM --> M I --> L O --> L style G fill:#43e97b,stroke:#333,stroke-width:2px style GM fill:#43e97b,stroke:#333,stroke-width:2px style H fill:#667eea,stroke:#333,stroke-width:2px,color:#fff style NM fill:#667eea,stroke:#333,stroke-width:2px,color:#fff style I fill:#fa709a,stroke:#333,stroke-width:2px,color:#fff style O fill:#fa709a,stroke:#333,stroke-width:2px,color:#fff

๐Ÿ“Œ Build Outputs:

โ€ข ESM (index.ts): module-level SECLASS, tree-shakeable, used for bundlers

โ€ข UMD (global.ts): globalThis.GLOBAL_SEUI pattern โ€” prevents duplicate loading when including multiple times

โ€ข Brotli .br files: pre-compressed for CDN serving, reduces ~70% compared to raw

โ€ข TypeScript declarations (.d.ts): full type safety for consumers

๐ŸŽฏ Usage Examples:

  • ES Module: import { bind, find, destroy } from 'selective-ui'
  • Browser UMD: window.GLOBAL_SEUI.bind(".my-select", options)
  • Plugin: registerPlugin({ id: "myPlugin", onBind(ctx) {...} })
  • CDN: https://cdn.jsdelivr.net/npm/selective-ui/dist/selective-ui.min.js