You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Refactor Toggle class (BootstrapToggle) to extend Component<"toggle"> from the component-lifecycle library, delegating lifecycle management (initialization, attachment, disposal, destruction) to the library's state machine. This replaces manual flags (eventsBound, suppressExternalSync) with a deterministic, observable lifecycle while preserving the existing public API surface and domain event system.
Current behavior:Toggle manages lifecycle via manual eventsBound flag and explicit destroy() method. Construction immediately initializes and attaches the component, with all setup in constructor.
Target behavior:Toggle extends Component<"toggle"> where the constructor calls super(element), then internally invokes this.init() and this.attach(). Lifecycle hooks (doInit(), doAttach(), doDispose(), doDestroy()) encapsulate setup/teardown logic.
⚠️ PREREQUISITE BLOCKER:rerender() method must be refactored BEFORE this feature (see "Additional Comments" for details).
Expected Benefits
Standardized lifecycle: Deterministic state machine (idle → initialized → attached ↔ disposed → destroyed) with validation
Observability: Automatic emission of typed lifecycle events (toggle:initialized, toggle:attached, toggle:disposed, toggle:destroyed) without manual dispatch
Resource management: Clear separation of concerns via hooks ensures proper cleanup and prevents memory leaks
Zero regression: Existing public API unchanged; existing domain events (toggle:on, toggle:off, etc.) continue to work alongside new lifecycle events
Acceptance Criteria
Toggle extends Component<"toggle"> from component-lifecycle
Static readonly PREFIX property set to "toggle"
Constructor calls super(element) as first statement, preserves OptionResolver.resolve() call, then calls this.init() and this.attach() internally
doInit() hook contains: StateReducer creation (state initialization only - no DOM or side effects)
doAttach() hook contains: DOMBuilder creation, bindEventListeners(), interceptInputProperties(), and setting this.element.bsToggle = this
doDispose() hook removes event listeners (reverse of doAttach())
doDestroy() hook contains current destroy() logic: restoreInputProperties(), unbindEventListeners(), domBuilder.destroy(), and delete this.element.bsToggle
Manual eventsBound flag is removed; replaced with isAttached() or lifecycle state checks
suppressExternalSync flag remains (internal state sync mechanism, not lifecycle-related)
All existing public methods (toggle, on, off, enable, disable, readonly, indeterminate, determinate, update, destroy) continue to work without signature changes
Existing domain events (toggle:on, toggle:off, toggle:mixed, toggle:enabled, toggle:disabled, toggle:readonly) are still dispatched via trigger() in addition to new lifecycle events
Auto-initialization via [data-toggle="toggle"] continues to work (constructor handles init()/attach() internally)
Existing tests pass with no regressions
Documentation
Event Emission Flow
Lifecycle events (automatic from base class with PREFIX = "toggle"):
toggle:initialized → emitted after doInit() completes
toggle:attached → emitted after doAttach() completes
toggle:disposed → emitted after doDispose() completes
toggle:destroyed → emitted after doDestroy() completes
Domain events (preserved existing behavior from trigger() method):
toggle:on → emitted when toggle becomes ON
toggle:off → emitted when toggle becomes OFF
toggle:mixed → emitted when toggle becomes MIXED (indeterminate)
toggle:enabled → emitted when toggle becomes enabled
toggle:disabled → emitted when toggle becomes disabled
toggle:readonly → emitted when toggle becomes readonly
Backward compatibility: Existing listeners for domain events continue to work unchanged. Lifecycle events are additive.
Short Description of the Feature
Refactor
Toggleclass (BootstrapToggle) to extendComponent<"toggle">from thecomponent-lifecyclelibrary, delegating lifecycle management (initialization, attachment, disposal, destruction) to the library's state machine. This replaces manual flags (eventsBound,suppressExternalSync) with a deterministic, observable lifecycle while preserving the existing public API surface and domain event system.Current behavior:
Togglemanages lifecycle via manualeventsBoundflag and explicitdestroy()method. Construction immediately initializes and attaches the component, with all setup in constructor.Target behavior:
Toggle extends Component<"toggle">where the constructor callssuper(element), then internally invokesthis.init()andthis.attach(). Lifecycle hooks (doInit(),doAttach(),doDispose(),doDestroy()) encapsulate setup/teardown logic.rerender()method must be refactored BEFORE this feature (see "Additional Comments" for details).Expected Benefits
idle→initialized→attached↔disposed→destroyed) with validationtoggle:initialized,toggle:attached,toggle:disposed,toggle:destroyed) without manual dispatchisAttached(),isDestroyed(), etc.) replace manual flagstoggle:on,toggle:off, etc.) continue to work alongside new lifecycle eventsAcceptance Criteria
ToggleextendsComponent<"toggle">fromcomponent-lifecyclePREFIXproperty set to"toggle"super(element)as first statement, preservesOptionResolver.resolve()call, then callsthis.init()andthis.attach()internallydoInit()hook contains:StateReducercreation (state initialization only - no DOM or side effects)doAttach()hook contains:DOMBuildercreation,bindEventListeners(),interceptInputProperties(), and settingthis.element.bsToggle = thisdoDispose()hook removes event listeners (reverse ofdoAttach())doDestroy()hook contains currentdestroy()logic:restoreInputProperties(),unbindEventListeners(),domBuilder.destroy(), anddelete this.element.bsToggleeventsBoundflag is removed; replaced withisAttached()or lifecycle state checkssuppressExternalSyncflag remains (internal state sync mechanism, not lifecycle-related)toggle,on,off,enable,disable,readonly,indeterminate,determinate,update,destroy) continue to work without signature changestoggle:on,toggle:off,toggle:mixed,toggle:enabled,toggle:disabled,toggle:readonly) are still dispatched viatrigger()in addition to new lifecycle events[data-toggle="toggle"]continues to work (constructor handlesinit()/attach()internally)Documentation
Event Emission Flow
Lifecycle events (automatic from base class with
PREFIX = "toggle"):toggle:initialized→ emitted afterdoInit()completestoggle:attached→ emitted afterdoAttach()completestoggle:disposed→ emitted afterdoDispose()completestoggle:destroyed→ emitted afterdoDestroy()completesDomain events (preserved existing behavior from
trigger()method):toggle:on→ emitted when toggle becomes ONtoggle:off→ emitted when toggle becomes OFFtoggle:mixed→ emitted when toggle becomes MIXED (indeterminate)toggle:enabled→ emitted when toggle becomes enabledtoggle:disabled→ emitted when toggle becomes disabledtoggle:readonly→ emitted when toggle becomes readonlyBackward compatibility: Existing listeners for domain events continue to work unchanged. Lifecycle events are additive.
See component-lifecycle events doc
State Query Methods (New Capabilities)
These methods from
Componentbecome available without additional implementation:isAttached()eventsBoundflag (indirectly)isDestroyed()isDisposed()isInitialized()isIdle()state(getter)LifecycleStateenumSee component-lifecycle lifecycle doc
Public API Compatibility Matrix
toggle(silent?)stateReducer.do())on(silent?)off(silent?)enable(silent?)disable(silent?)readonly(silent?)indeterminate(silent?)determinate(silent?)update()destroy()rerender()component-lifecycle API
See component-lifecycle API doc
BLOCKER:
rerender()Method RefactoringCurrent implementation (lines 477-480):
Problem with lifecycle integration:
Required refactoring (to be done BEFORE this feature):
Additional Comments
Risks & Mitigations
rerender()incompatibilitydoAttach()toggle:initialized, domain events usetoggle:on- no collision despite same prefixsuppressExternalSyncflagdoAttach(); lifecycle migration doesn't affect this internal sync mechanismDOMBuildernot observing lifecycleDOMBuilderis a helper class, not a component; it remains unchanged and is destroyed viadomBuilder.destroy()indoDestroy()eventsBoundflag exists; update tests to useisAttached()or remove flag assertionsDependencies
component-lifecycle(version as specified in user's links)Out of Scope
toggle:on, etc.) behavior or payloadrerender()(handled in separate prerequisite PR)DOMBuilder,OptionResolver, orStateReducerbeyond lifecycle integrationFeature Request Checklist