@ixfx/components 0.5.2 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/bundle/index.d.ts +87 -1
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +326 -23
  4. package/bundle/index.js.map +1 -1
  5. package/dist/ac-text.d.ts +1 -1
  6. package/dist/button.d.ts +1 -1
  7. package/dist/button.d.ts.map +1 -1
  8. package/dist/colour-picker.d.ts +1 -1
  9. package/dist/crumbs.d.ts +1 -1
  10. package/dist/{icon-DpA9x2Ve.d.ts → icon-BUwP1CZ1.d.ts} +2 -2
  11. package/dist/icon-BUwP1CZ1.d.ts.map +1 -0
  12. package/dist/icons.d.ts +2 -2
  13. package/dist/{index-DfEkTGvT.d.ts → index-B1UKcA7l.d.ts} +3 -3
  14. package/dist/{index-DfEkTGvT.d.ts.map → index-B1UKcA7l.d.ts.map} +1 -1
  15. package/dist/{index-D_ftz_IT.d.ts → index-BKAlRgbq.d.ts} +2 -2
  16. package/dist/{index-D_ftz_IT.d.ts.map → index-BKAlRgbq.d.ts.map} +1 -1
  17. package/dist/{index-CoqtMSd9.d.ts → index-BUB5SICW.d.ts} +2 -2
  18. package/dist/{index-CoqtMSd9.d.ts.map → index-BUB5SICW.d.ts.map} +1 -1
  19. package/dist/{index-BtGFYfaK.d.ts → index-RqMh1i-O.d.ts} +2 -2
  20. package/dist/{index-BtGFYfaK.d.ts.map → index-RqMh1i-O.d.ts.map} +1 -1
  21. package/dist/index.d.ts +95 -9
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +304 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/{labelled-input-base-DiTme1JO.d.ts → labelled-input-base-Dp_9KP0G.d.ts} +2 -2
  26. package/dist/{labelled-input-base-DiTme1JO.d.ts.map → labelled-input-base-Dp_9KP0G.d.ts.map} +1 -1
  27. package/dist/labelled-radial-input.d.ts +2 -2
  28. package/dist/labelled-radial-input.d.ts.map +1 -1
  29. package/dist/labelled-range-input.d.ts +1 -1
  30. package/dist/{menu-item-DSyf9f_L.d.ts → menu-item-CQ4Qg731.d.ts} +2 -2
  31. package/dist/{menu-item-DSyf9f_L.d.ts.map → menu-item-CQ4Qg731.d.ts.map} +1 -1
  32. package/dist/menu.d.ts +2 -2
  33. package/dist/panel.d.ts +1 -1
  34. package/dist/{tab-list-BohmbvqF.d.ts → tab-list-VtWT-_FG.d.ts} +2 -2
  35. package/dist/{tab-list-BohmbvqF.d.ts.map → tab-list-VtWT-_FG.d.ts.map} +1 -1
  36. package/dist/tabs.d.ts +1 -1
  37. package/docs-user/tabs.md +95 -1
  38. package/package.json +1 -1
  39. package/dist/icon-DpA9x2Ve.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -5398,6 +5398,309 @@ let TabPanelElement = class TabPanelElement extends LitElement {
5398
5398
  };
5399
5399
  TabPanelElement = __decorate([safeCustomElement(`ixfx-tab-panel`)], TabPanelElement);
5400
5400
  //#endregion
