0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-11 23:31:21 -05:00

Add new handoff flex item section"

This commit is contained in:
Eva 2022-10-24 13:22:31 +02:00 committed by Alonso Torres
parent c3fe8c8ebd
commit d663d2bebf
12 changed files with 381 additions and 55 deletions

View file

@ -11,7 +11,7 @@
[app.common.geom.shapes.rect :as gre]))
;; :layout ;; true if active, false if not
;; :layout-dir ;; :right, :left, :top, :bottom
;; :layout-flex-dir ;; :row, :column, :reverse-row, :reverse-column
;; :layout-gap ;; number could be negative
;; :layout-type ;; :packed, :space-between, :space-around
;; :layout-wrap-type ;; :wrap, :no-wrap
@ -21,12 +21,12 @@
;; :layout-v-orientation ;; :left, :center, :right
(defn col?
[{:keys [layout-dir]}]
(or (= :right layout-dir) (= :left layout-dir)))
[{:keys [layout-flex-dir]}]
(or (= :column layout-flex-dir) (= :reverse-column layout-flex-dir)))
(defn row?
[{:keys [layout-dir]}]
(or (= :top layout-dir) (= :bottom layout-dir)))
[{:keys [layout-flex-dir]}]
(or (= :row layout-flex-dir) (= :reverse-row layout-flex-dir)))
(defn h-start?
[{:keys [layout-h-orientation]}]
@ -247,9 +247,9 @@
(defn calc-layout-data
"Digest the layout data to pass it to the constrains"
[{:keys [layout-dir] :as shape} children layout-bounds]
[{:keys [layout-flex-dir] :as shape} children layout-bounds]
(let [reverse? (or (= :left layout-dir) (= :bottom layout-dir))
(let [reverse? (or (= :reverse-row layout-flex-dir) (= :reverse-column layout-flex-dir))
layout-bounds (-> layout-bounds (add-padding shape))
children (cond->> children reverse? reverse)
layout-lines

View file

@ -87,7 +87,7 @@
position: relative;
display: flex;
flex-direction: row;
padding: 1rem 1.6rem 1rem 0.5rem;
padding: 0.6rem 1.6rem 0.6rem 0.5rem;
.attributes-label,
.attributes-value {
@ -96,9 +96,12 @@
text-overflow: ellipsis;
white-space: nowrap;
width: 50%;
.items {
margin-right: 5px;
}
}
.copy-button {
padding: 1rem 0.5rem;
padding: 0.6rem 0.5rem;
margin-top: 0.25rem;
}
}

View file

@ -16,34 +16,29 @@
(def layout-keys
[:layout
:layout-dir
:layout-flex-dir
:layout-gap-type
:layout-gap
:layout-type
:layout-align-items
:layout-justify-content
:layout-align-content
:layout-wrap-type
:layout-padding-type
:layout-padding
:layout-h-orientation
:layout-v-orientation
:layout-align-content
:layout-flex-dir
:layout-align-items
:layout-justify-content
:layout-gap-type
])
(def initial-flex-layout
{:layout :flex
:layout-flex-dir :row
:layout-gap-type :simple
:layout-gap {:row-gap 0 :column-gap 0}
:layout-align-items :start
{:layout :flex
:layout-flex-dir :row
:layout-gap-type :simple
:layout-gap {:row-gap 0 :column-gap 0}
:layout-align-items :start
:layout-justify-content :start
:layout-align-content :strech
:layout-wrap-type :no-wrap
:layout-padding-type :simple
:layout-padding {:p1 0 :p2 0 :p3 0 :p4 0}})
:layout-align-content :strech
:layout-wrap-type :no-wrap
:layout-padding-type :simple
:layout-padding {:p1 0 :p2 0 :p3 0 :p4 0}})
(def initial-grid-layout ;; TODO
{:layout :grid})

View file

@ -28,6 +28,10 @@
([state page-id]
(dm/get-in state [:workspace-data :pages-index page-id :objects])))
(defn lookup-viewer-objects
([state page-id]
(dm/get-in state [:viewer :pages page-id :objects])))
(defn lookup-page-options
([state]
(lookup-page-options state (:current-page-id state)))

View file

@ -363,6 +363,10 @@
;; ---- Viewer refs
(defn lookup-viewer-objects-by-id
[page-id]
(l/derived #(wsh/lookup-viewer-objects % page-id) st/state =))
(def viewer-data
(l/derived :viewer st/state))
@ -427,5 +431,13 @@
(some #(-> (cph/get-parent objects %) :layout))))
workspace-page-objects))
(defn get-flex-child-viewer?
[ids page-id]
(l/derived
(fn [state]
(let [objects (wsh/lookup-viewer-objects state page-id)]
(filterv #(= :flex (:layout (cph/get-parent objects %))) ids)))
st/state =))
(def colorpicker
(l/derived :colorpicker st/state))

View file

@ -41,3 +41,23 @@
(when (d/num? value)
(let [value (mth/precision value 0)]
(dm/str value))))
(defn format-padding-margin-shorthand
[values]
;; Values come in [p1 p2 p3 p4]
(let [[p1 p2 p3 p4] values]
(cond
(apply = values)
{:p1 p1}
(= 4 (count (set values)))
{:p1 p1 :p2 p2 :p3 p3}
(and (= p1 p3) (= p2 p4))
{:p1 p1 :p3 p3}
(and (not= p1 p3) (= p2 p4))
{:p1 p1 :p2 p2 :p3 p3}
:else
{:p1 p1 :p2 p2 :p3 p3})))

View file

@ -12,6 +12,8 @@
[app.main.ui.viewer.handoff.attributes.fill :refer [fill-panel]]
[app.main.ui.viewer.handoff.attributes.image :refer [image-panel]]
[app.main.ui.viewer.handoff.attributes.layout :refer [layout-panel]]
[app.main.ui.viewer.handoff.attributes.layout-flex :refer [layout-flex-panel]]
[app.main.ui.viewer.handoff.attributes.layout-flex-element :refer [layout-flex-element-panel]]
[app.main.ui.viewer.handoff.attributes.shadow :refer [shadow-panel]]
[app.main.ui.viewer.handoff.attributes.stroke :refer [stroke-panel]]
[app.main.ui.viewer.handoff.attributes.svg :refer [svg-panel]]
@ -20,14 +22,14 @@
[rumext.v2 :as mf]))
(def type->options
{:multiple [:fill :stroke :image :text :shadow :blur]
:frame [:layout :fill :stroke :shadow :blur]
:group [:layout :svg]
:rect [:layout :fill :stroke :shadow :blur :svg]
:circle [:layout :fill :stroke :shadow :blur :svg]
{:multiple [:fill :stroke :image :text :shadow :blur :layout-flex-item]
:frame [:layout :fill :stroke :shadow :blur :layout-flex :layout-flex-item]
:group [:layout :svg :layout-flex-item]
:rect [:layout :fill :stroke :shadow :blur :svg :layout-flex-item]
:circle [:layout :fill :stroke :shadow :blur :svg :layout-flex-item]
:path [:layout :fill :stroke :shadow :blur :svg]
:image [:image :layout :fill :stroke :shadow :blur :svg]
:text [:layout :text :shadow :blur :stroke]})
:image [:image :layout :fill :stroke :shadow :blur :svg :layout-flex-item]
:text [:layout :text :shadow :blur :stroke :layout-flex-item]})
(mf/defc attributes
[{:keys [page-id file-id shapes frame]}]
@ -39,14 +41,16 @@
[:div.element-options
(for [option options]
[:> (case option
:layout layout-panel
:fill fill-panel
:stroke stroke-panel
:shadow shadow-panel
:blur blur-panel
:image image-panel
:text text-panel
:svg svg-panel)
:layout layout-panel
:layout-flex layout-flex-panel
:layout-flex-item layout-flex-element-panel
:fill fill-panel
:stroke stroke-panel
:shadow shadow-panel
:blur blur-panel
:image image-panel
:text text-panel
:svg svg-panel)
{:shapes shapes
:frame frame}])
[:& exports

View file

@ -91,4 +91,5 @@
[:& copy-button {:data (copy-data (first shapes))}])]
(for [shape shapes]
[:& layout-block {:shape shape}])])
[:& layout-block {:shape shape
:key (:id shape)}])])

