mirror of
https://github.com/penpot/penpot.git
synced 2025-05-19 16:46:11 +02:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
68b26d5f41
73 changed files with 693 additions and 295 deletions
|
@ -925,6 +925,13 @@
|
|||
{:num-files (count ids)
|
||||
:project-id project-id})
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [origin-project (get-in state [:dashboard-files (first ids) :project-id])]
|
||||
(-> state
|
||||
(update-in [:dashboard-projects origin-project :count] #(- % (count ids)))
|
||||
(update-in [:dashboard-projects project-id :count] #(+ % (count ids))))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(let [{:keys [on-success on-error]
|
||||
|
|
|
@ -9,6 +9,12 @@
|
|||
|
||||
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 class "mousetrap" then no need to stop
|
||||
|
@ -20,7 +26,7 @@ target.stopCallback = function(e, element, combo) {
|
|||
return element.tagName == 'INPUT' ||
|
||||
element.tagName == 'SELECT' ||
|
||||
element.tagName == 'TEXTAREA' ||
|
||||
element.tagName == 'BUTTON' ||
|
||||
(element.tagName == 'BUTTON' && combo.includes("tab")) ||
|
||||
(element.contentEditable && element.contentEditable == 'true');
|
||||
}
|
||||
|
||||
|
|
|
@ -676,6 +676,10 @@
|
|||
(cond-> (not (ctl/any-layout? objects parent-id))
|
||||
(pcb/update-shapes ordered-indexes ctl/remove-layout-item-data))
|
||||
|
||||
;; Remove the hide in viewer flag
|
||||
(cond-> (and (not= uuid/zero parent-id) (cph/frame-shape? objects parent-id))
|
||||
(pcb/update-shapes ordered-indexes #(cond-> % (cph/frame-shape? %) (assoc :hide-in-viewer true))))
|
||||
|
||||
;; Move the shapes
|
||||
(pcb/change-parent parent-id
|
||||
shapes
|
||||
|
@ -1311,8 +1315,8 @@
|
|||
;; Prepare the shape object. Mainly needed for image shapes
|
||||
;; for retrieve the image data and convert it to the
|
||||
;; data-url.
|
||||
(prepare-object [objects selected+children {:keys [type] :as obj}]
|
||||
(let [obj (maybe-translate obj objects selected+children)]
|
||||
(prepare-object [objects parent-frame-id {:keys [type] :as obj}]
|
||||
(let [obj (maybe-translate obj objects parent-frame-id)]
|
||||
(if (= type :image)
|
||||
(let [url (cf/resolve-file-media (:metadata obj))]
|
||||
(->> (http/send! {:method :get
|
||||
|
@ -1335,15 +1339,11 @@
|
|||
(update res :images conj img-part))
|
||||
res)))
|
||||
|
||||
(maybe-translate [shape objects selected+children]
|
||||
(let [root-frame-id (cph/get-shape-id-root-frame objects (:id shape))]
|
||||
(if (and (not (cph/root-frame? shape))
|
||||
(not (contains? selected+children root-frame-id)))
|
||||
;; When the parent frame is not selected we change to relative
|
||||
;; coordinates
|
||||
(let [frame (get objects root-frame-id)]
|
||||
(gsh/translate-to-frame shape frame))
|
||||
shape)))
|
||||
(maybe-translate [shape objects parent-frame-id]
|
||||
(if (= parent-frame-id uuid/zero)
|
||||
shape
|
||||
(let [frame (get objects parent-frame-id)]
|
||||
(gsh/translate-to-frame shape frame))))
|
||||
|
||||
(on-copy-error [error]
|
||||
(js/console.error "Clipboard blocked:" error)
|
||||
|
@ -1356,7 +1356,7 @@
|
|||
selected (->> (wsh/lookup-selected state)
|
||||
(cph/clean-loops objects))
|
||||
|
||||
selected+children (cph/selected-with-children objects selected)
|
||||
parent-frame-id (cph/common-parent-frame objects selected)
|
||||
pdata (reduce (partial collect-object-ids objects) {} selected)
|
||||
initial {:type :copied-shapes
|
||||
:file-id (:current-file-id state)
|
||||
|
@ -1371,7 +1371,7 @@
|
|||
(catch :default e
|
||||
(on-copy-error e)))
|
||||
(->> (rx/from (seq (vals pdata)))
|
||||
(rx/merge-map (partial prepare-object objects selected+children))
|
||||
(rx/merge-map (partial prepare-object objects parent-frame-id))
|
||||
(rx/reduce collect-data initial)
|
||||
(rx/map (partial sort-selected state))
|
||||
(rx/map t/encode-str)
|
||||
|
|
|
@ -138,6 +138,7 @@
|
|||
(pcb/with-objects objects)
|
||||
(cond-> (not (ctl/any-layout? objects frame-id))
|
||||
(pcb/update-shapes ordered-indexes ctl/remove-layout-item-data))
|
||||
(pcb/update-shapes ordered-indexes #(cond-> % (cph/frame-shape? %) (assoc :hide-in-viewer true)))
|
||||
(pcb/change-parent frame-id to-move-shapes 0)
|
||||
(cond-> (ctl/grid-layout? objects frame-id)
|
||||
(pcb/update-shapes [frame-id] ctl/assign-cells))))]
|
||||
|
|
|
@ -354,12 +354,14 @@
|
|||
:fn #(st/emit! (dw/select-all))}
|
||||
|
||||
:toggle-grid {:tooltip (ds/meta "'")
|
||||
:command (ds/c-mod "'")
|
||||
;;https://github.com/ccampbell/mousetrap/issues/85
|
||||
:command [(ds/c-mod "'") (ds/c-mod "219")]
|
||||
:subsections [:main-menu]
|
||||
:fn #(st/emit! (toggle-layout-flag :display-grid))}
|
||||
|
||||
:toggle-snap-grid {:tooltip (ds/meta-shift "'")
|
||||
:command (ds/c-mod "shift+'")
|
||||
;;https://github.com/ccampbell/mousetrap/issues/85
|
||||
:command [(ds/c-mod "shift+'") (ds/c-mod "shift+219")]
|
||||
:subsections [:main-menu]
|
||||
:fn #(st/emit! (toggle-layout-flag :snap-grid))}
|
||||
|
||||
|
|
|
@ -164,11 +164,13 @@
|
|||
(cond-> shape
|
||||
(get-in shape [:svg-attrs :opacity])
|
||||
(-> (update :svg-attrs dissoc :opacity)
|
||||
(assoc :opacity (get-in shape [:svg-attrs :opacity])))
|
||||
(assoc :opacity (-> (get-in shape [:svg-attrs :opacity])
|
||||
(d/parse-double))))
|
||||
|
||||
(get-in shape [:svg-attrs :style :opacity])
|
||||
(-> (update-in [:svg-attrs :style] dissoc :opacity)
|
||||
(assoc :opacity (get-in shape [:svg-attrs :style :opacity])))
|
||||
(assoc :opacity (-> (get-in shape [:svg-attrs :style :opacity])
|
||||
(d/parse-double))))
|
||||
|
||||
|
||||
(get-in shape [:svg-attrs :mix-blend-mode])
|
||||
|
@ -410,7 +412,8 @@
|
|||
(assoc :strokes [])
|
||||
(assoc :svg-defs (select-keys (:defs svg-data) references))
|
||||
(setup-fill)
|
||||
(setup-stroke))
|
||||
(setup-stroke)
|
||||
(setup-opacity))
|
||||
|
||||
shape (cond-> shape
|
||||
hidden (assoc :hidden true))
|
||||
|
|
|
@ -377,7 +377,7 @@
|
|||
(assoc-in [:workspace-local :edition] (-> selected first :id)))))))
|
||||
|
||||
(defn not-changed? [old-dim new-dim]
|
||||
(> (mth/abs (- old-dim new-dim)) 1))
|
||||
(> (mth/abs (- old-dim new-dim)) 0.1))
|
||||
|
||||
(defn commit-resize-text
|
||||
[]
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
(defn start-resize
|
||||
"Enter mouse resize mode, until mouse button is released."
|
||||
[handler ids shape]
|
||||
(letfn [(resize
|
||||
(letfn [(resize
|
||||
[shape initial layout [point lock? center? point-snap]]
|
||||
(let [{:keys [width height]} (:selrect shape)
|
||||
{:keys [rotation]} shape
|
||||
|
@ -745,13 +745,16 @@
|
|||
(remove (fn [shape]
|
||||
(and (ctl/layout-absolute? shape)
|
||||
(= frame-id (:parent-id shape))))))
|
||||
moving-shapes-ids
|
||||
(map :id moving-shapes)
|
||||
|
||||
changes
|
||||
(-> (pcb/empty-changes it page-id)
|
||||
(pcb/with-objects objects)
|
||||
;; Remove layout-item properties when moving a shape outside a layout
|
||||
(cond-> (not (ctl/any-layout? objects frame-id))
|
||||
(pcb/update-shapes (map :id moving-shapes) ctl/remove-layout-item-data))
|
||||
(pcb/update-shapes moving-shapes-ids ctl/remove-layout-item-data))
|
||||
(pcb/update-shapes moving-shapes-ids #(cond-> % (cph/frame-shape? %) (assoc :hide-in-viewer true)))
|
||||
(pcb/change-parent frame-id moving-shapes drop-index)
|
||||
(pcb/remove-objects empty-parents))]
|
||||
|
||||
|
|
|
@ -269,7 +269,8 @@
|
|||
{:option-name (tr "labels.rename")
|
||||
:id "file-rename"
|
||||
:option-handler on-edit
|
||||
:data-test "file-rename"}
|
||||
:data-test "file-rename"})
|
||||
(when (not is-search-page?)
|
||||
{:option-name (tr "dashboard.duplicate")
|
||||
:id "file-duplicate"
|
||||
:option-handler on-duplicate
|
||||
|
|
|
@ -144,6 +144,7 @@
|
|||
|
||||
create-file
|
||||
(mf/use-fn
|
||||
(mf/deps project)
|
||||
(fn [origin]
|
||||
(st/emit! (with-meta (dd/create-file {:project-id (:id project)})
|
||||
{::ev/origin origin}))))]
|
||||
|
|
|
@ -269,7 +269,9 @@
|
|||
(mf/use-fn
|
||||
(mf/deps file)
|
||||
(fn [name]
|
||||
(st/emit! (dd/rename-file (assoc file :name name)))
|
||||
(let [name (str/trim name)]
|
||||
(when (not= name "")
|
||||
(st/emit! (dd/rename-file (assoc file :name name)))))
|
||||
(swap! local assoc :edition false)))
|
||||
|
||||
on-edit
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
[app.util.keyboard :as kbd]
|
||||
[app.util.webapi :as wapi]
|
||||
[beicon.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
|
@ -61,9 +62,11 @@
|
|||
(->> files
|
||||
(mapv
|
||||
(fn [file]
|
||||
(cond-> file
|
||||
(= (:file-id file) file-id)
|
||||
(assoc :name new-name))))))
|
||||
(let [new-name (str/trim new-name)]
|
||||
(cond-> file
|
||||
(and (= (:file-id file) file-id)
|
||||
(not= "" new-name))
|
||||
(assoc :name new-name)))))))
|
||||
|
||||
(defn remove-file [files file-id]
|
||||
(->> files
|
||||
|
@ -378,7 +381,7 @@
|
|||
|
||||
[:div.feedback-banner
|
||||
[:div.icon i/checkbox-checked]
|
||||
[:div.message (tr "dashboard.import.import-message" (if (some? template) 1 success-files))]]))
|
||||
[:div.message (tr "dashboard.import.import-message" (i18n/c (if (some? template) 1 success-files)))]]))
|
||||
|
||||
(for [file files]
|
||||
(let [editing? (and (some? (:file-id file))
|
||||
|
|
|
@ -362,8 +362,13 @@
|
|||
(reverse))
|
||||
recent-map (mf/deref recent-files-ref)
|
||||
props (some-> profile (get :props {}))
|
||||
team-hero? (and (:team-hero? props true)
|
||||
(not (:is-default team)))
|
||||
you-owner? (get-in team [:permissions :is-owner])
|
||||
you-admin? (get-in team [:permissions :is-admin])
|
||||
can-invite? (or you-owner? you-admin?)
|
||||
team-hero? (and can-invite?
|
||||
(:team-hero? props true)
|
||||
(not (:is-default team)))
|
||||
|
||||
tutorial-viewed? (:viewed-tutorial? props true)
|
||||
walkthrough-viewed? (:viewed-walkthrough? props true)
|
||||
|
||||
|
|
|
@ -177,7 +177,9 @@
|
|||
:on-submit on-submit}]]
|
||||
|
||||
[:div.action-buttons
|
||||
[:& fm/submit-button {:label (tr "modals.invite-member-confirm.accept")}]]]]))
|
||||
[:& fm/submit-button {:label (tr "modals.invite-member-confirm.accept")
|
||||
:disabled (and (boolean (some current-data-emails current-members-emails))
|
||||
(empty? (remove current-members-emails current-data-emails)))}]]]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; MEMBERS SECTION
|
||||
|
@ -586,7 +588,8 @@
|
|||
[:div.empty-invitations
|
||||
[:span (tr "labels.no-invitations")]
|
||||
(when can-invite?
|
||||
[:span (tr "labels.no-invitations-hint")])])
|
||||
[:& i18n/tr-html {:label "labels.no-invitations-hint"
|
||||
:tag-name "span"}])])
|
||||
|
||||
(mf/defc invitation-section
|
||||
[{:keys [team invitations] :as props}]
|
||||
|
@ -897,6 +900,10 @@
|
|||
|
||||
stats (mf/deref refs/dashboard-team-stats)
|
||||
|
||||
you-owner? (get-in team [:permissions :is-owner])
|
||||
you-admin? (get-in team [:permissions :is-admin])
|
||||
can-edit? (or you-owner? you-admin?)
|
||||
|
||||
on-image-click
|
||||
(mf/use-callback #(dom/click (mf/ref-val finput)))
|
||||
|
||||
|
@ -928,12 +935,14 @@
|
|||
[:div.label (tr "dashboard.team-info")]
|
||||
[:div.name (:name team)]
|
||||
[:div.icon
|
||||
[:span.update-overlay {:on-click on-image-click} i/image]
|
||||
(when can-edit?
|
||||
[:span.update-overlay {:on-click on-image-click} i/image])
|
||||
[:img {:src (cfg/resolve-team-photo-url team)}]
|
||||
[:& file-uploader {:accept "image/jpeg,image/png"
|
||||
:multi false
|
||||
:ref finput
|
||||
:on-selected on-file-selected}]]]
|
||||
(when can-edit?
|
||||
[:& file-uploader {:accept "image/jpeg,image/png"
|
||||
:multi false
|
||||
:ref finput
|
||||
:on-selected on-file-selected}])]]
|
||||
|
||||
[:div.block.owner-block
|
||||
[:div.label (tr "dashboard.team-members")]
|
||||
|
|
|
@ -185,6 +185,7 @@
|
|||
:auto-focus? true
|
||||
:trim true
|
||||
:valid-item-fn us/parse-email
|
||||
:caution-item-fn #{}
|
||||
:on-submit on-submit
|
||||
:label (tr "modals.invite-member.emails")}]]
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
[app.main.ui.releases.v1-15]
|
||||
[app.main.ui.releases.v1-16]
|
||||
[app.main.ui.releases.v1-17]
|
||||
[app.main.ui.releases.v1-18]
|
||||
[app.main.ui.releases.v1-4]
|
||||
[app.main.ui.releases.v1-5]
|
||||
[app.main.ui.releases.v1-6]
|
||||
|
@ -87,4 +88,4 @@
|
|||
|
||||
(defmethod rc/render-release-notes "0.0"
|
||||
[params]
|
||||
(rc/render-release-notes (assoc params :version "1.17")))
|
||||
(rc/render-release-notes (assoc params :version "1.18")))
|
||||
|
|
108
frontend/src/app/main/ui/releases/v1_18.cljs
Normal file
108
frontend/src/app/main/ui/releases/v1_18.cljs
Normal file
|
@ -0,0 +1,108 @@
|
|||
;; 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.main.ui.releases.v1-18
|
||||
(:require
|
||||
[app.main.ui.releases.common :as c]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defmethod c/render-release-notes "1.18"
|
||||
[{:keys [slide klass next finish navigate version]}]
|
||||
(mf/html
|
||||
(case @slide
|
||||
:start
|
||||
[:div.modal-overlay
|
||||
[:div.animated {:class @klass}
|
||||
[:div.modal-container.onboarding.feature
|
||||
[:div.modal-left
|
||||
[:img {:src "images/onboarding-version.jpg" :border "0" :alt "What's new release 1.18"}]]
|
||||
[:div.modal-right
|
||||
[:div.modal-title
|
||||
[:h2 "What's new?"]]
|
||||
[:span.release "Version " version]
|
||||
[:div.modal-content
|
||||
[:p "On this 1.18 release we make Flex Layout even more powerful with smart spacing, absolute position and z-index management."]
|
||||
[:p "We also continued implementing accessibility improvements to make Penpot more inclusive and published stability and performance enhancements."]]
|
||||
[:div.modal-navigation
|
||||
[:button.btn-secondary {:on-click next} "Continue"]]]
|
||||
[:img.deco {:src "images/deco-left.png" :border "0"}]
|
||||
[:img.deco.right {:src "images/deco-right.png" :border "0"}]]]]
|
||||
|
||||
0
|
||||
[:div.modal-overlay
|
||||
[:div.animated {:class @klass}
|
||||
[:div.modal-container.onboarding.feature
|
||||
[:div.modal-left
|
||||
[:img {:src "images/features/1.18-spacing.gif" :border "0" :alt "Spacing management"}]]
|
||||
[:div.modal-right
|
||||
[:div.modal-title
|
||||
[:h2 "Spacing management for Flex layoutFlex-Layout"]]
|
||||
[:div.modal-content
|
||||
[:p "Managing Flex Layout spacing is much more intuitive now. Visualize paddings, margins and gaps and drag to resize them."]
|
||||
[:p "And not only that, when creating Flex layouts, the spacing is predicted, helping you to maintain your design composition."]]
|
||||
[:div.modal-navigation
|
||||
[:button.btn-secondary {:on-click next} "Continue"]
|
||||
[:& c/navigation-bullets
|
||||
{:slide @slide
|
||||
:navigate navigate
|
||||
:total 4}]]]]]]
|
||||
|
||||
1
|
||||
[:div.modal-overlay
|
||||
[:div.animated {:class @klass}
|
||||
[:div.modal-container.onboarding.feature
|
||||
[:div.modal-left
|
||||
[:img {:src "images/features/1.18-absolute.gif" :border "0" :alt "Position absolute feature"}]]
|
||||
[:div.modal-right
|
||||
[:div.modal-title
|
||||
[:h2 "Absolute position elements in Flex layout"]]
|
||||
[:div.modal-content
|
||||
[:p "Sometimes you need to freely position an element in a specific place regardless of the size of the layout where it belongs."]
|
||||
[:p "Now you can exclude elements from the Flex layout flow using absolute position."]]
|
||||
[:div.modal-navigation
|
||||
[:button.btn-secondary {:on-click next} "Continue"]
|
||||
[:& c/navigation-bullets
|
||||
{:slide @slide
|
||||
:navigate navigate
|
||||
:total 4}]]]]]]
|
||||
|
||||
2
|
||||
[:div.modal-overlay
|
||||
[:div.animated {:class @klass}
|
||||
[:div.modal-container.onboarding.feature
|
||||
[:div.modal-left
|
||||
[:img {:src "images/features/1.18-z-index.gif" :border "0" :alt "Z-index feature"}]]
|
||||
[:div.modal-right
|
||||
[:div.modal-title
|
||||
[:h2 "More on Flex layout: z-index"]]
|
||||
[:div.modal-content
|
||||
[:p "With the new z-index option you can decide the order of overlapping elements while maintaining the layers order."]
|
||||
[:p "This is another capability that brings Penpot Flex layout even closer to the power of CSS standards."]]
|
||||
[:div.modal-navigation
|
||||
[:button.btn-secondary {:on-click next} "Continue"]
|
||||
[:& c/navigation-bullets
|
||||
{:slide @slide
|
||||
:navigate navigate
|
||||
:total 4}]]]]]]
|
||||
|
||||
3
|
||||
[:div.modal-overlay
|
||||
[:div.animated {:class @klass}
|
||||
[:div.modal-container.onboarding.feature
|
||||
[:div.modal-left
|
||||
[:img {:src "images/features/1.18-scale.gif" :border "0" :alt "Scale content proportionally"}]]
|
||||
[:div.modal-right
|
||||
[:div.modal-title
|
||||
[:h2 "Scale content proportionally affects strokes, shadows, blurs and corners"]]
|
||||
[:div.modal-content
|
||||
[:p "Now you can resize your layers and groups preserving their aspect ratio while scaling their properties proportionally, including strokes, shadows, blurs and corners."]
|
||||
[:p "Activate the scale tool by pressing K and scale your elements, maintaining their visual aspect."]]
|
||||
[:div.modal-navigation
|
||||
[:button.btn-secondary {:on-click finish} "Start!"]
|
||||
[:& c/navigation-bullets
|
||||
{:slide @slide
|
||||
:navigate navigate
|
||||
:total 4}]]]]]])))
|
|
@ -38,20 +38,27 @@
|
|||
[:use {:href (str "#" shape-id)}]]))
|
||||
|
||||
(mf/defc outer-stroke-mask
|
||||
[{:keys [shape render-id index]}]
|
||||
[{:keys [shape stroke render-id index]}]
|
||||
(let [suffix (if index (str "-" index) "")
|
||||
stroke-mask-id (str "outer-stroke-" render-id "-" (:id shape) suffix)
|
||||
shape-id (str "stroke-shape-" render-id "-" (:id shape) suffix)
|
||||
stroke-width (case (:stroke-alignment shape :center)
|
||||
:center (/ (:stroke-width shape 0) 2)
|
||||
:outer (:stroke-width shape 0)
|
||||
stroke-width (case (:stroke-alignment stroke :center)
|
||||
:center (/ (:stroke-width stroke 0) 2)
|
||||
:outer (:stroke-width stroke 0)
|
||||
0)
|
||||
margin (gsb/shape-stroke-margin shape stroke-width)
|
||||
bounding-box (-> (gsh/points->selrect (:points shape))
|
||||
(update :x - (+ stroke-width margin))
|
||||
(update :y - (+ stroke-width margin))
|
||||
(update :width + (* 2 (+ stroke-width margin)))
|
||||
(update :height + (* 2 (+ stroke-width margin))))]
|
||||
margin (gsb/shape-stroke-margin stroke stroke-width)
|
||||
|
||||
selrect
|
||||
(if (cph/text-shape? shape)
|
||||
(gsh/position-data-selrect shape)
|
||||
(gsh/points->selrect (:points shape)))
|
||||
|
||||
bounding-box
|
||||
(-> selrect
|
||||
(update :x - (+ stroke-width margin))
|
||||
(update :y - (+ stroke-width margin))
|
||||
(update :width + (* 2 (+ stroke-width margin)))
|
||||
(update :height + (* 2 (+ stroke-width margin))))]
|
||||
|
||||
[:mask {:id stroke-mask-id
|
||||
:x (:x bounding-box)
|
||||
|
@ -67,17 +74,17 @@
|
|||
:stroke "none"}}]]))
|
||||
|
||||
(mf/defc cap-markers
|
||||
[{:keys [shape render-id index]}]
|
||||
[{:keys [stroke render-id index]}]
|
||||
(let [marker-id-prefix (str "marker-" render-id)
|
||||
cap-start (:stroke-cap-start shape)
|
||||
cap-end (:stroke-cap-end shape)
|
||||
cap-start (:stroke-cap-start stroke)
|
||||
cap-end (:stroke-cap-end stroke)
|
||||
|
||||
stroke-color (if (:stroke-color-gradient shape)
|
||||
stroke-color (if (:stroke-color-gradient stroke)
|
||||
(str/format "url(#%s)" (str "stroke-color-gradient_" render-id "_" index))
|
||||
(:stroke-color shape))
|
||||
(:stroke-color stroke))
|
||||
|
||||
stroke-opacity (when-not (:stroke-color-gradient shape)
|
||||
(:stroke-opacity shape))]
|
||||
stroke-opacity (when-not (:stroke-color-gradient stroke)
|
||||
(:stroke-opacity stroke))]
|
||||
|
||||
[:*
|
||||
(when (or (= cap-start :line-arrow) (= cap-end :line-arrow))
|
||||
|
@ -169,36 +176,37 @@
|
|||
[:rect {:x 3 :y 2.5 :width 0.5 :height 1}]])]))
|
||||
|
||||
(mf/defc stroke-defs
|
||||
[{:keys [shape render-id index]}]
|
||||
[{:keys [shape stroke render-id index]}]
|
||||
|
||||
(let [open-path? (and (= :path (:type shape)) (gsh/open-path? shape))]
|
||||
[:*
|
||||
(cond (some? (:stroke-color-gradient shape))
|
||||
(case (:type (:stroke-color-gradient shape))
|
||||
(cond (some? (:stroke-color-gradient stroke))
|
||||
(case (:type (:stroke-color-gradient stroke))
|
||||
:linear [:> grad/linear-gradient #js {:id (str (name :stroke-color-gradient) "_" render-id "_" index)
|
||||
:gradient (:stroke-color-gradient shape)
|
||||
:gradient (:stroke-color-gradient stroke)
|
||||
:shape shape}]
|
||||
:radial [:> grad/radial-gradient #js {:id (str (name :stroke-color-gradient) "_" render-id "_" index)
|
||||
:gradient (:stroke-color-gradient shape)
|
||||
:gradient (:stroke-color-gradient stroke)
|
||||
:shape shape}]))
|
||||
(cond
|
||||
(and (not open-path?)
|
||||
(= :inner (:stroke-alignment shape :center))
|
||||
(> (:stroke-width shape 0) 0))
|
||||
(= :inner (:stroke-alignment stroke :center))
|
||||
(> (:stroke-width stroke 0) 0))
|
||||
[:& inner-stroke-clip-path {:shape shape
|
||||
:render-id render-id
|
||||
:index index}]
|
||||
|
||||
(and (not open-path?)
|
||||
(= :outer (:stroke-alignment shape :center))
|
||||
(> (:stroke-width shape 0) 0))
|
||||
(= :outer (:stroke-alignment stroke :center))
|
||||
(> (:stroke-width stroke 0) 0))
|
||||
[:& outer-stroke-mask {:shape shape
|
||||
:stroke stroke
|
||||
:render-id render-id
|
||||
:index index}]
|
||||
|
||||
(or (some? (:stroke-cap-start shape))
|
||||
(some? (:stroke-cap-end shape)))
|
||||
[:& cap-markers {:shape shape
|
||||
(or (some? (:stroke-cap-start stroke))
|
||||
(some? (:stroke-cap-end stroke)))
|
||||
[:& cap-markers {:stroke stroke
|
||||
:render-id render-id
|
||||
:index index}])]))
|
||||
|
||||
|
@ -216,8 +224,9 @@
|
|||
base-props (obj/get child "props")
|
||||
elem-name (obj/get child "type")
|
||||
shape (obj/get props "shape")
|
||||
stroke (obj/get props "stroke")
|
||||
index (obj/get props "index")
|
||||
stroke-width (:stroke-width shape)
|
||||
stroke-width (:stroke-width stroke)
|
||||
|
||||
suffix (if index (str "-" index) "")
|
||||
stroke-mask-id (str "outer-stroke-" render-id "-" (:id shape) suffix)
|
||||
|
@ -225,7 +234,7 @@
|
|||
|
||||
[:g.outer-stroke-shape
|
||||
[:defs
|
||||
[:& stroke-defs {:shape shape :render-id render-id :index index}]
|
||||
[:& stroke-defs {:shape shape :stroke stroke :render-id render-id :index index}]
|
||||
[:> elem-name (-> (obj/clone base-props)
|
||||
(obj/set! "id" shape-id)
|
||||
(obj/set!
|
||||
|
@ -258,10 +267,11 @@
|
|||
base-props (obj/get child "props")
|
||||
elem-name (obj/get child "type")
|
||||
shape (obj/get props "shape")
|
||||
stroke (obj/get props "stroke")
|
||||
index (obj/get props "index")
|
||||
transform (obj/get base-props "transform")
|
||||
|
||||
stroke-width (:stroke-width shape 0)
|
||||
stroke-width (:stroke-width stroke 0)
|
||||
|
||||
suffix (if index (str "-" index) "")
|
||||
clip-id (str "inner-stroke-" render-id "-" (:id shape) suffix)
|
||||
|
@ -275,7 +285,7 @@
|
|||
|
||||
[:g.inner-stroke-shape {:transform transform}
|
||||
[:defs
|
||||
[:& stroke-defs {:shape shape :render-id render-id :index index}]
|
||||
[:& stroke-defs {:shape shape :stroke stroke :render-id render-id :index index}]
|
||||
[:> elem-name shape-props]]
|
||||
|
||||
[:use {:href (str "#" shape-id)
|
||||
|
@ -290,33 +300,34 @@
|
|||
{::mf/wrap-props false}
|
||||
[props]
|
||||
|
||||
(let [child (obj/get props "children")
|
||||
shape (obj/get props "shape")
|
||||
(let [child (obj/get props "children")
|
||||
shape (obj/get props "shape")
|
||||
stroke (obj/get props "stroke")
|
||||
|
||||
render-id (mf/use-ctx muc/render-id)
|
||||
index (obj/get props "index")
|
||||
stroke-width (:stroke-width shape 0)
|
||||
stroke-style (:stroke-style shape :none)
|
||||
stroke-position (:stroke-alignment shape :center)
|
||||
stroke-width (:stroke-width stroke 0)
|
||||
stroke-style (:stroke-style stroke :none)
|
||||
stroke-position (:stroke-alignment stroke :center)
|
||||
has-stroke? (and (> stroke-width 0)
|
||||
(not= stroke-style :none))
|
||||
closed? (or (not= :path (:type shape))
|
||||
(not (gsh/open-path? shape)))
|
||||
closed? (or (not= :path (:type shape)) (not (gsh/open-path? shape)))
|
||||
inner? (= :inner stroke-position)
|
||||
outer? (= :outer stroke-position)]
|
||||
|
||||
(cond
|
||||
(and has-stroke? inner? closed?)
|
||||
[:& inner-stroke {:shape shape :index index}
|
||||
[:& inner-stroke {:shape shape :stroke stroke :index index}
|
||||
child]
|
||||
|
||||
(and has-stroke? outer? closed?)
|
||||
[:& outer-stroke {:shape shape :index index}
|
||||
[:& outer-stroke {:shape shape :stroke stroke :index index}
|
||||
child]
|
||||
|
||||
:else
|
||||
[:g.stroke-shape
|
||||
[:defs
|
||||
[:& stroke-defs {:shape shape :render-id render-id :index index}]]
|
||||
[:& stroke-defs {:shape shape :stroke stroke :render-id render-id :index index}]]
|
||||
child])))
|
||||
|
||||
(defn build-fill-props [shape child position render-id]
|
||||
|
@ -426,6 +437,7 @@
|
|||
[props]
|
||||
(let [child (obj/get props "children")
|
||||
shape (obj/get props "shape")
|
||||
|
||||
elem-name (obj/get child "type")
|
||||
render-id (or (obj/get props "render-id") (mf/use-ctx muc/render-id))
|
||||
stroke-id (dm/fmt "strokes-%" (:id shape))
|
||||
|
@ -445,9 +457,8 @@
|
|||
(d/not-empty? (:strokes shape))
|
||||
[:> :g stroke-props
|
||||
(for [[index value] (-> (d/enumerate (:strokes shape)) reverse)]
|
||||
(let [props (build-stroke-props index child value render-id)
|
||||
shape (assoc value :points (:points shape))]
|
||||
[:& shape-custom-stroke {:shape shape :index index :key (dm/str index "-" stroke-id)}
|
||||
(let [props (build-stroke-props index child value render-id)]
|
||||
[:& shape-custom-stroke {:shape shape :stroke value :index index :key (dm/str index "-" stroke-id)}
|
||||
[:> elem-name props]]))])]))
|
||||
|
||||
(mf/defc shape-custom-strokes
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
|
||||
(defn- activate-interaction
|
||||
[interaction shape base-frame frame-offset objects overlays]
|
||||
|
||||
(case (:action-type interaction)
|
||||
:navigate
|
||||
(when-let [frame-id (:destination interaction)]
|
||||
|
@ -69,6 +70,7 @@
|
|||
overlays-ids (set (map :id overlays))
|
||||
relative-to-base-frame (find-relative-to-base-frame relative-to-shape objects overlays-ids base-frame)
|
||||
position (ctsi/calc-overlay-position interaction
|
||||
shape
|
||||
viewer-objects
|
||||
relative-to-shape
|
||||
relative-to-base-frame
|
||||
|
@ -91,6 +93,7 @@
|
|||
overlays-ids (set (map :id overlays))
|
||||
relative-to-base-frame (find-relative-to-base-frame relative-to-shape objects overlays-ids base-frame)
|
||||
position (ctsi/calc-overlay-position interaction
|
||||
shape
|
||||
objects
|
||||
relative-to-shape
|
||||
relative-to-base-frame
|
||||
|
@ -156,6 +159,7 @@
|
|||
overlays-ids (set (map :id overlays))
|
||||
relative-to-base-frame (find-relative-to-base-frame relative-to-shape objects overlays-ids base-frame)
|
||||
position (ctsi/calc-overlay-position interaction
|
||||
shape
|
||||
objects
|
||||
relative-to-shape
|
||||
relative-to-base-frame
|
||||
|
|
|
@ -67,7 +67,8 @@
|
|||
(mf/use-fn
|
||||
(mf/deps current-color @drag?)
|
||||
(fn [color]
|
||||
(when (not= (str/lower (:hex color)) (str/lower (:hex current-color)))
|
||||
(when (or (not= (str/lower (:hex color)) (str/lower (:hex current-color)))
|
||||
(not= (:h color) (:h current-color)))
|
||||
(let [recent-color (merge current-color color)
|
||||
recent-color (dc/materialize-color-components recent-color)]
|
||||
(st/emit! (dc/update-colorpicker-color recent-color (not @drag?)))))))
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
[app.main.data.modal :as modal]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.colors :as dc]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.shortcuts :as sc]
|
||||
[app.main.refs :as refs]
|
||||
|
@ -32,6 +33,7 @@
|
|||
[app.util.keyboard :as kbd]
|
||||
[app.util.router :as rt]
|
||||
[beicon.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[okulary.core :as l]
|
||||
[potok.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
@ -149,10 +151,12 @@
|
|||
:on-accept #(st/emit! (dwl/set-file-shared (:id file) false))
|
||||
:count-libraries 1}))))
|
||||
|
||||
handle-blur (fn [_]
|
||||
(let [value (-> edit-input-ref mf/ref-val dom/get-value)]
|
||||
(st/emit! (dw/rename-file (:id file) value)))
|
||||
(reset! editing? false))
|
||||
handle-blur
|
||||
(fn [_]
|
||||
(let [value (str/trim (-> edit-input-ref mf/ref-val dom/get-value))]
|
||||
(when (not= value "")
|
||||
(st/emit! (dw/rename-file (:id file) value))))
|
||||
(reset! editing? false))
|
||||
|
||||
handle-name-keydown (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
|
@ -306,12 +310,14 @@
|
|||
[:li {:on-click #(st/emit! (dw/select-all))}
|
||||
[:span (tr "workspace.header.menu.select-all")]
|
||||
[:span.shortcut (sc/get-tooltip :select-all)]]
|
||||
[:li {:on-click #(st/emit! (toggle-flag :scale-text))}
|
||||
[:span
|
||||
(if (contains? layout :scale-text)
|
||||
(tr "workspace.header.menu.disable-scale-text")
|
||||
(tr "workspace.header.menu.enable-scale-text"))]
|
||||
[:span.shortcut (sc/get-tooltip :toggle-scale-text)]]]]
|
||||
|
||||
[:li {:on-click #(st/emit! dwc/undo)}
|
||||
[:span (tr "workspace.header.menu.undo")]
|
||||
[:span.shortcut (sc/get-tooltip :undo)]]
|
||||
|
||||
[:li {:on-click #(st/emit! dwc/redo)}
|
||||
[:span (tr "workspace.header.menu.redo")]
|
||||
[:span.shortcut (sc/get-tooltip :redo)]]]]
|
||||
|
||||
[:& dropdown {:show (= @show-sub-menu? :view)
|
||||
:on-close #(reset! show-sub-menu? false)}
|
||||
|
@ -374,6 +380,13 @@
|
|||
[:& dropdown {:show (= @show-sub-menu? :preferences)
|
||||
:on-close #(reset! show-sub-menu? false)}
|
||||
[:ul.sub-menu.preferences
|
||||
[:li {:on-click #(st/emit! (toggle-flag :scale-text))}
|
||||
[:span
|
||||
(if (contains? layout :scale-text)
|
||||
(tr "workspace.header.menu.disable-scale-content")
|
||||
(tr "workspace.header.menu.enable-scale-content"))]
|
||||
[:span.shortcut (sc/get-tooltip :toggle-scale-text)]]
|
||||
|
||||
[:li {:on-click #(st/emit! (toggle-flag :snap-guides))}
|
||||
[:span
|
||||
(if (contains? layout :snap-guides)
|
||||
|
|
|
@ -66,7 +66,8 @@
|
|||
(when (contains? #{:auto-height :auto-width} grow-type)
|
||||
(let [{:keys [width height]}
|
||||
(-> (dom/query node ".paragraph-set")
|
||||
(dom/get-client-size))
|
||||
(dom/get-bounding-rect))
|
||||
|
||||
width (mth/ceil width)
|
||||
height (mth/ceil height)]
|
||||
(when (and (not (mth/almost-zero? width))
|
||||
|
|
|
@ -161,27 +161,37 @@
|
|||
|
||||
on-change
|
||||
(mf/use-fn
|
||||
(fn [new-color old-color]
|
||||
(fn [new-color old-color from-picker?]
|
||||
(let [old-color (-> old-color
|
||||
(dissoc :name)
|
||||
(dissoc :path)
|
||||
(d/without-nils))
|
||||
|
||||
prev-color (-> @prev-color*
|
||||
(dissoc :name)
|
||||
(dissoc :path)
|
||||
(d/without-nils))
|
||||
prev-color (when @prev-color*
|
||||
(-> @prev-color*
|
||||
(dissoc :name)
|
||||
(dissoc :path)
|
||||
(d/without-nils)))
|
||||
|
||||
;; When dragging on the color picker sometimes all the shapes hasn't updated the color to the prev value so we need this extra calculation
|
||||
shapes-by-old-color (get @grouped-colors* old-color)
|
||||
shapes-by-prev-color (get @grouped-colors* prev-color)
|
||||
shapes-by-color (or shapes-by-prev-color shapes-by-old-color)]
|
||||
(reset! prev-color* new-color)
|
||||
(st/emit! (dc/change-color-in-selected new-color shapes-by-color old-color)))))
|
||||
|
||||
on-open (mf/use-fn
|
||||
(fn [color]
|
||||
(reset! prev-color* color)))
|
||||
(when from-picker?
|
||||
(reset! prev-color* new-color))
|
||||
|
||||
(st/emit! (dc/change-color-in-selected new-color shapes-by-color (or prev-color old-color))))))
|
||||
|
||||
on-open
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(reset! prev-color* nil)))
|
||||
|
||||
on-close
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(reset! prev-color* nil)))
|
||||
|
||||
on-detach
|
||||
(mf/use-fn
|
||||
|
@ -212,8 +222,9 @@
|
|||
:index index
|
||||
:on-detach on-detach
|
||||
:select-only select-only
|
||||
:on-change #(on-change % color)
|
||||
:on-open on-open}])
|
||||
:on-change #(on-change %1 color %2)
|
||||
:on-open on-open
|
||||
:on-close on-close}])
|
||||
(when (and (false? @expand-lib-color) (< 3 (count library-colors)))
|
||||
[:div.expand-colors {:on-click #(reset! expand-lib-color true)}
|
||||
[:span i/actions]
|
||||
|
@ -225,8 +236,9 @@
|
|||
:index index
|
||||
:on-detach on-detach
|
||||
:select-only select-only
|
||||
:on-change #(on-change % color)
|
||||
:on-open on-open}]))]
|
||||
:on-change #(on-change %1 color %2)
|
||||
:on-open on-open
|
||||
:on-close on-close}]))]
|
||||
|
||||
[:div.selected-colors
|
||||
(for [[index color] (d/enumerate (take 3 colors))]
|
||||
|
@ -234,8 +246,9 @@
|
|||
:color color
|
||||
:index index
|
||||
:select-only select-only
|
||||
:on-change #(on-change % color)
|
||||
:on-open on-open}])
|
||||
:on-change #(on-change %1 color %2)
|
||||
:on-open on-open
|
||||
:on-close on-close}])
|
||||
(when (and (false? @expand-color) (< 3 (count colors)))
|
||||
[:div.expand-colors {:on-click #(reset! expand-color true)}
|
||||
[:span i/actions]
|
||||
|
@ -246,5 +259,6 @@
|
|||
:color color
|
||||
:index index
|
||||
:select-only select-only
|
||||
:on-change #(on-change % color)
|
||||
:on-open on-open}]))]]])))
|
||||
:on-change #(on-change %1 color %2)
|
||||
:on-open on-open
|
||||
:on-close on-close}]))]]])))
|
||||
|
|
|
@ -113,7 +113,8 @@
|
|||
:y y
|
||||
:disable-gradient disable-gradient
|
||||
:disable-opacity disable-opacity
|
||||
:on-change #(on-change (merge uc/empty-color %))
|
||||
;; on-change second parameter means if the source is the color-picker
|
||||
:on-change #(on-change (merge uc/empty-color %) true)
|
||||
:on-close (fn [value opacity id file-id]
|
||||
(when on-close
|
||||
(on-close value opacity id file-id)))
|
||||
|
|
|
@ -46,4 +46,5 @@
|
|||
(defn camelize
|
||||
[str]
|
||||
;; str.replace(":", "-").replace(/-./g, x=>x[1].toUpperCase())
|
||||
(js* "~{}.replace(\":\", \"-\").replace(/-./g, x=>x[1].toUpperCase())", str))
|
||||
(when (not (nil? str))
|
||||
(js* "~{}.replace(\":\", \"-\").replace(/-./g, x=>x[1].toUpperCase())", str)))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue