diff --git a/src/editor/components/seMenu.js b/src/editor/components/seMenu.js index 2c80fa5e..78e9b7fd 100644 --- a/src/editor/components/seMenu.js +++ b/src/editor/components/seMenu.js @@ -1,6 +1,6 @@ /* eslint-disable node/no-unpublished-import */ -import 'elix/define/MenuButton.js'; import 'elix/define/MenuItem.js'; +import './sePlainMenuButton.js'; const template = document.createElement('template'); template.innerHTML = ` diff --git a/src/editor/components/sePlainBorderButton.js b/src/editor/components/sePlainBorderButton.js new file mode 100644 index 00000000..6fd53c75 --- /dev/null +++ b/src/editor/components/sePlainBorderButton.js @@ -0,0 +1,34 @@ +/* eslint-disable node/no-unpublished-import */ +import {template} from 'elix/src/base/internal.js'; +import {fragmentFrom} from 'elix/src/core/htmlLiterals.js'; +import PlainButton from 'elix/src/plain/PlainButton.js'; + +/** + * @class SePlainBorderButton + * Button with a border in the Plain reference design system + * + * @inherits PlainButton + */ +class SePlainBorderButton extends PlainButton { + /** + * @function get + * @returns {PlainObject} + */ + get [template] () { + const result = super[template]; + result.content.append( + fragmentFrom.html` + + ` + ); + return result; + } +} + +export default SePlainBorderButton; diff --git a/src/editor/components/sePlainMenuButton.js b/src/editor/components/sePlainMenuButton.js new file mode 100644 index 00000000..85c29c21 --- /dev/null +++ b/src/editor/components/sePlainMenuButton.js @@ -0,0 +1,21 @@ +/* eslint-disable node/no-unpublished-import */ +import PlainMenuButton from 'elix/src/plain/PlainMenuButton.js'; +import {defaultState} from 'elix/src/base/internal.js'; +import sePlainBorderButton from './sePlainBorderButton.js'; + +/** + * @class ElixMenuButton + */ +export default class ElixMenuButton extends PlainMenuButton { + /** + * @function get + * @returns {PlainObject} + */ + get [defaultState] () { + return Object.assign(super[defaultState], { + sourcePartType: sePlainBorderButton + }); + } +} + +customElements.define('elix-menu-button', ElixMenuButton); diff --git a/src/editor/dialogs/sePromptDialog.js b/src/editor/dialogs/sePromptDialog.js index 05d0bb5b..17fb7cd5 100644 --- a/src/editor/dialogs/sePromptDialog.js +++ b/src/editor/dialogs/sePromptDialog.js @@ -42,7 +42,8 @@ export class SePromptDialog extends HTMLElement { } break; default: - // console.log('unkonw attr for:', name, 'newValue =', newValue); + // eslint-disable-next-line no-console + console.error('unknown attr for:', name, 'newValue =', newValue); break; } } diff --git a/src/editor/extensions/ext-markers/ext-markers.js b/src/editor/extensions/ext-markers/ext-markers.js index b47cf795..60980c65 100644 --- a/src/editor/extensions/ext-markers/ext-markers.js +++ b/src/editor/extensions/ext-markers/ext-markers.js @@ -121,7 +121,7 @@ export default { function setIcon (pos, id) { if (id.substr(0, 1) !== '\\') { id = '\\textmarker'; } const ci = '#' + idPrefix + pos + '_' + id.substr(1); - // svgEditor.setIcon('#cur_' + pos + '_marker_list', $(ci).children()); + svgEditor.setIcon('#cur_' + pos + '_marker_list', $(ci).children()); $(ci).addClass('current').siblings().removeClass('current'); } @@ -508,7 +508,7 @@ export default { buttons.push({ id: idPrefix + pos + '_' + id, svgicon: id, - icon: 'markers-' + id + '.png', + icon: id + '.svg', title, type: 'context', events: {click: setArrowFromButton}, @@ -565,7 +565,7 @@ export default { return { name: strings.name, - svgicons: 'markers-icons.xml', + svgicons: '', callback () { $('#marker_panel').addClass('toolset').hide(); }, @@ -595,7 +595,6 @@ export default { }, elementChanged (opts) { - // console.log('elementChanged',opts); const elem = opts.elems[0]; if (elem && ( elem.getAttribute('marker-start') || diff --git a/src/editor/images/box.svg b/src/editor/images/box.svg new file mode 100644 index 00000000..5f6f7afe --- /dev/null +++ b/src/editor/images/box.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/editor/images/box_o.svg b/src/editor/images/box_o.svg new file mode 100644 index 00000000..6be412d4 --- /dev/null +++ b/src/editor/images/box_o.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/editor/images/forwardslash.svg b/src/editor/images/forwardslash.svg new file mode 100644 index 00000000..15672b71 --- /dev/null +++ b/src/editor/images/forwardslash.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/editor/images/leftarrow.svg b/src/editor/images/leftarrow.svg new file mode 100644 index 00000000..239dc80f --- /dev/null +++ b/src/editor/images/leftarrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/editor/images/leftarrow_o.svg b/src/editor/images/leftarrow_o.svg new file mode 100644 index 00000000..8fb7aead --- /dev/null +++ b/src/editor/images/leftarrow_o.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/editor/images/mcircle.svg b/src/editor/images/mcircle.svg new file mode 100644 index 00000000..81f2b46f --- /dev/null +++ b/src/editor/images/mcircle.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/editor/images/mcircle_o.svg b/src/editor/images/mcircle_o.svg new file mode 100644 index 00000000..3999193a --- /dev/null +++ b/src/editor/images/mcircle_o.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/editor/images/nomarker.svg b/src/editor/images/nomarker.svg new file mode 100644 index 00000000..08ec8de5 --- /dev/null +++ b/src/editor/images/nomarker.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/editor/images/reverseslash.svg b/src/editor/images/reverseslash.svg new file mode 100644 index 00000000..49b2d920 --- /dev/null +++ b/src/editor/images/reverseslash.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/editor/images/rightarrow.svg b/src/editor/images/rightarrow.svg new file mode 100644 index 00000000..b817f9c1 --- /dev/null +++ b/src/editor/images/rightarrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/editor/images/rightarrow_o.svg b/src/editor/images/rightarrow_o.svg new file mode 100644 index 00000000..a225baf5 --- /dev/null +++ b/src/editor/images/rightarrow_o.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/editor/images/star_o.svg b/src/editor/images/star_o.svg new file mode 100644 index 00000000..26d38343 --- /dev/null +++ b/src/editor/images/star_o.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/editor/images/textmarker.svg b/src/editor/images/textmarker.svg new file mode 100644 index 00000000..294fa8cc --- /dev/null +++ b/src/editor/images/textmarker.svg @@ -0,0 +1,3 @@ + + T + diff --git a/src/editor/images/triangle.svg b/src/editor/images/triangle.svg new file mode 100644 index 00000000..d6f9d153 --- /dev/null +++ b/src/editor/images/triangle.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/editor/images/triangle_o.svg b/src/editor/images/triangle_o.svg new file mode 100644 index 00000000..ae048865 --- /dev/null +++ b/src/editor/images/triangle_o.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/editor/images/verticalslash.svg b/src/editor/images/verticalslash.svg new file mode 100644 index 00000000..27427393 --- /dev/null +++ b/src/editor/images/verticalslash.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/editor/images/xmark.svg b/src/editor/images/xmark.svg new file mode 100644 index 00000000..79462a13 --- /dev/null +++ b/src/editor/images/xmark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/editor/index.html b/src/editor/index.html index a12f52ac..57700aaa 100644 --- a/src/editor/index.html +++ b/src/editor/index.html @@ -352,7 +352,7 @@ - + diff --git a/src/editor/svgedit.css b/src/editor/svgedit.css index 8782f9e7..89994c4d 100644 --- a/src/editor/svgedit.css +++ b/src/editor/svgedit.css @@ -722,5 +722,8 @@ ul li.current { z-index: 20001; } +/* ------------ */ - +.dropdown li.tool_button { + width: 24px; +} \ No newline at end of file diff --git a/src/editor/svgedit.js b/src/editor/svgedit.js index 1aee2464..421672fc 100644 --- a/src/editor/svgedit.js +++ b/src/editor/svgedit.js @@ -17,7 +17,7 @@ */ import './touch.js'; -import {isChrome, isMac} from '../common/browser.js'; +import {isChrome, isMac, isTouch} from '../common/browser.js'; import {convertUnit, isValidUnit} from '../common/units.js'; import SvgCanvas from '../svgcanvas/svgcanvas.js'; @@ -77,6 +77,7 @@ class Editor extends EditorStartup { * @name module:SVGthis.uiStrings * @type {PlainObject} */ + this.flyoutFuncs = {}; this.uiStrings = {}; this.svgCanvas = null; this.isReady = false; @@ -839,6 +840,271 @@ class Editor extends EditorStartup { this.updateTitle(); } + /** + * Makes sure the current selected paint is available to work with. + * @returns {void} + */ + /* prepPaints () { + paintBox.fill.prep(); + paintBox.stroke.prep(); + } */ + + /** + * + * @returns {void} + */ + // eslint-disable-next-line class-methods-use-this + setFlyoutPositions () { + $('.tools_flyout').each(function () { + const shower = $('#' + this.id + '_show'); + const {left, top} = shower.offset(); + const w = shower.outerWidth(); + this.css({left: (left + w) * editor.tool_scale, top}); + }); + } + + /** + * + * @returns {void} + */ + // eslint-disable-next-line class-methods-use-this + setFlyoutTitles () { + $('.tools_flyout').each(function () { + const shower = $('#' + this.id + '_show'); + if (shower.data('isLibrary')) { + return; + } + const tooltips = this.children().map(function () { + return this.title; + }).get(); + shower[0].title = tooltips.join(' / '); + }); + } + + /** + * @param {PlainObject} holders Key is a selector + * @returns {void} + */ + setupFlyouts (holders) { + const allHolders = {}; + $.each(holders, function (holdSel, btnOpts) { + if (!allHolders[holdSel]) { + allHolders[holdSel] = []; + } + allHolders[holdSel].push(...btnOpts); + + const buttons = $(holdSel).children().not('.tool_button_evt_handled'); + const showSel = holdSel + '_show'; + const shower = $(showSel); + let def = false; + buttons.addClass('tool_button tool_button_evt_handled') + .unbind('click mousedown mouseup') // may not be necessary + .each(function () { + // Get this button's options + const idSel = '#' + this.getAttribute('id'); + const [i, opts] = Object.entries(btnOpts).find(([_, {sel}]) => { + return sel === idSel; + }); + + // Remember the function that goes with this ID + this.flyoutFuncs[opts.sel] = opts.fn; + + if (opts.isDefault) { def = i; } + + const flyoutAction = function (ev) { + let options = opts; + // Find the currently selected tool if comes from keystroke + if (ev.type === 'keydown') { + const flyoutIsSelected = $(options.parent + '_show').hasClass('tool_button_current'); + const currentOperation = $(options.parent + '_show').attr('data-curopt'); + Object.entries(holders[opts.parent]).some(([j, tool]) => { + if (tool.sel !== currentOperation) { + return false; + } + if (!ev.shiftKey || !flyoutIsSelected) { + options = tool; + } else { + // If flyout is selected, allow shift key to iterate through subitems + j = Number.parseInt(j); + // Use `allHolders` to include both extension `includeWith` and toolbarButtons + options = allHolders[opts.parent][j + 1] || + holders[opts.parent][0]; + } + return true; + }); + } + if ($(this).hasClass('disabled')) { return false; } + /* if (toolButtonClick(showSel)) { + options.fn(); + } */ + const icon = (options.icon) ? $.getSvgIcon(options.icon, true) : $(options.sel).children().eq(0).clone(); + icon[0].setAttribute('width', shower.width()); + icon[0].setAttribute('height', shower.height()); + shower.children(':not(.flyout_arrow_horiz)').remove(); + shower.append(icon).attr('data-curopt', options.sel); // This sets the current mode + return true; + }; + + $(this).mouseup(flyoutAction); + + if (opts.key) { + $(document).bind('keydown', opts.key[0] + ' shift+' + opts.key[0], flyoutAction); + } + return true; + }); + + if (def) { + shower.attr('data-curopt', btnOpts[def].sel); + } else if (!shower.attr('data-curopt')) { + // Set first as default + shower.attr('data-curopt', btnOpts[0].sel); + } + + let timer; + + // Clicking the "show" icon should set the current mode + shower.mousedown(function (evt) { + if (shower.hasClass('disabled')) { + return false; + } + const holder = $(holdSel); + const pos = $(showSel).position(); + const l = pos.left + 34; + const w = holder.width() * -1; + const time = holder.data('shown_popop') ? 200 : 0; + timer = setTimeout(function () { + // Show corresponding menu + if (!shower.data('isLibrary')) { + holder.css('left', w).show().animate({ + left: l + }, 150); + } else { + holder.css('left', l).show(); + } + holder.data('shown_popop', true); + }, time); + evt.preventDefault(); + return true; + }).mouseup(function (evt) { + clearTimeout(timer); + /* const opt = $(this).attr('data-curopt'); + // Is library and popped up, so do nothing + if (shower.data('isLibrary') && $(showSel.replace('_show', '')).is(':visible')) { + toolButtonClick(showSel, true); + return; + } + if (toolButtonClick(showSel) && this.flyoutFuncs[opt]) { + this.flyoutFuncs[opt](); + } */ + }); + // $('#tools_rect').mouseleave(function () { $('#tools_rect').fadeOut(); }); + }); + this.setFlyoutTitles(); + // this.setFlyoutPositions(); + } + + /** + * @param {string} id + * @param {external:jQuery} child + * @returns {external:jQuery} + */ + // eslint-disable-next-line class-methods-use-this + makeFlyoutHolder (id, child) { + const div = $('
', { + class: 'tools_flyout', + id + }).appendTo('#svg_editor').append(child); + return div; + } + + /** + * @function module:SVGEditor.setIcon + * @param {string|Element|external:jQuery} elem + * @param {string|external:jQuery} iconId + * @returns {void} + */ + setIcon (elem, iconId) { + // eslint-disable-next-line max-len + const icon = (typeof iconId === 'string') ? $('') : iconId.clone(); + if (!icon) { + // Todo: Investigate why this still occurs in some cases + console.log('NOTE: Icon image missing: ' + iconId); // eslint-disable-line no-console + return; + } + $(elem).empty().append(icon); + } + + /** + * @param {string} elemSel + * @param {string} listSel + * @param {external:jQuery.Function} callback + * @param {PlainObject} opts + * @param {boolean} opts.dropUp + * @param {boolean} opts.seticon + * @param {boolean} opts.multiclick + * @todo Combine this with `addDropDown` or find other way to optimize. + * @returns {void} + */ + addAltDropDown (elemSel, listSel, callback, opts) { + // eslint-disable-next-line no-shadow + const self = this; + const button = $(elemSel); + const {dropUp} = opts; + const list = $(listSel); + if (dropUp) { + $(elemSel).addClass('dropup'); + } + list.find('li').bind('mouseup', function (...args) { + if (opts.seticon) { + self.setIcon('#cur_' + button[0].id, $(this).children()); + $(this).addClass('current').siblings().removeClass('current'); + } + callback.apply(this, ...args); + }); + + let onButton = false; + $(window).mouseup(function (evt) { + if (!onButton) { + button.removeClass('down'); + list.hide(); + list.css({top: 0, left: 0}); + } + onButton = false; + }); + + // const height = list.height(); // Currently unused + button.bind('mousedown', function () { + const off = button.offset(); + if (dropUp) { + off.top -= list.height(); + off.left += 8; + } else { + off.top += button.height(); + } + list.offset(off); + + if (!button.hasClass('down')) { + list.show(); + onButton = true; + } else { + // CSS position must be reset for Webkit + list.hide(); + list.css({top: 0, left: 0}); + } + button.toggleClass('down'); + }).hover(function () { + onButton = true; + }).mouseout(function () { + onButton = false; + }); + + if (opts.multiclick) { + list.mousedown(function () { + onButton = true; + }); + } + } + /** * @param {external:Window} win * @param {module:svgcanvas.SvgCanvas#event:extension_added} ext @@ -846,6 +1112,9 @@ class Editor extends EditorStartup { * @returns {Promise|void} Resolves to `undefined` */ async extAdded (win, ext) { + // eslint-disable-next-line no-shadow + const self = this; + const btnSelects = []; if (!ext) { return undefined; } @@ -921,7 +1190,9 @@ class Editor extends EditorStartup { break; } case 'button-select': { html = ''; + '
' + + '' + + '
'; const list = $('').appendTo('#option_lists'); @@ -931,6 +1202,13 @@ class Editor extends EditorStartup { // Creates the tool, hides & adds it, returns the select element /* const dropdown = */ $(html).appendTo(panel).children(); + btnSelects.push({ + elem: ('#' + tool.id), + list: ('#' + tool.id + '_opts'), + title: tool.title, + callback: tool.events.change, + cur: ('#cur_' + tool.id) + }); break; } case 'input': { html = '' + @@ -956,7 +1234,274 @@ class Editor extends EditorStartup { } }); } + const {svgicons} = ext; + if (ext.buttons) { + const fallbackObj = {}, + altsObj = {}, + placementObj = {}, + holders = {}; + /** + * @typedef {GenericArray} module:SVGEditor.KeyArray + * @property {string} 0 The key to bind (on `keydown`) + * @property {boolean} 1 Whether to `preventDefault` on the `keydown` event + * @property {boolean} 2 Not apparently in use (NoDisableInInput) + */ + /** + * @typedef {string|module:SVGEditor.KeyArray} module:SVGEditor.Key + */ + /** + * @typedef {PlainObject} module:SVGEditor.Button + * @property {string} id A unique identifier for this button. If SVG icons are used, this must match the ID used in the icon file. Required. + * @property {"mode_flyout"|"mode"|"context"|"app_menu"} type Type of button. Required. + * @property {string} title The tooltip text that will appear when the user hovers over the icon. Required. + * @property {PlainObject|PlainObject<"click", external:jQuery.Function>} events DOM event names with associated functions. Example: `{click () { alert('Button was clicked') } }`. Click is used with `includeWith` and `type` of "mode_flyout" (and "mode"); any events may be added if `list` is not present. Expected. + * @property {string} panel The ID of the context panel to be included, if type is "context". Required only if type is "context". + * @property {string} icon The file path to the raster version of the icon image source. Required only if no `svgicons` is supplied from [ExtensionInitResponse]{@link module:svgcanvas.ExtensionInitResponse}. + * @property {string} [svgicon] If absent, will utilize the button "id"; used to set "placement" on the `svgIcons` call + * @property {string} [list] Points to the "id" of a `context_tools` item of type "button-select" into which the button will be added as a panel list item + * @property {Integer} [position] The numeric index for placement; defaults to last position (as of the time of extension addition) if not present. For use with {@link http://api.jquery.com/eq/}. + * @property {boolean} [isDefault] Whether or not the button is the default. Used with `list`. + * @property {PlainObject} [includeWith] Object with flyout menu data + * @property {boolean} [includeWith.isDefault] Indicates whether button is default in flyout list or not. + * @property {string} includeWith.button jQuery selector of the existing button to be joined. Example: '#tool_line'. Required if `includeWith` is used. + * @property {"last"|Integer} [includeWith.position] Position of icon in flyout list; will be added to end if not indicated. Integer is for use with {@link http://api.jquery.com/eq/}. + * @property {module:SVGEditor.Key} [key] The key to bind to the button + */ + // Add buttons given by extension + $.each(ext.buttons, function (i, /** @type {module:SVGEditor.Button} */ btn) { + let {id} = btn; + let num = i; + // Give button a unique ID + while ($('#' + id).length) { + id = btn.id + '_' + (++num); + } + + let icon; + if (!svgicons) { + icon = $( + '' + btn.title : '') +
+              '' + ); + } else { + fallbackObj[id] = btn.icon; + altsObj[id] = btn.title; + const svgicon = btn.svgicon || btn.id; + if (btn.type === 'app_menu') { + placementObj['#' + id + ' > div'] = svgicon; + } else { + placementObj['#' + id] = svgicon; + } + } + let cls, parent; + + // Set button up according to its type + switch (btn.type) { + case 'mode_flyout': + case 'mode': + cls = 'tool_button'; + parent = '#tools_left'; + break; + case 'context': + cls = 'tool_button'; + parent = '#' + btn.panel; + // create the panel if it doesn't exist + if (!$(parent).length) { + $('
', {id: btn.panel}).appendTo('#tools_top'); + } + break; + case 'app_menu': + cls = ''; + parent = '#main_menu ul'; + break; + } + // refData + let flyoutHolder, showBtn, refBtn; + const button = $((btn.list || btn.type === 'app_menu') ? '
  • ' : '
    ') + .attr('id', id) + .attr('title', btn.title) + .addClass(cls); + if (!btn.includeWith && !btn.list) { + if ('position' in btn) { + if ($(parent).children().eq(btn.position).length) { + $(parent).children().eq(btn.position).before(button); + } else { + $(parent).children().last().after(button); + } + } else { + button.appendTo(parent); + } + + if (btn.type === 'mode_flyout') { + // Add to flyout menu / make flyout menu + // const opts = btn.includeWith; + // // opts.button, default, position + refBtn = $(button); + + flyoutHolder = refBtn.parent(); + // Create a flyout menu if there isn't one already + let tlsId; + if (!refBtn.parent().hasClass('tools_flyout')) { + // Create flyout placeholder + tlsId = refBtn[0].id.replace('tool_', 'tools_'); + showBtn = refBtn.clone() + .attr('id', tlsId + '_show') + .append($('
    ', {class: 'flyout_arrow_horiz'})); + + refBtn.before(showBtn); + + // Create a flyout div + flyoutHolder = self.makeFlyoutHolder(tlsId, refBtn); + flyoutHolder.data('isLibrary', true); + showBtn.data('isLibrary', true); + } + // refData = Actions.getButtonData(opts.button); + + placementObj['#' + tlsId + '_show'] = btn.id; + // TODO: Find way to set the current icon using the iconloader if this is not default + + // Include data for extension button as well as ref button + /* curH = */ holders['#' + flyoutHolder[0].id] = [{ + sel: '#' + id, + fn: btn.events.click, + icon: btn.id, + // key: btn.key, + isDefault: true + }]; // , refData + // + // // {sel:'#tool_rect', fn: clickRect, evt: 'mouseup', key: 4, parent: '#tools_rect', icon: 'rect'} + // + // const pos = ('position' in opts)?opts.position:'last'; + // const len = flyoutHolder.children().length; + // + // // Add at given position or end + // if (!isNaN(pos) && pos >= 0 && pos < len) { + // flyoutHolder.children().eq(pos).before(button); + // } else { + // flyoutHolder.append(button); + // curH.reverse(); + // } + } else if (btn.type === 'app_menu') { + button.append('
    ').append(btn.title); + } + } else if (btn.list) { + // Add button to list + button.addClass('push_button'); + $('#' + btn.list + '_opts').append(button); + if (btn.isDefault) { + $('#cur_' + btn.list).append(button.children().clone()); + const svgicon = btn.svgicon || btn.id; + placementObj['#cur_' + btn.list] = svgicon; + } + } else if (btn.includeWith) { + // Add to flyout menu / make flyout menu + const opts = btn.includeWith; + // opts.button, default, position + refBtn = $(opts.button); + + flyoutHolder = refBtn.parent(); + // Create a flyout menu if there isn't one already + let tlsId; + if (!refBtn.parent().hasClass('tools_flyout')) { + // Create flyout placeholder + tlsId = refBtn[0].id.replace('tool_', 'tools_'); + showBtn = refBtn.clone() + .attr('id', tlsId + '_show') + .append($('
    ', {class: 'flyout_arrow_horiz'})); + + refBtn.before(showBtn); + // Create a flyout div + flyoutHolder = self.makeFlyoutHolder(tlsId, refBtn); + } + + // refData = Actions.getButtonData(opts.button); + + if (opts.isDefault) { + placementObj['#' + tlsId + '_show'] = btn.id; + } + // TODO: Find way to set the current icon using the iconloader if this is not default + + // Include data for extension button as well as ref button + /* const curH = */ holders['#' + flyoutHolder[0].id] = [{ + sel: '#' + id, + fn: btn.events.click, + icon: btn.id, + key: btn.key, + isDefault: Boolean(btn.includeWith && btn.includeWith.isDefault) + }]; // , refData + + // {sel:'#tool_rect', fn: clickRect, evt: 'mouseup', key: 4, parent: '#tools_rect', icon: 'rect'} + + const pos = ('position' in opts) ? opts.position : 'last'; + const len = flyoutHolder.children().length; + + // Add at given position or end + if (!isNaN(pos) && pos >= 0 && pos < len) { + flyoutHolder.children().eq(pos).before(button); + } else { + flyoutHolder.append(button); + // curH.reverse(); + } + } + if (!svgicons) { + button.append(icon); + } + if (!btn.list) { + // Add given events to button + $.each(btn.events, function (name, func) { + if (name === 'click' && btn.type === 'mode') { + // `touch.js` changes `touchstart` to `mousedown`, + // so we must map extension click events as well + if (isTouch() && name === 'click') { + name = 'mousedown'; + } + if (btn.includeWith) { + button.bind(name, func); + } else { + button.bind(name, function () { + /* if (toolButtonClick(button)) { + func(); + } */ + }); + } + if (btn.key) { + $(document).bind('keydown', btn.key, func); + if (btn.title) { + button.attr('title', btn.title + ' [' + btn.key + ']'); + } + } + } else { + button.bind(name, func); + } + }); + } + self.setupFlyouts(holders); + }); + $.each(btnSelects, function () { + self.addAltDropDown(this.elem, this.list, this.callback, {seticon: true}); + }); + /* if (svgicons) { + return new Promise((resolve, reject) => { // eslint-disable-line promise/avoid-new + $.svgIcons(`${this.configObj.curConfig.imgPath}${svgicons}`, { + w: 24, h: 24, + id_match: false, + no_img: (!isWebkit()), + fallback: fallbackObj, + placement: placementObj, + callback (icons) { + // Non-ideal hack to make the icon match the current size + // if (curPrefs.iconsize && curPrefs.iconsize !== 'm') { + if (editor.pref('iconsize') !== 'm') { + // prepResize(); + } + runCallback(); + resolve(); + } + }); + }); + } */ + } if (ext.events) { this.leftPanelHandlers.add(ext.events.id, ext.events.click); }