View file

@ -0,0 +1,152 @@
;; 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.viewer.handoff.attributes.layout-flex
(:require
[app.common.data :as d]
[app.main.ui.components.copy-button :refer [copy-button]]
[app.main.ui.formats :as fm]
[app.util.code-gen :as cg]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
(defn format-gap
[gap-values]
(let [row-gap (:row-gap gap-values)
column-gap (:column-gap gap-values)]
(if (= row-gap column-gap)
(str/fmt "%spx" row-gap)
(str/fmt "%spx %spx" row-gap column-gap))))
(defn format-padding
[padding-values]
(let [short-hand (fm/format-padding-margin-shorthand (vals padding-values))
parsed-values (map #(str/fmt "%spx" %) (vals short-hand))]
(str/join " " parsed-values)))
(def properties [:layout
:layout-flex-dir
:layout-align-items
:layout-justify-content
:layout-gap
:layout-padding
:layout-wrap-type])
(def align-contet-prop [:layout-align-content])
(def layout-flex-params
{:props [:layout
:layout-align-items
:layout-flex-dir
:layout-justify-content
:layout-gap
:layout-padding
:layout-wrap-type]
:to-prop {:layout "display"
:layout-flex-dir "flex-direction"
:layout-align-items "align-items"
:layout-justify-content "justify-content"
:layout-wrap-type "wrap"
:layout-gap "gap"
:layout-padding "padding"}
:format {:layout name
:layout-flex-dir name
:layout-align-items name
:layout-justify-content name
:layout-wrap-type name
:layout-gap format-gap
:layout-padding format-padding}})
(def layout-align-content-params
{:props [:layout-align-content]
:to-prop {:layout-align-content "align-content"}
:format {:layout-align-content name}})
(defn copy-data
([shape]
(let [properties-for-copy (if (:layout-align-content shape)
(into [] (concat properties align-contet-prop))
properties)]
(apply copy-data shape properties-for-copy)))
([shape & properties]
(let [params (if (:layout-align-content shape)
(d/deep-merge layout-align-content-params layout-flex-params )
layout-flex-params)]
(cg/generate-css-props shape properties params))))
(mf/defc manage-padding
[{:keys [padding type]}]
(let [values (fm/format-padding-margin-shorthand (vals padding))]
[:div.attributes-value
(for [[k v] values]
[:span.items {:key (str type "-" k "-" v)} v "px"])]))
(mf/defc layout-block
[{:keys [shape]}]
[:*
[:div.attributes-unit-row
[:div.attributes-label "Display"]
[:div.attributes-value "Flex"]
[:& copy-button {:data (copy-data shape)}]]
[:div.attributes-unit-row
[:div.attributes-label "Direction"]
[:div.attributes-value (str/capital (d/name (:layout-flex-dir shape)))]
[:& copy-button {:data (copy-data shape :layout-flex-dir)}]]
[:div.attributes-unit-row
[:div.attributes-label "Align-items"]
[:div.attributes-value (str/capital (d/name (:layout-align-items shape)))]
[:& copy-button {:data (copy-data shape :layout-align-items)}]]
[:div.attributes-unit-row
[:div.attributes-label "Justify-content"]
[:div.attributes-value (str/capital (d/name (:layout-justify-content shape)))]
[:& copy-button {:data (copy-data shape :layout-justify-content)}]]
[:div.attributes-unit-row
[:div.attributes-label "Wrap"]
[:div.attributes-value (str/capital (d/name (:layout-wrap-type shape)))]
[:& copy-button {:data (copy-data shape :layout-wrap-type)}]]
(when (= :wrap (:layout-wrap-type shape))
[:div.attributes-unit-row
[:div.attributes-label "Align-content"]
[:div.attributes-value (str/capital (d/name (:layout-align-content shape)))]
[:& copy-button {:data (copy-data shape :layout-align-content)}]])
[:div.attributes-unit-row
[:div.attributes-label "Gap"]
(if (= (:row-gap (:layout-gap shape)) (:column-gap (:layout-gap shape)))
[:div.attributes-value
[:span (str/capital (d/name (:row-gap (:layout-gap shape)))) "px"]]
[:div.attributes-value
[:span.items (:row-gap (:layout-gap shape)) "px"]
[:span (:column-gap (:layout-gap shape)) "px"]])
[:& copy-button {:data (copy-data shape :layout-gap)}]]
[:div.attributes-unit-row
[:div.attributes-label "Padding"]
[:& manage-padding {:padding (:layout-padding shape) :type "padding"}]
[:& copy-button {:data (copy-data shape :layout-padding)}]]])
(defn has-flex? [shape]
(= :flex (:layout shape)))
(mf/defc layout-flex-panel
[{:keys [shapes]}]
(let [shapes (->> shapes (filter has-flex?))]
(when (seq shapes)
[:div.attributes-block
[:div.attributes-block-title
[:div.attributes-block-title-text "Layout"]
(when (= (count shapes) 1)
[:& copy-button {:data (copy-data (first shapes))}])]
(for [shape shapes]
[:& layout-block {:shape shape
:key (:id shape)}])])))

View file

@ -0,0 +1,133 @@
;; 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.viewer.handoff.attributes.layout-flex-element
(:require
[app.common.data :as d]
[app.main.refs :as refs]
[app.main.ui.components.copy-button :refer [copy-button]]
[app.main.ui.formats :as fmt]
[app.main.ui.hooks :as hooks]
[app.util.code-gen :as cg]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
(defn format-margin
[margin-values]
(let [short-hand (fmt/format-padding-margin-shorthand (vals margin-values))
parsed-values (map #(str/fmt "%spx" %) (vals short-hand))]
(str/join " " parsed-values)))
(def properties [:layout-item-margin ;; {:m1 0 :m2 0 :m3 0 :m4 0}
:layout-item-h-sizing ;; :fill-width :fix-width :auto-width
:layout-item-v-sizing ;; :fill-height :fix-height :auto-height
:layout-item-max-h ;; num
:layout-item-min-h ;; num
:layout-item-max-w ;; num
:layout-item-min-w ;; num
:layout-item-align-self ;; :start :end :center :strech :baseline
])
(def layout-flex-item-params
{:props [:layout-item-margin
:layout-item-h-sizing
:layout-item-v-sizing
:layout-item-max-h
:layout-item-min-h
:layout-item-max-w
:layout-item-min-w
:layout-item-align-self]
:to-prop {:layout-item-margin "margin"
:layout-item-h-sizing "width"
:layout-item-v-sizing "height"
:layout-item-align-self "align-self"
:layout-item-max-h "max. height"
:layout-item-min-h "min. height"
:layout-item-max-w "max. width"
:layout-item-min-w "min. width"}
:format {:layout-item-margin format-margin
:layout-item-h-sizing name
:layout-item-v-sizing name
:layout-item-align-self name}})
(defn copy-data
([shape]
(apply copy-data shape properties))
([shape & properties]
(cg/generate-css-props shape properties layout-flex-item-params)))
(mf/defc manage-margin
[{:keys [margin type]}]
(let [values (fmt/format-padding-margin-shorthand (vals margin))]
[:div.attributes-value
(for [[k v] values]
[:span.items {:key (str type "-" k "-" v)} v "px"])]))
(mf/defc layout-element-block
[{:keys [shape]}]
[:*
[:div.attributes-unit-row
[:div.attributes-label "Width"]
[:div.attributes-value (str/capital (d/name (:layout-item-h-sizing shape)))]
[:& copy-button {:data (copy-data shape :layout-item-h-sizing)}]]
[:div.attributes-unit-row
[:div.attributes-label "Height"]
[:div.attributes-value (str/capital (d/name (:layout-item-v-sizing shape)))]
[:& copy-button {:data (copy-data shape :layout-item-v-sizing)}]]
[:div.attributes-unit-row
[:div.attributes-label "Align self"]
[:div.attributes-value (str/capital (d/name (:layout-item-align-self shape)))]
[:& copy-button {:data (copy-data shape :layout-item-align-self)}]]
[:div.attributes-unit-row
[:div.attributes-label "Margin"]
[:& manage-margin {:margin (:layout-item-margin shape) :type "margin"}]
[:& copy-button {:data (copy-data shape :layout-item-margin)}]]
[:div.attributes-unit-row
[:div.attributes-label "Max. width"]
[:div.attributes-value (fmt/format-pixels (:layout-item-max-w shape))]
[:& copy-button {:data (copy-data shape :layout-item-max-w)}]]
[:div.attributes-unit-row
[:div.attributes-label "Min. width"]
[:div.attributes-value (fmt/format-pixels (:layout-item-min-w shape))]
[:& copy-button {:data (copy-data shape :layout-item-min-w)}]]
[:div.attributes-unit-row
[:div.attributes-label "Max. height"]
[:div.attributes-value (fmt/format-pixels (:layout-item-max-h shape))]
[:& copy-button {:data (copy-data shape :layout-item-max-h)}]]
[:div.attributes-unit-row
[:div.attributes-label "Min. height"]
[:div.attributes-value (fmt/format-pixels (:layout-item-min-w shape))]
[:& copy-button {:data (copy-data shape :layout-item-min-h)}]]])
(defn get-flex-elements [page-id shapes]
(let [ids (mapv :id shapes)
ids (hooks/use-equal-memo ids)
get-layout-children-refs (mf/use-memo (mf/deps ids page-id) #(refs/get-flex-child-viewer? ids page-id))]
(mf/deref get-layout-children-refs)))
(mf/defc layout-flex-element-panel
[{:keys [shapes]}]
(let [route (mf/deref refs/route)
page-id (:page-id (:query-params route))
shapes (get-flex-elements page-id shapes)]
(when (and (= (count shapes) 1) (seq shapes))
[:div.attributes-block
[:div.attributes-block-title
[:div.attributes-block-title-text "Flex element"]
[:& copy-button {:data (copy-data (first shapes))}]]
[:& layout-element-block {:shape (first shapes)}]])))

View file

@ -18,14 +18,16 @@
[rumext.v2 :as mf]))
(def layout-item-attrs
[:layout-margin ;; {:m1 0 :m2 0 :m3 0 :m4 0}
:layout-margin-type ;; :simple :multiple
:layout-h-behavior ;; :fill :fix :auto
:layout-v-behavior ;; :fill :fix :auto
:layout-max-h ;; num
:layout-min-h ;; num
:layout-max-w ;; num
:layout-min-w ]) ;; num
[:layout-item-margin ;; {:m1 0 :m2 0 :m3 0 :m4 0}
:layout-item-margin-type ;; :simple :multiple
:layout-item-h-sizing ;; :fill :fix :auto
:layout-item-v-sizing ;; :fill :fix :auto
:layout-item-max-h ;; num
:layout-item-min-h ;; num
:layout-item-max-w ;; num
:layout-item-min-w ;; num
:layout-item-align-self ;; :start :end :center :strech :baseline
])
(mf/defc margin-section
[{:keys [values change-margin-style on-margin-change] :as props}]

View file

@ -55,9 +55,9 @@
:shadow {:props [:shadow]
:to-prop {:shadow :box-shadow}
:format {:shadow #(str/join ", " (map shadow->css %1))}}
:blur {:props [:blur]
:to-prop {:blur "filter"}
:format {:blur #(str/fmt "blur(%spx)" (:value %))}}})
:blur {:props [:blur]
:to-prop {:blur "filter"}
:format {:blur #(str/fmt "blur(%spx)" (:value %))}}})
(def style-text
{:props [:fill-color