update master to V7
144
src/editor/components/PaintBox.js
Normal file
@@ -0,0 +1,144 @@
|
||||
import {jGraduate} from './jgraduate/jQuery.jGraduate.js';
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class PaintBox {
|
||||
/**
|
||||
* @param {string|Element|external:jQuery} container
|
||||
* @param {"fill"} type
|
||||
*/
|
||||
constructor (container, type) {
|
||||
// set up gradients to be used for the buttons
|
||||
const svgdocbox = new DOMParser().parseFromString(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14">
|
||||
<rect
|
||||
fill="#000000" opacity="1" width="14" height="14"/>
|
||||
<defs><linearGradient id="gradbox_${PaintBox.ctr++}"/></defs>
|
||||
</svg>`,
|
||||
'text/xml'
|
||||
);
|
||||
|
||||
let docElem = svgdocbox.documentElement;
|
||||
docElem = document.importNode(docElem, true);
|
||||
container.appendChild(docElem);
|
||||
|
||||
this.rect = docElem.firstElementChild;
|
||||
this.defs = docElem.getElementsByTagName('defs')[0];
|
||||
this.grad = this.defs.firstElementChild;
|
||||
// this.paint = new $.jGraduate.Paint({solidColor: color});
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {module:jGraduate~Paint} paint
|
||||
* @returns {void}
|
||||
*/
|
||||
setPaint (paint) {
|
||||
this.paint = paint;
|
||||
|
||||
const ptype = paint.type;
|
||||
const opac = paint.alpha / 100;
|
||||
|
||||
let fillAttr = 'none';
|
||||
switch (ptype) {
|
||||
case 'solidColor':
|
||||
fillAttr = (paint[ptype] !== 'none') ? '#' + paint[ptype] : paint[ptype];
|
||||
break;
|
||||
case 'linearGradient':
|
||||
case 'radialGradient': {
|
||||
this.grad.remove();
|
||||
this.grad = paint[ptype];
|
||||
this.defs.appendChild(this.grad);
|
||||
const id = this.grad.id = 'gradbox_' + this.type;
|
||||
fillAttr = 'url(#' + id + ')';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.rect.setAttribute('fill', fillAttr);
|
||||
this.rect.setAttribute('opacity', opac);
|
||||
}
|
||||
/**
|
||||
* @param {PlainObject} svgCanvas
|
||||
* @param {string} color
|
||||
* @param {Float} opac
|
||||
* @param {string} type
|
||||
* @returns {module:jGraduate~Paint}
|
||||
*/
|
||||
static getPaint (svgCanvas, color, opac, type) {
|
||||
// update the editor's fill paint
|
||||
const opts = {alpha: opac};
|
||||
if (color.startsWith('url(#')) {
|
||||
let refElem = svgCanvas.getRefElem(color);
|
||||
refElem = (refElem) ? refElem.cloneNode(true) : document.querySelectorAll('#' + type + '_color defs *')[0];
|
||||
opts[refElem.tagName] = refElem;
|
||||
} else if (color.startsWith('#')) {
|
||||
opts.solidColor = color.substr(1);
|
||||
} else {
|
||||
opts.solidColor = 'none';
|
||||
}
|
||||
return new jGraduate.Paint(opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PlainObject} svgcanvas
|
||||
* @param {PlainObject} selectedElement
|
||||
* @returns {any}
|
||||
*/
|
||||
update (svgcanvas, selectedElement) {
|
||||
if (!selectedElement) { return null; }
|
||||
|
||||
const {type} = this;
|
||||
switch (selectedElement.tagName) {
|
||||
case 'use':
|
||||
case 'image':
|
||||
case 'foreignObject':
|
||||
// These elements don't have fill or stroke, so don't change
|
||||
// the current value
|
||||
return null;
|
||||
case 'g':
|
||||
case 'a': {
|
||||
const childs = selectedElement.getElementsByTagName('*');
|
||||
|
||||
let gPaint = null;
|
||||
for (let i = 0, len = childs.length; i < len; i++) {
|
||||
const elem = childs[i];
|
||||
const p = elem.getAttribute(type);
|
||||
if (i === 0) {
|
||||
gPaint = p;
|
||||
} else if (gPaint !== p) {
|
||||
gPaint = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (gPaint === null) {
|
||||
// No common color, don't update anything
|
||||
this._paintColor = null;
|
||||
return null;
|
||||
}
|
||||
this._paintColor = gPaint;
|
||||
this._paintOpacity = 1;
|
||||
break;
|
||||
} default: {
|
||||
this._paintOpacity = Number.parseFloat(selectedElement.getAttribute(type + '-opacity'));
|
||||
if (Number.isNaN(this._paintOpacity)) {
|
||||
this._paintOpacity = 1.0;
|
||||
}
|
||||
|
||||
const defColor = type === 'fill' ? 'black' : 'none';
|
||||
this._paintColor = selectedElement.getAttribute(type) || defColor;
|
||||
}
|
||||
}
|
||||
|
||||
this._paintOpacity *= 100;
|
||||
|
||||
const paint = PaintBox.getPaint(svgcanvas, this._paintColor, this._paintOpacity, type);
|
||||
// update the rect inside #fill_color/#stroke_color
|
||||
this.setPaint(paint);
|
||||
return (paint);
|
||||
}
|
||||
}
|
||||
PaintBox.ctr = 0;
|
||||
|
||||
export default PaintBox;
|
||||
12
src/editor/components/index.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import './seButton.js';
|
||||
import './seFlyingButton.js';
|
||||
import './seExplorerButton.js';
|
||||
import './seZoom.js';
|
||||
import './seInput.js';
|
||||
import './seSpinInput.js';
|
||||
import './sePalette.js';
|
||||
import './seMenu.js';
|
||||
import './seMenuItem.js';
|
||||
import './seList.js';
|
||||
import './seListItem.js';
|
||||
import './seColorPicker.js';
|
||||
391
src/editor/components/jgraduate/ColorValuePicker.js
Normal file
@@ -0,0 +1,391 @@
|
||||
/* eslint-disable max-len */
|
||||
/* eslint-disable no-bitwise */
|
||||
/**
|
||||
* @external Math
|
||||
*/
|
||||
/**
|
||||
* @memberof external:Math
|
||||
* @param {Float} value
|
||||
* @param {Float} precision
|
||||
* @returns {Float}
|
||||
*/
|
||||
function toFixedNumeric (value, precision) {
|
||||
if (precision === undefined) precision = 0;
|
||||
return Math.round(value * (10 ** precision)) / (10 ** precision);
|
||||
}
|
||||
/**
|
||||
* Whether a value is `null` or `undefined`.
|
||||
* @param {any} val
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isNullish = (val) => {
|
||||
return val === null || val === undefined;
|
||||
};
|
||||
/**
|
||||
* Controls for all the input elements for the typing in color values.
|
||||
*/
|
||||
export default class ColorValuePicker {
|
||||
/**
|
||||
* @param {external:jQuery} picker
|
||||
* @param {external:jQuery.jPicker.Color} color
|
||||
* @param {external:jQuery.fn.$.fn.jPicker} bindedHex
|
||||
* @param {Float} alphaPrecision
|
||||
*/
|
||||
constructor (picker, color, bindedHex, alphaPrecision) {
|
||||
const that = this; // private properties and methods
|
||||
const inputs = picker.querySelectorAll('td.Text input');
|
||||
// input box key down - use arrows to alter color
|
||||
/**
|
||||
*
|
||||
* @param {Event} e
|
||||
* @returns {Event|false|void}
|
||||
*/
|
||||
function keyDown (e) {
|
||||
if (e.target.value === '' && e.target !== hex && ((!isNullish(bindedHex) && e.target !== bindedHex) || isNullish(bindedHex))) return undefined;
|
||||
if (!validateKey(e)) return e;
|
||||
switch (e.target) {
|
||||
case red:
|
||||
switch (e.keyCode) {
|
||||
case 38:
|
||||
red.value = setValueInRange.call(that, (red.value << 0) + 1, 0, 255);
|
||||
color.val('r', red.value, e.target);
|
||||
return false;
|
||||
case 40:
|
||||
red.value = setValueInRange.call(that, (red.value << 0) - 1, 0, 255);
|
||||
color.val('r', red.value, e.target);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case green:
|
||||
switch (e.keyCode) {
|
||||
case 38:
|
||||
green.value = setValueInRange.call(that, (green.value << 0) + 1, 0, 255);
|
||||
color.val('g', green.value, e.target);
|
||||
return false;
|
||||
case 40:
|
||||
green.value = setValueInRange.call(that, (green.value << 0) - 1, 0, 255);
|
||||
color.val('g', green.value, e.target);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case blue:
|
||||
switch (e.keyCode) {
|
||||
case 38:
|
||||
blue.value = setValueInRange.call(that, (blue.value << 0) + 1, 0, 255);
|
||||
color.val('b', blue.value, e.target);
|
||||
return false;
|
||||
case 40:
|
||||
blue.value = setValueInRange.call(that, (blue.value << 0) - 1, 0, 255);
|
||||
color.val('b', blue.value, e.target);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case alpha:
|
||||
switch (e.keyCode) {
|
||||
case 38:
|
||||
alpha.value = setValueInRange.call(that, Number.parseFloat(alpha.value) + 1, 0, 100);
|
||||
color.val('a', toFixedNumeric((alpha.value * 255) / 100, alphaPrecision), e.target);
|
||||
return false;
|
||||
case 40:
|
||||
alpha.value = setValueInRange.call(that, Number.parseFloat(alpha.value) - 1, 0, 100);
|
||||
color.val('a', toFixedNumeric((alpha.value * 255) / 100, alphaPrecision), e.target);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case hue:
|
||||
switch (e.keyCode) {
|
||||
case 38:
|
||||
hue.value = setValueInRange.call(that, (hue.value << 0) + 1, 0, 360);
|
||||
color.val('h', hue.value, e.target);
|
||||
return false;
|
||||
case 40:
|
||||
hue.value =setValueInRange.call(that, (hue.value << 0) - 1, 0, 360);
|
||||
color.val('h', hue.value, e.target);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case saturation:
|
||||
switch (e.keyCode) {
|
||||
case 38:
|
||||
saturation.value = setValueInRange.call(that, (saturation.value << 0) + 1, 0, 100);
|
||||
color.val('s', saturation.value, e.target);
|
||||
return false;
|
||||
case 40:
|
||||
saturation.value = setValueInRange.call(that, (saturation.value << 0) - 1, 0, 100);
|
||||
color.val('s', saturation.value, e.target);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case value:
|
||||
switch (e.keyCode) {
|
||||
case 38:
|
||||
value.value = setValueInRange.call(that, (value.value << 0) + 1, 0, 100);
|
||||
color.val('v', value.value, e.target);
|
||||
return false;
|
||||
case 40:
|
||||
value.value = setValueInRange.call(that, (value.value << 0) - 1, 0, 100);
|
||||
color.val('v', value.value, e.target);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
// input box key up - validate value and set color
|
||||
/**
|
||||
* @param {Event} e
|
||||
* @returns {Event|void}
|
||||
* @todo Why is this returning an event?
|
||||
*/
|
||||
function keyUp (e) {
|
||||
if (e.target.value === '' && e.target !== hex &&
|
||||
((!isNullish(bindedHex) && e.target !== bindedHex) ||
|
||||
isNullish(bindedHex))) return undefined;
|
||||
if (!validateKey(e)) return e;
|
||||
switch (e.target) {
|
||||
case red:
|
||||
red.value = setValueInRange.call(that, red.value, 0, 255);
|
||||
color.val('r', red.value, e.target);
|
||||
break;
|
||||
case green:
|
||||
green.value = setValueInRange.call(that, green.value, 0, 255);
|
||||
color.val('g', green.value, e.target);
|
||||
break;
|
||||
case blue:
|
||||
blue.value = setValueInRange.call(that, blue.value, 0, 255);
|
||||
color.val('b', blue.value, e.target);
|
||||
break;
|
||||
case alpha:
|
||||
alpha.value = setValueInRange.call(that, alpha.value, 0, 100);
|
||||
color.val('a', toFixedNumeric((alpha.value * 255) / 100, alphaPrecision), e.target);
|
||||
break;
|
||||
case hue:
|
||||
hue.value = setValueInRange.call(that, hue.value, 0, 360);
|
||||
color.val('h', hue.value, e.target);
|
||||
break;
|
||||
case saturation:
|
||||
saturation.value = setValueInRange.call(that, saturation.value, 0, 100);
|
||||
color.val('s', saturation.value, e.target);
|
||||
break;
|
||||
case value:
|
||||
value.value = setValueInRange.call(that, value.value, 0, 100);
|
||||
color.val('v', value.value, e.target);
|
||||
break;
|
||||
case hex:
|
||||
hex.value = hex.value.replace(/[^a-fA-F\d]/g, '').toLowerCase().substring(0, 6);
|
||||
bindedHex && bindedHex.val(hex.value);
|
||||
color.val('hex', hex.value !== '' ? hex.value : null, e.target);
|
||||
break;
|
||||
case bindedHex:
|
||||
bindedHex.value = bindedHex.value.replace(/[^a-fA-F\d]/g, '').toLowerCase().substring(0, 6);
|
||||
hex.val(bindedHex.value);
|
||||
color.val('hex', bindedHex.value !== '' ? bindedHex.value : null, e.target);
|
||||
break;
|
||||
case ahex:
|
||||
ahex.value = ahex.value.replace(/[^a-fA-F\d]/g, '').toLowerCase().substring(0, 2);
|
||||
color.val('a', !isNullish(ahex.value) ? Number.parseInt(ahex.value, 16) : null, e.target);
|
||||
break;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
// input box blur - reset to original if value empty
|
||||
/**
|
||||
* @param {Event} e
|
||||
* @returns {void}
|
||||
*/
|
||||
function blur (e) {
|
||||
if (!isNullish(color.value)) {
|
||||
switch (e.target) {
|
||||
case red:
|
||||
color.value = 'r';
|
||||
red.value = color.value;
|
||||
break;
|
||||
case green:
|
||||
color.value = 'g';
|
||||
green.value = color.value;
|
||||
break;
|
||||
case blue:
|
||||
color.value = 'b';
|
||||
blue.value = color.value;
|
||||
break;
|
||||
case alpha:
|
||||
color.value = 'a';
|
||||
alpha.value = toFixedNumeric((color.value * 100) / 255, alphaPrecision);
|
||||
break;
|
||||
case hue:
|
||||
color.value = 'h';
|
||||
hue.value = color.value;
|
||||
break;
|
||||
case saturation:
|
||||
color.value = 's';
|
||||
saturation.value = color.value;
|
||||
break;
|
||||
case value:
|
||||
color.value = 'v';
|
||||
value.value = color.value;
|
||||
break;
|
||||
case hex:
|
||||
case bindedHex:
|
||||
color.value = 'hex';
|
||||
hex.value = color.value;
|
||||
bindedHex.value = color.value;
|
||||
break;
|
||||
case ahex:
|
||||
color.value = 'ahex';
|
||||
ahex.value = color.value.substring(6);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {Event} e
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function validateKey (e) {
|
||||
switch (e.keyCode) {
|
||||
case 9:
|
||||
case 16:
|
||||
case 29:
|
||||
case 37:
|
||||
case 39:
|
||||
return false;
|
||||
case 'c'.charCodeAt():
|
||||
case 'v'.charCodeAt():
|
||||
if (e.ctrlKey) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constrain value within range.
|
||||
* @param {Float|string} value
|
||||
* @param {Float} min
|
||||
* @param {Float} max
|
||||
* @returns {Float|string} Returns a number or numeric string
|
||||
*/
|
||||
function setValueInRange (value, min, max) {
|
||||
if (value === '' || isNaN(value)) return min;
|
||||
if (value > max) return max;
|
||||
if (value < min) return min;
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* @param {external:jQuery} ui
|
||||
* @param {Element} context
|
||||
* @returns {void}
|
||||
*/
|
||||
function colorChanged (ui, context) {
|
||||
const all = ui.val('all');
|
||||
if (context !== red) red.value = (!isNullish(all) ? all.r : '');
|
||||
if (context !== green) green.value = (!isNullish(all) ? all.g : '');
|
||||
if (context !== blue) blue.value = (!isNullish(all) ? all.b : '');
|
||||
if (alpha && context !== alpha) alpha.value = (!isNullish(all) ? toFixedNumeric((all.a * 100) / 255, alphaPrecision) : '');
|
||||
if (context !== hue) hue.value = (!isNullish(all) ? all.h : '');
|
||||
if (context !== saturation) saturation.value = (!isNullish(all) ? all.s : '');
|
||||
if (context !== value) value.value = (!isNullish(all) ? all.v : '');
|
||||
if (context !== hex && ((bindedHex && context !== bindedHex) || !bindedHex)) hex.value = (!isNullish(all) ? all.hex : '');
|
||||
if (bindedHex && context !== bindedHex && context !== hex) bindedHex.value = (!isNullish(all) ? all.hex : '');
|
||||
if (ahex && context !== ahex) ahex.value = (!isNullish(all) ? all.ahex.substring(6) : '');
|
||||
}
|
||||
/**
|
||||
* Unbind all events and null objects.
|
||||
* @returns {void}
|
||||
*/
|
||||
function destroy () {
|
||||
red.removeEventListener('keyup', keyUp);
|
||||
green.removeEventListener('keyup', keyUp);
|
||||
blue.removeEventListener('keyup', keyUp);
|
||||
hue.removeEventListener('keyup', keyUp);
|
||||
saturation.removeEventListener('keyup', keyUp);
|
||||
value.removeEventListener('keyup', keyUp);
|
||||
hex.removeEventListener('keyup', keyUp);
|
||||
|
||||
red.removeEventListener('blur', blur);
|
||||
green.removeEventListener('blur', blur);
|
||||
blue.removeEventListener('blur', blur);
|
||||
hue.removeEventListener('blur', blur);
|
||||
saturation.removeEventListener('blur', blur);
|
||||
value.removeEventListener('blur', blur);
|
||||
hex.removeEventListener('blur', blur);
|
||||
|
||||
red.removeEventListener('keydown', keyDown);
|
||||
green.removeEventListener('keydown', keyDown);
|
||||
blue.removeEventListener('keydown', keyDown);
|
||||
hue.removeEventListener('keydown', keyDown);
|
||||
saturation.removeEventListener('keydown', keyDown);
|
||||
value.removeEventListener('keydown', keyDown);
|
||||
|
||||
if (alpha !== null) {
|
||||
alpha.removeEventListener('keyup', keyUp);
|
||||
alpha.removeEventListener('blur', blur);
|
||||
alpha.removeEventListener('keydown', keyDown);
|
||||
}
|
||||
if (ahex !== null) {
|
||||
ahex.removeEventListener('keyup', keyUp);
|
||||
ahex.removeEventListener('blur', blur);
|
||||
}
|
||||
if (bindedHex !== null) {
|
||||
bindedHex.removeEventListener('keyup', keyUp);
|
||||
bindedHex.removeEventListener('blur', blur);
|
||||
}
|
||||
color.unbind(colorChanged);
|
||||
red = null;
|
||||
green = null;
|
||||
blue = null;
|
||||
alpha = null;
|
||||
hue = null;
|
||||
saturation = null;
|
||||
value = null;
|
||||
hex = null;
|
||||
ahex = null;
|
||||
}
|
||||
let
|
||||
red = inputs[3],
|
||||
green = inputs[4],
|
||||
blue = inputs[5],
|
||||
alpha = inputs.length > 7 ? inputs[6] : null,
|
||||
hue = inputs[0],
|
||||
saturation = inputs[1],
|
||||
value = inputs[2],
|
||||
hex = inputs[(inputs.length > 7) ? 7 : 6],
|
||||
ahex = inputs.length > 7 ? inputs[8] : null;
|
||||
Object.assign(that, {destroy});
|
||||
red.addEventListener('keyup', keyUp);
|
||||
green.addEventListener('keyup', keyUp);
|
||||
blue.addEventListener('keyup', keyUp);
|
||||
hue.addEventListener('keyup', keyUp);
|
||||
saturation.addEventListener('keyup', keyUp);
|
||||
value.addEventListener('keyup', keyUp);
|
||||
hex.addEventListener('keyup', keyUp);
|
||||
|
||||
red.addEventListener('blur', blur);
|
||||
green.addEventListener('blur', blur);
|
||||
blue.addEventListener('blur', blur);
|
||||
hue.addEventListener('blur', blur);
|
||||
saturation.addEventListener('blur', blur);
|
||||
value.addEventListener('blur', blur);
|
||||
hex.addEventListener('blur', blur);
|
||||
|
||||
red.addEventListener('keydown', keyDown);
|
||||
green.addEventListener('keydown', keyDown);
|
||||
blue.addEventListener('keydown', keyDown);
|
||||
hue.addEventListener('keydown', keyDown);
|
||||
saturation.addEventListener('keydown', keyDown);
|
||||
value.addEventListener('keydown', keyDown);
|
||||
|
||||
if (alpha !== null) {
|
||||
alpha.addEventListener('keyup', keyUp);
|
||||
alpha.addEventListener('blur', blur);
|
||||
alpha.addEventListener('keydown', keyDown);
|
||||
}
|
||||
if (ahex !== null) {
|
||||
ahex.addEventListener('keyup', keyUp);
|
||||
ahex.addEventListener('blur', blur);
|
||||
}
|
||||
if (bindedHex !== null) {
|
||||
bindedHex.addEventListener('keyup', keyUp);
|
||||
bindedHex.addEventListener('blur', blur);
|
||||
}
|
||||
color.bind(colorChanged);
|
||||
}
|
||||
}
|
||||
202
src/editor/components/jgraduate/LICENSE-Apache2.0.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
3
src/editor/components/jgraduate/README
Normal file
@@ -0,0 +1,3 @@
|
||||
jGraduate - A jQuery plugin for picking gradients
|
||||
|
||||
Licensed under the Apache License 2. See LICENSE for more information.
|
||||
338
src/editor/components/jgraduate/Slider.js
Normal file
@@ -0,0 +1,338 @@
|
||||
/* eslint-disable no-bitwise */
|
||||
import {findPos} from './Util.js';
|
||||
/**
|
||||
* Whether a value is `null` or `undefined`.
|
||||
* @param {any} val
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isNullish = (val) => {
|
||||
return val === null || val === undefined;
|
||||
};
|
||||
/**
|
||||
* Encapsulate slider functionality for the ColorMap and ColorBar -
|
||||
* could be useful to use a jQuery UI draggable for this with certain extensions.
|
||||
* @memberof module:jPicker
|
||||
*/
|
||||
export default class Slider {
|
||||
/**
|
||||
* @param {external:jQuery} bar
|
||||
* @param {module:jPicker.SliderOptions} options
|
||||
*/
|
||||
constructor (bar, options) {
|
||||
const that = this;
|
||||
/**
|
||||
* Fire events on the supplied `context`
|
||||
* @param {module:jPicker.JPickerInit} context
|
||||
* @returns {void}
|
||||
*/
|
||||
function fireChangeEvents (context) {
|
||||
changeEvents.forEach((changeEvent) => {
|
||||
changeEvent.call(that, that, context);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the mousedown to the bar not the arrow for quick snapping to the clicked location.
|
||||
* @param {external:jQuery.Event} e
|
||||
* @returns {void}
|
||||
*/
|
||||
function mouseDown (e) {
|
||||
const off = findPos(bar);
|
||||
offset = {l: off.left | 0, t: off.top | 0};
|
||||
clearTimeout(timeout);
|
||||
// using setTimeout for visual updates - once the style is updated the browser will re-render internally allowing the next Javascript to run
|
||||
timeout = setTimeout(function () {
|
||||
setValuesFromMousePosition.call(that, e);
|
||||
}, 0);
|
||||
// Bind mousemove and mouseup event to the document so it responds when dragged of of the bar - we will unbind these when on mouseup to save processing
|
||||
document.addEventListener('mousemove', mouseMove);
|
||||
document.addEventListener('mouseup', mouseUp);
|
||||
e.preventDefault(); // don't try to select anything or drag the image to the desktop
|
||||
}
|
||||
/**
|
||||
* Set the values as the mouse moves.
|
||||
* @param {external:jQuery.Event} e
|
||||
* @returns {false}
|
||||
*/
|
||||
function mouseMove (e) {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(function () {
|
||||
setValuesFromMousePosition.call(that, e);
|
||||
}, 0);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Unbind the document events - they aren't needed when not dragging.
|
||||
* @param {external:jQuery.Event} e
|
||||
* @returns {false}
|
||||
*/
|
||||
function mouseUp (e) {
|
||||
document.removeEventListener('mousemove', mouseMove);
|
||||
document.removeEventListener('mouseup', mouseUp);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate mouse position and set value within the current range.
|
||||
* @param {Event} e
|
||||
* @returns {void}
|
||||
*/
|
||||
function setValuesFromMousePosition (e) {
|
||||
const barW = bar.w, // local copies for YUI compressor
|
||||
barH = bar.h;
|
||||
let locX = e.pageX - offset.l,
|
||||
locY = e.pageY - offset.t;
|
||||
// keep the arrow within the bounds of the bar
|
||||
if (locX < 0) locX = 0;
|
||||
else if (locX > barW) locX = barW;
|
||||
if (locY < 0) locY = 0;
|
||||
else if (locY > barH) locY = barH;
|
||||
val.call(that, 'xy', {
|
||||
x: ((locX / barW) * rangeX) + minX,
|
||||
y: ((locY / barH) * rangeY) + minY
|
||||
});
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function draw () {
|
||||
const
|
||||
barW = bar.w,
|
||||
barH = bar.h,
|
||||
arrowW = arrow.w,
|
||||
arrowH = arrow.h;
|
||||
let arrowOffsetX = 0,
|
||||
arrowOffsetY = 0;
|
||||
setTimeout(function () {
|
||||
if (rangeX > 0) { // range is greater than zero
|
||||
// constrain to bounds
|
||||
if (x === maxX) arrowOffsetX = barW;
|
||||
else arrowOffsetX = ((x / rangeX) * barW) | 0;
|
||||
}
|
||||
if (rangeY > 0) { // range is greater than zero
|
||||
// constrain to bounds
|
||||
if (y === maxY) arrowOffsetY = barH;
|
||||
else arrowOffsetY = ((y / rangeY) * barH) | 0;
|
||||
}
|
||||
// if arrow width is greater than bar width, center arrow and prevent horizontal dragging
|
||||
if (arrowW >= barW) arrowOffsetX = (barW >> 1) - (arrowW >> 1); // number >> 1 - superfast bitwise divide by two and truncate (move bits over one bit discarding lowest)
|
||||
else arrowOffsetX -= arrowW >> 1;
|
||||
// if arrow height is greater than bar height, center arrow and prevent vertical dragging
|
||||
if (arrowH >= barH) arrowOffsetY = (barH >> 1) - (arrowH >> 1);
|
||||
else arrowOffsetY -= arrowH >> 1;
|
||||
// set the arrow position based on these offsets
|
||||
arrow.style.left = arrowOffsetX + 'px';
|
||||
arrow.style.top = arrowOffsetY + 'px';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set a value.
|
||||
* @param {?("xy"|"x"|"y")} name
|
||||
* @param {module:math.XYObject} value
|
||||
* @param {module:jPicker.Slider} context
|
||||
* @returns {module:math.XYObject|Float|void}
|
||||
*/
|
||||
function val (name, value, context) {
|
||||
const set = value !== undefined;
|
||||
if (!set) {
|
||||
if (isNullish(name)) name = 'xy';
|
||||
switch (name.toLowerCase()) {
|
||||
case 'x': return x;
|
||||
case 'y': return y;
|
||||
case 'xy':
|
||||
default: return {x, y};
|
||||
}
|
||||
}
|
||||
if (!isNullish(context) && context === that) return undefined;
|
||||
let changed = false;
|
||||
|
||||
let newX, newY;
|
||||
if (isNullish(name)) name = 'xy';
|
||||
switch (name.toLowerCase()) {
|
||||
case 'x':
|
||||
newX = (value && ((value.x && value.x | 0) || value | 0)) || 0;
|
||||
break;
|
||||
case 'y':
|
||||
newY = (value && ((value.y && value.y | 0) || value | 0)) || 0;
|
||||
break;
|
||||
case 'xy':
|
||||
default:
|
||||
newX = (value && value.x && value.x | 0) || 0;
|
||||
newY = (value && value.y && value.y | 0) || 0;
|
||||
break;
|
||||
}
|
||||
if (!isNullish(newX)) {
|
||||
if (newX < minX) newX = minX;
|
||||
else if (newX > maxX) newX = maxX;
|
||||
if (x !== newX) {
|
||||
x = newX;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (!isNullish(newY)) {
|
||||
if (newY < minY) newY = minY;
|
||||
else if (newY > maxY) newY = maxY;
|
||||
if (y !== newY) {
|
||||
y = newY;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
changed && fireChangeEvents.call(that, context || that);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {PlainObject} module:jPicker.MinMaxRangeX
|
||||
* @property {Float} minX
|
||||
* @property {Float} maxX
|
||||
* @property {Float} rangeX
|
||||
*/
|
||||
/**
|
||||
* @typedef {PlainObject} module:jPicker.MinMaxRangeY
|
||||
* @property {Float} minY
|
||||
* @property {Float} maxY
|
||||
* @property {Float} rangeY
|
||||
*/
|
||||
/**
|
||||
* @typedef {module:jPicker.MinMaxRangeY|module:jPicker.MinMaxRangeX} module:jPicker.MinMaxRangeXY
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {"minx"|"maxx"|"rangex"|"miny"|"maxy"|"rangey"|"all"} name
|
||||
* @param {module:jPicker.MinMaxRangeXY} value
|
||||
* @returns {module:jPicker.MinMaxRangeXY|module:jPicker.MinMaxRangeX|module:jPicker.MinMaxRangeY|void}
|
||||
*/
|
||||
function range (name, value) {
|
||||
const set = value !== undefined;
|
||||
if (!set) {
|
||||
if (isNullish(name)) name = 'all';
|
||||
switch (name.toLowerCase()) {
|
||||
case 'minx': return minX;
|
||||
case 'maxx': return maxX;
|
||||
case 'rangex': return {minX, maxX, rangeX};
|
||||
case 'miny': return minY;
|
||||
case 'maxy': return maxY;
|
||||
case 'rangey': return {minY, maxY, rangeY};
|
||||
case 'all':
|
||||
default: return {minX, maxX, rangeX, minY, maxY, rangeY};
|
||||
}
|
||||
}
|
||||
let // changed = false,
|
||||
newMinX,
|
||||
newMaxX,
|
||||
newMinY,
|
||||
newMaxY;
|
||||
if (isNullish(name)) name = 'all';
|
||||
switch (name.toLowerCase()) {
|
||||
case 'minx':
|
||||
newMinX = (value && ((value.minX && value.minX | 0) || value | 0)) || 0;
|
||||
break;
|
||||
case 'maxx':
|
||||
newMaxX = (value && ((value.maxX && value.maxX | 0) || value | 0)) || 0;
|
||||
break;
|
||||
case 'rangex':
|
||||
newMinX = (value && value.minX && value.minX | 0) || 0;
|
||||
newMaxX = (value && value.maxX && value.maxX | 0) || 0;
|
||||
break;
|
||||
case 'miny':
|
||||
newMinY = (value && ((value.minY && value.minY | 0) || value | 0)) || 0;
|
||||
break;
|
||||
case 'maxy':
|
||||
newMaxY = (value && ((value.maxY && value.maxY | 0) || value | 0)) || 0;
|
||||
break;
|
||||
case 'rangey':
|
||||
newMinY = (value && value.minY && value.minY | 0) || 0;
|
||||
newMaxY = (value && value.maxY && value.maxY | 0) || 0;
|
||||
break;
|
||||
case 'all':
|
||||
default:
|
||||
newMinX = (value && value.minX && value.minX | 0) || 0;
|
||||
newMaxX = (value && value.maxX && value.maxX | 0) || 0;
|
||||
newMinY = (value && value.minY && value.minY | 0) || 0;
|
||||
newMaxY = (value && value.maxY && value.maxY | 0) || 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isNullish(newMinX) && minX !== newMinX) {
|
||||
minX = newMinX;
|
||||
rangeX = maxX - minX;
|
||||
}
|
||||
if (!isNullish(newMaxX) && maxX !== newMaxX) {
|
||||
maxX = newMaxX;
|
||||
rangeX = maxX - minX;
|
||||
}
|
||||
if (!isNullish(newMinY) && minY !== newMinY) {
|
||||
minY = newMinY;
|
||||
rangeY = maxY - minY;
|
||||
}
|
||||
if (!isNullish(newMaxY) && maxY !== newMaxY) {
|
||||
maxY = newMaxY;
|
||||
rangeY = maxY - minY;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
/**
|
||||
* @param {GenericCallback} callback
|
||||
* @returns {void}
|
||||
*/
|
||||
function bind (callback) { // eslint-disable-line promise/prefer-await-to-callbacks
|
||||
if (typeof callback === 'function') changeEvents.push(callback);
|
||||
}
|
||||
/**
|
||||
* @param {GenericCallback} callback
|
||||
* @returns {void}
|
||||
*/
|
||||
function unbind (callback) { // eslint-disable-line promise/prefer-await-to-callbacks
|
||||
if (typeof callback !== 'function') return;
|
||||
let i;
|
||||
while ((i = changeEvents.includes(callback))) changeEvents.splice(i, 1);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function destroy () {
|
||||
// unbind all possible events and null objects
|
||||
document.removeEventListener('mousemove', mouseMove);
|
||||
document.removeEventListener('mouseup', mouseUp);
|
||||
bar.removeEventListener('mousedown', mouseDown);
|
||||
bar = null;
|
||||
arrow = null;
|
||||
changeEvents = null;
|
||||
}
|
||||
let offset,
|
||||
timeout,
|
||||
x = 0,
|
||||
y = 0,
|
||||
minX = 0,
|
||||
maxX = 100,
|
||||
rangeX = 100,
|
||||
minY = 0,
|
||||
maxY = 100,
|
||||
rangeY = 100,
|
||||
arrow = bar.querySelector('img'), // the arrow image to drag
|
||||
changeEvents = [];
|
||||
Object.assign(that, {
|
||||
val,
|
||||
range,
|
||||
bind,
|
||||
unbind,
|
||||
destroy
|
||||
});
|
||||
// initialize this control
|
||||
arrow.src = options.arrow && options.arrow.image;
|
||||
arrow.w = (options.arrow && options.arrow.width) || parseFloat(getComputedStyle(arrow, null).width.replace("px", ""));
|
||||
arrow.h = (options.arrow && options.arrow.height) || parseFloat(getComputedStyle(arrow, null).height.replace("px", ""));
|
||||
bar.w = (options.map && options.map.width) || parseFloat(getComputedStyle(bar, null).width.replace("px", ""));
|
||||
bar.h = (options.map && options.map.height) || parseFloat(getComputedStyle(bar, null).height.replace("px", ""));
|
||||
bar.addEventListener('mousedown', mouseDown);
|
||||
bind.call(that, draw);
|
||||
}
|
||||
}
|
||||
218
src/editor/components/jgraduate/Util.js
Normal file
@@ -0,0 +1,218 @@
|
||||
/* eslint-disable sonarjs/no-collapsible-if */
|
||||
/**
|
||||
* @param {any} obj
|
||||
* @returns {any}
|
||||
*/
|
||||
export function findPos(obj) {
|
||||
let curleft = 0;
|
||||
let curtop = 0;
|
||||
if (obj.offsetParent) {
|
||||
do {
|
||||
curleft += obj.offsetLeft;
|
||||
curtop += obj.offsetTop;
|
||||
} while (obj == obj.offsetParent);
|
||||
return { left: curleft, top: curtop };
|
||||
}
|
||||
return { left: curleft, top: curtop };
|
||||
}
|
||||
|
||||
export function isObject(item) {
|
||||
return (item && typeof item === 'object' && !Array.isArray(item));
|
||||
}
|
||||
|
||||
export function mergeDeep(target, source) {
|
||||
let output = Object.assign({}, target);
|
||||
if (isObject(target) && isObject(source)) {
|
||||
Object.keys(source).forEach(key => {
|
||||
if (isObject(source[key])) {
|
||||
if (!(key in target))
|
||||
Object.assign(output, { [key]: source[key] });
|
||||
else
|
||||
output[key] = mergeDeep(target[key], source[key]);
|
||||
} else {
|
||||
Object.assign(output, { [key]: source[key] });
|
||||
}
|
||||
});
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the closest matching element up the DOM tree.
|
||||
* @param {Element} elem Starting element
|
||||
* @param {String} selector Selector to match against (class, ID, data attribute, or tag)
|
||||
* @return {Boolean|Element} Returns null if not match found
|
||||
*/
|
||||
export function getClosest(elem, selector) {
|
||||
const firstChar = selector.charAt(0);
|
||||
const supports = 'classList' in document.documentElement;
|
||||
let attribute, value;
|
||||
// If selector is a data attribute, split attribute from value
|
||||
if (firstChar === '[') {
|
||||
selector = selector.substr(1, selector.length - 2);
|
||||
attribute = selector.split('=');
|
||||
if (attribute.length > 1) {
|
||||
value = true;
|
||||
attribute[1] = attribute[1].replace(/"/g, '').replace(/'/g, '');
|
||||
}
|
||||
}
|
||||
// Get closest match
|
||||
for (; elem && elem !== document && elem.nodeType === 1; elem = elem.parentNode) {
|
||||
// If selector is a class
|
||||
if (firstChar === '.') {
|
||||
if (supports) {
|
||||
if (elem.classList.contains(selector.substr(1))) {
|
||||
return elem;
|
||||
}
|
||||
} else {
|
||||
if (new RegExp('(^|\\s)' + selector.substr(1) + '(\\s|$)').test(elem.className)) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If selector is an ID
|
||||
if (firstChar === '#') {
|
||||
if (elem.id === selector.substr(1)) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
// If selector is a data attribute
|
||||
if (firstChar === '[') {
|
||||
if (elem.hasAttribute(attribute[0])) {
|
||||
if (value) {
|
||||
if (elem.getAttribute(attribute[0]) === attribute[1]) {
|
||||
return elem;
|
||||
}
|
||||
} else {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If selector is a tag
|
||||
if (elem.tagName.toLowerCase() === selector) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all DOM element up the tree that contain a class, ID, or data attribute
|
||||
* @param {Node} elem The base element
|
||||
* @param {String} selector The class, id, data attribute, or tag to look for
|
||||
* @return {Array} Null if no match
|
||||
*/
|
||||
export function getParents(elem, selector) {
|
||||
const parents = [];
|
||||
let firstChar;
|
||||
if ( selector ) {
|
||||
firstChar = selector.charAt(0);
|
||||
}
|
||||
// Get matches
|
||||
for ( ; elem && elem !== document; elem = elem.parentNode ) {
|
||||
if ( selector ) {
|
||||
// If selector is a class
|
||||
if ( firstChar === '.' ) {
|
||||
if ( elem.classList.contains( selector.substr(1) ) ) {
|
||||
parents.push( elem );
|
||||
}
|
||||
}
|
||||
// If selector is an ID
|
||||
if ( firstChar === '#' ) {
|
||||
if ( elem.id === selector.substr(1) ) {
|
||||
parents.push( elem );
|
||||
}
|
||||
}
|
||||
// If selector is a data attribute
|
||||
if ( firstChar === '[' ) {
|
||||
if ( elem.hasAttribute( selector.substr(1, selector.length - 1) ) ) {
|
||||
parents.push( elem );
|
||||
}
|
||||
}
|
||||
// If selector is a tag
|
||||
if ( elem.tagName.toLowerCase() === selector ) {
|
||||
parents.push( elem );
|
||||
}
|
||||
} else {
|
||||
parents.push( elem );
|
||||
}
|
||||
}
|
||||
// Return parents if any exist
|
||||
if ( parents.length === 0 ) {
|
||||
return null;
|
||||
} else {
|
||||
return parents;
|
||||
}
|
||||
}
|
||||
|
||||
export function getParentsUntil(elem, parent, selector) {
|
||||
let parents = [];
|
||||
let parentType;
|
||||
let selectorType;
|
||||
if ( parent ) {
|
||||
parentType = parent.charAt(0);
|
||||
}
|
||||
if ( selector ) {
|
||||
selectorType = selector.charAt(0);
|
||||
}
|
||||
// Get matches
|
||||
for ( ; elem && elem !== document; elem = elem.parentNode ) {
|
||||
// Check if parent has been reached
|
||||
if ( parent ) {
|
||||
// If parent is a class
|
||||
if ( parentType === '.' ) {
|
||||
if ( elem.classList.contains( parent.substr(1) ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If parent is an ID
|
||||
if ( parentType === '#' ) {
|
||||
if ( elem.id === parent.substr(1) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If parent is a data attribute
|
||||
if ( parentType === '[' ) {
|
||||
if ( elem.hasAttribute( parent.substr(1, parent.length - 1) ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If parent is a tag
|
||||
if ( elem.tagName.toLowerCase() === parent ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( selector ) {
|
||||
// If selector is a class
|
||||
if ( selectorType === '.' ) {
|
||||
if ( elem.classList.contains( selector.substr(1) ) ) {
|
||||
parents.push( elem );
|
||||
}
|
||||
}
|
||||
// If selector is an ID
|
||||
if ( selectorType === '#' ) {
|
||||
if ( elem.id === selector.substr(1) ) {
|
||||
parents.push( elem );
|
||||
}
|
||||
}
|
||||
// If selector is a data attribute
|
||||
if ( selectorType === '[' ) {
|
||||
if ( elem.hasAttribute( selector.substr(1, selector.length - 1) ) ) {
|
||||
parents.push( elem );
|
||||
}
|
||||
}
|
||||
// If selector is a tag
|
||||
if ( elem.tagName.toLowerCase() === selector ) {
|
||||
parents.push( elem );
|
||||
}
|
||||
} else {
|
||||
parents.push( elem );
|
||||
}
|
||||
}
|
||||
// Return parents if any exist
|
||||
if ( parents.length === 0 ) {
|
||||
return null;
|
||||
} else {
|
||||
return parents;
|
||||
}
|
||||
}
|
||||
BIN
src/editor/components/jgraduate/images/AlphaBar.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/editor/components/jgraduate/images/Bars.png
Normal file
|
After Width: | Height: | Size: 265 B |
BIN
src/editor/components/jgraduate/images/Maps.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
src/editor/components/jgraduate/images/NoColor.png
Normal file
|
After Width: | Height: | Size: 473 B |
BIN
src/editor/components/jgraduate/images/bar-opacity.png
Normal file
|
After Width: | Height: | Size: 80 B |
BIN
src/editor/components/jgraduate/images/map-opacity.png
Normal file
|
After Width: | Height: | Size: 81 B |
BIN
src/editor/components/jgraduate/images/mappoint.gif
Normal file
|
After Width: | Height: | Size: 93 B |
BIN
src/editor/components/jgraduate/images/mappoint_c.png
Normal file
|
After Width: | Height: | Size: 141 B |
BIN
src/editor/components/jgraduate/images/mappoint_f.png
Normal file
|
After Width: | Height: | Size: 144 B |
BIN
src/editor/components/jgraduate/images/picker.gif
Normal file
|
After Width: | Height: | Size: 146 B |
BIN
src/editor/components/jgraduate/images/preview-opacity.png
Normal file
|
After Width: | Height: | Size: 80 B |
BIN
src/editor/components/jgraduate/images/rangearrows.gif
Normal file
|
After Width: | Height: | Size: 76 B |
BIN
src/editor/components/jgraduate/images/rangearrows2.gif
Normal file
|
After Width: | Height: | Size: 93 B |
1286
src/editor/components/jgraduate/jQuery.jGraduate.js
Normal file
1926
src/editor/components/jgraduate/jQuery.jPicker.js
Executable file
78
src/editor/components/jgraduate/paint.js
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export default class Paint {
|
||||
/**
|
||||
* @param {module:jGraduate.jGraduatePaintOptions} [opt]
|
||||
*/
|
||||
constructor (opt) {
|
||||
const options = opt || {};
|
||||
this.alpha = isNaN(options.alpha) ? 100 : options.alpha;
|
||||
// copy paint object
|
||||
if (options.copy) {
|
||||
/**
|
||||
* @name module:jGraduate~Paint#type
|
||||
* @type {"none"|"solidColor"|"linearGradient"|"radialGradient"}
|
||||
*/
|
||||
this.type = options.copy.type;
|
||||
/**
|
||||
* Represents opacity (0-100).
|
||||
* @name module:jGraduate~Paint#alpha
|
||||
* @type {Float}
|
||||
*/
|
||||
this.alpha = options.copy.alpha;
|
||||
/**
|
||||
* Represents #RRGGBB hex of color.
|
||||
* @name module:jGraduate~Paint#solidColor
|
||||
* @type {string}
|
||||
*/
|
||||
this.solidColor = null;
|
||||
/**
|
||||
* @name module:jGraduate~Paint#linearGradient
|
||||
* @type {SVGLinearGradientElement}
|
||||
*/
|
||||
this.linearGradient = null;
|
||||
/**
|
||||
* @name module:jGraduate~Paint#radialGradient
|
||||
* @type {SVGRadialGradientElement}
|
||||
*/
|
||||
this.radialGradient = null;
|
||||
|
||||
switch (this.type) {
|
||||
case 'none':
|
||||
break;
|
||||
case 'solidColor':
|
||||
this.solidColor = options.copy.solidColor;
|
||||
break;
|
||||
case 'linearGradient':
|
||||
this.linearGradient = options.copy.linearGradient.cloneNode(true);
|
||||
break;
|
||||
case 'radialGradient':
|
||||
this.radialGradient = options.copy.radialGradient.cloneNode(true);
|
||||
break;
|
||||
}
|
||||
// create linear gradient paint
|
||||
} else if (options.linearGradient) {
|
||||
this.type = 'linearGradient';
|
||||
this.solidColor = null;
|
||||
this.radialGradient = null;
|
||||
this.linearGradient = options.linearGradient.cloneNode(true);
|
||||
// create linear gradient paint
|
||||
} else if (options.radialGradient) {
|
||||
this.type = 'radialGradient';
|
||||
this.solidColor = null;
|
||||
this.linearGradient = null;
|
||||
this.radialGradient = options.radialGradient.cloneNode(true);
|
||||
// create solid color paint
|
||||
} else if (options.solidColor) {
|
||||
this.type = 'solidColor';
|
||||
this.solidColor = options.solidColor;
|
||||
// create empty paint
|
||||
} else {
|
||||
this.type = 'none';
|
||||
this.solidColor = null;
|
||||
this.linearGradient = null;
|
||||
this.radialGradient = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
227
src/editor/components/seButton.js
Normal file
@@ -0,0 +1,227 @@
|
||||
// import {isMac} from '../../common/browser.js';
|
||||
// if (isMac() && !window.opera) 'Ctrl+' 'Cmd+'
|
||||
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
:host(:hover) :not(.disabled)
|
||||
{
|
||||
background-color: var(--icon-bg-color-hover);
|
||||
}
|
||||
div
|
||||
{
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
margin: 2px 1px 4px;
|
||||
padding: 3px;
|
||||
background-color: var(--icon-bg-color);
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.small {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
padding: 1px;
|
||||
border-radius: 1px;
|
||||
}
|
||||
img {
|
||||
border: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.pressed {
|
||||
background-color: var(--icon-bg-color-hover);
|
||||
}
|
||||
.disabled {
|
||||
opacity: 0.3;
|
||||
cursor: default;
|
||||
}
|
||||
</style>
|
||||
<div title="title">
|
||||
<img src="./images/logo.svg" alt="icon">
|
||||
</div>
|
||||
`;
|
||||
/**
|
||||
* @class ToolButton
|
||||
*/
|
||||
export class ToolButton extends HTMLElement {
|
||||
/**
|
||||
* @function constructor
|
||||
*/
|
||||
constructor () {
|
||||
super();
|
||||
// create the shadowDom and insert the template
|
||||
this._shadowRoot = this.attachShadow({mode: 'open'});
|
||||
this._shadowRoot.append(template.content.cloneNode(true));
|
||||
// locate the component
|
||||
this.$div = this._shadowRoot.querySelector('div');
|
||||
this.$img = this._shadowRoot.querySelector('img');
|
||||
}
|
||||
/**
|
||||
* @function observedAttributes
|
||||
* @returns {any} observed
|
||||
*/
|
||||
static get observedAttributes () {
|
||||
return ['title', 'src', 'pressed', 'disabled', 'size', 'style'];
|
||||
}
|
||||
/**
|
||||
* @function attributeChangedCallback
|
||||
* @param {string} name
|
||||
* @param {string} oldValue
|
||||
* @param {string} newValue
|
||||
* @returns {void}
|
||||
*/
|
||||
attributeChangedCallback (name, oldValue, newValue) {
|
||||
if (oldValue === newValue) return;
|
||||
switch (name) {
|
||||
case 'title':
|
||||
{
|
||||
const shortcut = this.getAttribute('shortcut');
|
||||
this.$div.setAttribute('title', `${newValue} ${shortcut ? `[${shortcut}]` : ''}`);
|
||||
}
|
||||
break;
|
||||
case 'style':
|
||||
this.$div.style = newValue;
|
||||
break;
|
||||
case 'src':
|
||||
this.$img.setAttribute('src', newValue);
|
||||
break;
|
||||
case 'pressed':
|
||||
if (newValue) {
|
||||
this.$div.classList.add('pressed');
|
||||
} else {
|
||||
this.$div.classList.remove('pressed');
|
||||
}
|
||||
break;
|
||||
case 'size':
|
||||
if (newValue === 'small') {
|
||||
this.$div.classList.add('small');
|
||||
} else {
|
||||
this.$div.classList.remove('small');
|
||||
}
|
||||
break;
|
||||
case 'disabled':
|
||||
if (newValue) {
|
||||
this.$div.classList.add('disabled');
|
||||
} else {
|
||||
this.$div.classList.remove('disabled');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`unknown attribute: ${name}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get title () {
|
||||
return this.getAttribute('title');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set title (value) {
|
||||
this.setAttribute('title', value);
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get pressed () {
|
||||
return this.hasAttribute('pressed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set pressed (value) {
|
||||
// boolean value => existence = true
|
||||
if (value) {
|
||||
this.setAttribute('pressed', 'true');
|
||||
} else {
|
||||
this.removeAttribute('pressed', '');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get disabled () {
|
||||
return this.hasAttribute('disabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set disabled (value) {
|
||||
// boolean value => existence = true
|
||||
if (value) {
|
||||
this.setAttribute('disabled', 'true');
|
||||
} else {
|
||||
this.removeAttribute('disabled', '');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get src () {
|
||||
return this.getAttribute('src');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set src (value) {
|
||||
this.setAttribute('src', value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get size () {
|
||||
return this.getAttribute('size');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set size (value) {
|
||||
this.setAttribute('size', value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function connectedCallback
|
||||
* @returns {void}
|
||||
*/
|
||||
connectedCallback () {
|
||||
// capture shortcuts
|
||||
const shortcut = this.getAttribute('shortcut');
|
||||
if (shortcut) {
|
||||
// register the keydown event
|
||||
document.addEventListener('keydown', (e) => {
|
||||
// only track keyboard shortcuts for the body containing the SVG-Editor
|
||||
if (e.target.nodeName !== 'BODY') return;
|
||||
// normalize key
|
||||
const key = `${(e.metaKey) ? 'meta+' : ''}${(e.ctrlKey) ? 'ctrl+' : ''}${e.key.toUpperCase()}`;
|
||||
if (shortcut !== key) return;
|
||||
// launch the click event
|
||||
this.click();
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Register
|
||||
customElements.define('se-button', ToolButton);
|
||||
805
src/editor/components/seColorPicker.js
Normal file
@@ -0,0 +1,805 @@
|
||||
/* eslint-disable max-len */
|
||||
import {jGraduate, jGraduateMethod} from './jgraduate/jQuery.jGraduate.js';
|
||||
import PaintBox from './PaintBox.js';
|
||||
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
.jPicker .Icon {
|
||||
display: inline-block;
|
||||
height: 24px;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
width: 25px
|
||||
}
|
||||
|
||||
.jPicker .Icon span.Color, .jPicker .Icon span.Alpha {
|
||||
background-position: 2px 2px;
|
||||
display: block;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.jPicker .Icon span.Image {
|
||||
background-repeat: no-repeat;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.jPicker.Container {
|
||||
z-index: 10
|
||||
}
|
||||
|
||||
table.jPicker {
|
||||
background-color: #efefef;
|
||||
border: 1px outset #666;
|
||||
font-family: Arial, Helvetica, Sans-Serif;
|
||||
font-size: 12px!important;
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
width: 545px;
|
||||
z-index: 20
|
||||
}
|
||||
|
||||
.jPicker .Move {
|
||||
background-color: #ddd;
|
||||
border-color: #fff #666 #666 #fff;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
cursor: move;
|
||||
height: 12px;
|
||||
padding: 0
|
||||
}
|
||||
|
||||
.jPicker .Title {
|
||||
font-size: 11px!important;
|
||||
font-weight: bold;
|
||||
margin: -2px 0 0 0;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.jPicker div.Map {
|
||||
border-bottom: 2px solid #fff;
|
||||
border-left: 2px solid #9a9a9a;
|
||||
border-right: 2px solid #fff;
|
||||
border-top: 2px solid #9a9a9a;
|
||||
cursor: crosshair;
|
||||
height: 260px;
|
||||
margin: 0 5px 0 5px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
width: 260px
|
||||
}
|
||||
|
||||
.jPicker div[class="Map"] {
|
||||
height: 256px;
|
||||
width: 256px
|
||||
}
|
||||
|
||||
.jPicker div.Bar {
|
||||
border-bottom: 2px solid #fff;
|
||||
border-left: 2px solid #9a9a9a;
|
||||
border-right: 2px solid #fff;
|
||||
border-top: 2px solid #9a9a9a;
|
||||
cursor: n-resize;
|
||||
height: 260px;
|
||||
margin: 12px 10px 0 5px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
width: 24px
|
||||
}
|
||||
|
||||
.jPicker div[class="Bar"] {
|
||||
height: 256px;
|
||||
width: 20px
|
||||
}
|
||||
|
||||
.jPicker .Map .Map1, .jPicker .Map .Map2, .jPicker .Map .Map3, .jPicker .Bar .Map1, .jPicker .Bar .Map2, .jPicker .Bar .Map3, .jPicker .Bar .Map4, .jPicker .Bar .Map5, .jPicker .Bar .Map6 {
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
display: block;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0
|
||||
}
|
||||
|
||||
.jPicker .Map .Map1, .jPicker .Map .Map2, .jPicker .Map .Map3 {
|
||||
height: 2596px;
|
||||
width: 256px
|
||||
}
|
||||
|
||||
.jPicker .Bar .Map1, .jPicker .Bar .Map2, .jPicker .Bar .Map3, .jPicker .Bar .Map4 {
|
||||
height: 3896px;
|
||||
width: 20px
|
||||
}
|
||||
|
||||
.jPicker .Bar .Map5, .jPicker .Bar .Map6 {
|
||||
height: 256px;
|
||||
width: 20px
|
||||
}
|
||||
|
||||
.jPicker .Map .Map1, .jPicker .Map .Map2, .jPicker .Bar .Map6 {
|
||||
background-repeat: no-repeat
|
||||
}
|
||||
|
||||
.jPicker .Map .Map3, .jPicker .Bar .Map5 {
|
||||
background-repeat: repeat
|
||||
}
|
||||
|
||||
.jPicker .Bar .Map1, .jPicker .Bar .Map2, .jPicker .Bar .Map3, .jPicker .Bar .Map4 {
|
||||
background-repeat: repeat-x
|
||||
}
|
||||
|
||||
.jPicker .Map .Arrow {
|
||||
display: block;
|
||||
position: absolute
|
||||
}
|
||||
|
||||
.jPicker .Bar .Arrow {
|
||||
display: block;
|
||||
left: 0;
|
||||
position: absolute
|
||||
}
|
||||
|
||||
.jPicker .Preview {
|
||||
font-size: 9px;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.jPicker .Preview div {
|
||||
border: 2px inset #eee;
|
||||
height: 62px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
width: 62px
|
||||
}
|
||||
|
||||
.jPicker .Preview div span {
|
||||
border: 1px solid #000;
|
||||
display: block;
|
||||
height: 30px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
width: 60px
|
||||
}
|
||||
|
||||
.jPicker .Preview .Active {
|
||||
border-bottom-width: 0
|
||||
}
|
||||
|
||||
.jPicker .Preview .Current {
|
||||
border-top-width: 0;
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.jPicker .Button {
|
||||
text-align: center;
|
||||
width: 115px
|
||||
}
|
||||
|
||||
.jPicker .Button input {
|
||||
width: 100px
|
||||
}
|
||||
|
||||
.jPicker .Button .Ok {
|
||||
margin: 12px 0 5px 0
|
||||
}
|
||||
|
||||
.jPicker td.Radio {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 31px
|
||||
}
|
||||
|
||||
.jPicker td.Radio input {
|
||||
margin: 0 5px 0 0;
|
||||
padding: 0
|
||||
}
|
||||
|
||||
.jPicker td.Text {
|
||||
font-size: 12px!important;
|
||||
height: 22px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
width: 70px
|
||||
}
|
||||
|
||||
.jPicker tr.Hex td.Text {
|
||||
width: 100px
|
||||
}
|
||||
|
||||
.jPicker td.Text input {
|
||||
background-color: #fff;
|
||||
border: 1px inset #aaa;
|
||||
height: 19px;
|
||||
margin: 0 0 0 5px;
|
||||
text-align: left;
|
||||
width: 30px
|
||||
}
|
||||
|
||||
.jPicker td[class="Text"] input {
|
||||
height: 15px
|
||||
}
|
||||
|
||||
.jPicker tr.Hex td.Text input.Hex {
|
||||
width: 50px
|
||||
}
|
||||
|
||||
.jPicker tr.Hex td.Text input.AHex {
|
||||
width: 20px
|
||||
}
|
||||
|
||||
.jPicker .Grid {
|
||||
text-align: center;
|
||||
width: 114px
|
||||
}
|
||||
|
||||
.jPicker .Grid span.QuickColor {
|
||||
border: 1px inset #aaa;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 15px;
|
||||
line-height: 15px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 19px
|
||||
}
|
||||
|
||||
.jPicker .Grid span[class="QuickColor"] {
|
||||
width: 17px
|
||||
}
|
||||
/*
|
||||
* jGraduate Default CSS
|
||||
*
|
||||
* Copyright (c) 2010 Jeff Schiller
|
||||
* http://blog.codedread.com/
|
||||
*
|
||||
* Copyright (c) 2010 Alexis Deveria
|
||||
* http://a.deveria.com/
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
*/
|
||||
|
||||
h2.jGraduate_Title {
|
||||
font-family: Arial, Helvetica, Sans-Serif;
|
||||
font-size: 11px !important;
|
||||
font-weight: bold;
|
||||
margin: -13px 0px 0px 0px;
|
||||
padding: 0px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.jGraduate_Picker {
|
||||
font-family: Arial, Helvetica, Sans-Serif;
|
||||
font-size: 12px;
|
||||
border-style: solid;
|
||||
border-color: lightgrey black black lightgrey;
|
||||
border-width: 1px;
|
||||
background-color: #EFEFEF;
|
||||
position: absolute;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.jGraduate_tabs li {
|
||||
background-color: #ccc;
|
||||
display: inline;
|
||||
border: solid 1px grey;
|
||||
padding: 3px;
|
||||
margin: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
li.jGraduate_tab_current {
|
||||
background-color: #EFEFEF;
|
||||
display: inline;
|
||||
padding: 3px;
|
||||
margin: 2px;
|
||||
border: solid 1px black;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.jGraduate_colPick {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jGraduate_gradPick {
|
||||
display: none;
|
||||
border: outset 1px #666;
|
||||
padding: 10px 7px 5px 5px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.jGraduate_gradPick {
|
||||
display: none;
|
||||
border: outset 1px #666;
|
||||
padding: 10px 7px 5px 5px;
|
||||
overflow: auto;
|
||||
/* position: relative;*/
|
||||
}
|
||||
|
||||
.jGraduate_tabs {
|
||||
position: relative;
|
||||
background-color: #EFEFEF;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
div.jGraduate_Swatch {
|
||||
float: left;
|
||||
margin: 8px;
|
||||
}
|
||||
div.jGraduate_GradContainer {
|
||||
border: 2px inset #EEE;
|
||||
background-image: url(../images/map-opacity.png);
|
||||
background-position: 0px 0px;
|
||||
height: 256px;
|
||||
width: 256px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.jGraduate_GradContainer div.grad_coord {
|
||||
background: #000;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
position: absolute;
|
||||
margin: -5px -5px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
font-size: xx-small;
|
||||
line-height: 10px;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.jGraduate_AlphaArrows {
|
||||
position: absolute;
|
||||
margin-top: -10px;
|
||||
margin-left: 250.5px;
|
||||
}
|
||||
|
||||
div.jGraduate_Opacity {
|
||||
border: 2px inset #eee;
|
||||
margin-top: 14px;
|
||||
background-color: black;
|
||||
background-image: url(../images/Maps.png);
|
||||
background-position: 0px -2816px;
|
||||
height: 20px;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
div.jGraduate_StopSlider {
|
||||
/* border: 2px inset #eee;*/
|
||||
margin: 0 0 0 -10px;
|
||||
width: 276px;
|
||||
overflow: visible;
|
||||
background: #efefef;
|
||||
height: 45px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.jGraduate_StopSection {
|
||||
width: 120px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
input.jGraduate_Ok, input.jGraduate_Cancel {
|
||||
display: block;
|
||||
width: 100px;
|
||||
margin-left: -4px;
|
||||
margin-right: -4px;
|
||||
}
|
||||
input.jGraduate_Ok {
|
||||
margin: 9px -4px 5px -4px;
|
||||
}
|
||||
|
||||
.colorBox {
|
||||
float: left;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
border: 1px solid #808080;
|
||||
cursor: pointer;
|
||||
margin: 4px 4px 4px 30px;
|
||||
}
|
||||
|
||||
.colorBox + label {
|
||||
float: left;
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
label.jGraduate_Form_Heading {
|
||||
position: relative;
|
||||
top: 10px;
|
||||
background-color: #EFEFEF;
|
||||
padding: 2px;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
div.jGraduate_Form_Section {
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: grey;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
padding: 15px 5px 5px 5px;
|
||||
margin: 5px 2px;
|
||||
width: 110px;
|
||||
text-align: center;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
div.jGraduate_Form_Section label {
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
div.jGraduate_StopSection input[type=text],
|
||||
div.jGraduate_Slider input[type=text] {
|
||||
width: 33px;
|
||||
}
|
||||
|
||||
div.jGraduate_LightBox {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
background-color: #000;
|
||||
opacity: 0.5;
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.jGraduate_stopPicker {
|
||||
position: absolute;
|
||||
display: none;
|
||||
background: #E8E8E8;
|
||||
}
|
||||
|
||||
|
||||
.jGraduate_gradPick {
|
||||
width: 535px;
|
||||
}
|
||||
|
||||
.jGraduate_gradPick div.jGraduate_OpacField {
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 5px;
|
||||
/*
|
||||
width: 270px;
|
||||
|
||||
left: 284px;
|
||||
width: 266px;
|
||||
height: 200px;
|
||||
top: 167px;
|
||||
margin: -3px 3px 0px 4px;
|
||||
*/
|
||||
}
|
||||
|
||||
.jGraduate_gradPick .jGraduate_Form {
|
||||
float: left;
|
||||
width: 270px;
|
||||
position: absolute;
|
||||
left: 284px;
|
||||
width: 266px;
|
||||
height: 200px;
|
||||
top: 167px;
|
||||
margin: -3px 3px 0px 10px;
|
||||
}
|
||||
|
||||
.jGraduate_gradPick .jGraduate_Points {
|
||||
position: static;
|
||||
width: 150px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.jGraduate_SpreadMethod {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 100px;
|
||||
}
|
||||
|
||||
.jGraduate_Colorblocks {
|
||||
display: table;
|
||||
border-spacing: 0 5px;
|
||||
}
|
||||
|
||||
.jGraduate_colorblock {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.jGraduate_Colorblocks .jGraduate_colorblock > * {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
margin: 0;
|
||||
float: none;
|
||||
}
|
||||
|
||||
.jGraduate_gradPick div.jGraduate_StopSection {
|
||||
float: left;
|
||||
width: 133px;
|
||||
margin-top: -8px;
|
||||
}
|
||||
|
||||
|
||||
.jGraduate_gradPick .jGraduate_Form_Section {
|
||||
padding-top: 9px;
|
||||
}
|
||||
|
||||
|
||||
.jGraduate_Slider {
|
||||
text-align: center;
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.jGraduate_Slider .jGraduate_Form_Section {
|
||||
border: none;
|
||||
width: 250px;
|
||||
padding: 0 2px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.jGraduate_Slider label {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
line-height: 50px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.jGraduate_Slider label.prelabel {
|
||||
width: 40px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.jGraduate_SliderBar {
|
||||
width: 140px;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
border:1px solid #BBB;
|
||||
height:20px;
|
||||
margin-top:14px;
|
||||
margin-left:5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.jGraduate_Slider input {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
div.jGraduate_Slider img {
|
||||
top: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
margin-top: -10px;
|
||||
cursor:ew-resize;
|
||||
}
|
||||
|
||||
|
||||
.jGraduate_gradPick .jGraduate_OkCancel {
|
||||
position: absolute;
|
||||
top: 39px;
|
||||
right: 10px;
|
||||
width: 113px;
|
||||
|
||||
}
|
||||
|
||||
.jGraduate_OpacField {
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
bottom: 0;
|
||||
}
|
||||
#logo {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
}
|
||||
#block {
|
||||
height: 13px;
|
||||
width: 14px;
|
||||
float: right;
|
||||
background-color: darkgrey;
|
||||
}
|
||||
#picker {
|
||||
background: var(--input-color);
|
||||
height: 19px;
|
||||
line-height: 19px;
|
||||
border-radius: 3px;
|
||||
width: 52px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 4px;
|
||||
margin-top: 1px;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
#color_picker {
|
||||
z-index: 1000;
|
||||
top: -350px;
|
||||
}
|
||||
</style>
|
||||
<div id="picker">
|
||||
<img src="./images/logo.svg" alt="icon" id="logo">
|
||||
<label for="color" title="Change xxx color" id="label"></label>
|
||||
<div id="block">
|
||||
</div>
|
||||
</div>
|
||||
<!-- hidden div -->
|
||||
<div id="color_picker"></div>
|
||||
`;
|
||||
/**
|
||||
* @class SeColorPicker
|
||||
*/
|
||||
export class SeColorPicker extends HTMLElement {
|
||||
/**
|
||||
* @function constructor
|
||||
*/
|
||||
constructor () {
|
||||
super();
|
||||
// create the shadowDom and insert the template
|
||||
this._shadowRoot = this.attachShadow({mode: 'open'});
|
||||
this._shadowRoot.append(template.content.cloneNode(true));
|
||||
this.$logo = this._shadowRoot.getElementById('logo');
|
||||
this.$label = this._shadowRoot.getElementById('label');
|
||||
this.$block = this._shadowRoot.getElementById('block');
|
||||
this.paintBox = null;
|
||||
this.$picker = this._shadowRoot.getElementById('picker');
|
||||
this.$color_picker = this._shadowRoot.getElementById('color_picker');
|
||||
}
|
||||
/**
|
||||
* @function observedAttributes
|
||||
* @returns {any} observed
|
||||
*/
|
||||
static get observedAttributes () {
|
||||
return ['label', 'src', 'type'];
|
||||
}
|
||||
/**
|
||||
* @function attributeChangedCallback
|
||||
* @param {string} name
|
||||
* @param {string} oldValue
|
||||
* @param {string} newValue
|
||||
* @returns {void}
|
||||
*/
|
||||
attributeChangedCallback (name, oldValue, newValue) {
|
||||
if (oldValue === newValue) return;
|
||||
switch (name) {
|
||||
case 'src':
|
||||
this.$logo.setAttribute('src', newValue);
|
||||
break;
|
||||
case 'label':
|
||||
this.setAttribute('title', newValue);
|
||||
break;
|
||||
case 'type':
|
||||
this.$label.setAttribute('title', `Pick a ${newValue} Paint and Opacity`);
|
||||
break;
|
||||
default:
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`unknown attribute: ${name}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get label () {
|
||||
return this.$label.getAttribute('title');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set label (value) {
|
||||
this.setAttribute('label', value);
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get type () {
|
||||
return this.getAttribute('type');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set type (value) {
|
||||
this.setAttribute('type', value);
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get src () {
|
||||
return this.getAttribute('src');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set src (value) {
|
||||
this.setAttribute('src', value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PlainObject} svgCanvas
|
||||
* @param {PlainObject} selectedElement
|
||||
* @param {bool} apply
|
||||
* @returns {void}
|
||||
*/
|
||||
update (svgCanvas, selectedElement, apply) {
|
||||
const paint = this.paintBox.update(svgCanvas, selectedElement);
|
||||
if (paint && apply) {
|
||||
const changeEvent = new CustomEvent('change', {detail: {
|
||||
paint
|
||||
}});
|
||||
this.dispatchEvent(changeEvent);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {PlainObject} paint
|
||||
* @returns {void}
|
||||
*/
|
||||
setPaint (paint) {
|
||||
this.paintBox.setPaint(paint);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function connectedCallback
|
||||
* @returns {void}
|
||||
*/
|
||||
connectedCallback () {
|
||||
this.paintBox = new PaintBox(this.$block, this.type);
|
||||
this.$picker.addEventListener('click', () => {
|
||||
let {paint} = this.paintBox;
|
||||
jGraduateMethod(
|
||||
this.$color_picker,
|
||||
{
|
||||
images: {clientPath: './components/jgraduate/images/'},
|
||||
paint,
|
||||
window: {pickerTitle: this.label},
|
||||
newstop: 'inverse'
|
||||
},
|
||||
(p) => {
|
||||
paint = new jGraduate.Paint(p);
|
||||
this.setPaint(paint);
|
||||
const changeEvent = new CustomEvent('change', {detail: {
|
||||
paint
|
||||
}});
|
||||
this.dispatchEvent(changeEvent);
|
||||
this.$color_picker.style.display = 'none';
|
||||
},
|
||||
() => {
|
||||
this.$color_picker.style.display = 'none';
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Register
|
||||
customElements.define('se-colorpicker', SeColorPicker);
|
||||
178
src/editor/components/seDropdown.js
Normal file
@@ -0,0 +1,178 @@
|
||||
import ListComboBox from 'elix/define/ListComboBox.js';
|
||||
import {defaultState} from 'elix/src/base/internal.js';
|
||||
import {templateFrom, fragmentFrom} from 'elix/src/core/htmlLiterals.js';
|
||||
import {internal} from 'elix';
|
||||
import NumberSpinBox from '../dialogs/se-elix/define/NumberSpinBox.js';
|
||||
|
||||
/**
|
||||
* @class Dropdown
|
||||
*/
|
||||
class Dropdown extends ListComboBox {
|
||||
/**
|
||||
* @function get
|
||||
* @returns {PlainObject}
|
||||
*/
|
||||
get [defaultState] () {
|
||||
return Object.assign(super[defaultState], {
|
||||
inputPartType: NumberSpinBox,
|
||||
src: './images/logo.svg',
|
||||
inputsize: '100%'
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {PlainObject}
|
||||
*/
|
||||
get [internal.template] () {
|
||||
const result = super[internal.template];
|
||||
const source = result.content.getElementById('source');
|
||||
// add a icon before our dropdown
|
||||
source.prepend(fragmentFrom.html`
|
||||
<img src="./images/logo.svg" alt="icon" width="18" height="18"></img>
|
||||
`.cloneNode(true));
|
||||
// change the style so it fits in our toolbar
|
||||
result.content.append(
|
||||
templateFrom.html`
|
||||
<style>
|
||||
[part~="source"] {
|
||||
grid-template-columns: 20px 1fr auto;
|
||||
}
|
||||
::slotted(*) {
|
||||
padding: 4px;
|
||||
background: #E8E8E8;
|
||||
border: 1px solid #B0B0B0;
|
||||
width: 100%;
|
||||
}
|
||||
[part~="popup"] {
|
||||
width: 150%;
|
||||
}
|
||||
</style>
|
||||
`.content
|
||||
);
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* @function observedAttributes
|
||||
* @returns {any} observed
|
||||
*/
|
||||
static get observedAttributes () {
|
||||
return ['title', 'src', 'inputsize', 'value'];
|
||||
}
|
||||
/**
|
||||
* @function attributeChangedCallback
|
||||
* @param {string} name
|
||||
* @param {string} oldValue
|
||||
* @param {string} newValue
|
||||
* @returns {void}
|
||||
*/
|
||||
attributeChangedCallback (name, oldValue, newValue) {
|
||||
if (oldValue === newValue) return;
|
||||
switch (name) {
|
||||
case 'title':
|
||||
// this.$span.setAttribute('title', `${newValue} ${shortcut ? `[${shortcut}]` : ''}`);
|
||||
break;
|
||||
case 'src':
|
||||
this.src = newValue;
|
||||
break;
|
||||
case 'inputsize':
|
||||
this.inputsize = newValue;
|
||||
break;
|
||||
default:
|
||||
super.attributeChangedCallback(name, oldValue, newValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function [internal.render]
|
||||
* @param {PlainObject} changed
|
||||
* @returns {void}
|
||||
*/
|
||||
[internal.render] (changed) {
|
||||
super[internal.render](changed);
|
||||
if (this[internal.firstRender]) {
|
||||
this.$img = this.shadowRoot.querySelector('img');
|
||||
this.$input = this.shadowRoot.getElementById('input');
|
||||
}
|
||||
if (changed.src) {
|
||||
this.$img.setAttribute('src', this[internal.state].src);
|
||||
}
|
||||
if (changed.inputsize) {
|
||||
this.$input.shadowRoot.querySelector('[part~="input"]').style.width = this[internal.state].inputsize;
|
||||
}
|
||||
if (changed.inputPartType) {
|
||||
// Wire up handler on new input.
|
||||
this.addEventListener('close', (e) => {
|
||||
e.preventDefault();
|
||||
const value = e.detail?.closeResult?.getAttribute('value');
|
||||
if (value) {
|
||||
const closeEvent = new CustomEvent('change', {detail: {value}});
|
||||
this.dispatchEvent(closeEvent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function src
|
||||
* @returns {string} src
|
||||
*/
|
||||
get src () {
|
||||
return this[internal.state].src;
|
||||
}
|
||||
/**
|
||||
* @function src
|
||||
* @returns {void}
|
||||
*/
|
||||
set src (src) {
|
||||
this[internal.setState]({src});
|
||||
}
|
||||
/**
|
||||
* @function inputsize
|
||||
* @returns {string} src
|
||||
*/
|
||||
get inputsize () {
|
||||
return this[internal.state].inputsize;
|
||||
}
|
||||
/**
|
||||
* @function src
|
||||
* @returns {void}
|
||||
*/
|
||||
set inputsize (inputsize) {
|
||||
this[internal.setState]({inputsize});
|
||||
}
|
||||
/**
|
||||
* @function value
|
||||
* @returns {string} src
|
||||
*/
|
||||
get value () {
|
||||
return this[internal.state].value;
|
||||
}
|
||||
/**
|
||||
* @function value
|
||||
* @returns {void}
|
||||
*/
|
||||
set value (value) {
|
||||
this[internal.setState]({value});
|
||||
}
|
||||
}
|
||||
|
||||
// Register
|
||||
customElements.define('se-dropdown', Dropdown);
|
||||
|
||||
/*
|
||||
{TODO
|
||||
min: 0.001, max: 10000, step: 50, stepfunc: stepZoom,
|
||||
function stepZoom (elem, step) {
|
||||
const origVal = Number(elem.value);
|
||||
if (origVal === 0) { return 100; }
|
||||
const sugVal = origVal + step;
|
||||
if (step === 0) { return origVal; }
|
||||
|
||||
if (origVal >= 100) {
|
||||
return sugVal;
|
||||
}
|
||||
if (sugVal >= origVal) {
|
||||
return origVal * 2;
|
||||
}
|
||||
return origVal / 2;
|
||||
}
|
||||
*/
|
||||
315
src/editor/components/seExplorerButton.js
Normal file
@@ -0,0 +1,315 @@
|
||||
/* eslint-disable no-unsanitized/property */
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
:host {
|
||||
position:relative;
|
||||
}
|
||||
.menu-button:hover, se-button:hover, .menu-item:hover
|
||||
{
|
||||
background-color: var(--icon-bg-color-hover);
|
||||
}
|
||||
img {
|
||||
border: none;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.overall.pressed .button-icon,
|
||||
.overall.pressed,
|
||||
.menu-item.pressed {
|
||||
background-color: var(--icon-bg-color-hover) !important;
|
||||
}
|
||||
.overall.pressed .menu-button {
|
||||
background-color: var(--icon-bg-color-hover) !important;
|
||||
}
|
||||
.disabled {
|
||||
opacity: 0.3;
|
||||
cursor: default;
|
||||
}
|
||||
.menu-button {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
margin: 2px 1px 4px;
|
||||
padding: 3px;
|
||||
background-color: var(--icon-bg-color);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.handle {
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
background-image: url(./images/handle.svg);
|
||||
position:absolute;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
.button-icon {
|
||||
}
|
||||
.menu {
|
||||
position: absolute;
|
||||
top:0px;
|
||||
left:204px;
|
||||
background: none !important;
|
||||
display:none;
|
||||
}
|
||||
.image-lib {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left:34px;
|
||||
background: #E8E8E8;
|
||||
display: none;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
width: 170px;
|
||||
}
|
||||
.menu-item {
|
||||
line-height: 1em;
|
||||
padding: 0.5em;
|
||||
border: 1px solid #B0B0B0;
|
||||
background: #E8E8E8;
|
||||
margin-bottom: -1px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.open-lib {
|
||||
display: inline-flex;
|
||||
}
|
||||
.open {
|
||||
display: block;
|
||||
}
|
||||
.overall {
|
||||
background: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="overall">
|
||||
<div class="menu-button">
|
||||
<img class="button-icon" src="./images/logo.svg" alt="icon">
|
||||
<div class="handle"></div>
|
||||
</div>
|
||||
<div class="image-lib"">
|
||||
<se-button></se-button>
|
||||
</div>
|
||||
<div class="menu">
|
||||
<div class="menu-item">menu</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
`;
|
||||
/**
|
||||
* @class ExplorerButton
|
||||
*/
|
||||
export class ExplorerButton extends HTMLElement {
|
||||
/**
|
||||
* @function constructor
|
||||
*/
|
||||
constructor () {
|
||||
super();
|
||||
// create the shadowDom and insert the template
|
||||
this._shadowRoot = this.attachShadow({mode: 'open'});
|
||||
this._shadowRoot.append(template.content.cloneNode(true));
|
||||
// locate the component
|
||||
this.$button = this._shadowRoot.querySelector('.menu-button');
|
||||
this.$overall = this._shadowRoot.querySelector('.overall');
|
||||
this.$img = this._shadowRoot.querySelector('.menu-button img');
|
||||
this.$menu = this._shadowRoot.querySelector('.menu');
|
||||
this.$handle = this._shadowRoot.querySelector('.handle');
|
||||
this.$lib = this._shadowRoot.querySelector('.image-lib');
|
||||
this.files = [];
|
||||
this.request = new XMLHttpRequest();
|
||||
}
|
||||
/**
|
||||
* @function observedAttributes
|
||||
* @returns {any} observed
|
||||
*/
|
||||
static get observedAttributes () {
|
||||
return ['title', 'pressed', 'disabled', 'lib', 'src'];
|
||||
}
|
||||
/**
|
||||
* @function attributeChangedCallback
|
||||
* @param {string} name
|
||||
* @param {string} oldValue
|
||||
* @param {string} newValue
|
||||
* @returns {void}
|
||||
*/
|
||||
async attributeChangedCallback (name, oldValue, newValue) {
|
||||
if (oldValue === newValue) return;
|
||||
switch (name) {
|
||||
case 'title':
|
||||
{
|
||||
const shortcut = this.getAttribute('shortcut');
|
||||
this.$button.setAttribute('title', `${newValue} [${shortcut}]`);
|
||||
}
|
||||
break;
|
||||
case 'pressed':
|
||||
if (newValue) {
|
||||
this.$overall.classList.add('pressed');
|
||||
} else {
|
||||
this.$overall.classList.remove('pressed');
|
||||
}
|
||||
break;
|
||||
case 'disabled':
|
||||
if (newValue) {
|
||||
this.$div.classList.add('disabled');
|
||||
} else {
|
||||
this.$div.classList.remove('disabled');
|
||||
}
|
||||
break;
|
||||
case 'lib':
|
||||
try {
|
||||
const response = await fetch(`${newValue}index.json`);
|
||||
const json = await response.json();
|
||||
const {lib} = json;
|
||||
this.$menu.innerHTML = lib.map((menu, i) => (
|
||||
`<div data-menu="${menu}" class="menu-item ${(i === 0) ? 'pressed' : ''} ">${menu}</div>`
|
||||
)).join('');
|
||||
await this.updateLib(lib[0]);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
}
|
||||
break;
|
||||
case 'src':
|
||||
this.$img.setAttribute('src', newValue);
|
||||
break;
|
||||
default:
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`unknown attribute: ${name}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get title () {
|
||||
return this.getAttribute('title');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set title (value) {
|
||||
this.setAttribute('title', value);
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get pressed () {
|
||||
return this.hasAttribute('pressed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set pressed (value) {
|
||||
// boolean value => existence = true
|
||||
if (value) {
|
||||
this.setAttribute('pressed', 'true');
|
||||
} else {
|
||||
this.removeAttribute('pressed', '');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get disabled () {
|
||||
return this.hasAttribute('disabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set disabled (value) {
|
||||
// boolean value => existence = true
|
||||
if (value) {
|
||||
this.setAttribute('disabled', 'true');
|
||||
} else {
|
||||
this.removeAttribute('disabled', '');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function connectedCallback
|
||||
* @returns {void}
|
||||
*/
|
||||
connectedCallback () {
|
||||
// capture click event on the button to manage the logic
|
||||
const onClickHandler = (ev) => {
|
||||
ev.stopPropagation();
|
||||
switch (ev.target.nodeName) {
|
||||
case 'SE-EXPLORERBUTTON':
|
||||
this.$menu.classList.add('open');
|
||||
this.$lib.classList.add('open-lib');
|
||||
break;
|
||||
case 'SE-BUTTON':
|
||||
// change to the current action
|
||||
this.currentAction = ev.target;
|
||||
this.$img.setAttribute('src', this.currentAction.getAttribute('src'));
|
||||
this.dataset.draw = this.data[this.currentAction.dataset.shape];
|
||||
this._shadowRoot.querySelectorAll('.image-lib [pressed]').forEach((b) => { b.pressed = false; });
|
||||
this.currentAction.setAttribute('pressed', 'pressed');
|
||||
// and close the menu
|
||||
this.$menu.classList.remove('open');
|
||||
this.$lib.classList.remove('open-lib');
|
||||
break;
|
||||
case 'DIV':
|
||||
if (ev.target.classList[0] === 'handle') {
|
||||
// this is a click on the handle so let's open/close the menu.
|
||||
this.$menu.classList.toggle('open');
|
||||
this.$lib.classList.toggle('open-lib');
|
||||
} else {
|
||||
this._shadowRoot.querySelectorAll('.menu > .pressed').forEach((b) => { b.classList.remove('pressed'); });
|
||||
ev.target.classList.add('pressed');
|
||||
this.updateLib(ev.target.dataset.menu);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('unknown nodeName for:', ev.target, ev.target.className);
|
||||
}
|
||||
};
|
||||
// capture event from slots
|
||||
this.addEventListener('click', onClickHandler);
|
||||
this.$menu.addEventListener('click', onClickHandler);
|
||||
this.$lib.addEventListener('click', onClickHandler);
|
||||
this.$handle.addEventListener('click', onClickHandler);
|
||||
}
|
||||
/**
|
||||
* @function updateLib
|
||||
* @param {string} lib
|
||||
* @returns {void}
|
||||
*/
|
||||
async updateLib (lib) {
|
||||
const libDir = this.getAttribute('lib');
|
||||
try {
|
||||
// initialize buttons for all shapes defined for this library
|
||||
const response = await fetch(`${libDir}${lib}.json`);
|
||||
const json = await response.json();
|
||||
this.data = json.data;
|
||||
const size = json.size ?? 300;
|
||||
const fill = json.fill ? '#333' : 'none';
|
||||
const off = size * 0.05;
|
||||
const vb = [-off, -off, size + off * 2, size + off * 2].join(' ');
|
||||
const stroke = json.fill ? 0 : (size / 30);
|
||||
this.$lib.innerHTML = Object.entries(this.data).map(([key, path]) => {
|
||||
const encoded = btoa(`
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
||||
<svg viewBox="${vb}"><path fill="${fill}" stroke="#000" stroke-width="${stroke}" d="${path}"></path></svg>
|
||||
</svg>`);
|
||||
return `<se-button data-shape="${key}"src="data:image/svg+xml;base64,${encoded}"></se-button>`;
|
||||
}).join('');
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`could not read file:${libDir}${lib}.json`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Register
|
||||
customElements.define('se-explorerbutton', ExplorerButton);
|
||||
279
src/editor/components/seFlyingButton.js
Normal file
@@ -0,0 +1,279 @@
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
:host {
|
||||
position:relative;
|
||||
}
|
||||
.overall:hover *
|
||||
{
|
||||
background-color: var(--icon-bg-color-hover);
|
||||
}
|
||||
img {
|
||||
border: none;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.overall.pressed .button-icon,
|
||||
.overall.pressed .handle {
|
||||
background-color: var(--icon-bg-color-hover) !important;
|
||||
}
|
||||
.overall.pressed .menu-button {
|
||||
background-color: var(--icon-bg-color-hover) !important;
|
||||
}
|
||||
.disabled {
|
||||
opacity: 0.3;
|
||||
cursor: default;
|
||||
}
|
||||
.menu-button {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
margin: 2px 1px 4px;
|
||||
padding: 3px;
|
||||
background-color: var(--icon-bg-color);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.handle {
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
background-image: url(./images/handle.svg);
|
||||
position:absolute;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
.button-icon {
|
||||
}
|
||||
.menu {
|
||||
position: absolute;
|
||||
top:-2px;
|
||||
left:32px;
|
||||
background: none !important;
|
||||
display:none;
|
||||
}
|
||||
.open {
|
||||
display: flex;
|
||||
}
|
||||
.menu-item {
|
||||
align-content: flex-start;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
top:0px;
|
||||
left:0px;
|
||||
}
|
||||
.overall {
|
||||
background: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="overall">
|
||||
<div class="menu-button">
|
||||
<img class="button-icon" src="./images/logo.svg" alt="icon">
|
||||
<div class="handle"></div>
|
||||
</div>
|
||||
<div class="menu">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
`;
|
||||
/**
|
||||
* @class FlyingButton
|
||||
*/
|
||||
export class FlyingButton extends HTMLElement {
|
||||
/**
|
||||
* @function constructor
|
||||
*/
|
||||
constructor () {
|
||||
super();
|
||||
// create the shadowDom and insert the template
|
||||
this._shadowRoot = this.attachShadow({mode: 'open'});
|
||||
this._shadowRoot.append(template.content.cloneNode(true));
|
||||
// locate the component
|
||||
this.$button = this._shadowRoot.querySelector('.menu-button');
|
||||
this.$handle = this._shadowRoot.querySelector('.handle');
|
||||
this.$overall = this._shadowRoot.querySelector('.overall');
|
||||
this.$img = this._shadowRoot.querySelector('img');
|
||||
this.$menu = this._shadowRoot.querySelector('.menu');
|
||||
// the last element of the div is the slot
|
||||
// we retrieve all elements added in the slot (i.e. se-buttons)
|
||||
this.$elements = this.$menu.lastElementChild.assignedElements();
|
||||
}
|
||||
/**
|
||||
* @function observedAttributes
|
||||
* @returns {any} observed
|
||||
*/
|
||||
static get observedAttributes () {
|
||||
return ['title', 'pressed', 'disabled', 'opened'];
|
||||
}
|
||||
/**
|
||||
* @function attributeChangedCallback
|
||||
* @param {string} name
|
||||
* @param {string} oldValue
|
||||
* @param {string} newValue
|
||||
* @returns {void}
|
||||
*/
|
||||
attributeChangedCallback (name, oldValue, newValue) {
|
||||
if (oldValue === newValue) return;
|
||||
switch (name) {
|
||||
case 'title':
|
||||
{
|
||||
const shortcut = this.getAttribute('shortcut');
|
||||
this.$button.setAttribute('title', `${newValue} [${shortcut}]`);
|
||||
}
|
||||
break;
|
||||
case 'pressed':
|
||||
if (newValue) {
|
||||
this.$overall.classList.add('pressed');
|
||||
} else {
|
||||
this.$overall.classList.remove('pressed');
|
||||
}
|
||||
break;
|
||||
case 'opened':
|
||||
if (newValue) {
|
||||
this.$menu.classList.add('open');
|
||||
} else {
|
||||
this.$menu.classList.remove('open');
|
||||
}
|
||||
break;
|
||||
case 'disabled':
|
||||
if (newValue) {
|
||||
this.$div.classList.add('disabled');
|
||||
} else {
|
||||
this.$div.classList.remove('disabled');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`unknown attribute: ${name}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get title () {
|
||||
return this.getAttribute('title');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set title (value) {
|
||||
this.setAttribute('title', value);
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get pressed () {
|
||||
return this.hasAttribute('pressed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set pressed (value) {
|
||||
// boolean value => existence = true
|
||||
if (value) {
|
||||
this.setAttribute('pressed', 'true');
|
||||
} else {
|
||||
this.removeAttribute('pressed', '');
|
||||
// close also the menu if open
|
||||
this.removeAttribute('opened');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get opened () {
|
||||
return this.hasAttribute('opened');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set opened (value) {
|
||||
// boolean value => existence = true
|
||||
if (value) {
|
||||
this.setAttribute('opened', 'opened');
|
||||
} else {
|
||||
this.removeAttribute('opened');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get disabled () {
|
||||
return this.hasAttribute('disabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set disabled (value) {
|
||||
// boolean value => existence = true
|
||||
if (value) {
|
||||
this.setAttribute('disabled', 'true');
|
||||
} else {
|
||||
this.removeAttribute('disabled', '');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function connectedCallback
|
||||
* @returns {void}
|
||||
*/
|
||||
connectedCallback () {
|
||||
// initialize currentAction with the first slot of the list
|
||||
this.activeSlot = this.shadowRoot.querySelector('slot').assignedElements()[0];
|
||||
this.$img.setAttribute('src', this.activeSlot.getAttribute('src'));
|
||||
// capture click event on the button to manage the logic
|
||||
const onClickHandler = (ev) => {
|
||||
ev.stopPropagation();
|
||||
switch (ev.target.nodeName) {
|
||||
case 'SE-FLYINGBUTTON':
|
||||
if (this.pressed) {
|
||||
this.setAttribute('opened', 'opened');
|
||||
} else {
|
||||
// launch current action
|
||||
this.activeSlot.click();
|
||||
this.setAttribute('pressed', 'pressed');
|
||||
}
|
||||
break;
|
||||
case 'SE-BUTTON':
|
||||
// change to the current action
|
||||
this.$img.setAttribute('src', ev.target.getAttribute('src'));
|
||||
this.activeSlot = ev.target;
|
||||
this.setAttribute('pressed', 'pressed');
|
||||
// and close the menu
|
||||
this.$menu.classList.remove('open');
|
||||
break;
|
||||
case 'DIV':
|
||||
// this is a click on the handle so let's open/close the menu.
|
||||
if (this.opened) {
|
||||
this.removeAttribute('opened');
|
||||
} else {
|
||||
this.setAttribute('opened', 'opened');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('unkonw nodeName for:', ev.target, ev.target.className);
|
||||
}
|
||||
};
|
||||
// capture event from slots
|
||||
this.addEventListener('click', onClickHandler);
|
||||
this.$handle.addEventListener('click', onClickHandler);
|
||||
}
|
||||
}
|
||||
|
||||
// Register
|
||||
customElements.define('se-flyingbutton', FlyingButton);
|
||||
158
src/editor/components/seInput.js
Normal file
@@ -0,0 +1,158 @@
|
||||
import 'elix/define/Input.js';
|
||||
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
img {
|
||||
top: 2px;
|
||||
left: 4px;
|
||||
position: relative;
|
||||
}
|
||||
span {
|
||||
bottom: 1px;
|
||||
right: -4px;
|
||||
position: relative;
|
||||
margin-right: 4px;
|
||||
color: #fff;
|
||||
}
|
||||
elix-input {
|
||||
background-color: var(--input-color);
|
||||
border-radius: 3px;
|
||||
}
|
||||
</style>
|
||||
<img src="./images/logo.svg" alt="icon" width="12" height="12" />
|
||||
<span id="label">label</span>
|
||||
<elix-input></elix-input>
|
||||
`;
|
||||
|
||||
/**
|
||||
* @class SEInput
|
||||
*/
|
||||
export class SEInput extends HTMLElement {
|
||||
/**
|
||||
* @function constructor
|
||||
*/
|
||||
constructor () {
|
||||
super();
|
||||
// create the shadowDom and insert the template
|
||||
this._shadowRoot = this.attachShadow({mode: 'open'});
|
||||
this._shadowRoot.append(template.content.cloneNode(true));
|
||||
// locate the component
|
||||
this.$img = this._shadowRoot.querySelector('img');
|
||||
this.$label = this.shadowRoot.getElementById('label');
|
||||
this.$event = new CustomEvent('change');
|
||||
this.$input = this._shadowRoot.querySelector('elix-input');
|
||||
}
|
||||
/**
|
||||
* @function observedAttributes
|
||||
* @returns {any} observed
|
||||
*/
|
||||
static get observedAttributes () {
|
||||
return ['value', 'label', 'src', 'size'];
|
||||
}
|
||||
/**
|
||||
* @function attributeChangedCallback
|
||||
* @param {string} name
|
||||
* @param {string} oldValue
|
||||
* @param {string} newValue
|
||||
* @returns {void}
|
||||
*/
|
||||
attributeChangedCallback (name, oldValue, newValue) {
|
||||
if (oldValue === newValue) return;
|
||||
switch (name) {
|
||||
case 'src':
|
||||
this.$img.setAttribute('src', newValue);
|
||||
this.$label.remove();
|
||||
break;
|
||||
case 'size':
|
||||
this.$input.setAttribute('size', newValue);
|
||||
break;
|
||||
case 'label':
|
||||
this.$label.textContent = newValue;
|
||||
this.$img.remove();
|
||||
break;
|
||||
case 'value':
|
||||
this.$input.value = newValue;
|
||||
break;
|
||||
default:
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`unknown attribute: ${name}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get label () {
|
||||
return this.getAttribute('label');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set label (value) {
|
||||
this.setAttribute('label', value);
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get value () {
|
||||
return this.$input.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set value (value) {
|
||||
this.$input.value = value;
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get src () {
|
||||
return this.getAttribute('src');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set src (value) {
|
||||
this.setAttribute('src', value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get size () {
|
||||
return this.getAttribute('size');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set size (value) {
|
||||
this.setAttribute('size', value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function connectedCallback
|
||||
* @returns {void}
|
||||
*/
|
||||
connectedCallback () {
|
||||
this.addEventListener('change', (e) => {
|
||||
e.preventDefault();
|
||||
this.value = e.target.value;
|
||||
});
|
||||
this.dispatchEvent(this.$event);
|
||||
}
|
||||
}
|
||||
// Register
|
||||
customElements.define('se-input', SEInput);
|
||||
140
src/editor/components/seList.js
Normal file
@@ -0,0 +1,140 @@
|
||||
import 'elix/define/DropdownList.js';
|
||||
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
elix-dropdown-list {
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
elix-dropdown-list:hover {
|
||||
background-color: var(--icon-bg-color-hover);
|
||||
}
|
||||
|
||||
::part(popup-toggle) {
|
||||
display: none;
|
||||
}
|
||||
::slotted(*) {
|
||||
padding:0;
|
||||
width:100%;
|
||||
}
|
||||
</style>
|
||||
<label>Label</label>
|
||||
<elix-dropdown-list>
|
||||
<slot></slot>
|
||||
</elix-dropdown-list>
|
||||
|
||||
`;
|
||||
/**
|
||||
* @class SeList
|
||||
*/
|
||||
export class SeList extends HTMLElement {
|
||||
/**
|
||||
* @function constructor
|
||||
*/
|
||||
constructor () {
|
||||
super();
|
||||
// create the shadowDom and insert the template
|
||||
this._shadowRoot = this.attachShadow({mode: 'open'});
|
||||
this._shadowRoot.append(template.content.cloneNode(true));
|
||||
this.$dropdown = this._shadowRoot.querySelector('elix-dropdown-list');
|
||||
this.$label = this._shadowRoot.querySelector('label');
|
||||
}
|
||||
/**
|
||||
* @function observedAttributes
|
||||
* @returns {any} observed
|
||||
*/
|
||||
static get observedAttributes () {
|
||||
return ['label', 'width', 'height'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @function attributeChangedCallback
|
||||
* @param {string} name
|
||||
* @param {string} oldValue
|
||||
* @param {string} newValue
|
||||
* @returns {void}
|
||||
*/
|
||||
attributeChangedCallback (name, oldValue, newValue) {
|
||||
if (oldValue === newValue) return;
|
||||
switch (name) {
|
||||
case 'label':
|
||||
this.$label.textContent = newValue;
|
||||
break;
|
||||
case 'height':
|
||||
this.$dropdown.style.height = newValue;
|
||||
break;
|
||||
case 'width':
|
||||
this.$dropdown.style.width = newValue;
|
||||
break;
|
||||
default:
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`unknown attribute: ${name}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get label () {
|
||||
return this.getAttribute('label');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set label (value) {
|
||||
this.setAttribute('label', value);
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get width () {
|
||||
return this.getAttribute('width');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set width (value) {
|
||||
this.setAttribute('width', value);
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get height () {
|
||||
return this.getAttribute('height');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set height (value) {
|
||||
this.setAttribute('height', value);
|
||||
}
|
||||
/**
|
||||
* @function connectedCallback
|
||||
* @returns {void}
|
||||
*/
|
||||
connectedCallback () {
|
||||
const currentObj = this;
|
||||
this.$dropdown.addEventListener('selectedindexchange', (e) => {
|
||||
e.preventDefault();
|
||||
if (e?.detail?.selectedIndex !== undefined) {
|
||||
const value = this.$dropdown.selectedItem.getAttribute('value');
|
||||
const closeEvent = new CustomEvent('change', {detail: {value}});
|
||||
currentObj.dispatchEvent(closeEvent);
|
||||
currentObj.value = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Register
|
||||
customElements.define('se-list', SeList);
|
||||
80
src/editor/components/seListItem.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import 'elix/define/Option.js';
|
||||
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
elix-option{
|
||||
padding:0.25rem 0.125rem !important;
|
||||
background-color: var(--icon-bg-color);
|
||||
}
|
||||
elix-option:hover{
|
||||
background-color: var(--icon-bg-color-hover);
|
||||
}
|
||||
</style>
|
||||
<elix-option aria-label="option">
|
||||
<slot></slot>
|
||||
</elix-option>
|
||||
`;
|
||||
/**
|
||||
* @class SeMenu
|
||||
*/
|
||||
export class SeListItem extends HTMLElement {
|
||||
/**
|
||||
* @function constructor
|
||||
*/
|
||||
constructor () {
|
||||
super();
|
||||
// create the shadowDom and insert the template
|
||||
this._shadowRoot = this.attachShadow({mode: 'open'});
|
||||
this._shadowRoot.append(template.content.cloneNode(true));
|
||||
this.$menuitem = this._shadowRoot.querySelector('elix-option');
|
||||
this.$svg = this.$menuitem.shadowRoot.querySelector('#checkmark');
|
||||
this.$svg.setAttribute('style', 'display: none;');
|
||||
}
|
||||
/**
|
||||
* @function observedAttributes
|
||||
* @returns {any} observed
|
||||
*/
|
||||
static get observedAttributes () {
|
||||
return ['option'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @function attributeChangedCallback
|
||||
* @param {string} name
|
||||
* @param {string} oldValue
|
||||
* @param {string} newValue
|
||||
* @returns {void}
|
||||
*/
|
||||
attributeChangedCallback (name, oldValue, newValue) {
|
||||
if (oldValue === newValue) return;
|
||||
// eslint-disable-next-line sonarjs/no-small-switch
|
||||
switch (name) {
|
||||
case 'option':
|
||||
this.$menuitem.setAttribute('option', newValue);
|
||||
break;
|
||||
default:
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`unknown attribute: ${name}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get option () {
|
||||
return this.getAttribute('option');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set option (value) {
|
||||
this.setAttribute('option', value);
|
||||
}
|
||||
}
|
||||
|
||||
// Register
|
||||
customElements.define('se-list-item', SeListItem);
|
||||
127
src/editor/components/seMenu.js
Normal file
@@ -0,0 +1,127 @@
|
||||
import 'elix/define/MenuItem.js';
|
||||
import './sePlainMenuButton.js';
|
||||
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
:host {
|
||||
padding: 0px;
|
||||
}
|
||||
elix-menu-button::part(menu) {
|
||||
background-color: var(--icon-bg-color) !important;
|
||||
color: #fff;
|
||||
}
|
||||
elix-menu-button::part(popup-toggle) {
|
||||
padding: 0.25em 0.30em !important
|
||||
}
|
||||
:host ::slotted([current]){
|
||||
background-color: var(--icon-bg-color-hover) !important;
|
||||
color: #fff;
|
||||
}
|
||||
:host ::slotted(*){
|
||||
padding: 0.25em 1.25em 0.25em 0.25em !important;
|
||||
margin: 2px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<elix-menu-button id="MenuButton" aria-label="Main Menu">
|
||||
<slot></slot>
|
||||
</elix-menu-button>
|
||||
|
||||
`;
|
||||
/**
|
||||
* @class SeMenu
|
||||
*/
|
||||
export class SeMenu extends HTMLElement {
|
||||
/**
|
||||
* @function constructor
|
||||
*/
|
||||
constructor () {
|
||||
super();
|
||||
// create the shadowDom and insert the template
|
||||
this._shadowRoot = this.attachShadow({mode: 'open'});
|
||||
this._shadowRoot.append(template.content.cloneNode(true));
|
||||
this.$menu = this._shadowRoot.querySelector('elix-menu-button');
|
||||
this.$label = this.$menu.shadowRoot.querySelector('#popupToggle').shadowRoot;
|
||||
}
|
||||
/**
|
||||
* @function observedAttributes
|
||||
* @returns {any} observed
|
||||
*/
|
||||
static get observedAttributes () {
|
||||
return ['label', 'src'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @function attributeChangedCallback
|
||||
* @param {string} name
|
||||
* @param {string} oldValue
|
||||
* @param {string} newValue
|
||||
* @returns {void}
|
||||
*/
|
||||
attributeChangedCallback (name, oldValue, newValue) {
|
||||
const image = new Image();
|
||||
if (oldValue === newValue) return;
|
||||
switch (name) {
|
||||
case 'src':
|
||||
image.src = newValue;
|
||||
image.width = 24;
|
||||
image.height = 24;
|
||||
this.$label.prepend(image);
|
||||
break;
|
||||
case 'label':
|
||||
this.$label.prepend(newValue);
|
||||
break;
|
||||
default:
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`unknown attribute: ${name}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get label () {
|
||||
return this.getAttribute('label');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set label (value) {
|
||||
this.setAttribute('label', value);
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get src () {
|
||||
return this.getAttribute('src');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set src (value) {
|
||||
this.setAttribute('src', value);
|
||||
}
|
||||
/**
|
||||
* @function connectedCallback
|
||||
* @returns {void}
|
||||
*/
|
||||
/* connectedCallback () {
|
||||
this.$menu.addEventListener('openedchange', (e) => {
|
||||
e.preventDefault();
|
||||
const selectedItem = e?.detail?.closeResult;
|
||||
if (selectedItem !== undefined && selectedItem?.id !== undefined) {
|
||||
document.getElementById(selectedItem.id).click();
|
||||
}
|
||||
});
|
||||
} */
|
||||
}
|
||||
|
||||
// Register
|
||||
customElements.define('se-menu', SeMenu);
|
||||
122
src/editor/components/seMenuItem.js
Normal file
@@ -0,0 +1,122 @@
|
||||
import 'elix/define/Menu.js';
|
||||
import 'elix/define/MenuItem.js';
|
||||
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
</style>
|
||||
<elix-menu-item>
|
||||
<div style="display:flex; align-items: center;">
|
||||
<img src="./images/logo.svg" alt="icon" style="display:none;" width="24"/>
|
||||
<span style="margin-left: 7px;"></span>
|
||||
</div>
|
||||
</elix-menu-item>
|
||||
`;
|
||||
/**
|
||||
* @class SeMenuItem
|
||||
*/
|
||||
export class SeMenuItem extends HTMLElement {
|
||||
/**
|
||||
* @function constructor
|
||||
*/
|
||||
constructor () {
|
||||
super();
|
||||
// create the shadowDom and insert the template
|
||||
this._shadowRoot = this.attachShadow({mode: 'open'});
|
||||
this._shadowRoot.append(template.content.cloneNode(true));
|
||||
this.$img = this._shadowRoot.querySelector('img');
|
||||
this.$label = this._shadowRoot.querySelector('span');
|
||||
this.$menuitem = this._shadowRoot.querySelector('elix-menu-item');
|
||||
this.$svg = this.$menuitem.shadowRoot.querySelector('#checkmark');
|
||||
this.$svg.setAttribute('style', 'display: none;');
|
||||
}
|
||||
/**
|
||||
* @function observedAttributes
|
||||
* @returns {any} observed
|
||||
*/
|
||||
static get observedAttributes () {
|
||||
return ['label', 'src'];
|
||||
}
|
||||
/**
|
||||
* @function attributeChangedCallback
|
||||
* @param {string} name
|
||||
* @param {string} oldValue
|
||||
* @param {string} newValue
|
||||
* @returns {void}
|
||||
*/
|
||||
attributeChangedCallback (name, oldValue, newValue) {
|
||||
let shortcut = '';
|
||||
if (oldValue === newValue) return;
|
||||
switch (name) {
|
||||
case 'src':
|
||||
this.$img.setAttribute('src', newValue);
|
||||
this.$img.style.display = 'inline-block';
|
||||
break;
|
||||
case 'label':
|
||||
shortcut = this.getAttribute('shortcut');
|
||||
this.$label.textContent = `${newValue} ${shortcut ? `(${shortcut})` : ''}`;
|
||||
break;
|
||||
default:
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`unknown attribute: ${name}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get label () {
|
||||
return this.getAttribute('label');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set label (value) {
|
||||
this.setAttribute('label', value);
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get src () {
|
||||
return this.getAttribute('src');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set src (value) {
|
||||
this.setAttribute('src', value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function connectedCallback
|
||||
* @returns {void}
|
||||
*/
|
||||
connectedCallback () {
|
||||
// capture shortcuts
|
||||
const shortcut = this.getAttribute('shortcut');
|
||||
if (shortcut) {
|
||||
// register the keydown event
|
||||
document.addEventListener('keydown', (e) => {
|
||||
// only track keyboard shortcuts for the body containing the SVG-Editor
|
||||
if (e.target.nodeName !== 'BODY') return;
|
||||
// normalize key
|
||||
const key = `${(e.metaKey) ? 'meta+' : ''}${(e.ctrlKey) ? 'ctrl+' : ''}${e.key.toUpperCase()}`;
|
||||
if (shortcut !== key) return;
|
||||
// launch the click event
|
||||
if (this.id) {
|
||||
document.getElementById(this.id).click();
|
||||
}
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Register
|
||||
customElements.define('se-menu-item', SeMenuItem);
|
||||
136
src/editor/components/sePalette.js
Normal file
@@ -0,0 +1,136 @@
|
||||
/* eslint-disable max-len */
|
||||
const palette = [
|
||||
// Todo: Make into configuration item?
|
||||
'none', '#000000', '#3f3f3f', '#7f7f7f', '#bfbfbf', '#ffffff',
|
||||
'#ff0000', '#ff7f00', '#ffff00', '#7fff00',
|
||||
'#00ff00', '#00ff7f', '#00ffff', '#007fff',
|
||||
'#0000ff', '#7f00ff', '#ff00ff', '#ff007f',
|
||||
'#7f0000', '#7f3f00', '#7f7f00', '#3f7f00',
|
||||
'#007f00', '#007f3f', '#007f7f', '#003f7f',
|
||||
'#00007f', '#3f007f', '#7f007f', '#7f003f',
|
||||
'#ffaaaa', '#ffd4aa', '#ffffaa', '#d4ffaa',
|
||||
'#aaffaa', '#aaffd4', '#aaffff', '#aad4ff',
|
||||
'#aaaaff', '#d4aaff', '#ffaaff', '#ffaad4'
|
||||
];
|
||||
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
.square {
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
float: left;
|
||||
}
|
||||
#palette_holder {
|
||||
overflow: hidden;
|
||||
margin-top: 5px;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
height: 16px;
|
||||
background: #f0f0f0;
|
||||
border-radius: 3px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#js-se-palette {
|
||||
float: left;
|
||||
width: 632px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
div.palette_item {
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
div.palette_item:first-child {
|
||||
background: white;
|
||||
}
|
||||
@media screen and (max-width:1100px) {
|
||||
#palette_holder {
|
||||
left: 410px;
|
||||
overflow-x: scroll;
|
||||
padding: 0 5px;
|
||||
margin-top: 2px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width:1250px) {
|
||||
#palette_holder {
|
||||
left: 560px;
|
||||
overflow-x: scroll;
|
||||
padding: 0 5px;
|
||||
margin-top: 2px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width:540px) {
|
||||
#palette_holder {
|
||||
left: 0px;
|
||||
overflow-x: scroll;
|
||||
padding: 0 5px;
|
||||
margin-top: 32px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div id="palette_holder" title="Click to change fill color, shift-click to change stroke color">
|
||||
<div id="js-se-palette">
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
/**
|
||||
* @class SEPalette
|
||||
*/
|
||||
export class SEPalette extends HTMLElement {
|
||||
/**
|
||||
* @function constructor
|
||||
*/
|
||||
constructor () {
|
||||
super();
|
||||
// create the shadowDom and insert the template
|
||||
this._shadowRoot = this.attachShadow({mode: 'open'});
|
||||
this._shadowRoot.append(template.content.cloneNode(true));
|
||||
this.$strip = this._shadowRoot.querySelector('#js-se-palette');
|
||||
palette.forEach((rgb) => {
|
||||
const newDiv = document.createElement('div');
|
||||
newDiv.classList.add('square');
|
||||
if(rgb === 'none') {
|
||||
const img = document.createElement('img');
|
||||
img.src = `data:image/svg+xml;charset=utf-8;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgY2xhc3M9InN2Z19pY29uIj48c3ZnIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgICA8bGluZSBmaWxsPSJub25lIiBzdHJva2U9IiNkNDAwMDAiIGlkPSJzdmdfOTAiIHkyPSIyNCIgeDI9IjI0IiB5MT0iMCIgeDE9IjAiLz4KICAgIDxsaW5lIGlkPSJzdmdfOTIiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2Q0MDAwMCIgeTI9IjI0IiB4Mj0iMCIgeTE9IjAiIHgxPSIyNCIvPgogIDwvc3ZnPjwvc3ZnPg==`;
|
||||
img.style.width = "15px";
|
||||
img.style.height = "15px";
|
||||
newDiv.append(img);
|
||||
} else {
|
||||
newDiv.style.backgroundColor = rgb;
|
||||
}
|
||||
newDiv.dataset.rgb = rgb;
|
||||
newDiv.addEventListener('click', (evt) => {
|
||||
evt.preventDefault();
|
||||
// shift key or right click for stroke
|
||||
const picker = evt.shiftKey || evt.button === 2 ? 'stroke' : 'fill';
|
||||
let color = newDiv.dataset.rgb;
|
||||
// Webkit-based browsers returned 'initial' here for no stroke
|
||||
if (color === 'none' || color === 'transparent' || color === 'initial') {
|
||||
color = 'none';
|
||||
}
|
||||
const paletteEvent = new CustomEvent('change', {detail: {picker, color}, bubbles: false});
|
||||
this.dispatchEvent(paletteEvent);
|
||||
});
|
||||
this.$strip.append(newDiv);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @function connectedCallback
|
||||
* @returns {void}
|
||||
*/
|
||||
connectedCallback () {
|
||||
}
|
||||
}
|
||||
|
||||
// Register
|
||||
customElements.define('se-palette', SEPalette);
|
||||
32
src/editor/components/sePlainBorderButton.js
Normal file
@@ -0,0 +1,32 @@
|
||||
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;
|
||||
}
|
||||
</style>
|
||||
`
|
||||
);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export default SePlainBorderButton;
|
||||
20
src/editor/components/sePlainMenuButton.js
Normal file
@@ -0,0 +1,20 @@
|
||||
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);
|
||||
191
src/editor/components/seSpinInput.js
Normal file
@@ -0,0 +1,191 @@
|
||||
import '../dialogs/se-elix/define/NumberSpinBox.js';
|
||||
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
img {
|
||||
position: relative;
|
||||
right: -4px;
|
||||
}
|
||||
span {
|
||||
bottom: -3px;
|
||||
right: -4px;
|
||||
position: relative;
|
||||
margin-left: 4px;
|
||||
margin-right: 4px;
|
||||
color: #fff;
|
||||
vertical-align: ;
|
||||
}
|
||||
elix-number-spin-box {
|
||||
background-color: var(--input-color);
|
||||
border-radius: 3px;
|
||||
height: 20px !important;
|
||||
margin-top: 1px;
|
||||
vertical-align: top;
|
||||
}
|
||||
elix-number-spin-box::part(spin-button) {
|
||||
padding: 0px;
|
||||
}
|
||||
elix-number-spin-box::part(input) {
|
||||
width: 3em;
|
||||
}
|
||||
elix-number-spin-box{
|
||||
width: 54px;
|
||||
height: 24px;
|
||||
}
|
||||
</style>
|
||||
<img src="./images/logo.svg" alt="icon" width="24" height="24" aria-labelledby="label" />
|
||||
<span id="label">label</span>
|
||||
<elix-number-spin-box min="1" step="1"></elix-number-spin-box>
|
||||
`;
|
||||
|
||||
/**
|
||||
* @class SESpinInput
|
||||
*/
|
||||
export class SESpinInput extends HTMLElement {
|
||||
/**
|
||||
* @function constructor
|
||||
*/
|
||||
constructor () {
|
||||
super();
|
||||
// create the shadowDom and insert the template
|
||||
this._shadowRoot = this.attachShadow({mode: 'open'});
|
||||
this._shadowRoot.append(template.content.cloneNode(true));
|
||||
// locate the component
|
||||
this.$img = this._shadowRoot.querySelector('img');
|
||||
this.$label = this.shadowRoot.getElementById('label');
|
||||
this.$event = new CustomEvent('change');
|
||||
this.$input = this._shadowRoot.querySelector('elix-number-spin-box');
|
||||
}
|
||||
/**
|
||||
* @function observedAttributes
|
||||
* @returns {any} observed
|
||||
*/
|
||||
static get observedAttributes () {
|
||||
return ['value', 'label', 'src', 'size', 'min', 'max', 'step'];
|
||||
}
|
||||
/**
|
||||
* @function attributeChangedCallback
|
||||
* @param {string} name
|
||||
* @param {string} oldValue
|
||||
* @param {string} newValue
|
||||
* @returns {void}
|
||||
*/
|
||||
attributeChangedCallback (name, oldValue, newValue) {
|
||||
if (oldValue === newValue) return;
|
||||
switch (name) {
|
||||
case 'src':
|
||||
this.$img.setAttribute('src', newValue);
|
||||
this.$label.remove();
|
||||
break;
|
||||
case 'size':
|
||||
// access to the underlying input box
|
||||
this.$input.shadowRoot.getElementById('input').size = newValue;
|
||||
// below seems mandatory to override the default width style that takes precedence on size
|
||||
this.$input.shadowRoot.getElementById('input').style.width = 'unset';
|
||||
break;
|
||||
case 'step':
|
||||
this.$input.setAttribute('step', newValue);
|
||||
break;
|
||||
case 'min':
|
||||
this.$input.setAttribute('min', newValue);
|
||||
break;
|
||||
case 'max':
|
||||
this.$input.setAttribute('max', newValue);
|
||||
break;
|
||||
case 'label':
|
||||
this.$label.textContent = newValue;
|
||||
this.$img.remove();
|
||||
break;
|
||||
case 'value':
|
||||
this.$input.value = newValue;
|
||||
break;
|
||||
default:
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`unknown attribute: ${name}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get label () {
|
||||
return this.getAttribute('label');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set label (value) {
|
||||
this.setAttribute('label', value);
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get value () {
|
||||
return this.$input.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set value (value) {
|
||||
this.$input.value = value;
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get src () {
|
||||
return this.getAttribute('src');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set src (value) {
|
||||
this.setAttribute('src', value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get size () {
|
||||
return this.getAttribute('size');
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set size (value) {
|
||||
this.setAttribute('size', value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function connectedCallback
|
||||
* @returns {void}
|
||||
*/
|
||||
connectedCallback () {
|
||||
this.$input.addEventListener('change', (e) => {
|
||||
e.preventDefault();
|
||||
this.value = e.target.value;
|
||||
this.dispatchEvent(this.$event);
|
||||
});
|
||||
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||
this.$input.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.value = e.target.value;
|
||||
this.dispatchEvent(this.$event);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Register
|
||||
customElements.define('se-spin-input', SESpinInput);
|
||||
188
src/editor/components/seZoom.js
Normal file
@@ -0,0 +1,188 @@
|
||||
import ListComboBox from 'elix/define/ListComboBox.js';
|
||||
import * as internal from 'elix/src/base/internal.js';
|
||||
import {templateFrom, fragmentFrom} from 'elix/src/core/htmlLiterals.js';
|
||||
import NumberSpinBox from '../dialogs/se-elix/define/NumberSpinBox.js';
|
||||
|
||||
/**
|
||||
* @class Dropdown
|
||||
*/
|
||||
class Zoom extends ListComboBox {
|
||||
/**
|
||||
* @function get
|
||||
* @returns {PlainObject}
|
||||
*/
|
||||
get [internal.defaultState] () {
|
||||
return Object.assign(super[internal.defaultState], {
|
||||
inputPartType: NumberSpinBox,
|
||||
src: './images/logo.svg',
|
||||
inputsize: '100%'
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @function get
|
||||
* @returns {PlainObject}
|
||||
*/
|
||||
get [internal.template] () {
|
||||
const result = super[internal.template];
|
||||
const source = result.content.getElementById('source');
|
||||
// add a icon before our dropdown
|
||||
source.prepend(fragmentFrom.html`
|
||||
<img src="./images/logo.svg" alt="icon" width="18" height="18"></img>
|
||||
`.cloneNode(true));
|
||||
// change the style so it fits in our toolbar
|
||||
result.content.append(
|
||||
templateFrom.html`
|
||||
<style>
|
||||
[part~="source"] {
|
||||
grid-template-columns: 20px 1fr auto;
|
||||
}
|
||||
::slotted(*) {
|
||||
padding: 4px;
|
||||
width: 100%;
|
||||
background-color: var(--icon-bg-color);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
[part~="popup"] {
|
||||
width: 150%;
|
||||
}
|
||||
elix-number-spin-box {
|
||||
background-color: var(--input-color);
|
||||
border-radius: 3px;
|
||||
height: 20px !important;
|
||||
margin-top: 1px;
|
||||
}
|
||||
elix-number-spin-box::part(spin-button) {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
</style>
|
||||
`.content
|
||||
);
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* @function observedAttributes
|
||||
* @returns {any} observed
|
||||
*/
|
||||
static get observedAttributes () {
|
||||
return ['title', 'src', 'inputsize', 'value'];
|
||||
}
|
||||
/**
|
||||
* @function attributeChangedCallback
|
||||
* @param {string} name
|
||||
* @param {string} oldValue
|
||||
* @param {string} newValue
|
||||
* @returns {void}
|
||||
*/
|
||||
attributeChangedCallback (name, oldValue, newValue) {
|
||||
if (oldValue === newValue) return;
|
||||
switch (name) {
|
||||
case 'title':
|
||||
// this.$span.setAttribute('title', `${newValue} ${shortcut ? `[${shortcut}]` : ''}`);
|
||||
break;
|
||||
case 'src':
|
||||
this.src = newValue;
|
||||
break;
|
||||
case 'inputsize':
|
||||
this.inputsize = newValue;
|
||||
break;
|
||||
default:
|
||||
super.attributeChangedCallback(name, oldValue, newValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function [internal.render]
|
||||
* @param {PlainObject} changed
|
||||
* @returns {void}
|
||||
*/
|
||||
[internal.render] (changed) {
|
||||
super[internal.render](changed);
|
||||
if (this[internal.firstRender]) {
|
||||
this.$img = this.shadowRoot.querySelector('img');
|
||||
this.$input = this.shadowRoot.getElementById('input');
|
||||
}
|
||||
if (changed.src) {
|
||||
this.$img.setAttribute('src', this[internal.state].src);
|
||||
}
|
||||
if (changed.inputsize) {
|
||||
this.$input.shadowRoot.querySelector('[part~="input"]').style.width = this[internal.state].inputsize;
|
||||
}
|
||||
if (changed.inputPartType) {
|
||||
// Wire up handler on new input.
|
||||
this.addEventListener('close', (e) => {
|
||||
e.preventDefault();
|
||||
const value = e.detail?.closeResult?.getAttribute('value');
|
||||
if (value) {
|
||||
const closeEvent = new CustomEvent('change', {detail: {value}});
|
||||
this.dispatchEvent(closeEvent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @function src
|
||||
* @returns {string} src
|
||||
*/
|
||||
get src () {
|
||||
return this[internal.state].src;
|
||||
}
|
||||
/**
|
||||
* @function src
|
||||
* @returns {void}
|
||||
*/
|
||||
set src (src) {
|
||||
this[internal.setState]({src});
|
||||
}
|
||||
/**
|
||||
* @function inputsize
|
||||
* @returns {string} src
|
||||
*/
|
||||
get inputsize () {
|
||||
return this[internal.state].inputsize;
|
||||
}
|
||||
/**
|
||||
* @function src
|
||||
* @returns {void}
|
||||
*/
|
||||
set inputsize (inputsize) {
|
||||
this[internal.setState]({inputsize});
|
||||
}
|
||||
/**
|
||||
* @function value
|
||||
* @returns {string} src
|
||||
*/
|
||||
get value () {
|
||||
return this[internal.state].value;
|
||||
}
|
||||
/**
|
||||
* @function value
|
||||
* @returns {void}
|
||||
*/
|
||||
set value (value) {
|
||||
this[internal.setState]({value});
|
||||
}
|
||||
}
|
||||
|
||||
// Register
|
||||
customElements.define('se-zoom', Zoom);
|
||||
|
||||
/*
|
||||
{TODO
|
||||
min: 0.001, max: 10000, step: 50, stepfunc: stepZoom,
|
||||
function stepZoom (elem, step) {
|
||||
const origVal = Number(elem.value);
|
||||
if (origVal === 0) { return 100; }
|
||||
const sugVal = origVal + step;
|
||||
if (step === 0) { return origVal; }
|
||||
|
||||
if (origVal >= 100) {
|
||||
return sugVal;
|
||||
}
|
||||
if (sugVal >= origVal) {
|
||||
return origVal * 2;
|
||||
}
|
||||
return origVal / 2;
|
||||
}
|
||||
*/
|
||||