bpmn-js 18.20.0 → 18.21.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.
@@ -57,6 +57,7 @@
57
57
 
58
58
  --popup-font-family: "IBM Plex Sans", sans-serif;
59
59
  --popup-font-size: 14px;
60
+ --popup-secondary-font-size: .9em;
60
61
  --popup-header-entry-selected-color: var(--color-blue-205-100-50);
61
62
  --popup-header-font-weight: bolder;
62
63
  --popup-header-group-divider-color: var(--color-grey-225-10-75);
@@ -696,7 +697,7 @@
696
697
 
697
698
  .djs-popup-search-count {
698
699
  margin: 0 12px;
699
- font-size: 11px;
700
+ font-size: var(--popup-secondary-font-size);
700
701
  color: var(--popup-description-color);
701
702
  }
702
703
 
@@ -708,6 +709,16 @@
708
709
  padding-right: 9px;
709
710
  }
710
711
 
712
+ /**
713
+ * The results list is focusable (as a listbox holding
714
+ * `aria-activedescendant`) when there is no search field.
715
+ * The active entry is indicated via `.entry.selected`, so we hide
716
+ * the outline on the list container itself.
717
+ */
718
+ .djs-popup-results:focus {
719
+ outline: none;
720
+ }
721
+
711
722
  .djs-popup-group {
712
723
  margin: 0;
713
724
  padding: 0;
@@ -773,7 +784,7 @@
773
784
  }
774
785
 
775
786
  .djs-popup-entry-description {
776
- font-size: .9em;
787
+ font-size: var(--popup-secondary-font-size);
777
788
  margin-top: .25em;
778
789
 
779
790
  color: var(--popup-description-color);
@@ -815,14 +826,28 @@
815
826
  color: var(--popup-no-results-color);
816
827
  }
817
828
 
829
+ .djs-popup-footer {
830
+ display: flex;
831
+ padding: 5px 12px 7px 12px;
832
+ border-top: solid 1px var(--popup-border-color);
833
+ }
834
+
835
+ /**
836
+ * The footer holds the keyboard/assistive-technology reachable documentation
837
+ * link. Mouse users get the per-entry `.djs-popup-entry-docs` affordance on
838
+ * hover instead, so the (redundant) footer link is hidden while the results
839
+ * list is hovered and shown during keyboard navigation.
840
+ */
841
+ .djs-popup-results:hover ~ .djs-popup-footer {
842
+ display: none;
843
+ }
844
+
818
845
  .djs-popup-entry-docs {
819
846
  display: none;
820
847
  margin-left: 3px;
821
848
  color: var(--popup-description-color);
822
849
  }
823
850
 
824
- .djs-popup-body .entry:focus-within .djs-popup-entry-docs,
825
- .djs-popup-body .entry:focus .djs-popup-entry-docs,
826
851
  .djs-popup-body .entry:hover .djs-popup-entry-docs {
827
852
  display: inline;
828
853
  }
@@ -832,6 +857,24 @@
832
857
  margin: 2px;
833
858
  }
834
859
 
860
+ .djs-popup-footer-docs {
861
+ display: inline-flex;
862
+ align-items: center;
863
+ gap: 3px;
864
+ font-size: var(--popup-secondary-font-size);
865
+ color: var(--popup-description-color);
866
+ text-decoration: none;
867
+ }
868
+
869
+ .djs-popup-footer-docs:hover {
870
+ text-decoration: underline;
871
+ }
872
+
873
+ .djs-popup-footer-docs svg {
874
+ vertical-align: middle;
875
+ margin: 2px;
876
+ }
877
+
835
878
  /**
836
879
  * palette styles
837
880
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * bpmn-js - bpmn-modeler v18.20.0
2
+ * bpmn-js - bpmn-modeler v18.21.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-07-06
11
+ * Date: 2026-07-08
12
12
  */
