123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- import {
- isFunction,
- isArray,
- forEach
- } from 'min-dash';
- import {
- domify,
- query as domQuery,
- attr as domAttr,
- clear as domClear,
- classes as domClasses,
- matches as domMatches,
- delegate as domDelegate,
- event as domEvent
- } from 'min-dom';
- var TOGGLE_SELECTOR = '.djs-palette-toggle',
- ENTRY_SELECTOR = '.entry',
- ELEMENT_SELECTOR = TOGGLE_SELECTOR + ', ' + ENTRY_SELECTOR;
- var PALETTE_OPEN_CLS = 'open',
- PALETTE_TWO_COLUMN_CLS = 'two-column';
- /**
- * A palette containing modeling elements.
- */
- export default function Palette(eventBus, canvas) {
- this._eventBus = eventBus;
- this._canvas = canvas;
- this._providers = [];
- var self = this;
- eventBus.on('tool-manager.update', function(event) {
- var tool = event.tool;
- self.updateToolHighlight(tool);
- });
- eventBus.on('i18n.changed', function() {
- self._update();
- });
- eventBus.on('diagram.init', function() {
- self._diagramInitialized = true;
- // initialize + update once diagram is ready
- if (self._providers.length) {
- self._init();
- self._update();
- }
- });
- }
- Palette.$inject = [ 'eventBus', 'canvas' ];
- /**
- * Register a provider with the palette
- *
- * @param {PaletteProvider} provider
- */
- Palette.prototype.registerProvider = function(provider) {
- this._providers.push(provider);
- // postpone init / update until diagram is initialized
- if (!this._diagramInitialized) {
- return;
- }
- if (!this._container) {
- this._init();
- }
- this._update();
- };
- /**
- * Returns the palette entries for a given element
- *
- * @return {Array<PaletteEntryDescriptor>} list of entries
- */
- Palette.prototype.getEntries = function() {
- var entries = {};
- // loop through all providers and their entries.
- // group entries by id so that overriding an entry is possible
- forEach(this._providers, function(provider) {
- var e = provider.getPaletteEntries();
- forEach(e, function(entry, id) {
- entries[id] = entry;
- });
- });
- return entries;
- };
- /**
- * Initialize
- */
- Palette.prototype._init = function() {
- var canvas = this._canvas,
- eventBus = this._eventBus;
- var parent = canvas.getContainer(),
- container = this._container = domify(Palette.HTML_MARKUP),
- self = this;
- parent.appendChild(container);
- domDelegate.bind(container, ELEMENT_SELECTOR, 'click', function(event) {
- var target = event.delegateTarget;
- if (domMatches(target, TOGGLE_SELECTOR)) {
- return self.toggle();
- }
- self.trigger('click', event);
- });
- // prevent drag propagation
- domEvent.bind(container, 'mousedown', function(event) {
- event.stopPropagation();
- });
- // prevent drag propagation
- domDelegate.bind(container, ENTRY_SELECTOR, 'dragstart', function(event) {
- self.trigger('dragstart', event);
- });
- eventBus.on('canvas.resized', this._layoutChanged, this);
- eventBus.fire('palette.create', {
- container: container
- });
- };
- /**
- * Update palette state.
- *
- * @param {Object} [state] { open, twoColumn }
- */
- Palette.prototype._toggleState = function(state) {
- state = state || {};
- var parent = this._getParentContainer(),
- container = this._container;
- var eventBus = this._eventBus;
- var twoColumn;
- var cls = domClasses(container);
- if ('twoColumn' in state) {
- twoColumn = state.twoColumn;
- } else {
- twoColumn = this._needsCollapse(parent.clientHeight, this._entries || {});
- }
- // always update two column
- cls.toggle(PALETTE_TWO_COLUMN_CLS, twoColumn);
- if ('open' in state) {
- cls.toggle(PALETTE_OPEN_CLS, state.open);
- }
- eventBus.fire('palette.changed', {
- twoColumn: twoColumn,
- open: this.isOpen()
- });
- };
- Palette.prototype._update = function() {
- var entriesContainer = domQuery('.djs-palette-entries', this._container),
- entries = this._entries = this.getEntries();
- domClear(entriesContainer);
- forEach(entries, function(entry, id) {
- var grouping = entry.group || 'default';
- var container = domQuery('[data-group=' + grouping + ']', entriesContainer);
- if (!container) {
- container = domify('<div class="group" data-group="' + grouping + '"></div>');
- entriesContainer.appendChild(container);
- }
- var html = entry.html || (
- entry.separator ?
- '<hr class="separator" />' :
- '<div class="entry" draggable="true"></div>');
- var control = domify(html);
- container.appendChild(control);
- if (!entry.separator) {
- domAttr(control, 'data-action', id);
- if (entry.title) {
- domAttr(control, 'title', entry.title);
- }
- if (entry.className) {
- addClasses(control, entry.className);
- }
- if (entry.imageUrl) {
- control.appendChild(domify('<img src="' + entry.imageUrl + '">'));
- }
- }
- });
- // open after update
- this.open();
- };
- /**
- * Trigger an action available on the palette
- *
- * @param {String} action
- * @param {Event} event
- */
- Palette.prototype.trigger = function(action, event, autoActivate) {
- var entries = this._entries,
- entry,
- handler,
- originalEvent,
- button = event.delegateTarget || event.target;
- if (!button) {
- return event.preventDefault();
- }
- entry = entries[domAttr(button, 'data-action')];
- // when user clicks on the palette and not on an action
- if (!entry) {
- return;
- }
- handler = entry.action;
- originalEvent = event.originalEvent || event;
- // simple action (via callback function)
- if (isFunction(handler)) {
- if (action === 'click') {
- handler(originalEvent, autoActivate);
- }
- } else {
- if (handler[action]) {
- handler[action](originalEvent, autoActivate);
- }
- }
- // silence other actions
- event.preventDefault();
- };
- Palette.prototype._layoutChanged = function() {
- this._toggleState({});
- };
- /**
- * Do we need to collapse to two columns?
- *
- * @param {Number} availableHeight
- * @param {Object} entries
- *
- * @return {Boolean}
- */
- Palette.prototype._needsCollapse = function(availableHeight, entries) {
- // top margin + bottom toggle + bottom margin
- // implementors must override this method if they
- // change the palette styles
- var margin = 20 + 10 + 20;
- var entriesHeight = Object.keys(entries).length * 46;
- return availableHeight < entriesHeight + margin;
- };
- /**
- * Close the palette
- */
- Palette.prototype.close = function() {
- this._toggleState({
- open: false,
- twoColumn: false
- });
- };
- /**
- * Open the palette
- */
- Palette.prototype.open = function() {
- this._toggleState({ open: true });
- };
- Palette.prototype.toggle = function(open) {
- if (this.isOpen()) {
- this.close();
- } else {
- this.open();
- }
- };
- Palette.prototype.isActiveTool = function(tool) {
- return tool && this._activeTool === tool;
- };
- Palette.prototype.updateToolHighlight = function(name) {
- var entriesContainer,
- toolsContainer;
- if (!this._toolsContainer) {
- entriesContainer = domQuery('.djs-palette-entries', this._container);
- this._toolsContainer = domQuery('[data-group=tools]', entriesContainer);
- }
- toolsContainer = this._toolsContainer;
- forEach(toolsContainer.children, function(tool) {
- var actionName = tool.getAttribute('data-action');
- if (!actionName) {
- return;
- }
- var toolClasses = domClasses(tool);
- actionName = actionName.replace('-tool', '');
- if (toolClasses.contains('entry') && actionName === name) {
- toolClasses.add('highlighted-entry');
- } else {
- toolClasses.remove('highlighted-entry');
- }
- });
- };
- /**
- * Return true if the palette is opened.
- *
- * @example
- *
- * palette.open();
- *
- * if (palette.isOpen()) {
- * // yes, we are open
- * }
- *
- * @return {boolean} true if palette is opened
- */
- Palette.prototype.isOpen = function() {
- return domClasses(this._container).has(PALETTE_OPEN_CLS);
- };
- /**
- * Get container the palette lives in.
- *
- * @return {Element}
- */
- Palette.prototype._getParentContainer = function() {
- return this._canvas.getContainer();
- };
- /* markup definition */
- Palette.HTML_MARKUP =
- '<div class="djs-palette">' +
- '<div class="djs-palette-entries"></div>' +
- '<div class="djs-palette-toggle"></div>' +
- '</div>';
- // helpers //////////////////////
- function addClasses(element, classNames) {
- var classes = domClasses(element);
- var actualClassNames = isArray(classNames) ? classNames : classNames.split(/\s+/g);
- actualClassNames.forEach(function(cls) {
- classes.add(cls);
- });
- }
|