From 1d6879e97e6fe65d97b2d60e998e796f3a456192 Mon Sep 17 00:00:00 2001 From: Beatriz Mendes Date: Thu, 27 Oct 2022 13:14:57 +0200 Subject: [PATCH] feat(popup): update to new UI Closes #686 --- assets/diagram-js.css | 307 ++++++-- lib/features/popup-menu/PopupMenu.js | 659 ++++++++---------- lib/features/popup-menu/PopupMenuComponent.js | 279 ++++++++ lib/features/popup-menu/PopupMenuItem.js | 59 ++ lib/features/popup-menu/PopupMenuList.js | 82 +++ lib/features/popup-menu/index.js | 2 + package-lock.json | 214 +++--- package.json | 2 + 8 files changed, 1055 insertions(+), 549 deletions(-) create mode 100644 lib/features/popup-menu/PopupMenuComponent.js create mode 100644 lib/features/popup-menu/PopupMenuItem.js create mode 100644 lib/features/popup-menu/PopupMenuList.js diff --git a/assets/diagram-js.css b/assets/diagram-js.css index d882133e4..ccf9df7ae 100644 --- a/assets/diagram-js.css +++ b/assets/diagram-js.css @@ -508,73 +508,302 @@ marker.djs-dragger tspan { /** * popup styles */ -.djs-popup .entry { +.djs-popup { + position: fixed; + width: 100vw; + height: 100vh; + top: 0; + left: 0; + z-index: 200; + line-height: 1; + font-family: "IBM Plex Sans", sans-serif; + background: var(--popup-background-color); + border: solid 1px var(--popup-border-color); + border-radius: 2px; + + --color-white: #fff; + --color-black-opacity-10: hsla(0, 0%, 0%, 10%); + --color-black-opacity-25: hsla(0, 0%, 0%, 25%); + --color-grey-225-10-97: hsl(225, 10%, 97%); + --color-grey-225-10-75: hsl(225, 10%, 75%); + --color-grey-225-10-50: hsl(225, 10%, 50%); + --color-blue-205-100-50: hsl(205, 100%, 50%); + --color-blue-205-100-95: hsl(205, 100%, 95%); + --input-border-color: var(--color-grey-225-10-75); + --input-focus-background-color: var(--color-blue-205-100-95); + --input-focus-border-color: var(--color-blue-205-100-50); + --placeholder-color: var(--color-grey-225-10-50); + --hover-background-color: #F1F2F4; + --header-entry-selected-color: var(--color-blue-205-100-50); + --shadow-color: var(--color-black-opacity-25); + --text-color: #22242A; + --text-muted-color: #707585; + --text-size-base: 14px; + --text-line-height: 21px; + --header-font-weight: 500; +} + +.djs-popup .overlay { + width: min-content; + background: var(--color-white); + overflow: hidden; + position: absolute; + + box-shadow: 0px 2px 10px var(--shadow-color); + color: var(--text-color); + padding-top: 12px; + min-width: 120px; +} + +.djs-popup .search.hidden { + position: absolute; + height: 0; + top: -1000px; +} + +.djs-popup .search input { + width: 100%; + box-sizing: border-box; + font-size: var(--text-size-base); + padding: 3px 6px; + border-radius: 2px; + border: solid 1px var(--input-border-color); + line-height: var(--text-line-height); + color: var(--text-color); +} + +.djs-popup .search input:focus { + background-color: var(--input-focus-background-color); + border: solid 1px var(--input-focus-border-color); + outline: none; +} + +.djs-popup .search input::placeholder { + color: var(--placeholder-color); +} + +.djs-popup_header { + display: flex; + align-items: stretch; line-height: 20px; - white-space: nowrap; - cursor: default; + margin: 12px 12px 10px 12px; } -/* larger font for prefixed icons */ -.djs-popup .entry:before { - vertical-align: middle; - font-size: 20px; +.djs-popup .djs-popup-header { + display: flex; + align-items: stretch; + line-height: 20px; + margin: 0 12px 10px 12px; } -.djs-popup .entry > span { - vertical-align: middle; +.djs-popup .header-entry { + font-size: 19.5px; + border-radius: 2px; +} + +.djs-popup .header-entry.active { + color: var(--header-entry-selected-color); +} + +.djs-popup .header-entry.disabled { + color: inherit; +} + +.djs-popup .header-entry.selected { + background-color: var(--hover-background-color); +} + +.djs-popup .search { + margin: 10px 12px; +} + +.djs-popup .title { font-size: 14px; + font-weight: var(--header-font-weight); + flex: 1; + margin: 0; + width: max-content; } -.djs-popup .entry:hover, -.djs-popup .entry.active:hover { - background: var(--popup-header-entry-selected-background-color); +.djs-popup .search { + position: relative; + width: auto; } -.djs-popup .entry.disabled { - background: inherit; +.djs-popup .search-icon { + position: absolute; + left: 8px; + top: 7px; } -.djs-popup .djs-popup-header .entry { - display: inline-block; - padding: 2px 3px 2px 3px; +.djs-popup .search input { + padding-left: 25px; +} - border: solid 1px transparent; - border-radius: 3px; +.djs-popup .results { + margin: 0 3px 7px 12px; + list-style: none; + max-height: 280px; + overflow: overlay; + padding-right: 9px; } -.djs-popup .djs-popup-header .entry.active { - color: var(--popup-header-entry-selected-color); - border: solid 1px var(--popup-header-entry-selected-color); - background-color: var(--popup-header-entry-selected-background-color); +.djs-popup .group { + margin: 0; + padding: 0; + width: 100%; +} + +.djs-popup .entry, +.djs-popup__muted-entry, +.djs-popup .entry-header { + padding: 5px 7px; + cursor: default; + border-radius: 4px; + font-size: var(--text-size-base); } -.djs-popup-body .entry { - padding: 4px 5px; +.djs-popup .entry-header { + font-weight: var(--header-font-weight); + color: var(--text-muted-color); + padding-left: 0; } -.djs-popup-body .entry > span { - margin-left: 5px; +.djs-popup .entry-icon { + width: 16px; + margin-right: 0.5rem; + margin-bottom: -0.2rem; +} + +.djs-popup .entry-header:not(:first-child) { + margin-top: 8px; + margin-bottom: 2px; +} + +.djs-popup__muted-entry { + color: var(--text-muted-color); +} + +.djs-popup .entry { + display: flex; + flex-direction: row; + align-items: stretch; + height: min-content; +} + +.djs-popup .entry.selected { + background-color: var(--hover-background-color); +} + +.djs-popup .entry:not(:first-child) { + margin-top: 2px; +} + +.djs-popup .entry .content { + display: flex; + flex-direction: column; + flex: 1; + overflow: hidden; +} + +.djs-popup .entry .description { + font-weight: normal; + color: var(--text-muted-color); +} + +.djs-popup .entry .name, +.djs-popup .entry .description { + line-height: 1.4em; + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.djs-popup .entry .name { + font-weight: normal; + display: flex; +} + +.djs-popup .entry-content { + display: flex; + flex-direction: column; + flex: 1; + overflow: hidden; +} + +.djs-popup .entry-help { + flex: 0; + flex-direction: row; + align-items: center; + padding-left: 5px; + display: none; +} + +.djs-popup .entry:hover .djs-popup .entry-help { + display: flex; +} + +.djs-popup .entry-help svg { + vertical-align: middle; + margin: auto 2px auto 5px; +} + +.djs-popup .entry .name[class^="bpmn-icon-"]:before, +.djs-popup .entry .name[class*=" bpmn-icon-"]:before, +.djs-popup .entry .icon { + display: inline-block; + font-size: 1.4em; + vertical-align: middle; + margin-right: 0.5rem; } .djs-popup-body { - background-color: var(--popup-body-background-color); + flex-direction: column; + width: auto; +} + +.djs-popup *::-webkit-scrollbar { + width: 6px; +} + +.djs-popup *::-webkit-scrollbar-thumb { + border-radius: 3px; + background-color: rgba(0, 0, 0, 0.2); } -.djs-popup-header { - border-bottom: 1px solid var(--popup-header-separator-color); +.djs-popup *::-webkit-scrollbar-track { + box-shadow: none; + background: transparent1; + margin: 0; + padding: 5px; +} + +.djs-popup .no-results { + font-size: 14px; + padding: 0 12px 12px 12px; + color: var(--text-muted-color); +} + +.djs-popup .docs { + flex: 0; + flex-direction: row; + align-items: center; + padding-left: 5px; + display: none; } -.djs-popup-header .entry { - margin: 1px; - margin-left: 3px; +.djs-popup .entry:hover .docs { + display: flex; } -.djs-popup-header .entry:last-child { - margin-right: 3px; +.djs-popup .entry .docs svg { + vertical-align: middle; + margin: auto 2px auto 5px; } /** - * popup / palette styles + * palette styles */ .djs-palette { background: var(--palette-background-color); @@ -582,12 +811,6 @@ marker.djs-dragger tspan { border-radius: 2px; } -.djs-popup { - background: var(--popup-background-color); - border: solid 1px var(--popup-border-color); - border-radius: 2px; -} - /** * touch */ diff --git a/lib/features/popup-menu/PopupMenu.js b/lib/features/popup-menu/PopupMenu.js index 61dbc2b61..46dc03051 100644 --- a/lib/features/popup-menu/PopupMenu.js +++ b/lib/features/popup-menu/PopupMenu.js @@ -1,25 +1,19 @@ + +import { + domify, + remove as domRemove, + attr as domAttr +} from 'min-dom'; + import { - assign, forEach, isFunction, - isDefined, omit, - size + isDefined } from 'min-dash'; -import { - assignStyle, - delegate as domDelegate, - domify as domify, - classes as domClasses, - attr as domAttr, - query as domQuery, - remove as domRemove -} from 'min-dom'; +import PopupMenuComponent from './PopupMenuComponent'; -import { - escapeCSS -} from '../../util/EscapeUtil'; var DATA_REF = 'data-id'; @@ -45,7 +39,11 @@ var DEFAULT_PRIORITY = 1000; * @class * @constructor */ -export default function PopupMenu(config, eventBus, canvas) { +export default function PopupMenu(config, eventBus, canvas, diagramJSui) { + this._eventBus = eventBus; + this._canvas = canvas; + this._diagramJSui = diagramJSui; + this._current = {}; var scale = isDefined(config && config.scale) ? config.scale : { min: 1, @@ -56,77 +54,68 @@ export default function PopupMenu(config, eventBus, canvas) { scale: scale }; - this._eventBus = eventBus; - this._canvas = canvas; - this._providers = {}; - this._current = {}; -} - -PopupMenu.$inject = [ - 'config.popupMenu', - 'eventBus', - 'canvas' -]; - -/** - * Registers a popup menu provider - * - * @param {string} id - * @param {number} [priority=1000] - * @param {Object} provider - * - * @example - * const popupMenuProvider = { - * getPopupMenuEntries: function(element) { - * return { - * 'entry-1': { - * label: 'My Entry', - * action: function() { alert("I have been clicked!"); } - * } - * } - * } - * }; - * - * popupMenu.registerProvider('myMenuID', popupMenuProvider); - */ -PopupMenu.prototype.registerProvider = function(id, priority, provider) { - if (!provider) { - provider = priority; - priority = DEFAULT_PRIORITY; - } - this._eventBus.on('popupMenu.getProviders.' + id, priority, function(event) { - event.providers.push(provider); + eventBus.on('diagram.destroy', () => { + this._destroy(); }); -}; -/** - * Determine if the popup menu has entries. - * - * @return {boolean} true if empty - */ -PopupMenu.prototype.isEmpty = function(element, providerId) { - if (!element) { - throw new Error('element parameter is missing'); - } + eventBus.on('element.changed', event => { - if (!providerId) { - throw new Error('providerId parameter is missing'); - } + const element = this.isOpen() && this._current.element; - var providers = this._getProviders(providerId); + if (event.element === element) { + this._render(); + } + }); - if (!providers) { - return true; - } +} - var entries = this._getEntries(element, providers), - headerEntries = this._getHeaderEntries(element, providers); +PopupMenu.$inject = [ + 'config.popupMenu', + 'eventBus', + 'canvas', + 'diagramJSui' +]; - var hasEntries = size(entries) > 0, - hasHeaderEntries = headerEntries && size(headerEntries) > 0; - return !hasEntries && !hasHeaderEntries; +PopupMenu.prototype._render = function() { + + const { + position: _position, + className, + entries, + headerEntries, + options + } = this._current; + + const entriesArray = [ + ...Object.entries(entries).map( + ([ key, value ]) => ({ id: key, ...value }) + ) + ]; + + const position = _position && ( + (container) => this._ensureVisible(container, _position) + ); + + const scale = this._updateScale(this._current.container); + + const onClose = result => this.close(result); + + this._diagramJSui.render(this._diagramJSui.html` + <${PopupMenuComponent} + onClose=${ onClose } + position=${ position } + className=${ className } + entries=${ entriesArray } + headerEntries=${ headerEntries } + diagramJSui=${ this._diagramJSui } + scale=${ scale } + ...${{ ...options }} + /> + `, + this._current.container + ); }; @@ -139,16 +128,13 @@ PopupMenu.prototype.isEmpty = function(element, providerId) { * * @return {Object} popup menu instance */ -PopupMenu.prototype.open = function(element, id, position) { - - var providers = this._getProviders(id); - +PopupMenu.prototype.open = function(element, providerId, position, options) { if (!element) { throw new Error('Element is missing'); } - if (!providers || !providers.length) { - throw new Error('No registered providers for: ' + id); + if (!providerId) { + throw new Error('No registered providers for: ' + providerId); } if (!position) { @@ -158,45 +144,52 @@ PopupMenu.prototype.open = function(element, id, position) { if (this.isOpen()) { this.close(); } + const { + entries, + headerEntries + } = this._getContext(element, providerId); + + this._current = { + position, + className: providerId, + element, + entries, + headerEntries, + container: this._createContainer({ provider: providerId }), + options + }; this._emit('open'); - var current = this._current = { - className: id, - element: element, - position: position - }; + this._bindAutoClose(); - var entries = this._getEntries(element, providers), - headerEntries = this._getHeaderEntries(element, providers); + this._render(); - current.entries = assign({}, entries, headerEntries); - current.container = this._createContainer(id); +}; - if (size(headerEntries)) { - current.container.appendChild( - this._createEntries(headerEntries, 'djs-popup-header') - ); - } - if (size(entries)) { - current.container.appendChild( - this._createEntries(entries, 'djs-popup-body') - ); +PopupMenu.prototype._getContext = function(element, provider) { + + const providers = this._getProviders(provider); + + if (!providers || !providers.length) { + throw new Error('No registered providers for: ' + provider); } - var canvas = this._canvas, - parent = canvas.getContainer(); + const entries = this._getEntries(element, providers); + const headerEntries = this._getHeaderEntries(element, providers); - this._attachContainer(current.container, parent, position.cursor); - this._bindAutoClose(); + return { + entries, + headerEntries, + empty: !( + Object.keys(entries).length || + Object.keys(headerEntries).length + ) + }; }; - -/** - * Removes the popup menu and unbinds the event handlers. - */ PopupMenu.prototype.close = function() { if (!this.isOpen()) { @@ -205,44 +198,182 @@ PopupMenu.prototype.close = function() { this._emit('close'); - this._unbindAutoClose(); - domRemove(this._current.container); + this.reset(); + this._current.container = null; }; +PopupMenu.prototype.reset = function() { + this._diagramJSui.render(null, this._current.container); +}; + +PopupMenu.prototype._emit = function(event, payload) { + this._eventBus.fire(`popupMenu.${ event }`, payload); +}; + +PopupMenu.prototype._createContainer = function(config) { + + let parent = config && config.parent || 'body'; + + + if (typeof parent === 'string') { + parent = document.querySelector(parent); + } + + const container = domify(`
`); + + parent.appendChild(container); + + return container; +}; /** - * Determine if an open popup menu exist. - * - * @return {boolean} true if open + * Set up listener to close popup automatically on certain events. */ -PopupMenu.prototype.isOpen = function() { - return !!this._current.container; +PopupMenu.prototype._bindAutoClose = function() { + this._eventBus.once(CLOSE_EVENTS, this.close, this); }; /** - * Trigger an action associated with an entry. + * Remove the auto-closing listener. +*/ +PopupMenu.prototype._unbindAutoClose = function() { + this._eventBus.off(CLOSE_EVENTS, this.close, this); +}; + + +PopupMenu.prototype._destroy = function() { + this._current.container && domRemove(this._current.container); +}; + + +/** + * Updates popup style.transform with respect to the config and zoom level. * - * @param {Object} event + * @method _updateScale * - * @return the result of the action callback, if any + * @param {Object} container */ -PopupMenu.prototype.trigger = function(event) { +PopupMenu.prototype._updateScale = function(container) { + var zoom = this._canvas.zoom(); - // silence other actions - event.preventDefault(); + var scaleConfig = this._config.scale, + minScale, + maxScale, + scale = zoom; - var element = event.delegateTarget || event.target, - entryId = domAttr(element, DATA_REF); + if (scaleConfig !== true) { - var entry = this._getEntry(entryId); + if (scaleConfig === false) { + minScale = 1; + maxScale = 1; + } else { + minScale = scaleConfig.min; + maxScale = scaleConfig.max; + } + + if (isDefined(minScale) && zoom < minScale) { + scale = minScale; + } + + if (isDefined(maxScale) && zoom > maxScale) { + scale = maxScale; + } - if (entry.action) { - return entry.action.call(null, event, entry); } + + return scale; + }; +PopupMenu.prototype._ensureVisible = function(container, position) { + var documentBounds = document.documentElement.getBoundingClientRect(); + var containerBounds = container.getBoundingClientRect(); + + var overAxis = {}, + left = position.x, + top = position.y; + + if (position.x + containerBounds.width > documentBounds.width) { + overAxis.x = true; + } + + if (position.y + containerBounds.height > documentBounds.height) { + overAxis.y = true; + } + + if (overAxis.x && overAxis.y) { + left = position.x - containerBounds.width; + top = position.y - containerBounds.height; + } else if (overAxis.x) { + left = position.x - containerBounds.width; + top = position.y; + } else if (overAxis.y && position.y < containerBounds.height) { + left = position.x; + top = 10; + } else if (overAxis.y) { + left = position.x; + top = position.y - containerBounds.height; + } + + return { + x: left, + y: top + }; +}; + +PopupMenu.prototype.isEmpty = function(element, providerId) { + if (!element) { + throw new Error('element parameter is missing'); + } + + if (!providerId) { + throw new Error('providerId parameter is missing'); + } + + const providers = this._getProviders(providerId); + + if (!providers || !providers.length) { + return true; + } + + return this._getContext(element, providerId).empty; +}; + +/** + * Registers a popup menu provider + * + * @param {string} id + * @param {number} [priority=1000] + * @param {Object} provider + * + * @example + * const popupMenuProvider = { + * getPopupMenuEntries: function(element) { + * return { + * 'entry-1': { + * label: 'My Entry', + * action: function() { alert("I have been clicked!"); } + * } + * } + * } + * }; + * + * popupMenu.registerProvider('myMenuID', popupMenuProvider); + */ +PopupMenu.prototype.registerProvider = function(id, priority, provider) { + if (!provider) { + provider = priority; + priority = DEFAULT_PRIORITY; + } + + this._eventBus.on('popupMenu.getProviders.' + id, priority, function(event) { + event.providers.push(provider); + }); +}; + + PopupMenu.prototype._getProviders = function(id) { var event = this._eventBus.createEvent({ @@ -331,278 +462,54 @@ PopupMenu.prototype._getHeaderEntries = function(element, providers) { }; -/** - * Gets an entry instance (either entry or headerEntry) by id. - * - * @param {string} entryId - * - * @return {Object} entry instance - */ -PopupMenu.prototype._getEntry = function(entryId) { - - var entry = this._current.entries[entryId]; - - if (!entry) { - throw new Error('entry not found'); - } - - return entry; -}; - -PopupMenu.prototype._emit = function(eventName) { - this._eventBus.fire('popupMenu.' + eventName); -}; - -/** - * Creates the popup menu container. - * - * @return {Object} a DOM container - */ -PopupMenu.prototype._createContainer = function(id) { - var container = domify('
'), - position = this._current.position, - className = this._current.className; - - assignStyle(container, { - position: 'absolute', - left: position.x + 'px', - top: position.y + 'px', - visibility: 'hidden' - }); - - domClasses(container).add(className); - - domAttr(container, 'data-popup', id); - - return container; -}; - /** - * Attaches the container to the DOM. + * Determine if an open popup menu exist. * - * @param {Object} container - * @param {Object} parent + * @return {boolean} true if open */ -PopupMenu.prototype._attachContainer = function(container, parent, cursor) { - var self = this; - - // Event handler - domDelegate.bind(container, '.entry' ,'click', function(event) { - self.trigger(event); - }); - - this._updateScale(container); - - // Attach to DOM - parent.appendChild(container); - - if (cursor) { - this._assureIsInbounds(container, cursor); - } - - // display after position adjustment to avoid flickering - assignStyle(container, { visibility: 'visible' }); +PopupMenu.prototype.isOpen = function() { + return !!this._current.container; }; /** - * Updates popup style.transform with respect to the config and zoom level. + * Trigger an action associated with an entry. * - * @method _updateScale + * @param {Object} event * - * @param {Object} container + * @return the result of the action callback, if any */ -PopupMenu.prototype._updateScale = function(container) { - var zoom = this._canvas.zoom(); - - var scaleConfig = this._config.scale, - minScale, - maxScale, - scale = zoom; - - if (scaleConfig !== true) { - - if (scaleConfig === false) { - minScale = 1; - maxScale = 1; - } else { - minScale = scaleConfig.min; - maxScale = scaleConfig.max; - } - - if (isDefined(minScale) && zoom < minScale) { - scale = minScale; - } - - if (isDefined(maxScale) && zoom > maxScale) { - scale = maxScale; - } - - } - - setTransform(container, 'scale(' + scale + ')'); -}; - +PopupMenu.prototype.trigger = function(event) { -/** - * Make sure that the menu is always fully shown - * - * @method function - * - * @param {Object} container - * @param {Position} cursor {x, y} - */ -PopupMenu.prototype._assureIsInbounds = function(container, cursor) { - var canvas = this._canvas, - clientRect = canvas._container.getBoundingClientRect(); - - var containerX = container.offsetLeft, - containerY = container.offsetTop, - containerWidth = container.scrollWidth, - containerHeight = container.scrollHeight, - overAxis = {}, - left, top; - - var cursorPosition = { - x: cursor.x - clientRect.left, - y: cursor.y - clientRect.top - }; + // silence other actions + event.preventDefault(); - if (containerX + containerWidth > clientRect.width) { - overAxis.x = true; - } + var element = event.delegateTarget || event.target, + entryId = domAttr(element, DATA_REF); - if (containerY + containerHeight > clientRect.height) { - overAxis.y = true; - } + var entry = this._getEntry(entryId); - if (overAxis.x && overAxis.y) { - left = cursorPosition.x - containerWidth + 'px'; - top = cursorPosition.y - containerHeight + 'px'; - } else if (overAxis.x) { - left = cursorPosition.x - containerWidth + 'px'; - top = cursorPosition.y + 'px'; - } else if (overAxis.y && cursorPosition.y < containerHeight) { - left = cursorPosition.x + 'px'; - top = 10 + 'px'; - } else if (overAxis.y) { - left = cursorPosition.x + 'px'; - top = cursorPosition.y - containerHeight + 'px'; + if (entry.action) { + return entry.action.call(null, event, entry); } - - assignStyle(container, { left: left, top: top }, { 'zIndex': 1000 }); }; - /** - * Creates a list of entries and returns them as a DOM container. - * - * @param {Array} entries an array of entry objects - * @param {string} className the class name of the entry container - * - * @return {Object} a DOM container - */ -PopupMenu.prototype._createEntries = function(entries, className) { - - var entriesContainer = domify('
'), - self = this; - - domClasses(entriesContainer).add(className); - - forEach(entries, function(entry, id) { - var entryContainer = self._createEntry(entry, id), - grouping = entry.group || 'default', - groupContainer = domQuery('[data-group=' + escapeCSS(grouping) + ']', entriesContainer); - - if (!groupContainer) { - groupContainer = domify('
'); - domAttr(groupContainer, 'data-group', grouping); - - entriesContainer.appendChild(groupContainer); - } - - groupContainer.appendChild(entryContainer); - }); - - return entriesContainer; -}; - - -/** - * Creates a single entry and returns it as a DOM container. + * Gets an entry instance (either entry or headerEntry) by id. * - * @param {Object} entry + * @param {string} entryId * - * @return {Object} a DOM container + * @return {Object} entry instance */ -PopupMenu.prototype._createEntry = function(entry, id) { - - var entryContainer = domify('
'), - entryClasses = domClasses(entryContainer); - - entryClasses.add('entry'); - - if (entry.className) { - entry.className.split(' ').forEach(function(className) { - entryClasses.add(className); - }); - } - - domAttr(entryContainer, DATA_REF, id); - - if (entry.label) { - var label = domify(''); - label.textContent = entry.label; - entryContainer.appendChild(label); - } - - if (entry.imageUrl) { - var image = domify(''); - domAttr(image, 'src', entry.imageUrl); - - entryContainer.appendChild(image); - } +PopupMenu.prototype._getEntry = function(entryId) { - if (entry.active === true) { - entryClasses.add('active'); - } + var entry = this._current.entries[entryId]; - if (entry.disabled === true) { - entryClasses.add('disabled'); - } - if (entry.title) { - entryContainer.title = entry.title; + if (!entry) { + throw new Error('entry not found'); } - return entryContainer; -}; - - -/** - * Set up listener to close popup automatically on certain events. - */ -PopupMenu.prototype._bindAutoClose = function() { - this._eventBus.once(CLOSE_EVENTS, this.close, this); -}; - - -/** - * Remove the auto-closing listener. - */ -PopupMenu.prototype._unbindAutoClose = function() { - this._eventBus.off(CLOSE_EVENTS, this.close, this); -}; - - - -// helpers ///////////////////////////// - -function setTransform(element, transform) { - element.style['transform-origin'] = 'top left'; - - [ '', '-ms-', '-webkit-' ].forEach(function(prefix) { - element.style[prefix + 'transform'] = transform; - }); -} \ No newline at end of file + return entry; +}; \ No newline at end of file diff --git a/lib/features/popup-menu/PopupMenuComponent.js b/lib/features/popup-menu/PopupMenuComponent.js new file mode 100644 index 000000000..5c589b5dc --- /dev/null +++ b/lib/features/popup-menu/PopupMenuComponent.js @@ -0,0 +1,279 @@ +import PopupMenuList from './PopupMenuList'; +import classnames from 'classnames'; +import { isDefined } from 'min-dash'; + +/** + * A preact component that renders the popup menus. + * + * @param {function} onClose + * @param {function} position + * @param {string} className + * @param {Array} entries + * @param {Object} headerEntries + * @param {Object} diagramJSui + * @param {number} scale + * @param {string} [title] + * @param {boolean} [search] + * @param {number} [minWidth] + */ +export default function PopupMenuComponent(props) { + const { onClose, headerEntries } = props; + const html = props.diagramJSui.html; + + const { + useEffect, + useRef, + useState, + useLayoutEffect + } = props.diagramJSui.getHooks(); + + const onSelect = (event, entry, shouldClose = true) => { + entry.action(event, entry); + + shouldClose && onClose(); + }; + + const isSearchable = () => { + if (isDefined(props.search)) + return !!props.search; + + return props.entries.length > 5; + }; + + const inputRef = useRef(); + const resultsRef = useRef(); + + const [ value, setValue ] = useState(''); + + const [ entries, setEntries ] = useState(props.entries); + const [ selectedEntry, setSelectedEntry ] = useState(entries[0]); + + // filter entries on value change + useEffect(() => { + const filter = entry => { + if (!value) { + return true; + } + + const search = [ + entry.name, + entry.description || '', + entry.label || '', + entry.id || '' + ] + .join('---') + .toLowerCase(); + + return value + .toLowerCase() + .split(/\s/g) + .every(term => search.includes(term)); + }; + + if (!isSearchable()) { + return; + } + + const entries = props.entries.filter(filter); + + if (!entries.includes(selectedEntry) && !selectedEntry) { + setSelectedEntry(entries[0]); + } + + setEntries(entries); + + }, [ value, selectedEntry, props.entries ]); + + // focus input on initial mount + useLayoutEffect(() => { + inputRef.current && inputRef.current.focus(); + }, []); + + // scroll to keyboard selected result + useLayoutEffect(() => { + const containerEl = resultsRef.current; + + if (!containerEl) + return; + + const selectedEl = containerEl.querySelector('.selected'); + + if (selectedEl) { + scrollIntoView(selectedEl); + } + }, [ selectedEntry ]); + + // handle keyboard seleciton + const keyboardSelect = direction => { + const idx = entries.indexOf(selectedEntry); + + let nextIdx = idx + direction; + + if (nextIdx < 0) { + nextIdx = entries.length - 1; + } + + if (nextIdx >= entries.length) { + nextIdx = 0; + } + + setSelectedEntry(entries[nextIdx]); + }; + + const handleKeyDown = event => { + if (event.key === 'Enter' && selectedEntry) { + return onSelect(event, selectedEntry); + } + + if (event.key === 'Escape') { + return onClose(); + } + + // ARROW_UP or SHIFT + TAB navigation + if (event.key === 'ArrowUp' || (event.key === 'Tab' && event.shiftKey)) { + keyboardSelect(-1); + + return event.preventDefault(); + } + + // ARROW_DOWN or TAB navigation + if (event.key === 'ArrowDown' || event.key === 'Tab') { + keyboardSelect(1); + + return event.preventDefault(); + } + }; + + const handleKey = event => { + setValue(() => event.target.value); + }; + + const displayHeader = props.title || Object.keys(headerEntries).length > 0; + + return ( + html` + <${PopupMenuWrapper} ...${props}> + ${displayHeader && html` +
+

${props.title}

+ ${Object.entries(headerEntries).map(([ key, value ]) => html` + onSelect(event, value, false) } + title=${ `Toggle ${value.title}` } + data-id=${ key } + onMouseEnter=${ () => setSelectedEntry(headerEntries[key]) } + > + ${value.imageUrl ? html`` : null} + ${value.label ? value.label : null} + ` + )} +
` + } + ${props.entries.length && html` +
+ +
+ + + + +
+ + <${PopupMenuList} + entries=${ entries } + selectedEntry=${ selectedEntry } + setSelectedEntry=${ setSelectedEntry } + onSelect=${ onSelect } + resultsRef=${ resultsRef } + html=${ html } + /> +
+ `} + ${entries.length === 0 && html` +
No matching entries found.
+ `} + ` + ); +} + +/** + * A preact component that wraps the popup menu. + * + * @param {any} props + */ +function PopupMenuWrapper(props) { + const { onClose, className, children } = props; + const html = props.diagramJSui.html; + const { + useRef, + useLayoutEffect + } = props.diagramJSui.getHooks(); + + const overlayRef = useRef(); + + useLayoutEffect(() => { + if (typeof props.position !== 'function') { + return; + } + + const overlayEl = overlayRef.current; + const position = props.position(overlayEl); + + overlayEl.style.left = `${position.x}px`; + overlayEl.style.top = `${position.y}px`; + }, [ overlayRef.current, props.position ]); + + return (html` +
onClose() } + > +
e.stopPropagation() } + style=${getOverlayStyle(props)} + > + ${children} +
+
+ ` + ); +} + +// helpers ////////////////////// +function scrollIntoView(el) { + if (typeof el.scrollIntoViewIfNeeded === 'function') { + el.scrollIntoViewIfNeeded(); + } else { + el.scrollIntoView({ + scrollMode: 'if-needed', + block: 'nearest' + }); + } +} + +function getOverlayStyle(props) { + return { + transform: `scale(${props.scale})`, + width: `${props.width}px` + }; +} + +function getHeaderClasses(entry, selected) { + return classnames( + 'header-entry', + entry.className, + entry.active ? 'active' : '', + entry.disabled ? 'disabled' : '', + selected ? 'selected' : '' + ); +} \ No newline at end of file diff --git a/lib/features/popup-menu/PopupMenuItem.js b/lib/features/popup-menu/PopupMenuItem.js new file mode 100644 index 000000000..c7333d397 --- /dev/null +++ b/lib/features/popup-menu/PopupMenuItem.js @@ -0,0 +1,59 @@ +import classnames from 'classnames'; + +/** + * Component that renders a popup menu entry. + * + * @param {string} key + * @param {Object} entry + * @param {boolean} selected + * @param {function} onMouseEnter + * @param {function} onClick + * @param {function} html + */ +export default function PopupMenuItem(props) { + const { entry, selected, html, ...restProps } = props; + + const createImage = imageUrl => { + return html``; + }; + + return (html` +
  • +
    + + ${entry.imageUrl ? createImage(entry.imageUrl) : null} + ${entry.label || entry.name} + + + ${ entry.description } + +
    + ${ entry.documentationRef && html` + + ` } +
  • + ` + ); +} diff --git a/lib/features/popup-menu/PopupMenuList.js b/lib/features/popup-menu/PopupMenuList.js new file mode 100644 index 000000000..f269d1d77 --- /dev/null +++ b/lib/features/popup-menu/PopupMenuList.js @@ -0,0 +1,82 @@ +import PopupMenuItem from './PopupMenuItem'; + +/** + * Component that renders a popup menu entry list. + * + * @param {Array} entries + * @param {Object} selectedEntry + * @param {function} setSelectedEntry + * @param {Object} resultsRef + * @param {function} html + */ + +export default function PopupMenuList(props) { + const { + selectedEntry, + setSelectedEntry, + onSelect, + resultsRef, + html + } = props; + + const groups = groupEntries(props.entries); + + const entries = groups.map(group => { + const groupEntries = group.entries; + + return (html` + ${group.name && group && html` +
    + ${group.name} +
    + `} +
      + ${groupEntries.map((entry, idx) => (html` + <${PopupMenuItem} + key=${ entry.id } + entry=${ entry } + selected=${ entry === selectedEntry } + onMouseEnter=${ () => setSelectedEntry(entry) } + onClick=${ event => onSelect(event, entry) } + html=${ html } + /> + `))} +
    + ` + ); + }); + + return (html` +
    + ${entries} +
    + `); +} + + +// helpers +function groupEntries(entries) { + const groups = []; + + const getGroup = group => groups.find(elem => group.id === elem.id); + + const containsGroup = group => !!getGroup(group); + + // legacy support for provider built for the old popUp menu + const formatGroup = group => + typeof group === 'string' ? { id: group } : group; + + entries.forEach(entry => { + + // assume a default group when none is provided + const group = entry.group ? formatGroup(entry.group) : { id: 'default' }; + + if (!containsGroup(group)) { + groups.push({ ...group, entries: [ entry ] }); + } else { + getGroup(group).entries.push(entry); + } + }); + + return groups; +} \ No newline at end of file diff --git a/lib/features/popup-menu/index.js b/lib/features/popup-menu/index.js index 50840beb6..c9650a497 100644 --- a/lib/features/popup-menu/index.js +++ b/lib/features/popup-menu/index.js @@ -1,6 +1,8 @@ +import DiagramJSui from '@bpmn-io/diagram-js-ui'; import PopupMenu from './PopupMenu'; export default { + __depends__: [ DiagramJSui ], __init__: [ 'popupMenu' ], popupMenu: [ 'type', PopupMenu ] }; diff --git a/package-lock.json b/package-lock.json index 7795204f0..09ee82ce5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "10.0.0", "license": "MIT", "dependencies": { + "@bpmn-io/diagram-js-ui": "^0.1.1", + "classnames": "^2.3.2", "css.escape": "^1.5.1", "didi": "^9.0.0", "hammerjs": "^2.0.1", @@ -367,6 +369,14 @@ "node": ">=6.9.0" } }, + "node_modules/@bpmn-io/diagram-js-ui": { + "version": "0.1.1", + "license": "MIT", + "dependencies": { + "htm": "^3.1.1", + "preact": "^10.11.2" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", "dev": true, @@ -377,9 +387,8 @@ }, "node_modules/@eslint/eslintrc": { "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz", - "integrity": "sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -400,9 +409,8 @@ }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -415,9 +423,8 @@ }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -427,9 +434,8 @@ }, "node_modules/@humanwhocodes/config-array": { "version": "0.10.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz", - "integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", @@ -450,9 +456,8 @@ }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -463,9 +468,8 @@ }, "node_modules/@humanwhocodes/object-schema": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -924,9 +928,8 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -944,9 +947,8 @@ }, "node_modules/ajv": { "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1046,9 +1048,8 @@ }, "node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/array-includes": { "version": "3.1.5", @@ -1596,8 +1597,6 @@ }, "node_modules/browserslist": { "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", "dev": true, "funding": [ { @@ -1609,6 +1608,7 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], + "license": "MIT", "dependencies": { "caniuse-lite": "^1.0.30001370", "electron-to-chromium": "^1.4.202", @@ -1688,9 +1688,8 @@ }, "node_modules/callsites": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -1799,6 +1798,10 @@ "node": ">=6.0" } }, + "node_modules/classnames": { + "version": "2.3.2", + "license": "MIT" + }, "node_modules/cliui": { "version": "7.0.4", "dev": true, @@ -2011,9 +2014,8 @@ }, "node_modules/debug": { "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -2094,8 +2096,7 @@ }, "node_modules/didi": { "version": "9.0.0", - "resolved": "https://registry.npmjs.org/didi/-/didi-9.0.0.tgz", - "integrity": "sha512-bOZ7WAah3t8TxKV81pbIivHjWyABot49YXG1M3QztnUlZDHz3MRNJ1nZO87JbqrkqNI/2GR4ncHfXdGIP9LX+w==" + "license": "MIT" }, "node_modules/diff": { "version": "5.0.0", @@ -2149,9 +2150,8 @@ }, "node_modules/electron-to-chromium": { "version": "1.4.225", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.225.tgz", - "integrity": "sha512-ICHvGaCIQR3P88uK8aRtx8gmejbVJyC6bB4LEC3anzBrIzdzC7aiZHY4iFfXhN4st6I7lMO0x4sgBHf/7kBvRw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/encodeurl": { "version": "1.0.2", @@ -2291,9 +2291,8 @@ }, "node_modules/eslint": { "version": "8.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.24.0.tgz", - "integrity": "sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ==", "dev": true, + "license": "MIT", "dependencies": { "@eslint/eslintrc": "^1.3.2", "@humanwhocodes/config-array": "^0.10.5", @@ -2404,9 +2403,8 @@ }, "node_modules/eslint-plugin-bpmn-io": { "version": "0.16.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-bpmn-io/-/eslint-plugin-bpmn-io-0.16.0.tgz", - "integrity": "sha512-33lxw01ombcLWX5gEGpnqKh7DXbTdBh6CWlmkfCwTGFqklndvrP7qemZczO8Sg0mMlQiF7Eu17Zlv7etwX2BIg==", "dev": true, + "license": "MIT", "dependencies": { "eslint-plugin-mocha": "^10.1.0", "eslint-plugin-react": "^7.30.1" @@ -2622,9 +2620,8 @@ }, "node_modules/eslint-visitor-keys": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -2842,9 +2839,8 @@ }, "node_modules/espree": { "version": "9.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", - "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", @@ -2962,9 +2958,8 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-glob": { "version": "3.2.11", @@ -3581,6 +3576,10 @@ "dev": true, "license": "ISC" }, + "node_modules/htm": { + "version": "3.1.1", + "license": "Apache-2.0" + }, "node_modules/html-escaper": { "version": "2.0.2", "dev": true, @@ -3666,9 +3665,8 @@ }, "node_modules/import-fresh": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -3704,8 +3702,7 @@ }, "node_modules/inherits-browser": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/inherits-browser/-/inherits-browser-0.1.0.tgz", - "integrity": "sha512-CJHHvW3jQ6q7lzsXPpapLdMx5hDpSF3FSh45pwsj6bKxJJ8Nl8v43i5yXnr3BdfOimGHKyniewQtnAIp3vyJJw==" + "license": "ISC" }, "node_modules/internal-slot": { "version": "1.0.3", @@ -4152,9 +4149,8 @@ }, "node_modules/js-sdsl": { "version": "4.1.4", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz", - "integrity": "sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/js-tokens": { "version": "4.0.0", @@ -4163,9 +4159,8 @@ }, "node_modules/js-yaml": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -4695,13 +4690,11 @@ }, "node_modules/min-dash": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/min-dash/-/min-dash-4.0.0.tgz", - "integrity": "sha512-piIvVJ/nxuA4+LpnYIzF6oCtRvdtDvQJteSC+H768H2UvPKFKIt5oiJnUVtr0ZdchneXTcvUZ91vIrvWVIN0AA==" + "license": "MIT" }, "node_modules/min-dom": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/min-dom/-/min-dom-4.0.2.tgz", - "integrity": "sha512-zOlfZJ/mfgHKnmfAdZdNEaELq52z6Q8kWNZzupPv4aKLdpc67Uq6tCDAo/UIm1MKZrJIok7XMjRrYctG/jtqGw==", + "license": "MIT", "dependencies": { "component-event": "^0.1.4", "domify": "^1.4.1", @@ -4710,8 +4703,7 @@ }, "node_modules/min-dom/node_modules/min-dash": { "version": "3.8.1", - "resolved": "https://registry.npmjs.org/min-dash/-/min-dash-3.8.1.tgz", - "integrity": "sha512-evumdlmIlg9mbRVPbC4F5FuRhNmcMS5pvuBUbqb1G9v09Ro0ImPEgz5n3khir83lFok1inKqVDjnKEg3GpDxQg==" + "license": "MIT" }, "node_modules/minimatch": { "version": "3.0.4", @@ -4977,9 +4969,8 @@ }, "node_modules/node-releases": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/normalize-package-data": { "version": "2.4.0", @@ -5694,9 +5685,8 @@ }, "node_modules/parent-module": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -5831,6 +5821,14 @@ "node": ">=8" } }, + "node_modules/preact": { + "version": "10.11.2", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "dev": true, @@ -6084,9 +6082,8 @@ }, "node_modules/resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -7044,8 +7041,7 @@ }, "node_modules/tiny-svg": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-3.0.0.tgz", - "integrity": "sha512-+u6VomQO7MbI7CQe5q1IwNePpbVKG/HVdUQBmaEpSCdP/QmeyjhrS6WKFsNetXlvf9LWu/f5woRqjMdxBMe/0w==" + "license": "MIT" }, "node_modules/tmp": { "version": "0.2.1", @@ -7984,14 +7980,19 @@ "to-fast-properties": "^2.0.0" } }, + "@bpmn-io/diagram-js-ui": { + "version": "0.1.1", + "requires": { + "htm": "^3.1.1", + "preact": "^10.11.2" + } + }, "@colors/colors": { "version": "1.5.0", "dev": true }, "@eslint/eslintrc": { "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz", - "integrity": "sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -8007,8 +8008,6 @@ "dependencies": { "globals": { "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -8016,8 +8015,6 @@ }, "minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -8027,8 +8024,6 @@ }, "@humanwhocodes/config-array": { "version": "0.10.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz", - "integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -8042,14 +8037,10 @@ }, "@humanwhocodes/module-importer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true }, "@humanwhocodes/object-schema": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, "@istanbuljs/load-nyc-config": { @@ -8418,8 +8409,6 @@ }, "acorn-jsx": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "requires": {} }, @@ -8432,8 +8421,6 @@ }, "ajv": { "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -8501,8 +8488,6 @@ }, "argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "array-includes": { @@ -8848,8 +8833,6 @@ }, "browserslist": { "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", "dev": true, "requires": { "caniuse-lite": "^1.0.30001370", @@ -8892,8 +8875,6 @@ }, "callsites": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, "camelcase": { @@ -8954,6 +8935,9 @@ "version": "1.0.3", "dev": true }, + "classnames": { + "version": "2.3.2" + }, "cliui": { "version": "7.0.4", "dev": true, @@ -9113,8 +9097,6 @@ }, "debug": { "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -9161,9 +9143,7 @@ "dev": true }, "didi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/didi/-/didi-9.0.0.tgz", - "integrity": "sha512-bOZ7WAah3t8TxKV81pbIivHjWyABot49YXG1M3QztnUlZDHz3MRNJ1nZO87JbqrkqNI/2GR4ncHfXdGIP9LX+w==" + "version": "9.0.0" }, "diff": { "version": "5.0.0", @@ -9202,8 +9182,6 @@ }, "electron-to-chromium": { "version": "1.4.225", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.225.tgz", - "integrity": "sha512-ICHvGaCIQR3P88uK8aRtx8gmejbVJyC6bB4LEC3anzBrIzdzC7aiZHY4iFfXhN4st6I7lMO0x4sgBHf/7kBvRw==", "dev": true }, "encodeurl": { @@ -9307,8 +9285,6 @@ }, "eslint": { "version": "8.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.24.0.tgz", - "integrity": "sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ==", "dev": true, "requires": { "@eslint/eslintrc": "^1.3.2", @@ -9520,8 +9496,6 @@ }, "eslint-plugin-bpmn-io": { "version": "0.16.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-bpmn-io/-/eslint-plugin-bpmn-io-0.16.0.tgz", - "integrity": "sha512-33lxw01ombcLWX5gEGpnqKh7DXbTdBh6CWlmkfCwTGFqklndvrP7qemZczO8Sg0mMlQiF7Eu17Zlv7etwX2BIg==", "dev": true, "requires": { "eslint-plugin-mocha": "^10.1.0", @@ -9665,14 +9639,10 @@ }, "eslint-visitor-keys": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true }, "espree": { "version": "9.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", - "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", "dev": true, "requires": { "acorn": "^8.8.0", @@ -9744,8 +9714,6 @@ }, "fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "fast-glob": { @@ -10128,6 +10096,9 @@ "version": "2.8.9", "dev": true }, + "htm": { + "version": "3.1.1" + }, "html-escaper": { "version": "2.0.2", "dev": true @@ -10177,8 +10148,6 @@ }, "import-fresh": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -10202,9 +10171,7 @@ "dev": true }, "inherits-browser": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/inherits-browser/-/inherits-browser-0.1.0.tgz", - "integrity": "sha512-CJHHvW3jQ6q7lzsXPpapLdMx5hDpSF3FSh45pwsj6bKxJJ8Nl8v43i5yXnr3BdfOimGHKyniewQtnAIp3vyJJw==" + "version": "0.1.0" }, "internal-slot": { "version": "1.0.3", @@ -10470,8 +10437,6 @@ }, "js-sdsl": { "version": "4.1.4", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz", - "integrity": "sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==", "dev": true }, "js-tokens": { @@ -10480,8 +10445,6 @@ }, "js-yaml": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -10838,14 +10801,10 @@ "dev": true }, "min-dash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/min-dash/-/min-dash-4.0.0.tgz", - "integrity": "sha512-piIvVJ/nxuA4+LpnYIzF6oCtRvdtDvQJteSC+H768H2UvPKFKIt5oiJnUVtr0ZdchneXTcvUZ91vIrvWVIN0AA==" + "version": "4.0.0" }, "min-dom": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/min-dom/-/min-dom-4.0.2.tgz", - "integrity": "sha512-zOlfZJ/mfgHKnmfAdZdNEaELq52z6Q8kWNZzupPv4aKLdpc67Uq6tCDAo/UIm1MKZrJIok7XMjRrYctG/jtqGw==", "requires": { "component-event": "^0.1.4", "domify": "^1.4.1", @@ -10853,9 +10812,7 @@ }, "dependencies": { "min-dash": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/min-dash/-/min-dash-3.8.1.tgz", - "integrity": "sha512-evumdlmIlg9mbRVPbC4F5FuRhNmcMS5pvuBUbqb1G9v09Ro0ImPEgz5n3khir83lFok1inKqVDjnKEg3GpDxQg==" + "version": "3.8.1" } } }, @@ -11038,8 +10995,6 @@ }, "node-releases": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", "dev": true }, "normalize-package-data": { @@ -11483,8 +11438,6 @@ }, "parent-module": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "requires": { "callsites": "^3.0.0" @@ -11565,6 +11518,9 @@ "find-up": "^4.0.0" } }, + "preact": { + "version": "10.11.2" + }, "prelude-ls": { "version": "1.2.1", "dev": true @@ -11721,8 +11677,6 @@ }, "resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, "reusify": { @@ -12347,9 +12301,7 @@ "dev": true }, "tiny-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-3.0.0.tgz", - "integrity": "sha512-+u6VomQO7MbI7CQe5q1IwNePpbVKG/HVdUQBmaEpSCdP/QmeyjhrS6WKFsNetXlvf9LWu/f5woRqjMdxBMe/0w==" + "version": "3.0.0" }, "tmp": { "version": "0.2.1", diff --git a/package.json b/package.json index 83e4b8abc..c9ad16ec7 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,8 @@ "webpack": "^5.74.0" }, "dependencies": { + "@bpmn-io/diagram-js-ui": "^0.1.1", + "classnames": "^2.3.2", "css.escape": "^1.5.1", "didi": "^9.0.0", "hammerjs": "^2.0.1",