5401
+ //#region src/tabs/tab-controller.ts
5402
+ /**
5403
+ * Connects an `ixfx-tab-list` to an `ixfx-tab-panels` container, handling the
5404
+ * wiring between them (header clicks show the matching panel, closing a tab
5405
+ * removes its panel, etc.) and offering an API to select, add, remove, rename
5406
+ * and reorder tabs.
5407
+ *
5408
+ * @example
5409
+ * ```ts
5410
+ * const ctrl = new TabController(
5411
+ * document.querySelector('ixfx-tab-list'),
5412
+ * document.querySelector('ixfx-tab-panels'),
5413
+ * );
5414
+ * ctrl.connect();
5415
+ *
5416
+ * ctrl.addTab({ label: 'Console', content: 'Console panel' });
5417
+ * ctrl.selectTab('Console');
5418
+ * ```
5419
+ */
5420
+ var TabController = class {
5421
+ #list;
5422
+ #panels;
5423
+ #opts;
5424
+ #tabs = [];
5425
+ #signal;
5426
+ #observer;
5427
+ #commands;
5428
+ #registeredCommands = /* @__PURE__ */ new Set();
5429
+ constructor(list, panels, options = {}) {
5430
+ this.#list = list;
5431
+ this.#panels = panels;
5432
+ this.#opts = options;
5433
+ this.#commands = options.commands;
5434
+ }
5435
+ connect() {
5436
+ this.disconnect();
5437
+ this.#signal = new AbortController();
5438
+ const { signal } = this.#signal;
5439
+ this.#list.addEventListener(`change`, this.#onChange, { signal });
5440
+ this.#list.addEventListener(`reorder`, this.#onReorder, { signal });
5441
+ this.#list.addEventListener(`close`, this.#onClose, { signal });
5442
+ this.#observer = new MutationObserver(() => this.#rebuild());
5443
+ this.#observer.observe(this.#list, { childList: true });
5444
+ this.#observer.observe(this.#panels, { childList: true });
5445
+ this.#rebuild();
5446
+ this.#panels.syncWithList(this.#list);
5447
+ for (const tab of this.#tabs) this.#registerCommand(tab);
5448
+ }
5449
+ disconnect() {
5450
+ this.#signal?.abort();
5451
+ this.#signal = void 0;
5452
+ this.#observer?.disconnect();
5453
+ this.#observer = void 0;
5454
+ for (const tab of this.#tabs) this.#unregisterCommand(tab);
5455
+ this.#registeredCommands.clear();
5456
+ this.#tabs = [];
5457
+ }
5458
+ /**
5459
+ * Select a tab as if it was clicked — activates the header (deactivating the
5460
+ * others) and shows its panel.
5461
+ *
5462
+ * `sel` may be a tab id, the panel id its `for` points at, its label text
5463
+ * (case-insensitive), or the tab item element itself.
5464
+ *
5465
+ * Returns true if a tab was found.
5466
+ */
5467
+ selectTab(sel) {
5468
+ const tab = this.#resolveTab(sel);
5469
+ if (!tab) return false;
5470
+ const previous = this.getSelected();
5471
+ if (previous?.id === tab.id) {
5472
+ this.#panels.selectPanel(tab.forId);
5473
+ return true;
5474
+ }
5475
+ this.#list.selectElement(tab.item);
5476
+ this.#panels.selectPanel(tab.forId);
5477
+ this.#opts.onSelect?.(tab, previous);
5478
+ return true;
5479
+ }
5480
+ selectNext() {
5481
+ return this.#selectOffset(1);
5482
+ }
5483
+ selectPrevious() {
5484
+ return this.#selectOffset(-1);
5485
+ }
5486
+ getSelected() {
5487
+ return this.#tabs.find((t) => t.item.hasAttribute(`selected`));
5488
+ }
5489
+ getTabs() {
5490
+ return [...this.#tabs];
5491
+ }
5492
+ getTab(id) {
5493
+ return this.#tabs.find((t) => t.id === id);
5494
+ }
5495
+ addTab(opts) {
5496
+ const { id, forId } = this.#idsFor(opts);
5497
+ const item = document.createElement(`ixfx-tab-list-item`);
5498
+ item.id = id;
5499
+ item.setAttribute(`for`, forId);
5500
+ item.closeable = opts.closeable ?? true;
5501
+ item.append(opts.label);
5502
+ if (opts.iconName) item.iconName = opts.iconName;
5503
+ if (opts.description) item.description = opts.description;
5504
+ for (const el of opts.toolbar ?? []) {
5505
+ el.slot = `toolbar`;
5506
+ item.append(el);
5507
+ }
5508
+ const panel = document.createElement(`ixfx-tab-panel`);
5509
+ panel.id = forId;
5510
+ if (opts.content !== void 0) this.#fillContent(panel, opts.content);
5511
+ const index = Math.max(0, Math.min(opts.index ?? this.#tabs.length, this.#tabs.length));
5512
+ this.#insertElement(this.#list, item, index);
5513
+ this.#insertElement(this.#panels, panel, index);
5514
+ const tab = {
5515
+ id,
5516
+ forId,
5517
+ item,
5518
+ panel,
5519
+ label: opts.label,
5520
+ iconName: opts.iconName
5521
+ };
5522
+ this.#tabs.splice(index, 0, tab);
5523
+ this.#registerCommand(tab);
5524
+ this.#opts.onAdd?.(tab);
5525
+ if (opts.select ?? true) this.selectTab(id);
5526
+ return tab;
5527
+ }
5528
+ removeTab(sel) {
5529
+ const tab = this.#resolveTab(sel);
5530
+ if (!tab) return false;
5531
+ const index = this.#tabs.indexOf(tab);
5532
+ const adjacent = tab.item.hasAttribute(`selected`) ? this.#adjacentTab(index) : void 0;
5533
+ tab.item.remove();
5534
+ tab.panel.remove();
5535
+ this.#removeFromIndex(tab);
5536
+ this.#unregisterCommand(tab);
5537
+ this.#opts.onRemove?.(tab);
5538
+ if (adjacent) this.selectTab(adjacent.id);
5539
+ return true;
5540
+ }
5541
+ renameTab(sel, label) {
5542
+ const tab = this.#resolveTab(sel);
5543
+ if (!tab) return false;
5544
+ for (const child of [...tab.item.childNodes]) {
5545
+ if (child.nodeType === Node.ELEMENT_NODE && child.slot === `toolbar`) continue;
5546
+ tab.item.removeChild(child);
5547
+ }
5548
+ tab.item.prepend(label);
5549
+ tab.label = label;
5550
+ this.#reregisterCommand(tab);
5551
+ return true;
5552
+ }
5553
+ setTabIcon(sel, iconName) {
5554
+ const tab = this.#resolveTab(sel);
5555
+ if (!tab) return false;
5556
+ if (iconName) {
5557
+ tab.item.iconName = iconName;
5558
+ tab.iconName = iconName;
5559
+ } else {
5560
+ tab.item.iconName = void 0;
5561
+ tab.item.removeAttribute(`icon-name`);
5562
+ tab.iconName = void 0;
5563
+ }
5564
+ return true;
5565
+ }
5566
+ #onChange = (event) => {
5567
+ const detail = event.detail;
5568
+ this.#panels.selectPanel(detail.for);
5569
+ const tab = this.#tabs.find((t) => t.forId === detail.for);
5570
+ if (!tab) return;
5571
+ const previous = detail.previous ? this.#tabs.find((t) => t.id === detail.previous) : void 0;
5572
+ this.#opts.onSelect?.(tab, previous);
5573
+ };
5574
+ #onReorder = () => {
5575
+ this.#rebuild();
5576
+ this.#opts.onReorder?.(this.#tabs);
5577
+ };
5578
+ #onClose = (event) => {
5579
+ const item = event.target?.closest(`ixfx-tab-list-item`);
5580
+ const tab = item ? this.#tabByItem(item) : void 0;
5581
+ if (!tab) return;
5582
+ const index = this.#tabs.indexOf(tab);
5583
+ const adjacent = tab.item.hasAttribute(`selected`) ? this.#adjacentTab(index) : void 0;
5584
+ tab.panel.remove();
5585
+ this.#removeFromIndex(tab);
5586
+ this.#unregisterCommand(tab);
5587
+ this.#opts.onRemove?.(tab);
5588
+ if (adjacent) this.selectTab(adjacent.id);
5589
+ };
5590
+ #selectOffset(offset) {
5591
+ const tabs = this.#tabs;
5592
+ if (tabs.length === 0) return false;
5593
+ const current = this.getSelected();
5594
+ const next = ((current ? tabs.indexOf(current) : offset === 1 ? -1 : 0) + offset + tabs.length) % tabs.length;
5595
+ return this.selectTab(tabs[next].id);
5596
+ }
5597
+ #resolveTab(sel) {
5598
+ if (typeof sel !== `string`) return this.#tabs.find((t) => t.item === sel);
5599
+ const label = sel.trim().toLowerCase();
5600
+ return this.#tabs.find((t) => t.id === sel) ?? this.#tabs.find((t) => t.forId === sel) ?? this.#tabs.find((t) => t.label.toLowerCase() === label);
5601
+ }
5602
+ #tabByItem(item) {
5603
+ return this.#tabs.find((t) => t.item === item);
5604
+ }
5605
+ #adjacentTab(index) {
5606
+ const tabs = this.#tabs;
5607
+ if (tabs.length <= 1) return void 0;
5608
+ return tabs[index + 1] ?? tabs[index - 1];
5609
+ }
5610
+ #removeFromIndex(tab) {
5611
+ const index = this.#tabs.indexOf(tab);
5612
+ if (index >= 0) this.#tabs.splice(index, 1);
5613
+ }
5614
+ #idsFor(opts) {
5615
+ const base = this.#slug(opts.label) || `tab`;
5616
+ return {
5617
+ id: opts.id ?? this.#uniqueId(`tab-${base}`),
5618
+ forId: opts.forId ?? this.#uniqueId(`panel-${base}`)
5619
+ };
5620
+ }
5621
+ #slug(value) {
5622
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, `-`).replace(/^-+|-+$/g, ``);
5623
+ }
5624
+ #uniqueId(base) {
5625
+ let candidate = base;
5626
+ let n = 2;
5627
+ while (document.getElementById(candidate)) candidate = `${base}-${n++}`;
5628
+ return candidate;
5629
+ }
5630
+ #fillContent(panel, content) {
5631
+ if (typeof content === `function`) {
5632
+ content(panel);
5633
+ return;
5634
+ }
5635
+ if (typeof content === `string`) {
5636
+ panel.append(document.createTextNode(content));
5637
+ return;
5638
+ }
5639
+ panel.append(content);
5640
+ }
5641
+ #insertElement(container, el, index) {
5642
+ const children = Array.from(container.children);
5643
+ container.insertBefore(el, children[index] ?? null);
5644
+ }
5645
+ #rebuild() {
5646
+ const panels = /* @__PURE__ */ new Map();
5647
+ for (const el of this.#panels.querySelectorAll(`ixfx-tab-panel`)) panels.set(el.id, el);
5648
+ const tabs = [];
5649
+ for (const item of this.#list.querySelectorAll(`ixfx-tab-list-item`)) {
5650
+ const forId = item.getAttribute(`for`) ?? ``;
5651
+ const panel = panels.get(forId);
5652
+ if (!panel) continue;
5653
+ tabs.push({
5654
+ id: item.id || forId,
5655
+ forId,
5656
+ item,
5657
+ panel,
5658
+ label: this.#labelOf(item),
5659
+ iconName: item.iconName
5660
+ });
5661
+ }
5662
+ this.#tabs = tabs;
5663
+ }
5664
+ /** The tab's visible label text, excluding any `slot="toolbar"` content. */
5665
+ #labelOf(item) {
5666
+ const clone = item.cloneNode(true);
5667
+ clone.querySelectorAll(`[slot="toolbar"]`).forEach((el) => el.remove());
5668
+ return (clone.textContent ?? ``).trim();
5669
+ }
5670
+ #registerCommand(tab) {
5671
+ if (!this.#commands) return;
5672
+ if (tab.item.commandActive) return;
5673
+ if (this.#commands.has(tab.id)) return;
5674
+ this.#commands.register({
5675
+ id: tab.id,
5676
+ label: tab.label,
5677
+ description: tab.item.description ?? tab.label,
5678
+ icon: tab.iconName,
5679
+ execute: () => {
5680
+ this.selectTab(tab.id);
5681
+ }
5682
+ });
5683
+ this.#registeredCommands.add(tab.id);
5684
+ }
5685
+ #unregisterCommand(tab) {
5686
+ if (!this.#commands) return;
5687
+ if (this.#registeredCommands.has(tab.id)) {
5688
+ this.#commands.unregister(tab.id);
5689
+ this.#registeredCommands.delete(tab.id);
5690
+ }
5691
+ }
5692
+ #reregisterCommand(tab) {
5693
+ if (!this.#commands || !this.#registeredCommands.has(tab.id)) return;
5694
+ const existing = this.#commands.get(tab.id);
5695
+ if (!existing) return;
5696
+ this.#commands.unregister(tab.id);
5697
+ this.#commands.register({
5698
+ ...existing,
5699
+ label: tab.label
5700
+ });
5701
+ }
5702
+ };
5703
+ //#endregion
5401
5704
  //#region src/tabs/tab-panels.ts
