PopupMenuList.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import {
  2. html,
  3. useMemo,
  4. useLayoutEffect,
  5. useRef
  6. } from '../../ui';
  7. import PopupMenuItem from './PopupMenuItem';
  8. /**
  9. * Component that renders a popup menu entry list.
  10. *
  11. * @param {Array} entries
  12. * @param {Object} selectedEntry
  13. * @param {function} setSelectedEntry
  14. * @param {function} onAction
  15. * @param {Object} resultsRef
  16. */
  17. export default function PopupMenuList(props) {
  18. const {
  19. selectedEntry,
  20. setSelectedEntry,
  21. entries,
  22. ...restProps
  23. } = props;
  24. const resultsRef = useRef();
  25. const groups = useMemo(() => groupEntries(entries), [ entries ]);
  26. // scroll to selected result
  27. useLayoutEffect(() => {
  28. const containerEl = resultsRef.current;
  29. if (!containerEl)
  30. return;
  31. const selectedEl = containerEl.querySelector('.selected');
  32. if (selectedEl) {
  33. scrollIntoView(selectedEl);
  34. }
  35. }, [ selectedEntry ]);
  36. return html`
  37. <div class="djs-popup-results" ref=${ resultsRef }>
  38. ${ groups.map(group => html`
  39. ${ group.name && html`
  40. <div key=${ group.id } class="entry-header" title=${ group.name }>
  41. ${ group.name }
  42. </div>
  43. ` }
  44. <ul class="djs-popup-group" data-group=${ group.id }>
  45. ${ group.entries.map(entry => html`
  46. <${PopupMenuItem}
  47. key=${ entry.id }
  48. entry=${ entry }
  49. selected=${ entry === selectedEntry }
  50. onMouseEnter=${ () => setSelectedEntry(entry) }
  51. onMouseLeave=${ () => setSelectedEntry(null) }
  52. ...${ restProps }
  53. />
  54. `) }
  55. </ul>
  56. `) }
  57. </div>
  58. `;
  59. }
  60. // helpers
  61. function groupEntries(entries) {
  62. const groups = [];
  63. const getGroup = group => groups.find(elem => group.id === elem.id);
  64. const containsGroup = group => !!getGroup(group);
  65. // legacy support for provider built for the old popUp menu
  66. const formatGroup = group =>
  67. typeof group === 'string' ? { id: group } : group;
  68. entries.forEach(entry => {
  69. // assume a default group when none is provided
  70. const group = entry.group ? formatGroup(entry.group) : { id: 'default' };
  71. if (!containsGroup(group)) {
  72. groups.push({ ...group, entries: [ entry ] });
  73. } else {
  74. getGroup(group).entries.push(entry);
  75. }
  76. });
  77. return groups;
  78. }
  79. // helpers ////////////////
  80. function scrollIntoView(el) {
  81. if (typeof el.scrollIntoViewIfNeeded === 'function') {
  82. el.scrollIntoViewIfNeeded();
  83. } else {
  84. el.scrollIntoView({
  85. scrollMode: 'if-needed',
  86. block: 'nearest'
  87. });
  88. }
  89. }