The user asked to move to / activate a tab. The host maps the index onto its own model.
The user pressed Delete/Backspace on a tab. Hosts without closeable tabs simply ignore this.
The primary trigger. Deliberately a lifecycle hook rather than only the MutationObserver:
observer callbacks are asynchronous, so between a selection change and the flush the tab order
would point at the PREVIOUS tab — and a tab with no tabindex at all is not focusable, making
focus() a silent no-op. Running here keeps the tab order correct in the same frame the
selection changed.
Safe to run every cycle: it writes only when a value actually differs, so it cannot loop.
A callback method that is invoked immediately after Angular has completed initialization of a component's view. It is invoked only once when the view is instantiated.
A callback method that performs custom clean-up, invoked immediately before a directive, pipe, or service instance is destroyed.
mjTabList— the ARIA tabs keyboard contract, shared by every MJ tab strip.Applied to the element that holds the tabs. It manages the things the APG tabs pattern requires and that hand-rolled strips almost always miss:
Why a directive rather than a base class
MJ's two strips disagree about what a tab IS: one projects
<mj-tab>component instances and addresses them by positional index, the other renders a data array and addresses it by stable id. A shared base class would have to pick one of those models. This directive picks neither — it operates on the DOM, so each strip keeps its own contract and only has to agree that a tab is[role="tab"]and that the selected one carriesaria-selected="true". That agreement is the accessibility requirement anyway, so nothing is invented to make the sharing work.What the host must do
Mark each tab
role="tab", keeparia-selectedtruthful, and act on(TabActivateRequested)/(TabCloseRequested). The directive setsrole="tablist"and ownstabindex— do not also bind it, or the two will fight.