Merge remote-tracking branch 'origin/main' into develop

This commit is contained in:
alonso.torres 2022-05-20 11:10:14 +02:00
commit 235d3dbf3d
110 changed files with 1833 additions and 1006 deletions

View file

@ -8,6 +8,7 @@
(:require
[app.common.data :as d]
[app.common.text :as txt]
[app.main.ui.formats :as fmt]
[app.util.color :as uc]
[cuerdas.core :as str]))
@ -108,7 +109,7 @@
(every? #(or (nil? %) (= % 0)) value)
(or (nil? value) (= value 0))))
default-format (fn [value] (str value "px"))
default-format (fn [value] (str (fmt/format-pixels value)))
format-property (fn [prop]
(let [css-prop (or (prop to-prop) (name prop))
format-fn (or (prop format) default-format)

View file

@ -345,8 +345,9 @@
(defn node->xml
[node]
(-> (js/XMLSerializer.)
(.serializeToString node)))
(when (some? node)
(-> (js/XMLSerializer.)
(.serializeToString node))))
(defn svg->data-uri
[svg]

View file

@ -0,0 +1,179 @@
/**
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
/**
Adapted to google closure from:
https://raw.githubusercontent.com/facebookarchive/fixed-data-table/master/src/vendor_upstream/dom/normalizeWheel.js
*/
'use strict';
goog.provide("app.util.normalize_wheel");
goog.scope(function() {
const self = app.util.normalize_wheel;
// const UserAgent_DEPRECATED = require('UserAgent_DEPRECATED');
// const isEventSupported = require('isEventSupported');
// Reasonable defaults
const PIXEL_STEP = 10;
const LINE_HEIGHT = 40;
const PAGE_HEIGHT = 800;
/**
* Mouse wheel (and 2-finger trackpad) support on the web sucks. It is
* complicated, thus this doc is long and (hopefully) detailed enough to answer
* your questions.
*
* If you need to react to the mouse wheel in a predictable way, this code is
* like your bestest friend. * hugs *
*
* As of today, there are 4 DOM event types you can listen to:
*
* 'wheel' -- Chrome(31+), FF(17+), IE(9+)
* 'mousewheel' -- Chrome, IE(6+), Opera, Safari
* 'MozMousePixelScroll' -- FF(3.5 only!) (2010-2013) -- don't bother!
* 'DOMMouseScroll' -- FF(0.9.7+) since 2003
*
* So what to do? The is the best:
*
* normalizeWheel.getEventType();
*
* In your event callback, use this code to get sane interpretation of the
* deltas. This code will return an object with properties:
*
* spinX -- normalized spin speed (use for zoom) - x plane
* spinY -- " - y plane
* pixelX -- normalized distance (to pixels) - x plane
* pixelY -- " - y plane
*
* Wheel values are provided by the browser assuming you are using the wheel to
* scroll a web page by a number of lines or pixels (or pages). Values can vary
* significantly on different platforms and browsers, forgetting that you can
* scroll at different speeds. Some devices (like trackpads) emit more events
* at smaller increments with fine granularity, and some emit massive jumps with
* linear speed or acceleration.
*
* This code does its best to normalize the deltas for you:
*
* - spin is trying to normalize how far the wheel was spun (or trackpad
* dragged). This is super useful for zoom support where you want to
* throw away the chunky scroll steps on the PC and make those equal to
* the slow and smooth tiny steps on the Mac. Key data: This code tries to
* resolve a single slow step on a wheel to 1.
*
* - pixel is normalizing the desired scroll delta in pixel units. You'll
* get the crazy differences between browsers, but at least it'll be in
* pixels!
*
* - positive value indicates scrolling DOWN/RIGHT, negative UP/LEFT. This
* should translate to positive value zooming IN, negative zooming OUT.
* This matches the newer 'wheel' event.
*
* Why are there spinX, spinY (or pixels)?
*
* - spinX is a 2-finger side drag on the trackpad, and a shift + wheel turn
* with a mouse. It results in side-scrolling in the browser by default.
*
* - spinY is what you expect -- it's the classic axis of a mouse wheel.
*
* - I dropped spinZ/pixelZ. It is supported by the DOM 3 'wheel' event and
* probably is by browsers in conjunction with fancy 3D controllers .. but
* you know.
*
* Implementation info:
*
* Examples of 'wheel' event if you scroll slowly (down) by one step with an
* average mouse:
*
* OS X + Chrome (mouse) - 4 pixel delta (wheelDelta -120)
* OS X + Safari (mouse) - N/A pixel delta (wheelDelta -12)
* OS X + Firefox (mouse) - 0.1 line delta (wheelDelta N/A)
* Win8 + Chrome (mouse) - 100 pixel delta (wheelDelta -120)
* Win8 + Firefox (mouse) - 3 line delta (wheelDelta -120)
*
* On the trackpad:
*
* OS X + Chrome (trackpad) - 2 pixel delta (wheelDelta -6)
* OS X + Firefox (trackpad) - 1 pixel delta (wheelDelta N/A)
*
* On other/older browsers.. it's more complicated as there can be multiple and
* also missing delta values.
*
* The 'wheel' event is more standard:
*
* http://www.w3.org/TR/DOM-Level-3-Events/#events-wheelevents
*
* The basics is that it includes a unit, deltaMode (pixels, lines, pages), and
* deltaX, deltaY and deltaZ. Some browsers provide other values to maintain
* backward compatibility with older events. Those other values help us
* better normalize spin speed. Example of what the browsers provide:
*
* | event.wheelDelta | event.detail
* ------------------+------------------+--------------
* Safari v5/OS X | -120 | 0
* Safari v5/Win7 | -120 | 0
* Chrome v17/OS X | -120 | 0
* Chrome v17/Win7 | -120 | 0
* IE9/Win7 | -120 | undefined
* Firefox v4/OS X | undefined | 1
* Firefox v4/Win7 | undefined | 3
*
*/
function normalizeWheel(/*object*/ event) /*object*/ {
var sX = 0, sY = 0, // spinX, spinY
pX = 0, pY = 0; // pixelX, pixelY
// Legacy
if ('detail' in event) { sY = event.detail; }
if ('wheelDelta' in event) { sY = -event.wheelDelta / 120; }
if ('wheelDeltaY' in event) { sY = -event.wheelDeltaY / 120; }
if ('wheelDeltaX' in event) { sX = -event.wheelDeltaX / 120; }
// side scrolling on FF with DOMMouseScroll
if ( 'axis' in event && event.axis === event.HORIZONTAL_AXIS ) {
sX = sY;
sY = 0;
}
pX = sX * PIXEL_STEP;
pY = sY * PIXEL_STEP;
if ('deltaY' in event) { pY = event.deltaY; }
if ('deltaX' in event) { pX = event.deltaX; }
if ((pX || pY) && event.deltaMode) {
if (event.deltaMode == 1) { // delta in LINE units
pX *= LINE_HEIGHT;
pY *= LINE_HEIGHT;
} else { // delta in PAGE units
pX *= PAGE_HEIGHT;
pY *= PAGE_HEIGHT;
}
}
// Fall-back if spin cannot be determined
if (pX && !sX) { sX = (pX < 1) ? -1 : 1; }
if (pY && !sY) { sY = (pY < 1) ? -1 : 1; }
return { spinX : sX,
spinY : sY,
pixelX : pX,
pixelY : pY };
}
self.normalize_wheel = normalizeWheel;
});

View file

@ -811,7 +811,7 @@
:attrs)
image-data (get-svg-data :image node)
svg-data (or image-data pattern-data)]
(:xlink:href svg-data)))
(or (:href svg-data) (:xlink:href svg-data))))
(defn get-image-fill
[node]

View file

@ -90,7 +90,6 @@
:polyline
:radialGradient
:rect
:script
:set
:stop
:style
@ -495,7 +494,6 @@
:marker
:mask
:pattern
:script
:style
:switch
:text
@ -963,5 +961,5 @@
(let [redfn (fn [acc {:keys [tag attrs]}]
(cond-> acc
(= :image tag)
(conj (:xlink:href attrs))))]
(conj (or (:href attrs) (:xlink:href attrs)))))]
(reduce-nodes redfn [] svg-data )))