5402
5705
  let TabPanelsElement = class TabPanelsElement extends LitElement {
5403
5706
  /**
@@ -5794,6 +6097,6 @@ function init() {
5794
6097
  console.log(`Init`);
5795
6098
  }
5796
6099
  //#endregion
5797
- export { AcTextElement, AcTokenElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataController, DataDisplayComponent, EditableLabel, EditableNumber, FormElement, FormGroupElement, FormPartElement, GroupedItemListerElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, LabelledRadialInput, LabelledRangeInput, LedElement, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, NotificationManager, NotificationPillElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, SelectHorizontalElement, SliderInputElement, SnapContainer, SplitButton, SplitLayoutElement, SwipeElement, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TimelineController, TimelineTrackTabElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeListElement, VerticalListElement, XyPad, applyAgeModification, applyHighlights, attachTimelineInteractions, buildHighlightTargets, clearHighlights, collectDataResult, computeColour, createAcCompletionProvider, createElementIncrSearch, createFuzzySearch, createIncrSearch, createPillRenderer, createTreeListAdapter, createVerticalListAdapter, getIcon, hasIcon, iconBus, init, monitorIndeterminate, parseColourScaleAttr, readBaseColour, registerIcon, resolveCssVars, resolveGrid, svgToDataUri, timelineStyles };
6100
+ export { AcTextElement, AcTokenElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataController, DataDisplayComponent, EditableLabel, EditableNumber, FormElement, FormGroupElement, FormPartElement, GroupedItemListerElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, LabelledRadialInput, LabelledRangeInput, LedElement, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, NotificationManager, NotificationPillElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, SelectHorizontalElement, SliderInputElement, SnapContainer, SplitButton, SplitLayoutElement, SwipeElement, TabController, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TimelineController, TimelineTrackTabElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeListElement, VerticalListElement, XyPad, applyAgeModification, applyHighlights, attachTimelineInteractions, buildHighlightTargets, clearHighlights, collectDataResult, computeColour, createAcCompletionProvider, createElementIncrSearch, createFuzzySearch, createIncrSearch, createPillRenderer, createTreeListAdapter, createVerticalListAdapter, getIcon, hasIcon, iconBus, init, monitorIndeterminate, parseColourScaleAttr, readBaseColour, registerIcon, resolveCssVars, resolveGrid, svgToDataUri, timelineStyles };
5798
6101
 
5799
6102
  //# sourceMappingURL=index.js.map