13
13
  (function (global, factory) {
14
14
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
@@ -4842,6 +4842,8 @@
4842
4842
  * @typedef {import('./Types.js').ParentLike } ParentLike
4843
4843
  * @typedef {import('./Types.js').ShapeLike} ShapeLike
4844
4844
  *
4845
+ * @typedef {ShapeLike|ConnectionLike|RootLike|string} ElementOrIdentifier
4846
+ *
4845
4847
  * @typedef { {
4846
4848
  * container?: HTMLElement;
4847
4849
  * deferUpdate?: boolean;
@@ -5373,26 +5375,40 @@
5373
5375
 
5374
5376
 
5375
5377
  /**
5376
- * Returns the plane which contains the given element.
5378
+ * Returns the plane which contains the given element(s).
5377
5379
  *
5378
- * @param {ShapeLike|ConnectionLike|string} element The element or its ID.
5380
+ * If multiple elements are passed, the shared plane is returned
5381
+ * (or undefined if no shared plane exists).
5382
+ *
5383
+ * @param {ElementOrIdentifier|ElementOrIdentifier[]} element
5379
5384
  *
5380
5385
  * @return {RootLike|undefined} The root of the element.
5381
5386
  */
5382
5387
  Canvas.prototype.findRoot = function(element) {
5383
- if (typeof element === 'string') {
5384
- element = this._elementRegistry.get(element);
5385
- }
5386
5388
 
5387
- if (!element) {
5388
- return;
5389
- }
5389
+ const elements = this._resolveElements(element);
5390
+
5391
+ let root;
5392
+
5393
+ for (const element of elements) {
5394
+ const plane = this._findPlaneForRoot(
5395
+ findRoot(element)
5396
+ );
5397
+
5398
+ if (!plane) {
5399
+ return;
5400
+ }
5390
5401
 
5391
- const plane = this._findPlaneForRoot(
5392
- findRoot(element)
5393
- ) || {};
5402
+ if (root && plane.rootElement !== root) {
5394
5403
 
5395
- return plane.rootElement;
5404
+ // no shared root
5405
+ return undefined;
5406
+ }
5407
+
5408
+ root = plane.rootElement;
5409
+ }
5410
+
5411
+ return root;
5396
5412
  };
5397
5413
 
5398
5414
  /**
@@ -5412,6 +5428,33 @@
5412
5428
  });
5413
5429
  };
5414
5430
 
5431
+ /**
5432
+ * @param {ElementOrIdentifier} element
5433
+ *
5434
+ * @return {ShapeLike|ConnectionLike|RootLike|undefined}
5435
+ */
5436
+ Canvas.prototype._resolveElement = function(element) {
5437
+
5438
+ if (typeof element === 'string') {
5439
+ return this._elementRegistry.get(element);
5440
+ }
5441
+
5442
+ return element;
5443
+ };
5444
+
5445
+ /**
5446
+ * @param {ElementOrIdentifier|ElementOrIdentifier[]} elements
5447
+ *
5448
+ * @return {Array<ShapeLike|ConnectionLike|RootLike>}
5449
+ */
5450
+ Canvas.prototype._resolveElements = function(elements) {
5451
+
5452
+ if (!isArray$2(elements)) {
5453
+ elements = [ elements ];
5454
+ }
5455
+
5456
+ return elements.map(element => this._resolveElement(element)).filter(Boolean);
5457
+ };
5415
5458
 
5416
5459
  /**
5417
5460
  * Returns the html element that encloses the
@@ -5429,9 +5472,7 @@
5429
5472
  Canvas.prototype._updateMarker = function(element, marker, add) {
5430
5473
  let container;
5431
5474
 
5432
- if (!element.id) {
5433
- element = this._elementRegistry.get(element);
5434
- }
5475
+ element = this._resolveElement(element);
5435
5476
 
5436
5477
  element.markers = element.markers || new Set();
5437
5478
 
@@ -5514,9 +5555,7 @@
5514
5555
  * @param {string} marker The marker.
5515
5556
  */
5516
5557
  Canvas.prototype.hasMarker = function(element, marker) {
5517
- if (!element.id) {
5518
- element = this._elementRegistry.get(element);
5519
- }
5558
+ element = this._resolveElement(element);
5520
5559
 
5521
5560
  if (!element.markers) {
5522
5561
  return false;
@@ -5613,9 +5652,7 @@
5613
5652
  */
5614
5653
  Canvas.prototype.removeRootElement = function(rootElement) {
5615
5654
 
5616
- if (typeof rootElement === 'string') {
5617
- rootElement = this._elementRegistry.get(rootElement);
5618
- }
5655
+ rootElement = this._resolveElement(rootElement);
5619
5656
 
5620
5657
  const plane = this._findPlaneForRoot(rootElement);
5621
5658
 
@@ -6112,34 +6149,44 @@
6112
6149
  };
6113
6150
 
6114
6151
  /**
6115
- * Scrolls the viewbox to contain the given element.
6152
+ * Scrolls the viewbox to contain the given element or elements.
6116
6153
  * Optionally specify a padding to be applied to the edges.
6117
6154
  *
6118
- * @param {ShapeLike|ConnectionLike|string} element The element to scroll to or its ID.
6155
+ * The canvas navigates to the shared root of the element(s). If no shared
6156
+ * root can be found, no navigation is performed.
6157
+ *
6158
+ * @param {ElementOrIdentifier|ElementOrIdentifier[]} element The element(s) to scroll to or their ID(s).
6119
6159
  * @param {RectTRBL|number} [padding=100] The padding to be applied. Can also specify top, bottom, left and right.
6120
6160
  */
6121
6161
  Canvas.prototype.scrollToElement = function(element, padding) {
6122
6162
  let defaultPadding = 100;
6123
6163
 
6124
- if (typeof element === 'string') {
6125
- element = this._elementRegistry.get(element);
6164
+ const elements = this._resolveElements(element);
6165
+
6166
+ if (!elements.length) {
6167
+ return;
6126
6168
  }
6127
6169
 
6128
- // set to correct rootElement
6129
- const rootElement = this.findRoot(element);
6170
+ const rootElement = this.findRoot(elements);
6171
+
6172
+ if (!rootElement) {
6173
+ return;
6174
+ }
6130
6175
 
6131
6176
  if (rootElement !== this.getRootElement()) {
6132
6177
  this.setRootElement(rootElement);
6133
6178
  }
6134
6179
 
6135
- // element is rootElement, do not change viewport
6136
- if (rootElement === element) {
6180
+ const visibleElements = elements.filter(e => e != rootElement);
6181
+
6182
+ if (!visibleElements.length) {
6137
6183
  return;
6138
6184
  }
6139
6185
 
6140
6186
  if (!padding) {
6141
6187
  padding = {};
6142
6188
  }
6189
+
6143
6190
  if (typeof padding === 'number') {
6144
6191
  defaultPadding = padding;
6145
6192
  }
@@ -6151,7 +6198,7 @@
6151
6198
  left: padding.left || defaultPadding
6152
6199
  };
6153
6200
 
6154
- const elementBounds = getBBox(element),
6201
+ const elementBounds = getBBox(visibleElements),
6155
6202
  elementTrbl = asTRBL(elementBounds),
6156
6203
  viewboxBounds = this.viewbox(),
6157
6204
  zoom = this.zoom();
@@ -22458,7 +22505,7 @@
22458
22505
  };
22459
22506
 
22460
22507
  // document wide unique overlay ids
22461
- var ids$1 = new IdGenerator('ov');
22508
+ var ids$2 = new IdGenerator('ov');
22462
22509
 
22463
22510
  var LOW_PRIORITY$q = 500;
22464
22511
 
@@ -22590,7 +22637,7 @@
22590
22637
  this._canvas = canvas;
22591
22638
  this._elementRegistry = elementRegistry;
22592
22639
 
22593
- this._ids = ids$1;
22640
+ this._ids = ids$2;
22594
22641
 
22595
22642
  /**
22596
22643
  * @type {OverlaysConfigDefault}
@@ -27791,11 +27838,27 @@
27791
27838
  * @typedef {import('./PopupMenuProvider.js').PopupMenuEntry} PopupMenuEntry
27792
27839
  */
27793
27840
 
27841
+ /**
27842
+ * Compute the DOM `id` of a rendered popup menu entry.
27843
+ *
27844
+ * Used to wire the entry (`role="option"`) to the search field
27845
+ * via `aria-activedescendant`.
27846
+ *
27847
+ * @param {string} idPrefix
27848
+ * @param {PopupMenuEntry} entry
27849
+ *
27850
+ * @return {string}
27851
+ */
27852
+ function getPopupMenuItemId(idPrefix, entry) {
27853
+ return `${ idPrefix }-entry-${ entry.id }`;
27854
+ }
27855
+
27794
27856
  /**
27795
27857
  * Component that renders a popup menu entry.
27796
27858
  *
27797
27859
  * @param {Object} props
27798
27860
  * @param {string} props.key
27861
+ * @param {string} props.idPrefix
27799
27862
  * @param {PopupMenuEntry} props.entry
27800
27863
  * @param {boolean} props.selected
27801
27864
  * @param {(event: MouseEvent) => void} props.onMouseEnter
@@ -27804,6 +27867,7 @@
27804
27867
  */
27805
27868
  function PopupMenuItem(props) {
27806
27869
  const {
27870
+ idPrefix,
27807
27871
  entry,
27808
27872
  selected,
27809
27873
  onMouseEnter,
@@ -27825,13 +27889,13 @@
27825
27889
  <li
27826
27890
  class=${ clsx('entry', { selected, disabled: entry.disabled }) }
27827
27891
  data-id=${ entry.id }
27892
+ id=${ getPopupMenuItemId(idPrefix, entry) }
27893
+ role="option"
27894
+ aria-selected=${ selected || undefined }
27895
+ aria-haspopup=${ entry.entries ? 'true' : undefined }
27828
27896
  title=${ entry.title }
27829
- aria-label=${ entry.title || entry.label }
27830
27897
  aria-disabled=${ entry.disabled || undefined }
27831
- tabIndex="0"
27832
27898
  onClick=${ handleClick }
27833
- onFocus=${ onMouseEnter }
27834
- onBlur=${ onMouseLeave }
27835
27899
  onMouseEnter=${ onMouseEnter }
27836
27900
  onMouseLeave=${ onMouseLeave }
27837
27901
  onDragStart=${ (event) => draggable && handleClick(event, 'dragstart') }
@@ -27851,15 +27915,17 @@
27851
27915
  ` : null }
27852
27916
 
27853
27917
  ${ entry.documentationRef && m$1`
27854
- <a class="djs-popup-entry-docs"
27918
+ <a
27919
+ class="djs-popup-entry-docs"
27855
27920
  href=${ entry.documentationRef }
27856
27921
  onClick=${ (event) => event.stopPropagation() }
27857
- title="Open element documentation"
27858
- aria-label="Open element documentation"
27922
+ title="Open entry documentation"
27923
+ tabIndex="-1"
27924
+ aria-hidden="true"
27859
27925
  target="_blank"
27860
27926
  rel="noopener"
27861
27927
  >
27862
- <svg aria-hidden="true" width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
27928
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
27863
27929
  <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"/>
27864
27930
  </svg>
27865
27931
  </a>
@@ -27897,6 +27963,11 @@
27897
27963
  * @param {PopupMenuEntry} props.selectedEntry
27898
27964
  * @param {(entry: PopupMenuEntry | null) => void} props.setSelectedEntry
27899
27965
  * @param {import('@bpmn-io/diagram-js-ui').Ref<boolean>} props.keyboardSelection
27966
+ * @param {string} props.idPrefix
27967
+ * @param {string} props.id
27968
+ * @param {string} [props.ariaLabel]
27969
+ * @param {number} [props.tabIndex]
27970
+ * @param {string} [props.ariaActivedescendant]
27900
27971
  */
27901
27972
  function PopupMenuList(props) {
27902
27973
  const {
@@ -27904,6 +27975,11 @@
27904
27975
  setSelectedEntry,
27905
27976
  groupedEntries,
27906
27977
  keyboardSelection,
27978
+ idPrefix,
27979
+ id,
27980
+ ariaLabel,
27981
+ tabIndex,
27982
+ ariaActivedescendant,
27907
27983
  ...restProps
27908
27984
  } = props;
27909
27985
 
@@ -27915,8 +27991,6 @@
27915
27991
  return;
27916
27992
  }
27917
27993
 
27918
- keyboardSelection.current = false;
27919
-
27920
27994
  const containerEl = resultsRef.current;
27921
27995
 
27922
27996
  if (!containerEl)
@@ -27929,22 +28003,60 @@
27929
28003
  }
27930
28004
  }, [ selectedEntry ]);
27931
28005
 
28006
+ const handleMouseMove = () => {
28007
+ keyboardSelection.current = false;
28008
+ };
28009
+
28010
+ const handleEntryMouseEnter = (entry) => {
28011
+
28012
+ // Prevents keyboard selection from being overridden
28013
+ // by mouse hover when `scrollIntoView` is called.
28014
+ if (keyboardSelection.current) {
28015
+ return;
28016
+ }
28017
+
28018
+ setSelectedEntry(entry);
28019
+ };
28020
+
28021
+ const handleEntryMouseLeave = () => {
28022
+ if (keyboardSelection.current) {
28023
+ return;
28024
+ }
28025
+
28026
+ setSelectedEntry(null);
28027
+ };
28028
+
27932
28029
  return m$1`
27933
- <div class="djs-popup-results" ref=${ resultsRef }>
28030
+ <div
28031
+ class="djs-popup-results"
28032
+ ref=${ resultsRef }
28033
+ id=${ id }
28034
+ role="listbox"
28035
+ aria-label=${ ariaLabel }
28036
+ tabIndex=${ tabIndex }
28037
+ aria-activedescendant=${ ariaActivedescendant }
28038
+ onMouseMove=${ handleMouseMove }
28039
+ >
27934
28040
  ${ groupedEntries.map(group => m$1`
27935
28041
  ${ group.name && m$1`
27936
- <div key=${ group.id } class="entry-header" title=${ group.name }>
28042
+ <div key=${ group.id } id=${ getGroupHeaderId(idPrefix, group) } class="entry-header" title=${ group.name }>
27937
28043
  ${ group.name }
27938
28044
  </div>
27939
28045
  ` }
27940
- <ul class="djs-popup-group" data-group=${ group.id }>
28046
+ <ul
28047
+ class="djs-popup-group"
28048
+ data-group=${ group.id }
28049
+ role="group"
28050
+ aria-labelledby=${ group.name ? getGroupHeaderId(idPrefix, group) : undefined }
28051
+ >
27941
28052
  ${ group.entries.map(entry => m$1`
27942
28053
  <${PopupMenuItem}
27943
28054
  key=${ entry.id }
28055
+ idPrefix=${ idPrefix }
27944
28056
  entry=${ entry }
27945
28057
  selected=${ entry === selectedEntry }
27946
- onMouseEnter=${ () => setSelectedEntry(entry) }
27947
- onMouseLeave=${ () => setSelectedEntry(null) }
28058
+ onMouseEnter=${ () => handleEntryMouseEnter(entry) }
28059
+ onMouseLeave=${ handleEntryMouseLeave }
27948
28060
  ...${ restProps }
27949
28061
  />
27950
28062
  `) }
@@ -27957,6 +28069,10 @@
27957
28069
 
27958
28070
  // helpers ////////////////
27959
28071
 
28072
+ function getGroupHeaderId(idPrefix, group) {
28073
+ return `${ idPrefix }-group-${ group.id }`;
28074
+ }
28075
+
27960
28076
  function scrollIntoView(el) {
27961
28077
  if (typeof el.scrollIntoViewIfNeeded === 'function') {
27962
28078
  el.scrollIntoViewIfNeeded();
@@ -27968,6 +28084,8 @@
27968
28084
  }
27969
28085
  }
27970
28086
 
28087
+ const ids$1 = new IdGenerator('djs-popup');
28088
+
27971
28089
  /**
27972
28090
  * @typedef {import('./PopupMenuProvider.js').PopupMenuEntry} PopupMenuEntry
27973
28091
  * @typedef {import('./PopupMenuProvider.js').PopupMenuActionEntry} PopupMenuActionEntry
@@ -28084,6 +28202,12 @@
28084
28202
  */
28085
28203
  const keyboardSelection = A(false);
28086
28204
 
28205
+ // per instance basis to generate field IDs
28206
+ const idPrefix = T(() => ids$1.next(), []);
28207
+
28208
+ const resultsId = `${ idPrefix }-results`;
28209
+ const activeDescendantId = selectedEntry && getPopupMenuItemId(idPrefix, selectedEntry);
28210
+
28087
28211
  y(() => {
28088
28212
  const restore = restoreSelection.current;
28089
28213
 
@@ -28217,6 +28341,7 @@
28217
28341
  position=${ position }
28218
28342
  width=${ width }
28219
28343
  scale=${ scale }
28344
+ navigationStack=${ navigationStack }
28220
28345
  >
28221
28346
  ${ displayHeader && m$1`
28222
28347
  <${PopupMenuHeader}
@@ -28241,7 +28366,16 @@
28241
28366
  <svg class="djs-popup-search-icon" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
28242
28367
  <path fill-rule="evenodd" clip-rule="evenodd" d="M9.0325 8.5H9.625L13.3675 12.25L12.25 13.3675L8.5 9.625V9.0325L8.2975 8.8225C7.4425 9.5575 6.3325 10 5.125 10C2.4325 10 0.25 7.8175 0.25 5.125C0.25 2.4325 2.4325 0.25 5.125 0.25C7.8175 0.25 10 2.4325 10 5.125C10 6.3325 9.5575 7.4425 8.8225 8.2975L9.0325 8.5ZM1.75 5.125C1.75 6.9925 3.2575 8.5 5.125 8.5C6.9925 8.5 8.5 6.9925 8.5 5.125C8.5 3.2575 6.9925 1.75 5.125 1.75C3.2575 1.75 1.75 3.2575 1.75 5.125Z" fill="#22242A"/>
28243
28368
  </svg>
28244
- <input type="text" spellcheck=${ false } aria-label="${ title || 'Search' }" />
28369
+ <input
28370
+ type="text"
28371
+ spellcheck=${ false }
28372
+ role="combobox"
28373
+ aria-label="${ title || 'Search' }"
28374
+ aria-expanded="true"
28375
+ aria-controls=${ resultsId }
28376
+ aria-activedescendant=${ activeDescendantId || undefined }
28377
+ aria-autocomplete="list"
28378
+ />
28245
28379
  </div>
28246
28380
  ` }
28247
28381
 
@@ -28257,7 +28391,31 @@
28257
28391
  setSelectedEntry=${ setSelectedEntry }
28258
28392
  keyboardSelection=${ keyboardSelection }
28259
28393
  onAction=${ handleEntryAction }
28394
+ idPrefix=${ idPrefix }
28395
+ id=${ resultsId }
28396
+ ariaLabel=${ title || 'Results' }
28397
+ tabIndex=${ searchable ? -1 : 0 }
28398
+ ariaActivedescendant=${ searchable ? undefined : (activeDescendantId || undefined) }
28260
28399
  />
28400
+
28401
+ ${ selectedEntry && selectedEntry.documentationRef && m$1`
28402
+ <div class="djs-popup-footer">
28403
+ <a
28404
+ class="djs-popup-footer-docs"
28405
+ href=${ selectedEntry.documentationRef }
28406
+ onClick=${ (event) => event.stopPropagation() }
28407
+ aria-label=${ selectedEntry.label ? `Open entry documentation for ${ selectedEntry.label }` : 'Open entry documentation' }
28408
+ target="_blank"
28409
+ rel="noopener"
28410
+ >
28411
+ Open entry documentation
28412
+
28413
+ <svg aria-hidden="true" width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
28414
+ <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"/>
28415
+ </svg>
28416
+ </a>
28417
+ </div>
28418
+ ` }
28261
28419
  </div>
28262
28420
  ` }
28263
28421
  ${ emptyPlaceholder && entriesToShow.length === 0 && m$1`
@@ -28279,6 +28437,7 @@
28279
28437
  onKeyup,
28280
28438
  className,
28281
28439
  children,
28440
+ navigationStack,
28282
28441
  position: positionGetter
28283
28442
  } = props;
28284
28443
 
@@ -28297,7 +28456,6 @@
28297
28456
  popupEl.style.top = `${position.y}px`;
28298
28457
  }, [ popupRef.current, positionGetter ]);
28299
28458
 
28300
- // initial focus
28301
28459
  _(() => {
28302
28460
  const popupEl = popupRef.current;
28303
28461
 
@@ -28305,10 +28463,15 @@
28305
28463
  return;
28306
28464
  }
28307
28465
 
28308
- const inputEl = popupEl.querySelector('input');
28466
+ // focus the combobox (search) if present, otherwise the listbox
28467
+ // itself which then holds focus + `aria-activedescendant`;
28468
+ // fall back to the popup element (e.g. empty placeholder)
28469
+ const focusEl = popupEl.querySelector('input')
28470
+ || popupEl.querySelector('[role="listbox"]')
28471
+ || popupEl;
28309
28472
 
28310
- (inputEl || popupEl).focus();
28311
- }, []);
28473
+ focusEl.focus();
28474
+ }, [ navigationStack ]);
28312
28475
 
28313
28476
  // global <Escape> / blur handlers
28314
28477
  y(() => {