bpmn-js 18.23.0 → 18.24.0

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.
@@ -72,6 +72,12 @@
72
72
  --popup-search-border-color: var(--color-grey-225-10-75);
73
73
  --popup-search-focus-border-color: var(--color-blue-205-100-50);
74
74
  --popup-search-focus-background-color: var(--color-blue-205-100-95);
75
+ --popup-tab-color: var(--color-grey-225-10-55);
76
+ --popup-tab-hover-color: var(--color-grey-225-10-15);
77
+ --popup-tab-active-color: var(--color-grey-225-10-15);
78
+ --popup-tab-active-border-color: var(--color-blue-205-100-50);
79
+ --popup-tab-strip-border-color: var(--color-grey-225-10-90);
80
+ --popup-tab-focus-border-color: var(--color-blue-205-100-50);
75
81
 
76
82
  --resizer-fill-color: var(--color-blue-205-100-45);
77
83
  --resizer-stroke-color: var(--canvas-fill-color);
@@ -615,6 +621,97 @@
615
621
  margin: 10px 12px;
616
622
  }
617
623
 
624
+ .djs-popup-tabs-container {
625
+ position: relative;
626
+ border-bottom: solid 1px var(--popup-tab-strip-border-color);
627
+ }
628
+
629
+ /**
630
+ * The scroll control doubles as the edge fade: tabs scrolled past it fade out
631
+ * rather than being cut off behind an opaque block.
632
+ */
633
+ .djs-popup-tabs-scroll {
634
+ appearance: none;
635
+ position: absolute;
636
+ top: 0;
637
+ bottom: 1px;
638
+ width: 28px;
639
+ display: flex;
640
+ align-items: center;
641
+ padding: 0;
642
+ border: none;
643
+ border-radius: 0;
644
+ color: var(--popup-tab-color);
645
+ cursor: pointer;
646
+ }
647
+
648
+ .djs-popup-tabs-scroll:hover {
649
+ color: var(--popup-tab-hover-color);
650
+ }
651
+
652
+ .djs-popup-tabs-scroll-start {
653
+ left: 0;
654
+ justify-content: flex-start;
655
+ background: linear-gradient(to left, transparent, var(--popup-background-color) 60%);
656
+ }
657
+
658
+ .djs-popup-tabs-scroll-end {
659
+ right: 0;
660
+ justify-content: flex-end;
661
+ background: linear-gradient(to right, transparent, var(--popup-background-color) 60%);
662
+ }
663
+
664
+ .djs-popup-tabs-scroll-start svg {
665
+ transform: rotate(180deg);
666
+ }
667
+
668
+ .djs-popup-tabs {
669
+ display: flex;
670
+ flex-wrap: nowrap;
671
+ gap: 2px;
672
+ overflow-x: auto;
673
+ overflow-y: hidden;
674
+ padding: 0 12px;
675
+
676
+ /* keep scrolled-to tabs clear of the scroll controls */
677
+ scroll-padding: 0 28px;
678
+ scrollbar-width: none;
679
+ }
680
+
681
+ .djs-popup-tabs::-webkit-scrollbar {
682
+ display: none;
683
+ }
684
+
685
+ .djs-popup-tab {
686
+ appearance: none;
687
+ background: transparent;
688
+ border: none;
689
+ border-bottom: solid 2px transparent;
690
+ border-radius: 0;
691
+ flex: 0 0 auto;
692
+ padding: 6px 10px;
693
+ font: inherit;
694
+ font-size: var(--popup-font-size);
695
+ color: var(--popup-tab-color);
696
+ cursor: pointer;
697
+ white-space: nowrap;
698
+ }
699
+
700
+ .djs-popup-tab:hover {
701
+ color: var(--popup-tab-hover-color);
702
+ }
703
+
704
+ .djs-popup-tab:focus-visible {
705
+ outline: solid 1px var(--popup-tab-focus-border-color);
706
+ outline-offset: -2px;
707
+ }
708
+
709
+ .djs-popup-tab[aria-selected="true"] {
710
+ color: var(--popup-tab-active-color);
711
+ border-bottom-color: var(--popup-tab-active-border-color);
712
+ font-weight: var(--popup-header-font-weight);
713
+ }
714
+
618
715
  .djs-popup-title {
619
716
  font-size: var(--popup-font-size);
620
717
  font-weight: var(--popup-header-font-weight);
@@ -686,10 +783,17 @@
686
783
  text-decoration: none;
687
784
  }
