Merge branch 'V7-preview' into issues/39
|
@ -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 = `
|
||||
|
|
|
@ -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`
|
||||
<style>
|
||||
[part~="button"] {
|
||||
background: #72797A;
|
||||
border: 1px solid #ccc;
|
||||
padding: 0.25em 0.5em;
|
||||
}
|
||||
</style>
|
||||
`
|
||||
);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export default SePlainBorderButton;
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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') ||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120">
|
||||
<path stroke-width="10" stroke="#f9bc01" fill="#f9bc01" d="m-30,-30l0,60l60,0l0,-60z"/>
|
||||
</svg></svg>
|
After Width: | Height: | Size: 331 B |
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120">
|
||||
<path stroke-width="10" stroke="#f9bc01" fill="none" d="m-30,-30l0,60l60,0l0,-60z"/>
|
||||
</svg></svg>
|
After Width: | Height: | Size: 328 B |
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120">
|
||||
<path stroke-width="10" stroke="#f9bc01" fill="none" d="m-20,50l40,-100"/>
|
||||
</svg></svg>
|
After Width: | Height: | Size: 318 B |
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120">
|
||||
<path stroke-width="10" stroke="#f9bc01" fill="#f9bc01" d="m-50,0l100,40l-30,-40l30,-40z"/>
|
||||
</svg></svg>
|
After Width: | Height: | Size: 335 B |
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120">
|
||||
<path stroke-width="10" stroke="#f9bc01" fill="none" d="m-50,0l100,40l-30,-40l30,-40z"/>
|
||||
</svg></svg>
|
After Width: | Height: | Size: 332 B |
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120">
|
||||
<circle stroke-width="10" stroke="#f9bc01" fill="#f9bc01" cy="0" cx="0" r="30"/>
|
||||
</svg></svg>
|
After Width: | Height: | Size: 324 B |
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120">
|
||||
<circle stroke-width="10" stroke="#f9bc01" fill="none" cy="0" cx="0" r="30"/>
|
||||
</svg></svg>
|
After Width: | Height: | Size: 321 B |
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120">
|
||||
<path stroke-width="10" stroke="#f9bc01" fill="#f9bc01" d="m-50,0l100,0"/>
|
||||
</svg></svg>
|
After Width: | Height: | Size: 318 B |
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120">
|
||||
<path stroke-width="10" stroke="#f9bc01" fill="none" d="m-20,-50l40,100"/>
|
||||
</svg></svg>
|
After Width: | Height: | Size: 318 B |
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120">
|
||||
<path stroke-width="10" stroke="#f9bc01" fill="#f9bc01" d="m50,0l-100,40l30,-40l-30,-40z"/>
|
||||
</svg></svg>
|
After Width: | Height: | Size: 335 B |
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120">
|
||||
<path stroke-width="10" stroke="#f9bc01" fill="none" d="m50,0l-100,40l30,-40l-30,-40z"/>
|
||||
</svg></svg>
|
After Width: | Height: | Size: 332 B |
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120">
|
||||
<path stroke-width="10" stroke="#f9bc01" fill="none" d="m-40,-20l80,0l-70,60l30,-80l30,80z"/>
|
||||
</svg></svg>
|
After Width: | Height: | Size: 337 B |
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120">
|
||||
<text xml:space="preserve" text-anchor="middle" font-family="serif" font-size="120" y="40" x="0" stroke-width="0" stroke="#f9bc01" fill="#f9bc01">T</text>
|
||||
</svg></svg>
|
After Width: | Height: | Size: 392 B |
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120">
|
||||
<path stroke-width="10" stroke="#f9bc01" fill="#f9bc01" d="M-30,30 L0,-30 L30,30 Z"/>
|
||||
</svg></svg>
|
After Width: | Height: | Size: 329 B |
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120">
|
||||
<path stroke-width="10" stroke="#f9bc01" fill="none" d="M-30,30 L0,-30 L30,30 Z"/>
|
||||
</svg></svg>
|
After Width: | Height: | Size: 326 B |
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120">
|
||||
<path stroke-width="10" stroke="#f9bc01" fill="none" d="m0,-50l0,100"/>
|
||||
</svg></svg>
|
After Width: | Height: | Size: 315 B |
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg_icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120">
|
||||
<path stroke-width="10" stroke="#f9bc01" fill="#f9bc01" d="m-30,30l60,-60m0,60l-60,-60"/>
|
||||
</svg></svg>
|
After Width: | Height: | Size: 333 B |
|
@ -352,7 +352,7 @@
|
|||
<se-spin-input size="3" id="opacity" min=0 max=100 step=5 title="Change selected item opacity" src="./images/opacity.svg"></se-spin-input>
|
||||
<se-palette id="palette"></se-palette>
|
||||
</div> <!-- tools_bottom -->
|
||||
|
||||
<div id="option_lists" class="dropdown"></div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
|
|
|
@ -722,5 +722,8 @@ ul li.current {
|
|||
z-index: 20001;
|
||||
}
|
||||
|
||||
/* ------------ */
|
||||
|
||||
|
||||
.dropdown li.tool_button {
|
||||
width: 24px;
|
||||
}
|
|
@ -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<string, module:SVGEditor.ToolButton>} 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 = $('<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') ? $('<img src="' + this.configObj.curConfig.imgPath + iconId + '">') : 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>|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 = '<div id="' + tool.id + '" class="dropdown toolset" title="' + tool.title + '">' +
|
||||
'<div id="cur_' + tool.id + '" class="icon_label"></div><button></button></div>';
|
||||
'<div id="cur_' + tool.id + '" class="icon_label"></div>' +
|
||||
'<button><img class="svg_icon" src="./images/arrow_down.svg" alt="icon" width="7" height="7"></button>' +
|
||||
'</div>';
|
||||
|
||||
const list = $('<ul id="' + tool.id + '_opts"></ul>').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 = '<label' + contId + '>' +
|
||||
|
@ -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<string, external:jQuery.Function>|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 = $(
|
||||
'<img src="' + self.configObj.curConfig.imgPath + btn.icon +
|
||||
(btn.title ? '" alt="' + 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) {
|
||||
$('<div>', {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') ? '<li/>' : '<div/>')
|
||||
.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($('<div>', {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('<div>').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($('<div>', {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);
|
||||
}
|
||||
|
|