mirror of
https://github.com/penpot/penpot.git
synced 2025-08-03 10:48:31 +02:00
♻️ Refactor bundle mechanism
Mainly leave shadow-cljs for build cljs stuff and use esbuild for bundle all js dependencies, completly avoiding all possible incompatibility issues between js libraries and google closure compiler.
This commit is contained in:
parent
366bca5f93
commit
607deb31dc
45 changed files with 2833 additions and 257 deletions
|
@ -6,7 +6,7 @@
|
|||
|
||||
(ns app.main.data.events
|
||||
(:require
|
||||
["ua-parser-js" :as UAParser]
|
||||
["ua-parser-js" :as ua]
|
||||
[app.common.data :as d]
|
||||
[app.common.logging :as l]
|
||||
[app.config :as cf]
|
||||
|
@ -38,7 +38,7 @@
|
|||
|
||||
(defn- collect-context
|
||||
[]
|
||||
(let [uagent (UAParser.)]
|
||||
(let [uagent (new ua/UAParser)]
|
||||
(merge
|
||||
{:app-version (:full cf/version)
|
||||
:locale @i18n/locale}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
(ns app.main.data.shortcuts
|
||||
(:refer-clojure :exclude [meta reset!])
|
||||
(:require
|
||||
["./shortcuts_impl.js$default" :as mousetrap]
|
||||
["@penpot/mousetrap$default" :as mousetrap]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.logging :as log]
|
||||
[app.common.schema :as sm]
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
/**
|
||||
* 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) KALEIDOS INC
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
import Mousetrap from 'mousetrap'
|
||||
|
||||
if (Mousetrap.addKeycodes) {
|
||||
Mousetrap.addKeycodes({
|
||||
219: '219'
|
||||
});
|
||||
}
|
||||
|
||||
const target = Mousetrap.prototype || Mousetrap;
|
||||
target.stopCallback = function (e, element, combo) {
|
||||
// if the element has the data attribute "mousetrap-dont-stop" then no need
|
||||
// to stop. It should be used like <div data-mousetrap-dont-stop>...</div>
|
||||
// or :div {:data-mousetrap-dont-stop true}
|
||||
if ('mousetrapDontStop' in element.dataset) {
|
||||
return false
|
||||
}
|
||||
|
||||
if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ('composedPath' in e && typeof e.composedPath === 'function') {
|
||||
// For open shadow trees, update `element` so that the following check works.
|
||||
const initialEventTarget = e.composedPath()[0];
|
||||
if (initialEventTarget !== e.target) {
|
||||
element = initialEventTarget;
|
||||
}
|
||||
}
|
||||
|
||||
// stop for input, select, textarea and button
|
||||
const shouldStop = element.tagName == "INPUT" ||
|
||||
element.tagName == "SELECT" ||
|
||||
element.tagName == "TEXTAREA" ||
|
||||
(element.tagName == "BUTTON" && combo.includes("tab")) ||
|
||||
(element.contentEditable && element.contentEditable == "true");
|
||||
return shouldStop;
|
||||
}
|
||||
|
||||
export default Mousetrap;
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
(ns app.main.data.workspace.media
|
||||
(:require
|
||||
["svgo" :as svgo]
|
||||
["@penpot/svgo$default" :as svgo]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
(ns app.main.data.workspace.texts
|
||||
(:require
|
||||
["penpot/vendor/text-editor-v2" :as editor.v2]
|
||||
["@penpot/text-editor" :as editor.v2]
|
||||
[app.common.attrs :as attrs]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
|
|
|
@ -280,7 +280,7 @@
|
|||
(fn [event]
|
||||
(st/emit! (dd/hide-file-menu))
|
||||
(when can-edit
|
||||
(let [offset (dom/get-offset-position (.-nativeEvent event))
|
||||
(let [offset (dom/get-offset-position (dom/event->native-event event))
|
||||
|
||||
select-current? (not (contains? selected-files (:id file)))
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
[app.main.render :as render]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.object :as obj]
|
||||
[app.util.timers :as ts]
|
||||
|
@ -46,9 +47,10 @@
|
|||
|
||||
on-scroll
|
||||
(fn [event]
|
||||
(if (pos? (.. event -nativeEvent -deltaY))
|
||||
(on-right-arrow-click event)
|
||||
(on-left-arrow-click event)))
|
||||
(let [event (dom/event->native-event event)]
|
||||
(if (pos? (.-deltaY ^js event))
|
||||
(on-right-arrow-click event)
|
||||
(on-left-arrow-click event))))
|
||||
|
||||
on-mount
|
||||
(fn []
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
[app.main.ui.hooks :as h]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.color :as uc]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.keyboard :as kbd]
|
||||
[app.util.object :as obj]
|
||||
|
@ -92,7 +93,9 @@
|
|||
(mf/use-callback
|
||||
(mf/deps max-offset)
|
||||
(fn [event]
|
||||
(let [delta (+ (.. event -nativeEvent -deltaY) (.. event -nativeEvent -deltaX))]
|
||||
(let [event (dom/event->native-event event)
|
||||
delta (+ (.. ^js event -deltaY)
|
||||
(.. ^js event -deltaX))]
|
||||
(if (pos? delta)
|
||||
(on-right-arrow-click event)
|
||||
(on-left-arrow-click event)))))]
|
||||
|
|
|
@ -61,8 +61,8 @@
|
|||
nil)))
|
||||
|
||||
(defn- styles-fn [shape styles content]
|
||||
(let [data (if (= (.getText content) "")
|
||||
(-> (.getData content)
|
||||
(let [data (if (= (.getText ^js content) "")
|
||||
(-> ^js (.getData content)
|
||||
(.toJS)
|
||||
(js->clj :keywordize-keys true))
|
||||
(txt/styles-to-attrs styles))]
|
||||
|
|
|
@ -134,8 +134,8 @@
|
|||
(let [children (-> (array/normalize-to-array children)
|
||||
(array/without-nils))
|
||||
|
||||
is-button? #(= :title-button (.. % -props -role))
|
||||
is-content? #(= :content (.. % -props -role))
|
||||
is-button? #(as-> % $ (= :title-button (.. ^js $ -props -role)))
|
||||
is-content? #(as-> % $ (= :content (.. ^js $ -props -role)))
|
||||
|
||||
buttons (array/filter is-button? children)
|
||||
content (array/filter is-content? children)
|
||||
|
@ -222,7 +222,8 @@
|
|||
|
||||
(defn set-drag-image
|
||||
[event item-ref num-selected]
|
||||
(let [offset (dom/get-offset-position (.-nativeEvent event))
|
||||
(let [offset (dom/get-offset-position
|
||||
(dom/event->native-event event))
|
||||
item-el (mf/ref-val item-ref)
|
||||
counter-el (create-counter-element num-selected)]
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
[app.main.store :as st]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.object :as obj]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -111,7 +112,9 @@
|
|||
(mf/use-callback
|
||||
(mf/deps max-offset)
|
||||
(fn [event]
|
||||
(let [delta (+ (.. event -nativeEvent -deltaY) (.. event -nativeEvent -deltaX))]
|
||||
(let [event (dom/event->native-event event)
|
||||
delta (+ (.. ^js event -deltaY)
|
||||
(.. ^js event -deltaX))]
|
||||
(if (pos? delta)
|
||||
(on-right-arrow-click event)
|
||||
(on-left-arrow-click event)))))]
|
||||
|
|
|
@ -65,15 +65,15 @@
|
|||
(dom/stop-propagation bevent)
|
||||
|
||||
(when-not @z?
|
||||
(let [event (.-nativeEvent bevent)
|
||||
(let [event (dom/event->native-event bevent)
|
||||
ctrl? (kbd/ctrl? event)
|
||||
meta? (kbd/meta? event)
|
||||
shift? (kbd/shift? event)
|
||||
alt? (kbd/alt? event)
|
||||
mod? (kbd/mod? event)
|
||||
|
||||
left-click? (and (not panning) (= 1 (.-which event)))
|
||||
middle-click? (and (not panning) (= 2 (.-which event)))]
|
||||
left-click? (and (not panning) (dom/left-mouse? bevent))
|
||||
middle-click? (and (not panning) (dom/middle-mouse? bevent))]
|
||||
|
||||
(cond
|
||||
(or middle-click? (and left-click? @space?))
|
||||
|
@ -120,12 +120,11 @@
|
|||
(mf/use-callback
|
||||
(mf/deps @hover @hover-ids selected @space? @z? read-only?)
|
||||
(fn [bevent]
|
||||
(let [event (.-nativeEvent bevent)
|
||||
(let [event (dom/event->native-event bevent)
|
||||
shift? (kbd/shift? event)
|
||||
mod? (kbd/mod? event)
|
||||
left-click? (= 1 (.-which event))]
|
||||
mod? (kbd/mod? event)]
|
||||
|
||||
(when (and left-click?
|
||||
(when (and (dom/left-mouse? bevent)
|
||||
(not mod?)
|
||||
(not shift?)
|
||||
(not @space?))
|
||||
|
@ -275,7 +274,7 @@
|
|||
;; Release pointer on mouse up
|
||||
(.releasePointerCapture target (.-pointerId event)))
|
||||
|
||||
(let [event (.-nativeEvent event)
|
||||
(let [event (dom/event->native-event event)
|
||||
ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
alt? (kbd/alt? event)
|
||||
|
@ -475,7 +474,7 @@
|
|||
(assoc :y final-y)))))
|
||||
|
||||
(dnd/has-type? event "penpot/component")
|
||||
(let [event (.-nativeEvent event)
|
||||
(let [event (dom/event->native-event event)
|
||||
ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
alt? (kbd/alt? event)
|
||||
|
|
|
@ -959,9 +959,8 @@
|
|||
handle-pointer-down
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(let [left-click? (= 1 (.-which (.-nativeEvent event)))]
|
||||
(when left-click?
|
||||
(dom/stop-propagation event)))))
|
||||
(when (dom/left-mouse? event)
|
||||
(dom/stop-propagation event))))
|
||||
|
||||
handle-add-column
|
||||
(mf/use-fn
|
||||
|
|
|
@ -110,7 +110,7 @@
|
|||
(mf/use-fn
|
||||
(mf/deps frame workspace-read-only?)
|
||||
(fn [bevent]
|
||||
(let [event (.-nativeEvent bevent)
|
||||
(let [event (dom/event->native-event bevent)
|
||||
position (dom/get-client-position event)]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
(ns app.util.code-highlight
|
||||
(:require
|
||||
["highlight.js" :as hljs]
|
||||
["@penpot/hljs" :as hljs]
|
||||
[app.util.dom :as dom]))
|
||||
|
||||
(defn highlight!
|
||||
|
|
|
@ -756,10 +756,17 @@
|
|||
|
||||
(trigger-download-uri filename mtype uri)))
|
||||
|
||||
(defn left-mouse? [bevent]
|
||||
(let [event (.-nativeEvent ^js bevent)]
|
||||
(defn left-mouse?
|
||||
[bevent]
|
||||
(let [event (.-nativeEvent ^js bevent)]
|
||||
(= 1 (.-which event))))
|
||||
|
||||
(defn middle-mouse?
|
||||
[bevent]
|
||||
(let [event (.-nativeEvent ^js bevent)]
|
||||
(= 2 (.-which event))))
|
||||
|
||||
|
||||
;; Warning: need to protect against reverse tabnabbing attack
|
||||
;; https://www.comparitech.com/blog/information-security/reverse-tabnabbing/
|
||||
(defn open-new-window
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
(ns app.util.text-editor
|
||||
"Draft related abstraction functions."
|
||||
(:require
|
||||
["./text_editor_impl.js" :as impl]
|
||||
["draft-js" :as draft]
|
||||
["@penpot/draft-js" :as impl]
|
||||
[app.common.text :as txt]))
|
||||
|
||||
;; --- CONVERSION
|
||||
|
@ -34,12 +33,12 @@
|
|||
|
||||
(defn import-content
|
||||
[content]
|
||||
(-> content txt/convert-to-draft clj->js draft/convertFromRaw))
|
||||
(-> content txt/convert-to-draft clj->js impl/convertFromRaw))
|
||||
|
||||
(defn export-content
|
||||
[content]
|
||||
(-> content
|
||||
(draft/convertToRaw)
|
||||
(impl/convertToRaw)
|
||||
(js->clj :keywordize-keys true)
|
||||
(txt/convert-from-draft)))
|
||||
|
||||
|
|
|
@ -1,408 +0,0 @@
|
|||
/**
|
||||
* 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) KALEIDOS INC
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import {
|
||||
BlockMapBuilder,
|
||||
CharacterMetadata,
|
||||
CompositeDecorator,
|
||||
EditorState,
|
||||
Modifier,
|
||||
RichTextEditorUtil,
|
||||
SelectionState,
|
||||
} from "draft-js";
|
||||
|
||||
import DraftPasteProcessor from 'draft-js/lib/DraftPasteProcessor';
|
||||
import {Map, OrderedSet} from "immutable";
|
||||
|
||||
function isDefined(v) {
|
||||
return v !== undefined && v !== null;
|
||||
}
|
||||
|
||||
function mergeBlockData(block, newData) {
|
||||
let data = block.getData();
|
||||
|
||||
for (let key of Object.keys(newData)) {
|
||||
const oldVal = data.get(key);
|
||||
if (oldVal === newData[key]) {
|
||||
data = data.delete(key);
|
||||
} else {
|
||||
data = data.set(key, newData[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return block.mergeDeep({
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
export function createEditorState(content, decorator) {
|
||||
if (content === null) {
|
||||
return EditorState.createEmpty(decorator);
|
||||
} else {
|
||||
return EditorState.createWithContent(content, decorator);
|
||||
}
|
||||
}
|
||||
|
||||
export function createDecorator(type, component) {
|
||||
const strategy = (block, callback, content) => {
|
||||
return block.findEntityRanges((cmeta) => {
|
||||
const entityKey = cmeta.getEntity();
|
||||
return isDefined(entityKey) && (type === content.getEntity(entityKey).getType());
|
||||
}, callback);
|
||||
};
|
||||
|
||||
return new CompositeDecorator([
|
||||
{"strategy": strategy, "component": component}
|
||||
]);
|
||||
}
|
||||
|
||||
function getSelectAllSelection(state) {
|
||||
const content = state.getCurrentContent();
|
||||
const firstBlock = content.getBlockMap().first();
|
||||
const lastBlock = content.getBlockMap().last();
|
||||
|
||||
return new SelectionState({
|
||||
"anchorKey": firstBlock.getKey(),
|
||||
"anchorOffset": 0,
|
||||
"focusKey": lastBlock.getKey(),
|
||||
"focusOffset": lastBlock.getLength()
|
||||
});
|
||||
}
|
||||
|
||||
function getCursorInEndPosition(state) {
|
||||
const content = state.getCurrentContent();
|
||||
const lastBlock = content.getBlockMap().last();
|
||||
|
||||
return new SelectionState({
|
||||
"anchorKey": lastBlock.getKey(),
|
||||
"anchorOffset": lastBlock.getLength(),
|
||||
"focusKey": lastBlock.getKey(),
|
||||
"focusOffset": lastBlock.getLength()
|
||||
});
|
||||
}
|
||||
|
||||
export function selectAll(state) {
|
||||
return EditorState.forceSelection(state, getSelectAllSelection(state));
|
||||
}
|
||||
|
||||
function modifySelectedBlocks(contentState, selectionState, operation) {
|
||||
var startKey = selectionState.getStartKey();
|
||||
var endKey = selectionState.getEndKey();
|
||||
var blockMap = contentState.getBlockMap();
|
||||
|
||||
var newBlocks = blockMap.toSeq().skipUntil(function (_, k) {
|
||||
return k === startKey;
|
||||
}).takeUntil(function (_, k) {
|
||||
return k === endKey;
|
||||
}).concat(Map([[endKey, blockMap.get(endKey)]])).map(operation);
|
||||
|
||||
return contentState.merge({
|
||||
"blockMap": blockMap.merge(newBlocks),
|
||||
"selectionBefore": selectionState,
|
||||
"selectionAfter": selectionState
|
||||
});
|
||||
}
|
||||
|
||||
export function updateCurrentBlockData(state, attrs) {
|
||||
const selection = state.getSelection();
|
||||
let content = state.getCurrentContent();
|
||||
|
||||
content = modifySelectedBlocks(content, selection, (block) => {
|
||||
return mergeBlockData(block, attrs);
|
||||
});
|
||||
|
||||
return EditorState.push(state, content, "change-block-data");
|
||||
}
|
||||
|
||||
function addStylesToOverride(styles, other) {
|
||||
let result = styles;
|
||||
|
||||
for (let style of other) {
|
||||
const [p, k, v] = style.split("$$$");
|
||||
const prefix = [p, k, ""].join("$$$");
|
||||
|
||||
const curValue = result.find((it) => it.startsWith(prefix))
|
||||
if (curValue) {
|
||||
result = result.remove(curValue);
|
||||
}
|
||||
result = result.add(style);
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export function applyInlineStyle(state, styles) {
|
||||
const userSelection = state.getSelection();
|
||||
let selection = userSelection;
|
||||
let result = state;
|
||||
|
||||
if (selection.isCollapsed()) {
|
||||
const currentOverride = state.getCurrentInlineStyle() || new OrderedSet();
|
||||
const styleOverride = addStylesToOverride(currentOverride, styles)
|
||||
return EditorState.setInlineStyleOverride(state, styleOverride);
|
||||
}
|
||||
|
||||
let content = null;
|
||||
|
||||
for (let style of styles) {
|
||||
const [p, k, v] = style.split("$$$");
|
||||
const prefix = [p, k, ""].join("$$$");
|
||||
|
||||
content = result.getCurrentContent();
|
||||
content = removeInlineStylePrefix(content, selection, prefix);
|
||||
|
||||
if (v !== "z:null") {
|
||||
content = Modifier.applyInlineStyle(content, selection, style);
|
||||
}
|
||||
|
||||
result = EditorState.push(result, content, "change-inline-style");
|
||||
}
|
||||
|
||||
return EditorState.acceptSelection(result, userSelection);
|
||||
}
|
||||
|
||||
export function splitBlockPreservingData(state) {
|
||||
let content = state.getCurrentContent();
|
||||
const selection = state.getSelection();
|
||||
|
||||
content = Modifier.splitBlock(content, selection);
|
||||
|
||||
const blockData = content.blockMap.get(content.selectionBefore.getStartKey()).getData();
|
||||
const blockKey = content.selectionAfter.getStartKey();
|
||||
const blockMap = content.blockMap.update(blockKey, (block) => {
|
||||
return block.set("data", blockData);
|
||||
});
|
||||
|
||||
content = content.set("blockMap", blockMap);
|
||||
|
||||
return EditorState.push(state, content, "split-block");
|
||||
}
|
||||
|
||||
export function addBlurSelectionEntity(state) {
|
||||
let content = state.getCurrentContent(state);
|
||||
const selection = state.getSelection();
|
||||
|
||||
content = content.createEntity("PENPOT_SELECTION", "MUTABLE");
|
||||
const entityKey = content.getLastCreatedEntityKey();
|
||||
|
||||
content = Modifier.applyEntity(content, selection, entityKey);
|
||||
return EditorState.push(state, content, "apply-entity");
|
||||
}
|
||||
|
||||
export function removeBlurSelectionEntity(state) {
|
||||
const selectionAll = getSelectAllSelection(state);
|
||||
const selection = state.getSelection();
|
||||
|
||||
let content = state.getCurrentContent();
|
||||
content = Modifier.applyEntity(content, selectionAll, null);
|
||||
|
||||
state = EditorState.push(state, content, "apply-entity");
|
||||
state = EditorState.forceSelection(state, selection);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
export function getCurrentBlock(state) {
|
||||
const content = state.getCurrentContent();
|
||||
const selection = state.getSelection();
|
||||
const startKey = selection.getStartKey();
|
||||
return content.getBlockForKey(startKey);
|
||||
}
|
||||
|
||||
export function getCurrentEntityKey(state) {
|
||||
const block = getCurrentBlock(state);
|
||||
const selection = state.getSelection();
|
||||
const startOffset = selection.getStartOffset();
|
||||
return block.getEntityAt(startOffset);
|
||||
}
|
||||
|
||||
export function removeInlineStylePrefix(contentState, selectionState, stylePrefix) {
|
||||
const startKey = selectionState.getStartKey();
|
||||
const startOffset = selectionState.getStartOffset();
|
||||
const endKey = selectionState.getEndKey();
|
||||
const endOffset = selectionState.getEndOffset();
|
||||
|
||||
return modifySelectedBlocks(contentState, selectionState, (block, blockKey) => {
|
||||
let sliceStart;
|
||||
let sliceEnd;
|
||||
|
||||
if (startKey === endKey) {
|
||||
sliceStart = startOffset;
|
||||
sliceEnd = endOffset;
|
||||
} else {
|
||||
sliceStart = blockKey === startKey ? startOffset : 0;
|
||||
sliceEnd = blockKey === endKey ? endOffset : block.getLength();
|
||||
}
|
||||
|
||||
let chars = block.getCharacterList();
|
||||
let current;
|
||||
|
||||
while (sliceStart < sliceEnd) {
|
||||
current = chars.get(sliceStart);
|
||||
current = current.set("style", current.getStyle().filter((s) => !s.startsWith(stylePrefix)))
|
||||
chars = chars.set(sliceStart, CharacterMetadata.create(current));
|
||||
|
||||
sliceStart++;
|
||||
}
|
||||
|
||||
return block.set("characterList", chars);
|
||||
});
|
||||
}
|
||||
|
||||
export function cursorToEnd(state) {
|
||||
const newSelection = getCursorInEndPosition(state);
|
||||
const selection = state.getSelection();
|
||||
|
||||
let content = state.getCurrentContent();
|
||||
content = Modifier.applyEntity(content, newSelection, null);
|
||||
|
||||
state = EditorState.forceSelection(state, newSelection);
|
||||
state = EditorState.push(state, content, "apply-entity");
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
export function isCurrentEmpty(state) {
|
||||
const selection = state.getSelection();
|
||||
|
||||
if (!selection.isCollapsed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const blockKey = selection.getStartKey();
|
||||
const content = state.getCurrentContent();
|
||||
|
||||
const block = content.getBlockForKey(blockKey);
|
||||
|
||||
return block.getText() === "";
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the block keys between a selection
|
||||
*/
|
||||
export function getSelectedBlocks(state) {
|
||||
const selection = state.getSelection();
|
||||
const startKey = selection.getStartKey();
|
||||
const endKey = selection.getEndKey();
|
||||
const content = state.getCurrentContent();
|
||||
const result = [ startKey ];
|
||||
|
||||
let currentKey = startKey;
|
||||
|
||||
while (currentKey !== endKey) {
|
||||
const currentBlock = content.getBlockAfter(currentKey);
|
||||
currentKey = currentBlock.getKey();
|
||||
result.push(currentKey);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getBlockContent(state, blockKey) {
|
||||
const content = state.getCurrentContent();
|
||||
const block = content.getBlockForKey(blockKey);
|
||||
return block.getText();
|
||||
}
|
||||
|
||||
export function getBlockData(state, blockKey) {
|
||||
const content = state.getCurrentContent();
|
||||
const block = content.getBlockForKey(blockKey);
|
||||
return block && block.getData().toJS();
|
||||
}
|
||||
|
||||
export function updateBlockData(state, blockKey, data) {
|
||||
const userSelection = state.getSelection();
|
||||
const inlineStyleOverride = state.getInlineStyleOverride();
|
||||
const content = state.getCurrentContent();
|
||||
const block = content.getBlockForKey(blockKey);
|
||||
const newBlock = mergeBlockData(block, data);
|
||||
|
||||
const blockData = newBlock.getData();
|
||||
|
||||
const newContent = Modifier.setBlockData(
|
||||
state.getCurrentContent(),
|
||||
SelectionState.createEmpty(blockKey),
|
||||
blockData
|
||||
);
|
||||
|
||||
let result = EditorState.push(state, newContent, 'change-block-data');
|
||||
result = EditorState.acceptSelection(result, userSelection);
|
||||
result = EditorState.setInlineStyleOverride(result, inlineStyleOverride);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getSelection(state) {
|
||||
return state.getSelection();
|
||||
}
|
||||
|
||||
export function setSelection(state, selection) {
|
||||
return EditorState.acceptSelection(state, selection);
|
||||
}
|
||||
|
||||
export function selectBlock(state, blockKey) {
|
||||
const block = state.getCurrentContent().getBlockForKey(blockKey);
|
||||
const length = block.getText().length;
|
||||
const selection = SelectionState.createEmpty(blockKey).merge({
|
||||
focusOffset: length
|
||||
});
|
||||
return EditorState.acceptSelection(state, selection);
|
||||
}
|
||||
|
||||
export function getInlineStyle(state, blockKey, offset) {
|
||||
const content = state.getCurrentContent();
|
||||
const block = content.getBlockForKey(blockKey);
|
||||
return block.getInlineStyleAt(offset).toJS();
|
||||
}
|
||||
|
||||
const NEWLINE_REGEX = /\r\n?|\n/g;
|
||||
|
||||
function splitTextIntoTextBlocks(text) {
|
||||
return text.split(NEWLINE_REGEX);
|
||||
}
|
||||
|
||||
export function insertText(state, text, attrs, inlineStyles) {
|
||||
const blocks = splitTextIntoTextBlocks(text);
|
||||
|
||||
const character = CharacterMetadata.create({style: OrderedSet(inlineStyles)});
|
||||
|
||||
let blockArray = DraftPasteProcessor.processText(
|
||||
blocks,
|
||||
character,
|
||||
"unstyled",
|
||||
);
|
||||
|
||||
blockArray = blockArray.map((b) => {
|
||||
return mergeBlockData(b, attrs);
|
||||
});
|
||||
|
||||
const fragment = BlockMapBuilder.createFromArray(blockArray);
|
||||
const content = state.getCurrentContent();
|
||||
const selection = state.getSelection();
|
||||
|
||||
const newContent = Modifier.replaceWithFragment(
|
||||
content,
|
||||
selection,
|
||||
fragment
|
||||
);
|
||||
|
||||
const resultSelection = SelectionState.createEmpty(selection.getStartKey());
|
||||
return EditorState.push(state, newContent, 'insert-fragment');
|
||||
}
|
||||
|
||||
export function setInlineStyleOverride(state, inlineStyles) {
|
||||
return EditorState.setInlineStyleOverride(state, inlineStyles);
|
||||
}
|
||||
|
||||
export function selectionEquals(selection, other) {
|
||||
return selection.getAnchorKey() === other.getAnchorKey() &&
|
||||
selection.getAnchorOffset() === other.getAnchorOffset() &&
|
||||
selection.getFocusKey() === other.getFocusKey() &&
|
||||
selection.getFocusOffset() === other.getFocusOffset() &&
|
||||
selection.getIsBackward() === other.getIsBackward();
|
||||
}
|
|
@ -6,7 +6,36 @@
|
|||
|
||||
(ns app.util.time
|
||||
(:require
|
||||
["./time_impl" :as impl]
|
||||
["date-fns/format$default" :as dfn-format]
|
||||
["date-fns/formatDistanceToNowStrict$default" :as dfn-distance-to-now]
|
||||
["date-fns/locale/ar-SA$default" :as dfn-ar]
|
||||
["date-fns/locale/ca$default" :as dfn-ca]
|
||||
["date-fns/locale/cs$default" :as dfn-cs]
|
||||
["date-fns/locale/de$default" :as dfn-de]
|
||||
["date-fns/locale/el$default" :as dfn-el]
|
||||
["date-fns/locale/en-US$default" :as df-en-us]
|
||||
["date-fns/locale/es$default" :as dfn-es]
|
||||
["date-fns/locale/eu$default" :as dfn-eu]
|
||||
["date-fns/locale/fa-IR$default" :as dfn-fa-ir]
|
||||
["date-fns/locale/fr$default" :as dfn-fr]
|
||||
["date-fns/locale/gl$default" :as dfn-gl]
|
||||
["date-fns/locale/he$default" :as dfn-he]
|
||||
["date-fns/locale/hr$default" :as dfn-hr]
|
||||
["date-fns/locale/id$default" :as dfn-id]
|
||||
["date-fns/locale/it$default" :as dfn-it]
|
||||
["date-fns/locale/ja$default" :as dfn-ja]
|
||||
["date-fns/locale/ko$default" :as dfn-ko]
|
||||
["date-fns/locale/lv$default" :as dfn-lv]
|
||||
["date-fns/locale/nb$default" :as dfn-nb]
|
||||
["date-fns/locale/nl$default" :as dfn-nl]
|
||||
["date-fns/locale/pl$default" :as dfn-pl]
|
||||
["date-fns/locale/pt$default" :as dfn-pt]
|
||||
["date-fns/locale/pt-BR$default" :as dfn-pt-br]
|
||||
["date-fns/locale/ro$default" :as dfn-ro]
|
||||
["date-fns/locale/ru$default" :as dfn-ru]
|
||||
["date-fns/locale/tr$default" :as dfn-tr]
|
||||
["date-fns/locale/uk$default" :as dfn-uk]
|
||||
["date-fns/locale/zh-CN$default" :as dfn-zh-cn]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.time :as common-time]
|
||||
[app.util.object :as obj]
|
||||
|
@ -15,6 +44,42 @@
|
|||
(dm/export common-time/DateTime)
|
||||
(dm/export common-time/Duration)
|
||||
|
||||
(def locales
|
||||
#js {:ar dfn-ar
|
||||
:ca dfn-ca
|
||||
:de dfn-de
|
||||
:el dfn-el
|
||||
:en df-en-us
|
||||
:en_us df-en-us
|
||||
:es dfn-es
|
||||
:es_es dfn-es
|
||||
:fa dfn-fa-ir
|
||||
:fa_ir dfn-fa-ir
|
||||
:fr dfn-fr
|
||||
:he dfn-he
|
||||
:pt dfn-pt
|
||||
:pt_pt dfn-pt
|
||||
:pt_br dfn-pt-br
|
||||
:ro dfn-ro
|
||||
:ru dfn-ru
|
||||
:tr dfn-tr
|
||||
:zh-cn dfn-zh-cn
|
||||
:nl dfn-nl
|
||||
:eu dfn-eu
|
||||
:gl dfn-gl
|
||||
:hr dfn-hr
|
||||
:it dfn-it
|
||||
:nb dfn-nb
|
||||
:nb_no dfn-nb
|
||||
:pl dfn-pl
|
||||
:id dfn-id
|
||||
:uk dfn-uk
|
||||
:cs dfn-cs
|
||||
:lv dfn-lv
|
||||
:ko dfn-ko
|
||||
:ja dfn-ja
|
||||
:ja_jp dfn-ja})
|
||||
|
||||
(defprotocol ITimeMath
|
||||
(plus [_ o])
|
||||
(minus [_ o]))
|
||||
|
@ -199,18 +264,18 @@
|
|||
(let [v (if (datetime? v) (format v :date) v)]
|
||||
(->> #js {:includeSeconds true
|
||||
:addSuffix true
|
||||
:locale (obj/get impl/locales locale)}
|
||||
(impl/format-distance-to-now v))))))
|
||||
:locale (obj/get locales locale)}
|
||||
(dfn-distance-to-now v))))))
|
||||
|
||||
(defn format-date-locale
|
||||
([v] (format-date-locale v nil))
|
||||
([v {:keys [locale] :or {locale "en"}}]
|
||||
(when v
|
||||
(let [v (if (datetime? v) (format v :date) v)
|
||||
locale (obj/get impl/locales locale)
|
||||
locale (obj/get locales locale)
|
||||
f (.date (.-formatLong ^js locale) v)]
|
||||
(->> #js {:locale locale}
|
||||
(impl/format v f))))))
|
||||
(dfn-format v f))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Measurement Helpers
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
import {format as dfnFormat} from "date-fns/format";
|
||||
import {formatDistanceToNowStrict as dfnFormatDistance} from "date-fns/formatDistanceToNowStrict";
|
||||
|
||||
import {arSA} from "date-fns/locale/ar-SA";
|
||||
import {ca} from "date-fns/locale/ca";
|
||||
import {de} from "date-fns/locale/de";
|
||||
import {el} from "date-fns/locale/el";
|
||||
import {enUS} from "date-fns/locale/en-US";
|
||||
import {es} from "date-fns/locale/es";
|
||||
import {faIR} from "date-fns/locale/fa-IR";
|
||||
import {fr} from "date-fns/locale/fr";
|
||||
import {he} from "date-fns/locale/he";
|
||||
import {pt} from "date-fns/locale/pt";
|
||||
import {ptBR} from "date-fns/locale/pt-BR";
|
||||
import {ro} from "date-fns/locale/ro";
|
||||
import {ru} from "date-fns/locale/ru";
|
||||
import {tr} from "date-fns/locale/tr";
|
||||
import {zhCN} from "date-fns/locale/zh-CN";
|
||||
import {nl} from "date-fns/locale/nl";
|
||||
import {eu} from "date-fns/locale/eu";
|
||||
import {gl} from "date-fns/locale/gl";
|
||||
import {hr} from "date-fns/locale/hr";
|
||||
import {it} from "date-fns/locale/it";
|
||||
import {nb} from "date-fns/locale/nb";
|
||||
import {pl} from "date-fns/locale/pl";
|
||||
import {id} from "date-fns/locale/id";
|
||||
import {uk} from "date-fns/locale/uk";
|
||||
import {cs} from "date-fns/locale/cs";
|
||||
import {lv} from "date-fns/locale/lv";
|
||||
import {ko} from "date-fns/locale/ko";
|
||||
import {ja} from "date-fns/locale/ja";
|
||||
|
||||
export const locales = {
|
||||
"ar": arSA,
|
||||
"ca": ca,
|
||||
"de": de,
|
||||
"el": el,
|
||||
"en": enUS,
|
||||
"en_us": enUS,
|
||||
"es": es,
|
||||
"es_es": es,
|
||||
"fa": faIR,
|
||||
"fa_ir": faIR,
|
||||
"fr": fr,
|
||||
"he": he,
|
||||
"pt": pt,
|
||||
"pt_pt": pt,
|
||||
"pt_br": ptBR,
|
||||
"ro": ro,
|
||||
"ru": ru,
|
||||
"tr": tr,
|
||||
"zh_cn": zhCN,
|
||||
"nl": nl,
|
||||
"eu": eu,
|
||||
"gl": gl,
|
||||
"hr": hr,
|
||||
"it": it,
|
||||
"nb": nb,
|
||||
"nb_no": nb,
|
||||
"pl": pl,
|
||||
"id": id,
|
||||
"uk": uk,
|
||||
"cs": cs,
|
||||
"lv": lv,
|
||||
"ko": ko,
|
||||
"ja": ja,
|
||||
"ja_jp": ja,
|
||||
};
|
||||
|
||||
export const format = dfnFormat;
|
||||
export const format_distance_to_now = dfnFormatDistance;
|
Loading…
Add table
Add a link
Reference in a new issue