688
785
 
689
- .djs-popup-entry-chevron {
786
+ .djs-popup-entry-actions {
690
787
  display: flex;
691
788
  align-items: center;
789
+ flex-shrink: 0;
790
+ gap: 6px;
692
791
  margin-left: 6px;
792
+ }
793
+
794
+ .djs-popup-entry-chevron {
795
+ display: flex;
796
+ align-items: center;
693
797
  color: var(--popup-description-color);
694
798
  }
695
799
 
@@ -847,18 +951,30 @@
847
951
  }
848
952
 
849
953
  .djs-popup-entry-docs {
850
- display: none;
851
- margin-left: 3px;
954
+ display: flex;
955
+ align-items: center;
956
+ justify-content: center;
957
+ flex-shrink: 0;
958
+ visibility: hidden;
959
+ padding: 2px 3px;
852
960
  color: var(--popup-description-color);
961
+ background: #fff;
962
+ border: 1px solid var(--popup-search-border-color);
963
+ border-radius: 4px;
964
+ cursor: pointer;
965
+ transition: background-color .12s ease, border-color .12s ease, color .12s ease;
853
966
  }
854
967
 
855
968
  .djs-popup-body .entry:hover .djs-popup-entry-docs {
856
- display: inline;
969
+ visibility: visible;
970
+ }
971
+
972
+ .djs-popup-body .entry .djs-popup-entry-docs:hover {
973
+ background: #f2f3f5;
857
974
  }
858
975
 
859
976
  .djs-popup-entry-docs svg {
860
977
  vertical-align: middle;
861
- margin: 2px;
862
978
  }
863
979
 
864
980
  .djs-popup-footer-docs {
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * bpmn-js - bpmn-modeler v18.23.0
2
+ * bpmn-js - bpmn-modeler v18.24.0
3
3
  *
4
4
  * Copyright (c) 2014-present, camunda Services GmbH
5
5
  *
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * Source Code: https://github.com/bpmn-io/bpmn-js
10
10
  *
11
- * Date: 2026-08-10
11
+ * Date: 2026-08-11
12
12
  */
13
13
  (function (global, factory) {
14
14
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
@@ -27977,7 +27977,17 @@
27977
27977
  ${ entry.label }
27978
27978
  </span>
27979
27979
  ` : null }
27980
-
27980
+ </span>
27981
+ ${ entry.description && m$1`
27982
+ <span
27983
+ class="djs-popup-entry-description"
27984
+ >
27985
+ ${ entry.description }
27986
+ </span>
27987
+ ` }
27988
+ </div>
27989
+ ${ (entry.documentationRef || entry.entries) && m$1`
27990
+ <div class="djs-popup-entry-actions">
27981
27991
  ${ entry.documentationRef && m$1`
27982
27992
  <a
27983
27993
  class="djs-popup-entry-docs"
@@ -27990,24 +28000,17 @@
27990
28000
  rel="noopener"
27991
28001
  >
27992
28002
  <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
27993
- <path fill-rule="evenodd" clip-rule="evenodd" d="M10.6368 10.6375V5.91761H11.9995V10.6382C11.9995 10.9973 11.8623 11.3141 11.5878 11.5885C11.3134 11.863 10.9966 12.0002 10.6375 12.0002H1.36266C0.982345 12.0002 0.660159 11.8681 0.396102 11.6041C0.132044 11.34 1.52588e-05 11.0178 1.52588e-05 10.6375V1.36267C1.52588e-05 0.98236 0.132044 0.660173 0.396102 0.396116C0.660159 0.132058 0.982345 2.95639e-05 1.36266 2.95639e-05H5.91624V1.36267H1.36266V10.6375H10.6368ZM12 0H7.2794L7.27873 1.36197H9.68701L3.06507 7.98391L4.01541 8.93425L10.6373 2.31231V4.72059H12V0Z" fill="#818798"/>
28003
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M10.6368 10.6375V5.91761H11.9995V10.6382C11.9995 10.9973 11.8623 11.3141 11.5878 11.5885C11.3134 11.863 10.9966 12.0002 10.6375 12.0002H1.36266C0.982345 12.0002 0.660159 11.8681 0.396102 11.6041C0.132044 11.34 1.52588e-05 11.0178 1.52588e-05 10.6375V1.36267C1.52588e-05 0.98236 0.132044 0.660173 0.396102 0.396116C0.660159 0.132058 0.982345 2.95639e-05 1.36266 2.95639e-05H5.91624V1.36267H1.36266V10.6375H10.6368ZM12 0H7.2794L7.27873 1.36197H9.68701L3.06507 7.98391L4.01541 8.93425L10.6373 2.31231V4.72059H12V0Z" fill="currentColor"/>
27994
28004
  </svg>
27995
28005
  </a>
27996
28006
  ` }
27997
- </span>
27998
- ${ entry.description && m$1`
27999
- <span
28000
- class="djs-popup-entry-description"
28001
- >
28002
- ${ entry.description }
28003
- </span>
28004
- ` }
28005
- </div>
28006
- ${ entry.entries && m$1`
28007
- <div class="djs-popup-entry-chevron" aria-hidden="true">
28008
- <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
28009
- <path fill-rule="evenodd" clip-rule="evenodd" d="M3.96967 1.46967C4.26256 1.17678 4.73744 1.17678 5.03033 1.46967L9.03033 5.46967C9.32322 5.76256 9.32322 6.23744 9.03033 6.53033L5.03033 10.5303C4.73744 10.8232 4.26256 10.8232 3.96967 10.5303C3.67678 10.2374 3.67678 9.76256 3.96967 9.46967L7.43934 6L3.96967 2.53033C3.67678 2.23744 3.67678 1.76256 3.96967 1.46967Z" fill="currentColor"/>
28010
- </svg>
28007
+ ${ entry.entries && m$1`
28008
+ <div class="djs-popup-entry-chevron" aria-hidden="true">
28009
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
28010
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M3.96967 1.46967C4.26256 1.17678 4.73744 1.17678 5.03033 1.46967L9.03033 5.46967C9.32322 5.76256 9.32322 6.23744 9.03033 6.53033L5.03033 10.5303C4.73744 10.8232 4.26256 10.8232 3.96967 10.5303C3.67678 10.2374 3.67678 9.76256 3.96967 9.46967L7.43934 6L3.96967 2.53033C3.67678 2.23744 3.67678 1.76256 3.96967 1.46967Z" fill="currentColor"/>
28011
+ </svg>
28012
+ </div>
28013
+ ` }
28011
28014
  </div>
28012
28015
  ` }
28013
28016
  </li>
@@ -28063,7 +28066,7 @@
28063
28066
  const selectedEl = containerEl.querySelector('.selected');
28064
28067
 
28065
28068
  if (selectedEl) {
28066
- scrollIntoView(selectedEl);
28069
+ scrollIntoView$1(selectedEl);
28067
28070
  }
28068
28071
  }, [ selectedEntry ]);
28069
28072
 
@@ -28137,7 +28140,7 @@
28137
28140
  return `${ idPrefix }-group-${ group.id }`;
28138
28141
  }
28139
28142
 
