mirror of
https://github.com/penpot/penpot.git
synced 2025-04-12 15:01:28 -05:00
✨ Improved transformation from and to JS for plugins
This commit is contained in:
parent
42230f2630
commit
ac58a5b8fa
15 changed files with 1359 additions and 622 deletions
|
@ -27,6 +27,7 @@
|
|||
[app.plugins.events :as events]
|
||||
[app.plugins.file :as file]
|
||||
[app.plugins.fonts :as fonts]
|
||||
[app.plugins.format :as format]
|
||||
[app.plugins.library :as library]
|
||||
[app.plugins.page :as page]
|
||||
[app.plugins.parser :as parser]
|
||||
|
@ -101,26 +102,12 @@
|
|||
shapes (->> shapes
|
||||
(map #(obj/get % "$id"))
|
||||
(mapcat #(cfh/get-children-with-self objects %)))
|
||||
|
||||
file-id (:current-file-id @st/state)
|
||||
shared-libs (:workspace-libraries @st/state)
|
||||
shared-libs (:workspace-libraries @st/state)]
|
||||
|
||||
format-entry
|
||||
(fn [{:keys [prop shape-id index]}]
|
||||
#js {:property (d/name prop)
|
||||
:index index
|
||||
:shapeId (str shape-id)})
|
||||
format-result
|
||||
(fn [[color attrs]]
|
||||
(let [shapes-info (apply array (map format-entry attrs))
|
||||
color (u/to-js color)]
|
||||
(obj/set! color "shapeInfo" shapes-info)
|
||||
color))]
|
||||
(apply
|
||||
array
|
||||
(->> (ctc/extract-all-colors shapes file-id shared-libs)
|
||||
(group-by :attrs)
|
||||
(map format-result))))))
|
||||
(->> (ctc/extract-all-colors shapes file-id shared-libs)
|
||||
(group-by :attrs)
|
||||
(format/format-array format/format-color-result)))))
|
||||
|
||||
(replaceColor
|
||||
[_ shapes old-color new-color]
|
||||
|
@ -188,8 +175,8 @@
|
|||
(p/create
|
||||
(fn [resolve reject]
|
||||
(->> (dwm/upload-media-url name file-id url)
|
||||
(rx/map u/to-js)
|
||||
(rx/take 1)
|
||||
(rx/map format/format-image)
|
||||
(rx/subs! resolve reject)))))))
|
||||
|
||||
(uploadMediaData
|
||||
|
@ -205,7 +192,7 @@
|
|||
:on-image identity
|
||||
:on-svg identity})
|
||||
(rx/take 1)
|
||||
(rx/map u/to-js)
|
||||
(rx/map format/format-image)
|
||||
(rx/subs! resolve reject))))))
|
||||
|
||||
(group
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
[app.main.fonts :as fonts]
|
||||
[app.main.store :as st]
|
||||
[app.plugins.shape :as shape]
|
||||
[app.plugins.text :as text]
|
||||
[app.plugins.utils :as u]
|
||||
[app.util.object :as obj]
|
||||
[cuerdas.core :as str]))
|
||||
|
@ -39,7 +40,7 @@
|
|||
|
||||
(applyToRange [_ range variant]
|
||||
(cond
|
||||
(not (shape/text-range? range))
|
||||
(not (text/text-range? range))
|
||||
(u/display-not-valid :applyToRange range)
|
||||
|
||||
;; TODO: Check variant inside font variants
|
||||
|
|
|
@ -4,5 +4,408 @@
|
|||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.plugins.format)
|
||||
(ns app.plugins.format
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.util.object :as obj]))
|
||||
|
||||
(defn format-id
|
||||
[id]
|
||||
(when id (dm/str id)))
|
||||
|
||||
(defn format-key
|
||||
[kw]
|
||||
(when kw (d/name kw)))
|
||||
|
||||
(defn format-array
|
||||
[format-fn coll]
|
||||
(when (some? coll)
|
||||
(apply array (keep format-fn coll))))
|
||||
|
||||
;; export type PenpotPoint = { x: number; y: number };
|
||||
(defn format-point
|
||||
[{:keys [x y] :as point}]
|
||||
(when (some? point)
|
||||
(obj/clear-empty
|
||||
#js {:x x :y y})))
|
||||
|
||||
;;export type PenpotBounds = {
|
||||
;; x: number;
|
||||
;; y: number;
|
||||
;; width: number;
|
||||
;; height: number;
|
||||
;;};
|
||||
(defn format-bounds
|
||||
[{:keys [x y width height] :as bounds}]
|
||||
(when (some? bounds)
|
||||
(obj/clear-empty
|
||||
#js {:x x :y y :width width :height height})))
|
||||
|
||||
;; export interface PenpotColorShapeInfoEntry {
|
||||
;; readonly property: string;
|
||||
;; readonly index?: number;
|
||||
;; readonly shapeId: string;
|
||||
;; }
|
||||
(defn format-shape-info
|
||||
[{:keys [prop shape-id index] :as info}]
|
||||
(when (some? info)
|
||||
(obj/clear-empty
|
||||
#js {:property (d/name prop)
|
||||
:index index
|
||||
:shapeId (dm/str shape-id)})))
|
||||
|
||||
;; export type PenpotGradient = {
|
||||
;; type: 'linear' | 'radial';
|
||||
;; startX: number;
|
||||
;; startY: number;
|
||||
;; endX: number;
|
||||
;; endY: number;
|
||||
;; width: number;
|
||||
;; stops: Array<{ color: string; opacity?: number; offset: number }>;
|
||||
;; };
|
||||
(defn format-stop
|
||||
[{:keys [color opacity offset] :as stop}]
|
||||
(when (some? stop)
|
||||
(obj/clear-empty #js {:color color :opacity opacity :offset offset})))
|
||||
|
||||
(defn format-gradient
|
||||
[{:keys [type start-x start-y end-x end-y width stops] :as gradient}]
|
||||
(when (some? gradient)
|
||||
(obj/clear-empty
|
||||
#js {:type (format-key type)
|
||||
:startX start-x
|
||||
:startY start-y
|
||||
:endX end-x
|
||||
:endY end-y
|
||||
:width width
|
||||
:stops (format-array format-stop stops)})))
|
||||
|
||||
;; export type PenpotImageData = {
|
||||
;; name?: string;
|
||||
;; width: number;
|
||||
;; height: number;
|
||||
;; mtype?: string;
|
||||
;; id: string;
|
||||
;; keepApectRatio?: boolean;
|
||||
;; };
|
||||
(defn format-image
|
||||
[{:keys [name width height mtype id keep-aspect-ratio] :as image}]
|
||||
(when (some? image)
|
||||
(obj/clear-empty
|
||||
#js {:name name
|
||||
:width width
|
||||
:height height
|
||||
:mtype mtype
|
||||
:id (format-id id)
|
||||
:keepAspectRatio keep-aspect-ratio})))
|
||||
|
||||
;; export interface PenpotColor {
|
||||
;; id?: string;
|
||||
;; name?: string;
|
||||
;; path?: string;
|
||||
;; color?: string;
|
||||
;; opacity?: number;
|
||||
;; refId?: string;
|
||||
;; refFile?: string;
|
||||
;; gradient?: PenpotGradient;
|
||||
;; image?: PenpotImageData;
|
||||
;; }
|
||||
(defn format-color
|
||||
[{:keys [id name path color opacity ref-id ref-file gradient image] :as color-data}]
|
||||
(when (some? color-data)
|
||||
(obj/clear-empty
|
||||
#js {:id (format-id id)
|
||||
:name name
|
||||
:path path
|
||||
:color color
|
||||
:opacity opacity
|
||||
:refId (format-id ref-id)
|
||||
:refFile (format-id ref-file)
|
||||
:gradient (format-gradient gradient)
|
||||
:image (format-image image)})))
|
||||
|
||||
;; PenpotColor & PenpotColorShapeInfo
|
||||
(defn format-color-result
|
||||
[[color attrs]]
|
||||
(let [shapes-info (apply array (map format-shape-info attrs))
|
||||
color (format-color color)]
|
||||
(obj/set! color "shapeInfo" shapes-info)
|
||||
color))
|
||||
|
||||
|
||||
;; export interface PenpotShadow {
|
||||
;; id?: string;
|
||||
;; style?: 'drop-shadow' | 'inner-shadow';
|
||||
;; offsetX?: number;
|
||||
;; offsetY?: number;
|
||||
;; blur?: number;
|
||||
;; spread?: number;
|
||||
;; hidden?: boolean;
|
||||
;; color?: PenpotColor;
|
||||
;; }
|
||||
(defn format-shadow
|
||||
[{:keys [id style offset-x offset-y blur spread hidden color] :as shadow}]
|
||||
(when (some? shadow)
|
||||
(obj/clear-empty
|
||||
#js {:id (-> id format-id)
|
||||
:style (-> style format-key)
|
||||
:offsetX offset-x
|
||||
:offsetY offset-y
|
||||
:blur blur
|
||||
:spread spread
|
||||
:hidden hidden
|
||||
:color (format-color color)})))
|
||||
|
||||
(defn format-shadows
|
||||
[shadows]
|
||||
(when (some? shadows)
|
||||
(format-array format-shadow shadows)))
|
||||
|
||||
;;export interface PenpotFill {
|
||||
;; fillColor?: string;
|
||||
;; fillOpacity?: number;
|
||||
;; fillColorGradient?: PenpotGradient;
|
||||
;; fillColorRefFile?: string;
|
||||
;; fillColorRefId?: string;
|
||||
;; fillImage?: PenpotImageData;
|
||||
;;}
|
||||
(defn format-fill
|
||||
[{:keys [fill-color fill-opacity fill-color-gradient fill-color-ref-file fill-color-ref-id fill-image] :as fill}]
|
||||
(when (some? fill)
|
||||
(obj/clear-empty
|
||||
#js {:fillColor fill-color
|
||||
:fillOpacity fill-opacity
|
||||
:fillColorGradient (format-gradient fill-color-gradient)
|
||||
:fillColorRefFile (format-id fill-color-ref-file)
|
||||
:fillColorRefId (format-id fill-color-ref-id)
|
||||
:fillImage (format-image fill-image)})))
|
||||
|
||||
(defn format-fills
|
||||
[fills]
|
||||
(when (some? fills)
|
||||
(format-array format-fill fills)))
|
||||
|
||||
;; export interface PenpotStroke {
|
||||
;; strokeColor?: string;
|
||||
;; strokeColorRefFile?: string;
|
||||
;; strokeColorRefId?: string;
|
||||
;; strokeOpacity?: number;
|
||||
;; strokeStyle?: 'solid' | 'dotted' | 'dashed' | 'mixed' | 'none' | 'svg';
|
||||
;; strokeWidth?: number;
|
||||
;; strokeAlignment?: 'center' | 'inner' | 'outer';
|
||||
;; strokeCapStart?: PenpotStrokeCap;
|
||||
;; strokeCapEnd?: PenpotStrokeCap;
|
||||
;; strokeColorGradient?: PenpotGradient;
|
||||
;; }
|
||||
(defn format-stroke
|
||||
[{:keys [stroke-color stroke-color-ref-file stroke-color-ref-id
|
||||
stroke-opacity stroke-style stroke-width stroke-alignment
|
||||
stroke-cap-start stroke-cap-end stroke-color-gradient] :as stroke}]
|
||||
|
||||
(when (some? stroke)
|
||||
(obj/clear-empty
|
||||
#js {:strokeColor stroke-color
|
||||
:strokeColorRefFile (format-id stroke-color-ref-file)
|
||||
:strokeColorRefId (format-id stroke-color-ref-id)
|
||||
:strokeOpacity stroke-opacity
|
||||
:strokeStyle (format-key stroke-style)
|
||||
:strokeWidth stroke-width
|
||||
:strokeAlignment (format-key stroke-alignment)
|
||||
:strokeCapStart (format-key stroke-cap-start)
|
||||
:strokeCapEnd (format-key stroke-cap-end)
|
||||
:strokeColorGradient (format-gradient stroke-color-gradient)})))
|
||||
|
||||
(defn format-strokes
|
||||
[strokes]
|
||||
(when (some? strokes)
|
||||
(format-array format-stroke strokes)))
|
||||
|
||||
;; export interface PenpotBlur {
|
||||
;; id?: string;
|
||||
;; type?: 'layer-blur';
|
||||
;; value?: number;
|
||||
;; hidden?: boolean;
|
||||
;; }
|
||||
(defn format-blur
|
||||
[{:keys [id type value hidden] :as blur}]
|
||||
(when (some? blur)
|
||||
(obj/clear-empty
|
||||
#js {:id (format-id id)
|
||||
:type (format-key type)
|
||||
:value value
|
||||
:hidden hidden})))
|
||||
|
||||
;; export interface PenpotExport {
|
||||
;; type: 'png' | 'jpeg' | 'svg' | 'pdf';
|
||||
;; scale: number;
|
||||
;; suffix: string;
|
||||
;; }
|
||||
(defn format-export
|
||||
[{:keys [type scale suffix] :as export}]
|
||||
(when (some? export)
|
||||
(obj/clear-empty
|
||||
#js {:type (format-key type)
|
||||
:scale scale
|
||||
:suffix suffix})))
|
||||
|
||||
(defn format-exports
|
||||
[exports]
|
||||
(when (some? exports)
|
||||
(format-array format-export exports)))
|
||||
|
||||
;; export interface PenpotFrameGuideColumnParams {
|
||||
;; color: { color: string; opacity: number };
|
||||
;; type?: 'stretch' | 'left' | 'center' | 'right';
|
||||
;; size?: number;
|
||||
;; margin?: number;
|
||||
;; itemLength?: number;
|
||||
;; gutter?: number;
|
||||
;; }
|
||||
(defn format-frame-guide-column-params
|
||||
[{:keys [color type size margin item-length gutter] :as params}]
|
||||
(when (some? params)
|
||||
(obj/clear-empty
|
||||
#js {:color (format-color color)
|
||||
:type (format-key type)
|
||||
:size size
|
||||
:margin margin
|
||||
:itemLength item-length
|
||||
:gutter gutter})))
|
||||
|
||||
;; export interface PenpotFrameGuideColumn {
|
||||
;; type: 'column';
|
||||
;; display: boolean;
|
||||
;; params: PenpotFrameGuideColumnParams;
|
||||
;; }
|
||||
(defn format-frame-guide-column
|
||||
[{:keys [type display params] :as guide}]
|
||||
(when (some? guide)
|
||||
(obj/clear-empty
|
||||
#js {:type (format-key type)
|
||||
:display display
|
||||
:params (format-frame-guide-column-params params)})))
|
||||
|
||||
;; export interface PenpotFrameGuideRow {
|
||||
;; type: 'row';
|
||||
;; display: boolean;
|
||||
;; params: PenpotFrameGuideColumnParams;
|
||||
;; }
|
||||
(defn format-frame-guide-row
|
||||
[{:keys [type display params] :as guide}]
|
||||
(when (some? guide)
|
||||
(obj/clear-empty
|
||||
#js {:type (format-key type)
|
||||
:display display
|
||||
:params (format-frame-guide-column-params params)})))
|
||||
|
||||
;;export interface PenpotFrameGuideSquareParams {
|
||||
;; color: { color: string; opacity: number };
|
||||
;; size?: number;
|
||||
;;}
|
||||
(defn format-frame-guide-square-params
|
||||
[{:keys [color size] :as params}]
|
||||
(when (some? params)
|
||||
(obj/clear-empty
|
||||
#js {:color (format-color color)
|
||||
:size size})))
|
||||
|
||||
;; export interface PenpotFrameGuideSquare {
|
||||
;; type: 'square';
|
||||
;; display: boolean;
|
||||
;; params: PenpotFrameGuideSquareParams;
|
||||
;; }
|
||||
|
||||
(defn format-frame-guide-square
|
||||
[{:keys [type display params] :as guide}]
|
||||
(when (some? guide)
|
||||
(obj/clear-empty
|
||||
#js {:type (format-key type)
|
||||
:display display
|
||||
:params (format-frame-guide-column-params params)})))
|
||||
|
||||
(defn format-frame-guide
|
||||
[{:keys [type] :as guide}]
|
||||
(when (some? guide)
|
||||
(case type
|
||||
:column (format-frame-guide-column guide)
|
||||
:row (format-frame-guide-row guide)
|
||||
:square (format-frame-guide-square guide))))
|
||||
|
||||
(defn format-frame-guides
|
||||
[guides]
|
||||
(when (some? guides)
|
||||
(format-array format-frame-guide guides)))
|
||||
|
||||
;;interface PenpotPathCommand {
|
||||
;; command:
|
||||
;; | 'M' | 'move-to'
|
||||
;; | 'Z' | 'close-path'
|
||||
;; | 'L' | 'line-to'
|
||||
;; | 'H' | 'line-to-horizontal'
|
||||
;; | 'V' | 'line-to-vertical'
|
||||
;; | 'C' | 'curve-to'
|
||||
;; | 'S' | 'smooth-curve-to'
|
||||
;; | 'Q' | 'quadratic-bezier-curve-to'
|
||||
;; | 'T' | 'smooth-quadratic-bezier-curve-to'
|
||||
;; | 'A' | 'elliptical-arc';
|
||||
;;
|
||||
;; params?: {
|
||||
;; x?: number;
|
||||
;; y?: number;
|
||||
;; c1x: number;
|
||||
;; c1y: number;
|
||||
;; c2x: number;
|
||||
;; c2y: number;
|
||||
;; rx?: number;
|
||||
;; ry?: number;
|
||||
;; xAxisRotation?: number;
|
||||
;; largeArcFlag?: boolean;
|
||||
;; sweepFlag?: boolean;
|
||||
;; };
|
||||
;;}
|
||||
(defn format-command-params
|
||||
[{:keys [x y c1x c1y c2x c2y rx ry x-axis-rotation large-arc-flag sweep-flag] :as props}]
|
||||
(when (some? props)
|
||||
(obj/clear-empty
|
||||
#js {:x x
|
||||
:y y
|
||||
:c1x c1x
|
||||
:c1y c1y
|
||||
:c2x c2x
|
||||
:c2y c2y
|
||||
:rx rx
|
||||
:ry ry
|
||||
:xAxisRotation x-axis-rotation
|
||||
:largeArcFlag large-arc-flag
|
||||
:sweepFlag sweep-flag})))
|
||||
|
||||
(defn format-command
|
||||
[{:keys [command params] :as props}]
|
||||
(when (some? props)
|
||||
(obj/clear-empty
|
||||
#js {:command (format-key command)
|
||||
:params (format-command-params params)})))
|
||||
|
||||
(defn format-path-content
|
||||
[content]
|
||||
(when (some? content)
|
||||
(format-array format-command content)))
|
||||
|
||||
;; export type PenpotTrackType = 'flex' | 'fixed' | 'percent' | 'auto';
|
||||
;;
|
||||
;; export interface PenpotTrack {
|
||||
;; type: PenpotTrackType;
|
||||
;; value: number | null;
|
||||
;; }
|
||||
(defn format-track
|
||||
[{:keys [type value] :as track}]
|
||||
(when (some? track)
|
||||
(obj/clear-empty
|
||||
#js {:type (-> type format-key)
|
||||
:value value})))
|
||||
|
||||
(defn format-tracks
|
||||
[tracks]
|
||||
(when (some? tracks)
|
||||
(format-array format-track tracks)))
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
[app.main.data.workspace.transforms :as dwt]
|
||||
[app.main.store :as st]
|
||||
[app.plugins.format :as format]
|
||||
[app.plugins.utils :as u]
|
||||
[app.util.object :as obj]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
@ -20,12 +21,6 @@
|
|||
;; Define in `app.plugins.shape` we do this way to prevent circular dependency
|
||||
(def shape-proxy? nil)
|
||||
|
||||
(defn- make-tracks
|
||||
[tracks]
|
||||
(.freeze
|
||||
js/Object
|
||||
(apply array (->> tracks (map u/to-js)))))
|
||||
|
||||
(deftype GridLayout [$plugin $file $page $id]
|
||||
Object
|
||||
|
||||
|
@ -190,10 +185,10 @@
|
|||
(st/emit! (dwsl/update-layout #{id} {:layout-grid-dir value}))))))}
|
||||
|
||||
{:name "rows"
|
||||
:get #(-> % u/proxy->shape :layout-grid-rows make-tracks)}
|
||||
:get #(-> % u/proxy->shape :layout-grid-rows format/format-tracks)}
|
||||
|
||||
{:name "columns"
|
||||
:get #(-> % u/proxy->shape :layout-grid-columns make-tracks)}
|
||||
:get #(-> % u/proxy->shape :layout-grid-columns format/format-tracks)}
|
||||
|
||||
{:name "alignItems"
|
||||
:get #(-> % u/proxy->shape :layout-align-items d/name)
|
||||
|
|
|
@ -22,7 +22,10 @@
|
|||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.repo :as rp]
|
||||
[app.main.store :as st]
|
||||
[app.plugins.format :as format]
|
||||
[app.plugins.parser :as parser]
|
||||
[app.plugins.shape :as shape]
|
||||
[app.plugins.text :as text]
|
||||
[app.plugins.utils :as u]
|
||||
[app.util.object :as obj]
|
||||
[beicon.v2.core :as rx]
|
||||
|
@ -49,27 +52,25 @@
|
|||
|
||||
(asFill [_]
|
||||
(let [color (u/locate-library-color $file $id)]
|
||||
(u/to-js
|
||||
(d/without-nils
|
||||
{:fill-color (:color color)
|
||||
:fill-opacity (:opacity color)
|
||||
:fill-color-gradient (:gradient color)
|
||||
:fill-color-ref-file $file
|
||||
:fill-color-ref-id $id
|
||||
:fill-image (:image color)}))))
|
||||
(format/format-fill
|
||||
{:fill-color (:color color)
|
||||
:fill-opacity (:opacity color)
|
||||
:fill-color-gradient (:gradient color)
|
||||
:fill-color-ref-file $file
|
||||
:fill-color-ref-id $id
|
||||
:fill-image (:image color)})))
|
||||
|
||||
(asStroke [_]
|
||||
(let [color (u/locate-library-color $file $id)]
|
||||
(u/to-js
|
||||
(d/without-nils
|
||||
{:stroke-color (:color color)
|
||||
:stroke-opacity (:opacity color)
|
||||
:stroke-color-gradient (:gradient color)
|
||||
:stroke-color-ref-file $file
|
||||
:stroke-color-ref-id $id
|
||||
:stroke-image (:image color)
|
||||
:stroke-style :solid
|
||||
:stroke-alignment :inner}))))
|
||||
(format/format-stroke
|
||||
{:stroke-color (:color color)
|
||||
:stroke-opacity (:opacity color)
|
||||
:stroke-color-gradient (:gradient color)
|
||||
:stroke-color-ref-file $file
|
||||
:stroke-color-ref-id $id
|
||||
:stroke-image (:image color)
|
||||
:stroke-style :solid
|
||||
:stroke-alignment :inner})))
|
||||
|
||||
(getPluginData
|
||||
[self key]
|
||||
|
@ -211,10 +212,10 @@
|
|||
(st/emit! (dwl/update-color color file-id)))))}
|
||||
|
||||
{:name "gradient"
|
||||
:get #(-> % u/proxy->library-color :gradient u/to-js)
|
||||
:get #(-> % u/proxy->library-color :gradient format/format-gradient)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [value (u/from-js value)]
|
||||
(let [value (parser/parse-gradient value)]
|
||||
(cond
|
||||
(not (sm/validate ::ctc/gradient value))
|
||||
(u/display-not-valid :library-color-gradient value)
|
||||
|
@ -225,10 +226,10 @@
|
|||
(st/emit! (dwl/update-color color file-id))))))}
|
||||
|
||||
{:name "image"
|
||||
:get #(-> % u/proxy->library-color :image u/to-js)
|
||||
:get #(-> % u/proxy->library-color :image format/format-image)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [value (u/from-js value)]
|
||||
(let [value (parser/parse-image-data value)]
|
||||
(cond
|
||||
(not (sm/validate ::ctc/image-color value))
|
||||
(u/display-not-valid :library-color-image value)
|
||||
|
@ -266,7 +267,7 @@
|
|||
(applyToTextRange
|
||||
[self range]
|
||||
(cond
|
||||
(not (shape/text-range? range))
|
||||
(not (text/text-range? range))
|
||||
(u/display-not-valid :applyToText range)
|
||||
|
||||
:else
|
||||
|
|
|
@ -13,9 +13,11 @@
|
|||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.store :as st]
|
||||
[app.plugins.parser :as parser]
|
||||
[app.plugins.shape :as shape]
|
||||
[app.plugins.utils :as u]
|
||||
[app.util.object :as obj]))
|
||||
[app.util.object :as obj]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(deftype PageProxy [$plugin $file $id]
|
||||
Object
|
||||
|
@ -34,11 +36,28 @@
|
|||
(shape/shape-proxy $plugin $file $id uuid/zero))
|
||||
|
||||
(findShapes
|
||||
[_]
|
||||
[_ criteria]
|
||||
;; Returns a lazy (iterable) of all available shapes
|
||||
(when (and (some? $file) (some? $id))
|
||||
(let [page (u/locate-page $file $id)]
|
||||
(apply array (sequence (map (partial shape/shape-proxy $plugin)) (keys (:objects page)))))))
|
||||
(let [criteria (parser/parse-criteria criteria)
|
||||
match-criteria?
|
||||
(if (some? criteria)
|
||||
(fn [[_ shape]]
|
||||
(and
|
||||
(or (not (:name criteria))
|
||||
(= (str/lower (:name criteria)) (str/lower (:name shape))))
|
||||
|
||||
(or (not (:name-like criteria))
|
||||
(str/includes? (str/lower (:name shape)) (str/lower (:name-like criteria))))
|
||||
|
||||
(or (not (:type criteria))
|
||||
(= (:type criteria) (:type shape)))))
|
||||
identity)]
|
||||
(when (and (some? $file) (some? $id))
|
||||
(let [page (u/locate-page $file $id)
|
||||
xf (comp
|
||||
(filter match-criteria?)
|
||||
(map #(shape/shape-proxy $plugin $file $id (first %))))]
|
||||
(apply array (sequence xf (:objects page)))))))
|
||||
|
||||
;; Plugin data
|
||||
(getPluginData
|
||||
|
|
|
@ -21,7 +21,29 @@
|
|||
|
||||
(defn parse-hex
|
||||
[color]
|
||||
(when color (-> color str/lower)))
|
||||
(if (string? color) (-> color str/lower) color))
|
||||
|
||||
;; {
|
||||
;; name?: string;
|
||||
;; nameLike?: string;
|
||||
;; type?:
|
||||
;; | 'frame'
|
||||
;; | 'group'
|
||||
;; | 'bool'
|
||||
;; | 'rect'
|
||||
;; | 'path'
|
||||
;; | 'text'
|
||||
;; | 'circle'
|
||||
;; | 'svg-raw'
|
||||
;; | 'image';
|
||||
;; }
|
||||
(defn parse-criteria
|
||||
[^js criteria]
|
||||
(when (some? criteria)
|
||||
(d/without-nils
|
||||
{:name (obj/get criteria "name")
|
||||
:name-like (obj/get criteria "nameLike")
|
||||
:type (-> (obj/get criteria "type") parse-keyword)})))
|
||||
|
||||
;;export type PenpotImageData = {
|
||||
;; name?: string;
|
||||
|
@ -33,7 +55,7 @@
|
|||
;;}
|
||||
(defn parse-image-data
|
||||
[^js image-data]
|
||||
(when image-data
|
||||
(when (some? image-data)
|
||||
(d/without-nils
|
||||
{:id (-> (obj/get image-data "id") parse-id)
|
||||
:name (obj/get image-data "name")
|
||||
|
@ -53,7 +75,7 @@
|
|||
;; }
|
||||
(defn parse-gradient-stop
|
||||
[^js stop]
|
||||
(when stop
|
||||
(when (some? stop)
|
||||
(d/without-nils
|
||||
{:color (-> (obj/get stop "color") parse-hex)
|
||||
:opacity (obj/get stop "opacity")
|
||||
|
@ -61,7 +83,7 @@
|
|||
|
||||
(defn parse-gradient
|
||||
[^js gradient]
|
||||
(when gradient
|
||||
(when (some? gradient)
|
||||
(d/without-nils
|
||||
{:type (-> (obj/get gradient "type") parse-keyword)
|
||||
:start-x (obj/get gradient "startX")
|
||||
|
@ -85,7 +107,7 @@
|
|||
;; }
|
||||
(defn parse-color
|
||||
[^js color]
|
||||
(when color
|
||||
(when (some? color)
|
||||
(d/without-nils
|
||||
{:id (-> (obj/get color "id") parse-id)
|
||||
:name (obj/get color "name")
|
||||
|
@ -96,3 +118,279 @@
|
|||
:ref-file (-> (obj/get color "refFile") parse-id)
|
||||
:gradient (-> (obj/get color "gradient") parse-gradient)
|
||||
:image (-> (obj/get color "image") parse-image-data)})))
|
||||
|
||||
;; export interface PenpotShadow {
|
||||
;; id?: string;
|
||||
;; style?: 'drop-shadow' | 'inner-shadow';
|
||||
;; offsetX?: number;
|
||||
;; offsetY?: number;
|
||||
;; blur?: number;
|
||||
;; spread?: number;
|
||||
;; hidden?: boolean;
|
||||
;; color?: PenpotColor;
|
||||
;; }
|
||||
(defn parse-shadow
|
||||
[^js shadow]
|
||||
(when (some? shadow)
|
||||
(d/without-nils
|
||||
{:id (-> (obj/get shadow "id") parse-id)
|
||||
:style (-> (obj/get shadow "style") parse-keyword)
|
||||
:offset-x (obj/get shadow "offsetX")
|
||||
:offset-y (obj/get shadow "offsetY")
|
||||
:blur (obj/get shadow "blur")
|
||||
:spread (obj/get shadow "spread")
|
||||
:hidden (obj/get shadow "hidden")
|
||||
:color (-> (obj/get shadow "color") parse-color)})))
|
||||
|
||||
(defn parse-shadows
|
||||
[^js shadows]
|
||||
(when (some? shadows)
|
||||
(into [] (map parse-shadow) shadows)))
|
||||
|
||||
;;export interface PenpotFill {
|
||||
;; fillColor?: string;
|
||||
;; fillOpacity?: number;
|
||||
;; fillColorGradient?: PenpotGradient;
|
||||
;; fillColorRefFile?: string;
|
||||
;; fillColorRefId?: string;
|
||||
;; fillImage?: PenpotImageData;
|
||||
;;}
|
||||
(defn parse-fill
|
||||
[^js fill]
|
||||
(when (some? fill)
|
||||
(d/without-nils
|
||||
{:fill-color (-> (obj/get fill "fillColor") parse-hex)
|
||||
:fill-opacity (obj/get fill "fillOpacity")
|
||||
:fill-color-gradient (-> (obj/get fill "fillColorGradient") parse-gradient)
|
||||
:fill-color-ref-file (-> (obj/get fill "fillColorRefFile") parse-id)
|
||||
:fill-color-ref-id (-> (obj/get fill "fillColorRefId") parse-id)
|
||||
:fill-image (-> (obj/get fill "fillImage") parse-image-data)})))
|
||||
|
||||
(defn parse-fills
|
||||
[^js fills]
|
||||
(when (some? fills)
|
||||
(into [] (map parse-fill) fills)))
|
||||
|
||||
;; export interface PenpotStroke {
|
||||
;; strokeColor?: string;
|
||||
;; strokeColorRefFile?: string;
|
||||
;; strokeColorRefId?: string;
|
||||
;; strokeOpacity?: number;
|
||||
;; strokeStyle?: 'solid' | 'dotted' | 'dashed' | 'mixed' | 'none' | 'svg';
|
||||
;; strokeWidth?: number;
|
||||
;; strokeAlignment?: 'center' | 'inner' | 'outer';
|
||||
;; strokeCapStart?: PenpotStrokeCap;
|
||||
;; strokeCapEnd?: PenpotStrokeCap;
|
||||
;; strokeColorGradient?: PenpotGradient;
|
||||
;; }
|
||||
(defn parse-stroke
|
||||
[^js stroke]
|
||||
(when (some? stroke)
|
||||
(d/without-nils
|
||||
{:stroke-color (-> (obj/get stroke "strokeColor") parse-hex)
|
||||
:stroke-color-ref-file (-> (obj/get stroke "strokeColorRefFile") parse-id)
|
||||
:stroke-color-ref-id (-> (obj/get stroke "strokeColorRefId") parse-id)
|
||||
:stroke-opacity (obj/get stroke "strokeOpacity")
|
||||
:stroke-style (-> (obj/get stroke "strokeStyle") parse-keyword)
|
||||
:stroke-width (obj/get stroke "strokeWidth")
|
||||
:stroke-alignment (-> (obj/get stroke "strokeAlignment") parse-keyword)
|
||||
:stroke-cap-start (-> (obj/get stroke "strokeCapStart") parse-keyword)
|
||||
:stroke-cap-end (-> (obj/get stroke "strokeCapEnd") parse-keyword)
|
||||
:stroke-color-gradient (-> (obj/get stroke "strokeColorGradient") parse-gradient)})))
|
||||
|
||||
(defn parse-strokes
|
||||
[^js strokes]
|
||||
(when (some? strokes)
|
||||
(into [] (map parse-stroke) strokes)))
|
||||
|
||||
;; export interface PenpotBlur {
|
||||
;; id?: string;
|
||||
;; type?: 'layer-blur';
|
||||
;; value?: number;
|
||||
;; hidden?: boolean;
|
||||
;; }
|
||||
(defn parse-blur
|
||||
[^js blur]
|
||||
(when (some? blur)
|
||||
(d/without-nils
|
||||
{:id (-> (obj/get blur "id") parse-id)
|
||||
:type (-> (obj/get blur "type") parse-keyword)
|
||||
:value (obj/get blur "value")
|
||||
:hidden (obj/get blur "hidden")})))
|
||||
|
||||
|
||||
;; export interface PenpotExport {
|
||||
;; type: 'png' | 'jpeg' | 'svg' | 'pdf';
|
||||
;; scale: number;
|
||||
;; suffix: string;
|
||||
;; }
|
||||
(defn parse-export
|
||||
[^js export]
|
||||
(when (some? export)
|
||||
(d/without-nils
|
||||
{:type (-> (obj/get export "type") parse-keyword)
|
||||
:scale (obj/get export "scale")
|
||||
:suffix (obj/get export "suffix")})))
|
||||
|
||||
(defn parse-exports
|
||||
[^js exports]
|
||||
(when (some? exports)
|
||||
(into [] (map parse-export) exports)))
|
||||
|
||||
;; export interface PenpotFrameGuideColumnParams {
|
||||
;; color: { color: string; opacity: number };
|
||||
;; type?: 'stretch' | 'left' | 'center' | 'right';
|
||||
;; size?: number;
|
||||
;; margin?: number;
|
||||
;; itemLength?: number;
|
||||
;; gutter?: number;
|
||||
;; }
|
||||
(defn parse-frame-guide-column-params
|
||||
[^js params]
|
||||
(when params
|
||||
(d/without-nils
|
||||
{:color (-> (obj/get params "color") parse-color)
|
||||
:type (-> (obj/get params "type") parse-keyword)
|
||||
:size (obj/get params "size")
|
||||
:margin (obj/get params "margin")
|
||||
:item-length (obj/get params "itemLength")
|
||||
:gutter (obj/get params "gutter")})))
|
||||
|
||||
;; export interface PenpotFrameGuideColumn {
|
||||
;; type: 'column';
|
||||
;; display: boolean;
|
||||
;; params: PenpotFrameGuideColumnParams;
|
||||
;; }
|
||||
(defn parse-frame-guide-column
|
||||
[^js guide]
|
||||
(when guide
|
||||
(d/without-nils
|
||||
{:type (-> (obj/get guide "type") parse-keyword)
|
||||
:display (obj/get guide "display")
|
||||
:params (-> (obj/get guide "params") parse-frame-guide-column-params)})))
|
||||
|
||||
;; export interface PenpotFrameGuideRow {
|
||||
;; type: 'row';
|
||||
;; display: boolean;
|
||||
;; params: PenpotFrameGuideColumnParams;
|
||||
;; }
|
||||
|
||||
(defn parse-frame-guide-row
|
||||
[^js guide]
|
||||
(when guide
|
||||
(d/without-nils
|
||||
{:type (-> (obj/get guide "type") parse-keyword)
|
||||
:display (obj/get guide "display")
|
||||
:params (-> (obj/get guide "params") parse-frame-guide-column-params)})))
|
||||
|
||||
;;export interface PenpotFrameGuideSquareParams {
|
||||
;; color: { color: string; opacity: number };
|
||||
;; size?: number;
|
||||
;;}
|
||||
(defn parse-frame-guide-square-params
|
||||
[^js params]
|
||||
(when (some? params)
|
||||
(d/without-nils
|
||||
{:color (-> (obj/get params "color") parse-color)
|
||||
:size (obj/get params "size")})))
|
||||
|
||||
;; export interface PenpotFrameGuideSquare {
|
||||
;; type: 'square';
|
||||
;; display: boolean;
|
||||
;; params: PenpotFrameGuideSquareParams;
|
||||
;; }
|
||||
(defn parse-frame-guide-square
|
||||
[^js guide]
|
||||
(when guide
|
||||
(d/without-nils
|
||||
{:type (-> (obj/get guide "type") parse-keyword)
|
||||
:display (obj/get guide "display")
|
||||
:params (-> (obj/get guide "params") parse-frame-guide-column-params)})))
|
||||
|
||||
(defn parse-frame-guide
|
||||
[^js guide]
|
||||
(when (some? guide)
|
||||
(case (obj/get guide "type")
|
||||
"column"
|
||||
parse-frame-guide-column
|
||||
|
||||
"row"
|
||||
parse-frame-guide-row
|
||||
|
||||
"square"
|
||||
(parse-frame-guide-square guide))))
|
||||
|
||||
(defn parse-frame-guides
|
||||
[^js guides]
|
||||
(when (some? guides)
|
||||
(into [] (map parse-frame-guide) guides)))
|
||||
|
||||
;;interface PenpotPathCommand {
|
||||
;; command:
|
||||
;; | 'M' | 'move-to'
|
||||
;; | 'Z' | 'close-path'
|
||||
;; | 'L' | 'line-to'
|
||||
;; | 'H' | 'line-to-horizontal'
|
||||
;; | 'V' | 'line-to-vertical'
|
||||
;; | 'C' | 'curve-to'
|
||||
;; | 'S' | 'smooth-curve-to'
|
||||
;; | 'Q' | 'quadratic-bezier-curve-to'
|
||||
;; | 'T' | 'smooth-quadratic-bezier-curve-to'
|
||||
;; | 'A' | 'elliptical-arc';
|
||||
;;
|
||||
;; params?: {
|
||||
;; x?: number;
|
||||
;; y?: number;
|
||||
;; c1x: number;
|
||||
;; c1y: number;
|
||||
;; c2x: number;
|
||||
;; c2y: number;
|
||||
;; rx?: number;
|
||||
;; ry?: number;
|
||||
;; xAxisRotation?: number;
|
||||
;; largeArcFlag?: boolean;
|
||||
;; sweepFlag?: boolean;
|
||||
;; };
|
||||
;;}
|
||||
(defn parse-command-type
|
||||
[^string command-type]
|
||||
(case command-type
|
||||
"M" :move-to
|
||||
"Z" :close-path
|
||||
"L" :line-to
|
||||
"H" :line-to-horizontal
|
||||
"V" :line-to-vertical
|
||||
"C" :curve-to
|
||||
"S" :smooth-curve-to
|
||||
"Q" :quadratic-bezier-curve-to
|
||||
"T" :smooth-quadratic-bezier-curve-to
|
||||
"A" :elliptical-arc
|
||||
(parse-keyword command-type)))
|
||||
|
||||
(defn parse-command-params
|
||||
[^js params]
|
||||
(when (some? params)
|
||||
(d/without-nils
|
||||
{:x (obj/get params "x")
|
||||
:y (obj/get params "y")
|
||||
:c1x (obj/get params "c1x")
|
||||
:c1y (obj/get params "c1y")
|
||||
:c2x (obj/get params "c2x")
|
||||
:c2y (obj/get params "c2y")
|
||||
:rx (obj/get params "rx")
|
||||
:ry (obj/get params "ry")
|
||||
:x-axis-rotation (obj/get params "xAxisRotation")
|
||||
:large-arc-flag (obj/get params "largeArcFlag")
|
||||
:sweep-flag (obj/get params "sweepFlag")})))
|
||||
|
||||
(defn parse-command
|
||||
[^js command]
|
||||
(when (some? command)
|
||||
(d/without-nils
|
||||
{:command (-> (obj/get command "command") parse-command-type)
|
||||
:params (-> (obj/get command "paras") parse-command-params)})))
|
||||
|
||||
(defn parse-path-content
|
||||
[^js content]
|
||||
(when (some? content)
|
||||
(into [] (map parse-command) content)))
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
(:require
|
||||
[app.common.geom.rect :as grc]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.plugins.format :as format]
|
||||
[app.plugins.shape :as shape]
|
||||
[app.plugins.utils :as u]))
|
||||
|
||||
|
@ -22,4 +23,4 @@
|
|||
(let [shapes (->> shapes (map u/proxy->shape))]
|
||||
(-> (gsh/shapes->rect shapes)
|
||||
(grc/rect->center)
|
||||
(u/to-js)))))
|
||||
(format/format-point)))))
|
||||
|
|
|
@ -38,243 +38,18 @@
|
|||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.store :as st]
|
||||
[app.plugins.flex :as flex]
|
||||
[app.plugins.format :as format]
|
||||
[app.plugins.grid :as grid]
|
||||
[app.plugins.parser :as parser]
|
||||
[app.plugins.text :as text]
|
||||
[app.plugins.utils :as u]
|
||||
[app.util.object :as obj]
|
||||
[app.util.path.format :as upf]
|
||||
[app.util.text-editor :as ted]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(def lib-typography-proxy? nil)
|
||||
(def lib-component-proxy nil)
|
||||
|
||||
(deftype TextRange [$plugin $file $page $id start end]
|
||||
Object
|
||||
(applyTypography [_ typography]
|
||||
(let [typography (u/proxy->library-typography typography)
|
||||
attrs (-> typography
|
||||
(assoc :typography-ref-file $file)
|
||||
(assoc :typography-ref-id (:id typography))
|
||||
(dissoc :id :name))]
|
||||
(st/emit! (dwt/update-text-range $id start end attrs)))))
|
||||
|
||||
(defn mixed-value
|
||||
[values]
|
||||
(let [s (set values)]
|
||||
(if (= (count s) 1) (first s) "mixed")))
|
||||
|
||||
(defn text-range?
|
||||
[range]
|
||||
(instance? TextRange range))
|
||||
|
||||
(defn text-range
|
||||
[plugin-id file-id page-id id start end]
|
||||
(-> (TextRange. plugin-id file-id page-id id start end)
|
||||
(crc/add-properties!
|
||||
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
|
||||
{:name "$id" :enumerable false :get (constantly id)}
|
||||
{:name "$file" :enumerable false :get (constantly file-id)}
|
||||
{:name "$page" :enumerable false :get (constantly page-id)}
|
||||
|
||||
{:name "shape"
|
||||
:get #(-> % u/proxy->shape)}
|
||||
|
||||
{:name "characters"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :text) (str/join "")))}
|
||||
|
||||
{:name "fontId"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-id) mixed-value))
|
||||
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontId value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-id value}))))}
|
||||
|
||||
{:name "fontFamily"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-family) mixed-value))
|
||||
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontFamily value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-family value}))))}
|
||||
|
||||
{:name "fontVariantId"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-variant-id) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontVariantId value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-variant-id value}))))}
|
||||
|
||||
{:name "fontSize"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-size) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontSize value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-size value}))))}
|
||||
|
||||
{:name "fontWeight"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-weight) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontWeight value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-weight value}))))}
|
||||
|
||||
{:name "fontStyle"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-style) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontStyle value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-style value}))))}
|
||||
|
||||
{:name "lineHeight"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :line-height) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :lineHeight value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:line-height value}))))}
|
||||
|
||||
{:name "letterSpacing"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :letter-spacing) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :letterSpacing value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:letter-spacing value}))))}
|
||||
|
||||
{:name "textTransform"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :text-transform) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :textTransform value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:text-transform value}))))}
|
||||
|
||||
{:name "textDecoration"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :text-decoration) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :textDecoration value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:text-decoration value}))))}
|
||||
|
||||
{:name "direction"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :direction) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :direction value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:direction value}))))}
|
||||
|
||||
{:name "align"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :text-align) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :text-align value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:text-align value}))))}
|
||||
|
||||
{:name "fills"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :fills) mixed-value u/array-to-js))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (mapv #(u/from-js %) value)]
|
||||
(cond
|
||||
(not (sm/validate [:vector ::cts/fill] value))
|
||||
(u/display-not-valid :fills value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:fills value})))))})))
|
||||
|
||||
(declare shape-proxy)
|
||||
|
||||
(defn parse-command
|
||||
[entry]
|
||||
(update entry
|
||||
:command
|
||||
#(case %
|
||||
"M" :move-to
|
||||
"Z" :close-path
|
||||
"L" :line-to
|
||||
"H" :line-to-horizontal
|
||||
"V" :line-to-vertical
|
||||
"C" :curve-to
|
||||
"S" :smooth-curve-to
|
||||
"Q" :quadratic-bezier-curve-to
|
||||
"T" :smooth-quadratic-bezier-curve-to
|
||||
"A" :elliptical-arc
|
||||
(keyword %))))
|
||||
|
||||
(defn text-props
|
||||
[shape]
|
||||
(d/merge
|
||||
|
@ -282,6 +57,48 @@
|
|||
(dwt/current-paragraph-values {:shape shape :attrs txt/paragraph-attrs})
|
||||
(dwt/current-text-values {:shape shape :attrs txt/text-node-attrs})))
|
||||
|
||||
(declare shape-proxy)
|
||||
(declare shape-proxy?)
|
||||
|
||||
#_(defn parse-command
|
||||
[entry]
|
||||
(update entry
|
||||
:command
|
||||
#(case %
|
||||
"M" :move-to
|
||||
"Z" :close-path
|
||||
"L" :line-to
|
||||
"H" :line-to-horizontal
|
||||
"V" :line-to-vertical
|
||||
"C" :curve-to
|
||||
"S" :smooth-curve-to
|
||||
"Q" :quadratic-bezier-curve-to
|
||||
"T" :smooth-quadratic-bezier-curve-to
|
||||
"A" :elliptical-arc
|
||||
(keyword %))))
|
||||
|
||||
(defn- shadow-defaults
|
||||
[shadow]
|
||||
(d/patch-object
|
||||
{:id (uuid/next)
|
||||
:style :drop-shadow
|
||||
:color {:color clr/black :opacity 0.2}
|
||||
:offset-x 4
|
||||
:offset-y 4
|
||||
:blur 4
|
||||
:spread 0
|
||||
:hidden false}
|
||||
shadow))
|
||||
|
||||
(defn- blur-defaults
|
||||
[blur]
|
||||
(d/patch-object
|
||||
{:id (uuid/next)
|
||||
:type :layer-blur
|
||||
:value 4
|
||||
:hidden false}
|
||||
blur))
|
||||
|
||||
(deftype ShapeProxy [$plugin $file $page $id]
|
||||
Object
|
||||
(resize
|
||||
|
@ -392,67 +209,107 @@
|
|||
(getChildren
|
||||
[_]
|
||||
(let [shape (u/locate-shape $file $page $id)]
|
||||
(if (or (cfh/frame-shape? shape) (cfh/group-shape? shape) (cfh/svg-raw-shape? shape) (cfh/bool-shape? shape))
|
||||
(apply array (->> (u/locate-shape $file $page $id)
|
||||
:shapes
|
||||
(map #(shape-proxy $plugin $file $page %))))
|
||||
(u/display-not-valid :getChildren (:type shape)))))
|
||||
(cond
|
||||
|
||||
(and (not (cfh/frame-shape? shape))
|
||||
(not (cfh/group-shape? shape))
|
||||
(not (cfh/svg-raw-shape? shape))
|
||||
(not (cfh/bool-shape? shape)))
|
||||
(u/display-not-valid :getChildren (:type shape))
|
||||
|
||||
:else
|
||||
(->> (u/locate-shape $file $page $id)
|
||||
(:shapes)
|
||||
(format/format-array #(shape-proxy $plugin $file $page %))))))
|
||||
|
||||
(appendChild
|
||||
[_ child]
|
||||
(let [shape (u/locate-shape $file $page $id)]
|
||||
(if (or (cfh/frame-shape? shape) (cfh/group-shape? shape) (cfh/svg-raw-shape? shape) (cfh/bool-shape? shape))
|
||||
(cond
|
||||
(and (not (cfh/frame-shape? shape))
|
||||
(not (cfh/group-shape? shape))
|
||||
(not (cfh/svg-raw-shape? shape))
|
||||
(not (cfh/bool-shape? shape)))
|
||||
(u/display-not-valid :appendChild (:type shape))
|
||||
|
||||
(not (shape-proxy? child))
|
||||
(u/display-not-valid :appendChild-child child)
|
||||
|
||||
:else
|
||||
(let [child-id (obj/get child "$id")]
|
||||
(st/emit! (dw/relocate-shapes #{child-id} $id 0)))
|
||||
(u/display-not-valid :appendChild (:type shape)))))
|
||||
(st/emit! (dw/relocate-shapes #{child-id} $id 0))))))
|
||||
|
||||
(insertChild
|
||||
[_ index child]
|
||||
(let [shape (u/locate-shape $file $page $id)]
|
||||
(if (or (cfh/frame-shape? shape) (cfh/group-shape? shape) (cfh/svg-raw-shape? shape) (cfh/bool-shape? shape))
|
||||
(cond
|
||||
(and (not (cfh/frame-shape? shape))
|
||||
(not (cfh/group-shape? shape))
|
||||
(not (cfh/svg-raw-shape? shape))
|
||||
(not (cfh/bool-shape? shape)))
|
||||
(u/display-not-valid :insertChild (:type shape))
|
||||
|
||||
(not (shape-proxy? child))
|
||||
(u/display-not-valid :insertChild-child child)
|
||||
|
||||
:else
|
||||
(let [child-id (obj/get child "$id")]
|
||||
(st/emit! (dw/relocate-shapes #{child-id} $id index)))
|
||||
(u/display-not-valid :insertChild (:type shape)))))
|
||||
(st/emit! (dw/relocate-shapes #{child-id} $id index))))))
|
||||
|
||||
;; Only for frames
|
||||
(addFlexLayout
|
||||
[_]
|
||||
(let [shape (u/locate-shape $file $page $id)]
|
||||
(if (cfh/frame-shape? shape)
|
||||
(cond
|
||||
(not (cfh/frame-shape? shape))
|
||||
(u/display-not-valid :addFlexLayout (:type shape))
|
||||
|
||||
:else
|
||||
(do (st/emit! (dwsl/create-layout-from-id $id :flex :from-frame? true :calculate-params? false))
|
||||
(grid/grid-layout-proxy $plugin $file $page $id))
|
||||
(u/display-not-valid :addFlexLayout (:type shape)))))
|
||||
(grid/grid-layout-proxy $plugin $file $page $id)))))
|
||||
|
||||
(addGridLayout
|
||||
[_]
|
||||
(let [shape (u/locate-shape $file $page $id)]
|
||||
(if (cfh/frame-shape? shape)
|
||||
(cond
|
||||
(not (cfh/frame-shape? shape))
|
||||
(u/display-not-valid :addGridLayout (:type shape))
|
||||
|
||||
:else
|
||||
(do (st/emit! (dwsl/create-layout-from-id $id :grid :from-frame? true :calculate-params? false))
|
||||
(grid/grid-layout-proxy $plugin $file $page $id))
|
||||
(u/display-not-valid :addGridLayout (:type shape)))))
|
||||
(grid/grid-layout-proxy $plugin $file $page $id)))))
|
||||
|
||||
;; Make masks for groups
|
||||
(makeMask
|
||||
[_]
|
||||
(let [shape (u/locate-shape $file $page $id)]
|
||||
(if (cfh/group-shape? shape)
|
||||
(st/emit! (dwg/mask-group #{$id}))
|
||||
(u/display-not-valid :makeMask (:type shape)))))
|
||||
(cond
|
||||
(not (cfh/group-shape? shape))
|
||||
(u/display-not-valid :makeMask (:type shape))
|
||||
|
||||
:else
|
||||
(st/emit! (dwg/mask-group #{$id})))))
|
||||
|
||||
(removeMask
|
||||
[_]
|
||||
(let [shape (u/locate-shape $file $page $id)]
|
||||
(if (cfh/mask-shape? shape)
|
||||
(st/emit! (dwg/unmask-group #{$id}))
|
||||
(u/display-not-valid :removeMask (:type shape)))))
|
||||
(cond
|
||||
(not (cfh/mask-shape? shape))
|
||||
(u/display-not-valid :removeMask (:type shape))
|
||||
|
||||
:else
|
||||
(st/emit! (dwg/unmask-group #{$id})))))
|
||||
|
||||
;; Only for path and bool shapes
|
||||
(toD
|
||||
[_]
|
||||
(let [shape (u/locate-shape $file $page $id)]
|
||||
(if (cfh/path-shape? shape)
|
||||
(upf/format-path (:content shape))
|
||||
(u/display-not-valid :makeMask (:type shape)))))
|
||||
(cond
|
||||
(not (cfh/path-shape? shape))
|
||||
(u/display-not-valid :makeMask (:type shape))
|
||||
|
||||
:else
|
||||
(upf/format-path (:content shape)))))
|
||||
|
||||
;; Text shapes
|
||||
(getRange
|
||||
|
@ -469,7 +326,7 @@
|
|||
(u/display-not-valid :getRange-end end)
|
||||
|
||||
:else
|
||||
(text-range $plugin $file $page $id start end))))
|
||||
(text/text-range $plugin $file $page $id start end))))
|
||||
|
||||
(applyTypography
|
||||
[_ typography]
|
||||
|
@ -554,11 +411,6 @@
|
|||
(let [[root component] (u/locate-component objects shape)]
|
||||
(lib-component-proxy $plugin (:component-file root) (:id component)))))))
|
||||
|
||||
(crc/define-properties!
|
||||
ShapeProxy
|
||||
{:name js/Symbol.toStringTag
|
||||
:get (fn [] (str "ShapeProxy"))})
|
||||
|
||||
(defn shape-proxy? [p]
|
||||
(instance? ShapeProxy p))
|
||||
|
||||
|
@ -566,6 +418,11 @@
|
|||
(do (set! flex/shape-proxy? shape-proxy?)
|
||||
(set! grid/shape-proxy? shape-proxy?))
|
||||
|
||||
(crc/define-properties!
|
||||
ShapeProxy
|
||||
{:name js/Symbol.toStringTag
|
||||
:get (fn [] (str "ShapeProxy"))})
|
||||
|
||||
(defn shape-proxy
|
||||
([plugin-id id]
|
||||
(shape-proxy plugin-id (:current-file-id @st/state) (:current-page-id @st/state) id))
|
||||
|
@ -782,23 +639,11 @@
|
|||
(st/emit! (dwsh/update-shapes [id] #(assoc % :blend-mode value))))))}
|
||||
|
||||
{:name "shadows"
|
||||
:get #(-> % u/proxy->shape :shadow u/array-to-js)
|
||||
:get #(-> % u/proxy->shape :shadow format/format-shadows)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")
|
||||
value (mapv (fn [val]
|
||||
;; Merge default shadow properties
|
||||
(d/patch-object
|
||||
{:id (uuid/next)
|
||||
:style :drop-shadow
|
||||
:color {:color clr/black :opacity 0.2}
|
||||
:offset-x 4
|
||||
:offset-y 4
|
||||
:blur 4
|
||||
:spread 0
|
||||
:hidden false}
|
||||
(u/from-js val #{:style :type})))
|
||||
value)]
|
||||
value (mapv #(shadow-defaults (parser/parse-shadow %)) value)]
|
||||
(cond
|
||||
(not (sm/validate [:vector ::ctss/shadow] value))
|
||||
(u/display-not-valid :shadows value)
|
||||
|
@ -807,19 +652,13 @@
|
|||
(st/emit! (dwsh/update-shapes [id] #(assoc % :shadow value))))))}
|
||||
|
||||
{:name "blur"
|
||||
:get #(-> % u/proxy->shape :blur u/to-js)
|
||||
:get #(-> % u/proxy->shape :blur format/format-blur)
|
||||
:set
|
||||
(fn [self value]
|
||||
(if (nil? value)
|
||||
(st/emit! (dwsh/update-shapes [id] #(dissoc % :blur)))
|
||||
(let [id (obj/get self "$id")
|
||||
value
|
||||
(d/patch-object
|
||||
{:id (uuid/next)
|
||||
:type :layer-blur
|
||||
:value 4
|
||||
:hidden false}
|
||||
(u/from-js value))]
|
||||
value (blur-defaults (parser/parse-blur value))]
|
||||
(cond
|
||||
(not (sm/validate ::ctsb/blur value))
|
||||
(u/display-not-valid :blur value)
|
||||
|
@ -828,11 +667,11 @@
|
|||
(st/emit! (dwsh/update-shapes [id] #(assoc % :blur value)))))))}
|
||||
|
||||
{:name "exports"
|
||||
:get #(-> % u/proxy->shape :exports u/array-to-js)
|
||||
:get #(-> % u/proxy->shape :exports format/format-exports)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")
|
||||
value (mapv #(u/from-js %) value)]
|
||||
value (parser/parse-exports value)]
|
||||
(cond
|
||||
(not (sm/validate [:vector ::ctse/export] value))
|
||||
(u/display-not-valid :exports value)
|
||||
|
@ -989,13 +828,13 @@
|
|||
;; Strokes and fills
|
||||
{:name "fills"
|
||||
:get #(if (cfh/text-shape? data)
|
||||
(-> % u/proxy->shape text-props :fills u/array-to-js)
|
||||
(-> % u/proxy->shape :fills u/array-to-js))
|
||||
(-> % u/proxy->shape text-props :fills format/format-fills)
|
||||
(-> % u/proxy->shape :fills format/format-fills))
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [shape (u/proxy->shape self)
|
||||
id (:id shape)
|
||||
value (mapv #(u/from-js %) value)]
|
||||
value (parser/parse-fills value)]
|
||||
(cond
|
||||
(not (sm/validate [:vector ::cts/fill] value))
|
||||
(u/display-not-valid :fills value)
|
||||
|
@ -1007,11 +846,11 @@
|
|||
(st/emit! (dwsh/update-shapes [id] #(assoc % :fills value))))))}
|
||||
|
||||
{:name "strokes"
|
||||
:get #(-> % u/proxy->shape :strokes u/array-to-js)
|
||||
:get #(-> % u/proxy->shape :strokes format/format-strokes)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")
|
||||
value (mapv #(u/from-js % #{:stroke-style :stroke-alignment}) value)]
|
||||
value (parser/parse-strokes value)]
|
||||
(cond
|
||||
(not (sm/validate [:vector ::cts/stroke] value))
|
||||
(u/display-not-valid :strokes value)
|
||||
|
@ -1068,10 +907,10 @@
|
|||
(flex/flex-layout-proxy plugin-id file-id page-id id))))}
|
||||
|
||||
{:name "guides"
|
||||
:get #(-> % u/proxy->shape :grids u/array-to-js)
|
||||
:get #(-> % u/proxy->shape :grids format/format-frame-guides)
|
||||
:set (fn [self value]
|
||||
(let [id (obj/get self "$id")
|
||||
value (mapv #(u/from-js %) value)]
|
||||
value (parser/parse-frame-guides value)]
|
||||
(cond
|
||||
(not (sm/validate [:vector ::ctg/grid] value))
|
||||
(u/display-not-valid :guides value)
|
||||
|
@ -1105,213 +944,21 @@
|
|||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-item-v-sizing value})))))})))
|
||||
|
||||
(cond-> (cfh/text-shape? data)
|
||||
(crc/add-properties!
|
||||
{:name "characters"
|
||||
:get #(-> % u/proxy->shape :content txt/content->text)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
;; The user is currently editing the text. We need to update the
|
||||
;; editor as well
|
||||
(cond
|
||||
(or (not (string? value)) (empty? value))
|
||||
(u/display-not-valid :characters value)
|
||||
|
||||
(contains? (:workspace-editor-state @st/state) id)
|
||||
(let [shape (u/proxy->shape self)
|
||||
editor
|
||||
(-> shape
|
||||
(txt/change-text value)
|
||||
:content
|
||||
ted/import-content
|
||||
ted/create-editor-state)]
|
||||
(st/emit! (dwt/update-editor-state shape editor)))
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(txt/change-text % value))))))}
|
||||
|
||||
{:name "growType"
|
||||
:get #(-> % u/proxy->shape :grow-type d/name)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")
|
||||
value (keyword value)]
|
||||
(cond
|
||||
(not (contains? #{:auto-width :auto-height :fixed} value))
|
||||
(u/display-not-valid :growType value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :grow-type value))))))}
|
||||
|
||||
{:name "fontId"
|
||||
:get #(-> % u/proxy->shape text-props :font-id)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontId value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-id value})))))}
|
||||
|
||||
{:name "fontFamily"
|
||||
:get #(-> % u/proxy->shape text-props :font-family)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontFamily value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-family value})))))}
|
||||
|
||||
{:name "fontVariantId"
|
||||
:get #(-> % u/proxy->shape text-props :font-variant-id)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontVariantId value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-variant-id value})))))}
|
||||
|
||||
{:name "fontSize"
|
||||
:get #(-> % u/proxy->shape text-props :font-size)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontSize value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-size value})))))}
|
||||
|
||||
{:name "fontWeight"
|
||||
:get #(-> % u/proxy->shape text-props :font-weight)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontWeight value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-weight value})))))}
|
||||
|
||||
{:name "fontStyle"
|
||||
:get #(-> % u/proxy->shape text-props :font-style)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontStyle value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-style value})))))}
|
||||
|
||||
{:name "lineHeight"
|
||||
:get #(-> % u/proxy->shape text-props :line-height)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :lineHeight value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:line-height value})))))}
|
||||
|
||||
{:name "letterSpacing"
|
||||
:get #(-> % u/proxy->shape text-props :letter-spacing)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :letterSpacing value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:letter-spacing value})))))}
|
||||
|
||||
{:name "textTransform"
|
||||
:get #(-> % u/proxy->shape text-props :text-transform)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :textTransform value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:text-transform value})))))}
|
||||
|
||||
{:name "textDecoration"
|
||||
:get #(-> % u/proxy->shape text-props :text-decoration)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :textDecoration value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:text-decoration value})))))}
|
||||
|
||||
{:name "direction"
|
||||
:get #(-> % u/proxy->shape text-props :text-direction)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :textDecoration value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:text-decoration value})))))}
|
||||
|
||||
{:name "align"
|
||||
:get #(-> % u/proxy->shape text-props :text-align)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :align value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:text-align value})))))}
|
||||
|
||||
{:name "verticalAlign"
|
||||
:get #(-> % u/proxy->shape text-props :vertical-align)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :verticalAlign value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:vertical-align value})))))}))
|
||||
(cond-> (cfh/text-shape? data) (text/add-text-props))
|
||||
|
||||
(cond-> (or (cfh/path-shape? data) (cfh/bool-shape? data))
|
||||
(crc/add-properties!
|
||||
{:name "content"
|
||||
:get #(-> % u/proxy->shape :content u/array-to-js)
|
||||
:get #(-> % u/proxy->shape :content format/format-path-content)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [content
|
||||
(->> value
|
||||
(map u/from-js)
|
||||
(mapv parse-command)
|
||||
(->> (parser/parse-path-content value)
|
||||
(spp/simplify-commands))]
|
||||
(cond
|
||||
(not (cfh/path-shape? data))
|
||||
(u/display-not-valid :content-type type)
|
||||
|
||||
(not (sm/validate ::ctsp/content content))
|
||||
(u/display-not-valid :content value)
|
||||
|
||||
|
|
434
frontend/src/app/plugins/text.cljs
Normal file
434
frontend/src/app/plugins/text.cljs
Normal file
|
@ -0,0 +1,434 @@
|
|||
;; 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
|
||||
|
||||
(ns app.plugins.text
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.record :as crc]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.text :as txt]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.store :as st]
|
||||
[app.plugins.format :as format]
|
||||
[app.plugins.parser :as parser]
|
||||
[app.plugins.utils :as u]
|
||||
[app.util.object :as obj]
|
||||
[app.util.text-editor :as ted]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(defn mixed-value
|
||||
[values]
|
||||
(let [s (set values)]
|
||||
(if (= (count s) 1) (first s) "mixed")))
|
||||
|
||||
(deftype TextRange [$plugin $file $page $id start end]
|
||||
Object
|
||||
(applyTypography [_ typography]
|
||||
(let [typography (u/proxy->library-typography typography)
|
||||
attrs (-> typography
|
||||
(assoc :typography-ref-file $file)
|
||||
(assoc :typography-ref-id (:id typography))
|
||||
(dissoc :id :name))]
|
||||
(st/emit! (dwt/update-text-range $id start end attrs)))))
|
||||
|
||||
(defn text-range?
|
||||
[range]
|
||||
(instance? TextRange range))
|
||||
|
||||
(defn text-props
|
||||
[shape]
|
||||
(d/merge
|
||||
(dwt/current-root-values {:shape shape :attrs txt/root-attrs})
|
||||
(dwt/current-paragraph-values {:shape shape :attrs txt/paragraph-attrs})
|
||||
(dwt/current-text-values {:shape shape :attrs txt/text-node-attrs})))
|
||||
|
||||
(defn text-range
|
||||
[plugin-id file-id page-id id start end]
|
||||
(-> (TextRange. plugin-id file-id page-id id start end)
|
||||
(crc/add-properties!
|
||||
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
|
||||
{:name "$id" :enumerable false :get (constantly id)}
|
||||
{:name "$file" :enumerable false :get (constantly file-id)}
|
||||
{:name "$page" :enumerable false :get (constantly page-id)}
|
||||
|
||||
{:name "shape"
|
||||
:get #(-> % u/proxy->shape)}
|
||||
|
||||
{:name "characters"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :text) (str/join "")))}
|
||||
|
||||
{:name "fontId"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-id) mixed-value))
|
||||
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontId value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-id value}))))}
|
||||
|
||||
{:name "fontFamily"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-family) mixed-value))
|
||||
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontFamily value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-family value}))))}
|
||||
|
||||
{:name "fontVariantId"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-variant-id) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontVariantId value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-variant-id value}))))}
|
||||
|
||||
{:name "fontSize"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-size) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontSize value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-size value}))))}
|
||||
|
||||
{:name "fontWeight"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-weight) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontWeight value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-weight value}))))}
|
||||
|
||||
{:name "fontStyle"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-style) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontStyle value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-style value}))))}
|
||||
|
||||
{:name "lineHeight"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :line-height) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :lineHeight value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:line-height value}))))}
|
||||
|
||||
{:name "letterSpacing"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :letter-spacing) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :letterSpacing value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:letter-spacing value}))))}
|
||||
|
||||
{:name "textTransform"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :text-transform) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :textTransform value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:text-transform value}))))}
|
||||
|
||||
{:name "textDecoration"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :text-decoration) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :textDecoration value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:text-decoration value}))))}
|
||||
|
||||
{:name "direction"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :direction) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :direction value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:direction value}))))}
|
||||
|
||||
{:name "align"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :text-align) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :text-align value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:text-align value}))))}
|
||||
|
||||
{:name "fills"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :fills) mixed-value format/format-fills))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (parser/parse-fills value)]
|
||||
(cond
|
||||
(not (sm/validate [:vector ::cts/fill] value))
|
||||
(u/display-not-valid :fills value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:fills value})))))})))
|
||||
|
||||
(defn add-text-props
|
||||
[shape-proxy]
|
||||
(crc/add-properties!
|
||||
shape-proxy
|
||||
{:name "characters"
|
||||
:get #(-> % u/proxy->shape :content txt/content->text)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
;; The user is currently editing the text. We need to update the
|
||||
;; editor as well
|
||||
(cond
|
||||
(or (not (string? value)) (empty? value))
|
||||
(u/display-not-valid :characters value)
|
||||
|
||||
(contains? (:workspace-editor-state @st/state) id)
|
||||
(let [shape (u/proxy->shape self)
|
||||
editor
|
||||
(-> shape
|
||||
(txt/change-text value)
|
||||
:content
|
||||
ted/import-content
|
||||
ted/create-editor-state)]
|
||||
(st/emit! (dwt/update-editor-state shape editor)))
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(txt/change-text % value))))))}
|
||||
|
||||
{:name "growType"
|
||||
:get #(-> % u/proxy->shape :grow-type d/name)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")
|
||||
value (keyword value)]
|
||||
(cond
|
||||
(not (contains? #{:auto-width :auto-height :fixed} value))
|
||||
(u/display-not-valid :growType value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :grow-type value))))))}
|
||||
|
||||
{:name "fontId"
|
||||
:get #(-> % u/proxy->shape text-props :font-id)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontId value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-id value})))))}
|
||||
|
||||
{:name "fontFamily"
|
||||
:get #(-> % u/proxy->shape text-props :font-family)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontFamily value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-family value})))))}
|
||||
|
||||
{:name "fontVariantId"
|
||||
:get #(-> % u/proxy->shape text-props :font-variant-id)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontVariantId value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-variant-id value})))))}
|
||||
|
||||
{:name "fontSize"
|
||||
:get #(-> % u/proxy->shape text-props :font-size)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontSize value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-size value})))))}
|
||||
|
||||
{:name "fontWeight"
|
||||
:get #(-> % u/proxy->shape text-props :font-weight)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontWeight value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-weight value})))))}
|
||||
|
||||
{:name "fontStyle"
|
||||
:get #(-> % u/proxy->shape text-props :font-style)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontStyle value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-style value})))))}
|
||||
|
||||
{:name "lineHeight"
|
||||
:get #(-> % u/proxy->shape text-props :line-height)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :lineHeight value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:line-height value})))))}
|
||||
|
||||
{:name "letterSpacing"
|
||||
:get #(-> % u/proxy->shape text-props :letter-spacing)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :letterSpacing value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:letter-spacing value})))))}
|
||||
|
||||
{:name "textTransform"
|
||||
:get #(-> % u/proxy->shape text-props :text-transform)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :textTransform value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:text-transform value})))))}
|
||||
|
||||
{:name "textDecoration"
|
||||
:get #(-> % u/proxy->shape text-props :text-decoration)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :textDecoration value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:text-decoration value})))))}
|
||||
|
||||
{:name "direction"
|
||||
:get #(-> % u/proxy->shape text-props :text-direction)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :textDecoration value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:text-decoration value})))))}
|
||||
|
||||
{:name "align"
|
||||
:get #(-> % u/proxy->shape text-props :text-align)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :align value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:text-align value})))))}
|
||||
|
||||
{:name "verticalAlign"
|
||||
:get #(-> % u/proxy->shape text-props :vertical-align)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :verticalAlign value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:vertical-align value})))))}))
|
|
@ -8,6 +8,7 @@
|
|||
(:require
|
||||
[app.common.record :as crc]
|
||||
[app.config :as cfg]
|
||||
[app.plugins.format :as format]
|
||||
[app.plugins.utils :as u]
|
||||
[app.util.object :as obj]))
|
||||
|
||||
|
@ -54,5 +55,5 @@
|
|||
(-> (ActiveUserProxy. plugin-id session-id)
|
||||
(add-user-properties)
|
||||
(crc/add-properties!
|
||||
{:name "position" :get (fn [_] (-> (u/locate-presence session-id) :point u/to-js))}
|
||||
{:name "position" :get (fn [_] (-> (u/locate-presence session-id) :point format/format-point))}
|
||||
{:name "zoom" :get (fn [_] (-> (u/locate-presence session-id) :zoom))})))
|
||||
|
|
|
@ -9,13 +9,10 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.spec :as us]
|
||||
[app.common.types.container :as ctn]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.store :as st]
|
||||
[app.util.object :as obj]
|
||||
[cuerdas.core :as str]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(defn locate-file
|
||||
|
@ -143,58 +140,6 @@
|
|||
(-> (get-state self attr)
|
||||
(mapfn))))
|
||||
|
||||
(defn from-js
|
||||
"Converts the object back to js"
|
||||
([obj]
|
||||
(from-js obj #{:type}))
|
||||
([obj keyword-keys]
|
||||
(when (some? obj)
|
||||
(let [process-node
|
||||
(fn process-node [node]
|
||||
(reduce-kv
|
||||
(fn [m k v]
|
||||
(let [k (keyword (str/kebab k))
|
||||
v (cond (map? v)
|
||||
(process-node v)
|
||||
|
||||
(vector? v)
|
||||
(mapv process-node v)
|
||||
|
||||
(and (string? v) (re-matches us/uuid-rx v))
|
||||
(uuid/uuid v)
|
||||
|
||||
(contains? keyword-keys k)
|
||||
(keyword v)
|
||||
|
||||
:else v)]
|
||||
(assoc m k v)))
|
||||
{}
|
||||
node))]
|
||||
(process-node (js->clj obj))))))
|
||||
|
||||
(defn to-js
|
||||
"Converts to javascript an camelize the keys"
|
||||
[obj]
|
||||
(when (some? obj)
|
||||
(let [result
|
||||
(reduce-kv
|
||||
(fn [m k v]
|
||||
(let [v (cond (object? v) (to-js v)
|
||||
(uuid? v) (dm/str v)
|
||||
:else v)]
|
||||
(assoc m (str/camel (name k)) v)))
|
||||
{}
|
||||
obj)]
|
||||
(clj->js result))))
|
||||
|
||||
(defn array-to-js
|
||||
[value]
|
||||
(if (coll? value)
|
||||
(.freeze
|
||||
js/Object
|
||||
(apply array (->> value (map to-js))))
|
||||
value))
|
||||
|
||||
(defn result-p
|
||||
"Creates a pair of atom+promise. The promise will be resolved when the atom gets a value.
|
||||
We use this to return the promise to the library clients and resolve its value when a value is passed
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
[app.main.data.workspace.viewport :as dwv]
|
||||
[app.main.data.workspace.zoom :as dwz]
|
||||
[app.main.store :as st]
|
||||
[app.plugins.format :as format]
|
||||
[app.plugins.utils :as u]
|
||||
[app.util.object :as obj]))
|
||||
|
||||
|
@ -88,6 +89,6 @@
|
|||
:get
|
||||
(fn [_]
|
||||
(let [vport (dm/get-in @st/state [:workspace-local :vport])]
|
||||
(.freeze js/Object (clj->js vport))))}))
|
||||
(.freeze js/Object (format/format-bounds vport))))}))
|
||||
|
||||
|
||||
|
|
|
@ -156,3 +156,8 @@
|
|||
x)
|
||||
|
||||
:else x)))
|
||||
|
||||
(defn clear-empty
|
||||
[^js obj]
|
||||
(when (some? obj)
|
||||
(js* "Object.entries(~{}).reduce((a, [k,v]) => (v == null ? a : (a[k]=v, a)), {}) " obj)))
|
||||
|
|
|
@ -162,9 +162,8 @@
|
|||
:offset-y 4
|
||||
:blur 4
|
||||
:spread 0
|
||||
:color {:color "#FABADA" :opacity 1}
|
||||
:color {:color "#fabada" :opacity 1}
|
||||
:hidden false}]))))
|
||||
|
||||
(let [shadow #js {:style "fail"}]
|
||||
(set! (.-shadows shape) #js [shadow])
|
||||
(t/is (= (-> (. shape -shadows) (aget 0) (aget "style")) "drop-shadow"))))
|
||||
|
@ -208,20 +207,20 @@
|
|||
|
||||
(t/testing " - fills"
|
||||
(set! (.-fills shape) #js [#js {:fillColor 100}])
|
||||
(t/is (= (-> (. shape -fills) (aget 0) (aget "fillColor")) "#B1B2B5"))
|
||||
(t/is (= (get-in @store (get-shape-path :fills)) [{:fill-color "#B1B2B5" :fill-opacity 1}]))
|
||||
(t/is (= (-> (. shape -fills) (aget 0) (aget "fillColor")) "#B1B2B5"))
|
||||
|
||||
(set! (.-fills shape) #js [#js {:fillColor "#FABADA" :fillOpacity 1}])
|
||||
(t/is (= (-> (. shape -fills) (aget 0) (aget "fillColor")) "#FABADA"))
|
||||
(t/is (= (-> (. shape -fills) (aget 0) (aget "fillOpacity")) 1))
|
||||
(t/is (= (get-in @store (get-shape-path :fills)) [{:fill-color "#FABADA" :fill-opacity 1}])))
|
||||
(set! (.-fills shape) #js [#js {:fillColor "#fabada" :fillOpacity 1}])
|
||||
(t/is (= (get-in @store (get-shape-path :fills)) [{:fill-color "#fabada" :fill-opacity 1}]))
|
||||
(t/is (= (-> (. shape -fills) (aget 0) (aget "fillColor")) "#fabada"))
|
||||
(t/is (= (-> (. shape -fills) (aget 0) (aget "fillOpacity")) 1)))
|
||||
|
||||
(t/testing " - strokes"
|
||||
(set! (.-fills shape) #js [#js {:strokeColor "#FABADA" :strokeOpacity 1 :stroke-width 5}])
|
||||
(t/is (= (-> (. shape -fills) (aget 0) (aget "strokeColor")) "#FABADA"))
|
||||
(t/is (= (-> (. shape -fills) (aget 0) (aget "strokeOpacity")) 1))
|
||||
(t/is (= (-> (. shape -fills) (aget 0) (aget "strokeWidth")) 5))
|
||||
(t/is (= (get-in @store (get-shape-path :fills)) [{:stroke-color "#FABADA" :stroke-opacity 1 :stroke-width 5}]))))
|
||||
(set! (.-strokes shape) #js [#js {:strokeColor "#fabada" :strokeOpacity 1 :strokeWidth 5}])
|
||||
(t/is (= (get-in @store (get-shape-path :strokes)) [{:stroke-color "#fabada" :stroke-opacity 1 :stroke-width 5}]))
|
||||
(t/is (= (-> (. shape -strokes) (aget 0) (aget "strokeColor")) "#fabada"))
|
||||
(t/is (= (-> (. shape -strokes) (aget 0) (aget "strokeOpacity")) 1))
|
||||
(t/is (= (-> (. shape -strokes) (aget 0) (aget "strokeWidth")) 5))))
|
||||
|
||||
(t/testing "Relative properties"
|
||||
(let [frame (.createFrame context)]
|
||||
|
|
Loading…
Add table
Reference in a new issue