🎉 Add new flex layout menu

This commit is contained in:
Eva 2022-10-03 09:50:25 +02:00 committed by Alonso Torres
parent 5463671db1
commit e16da8bd2d
63 changed files with 955 additions and 586 deletions

View file

@ -23,18 +23,30 @@
:layout-padding-type
:layout-padding
:layout-h-orientation
:layout-v-orientation])
:layout-v-orientation
(def initial-layout
{:layout true
:layout-dir :left
:layout-gap 0
:layout-type :packed
:layout-wrap-type :wrap
: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-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-h-orientation :left
:layout-v-orientation :top})
:layout-padding {:p1 0 :p2 0 :p3 0 :p4 0}})
(def initial-grid-layout ;; TODO
{:layout :grid})
(defn update-layout-positions
[ids]
@ -50,12 +62,16 @@
;; TODO: Remove constraints from children
(defn create-layout
[ids]
[ids type]
(ptk/reify ::create-layout
ptk/WatchEvent
(watch [_ _ _]
(rx/of (dwc/update-shapes ids #(merge % initial-layout))
(update-layout-positions ids)))))
(if (= type :flex)
(rx/of (dwc/update-shapes ids #(merge % initial-flex-layout))
(update-layout-positions ids))
(rx/of (dwc/update-shapes ids #(merge % initial-grid-layout))
(update-layout-positions ids))))))
(defn remove-layout
[ids]

View file

@ -14,6 +14,36 @@
(def action (icon-xref :action))
(def actions (icon-xref :actions))
(def align-bottom (icon-xref :align-bottom))
(def align-content-column-around (icon-xref :align-content-column-around))
(def align-content-column-between (icon-xref :align-content-column-between))
(def align-content-column-center (icon-xref :align-content-column-center))
(def align-content-column-end (icon-xref :align-content-column-end))
(def align-content-column-start (icon-xref :align-content-column-start))
(def align-content-row-around (icon-xref :align-content-row-around))
(def align-content-row-between (icon-xref :align-content-row-between))
(def align-content-row-center (icon-xref :align-content-row-center))
(def align-content-row-end (icon-xref :align-content-row-end))
(def align-content-row-start (icon-xref :align-content-row-start))
(def align-items-column-baseline (icon-xref :align-items-column-baseline))
(def align-items-column-center (icon-xref :align-items-column-center))
(def align-items-column-end (icon-xref :align-items-column-end))
(def align-items-column-start (icon-xref :align-items-column-start))
(def align-items-column-strech (icon-xref :align-items-column-strech))
(def align-items-row-baseline (icon-xref :align-items-row-baseline))
(def align-items-row-center (icon-xref :align-items-row-center))
(def align-items-row-end (icon-xref :align-items-row-end))
(def align-items-row-start (icon-xref :align-items-row-start))
(def align-items-row-strech (icon-xref :align-items-row-strech))
(def align-self-column-baseline (icon-xref :align-self-column-baseline))
(def align-self-column-center (icon-xref :align-self-column-center))
(def align-self-column-top (icon-xref :align-self-column-top))
(def align-self-column-bottom (icon-xref :align-self-column-bottom))
(def align-self-column-strech (icon-xref :align-self-column-strech))
(def align-self-row-baseline (icon-xref :align-self-row-baseline))
(def align-self-row-center (icon-xref :align-self-row-center))
(def align-self-row-left (icon-xref :align-self-row-left))
(def align-self-row-right (icon-xref :align-self-row-right))
(def align-self-row-strech (icon-xref :align-self-row-strech))
(def align-middle (icon-xref :align-middle))
(def align-top (icon-xref :align-top))
(def alignment (icon-xref :alignment))
@ -39,6 +69,7 @@
(def auto-padding (icon-xref :auto-padding))
(def auto-padding-side (icon-xref :auto-padding-side))
(def auto-width (icon-xref :auto-width))
(def auto-wrap (icon-xref :auto-wrap))
(def bool-difference (icon-xref :boolean-difference))
(def bool-exclude (icon-xref :boolean-exclude))
(def bool-flatten (icon-xref :boolean-flatten))
@ -91,6 +122,16 @@
(def import (icon-xref :import))
(def infocard (icon-xref :infocard))
(def interaction (icon-xref :interaction))
(def justify-content-column-around (icon-xref :justify-content-column-around))
(def justify-content-column-between (icon-xref :justify-content-column-between))
(def justify-content-column-center (icon-xref :justify-content-column-center))
(def justify-content-column-end (icon-xref :justify-content-column-end))
(def justify-content-column-start (icon-xref :justify-content-column-start))
(def justify-content-row-around (icon-xref :justify-content-row-around))
(def justify-content-row-between (icon-xref :justify-content-row-between))
(def justify-content-row-center (icon-xref :justify-content-row-center))
(def justify-content-row-end (icon-xref :justify-content-row-end))
(def justify-content-row-start (icon-xref :justify-content-row-start))
(def layers (icon-xref :layers))
(def letter-spacing (icon-xref :letter-spacing))
(def libraries (icon-xref :libraries))

View file

@ -86,7 +86,7 @@
[{:keys [shapes]}]
[:div.attributes-block
[:div.attributes-block-title
[:div.attributes-block-title-text (tr "handoff.attributes.layout")]
[:div.attributes-block-title-text (tr "handoff.attributes.size")]
(when (= (count shapes) 1)
[:& copy-button {:data (copy-data (first shapes))}])]

View file

@ -5,63 +5,92 @@
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.workspace.sidebar.options.menus.layout-container
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.store :as st]
[app.main.ui.components.numeric-input :refer [numeric-input]]
[app.main.ui.icons :as i]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
(:require [app.common.data :as d]
[app.common.data.macros :as dm]
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.store :as st]
[app.main.ui.components.numeric-input :refer [numeric-input]]
[app.main.ui.icons :as i]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
(def layout-container-attrs
[:layout ;; true if active, false if not
:layout-dir ;; :right, :left, :top, :bottom
:layout-gap ;; number could be negative
:layout-type ;; :packed, :space-between, :space-around
(def layout-container-flex-attrs
[:layout ;; :flex, :grid in the future
:layout-flex-dir ;; :row, :reverse-row, :column, :reverse-column
:layout-gap-type ;; :simple, :multiple
:layout-gap ;; {:row-gap number , :column-gap number}
:layout-align-items ;; :start :end :center :strech
:layout-justify-content ;; :start :center :end :space-between :space-around
:layout-align-content ;; :start :center :end :space-between :space-around :strech (by default)
:layout-wrap-type ;; :wrap, :no-wrap
:layout-padding-type ;; :simple, :multiple
:layout-padding ;; {:p1 num :p2 num :p3 num :p4 num} number could be negative
:layout-h-orientation ;; :left, :center, :right
:layout-v-orientation]) ;; :top, :center, :bottom
])
(def grid-pos [[:top :left]
[:top :center]
[:top :right]
[:center :left]
[:center :center]
[:center :right]
[:bottom :left]
[:bottom :center]
[:bottom :right]])
(defn get-layout-flex-icon
[type val is-col?]
(case type
:align-items (if is-col?
(case val
:start i/align-items-column-start
:end i/align-items-column-end
:center i/align-items-column-center
:strech i/align-items-column-strech
:baseline i/align-items-column-baseline)
(case val
:start i/align-items-row-start
:end i/align-items-row-end
:center i/align-items-row-center
:strech i/align-items-row-strech
:baseline i/align-items-row-baseline))
:justify-content (if is-col?
(case val
:start i/justify-content-column-start
:end i/justify-content-column-end
:center i/justify-content-column-center
:space-around i/justify-content-column-around
:space-between i/justify-content-column-between)
(case val
:start i/justify-content-row-start
:end i/justify-content-row-end
:center i/justify-content-row-center
:space-around i/justify-content-row-around
:space-between i/justify-content-row-between))
(def grid-rows [:top :center :bottom])
(def grid-cols [:left :center :right])
:align-content (if is-col?
(case val
:start i/align-content-column-start
:end i/align-content-column-end
:center i/align-content-column-center
:space-around i/align-content-column-around
:space-between i/align-content-column-between
:strech nil)
(case val
:start i/align-content-row-start
:end i/align-content-row-end
:center i/align-content-row-center
:space-around i/align-content-row-around
:space-between i/align-content-row-between
:strech nil))
(defn- get-layout-icon
[dir layout-type v h]
(let [row? (or (= dir :right) (= dir :left))
manage-text-icon
(if row?
(case v
:top i/text-align-left
:center i/text-align-center
:bottom i/text-align-right
i/text-align-center)
(case h
:left i/text-align-left
:center i/text-align-center
:right i/text-align-right
i/text-align-center))]
(case layout-type
:packed manage-text-icon
:space-around i/space-around
:space-between i/space-between)))
:align-self (if is-col?
(case val
:start i/align-self-column-top
:end i/align-self-column-bottom
:center i/align-self-column-center
:strech i/align-self-column-strech
:baseline i/align-self-column-baseline)
(case val
:start i/align-self-row-left
:end i/align-self-row-right
:center i/align-self-row-center
:strech i/align-self-row-strech
:baseline i/align-self-row-baseline))))
(mf/defc direction-row
(mf/defc direction-btn
[{:keys [dir saved-dir set-direction] :as props}]
(let [handle-on-click
(mf/use-callback
@ -71,283 +100,331 @@
(set-direction dir))))]
[:button.dir.tooltip.tooltip-bottom
{:class (dom/classnames :active (= saved-dir dir)
:left (= :left dir)
:right (= :right dir)
:top (= :top dir)
:bottom (= :bottom dir))
{:class (dom/classnames :active (= saved-dir dir)
:row (= :row dir)
:reverse-row (= :reverse-row dir)
:reverse-column (= :reverse-column dir)
:column (= :column dir))
:key (dm/str "direction-" dir)
;; Execution time translation strings:
;; workspace.options.layout.direction.bottom
;; workspace.options.layout.direction.left
;; workspace.options.layout.direction.right
;; workspace.options.layout.direction.top
:alt (tr (dm/str "workspace.options.layout.direction." (d/name dir)))
:alt (str/replace (str/capital (d/name dir)) "-" " ")
:on-click handle-on-click}
i/auto-direction]))
(mf/defc orientation-grid
[{:keys [on-change-orientation values] :as props}]
(let [dir (:layout-dir values)
type (:layout-type values)
is-col? (or (= dir :top)
(= dir :bottom))
saved-pos [(:layout-v-orientation values)
(:layout-h-orientation values)]]
(mf/defc wrap-row
[{:keys [wrap-type set-wrap] :as props}]
[:*
[:button.tooltip.tooltip-bottom
{:class (dom/classnames :active (= wrap-type :no-wrap))
:alt "No-wrap"
:on-click #(set-wrap :no-wrap)
:style {:padding 0}}
[:span.no-wrap i/minus]]
[:button.wrap.tooltip.tooltip-bottom
{:class (dom/classnames :active (= wrap-type :wrap))
:alt "wrap"
:on-click #(set-wrap :wrap)}
i/auto-wrap]])
(if (= type :packed)
[:div.orientation-grid
[:div.button-wrapper
(for [[pv ph] grid-pos]
[:button.orientation
{:on-click (partial on-change-orientation pv ph type)
:class (dom/classnames
:active (= [pv ph] saved-pos)
:top (= :top pv)
:center (= :center pv)
:bottom (= :bottom pv)
:left (= :left ph)
:center (= :center ph)
:right (= :right ph))
:key (dm/str pv ph)}
[:span.icon
{:class (dom/classnames
:rotated (not is-col?))}
(get-layout-icon dir type pv ph)]])]]
(if (not is-col?)
[:div.orientation-grid.row
[:div.button-wrapper
(for [row grid-rows]
[:button.orientation
{:on-click (partial on-change-orientation row :left type)
:class (dom/classnames
:active (= row (first saved-pos))
:top (= :top row)
:centered (= :center row)
:bottom (= :bottom row))}
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type nil row)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type nil row)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type nil row)]])]]
(mf/defc align-row
[{:keys [is-col? align-items set-align] :as props}]
[:div.align-items-style
(for [align [:start :center :end :strech]]
[:button.align-start.tooltip
{:class (dom/classnames :active (= align-items align)
:tooltip-bottom-left (not= align :start)
:tooltip-bottom (= align :start))
:alt (dm/str "Align items " (d/name align))
:on-click #(set-align align)
:key (dm/str "align-items" (d/name align))}
(get-layout-flex-icon :align-items align is-col?)])])
(mf/defc align-content-row
[{:keys [is-col? align-content set-align-content] :as props}]
[:div.align-content-style
(for [align [:start :center :end :space-around :space-between]]
[:button.align-content.tooltip
{:class (dom/classnames :active (= align-content align)
:tooltip-bottom-left (not= align :start)
:tooltip-bottom (= align :start))
:alt (dm/str "Align content " (d/name align))
:on-click #(set-align-content align)
:key (dm/str "align-content" (d/name align))}
(get-layout-flex-icon :align-content align is-col?)])])
(mf/defc justify-content-row
[{:keys [is-col? justify-content set-justify] :as props}]
[:div.justify-content-style
(for [justify [:start :center :end :space-around :space-between]]
[:button.justify.tooltip
{:class (dom/classnames :active (= justify-content justify)
:tooltip-bottom-left (not= justify :start)
:tooltip-bottom (= justify :start))
:alt (dm/str "Justify content " (d/name justify))
:on-click #(set-justify justify)
:key (dm/str "justify-content" (d/name justify))}
(get-layout-flex-icon :justify-content justify is-col?)])])
[:div.orientation-grid.col
[:div.button-wrapper
(for [[idx col] (d/enumerate grid-cols)]
[:button.orientation
{:key (dm/str idx col)
:on-click (partial on-change-orientation :top col type)
:class (dom/classnames
:active (= col (second saved-pos))
:top (= :left col)
:centered (= :center col)
:bottom (= :right col))}
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type col nil)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type col nil)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type col nil)]])]]))))
(mf/defc padding-section
[{:keys [values on-change-style on-change] :as props}]
(let [padding-type (:layout-padding-type values)]
(let [padding-type (:layout-padding-type values)
rx (if (apply = (vals (:layout-padding values)))
(:p1 (:layout-padding values))
"--")]
[:div.row-flex
[:div.padding-options
[:div.padding-row
[:div.padding-icons
[:div.padding-icon.tooltip.tooltip-bottom
{:class (dom/classnames :selected (= padding-type :simple))
:alt (tr "workspace.options.layout.padding-simple")
:alt "Padding"
:on-click #(on-change-style :simple)}
i/auto-padding]
[:div.padding-icon.tooltip.tooltip-bottom
{:class (dom/classnames :selected (= padding-type :multiple))
:alt (tr "workspace.options.layout.padding")
:alt "Padding - sides"
:on-click #(on-change-style :multiple)}
i/auto-padding-side]]
[:div.wrapper
(cond
(= padding-type :simple)
[:div.tooltip.tooltip-bottom
{:alt (tr "workspace.options.layout.padding-all")}
[:div.input-element.mini
(cond
(= padding-type :simple)
[:div.tooltip.tooltip-bottom
{:alt (tr "workspace.options.layout.padding-all")}
[:div.input-element.mini
[:> numeric-input
{:placeholder "--"
:on-click #(dom/select-target %)
:on-change (partial on-change :simple)
:value rx}]]]
[:> numeric-input
{:placeholder "--"
:on-click #(dom/select-target %)
:on-change (partial on-change :simple)
:value (:p1 (:layout-padding values))}]]]
(= padding-type :multiple)
(for [num [:p1 :p2 :p3 :p4]]
[:div.tooltip.tooltip-bottom
{:key (dm/str "padding-" (d/name num))
:alt (case num
:p1 "Top"
:p2 "Right"
:p3 "Bottom"
:p4 "Left")}
[:div.input-element.mini
[:> numeric-input
{:placeholder "--"
:on-click #(dom/select-target %)
:on-change (partial on-change num)
:value (num (:layout-padding values))}]]]))]]))
(= padding-type :multiple)
(for [num [:p1 :p2 :p3 :p4]]
[:div.tooltip.tooltip-bottom
{:key (dm/str "padding-" (d/name num))
:alt (case num
:p1 (tr "workspace.options.layout.top")
:p2 (tr "workspace.options.layout.right")
:p3 (tr "workspace.options.layout.bottom")
:p4 (tr "workspace.options.layout.left"))}
[:div.input-element.mini
[:> numeric-input
{:placeholder "--"
:on-click #(dom/select-target %)
:on-change (partial on-change num)
:value (num (:layout-padding values))}]]]))]))
(mf/defc gap-section
[{:keys [gap-selected? set-gap gap-value toggle-gap-type gap-type]}]
(let [gap-locked? (= gap-type :simple)
some-gap-selected (not= @gap-selected? :none)
is-row-activated? (or (and (not gap-locked?) (= @gap-selected? :row-gap)) (and gap-locked? some-gap-selected))
is-column-activated? (or (and (not gap-locked?) (= @gap-selected? :column-gap)) (and gap-locked? some-gap-selected))]
[:div.gap-group
[:div.gap-row.tooltip.tooltip-bottom-left
{:alt "Row gap"}
[:span.icon
{:class (dom/classnames :activated is-row-activated?)}
i/auto-gap]
[:> numeric-input {:no-validate true
:placeholder "--"
:on-click (fn [event]
(reset! gap-selected? :row-gap)
(dom/select-target event))
:on-change (partial set-gap gap-locked? :row-gap)
:on-blur #(reset! gap-selected? :none)
:value (:row-gap gap-value)}]]
[:div.gap-column.tooltip.tooltip-bottom-left
{:alt "Column gap"}
[:span.icon.rotated
{:class (dom/classnames
:activated is-column-activated?)}
i/auto-gap]
[:> numeric-input {:no-validate true
:placeholder "--"
:on-click (fn [event]
(reset! gap-selected? :column-gap)
(dom/select-target event))
:on-change (partial set-gap gap-locked? :column-gap)
:on-blur #(reset! gap-selected? :none)
:value (:column-gap gap-value)}]]
[:button.lock {:on-click toggle-gap-type
:class (dom/classnames :active gap-locked?)}
(if gap-locked?
i/lock
i/unlock)]]))
(mf/defc layout-container-menu
{::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type"]))]}
[{:keys [ids _type values] :as props}]
(let [open? (mf/use-state false)
gap-selected? (mf/use-state false)
toggle-open (fn [] (swap! open? not))
(let [open? (mf/use-state false)
;; Display
layout-type (:layout values)
on-add-layout
(fn [_]
(st/emit! (dwsl/create-layout ids)))
(fn [type]
(st/emit! (dwsl/create-layout ids type)))
on-remove-layout
(fn [_]
(st/emit! (dwsl/remove-layout ids))
(reset! open? false))
set-flex (fn []
(st/emit! (dwsl/remove-layout ids))
(on-add-layout :flex))
set-grid (fn []
(st/emit! (dwsl/remove-layout ids))
(on-add-layout :grid))
;; Flex-direction
saved-dir (:layout-flex-dir values)
is-col? (or (= :column saved-dir) (= :reverse-column saved-dir))
set-direction
(fn [dir]
(st/emit! (dwsl/update-layout ids {:layout-dir dir})))
(st/emit! (dwsl/update-layout ids {:layout-flex-dir dir})))
set-gap
(fn [gap]
(st/emit! (dwsl/update-layout ids {:layout-gap gap})))
;; Wrap type
change-padding-style
wrap-type (:layout-wrap-type values)
set-wrap (fn [type]
(st/emit! (dwsl/update-layout ids {:layout-wrap-type type})))
;; Align items
align-items (:layout-align-items values)
set-align-items (fn [value]
(st/emit! (dwsl/update-layout ids {:layout-align-items value})))
;; Justify content
justify-content (:layout-justify-content values)
set-justify-content (fn [value]
(st/emit! (dwsl/update-layout ids {:layout-justify-content value})))
;; Align content
align-content (:layout-align-content values)
set-align-content (fn [value]
(if (= align-content value)
(st/emit! (dwsl/update-layout ids {:layout-align-content :strech}))
(st/emit! (dwsl/update-layout ids {:layout-align-content value}))))
;; Gap
change-gap-type
(fn [type]
(st/emit! (dwsl/update-layout ids {:layout-padding-type type})))
(st/emit! (dwsl/update-layout ids {:layout-gap-type type})))
gap-type (:layout-gap-type values)
gap-selected? (mf/use-state :none)
gap-locked? (= gap-type :simple)
toggle-gap-type (fn []
(let [layout-gap (:layout-gap values)
row-gap (:row-gap layout-gap)
column-gap (:column-gap layout-gap)
max (max row-gap column-gap)
new-type (if (= gap-type :simple)
:multiple
:simple)]
(when (and (not= row-gap column-gap) (= gap-type :multiple))
(st/emit! (dwsl/update-layout ids {:layout-gap {:row-gap max :column-gap max}})))
(change-gap-type new-type)))
set-gap
(fn [gap-locked? type val]
(if gap-locked?
(st/emit! (dwsl/update-layout ids {:layout-gap {:row-gap val :column-gap val}}))
(st/emit! (dwsl/update-layout ids {:layout-gap {type val}}))))
select-all-gap
(fn [event]
(reset! gap-selected? true)
(dom/select-target event))
(when gap-locked?
(dom/select-target event)))
;; Padding
change-padding-type
(fn [type]
(st/emit! (dwsl/update-layout ids {:layout-padding-type type})))
on-padding-change
(fn [type val]
(if (= type :simple)
(st/emit! (dwsl/update-layout ids {:layout-padding {:p1 val :p2 val :p3 val :p4 val}}))
(st/emit! (dwsl/update-layout ids {:layout-padding {type val}}))))
handle-change-type
(fn [event]
(let [target (dom/get-target event)
value (dom/get-value target)
value (keyword value)]
(st/emit! (dwsl/update-layout ids {:layout-type value}))))
handle-wrap-type
(mf/use-callback
(fn [event]
(let [target (dom/get-target event)
value (dom/get-value target)
value (keyword value)]
(st/emit! (dwsl/update-layout ids {:layout-wrap-type value})))))
handle-change-orientation
(fn [v-orientation h-orientation]
(st/emit! (dwsl/update-layout ids {:layout-h-orientation h-orientation :layout-v-orientation v-orientation})))
layout-info
(fn []
(let [type (:layout-type values)
dir (:layout-dir values)
is-col? (or (= dir :top) (= dir :bottom))
h (:layout-h-orientation values)
v (:layout-v-orientation values)
wrap (:layout-wrap-type values)
orientation
(if (= type :packed)
;; Execution time translation strings:
;; workspace.options.layout.h.center
;; workspace.options.layout.h.left
;; workspace.options.layout.h.right
;; workspace.options.layout.v.bottom
;; workspace.options.layout.v.center
;; workspace.options.layout.v.top
(dm/str (tr (dm/str "workspace.options.layout.v." (d/name v))) ", "
(tr (dm/str "workspace.options.layout.h." (d/name h))) ", ")
(if is-col?
(dm/str (tr (dm/str "workspace.options.layout.h." (d/name h))) ", ")
(dm/str (tr (dm/str "workspace.options.layout.v." (d/name v))) ", ")))]
(dm/str orientation
(str/replace (tr (dm/str "workspace.options.layout." (d/name type))) "-" " ") ", "
(str/replace (tr (dm/str "workspace.options.layout." (d/name wrap))) "-" " "))))]
(st/emit! (dwsl/update-layout ids {:layout-padding {type val}}))))]
[:div.element-set
[:div.element-set-title
[:*
[:span (tr "workspace.options.layout.title")]
[:span "Layout"]
(if (:layout values)
[:div.add-page {:on-click on-remove-layout} i/minus]
[:div.add-page {:on-click on-add-layout} i/close])]]
[:div.title-actions
[:div.layout-btns
[:button {:on-click set-flex
:class (dom/classnames
:active (= :flex layout-type))} "Flex"]
[:button {:on-click set-grid
:class (dom/classnames
:active (= :grid layout-type))} "Grid"]]
[:button.remove-layout {:on-click on-remove-layout} i/minus]]
[:button.add-page {:on-click #(on-add-layout :flex)} i/close])]]
(when (:layout values)
[:div.element-set-content.layout-menu
;; DIRECTION-GAP
[:div.direction-gap
[:div.direction
[:*
(for [dir [:left :right :bottom :top]]
[:& direction-row {:key (d/name dir)
:dir dir
:saved-dir (:layout-dir values)
:set-direction set-direction}])]]
[:div.gap.tooltip.tooltip-bottom-left
{:alt (tr "workspace.options.layout.gap")}
[:span.icon
{:class (dom/classnames
:rotated (or (= (:layout-dir values) :top)
(= (:layout-dir values) :bottom))
:activated (= @gap-selected? true))}
i/auto-gap]
[:> numeric-input {:no-validate true
:placeholder "--"
:on-click select-all-gap
:on-change set-gap
:on-blur #(reset! gap-selected? false)
:value (:layout-gap values)}]]]
(if (= :flex layout-type)
[:div.element-set-content.layout-menu
[:div.layout-row
[:div.direction-wrap.row-title "Direction"]
[:div.btn-wrapper
[:div.direction
[:*
(for [dir [:row :reverse-row :column :reverse-column]]
[:& direction-btn {:key (d/name dir)
:dir dir
:saved-dir saved-dir
:set-direction set-direction}])]]
;; LAYOUT FLEX
[:div.layout-container
[:div.layout-entry.tooltip.tooltip-bottom
{:on-click toggle-open
:alt (layout-info)}
[:div.element-set-actions-button i/actions]
[:div.layout-info (layout-info)]]
[:div.wrap-type
[:& wrap-row {:wrap-type wrap-type
:set-wrap set-wrap}]]]]
(when @open?
[:div.layout-body
[:& orientation-grid {:on-change-orientation handle-change-orientation :values values}]
(when (= :wrap wrap-type)
[:div.layout-row
[:div.align-content.row-title "Content"]
[:div.btn-wrapper
[:& align-content-row {:is-col? is-col?
:align-content align-content
:set-align-content set-align-content}]]])
[:div.selects-wrapper
[:select.input-select {:value (d/name (:layout-type values))
:on-change handle-change-type}
[:option {:value "packed" :label (tr "workspace.options.layout.packed")}]
[:option {:value "space-between" :label (tr "workspace.options.layout.space-between")}]
[:option {:value "space-around" :label (tr "workspace.options.layout.space-around")}]]
[:div.layout-row
[:div.align-items.row-title "Align"]
[:div.btn-wrapper
[:& align-row {:is-col? is-col?
:align-items align-items
:set-align set-align-items}]]]
[:select.input-select {:value (d/name (:layout-wrap-type values))
:on-change handle-wrap-type}
[:option {:value "wrap" :label (tr "workspace.options.layout.wrap")}]
[:option {:value "no-wrap" :label (tr "workspace.options.layout.no-wrap")}]]]])]
[:div.layout-row
[:div.justify-content.row-title "Justify"]
[:div.btn-wrapper
[:& justify-content-row {:is-col? is-col?
:justify-content justify-content
:set-justify set-justify-content}]]]
[:& gap-section {:gap-selected? gap-selected?
:select-all-gap select-all-gap
:set-gap set-gap
:gap-value (:layout-gap values)
:toggle-gap-type toggle-gap-type
:gap-type gap-type}]
[:& padding-section {:values values
:on-change-style change-padding-style
:on-change on-padding-change}]])]))
[:& padding-section {:values values
:on-change-style change-padding-type
:on-change on-padding-change}]]
[:div "GRID TO COME"]))]))

View file

@ -12,6 +12,7 @@
[app.main.store :as st]
[app.main.ui.components.numeric-input :refer [numeric-input]]
[app.main.ui.icons :as i]
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [get-layout-flex-icon]]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[rumext.v2 :as mf]))
@ -31,8 +32,8 @@
(let [margin-type (or (:layout-margin-type values) :simple)]
[:div.row-flex
[:div.margin-options
[:div.margin-row
[:div.margin-icons
[:div.margin-icon.tooltip.tooltip-bottom
{:class (dom/classnames :selected (= margin-type :simple))
:alt (tr "workspace.options.layout.margin-simple")
@ -43,80 +44,93 @@
:alt (tr "workspace.options.layout.margin")
:on-click #(change-margin-style :multiple)}
i/auto-margin-side]]
[:div.wrapper
(cond
(= margin-type :simple)
[:div.tooltip.tooltip-bottom
{:alt (tr "workspace.options.layout.margin-all")}
[:div.input-element.mini
(cond
(= margin-type :simple)
[:div.tooltip.tooltip-bottom
{:alt (tr "workspace.options.layout.margin-all")}
[:div.input-element.mini
[:> numeric-input
{:placeholder "--"
:on-click #(dom/select-target %)
:on-change (partial on-margin-change :simple)
:value (or (-> values :layout-margin :m1) 0)}]]]
[:> numeric-input
{:placeholder "--"
:on-click #(dom/select-target %)
:on-change (partial on-margin-change :simple)
:value (or (-> values :layout-margin :m1) 0)}]]]
(= margin-type :multiple)
[:*
(= margin-type :multiple)
(for [num [:m1 :m2 :m3 :m4]]
[:div.tooltip.tooltip-bottom
{:class (dm/str "margin-" (d/name num))
:key (dm/str "margin-" (d/name num))
{:key (dm/str "margin-" (d/name num))
:alt (case num
:m1 (tr "workspace.options.layout.top")
:m2 (tr "workspace.options.layout.right")
:m3 (tr "workspace.options.layout.bottom")
:m4 (tr "workspace.options.layout.left"))}
:m1 "Top"
:m2 "Right"
:m3 "Bottom"
:m4 "Left")}
[:div.input-element.mini
[:> numeric-input
{:placeholder "--"
:on-click #(dom/select-target %)
:on-change (partial on-margin-change num)
:value (or (-> values :layout-margin num) 0)}]]])])]))
:value (or (-> values :layout-margin num) 0)}]]]))]]))
(mf/defc element-behavior
[{:keys [is-layout-container? is-layout-child? layout-h-behavior layout-v-behavior on-change-behavior] :as props}]
(let [fill? is-layout-child?
auto? is-layout-container?]
[:div.layout-behavior
[:div.button-wrapper.horizontal
[:div.btn-wrapper
[:div.layout-behavior.horizontal
[:button.behavior-btn.tooltip.tooltip-bottom
{:alt "horizontal fix"
{:alt "Fix width"
:class (dom/classnames :activated (= layout-h-behavior :fix))
:on-click #(on-change-behavior :h :fix)}
[:span.icon i/auto-fix-layout]]
i/auto-fix-layout]
(when fill?
[:button.behavior-btn.tooltip.tooltip-bottom
{:alt "horizontal fill"
{:alt "Width 100%"
:class (dom/classnames :activated (= layout-h-behavior :fill))
:on-click #(on-change-behavior :h :fill)}
[:span.icon i/auto-fill]])
i/auto-fill])
(when auto?
[:button.behavior-btn.tooltip.tooltip-bottom
{:alt "horizontal auto"
{:alt "Fit content"
:class (dom/classnames :activated (= layout-v-behavior :auto))
:on-click #(on-change-behavior :h :auto)}
[:span.icon i/auto-hug]])]
i/auto-hug])]
[:div.button-wrapper
[:div.layout-behavior
[:button.behavior-btn.tooltip.tooltip-bottom
{:alt "vertical fix"
{:alt "Fix height"
:class (dom/classnames :activated (= layout-v-behavior :fix))
:on-click #(on-change-behavior :v :fix)}
[:span.icon i/auto-fix-layout]]
i/auto-fix-layout]
(when fill?
[:button.behavior-btn.tooltip.tooltip-bottom
{:alt "vertical fill"
{:alt "Height 100%"
:class (dom/classnames :activated (= layout-v-behavior :fill))
:on-click #(on-change-behavior :v :fill)}
[:span.icon i/auto-fill]])
i/auto-fill])
(when auto?
[:button.behavior-btn.tooltip.tooltip-bottom
{:alt "vertical auto"
[:button.behavior-btn.tooltip.tooltip-bottom-left
{:alt "Fit content"
:class (dom/classnames :activated (= layout-v-behavior :auto))
:on-click #(on-change-behavior :v :auto)}
[:span.icon i/auto-hug]])]]))
i/auto-hug])]]))
(mf/defc align-self-row
[{:keys [is-col? align-self set-align-self] :as props}]
(let [dir-v [:start :center :end :strech :baseline]]
[:div.align-self-style
(for [align dir-v]
[:button.align-self.tooltip.tooltip-bottom
{:class (dom/classnames :active (= align-self align)
:tooltip-bottom-left (not= align :start)
:tooltip-bottom (= align :start))
:alt (dm/str "Align self " (d/name align)) ;; TODO añadir lineas de texto a tradus
:on-click #(set-align-self align)
:key (str "align-self" align)}
(get-layout-flex-icon :align-self align is-col?)])]))
(mf/defc layout-item-menu
{::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type"]))]}
@ -128,6 +142,13 @@
(fn [type]
(st/emit! (dwsl/update-layout-child ids {:layout-margin-type type})))
align-self (:layout-align-self values)
set-align-self (fn [value]
(st/emit! (dwsl/update-layout-child ids {:layout-align-self value})))
saved-dir (:layout-flex-dir values)
is-col? (or (= :column saved-dir) (= :reverse-column saved-dir))
on-margin-change
(fn [type val]
(if (= type :simple)
@ -146,51 +167,54 @@
[:div.element-set
[:div.element-set-title
[:span (tr "workspace.options.layout-item.title")]]
[:span "Flex elements"]]
[:div.element-set-content.layout-item-menu
[:& element-behavior {:is-layout-child? is-layout-child?
:is-layout-container? is-layout-container?
:layout-v-behavior (or (:layout-v-behavior values) :fix)
:layout-h-behavior (or (:layout-h-behavior values) :fix)
:on-change-behavior on-change-behavior}]
[:div.layout-row
[:div.row-title "Sizing"]
[:& element-behavior {:is-layout-child? is-layout-child?
:is-layout-container? is-layout-container?
:layout-v-behavior (or (:layout-v-behavior values) :fix)
:layout-h-behavior (or (:layout-h-behavior values) :fix)
:on-change-behavior on-change-behavior}]]
[:div.margin [:& margin-section {:values values
:change-margin-style change-margin-style
:on-margin-change on-margin-change}]]
[:& margin-section {:values values
:change-margin-style change-margin-style
:on-margin-change on-margin-change}]
[:div.advanced-ops-container
[:div.advanced-ops.toltip.tooltip-bottom
[:button.advanced-ops.toltip.tooltip-bottom
{:on-click toggle-open
:alt (tr "workspace.options.layout-item.advanced-ops")}
[:div.element-set-actions-button i/actions]
[:span.icon i/actions]
[:span (tr "workspace.options.layout-item.advanced-ops")]]]
(when @open?
[:div.advanced-ops-body
(for [item [:layout-max-h :layout-min-h :layout-max-w :layout-min-w]]
[:div.input-element
{:key (d/name item)
;; Execution time translation strings:
;; workspace.options.layout-item.layout-max-h
;; workspace.options.layout-item.layout-max-w
;; workspace.options.layout-item.layout-min-h
;; workspace.options.layout-item.layout-min-w
;; workspace.options.layout-item.title.layout-max-h
;; workspace.options.layout-item.title.layout-max-w
;; workspace.options.layout-item.title.layout-min-h
;; workspace.options.layout-item.title.layout-min-w
:alt (tr (dm/str "workspace.options.layout-item." (d/name item)))
:title (tr (dm/str "workspace.options.layout-item." (d/name item)))
:class (dom/classnames "maxH" (= item :layout-max-h)
"minH" (= item :layout-min-h)
"maxW" (= item :layout-max-w)
"minW" (= item :layout-min-w))}
[:> numeric-input
{:no-validate true
:min 0
:data-wrap true
:placeholder "--"
:on-click #(dom/select-target %)
:on-change (partial on-size-change item)
:value (get values item)}]])])]]))
[:div.layout-row
[:div.direction-wrap.row-title "Align"] ;; TODO tradus
[:div.btn-wrapper
[:& align-self-row {:is-col? is-col?
:align-self align-self
:set-align-self set-align-self}]]]
[:div.input-wrapper
(for [item [:layout-max-h :layout-min-h :layout-max-w :layout-min-w]]
[:div.tooltip.tooltip-bottom
{:key (d/name item)
:alt (tr (dm/str "workspace.options.layout-item.title." (d/name item)))
:class (dom/classnames "maxH" (= item :layout-max-h)
"minH" (= item :layout-min-h)
"maxW" (= item :layout-max-w)
"minW" (= item :layout-min-w))}
[:div.input-element
{:alt (tr (dm/str "workspace.options.layout-item." (d/name item)))}
[:> numeric-input
{:no-validate true
:min 0
:data-wrap true
:placeholder "--"
:on-click #(dom/select-target %)
:on-change (partial on-size-change item)
;; :value (get values item)
:value 100}]]])]])]]
))

View file

@ -13,7 +13,7 @@
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs-shape fill-menu]]
[app.main.ui.workspace.sidebar.options.menus.frame-grid :refer [frame-grid]]
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-attrs layout-container-menu]]
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
[app.main.ui.workspace.sidebar.options.menus.measures :refer [select-measure-keys measures-menu]]
[app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-menu]]
@ -31,7 +31,7 @@
layer-values (select-keys shape layer-attrs)
measure-values (select-measure-keys shape)
constraint-values (select-keys shape constraint-attrs)
layout-container-values (select-keys shape layout-container-attrs)
layout-container-values (select-keys shape layout-container-flex-attrs)
layout-item-values (select-keys shape layout-item-attrs)
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))

View file

@ -19,7 +19,7 @@
[app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-attrs exports-menu]]
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-attrs layout-container-menu]]
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
[app.main.ui.workspace.sidebar.options.menus.measures :refer [select-measure-keys measure-attrs measures-menu]]
[app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-attrs shadow-menu]]
@ -142,7 +142,7 @@
:stroke stroke-attrs
:text ot/attrs
:exports exports-attrs
:layout layout-container-attrs
:layout layout-container-flex-attrs
:layout-item layout-item-attrs})
(def shadow-keys [:style :color :offset-x :offset-y :blur :spread])