0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-13 16:21:57 -05:00

♻️ Renamed layout to grid and some refactors

This commit is contained in:
alonso.torres 2020-05-19 13:47:52 +02:00 committed by Andrés Moya
parent 235e196094
commit 23ca77fe3a
10 changed files with 203 additions and 171 deletions

View file

@ -1489,76 +1489,6 @@
(rx/of (update-shape shape-id
{:interactions []}))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Layouts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defonce default-layout-params
{:square {:size 16
:color {:value "#59B9E2"
:opacity 0.9}}
:column {:size 12
:type :stretch
:item-length nil
:gutter 8
:margin 0
:color {:value "#DE4762"
:opacity 0.1}}
:row {:size 12
:type :stretch
:item-height nil
:gutter 8
:margin 0
:color {:value "#DE4762"
:opacity 0.1}}})
(defn add-frame-layout [frame-id]
(ptk/reify ::set-frame-layout
dwc/IBatchedChange
ptk/UpdateEvent
(update [_ state]
(let [pid (:current-page-id state)
default-params (or
(get-in state [:workspace-data pid :options :saved-layouts :square])
(:square default-layout-params))
prop-path [:workspace-data pid :objects frame-id :layouts]
layout {:type :square
:params default-params
:display true}]
(-> state
(update-in prop-path #(if (nil? %) [layout] (conj % layout))))))))
(defn remove-frame-layout [frame-id index]
(ptk/reify ::set-frame-layout
dwc/IBatchedChange
ptk/UpdateEvent
(update [_ state]
(let [pid (:current-page-id state)]
(-> state
(update-in [:workspace-data pid :objects frame-id :layouts] #(d/remove-at-index % index)))))))
(defn set-frame-layout [frame-id index data]
(ptk/reify ::set-frame-layout
dwc/IBatchedChange
ptk/UpdateEvent
(update [_ state]
(let [pid (:current-page-id state)]
(->
state
(assoc-in [:workspace-data pid :objects frame-id :layouts index] data))))))
(defn set-default-layout [type params]
(ptk/reify ::set-default-layout
ptk/WatchEvent
(watch [_ state stream]
(rx/of (dwc/commit-changes [{:type :set-option
:option [:saved-layouts type]
:value params}]
[]
{:commit-local? true})))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Exports
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -0,0 +1,83 @@
;; 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/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns uxbox.main.data.workspace.grid
(:require
[beicon.core :as rx]
[potok.core :as ptk]
[uxbox.common.data :as d]
[uxbox.main.data.workspace.common :as dwc]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Grid
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defonce ^:private default-square-params
{:size 16
:color {:value "#59B9E2"
:opacity 0.9}})
(defonce ^:private default-layout-params
{:size 12
:type :stretch
:item-length nil
:gutter 8
:margin 0
:color {:value "#DE4762"
:opacity 0.1}})
(defonce default-grid-params
{:square default-square-params
:column default-layout-params
:row default-layout-params})
(defn add-frame-grid [frame-id]
(ptk/reify ::set-frame-grid
dwc/IBatchedChange
ptk/UpdateEvent
(update [_ state]
(let [pid (:current-page-id state)
default-params (or
(get-in state [:workspace-data pid :options :saved-grids :square])
(:square default-grid-params))
prop-path [:workspace-data pid :objects frame-id :grids]
grid {:type :square
:params default-params
:display true}]
(-> state
(update-in prop-path #(if (nil? %) [grid] (conj % grid))))))))
(defn remove-frame-grid [frame-id index]
(ptk/reify ::set-frame-grid
dwc/IBatchedChange
ptk/UpdateEvent
(update [_ state]
(let [pid (:current-page-id state)]
(-> state
(update-in [:workspace-data pid :objects frame-id :grids] #(d/remove-at-index % index)))))))
(defn set-frame-grid [frame-id index data]
(ptk/reify ::set-frame-grid
dwc/IBatchedChange
ptk/UpdateEvent
(update [_ state]
(let [pid (:current-page-id state)]
(->
state
(assoc-in [:workspace-data pid :objects frame-id :grids index] data))))))
(defn set-default-grid [type params]
(ptk/reify ::set-default-grid
ptk/WatchEvent
(watch [_ state stream]
(rx/of (dwc/commit-changes [{:type :set-option
:option [:saved-grids type]
:value params}]
[]
{:commit-local? true})))))

View file

@ -71,8 +71,8 @@
(def workspace-page-options
(l/derived :options workspace-data))
(def workspace-saved-layouts
(l/derived :saved-layouts workspace-page-options))
(def workspace-saved-grids
(l/derived :saved-grids workspace-page-options))
(def workspace-objects

View file

@ -7,20 +7,20 @@
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns uxbox.main.ui.workspace.layout-display
(ns uxbox.main.ui.workspace.frame-grid
(:require
[rumext.alpha :as mf]
[uxbox.main.refs :as refs]
[uxbox.common.pages :as cp]
[uxbox.util.geom.shapes :as gsh]
[uxbox.util.geom.layout :as ula]))
[uxbox.util.geom.grid :as gg]))
(mf/defc grid-layout [{:keys [frame zoom layout] :as props}]
(let [{:keys [color size] :as params} (-> layout :params)
{color-value :value color-opacity :opacity} (-> layout :params :color)
(mf/defc square-grid [{:keys [frame zoom grid] :as props}]
(let [{:keys [color size] :as params} (-> grid :params)
{color-value :value color-opacity :opacity} (-> grid :params :color)
{frame-width :width frame-height :height :keys [x y]} frame]
(when (> size 0)
[:g.layout
[:g.grid
[:*
(for [xs (range size frame-width size)]
[:line {:key (str (:id frame) "-y-" xs)
@ -41,10 +41,10 @@
:stroke-opacity color-opacity
:stroke-width (str (/ 1 zoom))}}])]])))
(mf/defc flex-layout [{:keys [key frame zoom layout]}]
(let [{color-value :value color-opacity :opacity} (-> layout :params :color)]
[:g.layout
(for [{:keys [x y width height]} (ula/layout-rects frame layout)]
(mf/defc layout-grid [{:keys [key frame zoom grid]}]
(let [{color-value :value color-opacity :opacity} (-> grid :params :color)]
[:g.grid
(for [{:keys [x y width height]} (gg/grid-areas frame grid)]
[:rect {:key (str key "-" x "-" y)
:x x
:y y
@ -53,24 +53,24 @@
:style {:fill color-value
:opacity color-opacity}}])]))
(mf/defc layout-display-frame [{:keys [frame zoom]}]
(let [layouts (:layouts frame)]
(for [[index {:keys [type display] :as layout}] (map-indexed vector layouts)]
(let [props #js {:key (str (:id frame) "-layout-" index)
(mf/defc grid-display-frame [{:keys [frame zoom]}]
(let [grids (:grids frame)]
(for [[index {:keys [type display] :as grid}] (map-indexed vector grids)]
(let [props #js {:key (str (:id frame) "-grid-" index)
:frame frame
:zoom zoom
:layout layout}]
:grid grid}]
(when display
(case type
:square [:> grid-layout props]
:column [:> flex-layout props]
:row [:> flex-layout props]))))))
:square [:> square-grid props]
:column [:> layout-grid props]
:row [:> layout-grid props]))))))
(mf/defc layout-display [{:keys [zoom]}]
(mf/defc frame-grid [{:keys [zoom]}]
(let [frames (mf/deref refs/workspace-frames)]
[:g.layout-display {:style {:pointer-events "none"}}
[:g.grid-display {:style {:pointer-events "none"}}
(for [frame frames]
[:& layout-display-frame {:key (str "layout-" (:id frame))
:zoom zoom
:frame (gsh/transform-shape frame)}])]))
[:& grid-display-frame {:key (str "grid-" (:id frame))
:zoom zoom
:frame (gsh/transform-shape frame)}])]))

View file

@ -22,7 +22,7 @@
[uxbox.main.ui.components.dropdown :refer [dropdown]]
[uxbox.main.ui.workspace.sidebar.options.fill :refer [fill-menu]]
[uxbox.main.ui.workspace.sidebar.options.stroke :refer [stroke-menu]]
[uxbox.main.ui.workspace.sidebar.options.frame-layouts :refer [frame-layouts]]))
[uxbox.main.ui.workspace.sidebar.options.frame-grid :refer [frame-grid]]))
(declare +size-presets+)
@ -204,4 +204,5 @@
[:& measures-menu {:shape shape}]
[:& fill-menu {:shape shape}]
[:& stroke-menu {:shape shape}]
[:& frame-layouts {:shape shape}]])
[:& frame-grid {:shape shape}]])

View file

@ -7,7 +7,7 @@
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns uxbox.main.ui.workspace.sidebar.options.frame-layouts
(ns uxbox.main.ui.workspace.sidebar.options.frame-grid
(:require
[rumext.alpha :as mf]
[uxbox.util.dom :as dom]
@ -16,8 +16,8 @@
[uxbox.common.data :refer [parse-integer]]
[uxbox.main.store :as st]
[uxbox.main.refs :as refs]
[uxbox.main.data.workspace :as dw]
[uxbox.util.geom.layout :as gla]
[uxbox.main.data.workspace.grid :as dw]
[uxbox.util.geom.grid :as gg]
[uxbox.main.ui.icons :as i]
[uxbox.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]
[uxbox.main.ui.workspace.sidebar.options.rows.input-row :refer [input-row]]
@ -40,29 +40,29 @@
:separator
18 12 10 8 6 4 3 2])
(mf/defc layout-options [{:keys [frame layout default-layout-params on-change on-remove on-save-layout]}]
(mf/defc grid-options [{:keys [frame grid default-grid-params on-change on-remove on-save-grid]}]
(let [state (mf/use-state {:show-advanced-options false
:changes {}})
{:keys [type display params] :as layout} (d/deep-merge layout (:changes @state))
{:keys [type display params] :as grid} (d/deep-merge grid (:changes @state))
toggle-advanced-options #(swap! state update :show-advanced-options not)
emit-changes!
(fn [update-fn]
(swap! state update :changes update-fn)
(when on-change (on-change (d/deep-merge layout (-> @state :changes update-fn)))))
(when on-change (on-change (d/deep-merge grid (-> @state :changes update-fn)))))
handle-toggle-visibility
(fn [event]
(emit-changes! (fn [changes] (update changes :display #(if (nil? %) false (not %))))))
handle-remove-layout
handle-remove-grid
(fn [event]
(when on-remove (on-remove)))
handle-change-type
(fn [type]
(let [defaults (type default-layout-params)
(let [defaults (type default-grid-params)
keys (keys defaults)
params (->> @state :changes params (select-keys keys) (merge defaults))
to-merge {:type type :params params}]
@ -81,11 +81,11 @@
handle-change-size
(fn [size]
(let [layout (d/deep-merge layout (:changes @state))
{:keys [margin gutter item-length]} (:params layout)
frame-length (if (= :column (:type layout)) (:width frame) (:height frame))
(let [grid (d/deep-merge grid (:changes @state))
{:keys [margin gutter item-length]} (:params grid)
frame-length (if (= :column (:type grid)) (:width frame) (:height frame))
item-length (if (or (nil? size) (= :auto size))
(-> (gla/calculate-default-item-length frame-length margin gutter)
(-> (gg/calculate-default-item-length frame-length margin gutter)
(mth/round))
item-length)]
(emit-changes! #(-> %
@ -94,7 +94,7 @@
handle-change-item-length
(fn [item-length]
(let [{:keys [margin gutter size]} (->> @state :changes :params (d/deep-merge (:params layout)))
(let [{:keys [margin gutter size]} (->> @state :changes :params (d/deep-merge (:params grid)))
size (if (and (nil? item-length) (or (nil? size) (= :auto size))) 12 size)]
(emit-changes! #(-> %
(assoc-in [:params :size] size)
@ -102,15 +102,15 @@
handle-use-default
(fn []
(emit-changes! #(hash-map :params ((:type layout) default-layout-params))))
(emit-changes! #(hash-map :params ((:type grid) default-grid-params))))
handle-set-as-default
(fn []
(let [current-layout (d/deep-merge layout (-> @state :changes))]
(on-save-layout current-layout)))
(let [current-grid (d/deep-merge grid (-> @state :changes))]
(on-save-grid current-grid)))
is-default (= (->> @state :changes (d/deep-merge layout) :params)
(->> layout :type default-layout-params))]
is-default (= (->> @state :changes (d/deep-merge grid) :params)
(->> grid :type default-grid-params))]
[:div.grid-option
[:div.grid-option-main
@ -139,7 +139,7 @@
[:div.grid-option-main-actions
[:button.custom-button {:on-click handle-toggle-visibility} (if display i/eye i/eye-closed)]
[:button.custom-button {:on-click handle-remove-layout} i/trash]]]
[:button.custom-button {:on-click handle-remove-grid} i/trash]]]
[:& advanced-options {:visible? (:show-advanced-options @state)
:on-close toggle-advanced-options}
@ -203,26 +203,26 @@
[:button.btn-options {:disabled is-default
:on-click handle-set-as-default} "Set as default"]]]]))
(mf/defc frame-layouts [{:keys [shape]}]
(mf/defc frame-grid [{:keys [shape]}]
(let [id (:id shape)
default-layout-params (merge dw/default-layout-params (mf/deref refs/workspace-saved-layouts))
handle-create-layout #(st/emit! (dw/add-frame-layout id))
handle-remove-layout (fn [index] #(st/emit! (dw/remove-frame-layout id index)))
handle-edit-layout (fn [index] #(st/emit! (dw/set-frame-layout id index %)))
handle-save-layout (fn [layout] (st/emit! (dw/set-default-layout (:type layout) (:params layout))))]
default-grid-params (merge dw/default-grid-params (mf/deref refs/workspace-saved-grids))
handle-create-grid #(st/emit! (dw/add-frame-grid id))
handle-remove-grid (fn [index] #(st/emit! (dw/remove-frame-grid id index)))
handle-edit-grid (fn [index] #(st/emit! (dw/set-frame-grid id index %)))
handle-save-grid (fn [grid] (st/emit! (dw/set-default-grid (:type grid) (:params grid))))]
[:div.element-set
[:div.element-set-title
[:span "Grid & Layout"]
[:div.add-page {:on-click handle-create-layout} i/close]]
[:span "Grids"]
[:div.add-page {:on-click handle-create-grid} i/close]]
(when (not (empty? (:layouts shape)))
(when (not (empty? (:grids shape)))
[:div.element-set-content
(for [[index layout] (map-indexed vector (:layouts shape))]
[:& layout-options {:key (str (:id shape) "-" index)
:layout layout
:default-layout-params default-layout-params
(for [[index grid] (map-indexed vector (:grids shape))]
[:& grid-options {:key (str (:id shape) "-" index)
:grid grid
:default-grid-params default-grid-params
:frame shape
:on-change (handle-edit-layout index)
:on-remove (handle-remove-layout index)
:on-save-layout handle-save-layout}])])]))
:on-change (handle-edit-grid index)
:on-remove (handle-remove-grid index)
:on-save-grid handle-save-grid}])])]))

View file

@ -29,7 +29,7 @@
[uxbox.main.ui.workspace.selection :refer [selection-handlers]]
[uxbox.main.ui.workspace.presence :as presence]
[uxbox.main.ui.workspace.snap-feedback :refer [snap-feedback]]
[uxbox.main.ui.workspace.layout-display :refer [layout-display]]
[uxbox.main.ui.workspace.frame-grid :refer [frame-grid]]
[uxbox.util.math :as mth]
[uxbox.util.dom :as dom]
[uxbox.util.object :as obj]
@ -387,7 +387,7 @@
:modifiers (:modifiers local)}])
(when (contains? layout :display-grid)
[:& layout-display {:zoom zoom}])
[:& frame-grid {:zoom zoom}])
[:& snap-feedback {:layout layout}]

View file

@ -7,24 +7,27 @@
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns uxbox.util.geom.layout
(ns uxbox.util.geom.grid
(:require
[uxbox.util.math :as mth]
[uxbox.util.geom.point :as gpt]))
(def ^:private default-items 12)
(defn calculate-default-item-length [frame-length margin gutter]
(defn calculate-default-item-length
"Calculates the item-length so the default number of items fits inside the frame-length"
[frame-length margin gutter]
(/ (- frame-length (+ margin (- margin gutter)) (* gutter default-items)) default-items))
(defn calculate-size
"Calculates the number of rows/columns given the other layout parameters"
"Calculates the number of rows/columns given the other grid parameters"
[frame-length item-length margin gutter]
(let [item-length (or item-length (calculate-default-item-length frame-length margin gutter))
frame-length-no-margins (- frame-length (+ margin (- margin gutter)))]
(mth/floor (/ frame-length-no-margins (+ item-length gutter)))))
(defn calculate-column-layout [{:keys [width height x y] :as frame} {:keys [size gutter margin item-length type] :as params}]
(defn- calculate-column-grid
[{:keys [width height x y] :as frame} {:keys [size gutter margin item-length type] :as params}]
(let [size (if (number? size) size (calculate-size width item-length margin gutter))
parts (/ width size)
item-width (or item-length (+ parts (- gutter) (/ gutter size) (- (/ (* margin 2) size))))
@ -38,7 +41,8 @@
next-y (fn [cur-val] y)]
[size item-width item-height next-x next-y]))
(defn calculate-row-layout [{:keys [width height x y] :as frame} {:keys [size gutter margin item-length type] :as params}]
(defn- calculate-row-grid
[{:keys [width height x y] :as frame} {:keys [size gutter margin item-length type] :as params}]
(let [size (if (number? size) size (calculate-size height item-length margin gutter))
parts (/ height size)
item-width width
@ -52,7 +56,8 @@
next-y (fn [cur-val] (+ initial-offset y (* (+ item-height gutter) cur-val)))]
[size item-width item-height next-x next-y]))
(defn calculate-grid-layout [{:keys [width height x y] :as frame} {:keys [size] :as params}]
(defn- calculate-square-grid
[{:keys [width height x y] :as frame} {:keys [size] :as params}]
(let [col-size (quot width size)
row-size (quot height size)
as-row-col (fn [value] [(quot value col-size) (rem value col-size)])
@ -62,12 +67,14 @@
(let [[row _] (as-row-col cur-val)] (+ y (* row size))))]
[(* col-size row-size) size size next-x next-y]))
(defn layout-rects [frame layout]
(let [layout-fn (case (-> layout :type)
:column calculate-column-layout
:row calculate-row-layout
:square calculate-grid-layout)
[num-items item-width item-height next-x next-y] (layout-fn frame (-> layout :params))]
(defn grid-areas
"Given a frame and the grid parameters returns the areas defined on the grid"
[frame grid]
(let [grid-fn (case (-> grid :type)
:column calculate-column-grid
:row calculate-row-grid
:square calculate-square-grid)
[num-items item-width item-height next-x next-y] (grid-fn frame (-> grid :params))]
(->>
(range 0 num-items)
(map #(hash-map :x (next-x %)
@ -75,27 +82,35 @@
:width item-width
:height item-height)))))
(defn- layout-rect-points [{:keys [x y width height]}]
(defn grid-area-points
[{:keys [x y width height]}]
[(gpt/point x y)
(gpt/point (+ x width) y)
(gpt/point (+ x width) (+ y height))
(gpt/point x (+ y height))])
(defn- layout-snap-points
([shape coord] (mapcat #(layout-snap-points shape % coord) (:layouts shape)))
([shape {:keys [type display params] :as layout} coord]
(defn grid-snap-points
"Returns the snap points for a given grid"
([shape coord] (mapcat #(grid-snap-points shape % coord) (:grids shape)))
([shape {:keys [type display params] :as grid} coord]
(when (:display grid)
(case type
:square
(let [{:keys [x y width height]} shape
size (-> params :size)]
(when (> size 0)
(if (= coord :x)
(mapcat #(vector (gpt/point (+ x %) y)
(gpt/point (+ x %) (+ y height))) (range size width size))
(mapcat #(vector (gpt/point x (+ y %))
(gpt/point (+ x width) (+ y %))) (range size height size)))))
(case type
:square (let [{:keys [x y width height]} shape
size (-> params :size)]
(when (> size 0)
(if (= coord :x)
(mapcat #(vector (gpt/point (+ x %) y)
(gpt/point (+ x %) (+ y height))) (range size width size))
(mapcat #(vector (gpt/point x (+ y %))
(gpt/point (+ x width) (+ y %))) (range size height size)))))
:column (when (= coord :x) (->> (layout-rects shape layout)
(mapcat layout-rect-points)))
:column
(when (= coord :x)
(->> (grid-areas shape grid)
(mapcat grid-area-points)))
:row (when (= coord :y) (->> (layout-rects shape layout)
(mapcat layout-rect-points))))))
:row
(when (= coord :y)
(->> (grid-areas shape grid)
(mapcat grid-area-points)))))))

View file

@ -14,7 +14,7 @@
[uxbox.util.geom.shapes :as gsh]
[uxbox.util.geom.point :as gpt]))
(defn- frame-snap-points [{:keys [x y width height layouts] :as frame}]
(defn- frame-snap-points [{:keys [x y width height] :as frame}]
(into #{(gpt/point x y)
(gpt/point (+ x (/ width 2)) y)
(gpt/point (+ x width) y)

View file

@ -15,20 +15,23 @@
[uxbox.worker.impl :as impl]
[uxbox.util.range-tree :as rt]
[uxbox.util.geom.snap-points :as snap]
[uxbox.util.geom.layout :as gla]))
[uxbox.util.geom.grid :as gg]))
(defonce state (l/atom {}))
(defn- create-coord-data
"Initializes the range tree given the shapes"
[shapes coord]
[frame-id shapes coord]
(let [process-shape (fn [coord]
(fn [shape]
(concat
(let [points (snap/shape-snap-points shape)]
(map #(vector % (:id shape)) points))
(let [points (gla/layout-snap-points shape coord)]
(map #(vector % :layout) points)))))
;; The grid points are only added by the "root" of the coord-dat
(if (= (:id shape) frame-id)
(let [points (gg/grid-snap-points shape coord)]
(map #(vector % :layout) points))))))
into-tree (fn [tree [point _ :as data]]
(rt/insert tree (coord point) data))]
(->> shapes
@ -38,7 +41,7 @@
(defn- mapm
"Map over the values of a map"
[mfn coll]
(into {} (map (fn [[key val]] [key (mfn val)]) coll)))
(into {} (map (fn [[key val]] [key (mfn key val)]) coll)))
(defn- initialize-snap-data
"Initialize the snap information with the current workspace information"
@ -48,8 +51,8 @@
(group-by :frame-id))
frame-shapes (->> (cp/select-frames objects)
(reduce #(update %1 (:id %2) conj %2) frame-shapes))]
(mapm (fn [shapes] {:x (create-coord-data shapes :x)
:y (create-coord-data shapes :y)})
(mapm (fn [frame-id shapes] {:x (create-coord-data frame-id shapes :x)
:y (create-coord-data frame-id shapes :y)})
frame-shapes)))
(defn- log-state