update master to V7

This commit is contained in:
JFH
2021-05-09 19:29:45 +02:00
parent 41fc05672d
commit 593c415664
1000 changed files with 47537 additions and 54304 deletions

View 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;

View 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';

View 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);
}
}

View 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.

View File

@@ -0,0 +1,3 @@
jGraduate - A jQuery plugin for picking gradients
Licensed under the Apache License 2. See LICENSE for more information.

View 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);
}
}

View 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;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 B

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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;
}
}
}

View 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);

View 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);

View 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;
}
*/

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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;

View 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);

View 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);

View 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;
}
*/