From 88690b6a2bdc40ca1942bdf7799da4343eaea707 Mon Sep 17 00:00:00 2001 From: JFH <20402845+jfhenon@users.noreply.github.com> Date: Sun, 20 Dec 2020 19:12:43 +0100 Subject: [PATCH 1/6] first pass --- src/editor/ConfigObj.js | 5 +- src/editor/components/PaintBox.js | 172 ++++++++++++ src/editor/components/index.js | 1 + src/editor/components/seColorPicker.js | 146 +++++++++++ src/editor/index.html | 18 +- src/editor/index.js | 1 - src/editor/svgedit.js | 346 ++----------------------- 7 files changed, 348 insertions(+), 341 deletions(-) create mode 100644 src/editor/components/PaintBox.js create mode 100644 src/editor/components/seColorPicker.js diff --git a/src/editor/ConfigObj.js b/src/editor/ConfigObj.js index 3f1284da..c7a64c7e 100644 --- a/src/editor/ConfigObj.js +++ b/src/editor/ConfigObj.js @@ -61,7 +61,6 @@ export default class ConfigObj { * @property {boolean} [avoidClientSideOpen=false] Used by `ext-server_opensave.js`; set to `true` if you wish to always open from the server and not only as fallback when FileReader client support is lacking * @property {string[]} [extensions=[]] Extensions to load on startup. Use an array in `setConfig` and comma separated file names in the URL. Extension names must begin with "ext-". Note that as of version 2.7, paths containing "/", "\", or ":", are disallowed for security reasons. Although previous versions of this list would entirely override the default list, as of version 2.7, the defaults will always be added to this explicit list unless the configuration `noDefaultExtensions` is included. See {@link module:SVGEditor~defaultExtensions}. * @property {string[]} [allowedOrigins=[]] Used by `ext-xdomain-messaging.js` to indicate which origins are permitted for cross-domain messaging (e.g., between the embedded editor and main editor code). Besides explicit domains, one might add '*' to allow all domains (not recommended for privacy/data integrity of your user's content!), `window.location.origin` for allowing the same origin (should be safe if you trust all apps on your domain), 'null' to allow `file:///` URL usage - * @property {null|PlainObject} [colorPickerCSS=null] Object of CSS properties mapped to values (for jQuery) to apply to the color picker. See {@link http://api.jquery.com/css/#css-properties}. A `null` value (the default) will cause the CSS to default to `left` with a position equal to that of the `fill_color` or `stroke_color` element minus 140, and a `bottom` equal to 40 * @property {string} [paramurl] This was available via URL only. Allowed an un-encoded URL within the query string (use "url" or "source" with a data: URI instead) * @property {Float} [canvas_expansion=3] The minimum area visible outside the canvas, as a multiple of the image dimensions. The larger the number, the more one can scroll outside the canvas. * @property {PlainObject} [initFill] Init fill properties @@ -89,8 +88,7 @@ export default class ConfigObj { * @property {boolean} [showGrid=false] Set by `ext-grid.js`; determines whether or not to show the grid by default * @property {boolean} [show_outside_canvas=true] Defines whether or not elements outside the canvas should be visible. Set and used in `svgcanvas.js`. * @property {boolean} [selectNew=true] If true, will replace the selection with the current element and automatically select element objects (when not in "path" mode) after they are created, showing their grips (v2.6). Set and used in `svgcanvas.js` (`mouseUp`). - * @todo Some others could be preferences as well (e.g., preventing URL changing of extensions, defaultExtensions, stylesheets, colorPickerCSS); Change the following to preferences and add pref controls where missing to the UI (e.g., `canvas_expansion`, `initFill`, `initStroke`, `text`, `initOpacity`, `dimensions`, `initTool`, `wireframe`, `showlayers`, `gridSnapping`, `gridColor`, `baseUnit`, `snappingStep`, `showRulers`, `exportWindowType`, `showGrid`, `show_outside_canvas`, `selectNew`)? - */ + */ this.defaultConfig = { canvasName: 'default', canvas_expansion: 3, @@ -109,7 +107,6 @@ export default class ConfigObj { font_family: 'serif' }, initOpacity: 1, - colorPickerCSS: null, // Defaults to 'left' with a position equal to that of the fill_color or stroke_color element minus 140, and a 'bottom' equal to 40 initTool: 'select', exportWindowType: 'new', // 'same' (todo: also support 'download') wireframe: false, diff --git a/src/editor/components/PaintBox.js b/src/editor/components/PaintBox.js new file mode 100644 index 00000000..b1df023f --- /dev/null +++ b/src/editor/components/PaintBox.js @@ -0,0 +1,172 @@ +/* globals $ */ +/** + * + */ +class PaintBox { + /** + * @param {string|Element|external:jQuery} container + * @param {PlainObject} svgcanvas + * @param {"fill"} type + * @param {string} color + * @param {number} opacity + */ + constructor (container, svgcanvas, type, color, opacity) { + // set up gradients to be used for the buttons + const svgdocbox = new DOMParser().parseFromString( + ` + + + `, + 'text/xml' + ); + + let docElem = svgdocbox.documentElement; + docElem = $(container)[0].appendChild(document.importNode(docElem, true)); + docElem.setAttribute('width', 16.5); + + 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; + this.svgcanvas = svgcanvas; + } + + /** + * @param {module:jGraduate~Paint} paint + * @param {boolean} apply + * @returns {void} + */ + setPaint (paint, apply) { + 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 = this.defs.appendChild(paint[ptype]); + const id = this.grad.id = 'gradbox_' + this.type; + fillAttr = 'url(#' + id + ')'; + break; + } + } + + this.rect.setAttribute('fill', fillAttr); + this.rect.setAttribute('opacity', opac); + + if (apply) { + this.svgCanvas.setColor(this.type, this._paintColor, true); + this.svgCanvas.setPaintOpacity(this.type, this._paintOpacity, true); + } + } + /** + * @param {string} color + * @param {Float} opac + * @param {string} type + * @returns {module:jGraduate~Paint} + */ + getPaint (color, opac, type) { + // update the editor's fill paint + const opts = {alpha: opac}; + if (color.startsWith('url(#')) { + let refElem = this.svgCanvas.getRefElem(color); + refElem = (refElem) ? refElem.cloneNode(true) : $('#' + 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} selectedElement + * @param {boolean} apply + * @returns {void} + */ + update (selectedElement, apply) { + if (!selectedElement) { return; } + + 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; + 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; + } + 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; + } + } + + if (apply) { + this.svgCanvas.setColor(type, this._paintColor, true); + this.svgCanvas.setPaintOpacity(type, this._paintOpacity, true); + } + + this._paintOpacity *= 100; + + const paint = this.getPaint(this._paintColor, this._paintOpacity, type); + // update the rect inside #fill_color/#stroke_color + this.setPaint(paint); + } + + /** + * @returns {void} + */ + prep () { + const ptype = this.paint.type; + + switch (ptype) { + case 'linearGradient': + case 'radialGradient': { + const paint = new $.jGraduate.Paint({copy: this.paint}); + this.svgCanvas.setPaint(this.type, paint); + break; + } + } + } +} +PaintBox.ctr = 0; + +export default PaintBox; diff --git a/src/editor/components/index.js b/src/editor/components/index.js index a29c8d01..38adf0bb 100644 --- a/src/editor/components/index.js +++ b/src/editor/components/index.js @@ -7,3 +7,4 @@ import './seSpinInput.js'; import './sePalette.js'; import './seMenu.js'; import './seMenuItem.js'; +import './seColorPicker.js'; diff --git a/src/editor/components/seColorPicker.js b/src/editor/components/seColorPicker.js new file mode 100644 index 00000000..86f4e29a --- /dev/null +++ b/src/editor/components/seColorPicker.js @@ -0,0 +1,146 @@ +/* globals jQuery */ +import jQueryPluginJGraduate from '../jgraduate/jQuery.jGraduate.js'; +import jQueryPluginJPicker from '../jgraduate/jQuery.jPicker.js'; +import PaintBox from './PaintBox.js'; + +const $ = [ + jQueryPluginJGraduate, + jQueryPluginJPicker +].reduce((jq, func) => func(jq), jQuery); + +const template = document.createElement('template'); +template.innerHTML = ` + +
+ icon + +
+
+
+
+
+ +
+`; +/** + * @class SeMenuItem + */ +export class SeColorPicker extends HTMLElement { + /** + * @function constructor + */ + constructor () { + super(); + // create the shadowDom and insert the template + this._shadowRoot = this.attachShadow({mode: 'open'}); + this._shadowRoot.appendChild(template.content.cloneNode(true)); + this.$img = this._shadowRoot.querySelector('img'); + this.$label = this._shadowRoot.querySelector('label'); + this.$picker = this._shadowRoot.getElementById('picker'); + this.paintBox = null; + } + /** + * @function observedAttributes + * @returns {any} observed + */ + static get observedAttributes () { + return ['label', 'src', 'value', 'picker']; + } + /** + * @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); + break; + case 'label': + this.$label.setAttribute('title', 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); + } + + /** + * @param {PlainObject} selectedElement + * @param {bool} apply + * @returns {void} + */ + update (selectedElement, apply) { + this.paintBox.update(selectedElement, apply); + } + + /** + * @function connectedCallback + * @returns {void} + */ + connectedCallback () { + this.paintBox = new PaintBox(this, this.type); + let {paint} = this.paintBox; + $('#color_picker') + .draggable({ + cancel: '.jGraduate_tabs, .jGraduate_colPick, .jGraduate_gradPick, .jPicker', + containment: 'window' + }) + .jGraduate( + { + images: {clientPath: './jgraduate/images/'}, + paint, + window: {pickerTitle: this.label}, + // images: {clientPath: configObj.curConfig.imgPath}, + newstop: 'inverse' + }, + function (p) { + paint = new $.jGraduate.Paint(p); + this.paintBox.setPaint(paint); + this.svgCanvas.setPaint(this.picker, paint); + $('#color_picker').hide(); + }, + () => { + $('#color_picker').hide(); + } + ); + } +} + +// Register +customElements.define('se-colorpicker', SeColorPicker); diff --git a/src/editor/index.html b/src/editor/index.html index 9c363e8e..b9a93d8e 100644 --- a/src/editor/index.html +++ b/src/editor/index.html @@ -340,20 +340,8 @@
Fit to layer content
Fit to all content
-
- -
-
-
-
-
-
- -
-
-
-
-
+ +