28140
- function scrollIntoView(el) {
28143
+ function scrollIntoView$1(el) {
28141
28144
  if (typeof el.scrollIntoViewIfNeeded === 'function') {
28142
28145
  el.scrollIntoViewIfNeeded();
28143
28146
  } else {
@@ -28179,6 +28182,7 @@
28179
28182
  * @param {PopupMenuEmptyPlaceholder} [props.emptyPlaceholder]
28180
28183
  * @param {number|string} [props.width]
28181
28184
  * @param {searchFn} props.searchFn
28185
+ * @param {import('./PopupMenuProvider.js').PopupMenuTab} [props.defaultTab]
28182
28186
  */
28183
28187
  function PopupMenuComponent(props) {
28184
28188
  const {
@@ -28195,7 +28199,8 @@
28195
28199
  searchFn,
28196
28200
  entries,
28197
28201
  onOpened,
28198
- onClosed
28202
+ onClosed,
28203
+ defaultTab
28199
28204
  } = props;
28200
28205
 
28201
28206
  /**
@@ -28210,12 +28215,183 @@
28210
28215
 
28211
28216
  /**
28212
28217
  * On `PopupMenu#refresh()` (i.e. when `entries` change)
28213
- * the stack is reset to root.
28218
+ * the stack and active tab are reset to root.
28214
28219
  */
28215
28220
  y(() => {
28216
28221
  setNavigationStack([]);
28222
+ setActiveTab(null);
28217
28223
  }, [ entries ]);
28218
28224
 
28225
+ /**
28226
+ * Tabs partition root-level entries. They are derived from the entries'
28227
+ * `tab` property; entries without a `tab` belong to the default tab. The
28228
+ * tab strip only renders when at least two distinct tabs exist.
28229
+ *
28230
+ * @type {import('./PopupMenuProvider.js').PopupMenuTab[]}
28231
+ */
28232
+ const tabs = T(() => {
28233
+ const tabbed = [];
28234
+ const seen = new Set();
28235
+
28236
+ let hasUntagged = false;
28237
+
28238
+ entries.forEach(({ tab }) => {
28239
+ if (!tab) {
28240
+ hasUntagged = true;
28241
+
28242
+ return;
28243
+ }
28244
+
28245
+ if (!seen.has(tab.id)) {
28246
+ seen.add(tab.id);
28247
+ tabbed.push(tab);
28248
+ }
28249
+ });
28250
+
28251
+ if (!tabbed.length) {
28252
+ return [];
28253
+ }
28254
+
28255
+ // untagged entries need a tab to live in; without a configured
28256
+ // `defaultTab` there is no name to give it, so stay untabbed rather
28257
+ // than invent one
28258
+ if (hasUntagged && !defaultTab) {
28259
+ return [];
28260
+ }
28261
+
28262
+ if (!hasUntagged) {
28263
+ return tabbed;
28264
+ }
28265
+
28266
+ return [
28267
+ defaultTab,
28268
+ ...tabbed.filter(tab => tab.id !== defaultTab.id)
28269
+ ];
28270
+ }, [ entries, defaultTab ]);
28271
+
28272
+ const [ activeTabOverride, setActiveTab ] = d(null);
28273
+
28274
+ // the override may name a tab that no longer exists, e.g. on the render
28275
+ // following a `PopupMenu#refresh()` that dropped it
28276
+ const activeTabId = tabs.some(tab => tab.id === activeTabOverride)
28277
+ ? activeTabOverride
28278
+ : (tabs.length ? tabs[0].id : null);
28279
+
28280
+ const tabsRef = A();
28281
+
28282
+ _(() => {
28283
+ const tabsEl = tabsRef.current;
28284
+
28285
+ if (!tabsEl) {
28286
+ return;
28287
+ }
28288
+
28289
+ const activeTabEl = tabsEl.querySelector('[aria-selected="true"]');
28290
+
28291
+ if (activeTabEl) {
28292
+ scrollIntoView(activeTabEl);
28293
+ }
28294
+ }, [ activeTabId ]);
28295
+
28296
+ /**
28297
+ * Which directions the tab strip can still be scrolled in. Drives the
28298
+ * scroll controls, so they only show when there is somewhere to scroll.
28299
+ *
28300
+ * @type {[ { start: boolean, end: boolean }, (overflow: { start: boolean, end: boolean }) => void ]}
28301
+ */
28302
+ const [ overflow, setOverflow ] = d({ start: false, end: false });
28303
+
28304
+ // only ever called while the strip is rendered, so the ref is set
28305
+ const updateOverflow = q(() => {
28306
+ const { scrollLeft, scrollWidth, clientWidth } = tabsRef.current;
28307
+
28308
+ setOverflow({
28309
+ start: scrollLeft > 0,
28310
+
28311
+ // sub-pixel widths leave a fractional remainder at the end
28312
+ end: scrollWidth - clientWidth - scrollLeft > 1
28313
+ });
28314
+ }, []);
28315
+
28316
+ // horizontal scroll on tabs
28317
+ const handleWheel = q(event => {
28318
+ const tabsEl = tabsRef.current;
28319
+
28320
+ const delta = Math.abs(event.deltaX) > Math.abs(event.deltaY)
28321
+ ? event.deltaX
28322
+ : event.deltaY;
28323
+
28324
+ if (!delta) {
28325
+ return;
28326
+ }
28327
+
28328
+ const { scrollLeft, scrollWidth, clientWidth } = tabsEl;
28329
+
28330
+ if (scrollWidth - clientWidth < 1) {
28331
+ return;
28332
+ }
28333
+
28334
+ event.preventDefault();
28335
+
28336
+ tabsEl.scrollLeft = scrollLeft + delta;
28337
+
28338
+ updateOverflow();
28339
+ }, [ updateOverflow ]);
28340
+
28341
+ _(() => {
28342
+ const tabsEl = tabsRef.current;
28343
+
28344
+ if (!tabsEl) {
28345
+ return;
28346
+ }
28347
+
28348
+ updateOverflow();
28349
+
28350
+ tabsEl.addEventListener('scroll', updateOverflow);
28351
+ tabsEl.addEventListener('wheel', handleWheel, { passive: false });
28352
+
28353
+ return () => {
28354
+ tabsEl.removeEventListener('scroll', updateOverflow);
28355
+ tabsEl.removeEventListener('wheel', handleWheel);
28356
+ };
28357
+ }, [ tabs, activeTabId, updateOverflow, handleWheel ]);
28358
+
28359
+ /**
28360
+ * Scroll the nearest tab that is cut off in `direction` into view, so a
28361
+ * click always lands on a fully visible tab.
28362
+ *
28363
+ * @param {number} direction
28364
+ */
28365
+ const scrollTabs = q(direction => {
28366
+ const tabsEl = tabsRef.current;
28367
+
28368
+ const tabEls = direction > 0
28369
+ ? [ ...tabsEl.children ]
28370
+ : [ ...tabsEl.children ].reverse();
28371
+
28372
+ const stripBounds = tabsEl.getBoundingClientRect();
28373
+
28374
+ const cutOff = tabEls.find(tabEl => {
28375
+ const bounds = tabEl.getBoundingClientRect();
28376
+
28377
+ return direction > 0
28378
+ ? bounds.right > stripBounds.right + 1
28379
+ : bounds.left < stripBounds.left - 1;
28380
+ });
28381
+
28382
+ if (cutOff) {
28383
+ scrollIntoView(cutOff);
28384
+
28385
+ // recompute directly: a programmatic scroll does not reliably emit a
28386
+ // `scroll` event, so the controls would otherwise go stale
28387
+ updateOverflow();
28388
+ }
28389
+ }, [ updateOverflow ]);
28390
+
28391
+ const getEntryTabId = q(entry => {
28392
+ return (entry.tab && entry.tab.id) || (defaultTab && defaultTab.id);
28393
+ }, [ defaultTab ]);
28394
+
28219
28395
  const [ searchValue, setSearchValue ] = d('');
28220
28396
  const isSearching = searchValue.trim().length > 0;
28221
28397
 
@@ -28231,9 +28407,15 @@
28231
28407
 
28232
28408
  const entriesToShow = T(() => {
28233
28409
 
28410
+ // the tab filter applies at the root level only; drill-down
28411
+ // (step entries) and search operate on the full entry set
28234
28412
  const availableEntries = navigationStack.length
28235
28413
  ? navigationStack[navigationStack.length - 1].entries
28236
- : entries;
28414
+ : (
28415
+ tabs.length > 1
28416
+ ? entries.filter(entry => getEntryTabId(entry) === activeTabId)
28417
+ : entries
28418
+ );
28237
28419
 
28238
28420
  if (!searchable) return availableEntries;
28239
28421
 
@@ -28246,7 +28428,7 @@
28246
28428
  }
28247
28429
 
28248
28430
  return availableEntries.filter(({ rank = 0 }) => rank >= 0);
28249
- }, [ searchable, isSearching, actionableEntries, searchValue, searchFn, navigationStack, entries ]);
28431
+ }, [ searchable, isSearching, actionableEntries, searchValue, searchFn, navigationStack, entries, tabs, activeTabId, getEntryTabId ]);
28250
28432
 
28251
28433
  const groupedEntries = T(() => {
28252
28434
  if (isSearching) {
@@ -28387,6 +28569,77 @@
28387
28569
  }
28388
28570
  }, [ selectedEntry, keyboardSelect, keyboardDrilldown, handleEntryAction ]);
28389
28571
 
28572
+ const selectTab = q((event, tabId) => {
28573
+ setActiveTab(tabId);
28574
+
28575
+ // a keyboard activation reports no click detail; keep focus in the strip
28576
+ // so arrow navigation keeps working. A pointer click hands focus back to
28577
+ // the search input so typing continues to work
28578
+ if (!event.detail) {
28579
+ return;
28580
+ }
28581
+
28582
+ const popupEl = closest(event.currentTarget, '.djs-popup', true);
28583
+ const inputEl = popupEl && popupEl.querySelector('input');
28584
+
28585
+ inputEl && inputEl.focus();
28586
+ }, []);
28587
+
28588
+ // left/right/Home/End navigate between tabs while the strip has focus; stop
28589
+ // propagation so the global drill-down and entry-action bindings do not fire.
28590
+ // Up/down leave the strip instead
28591
+ const handleTabKeyDown = q(event => {
28592
+ if (event.key === 'Enter' || event.key === ' ') {
28593
+
28594
+ // let the button's native click happen, but keep the event
28595
+ // from reaching the global Enter handler (entry action)
28596
+ event.stopPropagation();
28597
+
28598
+ return;
28599
+ }
28600
+
28601
+ // <ArrowDown> / <ArrowUp> hand focus to the panel so the menu's own
28602
+ // bindings take over — notably <ArrowRight> to step into an entry, which
28603
+ // would otherwise keep switching tabs. The event still bubbles, so the
28604
+ // selection moves on the same keystroke
28605
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
28606
+ const popupEl = closest(event.currentTarget, '.djs-popup', true);
28607
+
28608
+ const focusEl = popupEl.querySelector('input')
28609
+ || popupEl.querySelector('[role="listbox"]');
28610
+
28611
+ focusEl && focusEl.focus();
28612
+
28613
+ return;
28614
+ }
28615
+
28616
+ const idx = tabs.findIndex(tab => tab.id === activeTabId);
28617
+
28618
+ let nextIdx;
28619
+
28620
+ if (event.key === 'ArrowLeft') {
28621
+ nextIdx = (idx - 1 + tabs.length) % tabs.length;
28622
+ } else if (event.key === 'ArrowRight') {
28623
+ nextIdx = (idx + 1) % tabs.length;
28624
+ } else if (event.key === 'Home') {
28625
+ nextIdx = 0;
28626
+ } else if (event.key === 'End') {
28627
+ nextIdx = tabs.length - 1;
28628
+ } else {
28629
+ return;
28630
+ }
28631
+
28632
+ event.preventDefault();
28633
+ event.stopPropagation();
28634
+
28635
+ setActiveTab(tabs[nextIdx].id);
28636
+
28637
+ const strip = event.currentTarget.parentNode;
28638
+ const nextButton = strip && strip.children[nextIdx];
28639
+
28640
+ nextButton && nextButton.focus();
28641
+ }, [ tabs, activeTabId ]);
28642
+
28390
28643
  const handleKey = q(event => {
28391
28644
  if (matches(event.target, 'input')) {
28392
28645
  setSearchValue(() => event.target.value);
@@ -28402,6 +28655,7 @@
28402
28655
  }, []);
28403
28656
 
28404
28657
  const displayBreadcrumbs = !isSearching && navigationStack.length > 0;
28658
+ const displayTabs = tabs.length > 1 && !isSearching && navigationStack.length === 0;
28405
28659
  const displayHeader = (title || headerEntries.length > 0) && !displayBreadcrumbs;
28406
28660
 
28407
28661
  return m$1`
@@ -28451,6 +28705,43 @@
28451
28705
  </div>
28452
28706
  ` }