View file

@ -0,0 +1,72 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) UXBOX Labs SL
*/
"use strict";
goog.provide("app.util.text_position_data");
goog.scope(function () {
const self = app.util.text_position_data;
const document = goog.global.document;
function getRangeRects(node, start, end) {
const range = document.createRange();
range.setStart(node, start);
range.setEnd(node, end);
return [...range.getClientRects()].filter((r) => r.width > 0);
}
self.parse_text_nodes = function(parent, textNode) {
const content = textNode.textContent;
const textSize = content.length;
let from = 0;
let to = 0;
let current = "";
let result = [];
let prevRect = null;
// This variable is to make sure there are not infinite loops
// when we don't advance `to` we store true and then force to
// advance `to` on the next iteration if the condition is true again
let safeguard = false;
while (to < textSize) {
const rects = getRangeRects(textNode, from, to + 1);
if (rects.length > 1 && safeguard) {
from++;
to++;
safeguard = false;
} else if (rects.length > 1) {
const position = prevRect;
result.push({
node: parent,
position: position,
text: current
});
from = to;
current = "";
safeguard = true;
} else {
prevRect = rects[0];
current += content[to];
to = to + 1;
safeguard = false;
}
}
// to == textSize
const rects = getRangeRects(textNode, from, to);
result.push({node: parent, position: rects[0], text: current});
return result;
};
});

View file

@ -11,45 +11,21 @@
[app.common.transit :as transit]
[app.main.store :as st]
[app.util.dom :as dom]
[app.util.globals :as global]))
[app.util.text-position-data :as tpd]))
(defn get-range-rects
"Retrieve the rectangles that cover the selection given by a `node` adn
the start and end index `start-i`, `end-i`"
[^js node start-i end-i]
(let [^js range (.createRange global/document)]
(.setStart range node start-i)
(.setEnd range node end-i)
(.getClientRects range)))
;; TODO: Evaluate to change this function to Javascript
(defn parse-text-nodes
"Given a text node retrieves the rectangles for everyone of its paragraphs and its text."
[parent-node rtl text-node]
[parent-node direction text-node]
(let [content (.-textContent text-node)
text-size (.-length content)]
(loop [from-i 0
to-i 0
current ""
result []]
(if (>= to-i text-size)
(let [rects (get-range-rects text-node from-i to-i)
entry {:node parent-node
:position (dom/bounding-rect->rect (first rects))
:text current}]
;; We need to add the last element not closed yet
(conj result entry))
(let [rects (get-range-rects text-node from-i (inc to-i))]
;; If the rects increase means we're in a new paragraph
(if (> (.-length rects) 1)
(let [entry {:node parent-node
:position (dom/bounding-rect->rect (if rtl (second rects) (first rects)))
:text current}]
(recur to-i to-i "" (conj result entry)))
(recur from-i (inc to-i) (str current (nth content to-i)) result)))))))
(letfn [(parse-entry [^js entry]
{:node (.-node entry)
:position (dom/bounding-rect->rect (.-position entry))
:text (.-text entry)
:direction direction})]
(into
[]
(map parse-entry)
(tpd/parse-text-nodes parent-node text-node))))
(defn calc-text-node-positions
@ -87,9 +63,9 @@
(->> text-nodes
(mapcat
(fn [parent-node]
(let [rtl (= "rtl" (.-dir (.-parentElement parent-node)))]
(let [direction (.-direction (js/getComputedStyle parent-node))]
(->> (.-childNodes parent-node)
(mapcat #(parse-text-nodes parent-node rtl %))))))
(mapcat #(parse-text-nodes parent-node direction %))))))
(mapv #(update % :position translate-rect))))))
(defn calc-position-data
@ -100,25 +76,27 @@
(let [text-data (calc-text-node-positions base-node viewport zoom)]
(when (d/not-empty? text-data)
(->> text-data
(mapv (fn [{:keys [node position text]}]
(mapv (fn [{:keys [node position text direction]}]
(let [{:keys [x y width height]} position
rtl (= "rtl" (.-dir (.-parentElement ^js node)))
styles (js/getComputedStyle ^js node)
get (fn [prop]
(let [value (.getPropertyValue styles prop)]
(when (and value (not= value ""))
value)))]
(d/without-nils
{:rtl rtl
:x (if rtl (+ x width) x)
{:x x
:y (+ y height)
:width width
:height height
:direction direction
:font-family (str (get "font-family"))
:font-size (str (get "font-size"))
:font-weight (str (get "font-weight"))
:text-transform (str (get "text-transform"))
:text-decoration (str (get "text-decoration"))
:letter-spacing (str (get "letter-spacing"))
:font-style (str (get "font-style"))
:fills (transit/decode-str (get "--fills"))
:text text}))))))))))