28453
28707
 
28708
+ ${ displayTabs && m$1`
28709
+ <div class="djs-popup-tabs-container">
28710
+ <div class="djs-popup-tabs" ref=${ tabsRef } role="tablist">
28711
+ ${ tabs.map(tab => m$1`
28712
+ <button
28713
+ type="button"
28714
+ class="djs-popup-tab"
28715
+ role="tab"
28716
+ title=${ tab.title || undefined }
28717
+ aria-controls=${ resultsId }
28718
+ aria-selected=${ tab.id === activeTabId }
28719
+ tabIndex=${ tab.id === activeTabId ? 0 : -1 }
28720
+ onClick=${ (event) => selectTab(event, tab.id) }
28721
+ onKeydown=${ handleTabKeyDown }
28722
+ >${ tab.label }</button>
28723
+ `) }
28724
+ </div>
28725
+
28726
+ ${ /* outside the tablist: its children must be tabs. Keyboard
28727
+ users navigate with the arrow keys instead */ '' }
28728
+ ${ [ -1, 1 ].map(direction => (direction < 0 ? overflow.start : overflow.end) && m$1`
28729
+ <button
28730
+ type="button"
28731
+ class=${ `djs-popup-tabs-scroll djs-popup-tabs-scroll-${ direction < 0 ? 'start' : 'end' }` }
28732
+ aria-hidden="true"
28733
+ tabIndex="-1"
28734
+ onMousedown=${ (event) => event.preventDefault() }
28735
+ onClick=${ () => scrollTabs(direction) }
28736
+ >
28737
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
28738
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M3.96967 1.46967C4.26256 1.17678 4.73744 1.17678 5.03033 1.46967L9.03033 5.46967C9.32322 5.76256 9.32322 6.23744 9.03033 6.53033L5.03033 10.5303C4.73744 10.8232 4.26256 10.8232 3.96967 10.5303C3.67678 10.2374 3.67678 9.76256 3.96967 9.46967L7.43934 6L3.96967 2.53033C3.67678 2.23744 3.67678 1.76256 3.96967 1.46967Z" fill="currentColor"/>
28739
+ </svg>
28740
+ </button>
28741
+ `) }
28742
+ </div>
28743
+ ` }
28744
+
28454
28745
  ${ isSearching && m$1`
28455
28746
  <div class="djs-popup-search-count" aria-live="polite">
28456
28747
  ${ entriesToShow.length } ${ entriesToShow.length === 1 ? 'result' : 'results' } found
@@ -28497,6 +28788,18 @@
28497
28788
  `;
28498
28789
  }
28499
28790
 
28791
+ function scrollIntoView(el) {
28792
+ if (typeof el.scrollIntoViewIfNeeded === 'function') {
28793
+ el.scrollIntoViewIfNeeded();
28794
+ } else {
28795
+ el.scrollIntoView({
28796
+ scrollMode: 'if-needed',
28797
+ block: 'nearest',
28798
+ inline: 'nearest'
28799
+ });
28800
+ }
28801
+ }
28802
+
28500
28803
  /**
28501
28804
  * A component that wraps the popup menu.
28502
28805
  *
@@ -28693,11 +28996,14 @@
28693
28996
  *
28694
28997
  * @typedef {import('../../model/Types.js').Element} Element
28695
28998
  *
28999
+ * @typedef {import('./PopupMenuProvider.js').PopupMenuTab} PopupMenuTab
29000
+ *
28696
29001
  * @typedef { {
28697
29002
  * scale?: {
28698
29003
  * min?: number;
28699
29004
  * max?: number;
28700
29005
  * } | boolean;
29006
+ * defaultTab?: PopupMenuTab;
28701
29007
  * } } PopupMenuConfig
28702
29008
  *
28703
29009
  * @typedef {Element|Element[]} PopupMenuTarget;
@@ -28734,7 +29040,8 @@
28734
29040
  };
28735
29041
 
28736
29042
  this._config = {
28737
- scale: scale
29043
+ scale: scale,
29044
+ defaultTab: config && config.defaultTab
28738
29045
  };
28739
29046
 
28740
29047
 
@@ -28800,6 +29107,7 @@
28800
29107
  onOpened=${ this._onOpened.bind(this) }
28801
29108
  onClosed=${ this._onClosed.bind(this) }
28802
29109
  searchFn=${ this._search }
29110
+ defaultTab=${ this._config.defaultTab }
28803
29111
  ...${{ ...options }}
28804
29112
  />
28805
29113
  `,