From 0b4996b31a77589410391ed275b60d96bc515de5 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 14 May 2020 14:08:17 +0200 Subject: [PATCH 01/11] :tada: Grid and layout UI --- common/uxbox/common/data.cljc | 10 +- .../resources/styles/common/framework.scss | 3 + .../partials/sidebar-element-options.scss | 139 +++++++++++++ frontend/src/uxbox/main/data/workspace.cljs | 38 ++++ frontend/src/uxbox/main/ui/colorpicker.cljs | 1 - .../main/ui/components/context_menu.cljs | 9 + .../main/ui/components/editable_label.cljs | 9 + .../src/uxbox/main/ui/components/select.cljs | 51 +++++ .../main/ui/components/tab_container.cljs | 9 + frontend/src/uxbox/main/ui/icons.cljs | 1 + .../main/ui/workspace/layout_display.cljs | 102 ++++++++++ .../uxbox/main/ui/workspace/shapes/frame.cljs | 14 +- .../ui/workspace/sidebar/options/frame.cljs | 28 +-- .../sidebar/options/grid_options.cljs | 188 ++++++++++++++++++ .../sidebar/options/rows/color_row.cljs | 90 +++++++++ .../sidebar/options/rows/input_row.cljs | 30 +++ 16 files changed, 700 insertions(+), 22 deletions(-) create mode 100644 frontend/src/uxbox/main/ui/components/select.cljs create mode 100644 frontend/src/uxbox/main/ui/workspace/layout_display.cljs create mode 100644 frontend/src/uxbox/main/ui/workspace/sidebar/options/grid_options.cljs create mode 100644 frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/color_row.cljs create mode 100644 frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/input_row.cljs diff --git a/common/uxbox/common/data.cljc b/common/uxbox/common/data.cljc index 48ec9b8840..af39f80f26 100644 --- a/common/uxbox/common/data.cljc +++ b/common/uxbox/common/data.cljc @@ -9,7 +9,9 @@ (:refer-clojure :exclude [concat read-string]) (:require [clojure.set :as set] #?(:cljs [cljs.reader :as r] - :clj [clojure.edn :as r]))) + :clj [clojure.edn :as r]) + #?(:cljs [cljs.core :as core] + :clj [clojure.core :as core]))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Data Structures Manipulation @@ -94,6 +96,12 @@ (persistent! (reduce #(dissoc! %1 %2) (transient data) keys))) +(defn remove-at-index + [v index] + (vec (core/concat + (subvec v 0 index) + (subvec v (inc index))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Data Parsing / Conversion ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/frontend/resources/styles/common/framework.scss b/frontend/resources/styles/common/framework.scss index 06bf218c13..965a449ecc 100644 --- a/frontend/resources/styles/common/framework.scss +++ b/frontend/resources/styles/common/framework.scss @@ -366,6 +366,9 @@ ul.slider-dots { // Input amounts &.pixels { + & input { + padding-right: 20px; + } &::after { content: "px"; diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index 7acfadd063..e40ab994b7 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -263,6 +263,11 @@ padding: $x-small $big $x-small $x-small; position: relative; + & hr { + margin: 0; + border-color: $color-gray-20; + } + .dropdown-button { position: absolute; right: $x-small; @@ -321,6 +326,30 @@ } } } + + & li.checked-element { + padding-left: 0; + + & span { + margin: 0; + color: $color-black; + } + + & svg { + visibility: hidden; + width: 8px; + height: 8px; + background: none; + margin: 0.25rem; + fill: $color-black; + } + + &.is-selected { + & svg { + visibility: visible; + } + } + } } .editable-select { @@ -535,3 +564,113 @@ } } } + +.custom-button { + cursor: pointer; + background: none; + border: none; + + & svg { + width: 1rem; + height: 1rem; + fill: $color-gray-20; + } +} + +.element-set-content .input-row { + & .element-set-subtitle { + width: 5.5rem; + } +} + +.grid-option { + margin-bottom: 0.5rem; +} + +.element-set-content .grid-option-main { + display: flex; + padding: 0.5rem 0; + border: 1px solid $color-black; + border-radius: 4px; + + &:hover { + background: #1F1F1F; + } + + & .custom-select { + height: 2rem; + border: none; + border-bottom: 1px solid #65666A; + } + + & .input-element { + width: 50px; + overflow: hidden; + } + + & .custom-select-dropdown { + width: 96px; + } + + & .input-option { + margin-left: 0.5rem; + + & .custom-select-dropdown { + width: 56px; + min-width: 56px; + max-height: 10rem; + } + + } + +} + +.grid-option-main-actions { + display: flex; + visibility: hidden; + + .grid-option:hover & { + visibility: visible; + } +} + +.focus-overlay { + background: $color-black; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; + opacity: 0.4; +} + +.element-set-content .advanced-options { + background-color: #303236; + border-radius: 4px; + left: -8px; + padding: 0.5rem; + position: relative; + top: 2px; + width: calc(100% + 16px); +} + +.btn-options { + cursor: pointer; + border: 1px solid $color-black; + background: #1F1F1F; + border-radius: 2px; + color: #B1B2B5; + font-size: 11px; + line-height: 16px; + flex-grow: 1; + padding: 0.25rem 0; + + &:first-child { + margin-right: 0.5rem; + } + + &:hover { + background: $color-primary; + color: $color-black; + } +} diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index 2e40877952..55c94cf262 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -1487,6 +1487,44 @@ (rx/of (update-shape shape-id {:interactions []})))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Layouts +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(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 {:size 16 :color {:value "#59B9E2" :opacity 0.9}} + 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)))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Exports ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/frontend/src/uxbox/main/ui/colorpicker.cljs b/frontend/src/uxbox/main/ui/colorpicker.cljs index 308cee66cd..ce7b333364 100644 --- a/frontend/src/uxbox/main/ui/colorpicker.cljs +++ b/frontend/src/uxbox/main/ui/colorpicker.cljs @@ -25,7 +25,6 @@ :onChangeComplete on-change-complete :style {:box-shadow "none"}}])) - (def most-used-colors (letfn [(selector [{:keys [objects]}] (as-> {} $ diff --git a/frontend/src/uxbox/main/ui/components/context_menu.cljs b/frontend/src/uxbox/main/ui/components/context_menu.cljs index a9ae18334c..df6fa4f691 100644 --- a/frontend/src/uxbox/main/ui/components/context_menu.cljs +++ b/frontend/src/uxbox/main/ui/components/context_menu.cljs @@ -1,3 +1,12 @@ +;; 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.ui.components.context-menu (:require [rumext.alpha :as mf] diff --git a/frontend/src/uxbox/main/ui/components/editable_label.cljs b/frontend/src/uxbox/main/ui/components/editable_label.cljs index 8ce7c65079..07b36cf267 100644 --- a/frontend/src/uxbox/main/ui/components/editable_label.cljs +++ b/frontend/src/uxbox/main/ui/components/editable_label.cljs @@ -1,3 +1,12 @@ +;; 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.ui.components.editable-label (:require [rumext.alpha :as mf] diff --git a/frontend/src/uxbox/main/ui/components/select.cljs b/frontend/src/uxbox/main/ui/components/select.cljs new file mode 100644 index 0000000000..fd733ea465 --- /dev/null +++ b/frontend/src/uxbox/main/ui/components/select.cljs @@ -0,0 +1,51 @@ +;; 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.ui.components.select + (:require + [rumext.alpha :as mf] + [uxbox.common.uuid :as uuid] + [uxbox.main.ui.icons :as i] + [uxbox.main.ui.components.dropdown :refer [dropdown]])) + +(mf/defc select [{:keys [default-value options class on-change]}] + (let [state (mf/use-state {:id (uuid/next) + :is-open? false + :current-value default-value}) + open-dropdown #(swap! state assoc :is-open? true) + close-dropdown #(swap! state assoc :is-open? false) + select-item (fn [value] (fn [event] + (swap! state assoc :current-value value) + (when on-change (on-change value)))) + as-key-value (fn [item] (if (map? item) [(:value item) (:label item)] [item item])) + value->label (into {} (->> options + (map as-key-value))) ] + + (mf/use-effect + (mf/deps options) + #(reset! state {:is-open? false + :current-value default-value})) + + [:div.custom-select {:on-click open-dropdown + :class class} + [:span (-> @state :current-value value->label)] + [:span.dropdown-button i/arrow-down] + [:& dropdown {:show (:is-open? @state) + :on-close close-dropdown} + [:ul.custom-select-dropdown + (for [[index item] (map-indexed vector options)] + (cond + (= :separator item) [:hr {:key (str (:id @state) "-" index)}] + :else (let [[value label] (as-key-value item)] + [:li.checked-element + {:key (str (:id @state) "-" index) + :class (when (= value (-> @state :current-value)) "is-selected") + :on-click (select-item value)} + [:span.check-icon i/tick] + [:span label]])))]]])) diff --git a/frontend/src/uxbox/main/ui/components/tab_container.cljs b/frontend/src/uxbox/main/ui/components/tab_container.cljs index 78accbbcc9..29292600b0 100644 --- a/frontend/src/uxbox/main/ui/components/tab_container.cljs +++ b/frontend/src/uxbox/main/ui/components/tab_container.cljs @@ -1,3 +1,12 @@ +;; 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.ui.components.tab-container (:require [rumext.alpha :as mf])) diff --git a/frontend/src/uxbox/main/ui/icons.cljs b/frontend/src/uxbox/main/ui/icons.cljs index b8d8efcc03..85f11ea14b 100644 --- a/frontend/src/uxbox/main/ui/icons.cljs +++ b/frontend/src/uxbox/main/ui/icons.cljs @@ -110,6 +110,7 @@ (def unlock (icon-xref :unlock)) (def uppercase (icon-xref :uppercase)) (def user (icon-xref :user)) +(def tick (icon-xref :tick)) (def loader-pencil (mf/html diff --git a/frontend/src/uxbox/main/ui/workspace/layout_display.cljs b/frontend/src/uxbox/main/ui/workspace/layout_display.cljs new file mode 100644 index 0000000000..01fe1e6c2e --- /dev/null +++ b/frontend/src/uxbox/main/ui/workspace/layout_display.cljs @@ -0,0 +1,102 @@ +;; 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.ui.workspace.layout-display + (:require + [rumext.alpha :as mf] + [uxbox.main.refs :as refs])) + +(mf/defc grid-layout [{:keys [frame zoom params] :as props}] + (let [{:keys [color size]} params + {color-value :value color-opacity :opacity} (:color params) + {frame-width :width frame-height :height :keys [x y]} frame] + [:g.grid + [:* + (for [xs (range size frame-width size)] + [:line {:key (str (:id frame) "-y-" xs) + :x1 (+ x xs) + :y1 y + :x2 (+ x xs) + :y2 (+ y frame-height) + :style {:stroke color-value + :stroke-opacity color-opacity + :stroke-width (str (/ 1 zoom))}}]) + (for [ys (range size frame-height size)] + [:line {:key (str (:id frame) "-x-" ys) + :x1 x + :y1 (+ y ys) + :x2 (+ x frame-width) + :y2 (+ y ys) + :style {:stroke color-value + :stroke-opacity color-opacity + :stroke-width (str (/ 1 zoom))}}])]])) + +(defn calculate-column-layout [frame size gutter margin item-width layout-type] + (let [{:keys [width height x y]} frame + parts (/ width size) + item-width (or item-width (+ parts (- gutter) (/ gutter size) (- (/ (* margin 2) size)))) + item-height height + initial-offset (case layout-type + :right (- width (* item-width size) (* gutter (dec size)) margin) + :center (/ (- width (* item-width size) (* gutter (dec size))) 2) + margin) + gutter (if (= :stretch layout-type) (/ (- width (* item-width size) (* margin 2)) (dec size)) gutter) + next-x (fn [cur-val] (+ initial-offset x (* (+ item-width gutter) cur-val))) + next-y (fn [cur-val] y)] + [parts item-width item-height next-x next-y])) + +(defn calculate-row-layout [frame size gutter margin item-height layout-type] + (let [{:keys [width height x y]} frame + parts (/ height size) + item-width width + item-height (or item-height (+ parts (- gutter) (/ gutter size) (- (/ (* margin 2) size)))) + initial-offset (case layout-type + :right (- height (* item-height size) (* gutter (dec size)) margin) + :center (/ (- height (* item-height size) (* gutter (dec size))) 2) + margin) + gutter (if (= :stretch layout-type) (/ (- height (* item-height size) (* margin 2)) (dec size)) gutter) + next-x (fn [cur-val] x) + next-y (fn [cur-val] (+ initial-offset y (* (+ item-height gutter) cur-val)))] + [parts item-width item-height next-x next-y])) + +(mf/defc flex-layout [{:keys [frame zoom params orientation]}] + (let [{:keys [color size type gutter margin item-width item-height]} params + {color-value :value color-opacity :opacity} (:color params) + + ;; calculates the layout configuration + [parts item-width item-height next-x next-y] + (if (= orientation :column) + (calculate-column-layout frame size gutter margin item-width type) + (calculate-row-layout frame size gutter margin item-height type))] + + (for [cur-val (range 0 size)] + [:rect {:x (next-x cur-val) + :y (next-y cur-val) + :width item-width + :height item-height + :style {:pointer-events "none" + :fill color-value + :opacity color-opacity}}]))) + +(mf/defc layout-display [{:keys [frame]}] + (let [zoom (mf/deref refs/selected-zoom) + layouts (:layouts frame)] + (for [[index {:keys [type display params]}] (map-indexed vector layouts)] + (let [props #js {:key (str (:id frame) "-layout-" index) + :frame frame + :zoom zoom + :params params + :orientation (cond (= type :column) :column + (= type :row) :row + :else nil) }] + (when display + (case type + :square [:> grid-layout props] + :column [:> flex-layout props] + :row [:> flex-layout props])))))) diff --git a/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs b/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs index c4471b231b..1cd176ea77 100644 --- a/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs @@ -22,7 +22,8 @@ [uxbox.util.geom.shapes :as geom] [uxbox.util.dom :as dom] [uxbox.main.streams :as ms] - [uxbox.util.timers :as ts])) + [uxbox.util.timers :as ts] + [uxbox.main.ui.workspace.layout-display :refer [layout-display]])) (defn- frame-wrapper-factory-equals? [np op] @@ -64,15 +65,14 @@ on-context-menu (mf/use-callback (mf/deps shape) #(common/on-context-menu % shape)) - + shape (geom/transform-shape shape) {:keys [x y width height]} shape inv-zoom (/ 1 zoom) childs (mapv #(get objects %) (:shapes shape)) ds-modifier (get-in shape [:modifiers :displacement]) - label-pos (cond-> (gpt/point x (- y 10)) - (gmt/matrix? ds-modifier) (gpt/transform ds-modifier)) + label-pos (gpt/point x (- y 10)) on-double-click (mf/use-callback @@ -104,7 +104,7 @@ :on-click on-double-click} (:name shape)] [:& frame-shape - {:shape (geom/transform-shape shape) - :childs childs}]]))))) - + {:shape shape + :childs childs}] + [:& layout-display {:frame shape}]]))))) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs index d0ad8bec07..19df4cba4d 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs @@ -12,16 +12,17 @@ (:require [rumext.alpha :as mf] [uxbox.common.data :as d] - [uxbox.main.ui.icons :as i] - [uxbox.main.data.workspace :as udw] - [uxbox.main.store :as st] - [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.util.dom :as dom] [uxbox.util.geom.point :as gpt] [uxbox.util.i18n :refer [tr]] - [uxbox.util.math :as math])) + [uxbox.util.math :as math] + [uxbox.main.store :as st] + [uxbox.main.data.workspace :as udw] + [uxbox.main.ui.icons :as i] + [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.grid-options :refer [grid-options]])) (declare +size-presets+) @@ -82,11 +83,11 @@ [:div.custom-select.flex-grow {:on-click #(reset! show-presets-dropdown? true)} [:span (tr "workspace.options.size-presets")] [:span.dropdown-button i/arrow-down] - [:& dropdown {:show @show-presets-dropdown? - :on-close #(reset! show-presets-dropdown? false)} - [:ul.custom-select-dropdown - (for [size-preset +size-presets+] - (if-not (:width size-preset) + [:& dropdown {:show @show-presets-dropdown? + :on-close #(reset! show-presets-dropdown? false)} + [:ul.custom-select-dropdown + (for [size-preset +size-presets+] + (if-not (:width size-preset) [:li.dropdown-label {:key (:name size-preset)} [:span (:name size-preset)]] [:li {:key (:name size-preset) @@ -202,4 +203,5 @@ [:div [:& measures-menu {:shape shape}] [:& fill-menu {:shape shape}] - [:& stroke-menu {:shape shape}]]) + [:& stroke-menu {:shape shape}] + [:& grid-options {:shape shape}]]) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/grid_options.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/grid_options.cljs new file mode 100644 index 0000000000..756184d411 --- /dev/null +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/grid_options.cljs @@ -0,0 +1,188 @@ +;; 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.ui.workspace.sidebar.options.grid-options + (:require + [rumext.alpha :as mf] + [uxbox.util.dom :as dom] + [uxbox.util.data :as d] + [uxbox.main.store :as st] + [uxbox.main.data.workspace :as dw] + [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]] + [uxbox.main.ui.components.select :refer [select]] + [uxbox.main.ui.components.dropdown :refer [dropdown]])) + +(mf/defc advanced-options [{:keys [visible? on-close children]}] + (when visible? + [:* + [:div.focus-overlay {:on-click #(when on-close (do + (dom/stop-propagation %) + (on-close)))}] + [:div.advanced-options {} + children]])) + +(defonce ^:private default-params + {:square {:size 16 + :color {:value "#59B9E2" + :opacity 0.9}} + + :column {:size 12 + :type :stretch + :item-width 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}}}) + +(mf/defc grid-option [{:keys [layout on-change on-remove]}] + (let [state (mf/use-state {:show-advanced-options false + :changes {}}) + {:keys [type display params] :as layout} (d/deep-merge layout (:changes @state)) + + toggle-advanced-options #(swap! state update :show-advanced-options not) + + size-options [{:value :auto :label "Auto"} + :separator + 18 12 10 8 6 4 3 2] + + emit-changes! (fn [update-fn] + (swap! state update :changes update-fn) + (when on-change (on-change (d/deep-merge layout (-> @state :changes update-fn))))) + + handle-toggle-visibility (fn [event] + (emit-changes! #(update % :display not))) + + handle-remove-layout (fn [event] + (when on-remove (on-remove))) + + handle-change-type (fn [type] + (let [defaults (type default-params) + params (merge + defaults + (select-keys (keys defaults) (-> @state :changes params))) + to-merge {:type type :params params}] + (emit-changes! #(d/deep-merge % to-merge)))) + + handle-change (fn [& keys] + (fn [value] + (emit-changes! #(assoc-in % keys value)))) + + handle-change-event (fn [& keys] + (fn [event] + (let [change-fn (apply handle-change keys)] + (-> event dom/get-target dom/get-value change-fn)))) + ] + + [:div.grid-option + [:div.grid-option-main + [:button.custom-button {:class (when (:show-advanced-options @state) "is-active") + :on-click toggle-advanced-options} i/actions] + + [:& select {:class "flex-grow" + :default-value type + :options [{:value :square :label "Square"} + {:value :column :label "Columns"} + {:value :row :label "Rows"}] + :on-change handle-change-type}] + + (if (= type :square) + [:div.input-element.pixels + [:input.input-text {:type "number" + :min "0" + :no-validate true + :value (:size params) + :on-change (handle-change-event :params :size)}]] + [:& select {:default-value (:size params) + :class "input-option" + :options size-options + :on-change (handle-change :params :size)}]) + + [: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]]] + + [:& advanced-options {:visible? (:show-advanced-options @state) + :on-close toggle-advanced-options} + (when (= :square type) + [:& input-row {:label "Size" + :value (:size params) + :on-change (handle-change :params :size)}]) + + (when (= :row type) + [:& input-row {:label "Rows" + :options size-options + :value (:size params) + :on-change (handle-change :params :size)}]) + + (when (= :column type) + [:& input-row {:label "Columns" + :options size-options + :value (:size params) + :on-change (handle-change :params :size)}]) + + (when (#{:row :column} type) + [:& input-row {:label "Type" + :options [{:value :stretch :label "Stretch"} + {:value :left :label "Left"} + {:value :center :label "Center"} + {:value :right :label "Right"}] + :value (:type params) + :on-change (handle-change :params :type)}]) + + (when (= :row type) + [:& input-row {:label "Height" + :value (or (:item-height params) "") + :on-change (handle-change :params :item-height)}]) + + (when (= :column type) + [:& input-row {:label "Width" + :value (or (:item-width params) "") + :on-change (handle-change :params :item-width)}]) + + (when (#{:row :column} type) + [:* + [:& input-row {:label "Gutter" + :value (:gutter params) + :on-change (handle-change :params :gutter)}] + [:& input-row {:label "Margin" + :value (:margin params) + :on-change (handle-change :params :margin)}]]) + + [:& color-row {:value (:color params) + :on-change (handle-change :params :color)}] + [:div.row-flex + [:button.btn-options "Use default"] + [:button.btn-options "Set as default"]]]])) + +(mf/defc grid-options [{:keys [shape]}] + (let [id (:id shape) + 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 %)))] + [:div.element-set + [:div.element-set-title + [:span "Grid & Layout"] + [:div.add-page {:on-click handle-create-layout} i/close]] + + [:div.element-set-content + (for [[index layout] (map-indexed vector (:layouts shape))] + [:& grid-option {:key (str (:id shape) "-" index) + :layout layout + :on-change (handle-edit-layout index) + :on-remove (handle-remove-layout index)}])]])) + diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/color_row.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/color_row.cljs new file mode 100644 index 0000000000..ebcb9f120c --- /dev/null +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/color_row.cljs @@ -0,0 +1,90 @@ +;; 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.ui.workspace.sidebar.options.rows.color-row + (:require + [rumext.alpha :as mf] + [uxbox.util.math :as math] + [uxbox.util.dom :as dom] + [uxbox.main.ui.modal :as modal] + [uxbox.main.ui.workspace.colorpicker :refer [colorpicker-modal]] + [uxbox.common.data :as d])) + +(defn color-picker-callback [color handle-change-color] + (fn [event] + (let [x (.-clientX event) + y (.-clientY event) + props {:x x + :y y + :on-change handle-change-color + :value (:value color) + :transparent? true}] + (modal/show! colorpicker-modal props)))) + +(defn opacity->string [opacity] + (str (-> opacity + (d/coalesce 1) + (* 100) + (math/round)))) + +(defn string->opacity [opacity-str] + (-> opacity-str + (d/parse-integer 1) + (/ 100))) + +(mf/defc color-row [{:keys [value on-change]}] + (let [state (mf/use-state value) + change-color (fn [color] + (let [update-color (fn [state] (assoc state :value color))] + (swap! state update-color) + (when on-change (on-change (update-color @state))))) + + change-opacity (fn [opacity] + (let [update-opacity (fn [state] (assoc state :opacity opacity))] + (swap! state update-opacity) + (when on-change (on-change (update-opacity @state))))) + + handle-pick-color (fn [color] + (change-color color)) + + handle-input-color-change (fn [event] + (let [target (dom/get-target event) + value (dom/get-value target)] + (when (dom/valid? target) + (change-color value)))) + handle-opacity-change (fn [event] + (-> event + dom/get-target + dom/get-value + string->opacity + change-opacity))] + + [:div.row-flex.color-data + [:span.color-th + {:style {:background-color (-> @state :value)} + :on-click (color-picker-callback @state handle-pick-color)}] + + [:div.color-info + [:input {:value (-> @state :value) + :pattern "^#(?:[0-9a-fA-F]{3}){1,2}$" + :on-change handle-input-color-change}]] + + [:div.input-element.percentail + [:input.input-text {:type "number" + :value (-> @state :opacity opacity->string) + :on-change handle-opacity-change + :min "0" + :max "100"}]] + + [:input.slidebar {:type "range" + :min "0" + :max "100" + :value (-> @state :opacity opacity->string) + :step "1" + :on-change handle-opacity-change}]])) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/input_row.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/input_row.cljs new file mode 100644 index 0000000000..50dbe8a34a --- /dev/null +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/input_row.cljs @@ -0,0 +1,30 @@ +;; 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.ui.workspace.sidebar.options.rows.input-row + (:require + [rumext.alpha :as mf] + [uxbox.common.data :as d] + [uxbox.main.ui.components.select :refer [select]] + [uxbox.util.dom :as dom])) + +(mf/defc input-row [{:keys [label options value on-change]}] + [:div.row-flex.input-row + [:span.element-set-subtitle label] + [:div.input-element + (if options + [:& select {:default-value value + :class "input-option" + :options options + :on-change on-change}] + [:input.input-text + {:placeholder label + :type "number" + :on-change #(-> % dom/get-target dom/get-value d/parse-integer on-change) + :value value}])]]) From 3308d762f1afe99917f4c378586c021da0e3cc25 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 14 May 2020 16:02:45 +0200 Subject: [PATCH 02/11] :tada: Snap to grid --- .../main/ui/workspace/layout_display.cljs | 66 ++++--------------- .../ui/workspace/sidebar/options/frame.cljs | 4 +- .../{grid_options.cljs => frame_layouts.cljs} | 14 ++-- frontend/src/uxbox/util/geom/layout.cljs | 49 ++++++++++++++ frontend/src/uxbox/util/geom/snap_points.cljs | 45 ++++++------- 5 files changed, 91 insertions(+), 87 deletions(-) rename frontend/src/uxbox/main/ui/workspace/sidebar/options/{grid_options.cljs => frame_layouts.cljs} (94%) create mode 100644 frontend/src/uxbox/util/geom/layout.cljs diff --git a/frontend/src/uxbox/main/ui/workspace/layout_display.cljs b/frontend/src/uxbox/main/ui/workspace/layout_display.cljs index 01fe1e6c2e..9702b0e1ec 100644 --- a/frontend/src/uxbox/main/ui/workspace/layout_display.cljs +++ b/frontend/src/uxbox/main/ui/workspace/layout_display.cljs @@ -10,11 +10,12 @@ (ns uxbox.main.ui.workspace.layout-display (:require [rumext.alpha :as mf] - [uxbox.main.refs :as refs])) + [uxbox.main.refs :as refs] + [uxbox.util.geom.layout :as ula])) -(mf/defc grid-layout [{:keys [frame zoom params] :as props}] - (let [{:keys [color size]} params - {color-value :value color-opacity :opacity} (:color params) +(mf/defc grid-layout [{:keys [frame zoom layout] :as props}] + (let [{:keys [color size] :as params} (-> layout :params) + {color-value :value color-opacity :opacity} color {frame-width :width frame-height :height :keys [x y]} frame] [:g.grid [:* @@ -37,49 +38,13 @@ :stroke-opacity color-opacity :stroke-width (str (/ 1 zoom))}}])]])) -(defn calculate-column-layout [frame size gutter margin item-width layout-type] - (let [{:keys [width height x y]} frame - parts (/ width size) - item-width (or item-width (+ parts (- gutter) (/ gutter size) (- (/ (* margin 2) size)))) - item-height height - initial-offset (case layout-type - :right (- width (* item-width size) (* gutter (dec size)) margin) - :center (/ (- width (* item-width size) (* gutter (dec size))) 2) - margin) - gutter (if (= :stretch layout-type) (/ (- width (* item-width size) (* margin 2)) (dec size)) gutter) - next-x (fn [cur-val] (+ initial-offset x (* (+ item-width gutter) cur-val))) - next-y (fn [cur-val] y)] - [parts item-width item-height next-x next-y])) - -(defn calculate-row-layout [frame size gutter margin item-height layout-type] - (let [{:keys [width height x y]} frame - parts (/ height size) - item-width width - item-height (or item-height (+ parts (- gutter) (/ gutter size) (- (/ (* margin 2) size)))) - initial-offset (case layout-type - :right (- height (* item-height size) (* gutter (dec size)) margin) - :center (/ (- height (* item-height size) (* gutter (dec size))) 2) - margin) - gutter (if (= :stretch layout-type) (/ (- height (* item-height size) (* margin 2)) (dec size)) gutter) - next-x (fn [cur-val] x) - next-y (fn [cur-val] (+ initial-offset y (* (+ item-height gutter) cur-val)))] - [parts item-width item-height next-x next-y])) - -(mf/defc flex-layout [{:keys [frame zoom params orientation]}] - (let [{:keys [color size type gutter margin item-width item-height]} params - {color-value :value color-opacity :opacity} (:color params) - - ;; calculates the layout configuration - [parts item-width item-height next-x next-y] - (if (= orientation :column) - (calculate-column-layout frame size gutter margin item-width type) - (calculate-row-layout frame size gutter margin item-height type))] - - (for [cur-val (range 0 size)] - [:rect {:x (next-x cur-val) - :y (next-y cur-val) - :width item-width - :height item-height +(mf/defc flex-layout [{:keys [frame zoom layout]}] + (let [{color-value :value color-opacity :opacity} (-> layout :params :color)] + (for [{:keys [x y width height]} (ula/layout-rects frame layout)] + [:rect {:x x + :y y + :width width + :height height :style {:pointer-events "none" :fill color-value :opacity color-opacity}}]))) @@ -87,14 +52,11 @@ (mf/defc layout-display [{:keys [frame]}] (let [zoom (mf/deref refs/selected-zoom) layouts (:layouts frame)] - (for [[index {:keys [type display params]}] (map-indexed vector layouts)] + (for [[index {:keys [type display] :as layout}] (map-indexed vector layouts)] (let [props #js {:key (str (:id frame) "-layout-" index) :frame frame :zoom zoom - :params params - :orientation (cond (= type :column) :column - (= type :row) :row - :else nil) }] + :layout layout}] (when display (case type :square [:> grid-layout props] diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs index 19df4cba4d..af6051a39e 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs @@ -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.grid-options :refer [grid-options]])) + [uxbox.main.ui.workspace.sidebar.options.frame-layouts :refer [frame-layouts]])) (declare +size-presets+) @@ -204,4 +204,4 @@ [:& measures-menu {:shape shape}] [:& fill-menu {:shape shape}] [:& stroke-menu {:shape shape}] - [:& grid-options {:shape shape}]]) + [:& frame-layouts {:shape shape}]]) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/grid_options.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs similarity index 94% rename from frontend/src/uxbox/main/ui/workspace/sidebar/options/grid_options.cljs rename to frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs index 756184d411..9f8ea3125b 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/grid_options.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs @@ -7,7 +7,7 @@ ;; ;; Copyright (c) 2020 UXBOX Labs SL -(ns uxbox.main.ui.workspace.sidebar.options.grid-options +(ns uxbox.main.ui.workspace.sidebar.options.frame-layouts (:require [rumext.alpha :as mf] [uxbox.util.dom :as dom] @@ -49,7 +49,7 @@ :color {:value "#DE4762" :opacity 0.1}}}) -(mf/defc grid-option [{:keys [layout on-change on-remove]}] +(mf/defc layout-options [{:keys [layout on-change on-remove]}] (let [state (mf/use-state {:show-advanced-options false :changes {}}) {:keys [type display params] :as layout} (d/deep-merge layout (:changes @state)) @@ -169,7 +169,7 @@ [:button.btn-options "Use default"] [:button.btn-options "Set as default"]]]])) -(mf/defc grid-options [{:keys [shape]}] +(mf/defc frame-layouts [{:keys [shape]}] (let [id (:id shape) handle-create-layout #(st/emit! (dw/add-frame-layout id)) handle-remove-layout (fn [index] #(st/emit! (dw/remove-frame-layout id index))) @@ -181,8 +181,8 @@ [:div.element-set-content (for [[index layout] (map-indexed vector (:layouts shape))] - [:& grid-option {:key (str (:id shape) "-" index) - :layout layout - :on-change (handle-edit-layout index) - :on-remove (handle-remove-layout index)}])]])) + [:& layout-options {:key (str (:id shape) "-" index) + :layout layout + :on-change (handle-edit-layout index) + :on-remove (handle-remove-layout index)}])]])) diff --git a/frontend/src/uxbox/util/geom/layout.cljs b/frontend/src/uxbox/util/geom/layout.cljs new file mode 100644 index 0000000000..164edea37d --- /dev/null +++ b/frontend/src/uxbox/util/geom/layout.cljs @@ -0,0 +1,49 @@ +;; 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.util.geom.layout) + +(defn calculate-column-layout [{:keys [width height x y] :as frame} {:keys [size gutter margin item-width type] :as params}] + (let [parts (/ width size) + item-width (or item-width (+ parts (- gutter) (/ gutter size) (- (/ (* margin 2) size)))) + item-height height + initial-offset (case type + :right (- width (* item-width size) (* gutter (dec size)) margin) + :center (/ (- width (* item-width size) (* gutter (dec size))) 2) + margin) + gutter (if (= :stretch type) (/ (- width (* item-width size) (* margin 2)) (dec size)) gutter) + next-x (fn [cur-val] (+ initial-offset x (* (+ item-width gutter) cur-val))) + next-y (fn [cur-val] y)] + [item-width item-height next-x next-y])) + +(defn calculate-row-layout [{:keys [width height x y] :as frame} {:keys [size gutter margin item-height type] :as params}] + (let [{:keys [width height x y]} frame + parts (/ height size) + item-width width + item-height (or item-height (+ parts (- gutter) (/ gutter size) (- (/ (* margin 2) size)))) + initial-offset (case type + :right (- height (* item-height size) (* gutter (dec size)) margin) + :center (/ (- height (* item-height size) (* gutter (dec size))) 2) + margin) + gutter (if (= :stretch type) (/ (- height (* item-height size) (* margin 2)) (dec size)) gutter) + next-x (fn [cur-val] x) + next-y (fn [cur-val] (+ initial-offset y (* (+ item-height gutter) cur-val)))] + [item-width item-height next-x next-y])) + +(defn layout-rects [frame layout] + (let [[item-width item-height next-x next-y] + (case (-> layout :type) + :column (calculate-column-layout frame (-> layout :params)) + :row (calculate-row-layout frame (-> layout :params)))] + (->> + (range 0 (-> layout :params :size)) + (map #(hash-map :x (next-x %) + :y (next-y %) + :width item-width + :height item-height))))) diff --git a/frontend/src/uxbox/util/geom/snap_points.cljs b/frontend/src/uxbox/util/geom/snap_points.cljs index a8274a5662..609b06cc95 100644 --- a/frontend/src/uxbox/util/geom/snap_points.cljs +++ b/frontend/src/uxbox/util/geom/snap_points.cljs @@ -12,38 +12,31 @@ [cljs.spec.alpha :as s] [clojure.set :as set] [uxbox.util.geom.shapes :as gsh] - [uxbox.util.geom.point :as gpt])) + [uxbox.util.geom.point :as gpt] + [uxbox.util.geom.layout :as gla])) -(defn- frame-snap-points [{:keys [x y width height]}] +(defn- layout-rect-snaps [{:keys [x y width height]}] #{(gpt/point x y) - (gpt/point (+ x (/ width 2)) y) (gpt/point (+ x width) y) - (gpt/point (+ x width) (+ y (/ height 2))) (gpt/point (+ x width) (+ y height)) - (gpt/point (+ x (/ width 2)) (+ y height)) - (gpt/point x (+ y height)) - (gpt/point x (+ y (/ height 2)))}) + (gpt/point x (+ y height))}) -(defn- frame-snap-points-resize [{:keys [x y width height]} handler] - (case handler - :top-left (gpt/point x y) - :top (gpt/point (+ x (/ width 2)) y) - :top-right (gpt/point (+ x width) y) - :right (gpt/point (+ x width) (+ y (/ height 2))) - :bottom-right (gpt/point (+ x width) (+ y height)) - :bottom (gpt/point (+ x (/ width 2)) (+ y height)) - :bottom-left (gpt/point x (+ y height)) - :left (gpt/point x (+ y (/ height 2))))) +(defn- layout-snap-points [frame {:keys [type] :as layout}] + (mapcat layout-rect-snaps (gla/layout-rects frame layout))) -(def ^:private handler->point-idx - {:top-left 0 - :top 0 - :top-right 1 - :right 1 - :bottom-right 2 - :bottom 2 - :bottom-left 3 - :left 3}) +(defn- frame-snap-points [{:keys [x y width height layouts] :as frame}] + (into #{(gpt/point x y) + (gpt/point (+ x (/ width 2)) y) + (gpt/point (+ x width) y) + (gpt/point (+ x width) (+ y (/ height 2))) + (gpt/point (+ x width) (+ y height)) + (gpt/point (+ x (/ width 2)) (+ y height)) + (gpt/point x (+ y height)) + (gpt/point x (+ y (/ height 2)))} + (->> + layouts + (filter #(and (not= :grid (:type %)) (:display %))) + (mapcat #(layout-snap-points frame %))))) (defn shape-snap-points [shape] From d2229f43c7ecb7e0e8557a2186d85c8af6e42b89 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 15 May 2020 15:00:40 +0200 Subject: [PATCH 03/11] :sparkles: Snap to square grid --- .../partials/sidebar-element-options.scss | 4 + frontend/src/uxbox/main/data/workspace.cljs | 14 +++- frontend/src/uxbox/main/refs.cljs | 9 +++ frontend/src/uxbox/main/ui/workspace.cljs | 3 +- .../main/ui/workspace/layout_display.cljs | 39 +++++---- .../src/uxbox/main/ui/workspace/ruler.cljs | 79 ------------------- .../uxbox/main/ui/workspace/shapes/frame.cljs | 6 +- .../sidebar/options/frame_layouts.cljs | 75 ++++++++++-------- .../src/uxbox/main/ui/workspace/viewport.cljs | 20 ++--- frontend/src/uxbox/util/geom/layout.cljs | 53 +++++++++++-- frontend/src/uxbox/util/geom/snap_points.cljs | 18 +---- frontend/src/uxbox/worker/snaps.cljs | 10 ++- 12 files changed, 159 insertions(+), 171 deletions(-) delete mode 100644 frontend/src/uxbox/main/ui/workspace/ruler.cljs diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index e40ab994b7..42756c248c 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -575,6 +575,10 @@ height: 1rem; fill: $color-gray-20; } + + &:hover svg, &.is-active svg { + fill: $color-primary; + } } .element-set-content .input-row { diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index 55c94cf262..699283f89e 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -66,7 +66,8 @@ :layers :element-options :rules - :dynamic-alignment}) + :dynamic-alignment + :layouts}) (s/def ::options-mode #{:design :prototype}) @@ -1525,6 +1526,17 @@ state (assoc-in [:workspace-data pid :objects frame-id :layouts index] data)))))) +(defn set-default-layout [type params] + (ptk/reify ::set-default-layout + dwc/IBatchedChange + + ;; TODO: Save into the backend + ptk/UpdateEvent + (update [_ state] + (-> + state + (assoc-in [:workspace-page :options :saved-layouts type] params))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Exports ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/frontend/src/uxbox/main/refs.cljs b/frontend/src/uxbox/main/refs.cljs index f0e7aab192..155e0ad813 100644 --- a/frontend/src/uxbox/main/refs.cljs +++ b/frontend/src/uxbox/main/refs.cljs @@ -42,6 +42,12 @@ (def workspace-page (l/derived :workspace-page st/state)) +(def workspace-page-options + (l/derived :options workspace-page)) + +(def workspace-saved-layouts + (l/derived :saved-layouts workspace-page-options)) + (def workspace-page-id (l/derived :id workspace-page)) @@ -71,6 +77,9 @@ (def workspace-objects (l/derived :objects workspace-data)) +(def workspace-frames + (l/derived cp/select-frames workspace-objects)) + (defn object-by-id [id] (letfn [(selector [state] diff --git a/frontend/src/uxbox/main/ui/workspace.cljs b/frontend/src/uxbox/main/ui/workspace.cljs index ac12a0887a..f33c86e769 100644 --- a/frontend/src/uxbox/main/ui/workspace.cljs +++ b/frontend/src/uxbox/main/ui/workspace.cljs @@ -73,7 +73,8 @@ [:& viewport {:page page :key (:id page) :file file - :local local}]]] + :local local + :layout layout}]]] [:& left-toolbar {:page page :layout layout}] diff --git a/frontend/src/uxbox/main/ui/workspace/layout_display.cljs b/frontend/src/uxbox/main/ui/workspace/layout_display.cljs index 9702b0e1ec..f04918b480 100644 --- a/frontend/src/uxbox/main/ui/workspace/layout_display.cljs +++ b/frontend/src/uxbox/main/ui/workspace/layout_display.cljs @@ -11,13 +11,15 @@ (: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])) (mf/defc grid-layout [{:keys [frame zoom layout] :as props}] (let [{:keys [color size] :as params} (-> layout :params) - {color-value :value color-opacity :opacity} color + {color-value :value color-opacity :opacity} (-> layout :params :color) {frame-width :width frame-height :height :keys [x y]} frame] - [:g.grid + [:g.layout [:* (for [xs (range size frame-width size)] [:line {:key (str (:id frame) "-y-" xs) @@ -38,20 +40,20 @@ :stroke-opacity color-opacity :stroke-width (str (/ 1 zoom))}}])]])) -(mf/defc flex-layout [{:keys [frame zoom layout]}] +(mf/defc flex-layout [{:keys [key frame zoom layout]}] (let [{color-value :value color-opacity :opacity} (-> layout :params :color)] - (for [{:keys [x y width height]} (ula/layout-rects frame layout)] - [:rect {:x x - :y y - :width width - :height height - :style {:pointer-events "none" - :fill color-value - :opacity color-opacity}}]))) + [:g.layout + (for [{:keys [x y width height]} (ula/layout-rects frame layout)] + [:rect {:key (str key "-" x "-" y) + :x x + :y y + :width width + :height height + :style {:fill color-value + :opacity color-opacity}}])])) -(mf/defc layout-display [{:keys [frame]}] - (let [zoom (mf/deref refs/selected-zoom) - layouts (:layouts frame)] +(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) :frame frame @@ -62,3 +64,12 @@ :square [:> grid-layout props] :column [:> flex-layout props] :row [:> flex-layout props])))))) + + +(mf/defc layout-display [{:keys [zoom]}] + (let [frames (mf/deref refs/workspace-frames)] + [:g.layout-display {:style {:pointer-events "none"}} + (for [frame frames] + [:& layout-display-frame {:key (str "layout-" (:id frame)) + :zoom zoom + :frame (gsh/transform-shape frame)}])])) diff --git a/frontend/src/uxbox/main/ui/workspace/ruler.cljs b/frontend/src/uxbox/main/ui/workspace/ruler.cljs deleted file mode 100644 index e4ec96db03..0000000000 --- a/frontend/src/uxbox/main/ui/workspace/ruler.cljs +++ /dev/null @@ -1,79 +0,0 @@ -;; 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) 2015-2017 Juan de la Cruz -;; Copyright (c) 2015-2019 Andrey Antukh - -(ns uxbox.main.ui.workspace.ruler - (:require - [rumext.alpha :as mf] - [uxbox.main.constants :as c] - [uxbox.main.data.workspace :as udw] - [uxbox.main.store :as st] - [uxbox.util.dom :as dom] - [uxbox.util.geom.point :as gpt] - [uxbox.util.math :as mth])) - -(mf/defc ruler-text - [{:keys [zoom ruler] :as props}] - #_(let [{:keys [start end]} ruler - distance (-> (gpt/distance (gpt/divide end zoom) - (gpt/divide start zoom)) - (mth/precision 2)) - angle (-> (gpt/angle end start) - (mth/precision 2)) - transform1 (str "translate(" (+ (:x end) 35) "," (- (:y end) 10) ")") - transform2 (str "translate(" (+ (:x end) 25) "," (- (:y end) 30) ")")] - [:g - [:rect {:fill "black" - :fill-opacity "0.4" - :rx "3" - :ry "3" - :width "90" - :height "50" - :transform transform2}] - [:text {:transform transform1 - :fill "white"} - [:tspan {:x "0"} - (str distance " px")] - [:tspan {:x "0" :y "20"} - (str angle "°")]]])) - -(mf/defc ruler-line - [{:keys [zoom ruler] :as props}] - #_(let [{:keys [start end]} ruler] - [:line {:x1 (:x start) - :y1 (:y start) - :x2 (:x end) - :y2 (:y end) - :style {:cursor "cell"} - :stroke-width "1" - :stroke "red"}])) - -(mf/defc ruler - [{:keys [ruler zoom] :as props}] - #_(letfn [(on-mouse-down [event] - (dom/stop-propagation event) - (st/emit! :interrupt - (udw/assign-cursor-tooltip nil) - (udw/start-ruler))) - (on-mouse-up [event] - (dom/stop-propagation event) - (st/emit! :interrupt)) - (on-unmount [] - (st/emit! :interrupt - (udw/clear-ruler)))] - (mf/use-effect (constantly on-unmount)) - [:svg {:on-mouse-down on-mouse-down - :on-mouse-up on-mouse-up} - [:rect {:style {:fill "transparent" - :stroke "transparent" - :cursor "cell"} - :width c/viewport-width - :height c/viewport-height}] - (when ruler - [:g - [:& ruler-line {:ruler ruler}] - [:& ruler-text {:ruler ruler :zoom zoom}]])])) - diff --git a/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs b/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs index 1cd176ea77..28cadea2c0 100644 --- a/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs @@ -22,8 +22,7 @@ [uxbox.util.geom.shapes :as geom] [uxbox.util.dom :as dom] [uxbox.main.streams :as ms] - [uxbox.util.timers :as ts] - [uxbox.main.ui.workspace.layout-display :refer [layout-display]])) + [uxbox.util.timers :as ts])) (defn- frame-wrapper-factory-equals? [np op] @@ -105,6 +104,5 @@ (:name shape)] [:& frame-shape {:shape shape - :childs childs}] - [:& layout-display {:frame shape}]]))))) + :childs childs}]]))))) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs index 9f8ea3125b..8c3b772a1a 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs @@ -12,7 +12,9 @@ [rumext.alpha :as mf] [uxbox.util.dom :as dom] [uxbox.util.data :as d] + [uxbox.common.data :refer [parse-integer]] [uxbox.main.store :as st] + [uxbox.main.refs :as refs] [uxbox.main.data.workspace :as dw] [uxbox.main.ui.icons :as i] [uxbox.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]] @@ -29,27 +31,8 @@ [:div.advanced-options {} children]])) -(defonce ^:private default-params - {:square {:size 16 - :color {:value "#59B9E2" - :opacity 0.9}} - - :column {:size 12 - :type :stretch - :item-width 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}}}) - -(mf/defc layout-options [{:keys [layout on-change on-remove]}] +(mf/defc layout-options [{:keys [layout default-layout-params on-change on-remove]}] + (prn "(render) layout" layout) (let [state (mf/use-state {:show-advanced-options false :changes {}}) {:keys [type display params] :as layout} (d/deep-merge layout (:changes @state)) @@ -61,17 +44,18 @@ 18 12 10 8 6 4 3 2] emit-changes! (fn [update-fn] - (swap! state update :changes update-fn) - (when on-change (on-change (d/deep-merge layout (-> @state :changes update-fn))))) + (swap! state update :changes update-fn) + (prn "(event) layout" (d/deep-merge layout (-> @state :changes update-fn))) + (when on-change (on-change (d/deep-merge layout (-> @state :changes update-fn))))) handle-toggle-visibility (fn [event] - (emit-changes! #(update % :display not))) + (emit-changes! (fn [changes] (update changes :display #(if (nil? %) false (not %)))))) handle-remove-layout (fn [event] (when on-remove (on-remove))) handle-change-type (fn [type] - (let [defaults (type default-params) + (let [defaults (type default-layout-params) params (merge defaults (select-keys (keys defaults) (-> @state :changes params))) @@ -85,7 +69,7 @@ handle-change-event (fn [& keys] (fn [event] (let [change-fn (apply handle-change keys)] - (-> event dom/get-target dom/get-value change-fn)))) + (-> event dom/get-target dom/get-value parse-integer change-fn)))) ] [:div.grid-option @@ -169,20 +153,45 @@ [:button.btn-options "Use default"] [:button.btn-options "Set as default"]]]])) +(defonce ^:private default-layout-params + {:square {:size 16 + :color {:value "#59B9E2" + :opacity 0.9}} + + :column {:size 12 + :type :stretch + :item-width 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}}}) + (mf/defc frame-layouts [{:keys [shape]}] (let [id (:id shape) + default-layout-params (merge 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-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))))] [:div.element-set [:div.element-set-title [:span "Grid & Layout"] [:div.add-page {:on-click handle-create-layout} i/close]] - [:div.element-set-content - (for [[index layout] (map-indexed vector (:layouts shape))] - [:& layout-options {:key (str (:id shape) "-" index) - :layout layout - :on-change (handle-edit-layout index) - :on-remove (handle-remove-layout index)}])]])) + (when (not (empty? (:layouts 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 + :on-change (handle-edit-layout index) + :on-remove (handle-remove-layout index) + :on-save-layout handle-save-layout}])])])) diff --git a/frontend/src/uxbox/main/ui/workspace/viewport.cljs b/frontend/src/uxbox/main/ui/workspace/viewport.cljs index 4baa5c6fc1..9819ea115c 100644 --- a/frontend/src/uxbox/main/ui/workspace/viewport.cljs +++ b/frontend/src/uxbox/main/ui/workspace/viewport.cljs @@ -26,11 +26,10 @@ [uxbox.main.ui.workspace.shapes :refer [shape-wrapper frame-wrapper]] [uxbox.main.ui.workspace.shapes.interactions :refer [interactions]] [uxbox.main.ui.workspace.drawarea :refer [draw-area start-drawing]] - [uxbox.main.ui.workspace.grid :refer [grid]] - [uxbox.main.ui.workspace.ruler :refer [ruler]] [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.util.math :as mth] [uxbox.util.dom :as dom] [uxbox.util.object :as obj] @@ -127,7 +126,7 @@ :key (:id item)}]))])) (mf/defc viewport - [{:keys [page local] :as props}] + [{:keys [page local layout] :as props}] (let [{:keys [drawing-tool options-mode zoom @@ -354,6 +353,7 @@ ] (mf/use-effect on-mount) + [:svg.viewport {:preserveAspectRatio "xMidYMid meet" :width (:width vport 0) @@ -381,22 +381,18 @@ :zoom zoom :edition edition}]) - (when-let [drawing-shape (:drawing local)] [:& draw-area {:shape drawing-shape :zoom zoom :modifiers (:modifiers local)}]) + (when (contains? layout :layouts) + [:& layout-display {:zoom zoom}]) + [:& snap-feedback] - (when (contains? flags :grid) - [:& grid])] - - (when tooltip - [:& cursor-tooltip {:zoom zoom :tooltip tooltip}]) - - (when (contains? flags :ruler) - [:& ruler {:zoom zoom :ruler (:ruler local)}]) + (when tooltip + [:& cursor-tooltip {:zoom zoom :tooltip tooltip}])] [:& presence/active-cursors {:page page}] [:& selection-rect {:data (:selrect local)}] diff --git a/frontend/src/uxbox/util/geom/layout.cljs b/frontend/src/uxbox/util/geom/layout.cljs index 164edea37d..0e6c232757 100644 --- a/frontend/src/uxbox/util/geom/layout.cljs +++ b/frontend/src/uxbox/util/geom/layout.cljs @@ -7,7 +7,9 @@ ;; ;; Copyright (c) 2020 UXBOX Labs SL -(ns uxbox.util.geom.layout) +(ns uxbox.util.geom.layout + (:require + [uxbox.util.geom.point :as gpt])) (defn calculate-column-layout [{:keys [width height x y] :as frame} {:keys [size gutter margin item-width type] :as params}] (let [parts (/ width size) @@ -20,7 +22,7 @@ gutter (if (= :stretch type) (/ (- width (* item-width size) (* margin 2)) (dec size)) gutter) next-x (fn [cur-val] (+ initial-offset x (* (+ item-width gutter) cur-val))) next-y (fn [cur-val] y)] - [item-width item-height next-x next-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-height type] :as params}] (let [{:keys [width height x y]} frame @@ -34,16 +36,51 @@ gutter (if (= :stretch type) (/ (- height (* item-height size) (* margin 2)) (dec size)) gutter) next-x (fn [cur-val] x) next-y (fn [cur-val] (+ initial-offset y (* (+ item-height gutter) cur-val)))] - [item-width item-height next-x next-y])) + [size item-width item-height next-x next-y])) + +(defn calculate-grid-layout [{: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)]) + next-x (fn [cur-val] + (let [[_ col] (as-row-col cur-val)] (+ x (* col size)))) + next-y (fn [cur-val] + (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 [[item-width item-height next-x next-y] - (case (-> layout :type) - :column (calculate-column-layout frame (-> layout :params)) - :row (calculate-row-layout frame (-> layout :params)))] + (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))] (->> - (range 0 (-> layout :params :size)) + (range 0 num-items) (map #(hash-map :x (next-x %) :y (next-y %) :width item-width :height item-height))))) + +(defn- layout-rect-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] + + (case type + :square (let [{:keys [x y width height]} shape + size (-> params :size)] + (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))) + + :row (when (= coord :y) (->> (layout-rects shape layout) + (mapcat layout-rect-points)))))) diff --git a/frontend/src/uxbox/util/geom/snap_points.cljs b/frontend/src/uxbox/util/geom/snap_points.cljs index 609b06cc95..6adbf3dfce 100644 --- a/frontend/src/uxbox/util/geom/snap_points.cljs +++ b/frontend/src/uxbox/util/geom/snap_points.cljs @@ -12,17 +12,7 @@ [cljs.spec.alpha :as s] [clojure.set :as set] [uxbox.util.geom.shapes :as gsh] - [uxbox.util.geom.point :as gpt] - [uxbox.util.geom.layout :as gla])) - -(defn- layout-rect-snaps [{: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 [frame {:keys [type] :as layout}] - (mapcat layout-rect-snaps (gla/layout-rects frame layout))) + [uxbox.util.geom.point :as gpt])) (defn- frame-snap-points [{:keys [x y width height layouts] :as frame}] (into #{(gpt/point x y) @@ -32,11 +22,7 @@ (gpt/point (+ x width) (+ y height)) (gpt/point (+ x (/ width 2)) (+ y height)) (gpt/point x (+ y height)) - (gpt/point x (+ y (/ height 2)))} - (->> - layouts - (filter #(and (not= :grid (:type %)) (:display %))) - (mapcat #(layout-snap-points frame %))))) + (gpt/point x (+ y (/ height 2)))})) (defn shape-snap-points [shape] diff --git a/frontend/src/uxbox/worker/snaps.cljs b/frontend/src/uxbox/worker/snaps.cljs index b1f7c70e1f..e14883860b 100644 --- a/frontend/src/uxbox/worker/snaps.cljs +++ b/frontend/src/uxbox/worker/snaps.cljs @@ -14,7 +14,8 @@ [uxbox.common.pages :as cp] [uxbox.worker.impl :as impl] [uxbox.util.range-tree :as rt] - [uxbox.util.geom.snap-points :as snap])) + [uxbox.util.geom.snap-points :as snap] + [uxbox.util.geom.layout :as gla])) (defonce state (l/atom {})) @@ -23,8 +24,11 @@ [shapes coord] (let [process-shape (fn [coord] (fn [shape] - (let [points (snap/shape-snap-points shape)] - (map #(vector % (:id shape)) points)))) + (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))))) into-tree (fn [tree [point _ :as data]] (rt/insert tree (coord point) data))] (->> shapes From 8d9e772dca6761d6eed3394bc898fb1ec1a3a7c9 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 15 May 2020 15:03:27 +0200 Subject: [PATCH 04/11] :sparkles: Removed some logs --- .../uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs index 8c3b772a1a..261bf17d12 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs @@ -32,7 +32,6 @@ children]])) (mf/defc layout-options [{:keys [layout default-layout-params on-change on-remove]}] - (prn "(render) layout" layout) (let [state (mf/use-state {:show-advanced-options false :changes {}}) {:keys [type display params] :as layout} (d/deep-merge layout (:changes @state)) @@ -45,7 +44,6 @@ emit-changes! (fn [update-fn] (swap! state update :changes update-fn) - (prn "(event) layout" (d/deep-merge layout (-> @state :changes update-fn))) (when on-change (on-change (d/deep-merge layout (-> @state :changes update-fn))))) handle-toggle-visibility (fn [event] From 1d2ae6d5eb783f82f13cc75dcee0ec2a6ca0c7fb Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 18 May 2020 15:30:40 +0200 Subject: [PATCH 05/11] :tada: Add option to save the layouts --- .../tests/uxbox/tests/test_common_pages.clj | 32 ++++ common/uxbox/common/pages.cljc | 12 ++ frontend/resources/locales.json | 154 ++++++++++-------- .../partials/sidebar-element-options.scss | 19 ++- frontend/src/uxbox/main/data/workspace.cljs | 42 +++-- frontend/src/uxbox/main/refs.cljs | 13 +- frontend/src/uxbox/main/snap.cljs | 46 +++--- .../src/uxbox/main/ui/workspace/header.cljs | 16 +- .../main/ui/workspace/layout_display.cljs | 41 ++--- .../sidebar/options/frame_layouts.cljs | 48 +++--- .../ui/workspace/sidebar/options/page.cljs | 87 +--------- .../sidebar/options/rows/color_row.cljs | 8 +- .../sidebar/options/rows/input_row.cljs | 30 ++-- .../main/ui/workspace/snap_feedback.cljs | 7 +- .../src/uxbox/main/ui/workspace/viewport.cljs | 4 +- frontend/src/uxbox/util/geom/layout.cljs | 11 +- frontend/src/uxbox/util/geom/point.cljs | 11 +- 17 files changed, 310 insertions(+), 271 deletions(-) diff --git a/backend/tests/uxbox/tests/test_common_pages.clj b/backend/tests/uxbox/tests/test_common_pages.clj index 9f8213737c..90c9ecec54 100644 --- a/backend/tests/uxbox/tests/test_common_pages.clj +++ b/backend/tests/uxbox/tests/test_common_pages.clj @@ -14,6 +14,38 @@ [uxbox.common.uuid :as uuid] [uxbox.tests.helpers :as th])) +(t/deftest process-change-set-option + (let [data cp/default-page-data] + (t/testing "Sets option single" + (let [chg {:type :set-option + :option :test + :value "test"} + res (cp/process-changes data [chg])] + (t/is (= "test" (get-in res [:options :test]))))) + + (t/testing "Sets option nested" + (let [chgs [{:type :set-option + :option [:values :test :a] + :value "a"} + {:type :set-option + :option [:values :test :b] + :value "b"}] + res (cp/process-changes data chgs)] + (t/is (= {:a "a" :b "b"} (get-in res [:options :values :test]))))) + + (t/testing "Remove option" + (let [chgs [{:type :set-option + :option [:values :test :a] + :value "a"} + {:type :set-option + :option [:values :test :b] + :value "b"} + {:type :set-option + :option [:values :test] + :value nil}] + res (cp/process-changes data chgs)] + (t/is (= nil (get-in res [:options :values :test]))))))) + (t/deftest process-change-add-obj (let [data cp/default-page-data id-a (uuid/next) diff --git a/common/uxbox/common/pages.cljc b/common/uxbox/common/pages.cljc index e02dfbffe1..db454761d3 100644 --- a/common/uxbox/common/pages.cljc +++ b/common/uxbox/common/pages.cljc @@ -253,6 +253,12 @@ (defmulti change-spec-impl :type) +(s/def :set-option/option any? #_(s/or keyword? (s/coll-of keyword?))) +(s/def :set-option/value any?) + +(defmethod change-spec-impl :set-option [_] + (s/keys :req-un [:set-option/option :set-option/value])) + (defmethod change-spec-impl :add-obj [_] (s/keys :req-un [::id ::frame-id ::obj] :opt-un [::session-id ::parent-id])) @@ -313,6 +319,12 @@ (declare insert-at-index) +(defmethod process-change :set-option + [data {:keys [option value]}] + (let [path (if (seqable? option) option [option])] + (-> data + (assoc-in (into [:options] path) value)))) + (defmethod process-change :add-obj [data {:keys [id obj frame-id parent-id index] :as change}] (let [parent-id (or parent-id frame-id) diff --git a/frontend/resources/locales.json b/frontend/resources/locales.json index a4ca8a71de..d0f049c549 100644 --- a/frontend/resources/locales.json +++ b/frontend/resources/locales.json @@ -1,28 +1,28 @@ { "dashboard.grid.delete" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:61", "src/uxbox/main/ui/dashboard/grid.cljs:92" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:102", "src/uxbox/main/ui/dashboard/project.cljs:62" ], "translations" : { "en" : "Delete" } }, "dashboard.grid.edit" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:60", "src/uxbox/main/ui/dashboard/grid.cljs:91" ], "translations" : { "en" : "Edit" + }, + "unused" : true + }, + "dashboard.grid.empty-files" : { + "used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:124" ], + "translations" : { + "en" : "You still have no files here" } }, "dashboard.grid.rename" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:60", "src/uxbox/main/ui/dashboard/grid.cljs:91" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:101", "src/uxbox/main/ui/dashboard/project.cljs:61" ], "translations" : { "en" : "Rename" } }, - "dashboard.grid.empty-files" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:114" ], - "translations" : { - "en" : "You still have no files here" - } - }, "dashboard.header.draft" : { "used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:55" ], "translations" : { @@ -63,7 +63,7 @@ } }, "dashboard.header.project" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:68" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:57" ], "translations" : { "en" : "Project %s" } @@ -176,7 +176,7 @@ } }, "ds.button.delete" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:152", "src/uxbox/main/ui/dashboard/library.cljs:220", "src/uxbox/main/ui/dashboard/library.cljs:257", "src/uxbox/main/ui/dashboard/library.cljs:296" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:152", "src/uxbox/main/ui/dashboard/library.cljs:220", "src/uxbox/main/ui/dashboard/library.cljs:259", "src/uxbox/main/ui/dashboard/library.cljs:300" ], "translations" : { "en" : "Delete" } @@ -257,7 +257,7 @@ "unused" : true }, "ds.new-file" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:110", "src/uxbox/main/ui/dashboard/grid.cljs:116" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:120", "src/uxbox/main/ui/dashboard/grid.cljs:126" ], "translations" : { "en" : "+ New File", "fr" : null @@ -299,7 +299,7 @@ } }, "ds.updated-at" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:35" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:45" ], "translations" : { "en" : "Updated: %s", "fr" : "Mis à jour: %s" @@ -341,21 +341,21 @@ } }, "errors.generic" : { - "used-in" : [ "src/uxbox/main/ui.cljs:179" ], + "used-in" : [ "src/uxbox/main/ui.cljs:178" ], "translations" : { "en" : "Something wrong has happened.", "fr" : "Quelque chose c'est mal passé." } }, "errors.network" : { - "used-in" : [ "src/uxbox/main/ui.cljs:173" ], + "used-in" : [ "src/uxbox/main/ui.cljs:172" ], "translations" : { "en" : "Unable to connect to backend server.", "fr" : "Impossible de se connecter au serveur principal." } }, "header.sitemap" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:74" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:68" ], "translations" : { "en" : null, "fr" : null @@ -459,7 +459,7 @@ } }, "profile.recovery.go-to-login" : { - "used-in" : [ "src/uxbox/main/ui/profile/recovery_request.cljs:65", "src/uxbox/main/ui/profile/recovery.cljs:81" ], + "used-in" : [ "src/uxbox/main/ui/profile/recovery.cljs:81", "src/uxbox/main/ui/profile/recovery_request.cljs:65" ], "translations" : { "en" : "Go back!", "fr" : "Retour!" @@ -702,73 +702,73 @@ } }, "viewer.header.dont-show-interactions" : { - "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:40" ], + "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:67" ], "translations" : { "en" : "Don't show interactions" } }, "viewer.header.edit-page" : { - "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:137" ], + "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:164" ], "translations" : { "en" : "Edit page" } }, "viewer.header.fullscreen" : { - "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:148" ], + "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:175" ], "translations" : { "en" : "Full Screen" } }, "viewer.header.share.copy-link" : { - "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:86" ], + "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:113" ], "translations" : { "en" : "Copy link" } }, "viewer.header.share.create-link" : { - "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:94" ], + "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:121" ], "translations" : { "en" : "Create link" } }, "viewer.header.share.placeholder" : { - "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:84" ], + "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:111" ], "translations" : { "en" : "Share link will apear here" } }, "viewer.header.share.remove-link" : { - "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:92" ], + "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:119" ], "translations" : { "en" : "Remove link" } }, "viewer.header.share.subtitle" : { - "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:88" ], + "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:115" ], "translations" : { "en" : "Anyone with the link will have access" } }, "viewer.header.share.title" : { - "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:72", "src/uxbox/main/ui/viewer/header.cljs:74", "src/uxbox/main/ui/viewer/header.cljs:80" ], + "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:99", "src/uxbox/main/ui/viewer/header.cljs:101", "src/uxbox/main/ui/viewer/header.cljs:107" ], "translations" : { "en" : "Share link" } }, "viewer.header.show-interactions" : { - "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:44" ], + "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:71" ], "translations" : { "en" : "Show interactions" } }, "viewer.header.show-interactions-on-click" : { - "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:48" ], + "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:75" ], "translations" : { "en" : "Show interactions on click" } }, "viewer.header.sitemap" : { - "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:121" ], + "used-in" : [ "src/uxbox/main/ui/viewer/header.cljs:148" ], "translations" : { "en" : "Sitemap" } @@ -821,70 +821,92 @@ "en" : "Align top" } }, + "workspace.header.menu.disable-dynamic-alignment" : { + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:116" ], + "translations" : { + "en" : "Disable dynamic alignment" + } + }, + "workspace.header.menu.disable-snap-grid" : { + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:92" ], + "translations" : { + "en" : "Disable snap to grid" + } + }, + "workspace.header.menu.enable-dynamic-alignment" : { + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:117" ], + "translations" : { + "en" : "Enable dynamic aligment" + } + }, + "workspace.header.menu.enable-snap-grid" : { + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:93" ], + "translations" : { + "en" : "Snap to grid" + } + }, "workspace.header.menu.hide-grid" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:94" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:86" ], "translations" : { "en" : "Hide grid" } }, "workspace.header.menu.hide-layers" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:101" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:98" ], "translations" : { "en" : "Hide layers" } }, "workspace.header.menu.hide-libraries" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:115" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:110" ], "translations" : { "en" : "Hide libraries" } }, "workspace.header.menu.hide-palette" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:108" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:104" ], "translations" : { "en" : "Hide color palette" } }, "workspace.header.menu.hide-rules" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:87" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:80" ], "translations" : { "en" : "Hide rules" } }, "workspace.header.menu.show-grid" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:95" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:87" ], "translations" : { "en" : "Show grid" } }, "workspace.header.menu.show-layers" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:102" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:99" ], "translations" : { "en" : "Show layers" } }, "workspace.header.menu.show-libraries" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:116" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:111" ], "translations" : { "en" : "Show libraries" } }, "workspace.header.menu.show-palette" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:109" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:105" ], "translations" : { "en" : "Show color palette" } }, "workspace.header.menu.show-rules" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:88" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:81" ], "translations" : { "en" : "Show rules" } }, - "workspace.header.menu.disable-dynamic-alignment": "Disable dynamic alignment", - "workspace.header.menu.enable-dynamic-alignment": "Enable dynamic aligment", "workspace.header.viewer" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:153" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:154" ], "translations" : { "en" : "View mode (Ctrl + P)", "fr" : "Mode visualisation (Ctrl + P)" @@ -927,11 +949,11 @@ } }, "workspace.options.color" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/page.cljs:89" ], "translations" : { "en" : "Color", "fr" : "Couleur" - } + }, + "unused" : true }, "workspace.options.design" : { "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options.cljs:76" ], @@ -940,7 +962,7 @@ } }, "workspace.options.fill" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/fill.cljs:69", "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:446" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:446", "src/uxbox/main/ui/workspace/sidebar/options/fill.cljs:71" ], "translations" : { "en" : "Fill", "fr" : "Fond" @@ -1076,11 +1098,11 @@ "unused" : true }, "workspace.options.grid-options" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/page.cljs:76" ], "translations" : { "en" : "Grid settings", "fr" : "Paramètres de la grille" - } + }, + "unused" : true }, "workspace.options.line-height-letter-spacing" : { "translations" : { @@ -1097,13 +1119,13 @@ "unused" : true }, "workspace.options.navigate-to" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/interactions.cljs:51" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/interactions.cljs:58" ], "translations" : { "en" : "Navigate to" } }, "workspace.options.none" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/interactions.cljs:64" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/interactions.cljs:71" ], "translations" : { "en" : "None" } @@ -1116,7 +1138,7 @@ "unused" : true }, "workspace.options.position" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:135", "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:126" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:127", "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:138" ], "translations" : { "en" : "Position", "fr" : "Position" @@ -1129,14 +1151,14 @@ } }, "workspace.options.radius" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:183" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:179" ], "translations" : { "en" : "Radius", "fr" : "TODO" } }, "workspace.options.rotation" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:159" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:154" ], "translations" : { "en" : "Rotation", "fr" : "TODO" @@ -1150,78 +1172,78 @@ "unused" : true }, "workspace.options.select-a-shape" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/interactions.cljs:45" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/interactions.cljs:52" ], "translations" : { "en" : "Select a shape, artboard or group to drag a connection to other artboard." } }, "workspace.options.select-artboard" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/interactions.cljs:57" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/interactions.cljs:64" ], "translations" : { "en" : "Select artboard" } }, "workspace.options.size" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/page.cljs:79", "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:107", "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:101" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:102", "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:110" ], "translations" : { "en" : "Size", "fr" : "Taille" } }, "workspace.options.size-presets" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:83" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:84" ], "translations" : { "en" : "Size presets" } }, "workspace.options.stroke" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:109", "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:173" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:111", "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:175" ], "translations" : { "en" : "Stroke", "fr" : null } }, "workspace.options.stroke.center" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:159" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:161" ], "translations" : { "en" : "Center" } }, "workspace.options.stroke.dashed" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:167" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:169" ], "translations" : { "en" : "Dashed", "fr" : "Tiré" } }, "workspace.options.stroke.dotted" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:166" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:168" ], "translations" : { "en" : "Dotted", "fr" : "Pointillé" } }, "workspace.options.stroke.inner" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:160" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:162" ], "translations" : { "en" : "Inside" } }, "workspace.options.stroke.mixed" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:168" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:170" ], "translations" : { "en" : "Mixed", "fr" : "Mixte" } }, "workspace.options.stroke.outer" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:161" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:163" ], "translations" : { "en" : "Outside" } }, "workspace.options.stroke.solid" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:165" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:167" ], "translations" : { "en" : "Solid", "fr" : "Solide" @@ -1242,7 +1264,7 @@ "unused" : true }, "workspace.options.use-play-button" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/interactions.cljs:47" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/interactions.cljs:54" ], "translations" : { "en" : "Use the play button at the header to run the prototype view." } @@ -1366,7 +1388,7 @@ } }, "workspace.viewport.click-to-close-path" : { - "used-in" : [ "src/uxbox/main/ui/workspace/drawarea.cljs:360" ], + "used-in" : [ "src/uxbox/main/ui/workspace/drawarea.cljs:357" ], "translations" : { "en" : "Click to close the path" } diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index 42756c248c..8f091d3fb1 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -591,6 +591,13 @@ margin-bottom: 0.5rem; } +.element-set-content .custom-select.input-option { + border-top: none; + border-left: none; + border-right: none; + margin-left: 0.25rem; +} + .element-set-content .grid-option-main { display: flex; padding: 0.5rem 0; @@ -626,7 +633,6 @@ } } - } .grid-option-main-actions { @@ -661,9 +667,9 @@ .btn-options { cursor: pointer; border: 1px solid $color-black; - background: #1F1F1F; + background: $color-gray-60; border-radius: 2px; - color: #B1B2B5; + color: $color-gray-20; font-size: 11px; line-height: 16px; flex-grow: 1; @@ -673,8 +679,13 @@ margin-right: 0.5rem; } - &:hover { + &:not([disabled]):hover { background: $color-primary; color: $color-black; } + + &[disabled] { + opacity: 0.4; + cursor: auto; + } } diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index 699283f89e..d607e35a9c 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -67,7 +67,8 @@ :element-options :rules :dynamic-alignment - :layouts}) + :display-grid + :snap-grid}) (s/def ::options-mode #{:design :prototype}) @@ -1493,13 +1494,35 @@ ;; Layouts ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defonce default-layout-params + {:square {:size 16 + :color {:value "#59B9E2" + :opacity 0.9}} + + :column {:size 12 + :type :stretch + :item-width 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 {:size 16 :color {:value "#59B9E2" :opacity 0.9}} + 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 @@ -1528,14 +1551,13 @@ (defn set-default-layout [type params] (ptk/reify ::set-default-layout - dwc/IBatchedChange - - ;; TODO: Save into the backend - ptk/UpdateEvent - (update [_ state] - (-> - state - (assoc-in [:workspace-page :options :saved-layouts type] params))))) + ptk/WatchEvent + (watch [_ state stream] + (rx/of (dwc/commit-changes [{:type :set-option + :option [:saved-layouts type] + :value params}] + [] + {:commit-local? true}))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Exports diff --git a/frontend/src/uxbox/main/refs.cljs b/frontend/src/uxbox/main/refs.cljs index 155e0ad813..2955304873 100644 --- a/frontend/src/uxbox/main/refs.cljs +++ b/frontend/src/uxbox/main/refs.cljs @@ -42,12 +42,6 @@ (def workspace-page (l/derived :workspace-page st/state)) -(def workspace-page-options - (l/derived :options workspace-page)) - -(def workspace-saved-layouts - (l/derived :saved-layouts workspace-page-options)) - (def workspace-page-id (l/derived :id workspace-page)) @@ -74,6 +68,13 @@ (get-in % [:workspace-data page-id])) (l/derived st/state))) +(def workspace-page-options + (l/derived :options workspace-data)) + +(def workspace-saved-layouts + (l/derived :saved-layouts workspace-page-options)) + + (def workspace-objects (l/derived :objects workspace-data)) diff --git a/frontend/src/uxbox/main/snap.cljs b/frontend/src/uxbox/main/snap.cljs index 70c0be0c9e..8e5c2c85c8 100644 --- a/frontend/src/uxbox/main/snap.cljs +++ b/frontend/src/uxbox/main/snap.cljs @@ -18,10 +18,10 @@ (def ^:private snap-accuracy 5) -(defn- remove-from-snap-points [ids-to-remove] +(defn- remove-from-snap-points [remove-id?] (fn [query-result] (->> query-result - (map (fn [[value data]] [value (remove (comp ids-to-remove second) data)])) + (map (fn [[value data]] [value (remove (comp remove-id? second) data)])) (filter (fn [[_ data]] (not (empty? data))))))) (defn- flatten-to-points @@ -90,24 +90,32 @@ (defn closest-snap-point [page-id shapes layout point] - (if (layout :dynamic-alignment) - (let [frame-id (snap-frame-id shapes) - filter-shapes (into #{} (map :id shapes))] - (->> (closest-snap page-id frame-id [point] filter-shapes) - (rx/map #(gpt/add point %)))) - (rx/of point))) + (let [frame-id (snap-frame-id shapes) + filter-shapes (into #{} (map :id shapes)) + filter-shapes (fn [id] (if (= id :layout) + (or (not (contains? layout :display-grid)) + (not (contains? layout :snap-grid))) + (or (filter-shapes id) + (not (contains? layout :dynamic-alignment)))))] + (->> (closest-snap page-id frame-id [point] filter-shapes) + (rx/map #(gpt/add point %)) + (rx/map gpt/round)))) (defn closest-snap-move [page-id shapes layout movev] - (if (layout :dynamic-alignment) - (let [frame-id (snap-frame-id shapes) - filter-shapes (into #{} (map :id shapes)) - shapes-points (->> shapes - ;; Unroll all the possible snap-points - (mapcat (partial sp/shape-snap-points)) + (let [frame-id (snap-frame-id shapes) + filter-shapes (into #{} (map :id shapes)) + filter-shapes (fn [id] (if (= id :layout) + (or (not (contains? layout :display-grid)) + (not (contains? layout :snap-grid))) + (or (filter-shapes id) + (not (contains? layout :dynamic-alignment))))) + shapes-points (->> shapes + ;; Unroll all the possible snap-points + (mapcat (partial sp/shape-snap-points)) - ;; Move the points in the translation vector - (map #(gpt/add % movev)))] - (->> (closest-snap page-id frame-id shapes-points filter-shapes) - (rx/map #(gpt/add movev %)))) - (rx/of movev))) + ;; Move the points in the translation vector + (map #(gpt/add % movev)))] + (->> (closest-snap page-id frame-id shapes-points filter-shapes) + (rx/map #(gpt/add movev %)) + (rx/map gpt/round)))) diff --git a/frontend/src/uxbox/main/ui/workspace/header.cljs b/frontend/src/uxbox/main/ui/workspace/header.cljs index b01d66bba0..ac38e9efb7 100644 --- a/frontend/src/uxbox/main/ui/workspace/header.cljs +++ b/frontend/src/uxbox/main/ui/workspace/header.cljs @@ -72,42 +72,42 @@ :on-close #(reset! show-menu? false)} [:ul.menu [:li {:on-click #(st/emit! (dw/toggle-layout-flag :rules))} - [:span i/ruler] [:span (if (contains? layout :rules) (t locale "workspace.header.menu.hide-rules") (t locale "workspace.header.menu.show-rules"))]] - [:li {:on-click #(st/emit! (dw/toggle-layout-flag :grid))} - [:span i/grid] + [:li {:on-click #(st/emit! (dw/toggle-layout-flag :display-grid))} [:span - (if (contains? layout :grid) + (if (contains? layout :display-grid) (t locale "workspace.header.menu.hide-grid") (t locale "workspace.header.menu.show-grid"))]] + [:li {:on-click #(st/emit! (dw/toggle-layout-flag :snap-grid))} + [:span + (if (contains? layout :snap-grid) + (t locale "workspace.header.menu.disable-snap-grid") + (t locale "workspace.header.menu.enable-snap-grid"))]] + [:li {:on-click #(st/emit! (dw/toggle-layout-flag :sitemap :layers))} - [:span i/layers] [:span (if (or (contains? layout :sitemap) (contains? layout :layers)) (t locale "workspace.header.menu.hide-layers") (t locale "workspace.header.menu.show-layers"))]] [:li {:on-click #(st/emit! (dw/toggle-layout-flag :colorpalette))} - [:span i/palette] [:span (if (contains? layout :colorpalette) (t locale "workspace.header.menu.hide-palette") (t locale "workspace.header.menu.show-palette"))]] [:li {:on-click #(st/emit! (dw/toggle-layout-flag :libraries))} - [:span i/icon-set] [:span (if (contains? layout :libraries) (t locale "workspace.header.menu.hide-libraries") (t locale "workspace.header.menu.show-libraries"))]] [:li {:on-click #(st/emit! (dw/toggle-layout-flag :dynamic-alignment))} - [:span i/shape-halign-left] [:span (if (contains? layout :dynamic-alignment) (t locale "workspace.header.menu.disable-dynamic-alignment") diff --git a/frontend/src/uxbox/main/ui/workspace/layout_display.cljs b/frontend/src/uxbox/main/ui/workspace/layout_display.cljs index f04918b480..6facef86d3 100644 --- a/frontend/src/uxbox/main/ui/workspace/layout_display.cljs +++ b/frontend/src/uxbox/main/ui/workspace/layout_display.cljs @@ -19,26 +19,27 @@ (let [{:keys [color size] :as params} (-> layout :params) {color-value :value color-opacity :opacity} (-> layout :params :color) {frame-width :width frame-height :height :keys [x y]} frame] - [:g.layout - [:* - (for [xs (range size frame-width size)] - [:line {:key (str (:id frame) "-y-" xs) - :x1 (+ x xs) - :y1 y - :x2 (+ x xs) - :y2 (+ y frame-height) - :style {:stroke color-value - :stroke-opacity color-opacity - :stroke-width (str (/ 1 zoom))}}]) - (for [ys (range size frame-height size)] - [:line {:key (str (:id frame) "-x-" ys) - :x1 x - :y1 (+ y ys) - :x2 (+ x frame-width) - :y2 (+ y ys) - :style {:stroke color-value - :stroke-opacity color-opacity - :stroke-width (str (/ 1 zoom))}}])]])) + (when (> size 0) + [:g.layout + [:* + (for [xs (range size frame-width size)] + [:line {:key (str (:id frame) "-y-" xs) + :x1 (+ x xs) + :y1 y + :x2 (+ x xs) + :y2 (+ y frame-height) + :style {:stroke color-value + :stroke-opacity color-opacity + :stroke-width (str (/ 1 zoom))}}]) + (for [ys (range size frame-height size)] + [:line {:key (str (:id frame) "-x-" ys) + :x1 x + :y1 (+ y ys) + :x2 (+ x frame-width) + :y2 (+ y ys) + :style {:stroke color-value + :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)] diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs index 261bf17d12..55c1cc1bd4 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs @@ -31,7 +31,7 @@ [:div.advanced-options {} children]])) -(mf/defc layout-options [{:keys [layout default-layout-params on-change on-remove]}] +(mf/defc layout-options [{:keys [layout default-layout-params on-change on-remove on-save-layout]}] (let [state (mf/use-state {:show-advanced-options false :changes {}}) {:keys [type display params] :as layout} (d/deep-merge layout (:changes @state)) @@ -68,7 +68,15 @@ (fn [event] (let [change-fn (apply handle-change keys)] (-> event dom/get-target dom/get-value parse-integer change-fn)))) - ] + + handle-use-default (fn [] + (emit-changes! #(hash-map :params ((:type layout) default-layout-params)))) + handle-set-as-default (fn [] + (let [current-layout (d/deep-merge layout (-> @state :changes))] + (on-save-layout current-layout))) + + is-default (= (->> @state :changes (d/deep-merge layout) :params) + (->> layout :type default-layout-params))] [:div.grid-option [:div.grid-option-main @@ -85,7 +93,7 @@ (if (= type :square) [:div.input-element.pixels [:input.input-text {:type "number" - :min "0" + :min "1" :no-validate true :value (:size params) :on-change (handle-change-event :params :size)}]] @@ -102,6 +110,8 @@ :on-close toggle-advanced-options} (when (= :square type) [:& input-row {:label "Size" + :class "pixels" + :min 1 :value (:size params) :on-change (handle-change :params :size)}]) @@ -128,52 +138,38 @@ (when (= :row type) [:& input-row {:label "Height" + :class "pixels" :value (or (:item-height params) "") :on-change (handle-change :params :item-height)}]) (when (= :column type) [:& input-row {:label "Width" + :class "pixels" :value (or (:item-width params) "") :on-change (handle-change :params :item-width)}]) (when (#{:row :column} type) [:* [:& input-row {:label "Gutter" + :class "pixels" :value (:gutter params) :on-change (handle-change :params :gutter)}] [:& input-row {:label "Margin" + :class "pixels" :value (:margin params) :on-change (handle-change :params :margin)}]]) [:& color-row {:value (:color params) :on-change (handle-change :params :color)}] [:div.row-flex - [:button.btn-options "Use default"] - [:button.btn-options "Set as default"]]]])) - -(defonce ^:private default-layout-params - {:square {:size 16 - :color {:value "#59B9E2" - :opacity 0.9}} - - :column {:size 12 - :type :stretch - :item-width 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}}}) + [:button.btn-options {:disabled is-default + :on-click handle-use-default} "Use default"] + [:button.btn-options {:disabled is-default + :on-click handle-set-as-default} "Set as default"]]]])) (mf/defc frame-layouts [{:keys [shape]}] (let [id (:id shape) - default-layout-params (merge default-layout-params (mf/deref refs/workspace-saved-layouts)) + 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 %))) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/page.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/page.cljs index 5eb3bcb439..98b3c22520 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/page.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/page.cljs @@ -10,95 +10,14 @@ (ns uxbox.main.ui.workspace.sidebar.options.page "Page options menu entries." (:require - [cuerdas.core :as str] [rumext.alpha :as mf] [okulary.core :as l] - [uxbox.common.data :as d] - [uxbox.main.ui.icons :as i] - [uxbox.main.data.workspace :as dw] - [uxbox.main.refs :as refs] - [uxbox.main.store :as st] - [uxbox.main.ui.modal :as modal] - [uxbox.main.ui.workspace.colorpicker :refer [colorpicker-modal]] - [uxbox.util.dom :as dom] - [uxbox.util.i18n :refer [tr]])) - -(def default-options - "Default data for page metadata." - {:grid-x 10 - :grid-y 10 - :grid-color "#cccccc"}) + [uxbox.main.refs :as refs])) (def options-iref (l/derived :options refs/workspace-data)) -(mf/defc grid-options - {:wrap [mf/memo]} - [props] - (let [options (->> (mf/deref options-iref) - (merge default-options)) - on-x-change - (fn [event] - (let [value (-> (dom/get-target event) - (dom/get-value) - (d/parse-integer 0))] - (st/emit! (dw/update-options {:grid-x value})))) - - on-y-change - (fn [event] - (let [value (-> (dom/get-target event) - (dom/get-value) - (d/parse-integer 0))] - (st/emit! (dw/update-options {:grid-y value})))) - - change-color - (fn [color] - (st/emit! (dw/update-options {:grid-color color}))) - - on-color-input-change - (fn [event] - (let [input (dom/get-target event) - value (dom/get-value input)] - (when (dom/valid? input) - (change-color value)))) - - show-color-picker - (fn [event] - (let [x (.-clientX event) - y (.-clientY event) - props {:x x :y y - :transparent? true - :default "#cccccc" - :attr :grid-color - :on-change change-color}] - (modal/show! colorpicker-modal props)))] - [:div.element-set - [:div.element-set-title (tr "workspace.options.grid-options")] - [:div.element-set-content - [:div.row-flex - [:span.element-set-subtitle (tr "workspace.options.size")] - [:div.input-element.pixels - [:input.input-text {:type "number" - :value (:grid-x options) - :on-change on-x-change}]] - [:div.input-element.pixels - [:input.input-text {:type "number" - :value (:grid-y options) - :on-change on-y-change}]]] - [:div.row-flex.color-data - [:span.element-set-subtitle (tr "workspace.options.color")] - [:span.color-th {:style {:background-color (:grid-color options)} - :on-click show-color-picker}] - [:div.color-info - [:input {:default-value (:grid-color options) - :ref (fn [el] - (when el - (set! (.-value el) (:grid-color options)))) - :pattern "^#(?:[0-9a-fA-F]{3}){1,2}$" - :on-change on-color-input-change}]]]]])) - (mf/defc options - [{:keys [page] :as props}] - [:div - [:& grid-options {:page page}]]) + ;; TODO: Define properties for page + [{:keys [page] :as props}]) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/color_row.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/color_row.cljs index ebcb9f120c..b5f0d7b314 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/color_row.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/color_row.cljs @@ -39,7 +39,8 @@ (/ 100))) (mf/defc color-row [{:keys [value on-change]}] - (let [state (mf/use-state value) + (let [value (or value {:value "#FFFFFF" :opacity 1}) + state (mf/use-state value) change-color (fn [color] (let [update-color (fn [state] (assoc state :value color))] (swap! state update-color) @@ -65,6 +66,10 @@ string->opacity change-opacity))] + (mf/use-effect + (mf/deps value) + #(reset! state value)) + [:div.row-flex.color-data [:span.color-th {:style {:background-color (-> @state :value)} @@ -88,3 +93,4 @@ :value (-> @state :opacity opacity->string) :step "1" :on-change handle-opacity-change}]])) + diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/input_row.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/input_row.cljs index 50dbe8a34a..ce4d9d48b0 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/input_row.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/input_row.cljs @@ -14,17 +14,19 @@ [uxbox.main.ui.components.select :refer [select]] [uxbox.util.dom :as dom])) -(mf/defc input-row [{:keys [label options value on-change]}] - [:div.row-flex.input-row - [:span.element-set-subtitle label] - [:div.input-element - (if options - [:& select {:default-value value - :class "input-option" - :options options - :on-change on-change}] - [:input.input-text - {:placeholder label - :type "number" - :on-change #(-> % dom/get-target dom/get-value d/parse-integer on-change) - :value value}])]]) +(mf/defc input-row [{:keys [label options value class min max on-change]}] + (let [handle-change (fn [value] (when (and (or (not min) (>= value min)) (or (not max) (<= value max))) + (on-change value)))] + [:div.row-flex.input-row + [:span.element-set-subtitle label] + [:div.input-element {:class class} + (if options + [:& select {:default-value value + :class "input-option" + :options options + :on-change on-change}] + [:input.input-text + {:placeholder label + :type "number" + :on-change #(-> % dom/get-target dom/get-value d/parse-integer handle-change) + :value value}])]])) diff --git a/frontend/src/uxbox/main/ui/workspace/snap_feedback.cljs b/frontend/src/uxbox/main/ui/workspace/snap_feedback.cljs index 53e7289af6..b219ee6272 100644 --- a/frontend/src/uxbox/main/ui/workspace/snap_feedback.cljs +++ b/frontend/src/uxbox/main/ui/workspace/snap_feedback.cljs @@ -80,12 +80,17 @@ :point point :zoom zoom}])]))])) -(mf/defc snap-feedback [{:keys []}] +(mf/defc snap-feedback [{:keys [layout]}] (let [page-id (mf/deref refs/workspace-page-id) selected (mf/deref refs/selected-shapes) selected-shapes (mf/deref (refs/objects-by-id selected)) drawing (mf/deref refs/current-drawing-shape) filter-shapes (mf/deref refs/selected-shapes-with-children) + filter-shapes (fn [id] (if (= id :layout) + (or (not (contains? layout :display-grid)) + (not (contains? layout :snap-grid))) + (or (filter-shapes id) + (not (contains? layout :dynamic-alignment))))) current-transform (mf/deref refs/current-transform) snap-data (mf/deref refs/workspace-snap-data) shapes (if drawing [drawing] selected-shapes) diff --git a/frontend/src/uxbox/main/ui/workspace/viewport.cljs b/frontend/src/uxbox/main/ui/workspace/viewport.cljs index 9819ea115c..0dbe00d006 100644 --- a/frontend/src/uxbox/main/ui/workspace/viewport.cljs +++ b/frontend/src/uxbox/main/ui/workspace/viewport.cljs @@ -386,10 +386,10 @@ :zoom zoom :modifiers (:modifiers local)}]) - (when (contains? layout :layouts) + (when (contains? layout :display-grid) [:& layout-display {:zoom zoom}]) - [:& snap-feedback] + [:& snap-feedback {:layout layout}] (when tooltip [:& cursor-tooltip {:zoom zoom :tooltip tooltip}])] diff --git a/frontend/src/uxbox/util/geom/layout.cljs b/frontend/src/uxbox/util/geom/layout.cljs index 0e6c232757..e1ff4a2d09 100644 --- a/frontend/src/uxbox/util/geom/layout.cljs +++ b/frontend/src/uxbox/util/geom/layout.cljs @@ -74,11 +74,12 @@ (case type :square (let [{:keys [x y width height]} shape size (-> params :size)] - (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)))) + (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))) diff --git a/frontend/src/uxbox/util/geom/point.cljs b/frontend/src/uxbox/util/geom/point.cljs index de1b3744d3..5433334b2d 100644 --- a/frontend/src/uxbox/util/geom/point.cljs +++ b/frontend/src/uxbox/util/geom/point.cljs @@ -151,11 +151,12 @@ (defn round "Change the precision of the point coordinates." - [{:keys [x y] :as p} decimanls] - (assert (point? p)) - (assert (number? decimanls)) - (Point. (mth/precision x decimanls) - (mth/precision y decimanls))) + ([point] (round point 0)) + ([{:keys [x y] :as p} decimanls] + (assert (point? p)) + (assert (number? decimanls)) + (Point. (mth/precision x decimanls) + (mth/precision y decimanls)))) (defn transform "Transform a point applying a matrix transfomation." From ad99e6df9d06cd9e01157a4f01817a5f8bbbe82f Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 19 May 2020 10:52:11 +0200 Subject: [PATCH 06/11] :tada: Adds support for auto --- .../partials/sidebar-element-options.scss | 139 +++++++++++------- .../uxbox/main/ui/components/dropdown.cljs | 4 +- .../main/ui/components/editable_select.cljs | 70 +++++++++ .../sidebar/options/frame_layouts.cljs | 92 +++++++----- .../sidebar/options/rows/input_row.cljs | 43 ++++-- frontend/src/uxbox/util/geom/layout.cljs | 18 ++- 6 files changed, 261 insertions(+), 105 deletions(-) create mode 100644 frontend/src/uxbox/main/ui/components/editable_select.cljs diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index 8f091d3fb1..c3c1e4e776 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -263,11 +263,6 @@ padding: $x-small $big $x-small $x-small; position: relative; - & hr { - margin: 0; - border-color: $color-gray-20; - } - .dropdown-button { position: absolute; right: $x-small; @@ -284,7 +279,9 @@ font-size: $fs13; } - .custom-select-dropdown { + + } + .custom-select-dropdown { position: absolute; left: 0; z-index: 12; @@ -297,6 +294,11 @@ border-radius: $br-small; box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25); + hr { + margin: 0; + border-color: $color-gray-20; + } + li { color: $color-gray-60; cursor: pointer; @@ -327,63 +329,96 @@ } } - & li.checked-element { - padding-left: 0; + & li.checked-element { + padding-left: 0; - & span { - margin: 0; - color: $color-black; - } + & span { + margin: 0; + color: $color-black; + } - & svg { - visibility: hidden; - width: 8px; - height: 8px; - background: none; - margin: 0.25rem; - fill: $color-black; - } + & svg { + visibility: hidden; + width: 8px; + height: 8px; + background: none; + margin: 0.25rem; + fill: $color-black; + } - &.is-selected { - & svg { - visibility: visible; - } - } - } - } + &.is-selected { + & svg { + visibility: visible; + } + } + } .editable-select { - height: 38px; - margin-right: $small; - position: relative; - width: 60%; - - .input-text { - left: 0; - position: absolute; - top: -1px; + position: relative; + height: 38px; + margin-right: $small; + position: relative; width: 60%; - } - - .input-select { - background-color: transparent; - border: none; - border-bottom: 1px solid $color-gray-40; - color: transparent; - left: 0; - position: absolute; - top: 0; - width: 100%; - option { - color: $color-gray-60; - background: $color-white; - font-size: $fs12; + svg { + fill: $color-gray-40; + height: 10px; + width: 10px; + } + + .input-text { + left: 0; + position: absolute; + top: -1px; + width: 60%; + } + + .input-select { + background-color: transparent; + border: none; + border-bottom: 1px solid $color-gray-40; + color: transparent; + left: 0; + position: absolute; + top: 0; + width: 100%; + + option { + color: $color-gray-60; + background: $color-white; + font-size: $fs12; + } + } + + .dropdown-button { + position: absolute; + top: 7px; + right: 0; + } + + &.input-option { + height: 2rem; + border-bottom: 1px solid #64666A; + width: 100%; + margin-left: 0.25rem; + + .input-text { + border: none; + margin: 0; + width: calc(100% - 12px); + height: 100%; + top: auto; + color: #b1b2b5; + } } - } } } +.grid-option-main .editable-select.input-option .input-text { + padding: 0; + padding-top: 0.18rem; +} + .color-th { background-color: $color-gray-10; border: 1px solid $color-gray-10; diff --git a/frontend/src/uxbox/main/ui/components/dropdown.cljs b/frontend/src/uxbox/main/ui/components/dropdown.cljs index 48f8f6d867..eae0f817d0 100644 --- a/frontend/src/uxbox/main/ui/components/dropdown.cljs +++ b/frontend/src/uxbox/main/ui/components/dropdown.cljs @@ -43,8 +43,8 @@ (mf/defc dropdown {::mf/wrap-props false} [props] - (assert (fn? (gobj/get props "on-close")) "missing `on-close` prop") - (assert (boolean? (gobj/get props "show")) "missing `show` prop") + #_(assert (fn? (gobj/get props "on-close")) "missing `on-close` prop") + #_(assert (boolean? (gobj/get props "show")) "missing `show` prop") (when (gobj/get props "show") (mf/element dropdown' props))) diff --git a/frontend/src/uxbox/main/ui/components/editable_select.cljs b/frontend/src/uxbox/main/ui/components/editable_select.cljs new file mode 100644 index 0000000000..57093c6cb4 --- /dev/null +++ b/frontend/src/uxbox/main/ui/components/editable_select.cljs @@ -0,0 +1,70 @@ +;; 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.ui.components.editable-select + (:require + [rumext.alpha :as mf] + [uxbox.common.uuid :as uuid] + [uxbox.common.data :as d] + [uxbox.util.dom :as dom] + [uxbox.main.ui.icons :as i] + [uxbox.main.ui.components.dropdown :refer [dropdown]])) + +(mf/defc editable-select [{:keys [value type options class on-change]}] + (let [state (mf/use-state {:id (uuid/next) + :is-open? false + :current-value value}) + open-dropdown #(swap! state assoc :is-open? true) + close-dropdown #(swap! state assoc :is-open? false) + + select-item (fn [value] + (fn [event] + (swap! state assoc :current-value value) + (when on-change (on-change value)))) + + as-key-value (fn [item] (if (map? item) [(:value item) (:label item)] [item item])) + + labels-map (into {} (->> options (map as-key-value))) + + value->label (fn [value] (get labels-map value value)) + + handle-change-input (fn [event] + (let [value (-> event dom/get-target dom/get-value) + value (or (d/parse-integer value) value)] + (swap! state assoc :current-value value) + (when on-change (on-change value))))] + + (mf/use-effect + (mf/deps value) + #(reset! state {:current-value value})) + + (mf/use-effect + (mf/deps options) + #(reset! state {:is-open? false + :current-value value})) + + [:div.editable-select {:class class} + [:input.input-text {:value (or (-> @state :current-value value->label) "") + :on-change handle-change-input + :type type}] + [:span.dropdown-button {:on-click open-dropdown} i/arrow-down] + + [:& dropdown {:show (:is-open? @state) + :on-close close-dropdown} + [:ul.custom-select-dropdown + (for [[index item] (map-indexed vector options)] + (cond + (= :separator item) [:hr {:key (str (:id @state) "-" index)}] + :else (let [[value label] (as-key-value item)] + [:li.checked-element + {:key (str (:id @state) "-" index) + :class (when (= value (-> @state :current-value)) "is-selected") + :on-click (select-item value)} + [:span.check-icon i/tick] + [:span label]])))]]])) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs index 55c1cc1bd4..fa0872c376 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs @@ -20,17 +20,24 @@ [uxbox.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]] [uxbox.main.ui.workspace.sidebar.options.rows.input-row :refer [input-row]] [uxbox.main.ui.components.select :refer [select]] + [uxbox.main.ui.components.editable-select :refer [editable-select]] [uxbox.main.ui.components.dropdown :refer [dropdown]])) (mf/defc advanced-options [{:keys [visible? on-close children]}] (when visible? [:* - [:div.focus-overlay {:on-click #(when on-close (do - (dom/stop-propagation %) - (on-close)))}] + [:div.focus-overlay {:on-click #(when on-close + (do + (dom/stop-propagation %) + (on-close)))}] [:div.advanced-options {} children]])) +(def ^:private size-options + [{:value :auto :label "Auto"} + :separator + 18 12 10 8 6 4 3 2]) + (mf/defc layout-options [{:keys [layout default-layout-params on-change on-remove on-save-layout]}] (let [state (mf/use-state {:show-advanced-options false :changes {}}) @@ -38,42 +45,46 @@ toggle-advanced-options #(swap! state update :show-advanced-options not) - size-options [{:value :auto :label "Auto"} - :separator - 18 12 10 8 6 4 3 2] + emit-changes! + (fn [update-fn] + (swap! state update :changes update-fn) + (when on-change (on-change (d/deep-merge layout (-> @state :changes update-fn))))) - emit-changes! (fn [update-fn] - (swap! state update :changes update-fn) - (when on-change (on-change (d/deep-merge layout (-> @state :changes update-fn))))) + handle-toggle-visibility + (fn [event] + (emit-changes! (fn [changes] (update changes :display #(if (nil? %) false (not %)))))) - handle-toggle-visibility (fn [event] - (emit-changes! (fn [changes] (update changes :display #(if (nil? %) false (not %)))))) + handle-remove-layout + (fn [event] + (when on-remove (on-remove))) - handle-remove-layout (fn [event] - (when on-remove (on-remove))) + handle-change-type + (fn [type] + (let [defaults (type default-layout-params) + keys (keys defaults) + params (->> @state :changes params (select-keys keys) (merge defaults)) + to-merge {:type type :params params}] + (emit-changes! #(d/deep-merge % to-merge)))) - handle-change-type (fn [type] - (let [defaults (type default-layout-params) - params (merge - defaults - (select-keys (keys defaults) (-> @state :changes params))) - to-merge {:type type :params params}] - (emit-changes! #(d/deep-merge % to-merge)))) + handle-change + (fn [& keys] + (fn [value] + (emit-changes! #(assoc-in % keys value)))) - handle-change (fn [& keys] - (fn [value] - (emit-changes! #(assoc-in % keys value)))) + handle-change-event + (fn [& keys] + (fn [event] + (let [change-fn (apply handle-change keys)] + (-> event dom/get-target dom/get-value parse-integer change-fn)))) - handle-change-event (fn [& keys] - (fn [event] - (let [change-fn (apply handle-change keys)] - (-> event dom/get-target dom/get-value parse-integer change-fn)))) + handle-use-default + (fn [] + (emit-changes! #(hash-map :params ((:type layout) default-layout-params)))) - handle-use-default (fn [] - (emit-changes! #(hash-map :params ((:type layout) default-layout-params)))) - handle-set-as-default (fn [] - (let [current-layout (d/deep-merge layout (-> @state :changes))] - (on-save-layout current-layout))) + handle-set-as-default + (fn [] + (let [current-layout (d/deep-merge layout (-> @state :changes))] + (on-save-layout current-layout))) is-default (= (->> @state :changes (d/deep-merge layout) :params) (->> layout :type default-layout-params))] @@ -97,10 +108,11 @@ :no-validate true :value (:size params) :on-change (handle-change-event :params :size)}]] - [:& select {:default-value (:size params) - :class "input-option" - :options size-options - :on-change (handle-change :params :size)}]) + [:& editable-select {:value (:size params) + :type (when (number? (:size params)) "number" ) + :class "input-option" + :options size-options + :on-change (handle-change :params :size)}]) [:div.grid-option-main-actions [:button.custom-button {:on-click handle-toggle-visibility} (if display i/eye i/eye-closed)] @@ -117,18 +129,23 @@ (when (= :row type) [:& input-row {:label "Rows" + :type :editable-select :options size-options :value (:size params) + :min 1 :on-change (handle-change :params :size)}]) (when (= :column type) [:& input-row {:label "Columns" + :type :editable-select :options size-options :value (:size params) + :min 1 :on-change (handle-change :params :size)}]) (when (#{:row :column} type) [:& input-row {:label "Type" + :type :select :options [{:value :stretch :label "Stretch"} {:value :left :label "Left"} {:value :center :label "Center"} @@ -139,6 +156,7 @@ (when (= :row type) [:& input-row {:label "Height" :class "pixels" + :min 1 :value (or (:item-height params) "") :on-change (handle-change :params :item-height)}]) @@ -153,9 +171,11 @@ [:& input-row {:label "Gutter" :class "pixels" :value (:gutter params) + :min 0 :on-change (handle-change :params :gutter)}] [:& input-row {:label "Margin" :class "pixels" + :min 0 :value (:margin params) :on-change (handle-change :params :margin)}]]) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/input_row.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/input_row.cljs index ce4d9d48b0..b6557e5b49 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/input_row.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/rows/input_row.cljs @@ -12,21 +12,38 @@ [rumext.alpha :as mf] [uxbox.common.data :as d] [uxbox.main.ui.components.select :refer [select]] + [uxbox.main.ui.components.editable-select :refer [editable-select]] [uxbox.util.dom :as dom])) -(mf/defc input-row [{:keys [label options value class min max on-change]}] - (let [handle-change (fn [value] (when (and (or (not min) (>= value min)) (or (not max) (<= value max))) - (on-change value)))] - [:div.row-flex.input-row - [:span.element-set-subtitle label] - [:div.input-element {:class class} - (if options - [:& select {:default-value value - :class "input-option" - :options options - :on-change on-change}] +(mf/defc input-row [{:keys [label options value class min max on-change type]}] + [:div.row-flex.input-row + [:span.element-set-subtitle label] + [:div.input-element {:class class} + + (case type + :select + [:& select {:default-value value + :class "input-option" + :options options + :on-change on-change}] + :editable-select + [:& editable-select {:value value + :class "input-option" + :options options + :type (when (number? value) "number") + :on-change on-change}] + + (let [handle-change + (fn [event] + (let [value (-> event dom/get-target dom/get-value d/parse-integer)] + (when (and (not (nil? on-change)) + (or (not min) (>= value min)) + (or (not max) (<= value max))) + (on-change value))))] [:input.input-text {:placeholder label :type "number" - :on-change #(-> % dom/get-target dom/get-value d/parse-integer handle-change) - :value value}])]])) + :on-change handle-change + :value value}])) + + ]]) diff --git a/frontend/src/uxbox/util/geom/layout.cljs b/frontend/src/uxbox/util/geom/layout.cljs index e1ff4a2d09..ddc453311c 100644 --- a/frontend/src/uxbox/util/geom/layout.cljs +++ b/frontend/src/uxbox/util/geom/layout.cljs @@ -9,10 +9,24 @@ (ns uxbox.util.geom.layout (: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] + (/ (- frame-length (+ margin (- margin gutter)) (* gutter default-items)) default-items)) + +(defn calculate-size + "Calculates the number of rows/columns given the other layout 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-width type] :as params}] - (let [parts (/ width size) + (let [size (if (number? size) size (calculate-size width item-width margin gutter)) + parts (/ width size) item-width (or item-width (+ parts (- gutter) (/ gutter size) (- (/ (* margin 2) size)))) item-height height initial-offset (case type @@ -25,7 +39,7 @@ [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-height type] :as params}] - (let [{:keys [width height x y]} frame + (let [size (if (number? size) size (calculate-size height item-height margin gutter)) parts (/ height size) item-width width item-height (or item-height (+ parts (- gutter) (/ gutter size) (- (/ (* margin 2) size)))) From 235e1960948d2c392d0cff50d4676b626a63e4fc Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 19 May 2020 12:45:05 +0200 Subject: [PATCH 07/11] :sparkles: Fixes problem with snap keys --- .../partials/sidebar-element-options.scss | 21 +++++++-- frontend/src/uxbox/main/data/workspace.cljs | 2 +- .../src/uxbox/main/ui/workspace/grid.cljs | 43 ----------------- .../sidebar/options/frame_layouts.cljs | 47 +++++++++++++------ .../main/ui/workspace/snap_feedback.cljs | 36 +++++++------- frontend/src/uxbox/util/geom/layout.cljs | 12 ++--- 6 files changed, 74 insertions(+), 87 deletions(-) delete mode 100644 frontend/src/uxbox/main/ui/workspace/grid.cljs diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index c3c1e4e776..937432c47e 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -285,7 +285,7 @@ position: absolute; left: 0; z-index: 12; - width: 200px; + // width: 200px; max-height: 30rem; min-width: 7rem; overflow-y: auto; @@ -414,9 +414,21 @@ } } -.grid-option-main .editable-select.input-option .input-text { - padding: 0; - padding-top: 0.18rem; +.element-set-content .grid-option-main { + .editable-select.input-option .input-text { + padding: 0; + padding-top: 0.18rem; + } + + .input-element { + width: 55px; + margin: 0 0.2rem; + } + + .input-text { + padding-left: 0; + color: #b1b2b5; + } } .color-th { @@ -644,6 +656,7 @@ } & .custom-select { + min-width: 4.75rem; height: 2rem; border: none; border-bottom: 1px solid #65666A; diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index d607e35a9c..a80432eb98 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -1501,7 +1501,7 @@ :column {:size 12 :type :stretch - :item-width nil + :item-length nil :gutter 8 :margin 0 :color {:value "#DE4762" diff --git a/frontend/src/uxbox/main/ui/workspace/grid.cljs b/frontend/src/uxbox/main/ui/workspace/grid.cljs deleted file mode 100644 index 31b1c30aab..0000000000 --- a/frontend/src/uxbox/main/ui/workspace/grid.cljs +++ /dev/null @@ -1,43 +0,0 @@ -;; 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.ui.workspace.grid - (:require - [cuerdas.core :as str] - [okulary.core :as l] - [rumext.alpha :as mf] - [uxbox.main.constants :as c] - [uxbox.main.refs :as refs])) - -;; --- Grid (Component) - -(def options-iref - (l/derived :options refs/workspace-data)) - -(mf/defc grid - {:wrap [mf/memo]} - [props] - (let [options (mf/deref options-iref) - width (:grid-x options 10) - height (:grid-y options 10) - color (:grid-color options "#cccccc")] - [:g.grid - [:defs - [:pattern {:id "grid-pattern" - :x "0" :y "0" - :width width :height height - :patternUnits "userSpaceOnUse"} - [:path {:d (str/format "M 0 %s L %s %s L %s 0" height width height width) - :fill "transparent" - :stroke color}]]] - [:rect {:style {:pointer-events "none"} - :x 0 :y 0 - :width "100%" - :height "100%" - :fill "url(#grid-pattern)"}]])) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs index fa0872c376..5bf198c65b 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs @@ -12,10 +12,12 @@ [rumext.alpha :as mf] [uxbox.util.dom :as dom] [uxbox.util.data :as d] + [uxbox.util.math :as mth] [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.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]] @@ -38,7 +40,7 @@ :separator 18 12 10 8 6 4 3 2]) -(mf/defc layout-options [{:keys [layout default-layout-params on-change on-remove on-save-layout]}] +(mf/defc layout-options [{:keys [frame layout default-layout-params on-change on-remove on-save-layout]}] (let [state (mf/use-state {:show-advanced-options false :changes {}}) {:keys [type display params] :as layout} (d/deep-merge layout (:changes @state)) @@ -77,6 +79,27 @@ (let [change-fn (apply handle-change keys)] (-> event dom/get-target dom/get-value parse-integer change-fn)))) + 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)) + item-length (if (or (nil? size) (= :auto size)) + (-> (gla/calculate-default-item-length frame-length margin gutter) + (mth/round)) + item-length)] + (emit-changes! #(-> % + (assoc-in [:params :size] size) + (assoc-in [:params :item-length] item-length))))) + + handle-change-item-length + (fn [item-length] + (let [{:keys [margin gutter size]} (->> @state :changes :params (d/deep-merge (:params layout))) + size (if (and (nil? item-length) (or (nil? size) (= :auto size))) 12 size)] + (emit-changes! #(-> % + (assoc-in [:params :size] size) + (assoc-in [:params :item-length] item-length))))) + handle-use-default (fn [] (emit-changes! #(hash-map :params ((:type layout) default-layout-params)))) @@ -112,7 +135,7 @@ :type (when (number? (:size params)) "number" ) :class "input-option" :options size-options - :on-change (handle-change :params :size)}]) + :on-change handle-change-size}]) [:div.grid-option-main-actions [:button.custom-button {:on-click handle-toggle-visibility} (if display i/eye i/eye-closed)] @@ -133,7 +156,7 @@ :options size-options :value (:size params) :min 1 - :on-change (handle-change :params :size)}]) + :on-change handle-change-size}]) (when (= :column type) [:& input-row {:label "Columns" @@ -141,7 +164,7 @@ :options size-options :value (:size params) :min 1 - :on-change (handle-change :params :size)}]) + :on-change handle-change-size}]) (when (#{:row :column} type) [:& input-row {:label "Type" @@ -153,18 +176,11 @@ :value (:type params) :on-change (handle-change :params :type)}]) - (when (= :row type) - [:& input-row {:label "Height" + (when (#{:row :column} type) + [:& input-row {:label (if (= :row type) "Height" "Width") :class "pixels" - :min 1 - :value (or (:item-height params) "") - :on-change (handle-change :params :item-height)}]) - - (when (= :column type) - [:& input-row {:label "Width" - :class "pixels" - :value (or (:item-width params) "") - :on-change (handle-change :params :item-width)}]) + :value (or (:item-length params) "") + :on-change handle-change-item-length}]) (when (#{:row :column} type) [:* @@ -205,6 +221,7 @@ [:& layout-options {:key (str (:id shape) "-" index) :layout layout :default-layout-params default-layout-params + :frame shape :on-change (handle-edit-layout index) :on-remove (handle-remove-layout index) :on-save-layout handle-save-layout}])])])) diff --git a/frontend/src/uxbox/main/ui/workspace/snap_feedback.cljs b/frontend/src/uxbox/main/ui/workspace/snap_feedback.cljs index b219ee6272..a708663078 100644 --- a/frontend/src/uxbox/main/ui/workspace/snap_feedback.cljs +++ b/frontend/src/uxbox/main/ui/workspace/snap_feedback.cljs @@ -45,8 +45,15 @@ (mf/defc snap-feedback-points [{:keys [shapes page-id filter-shapes zoom] :as props}] (let [state (mf/use-state []) - subject (mf/use-memo #(rx/subject))] + subject (mf/use-memo #(rx/subject)) + ;; We use sets to store points/lines so there are no points/lines repeated + ;; can cause problems with react keys + snap-points (into #{} (mapcat (fn [[point snaps coord]] + (when (not-empty snaps) (concat [point] snaps))) @state)) + + snap-lines (into #{} (mapcat (fn [[point snaps coord]] + (when (not-empty snaps) (map #(vector point %) snaps))) @state))] (mf/use-effect (fn [] (->> subject @@ -61,24 +68,17 @@ (fn [] (rx/push! subject props))) + [:g.snap-feedback - (for [[point snaps coord] @state] - (if (not-empty snaps) - [:g.point {:key (str "point-" (:x point) "-" (:y point) "-" (name coord))} - [:& snap-point {:key (str "point-" (:x point) "-" (:y point) "-" (name coord)) - :point point - :zoom zoom}] - - (for [snap snaps] - [:& snap-point {:key (str "snap-" (:x point) "-" (:y point) "-" (:x snap) "-" (:y snap) "-" (name coord)) - :point snap - :zoom zoom}]) - - (for [snap snaps] - [:& snap-line {:key (str "line-" (:x point) "-" (:y point) "-" (:x snap) "-" (:y snap) "-" (name coord)) - :snap snap - :point point - :zoom zoom}])]))])) + (for [[from-point to-point] snap-lines] + [:& snap-line {:key (str "line-" (:x from-point) "-" (:y from-point) "-" (:x to-point) "-" (:y to-point) "-") + :snap from-point + :point to-point + :zoom zoom}]) + (for [point snap-points] + [:& snap-point {:key (str "point-" (:x point) "-" (:y point)) + :point point + :zoom zoom}])])) (mf/defc snap-feedback [{:keys [layout]}] (let [page-id (mf/deref refs/workspace-page-id) diff --git a/frontend/src/uxbox/util/geom/layout.cljs b/frontend/src/uxbox/util/geom/layout.cljs index ddc453311c..7c12100bc8 100644 --- a/frontend/src/uxbox/util/geom/layout.cljs +++ b/frontend/src/uxbox/util/geom/layout.cljs @@ -24,10 +24,10 @@ 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-width type] :as params}] - (let [size (if (number? size) size (calculate-size width item-width margin gutter)) +(defn calculate-column-layout [{: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-width (+ parts (- gutter) (/ gutter size) (- (/ (* margin 2) size)))) + item-width (or item-length (+ parts (- gutter) (/ gutter size) (- (/ (* margin 2) size)))) item-height height initial-offset (case type :right (- width (* item-width size) (* gutter (dec size)) margin) @@ -38,11 +38,11 @@ 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-height type] :as params}] - (let [size (if (number? size) size (calculate-size height item-height margin gutter)) +(defn calculate-row-layout [{: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 - item-height (or item-height (+ parts (- gutter) (/ gutter size) (- (/ (* margin 2) size)))) + item-height (or item-length (+ parts (- gutter) (/ gutter size) (- (/ (* margin 2) size)))) initial-offset (case type :right (- height (* item-height size) (* gutter (dec size)) margin) :center (/ (- height (* item-height size) (* gutter (dec size))) 2) From 23ca77fe3a73e24e867331f5fe371be3043b9d4b Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 19 May 2020 13:47:52 +0200 Subject: [PATCH 08/11] :recycle: Renamed layout to grid and some refactors --- frontend/src/uxbox/main/data/workspace.cljs | 70 ---------------- .../src/uxbox/main/data/workspace/grid.cljs | 83 +++++++++++++++++++ frontend/src/uxbox/main/refs.cljs | 4 +- .../{layout_display.cljs => frame_grid.cljs} | 46 +++++----- .../ui/workspace/sidebar/options/frame.cljs | 5 +- .../{frame_layouts.cljs => frame_grid.cljs} | 70 ++++++++-------- .../src/uxbox/main/ui/workspace/viewport.cljs | 4 +- .../util/geom/{layout.cljs => grid.cljs} | 73 +++++++++------- frontend/src/uxbox/util/geom/snap_points.cljs | 2 +- frontend/src/uxbox/worker/snaps.cljs | 17 ++-- 10 files changed, 203 insertions(+), 171 deletions(-) create mode 100644 frontend/src/uxbox/main/data/workspace/grid.cljs rename frontend/src/uxbox/main/ui/workspace/{layout_display.cljs => frame_grid.cljs} (59%) rename frontend/src/uxbox/main/ui/workspace/sidebar/options/{frame_layouts.cljs => frame_grid.cljs} (75%) rename frontend/src/uxbox/util/geom/{layout.cljs => grid.cljs} (61%) diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index a80432eb98..11962eb434 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/frontend/src/uxbox/main/data/workspace/grid.cljs b/frontend/src/uxbox/main/data/workspace/grid.cljs new file mode 100644 index 0000000000..31e666e7b5 --- /dev/null +++ b/frontend/src/uxbox/main/data/workspace/grid.cljs @@ -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}))))) diff --git a/frontend/src/uxbox/main/refs.cljs b/frontend/src/uxbox/main/refs.cljs index 2955304873..84d72442f4 100644 --- a/frontend/src/uxbox/main/refs.cljs +++ b/frontend/src/uxbox/main/refs.cljs @@ -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 diff --git a/frontend/src/uxbox/main/ui/workspace/layout_display.cljs b/frontend/src/uxbox/main/ui/workspace/frame_grid.cljs similarity index 59% rename from frontend/src/uxbox/main/ui/workspace/layout_display.cljs rename to frontend/src/uxbox/main/ui/workspace/frame_grid.cljs index 6facef86d3..1707d03a5d 100644 --- a/frontend/src/uxbox/main/ui/workspace/layout_display.cljs +++ b/frontend/src/uxbox/main/ui/workspace/frame_grid.cljs @@ -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)}])])) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs index af6051a39e..13d525922b 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs @@ -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}]]) + diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs similarity index 75% rename from frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs rename to frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs index 5bf198c65b..5e835f44bf 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_layouts.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs @@ -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}])])])) diff --git a/frontend/src/uxbox/main/ui/workspace/viewport.cljs b/frontend/src/uxbox/main/ui/workspace/viewport.cljs index 0dbe00d006..8cd0e06627 100644 --- a/frontend/src/uxbox/main/ui/workspace/viewport.cljs +++ b/frontend/src/uxbox/main/ui/workspace/viewport.cljs @@ -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}] diff --git a/frontend/src/uxbox/util/geom/layout.cljs b/frontend/src/uxbox/util/geom/grid.cljs similarity index 61% rename from frontend/src/uxbox/util/geom/layout.cljs rename to frontend/src/uxbox/util/geom/grid.cljs index 7c12100bc8..b3706e25ae 100644 --- a/frontend/src/uxbox/util/geom/layout.cljs +++ b/frontend/src/uxbox/util/geom/grid.cljs @@ -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))))))) diff --git a/frontend/src/uxbox/util/geom/snap_points.cljs b/frontend/src/uxbox/util/geom/snap_points.cljs index 6adbf3dfce..ebdfbc54d0 100644 --- a/frontend/src/uxbox/util/geom/snap_points.cljs +++ b/frontend/src/uxbox/util/geom/snap_points.cljs @@ -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) diff --git a/frontend/src/uxbox/worker/snaps.cljs b/frontend/src/uxbox/worker/snaps.cljs index e14883860b..e314acda09 100644 --- a/frontend/src/uxbox/worker/snaps.cljs +++ b/frontend/src/uxbox/worker/snaps.cljs @@ -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 From 4c36d83e38f4de8b15e7d7943f711ef893b08cb9 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 19 May 2020 14:06:40 +0200 Subject: [PATCH 09/11] :sparkles: Add locales for grid options --- frontend/resources/locales.json | 146 ++++++++++++++++-- .../partials/sidebar-element-options.scss | 1 - .../src/uxbox/main/ui/workspace/header.cljs | 2 +- .../workspace/sidebar/options/frame_grid.cljs | 47 +++--- 4 files changed, 157 insertions(+), 39 deletions(-) diff --git a/frontend/resources/locales.json b/frontend/resources/locales.json index d0f049c549..0f5fc05598 100644 --- a/frontend/resources/locales.json +++ b/frontend/resources/locales.json @@ -355,7 +355,7 @@ } }, "header.sitemap" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:68" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:67" ], "translations" : { "en" : null, "fr" : null @@ -822,91 +822,91 @@ } }, "workspace.header.menu.disable-dynamic-alignment" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:116" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:113" ], "translations" : { "en" : "Disable dynamic alignment" } }, "workspace.header.menu.disable-snap-grid" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:92" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:89" ], "translations" : { "en" : "Disable snap to grid" } }, "workspace.header.menu.enable-dynamic-alignment" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:117" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:114" ], "translations" : { "en" : "Enable dynamic aligment" } }, "workspace.header.menu.enable-snap-grid" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:93" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:90" ], "translations" : { "en" : "Snap to grid" } }, "workspace.header.menu.hide-grid" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:86" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:83" ], "translations" : { "en" : "Hide grid" } }, "workspace.header.menu.hide-layers" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:98" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:95" ], "translations" : { "en" : "Hide layers" } }, "workspace.header.menu.hide-libraries" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:110" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:107" ], "translations" : { "en" : "Hide libraries" } }, "workspace.header.menu.hide-palette" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:104" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:101" ], "translations" : { "en" : "Hide color palette" } }, "workspace.header.menu.hide-rules" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:80" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:77" ], "translations" : { "en" : "Hide rules" } }, "workspace.header.menu.show-grid" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:87" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:84" ], "translations" : { "en" : "Show grid" } }, "workspace.header.menu.show-layers" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:99" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:96" ], "translations" : { "en" : "Show layers" } }, "workspace.header.menu.show-libraries" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:111" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:108" ], "translations" : { "en" : "Show libraries" } }, "workspace.header.menu.show-palette" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:105" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:102" ], "translations" : { "en" : "Show color palette" } }, "workspace.header.menu.show-rules" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:81" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:78" ], "translations" : { "en" : "Show rules" } }, "workspace.header.viewer" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:154" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:151" ], "translations" : { "en" : "View mode (Ctrl + P)", "fr" : "Mode visualisation (Ctrl + P)" @@ -1104,6 +1104,120 @@ }, "unused" : true }, + "workspace.options.grid.auto" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:40" ], + "translations" : { + "en" : "Auto" + } + }, + "workspace.options.grid.column" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:125" ], + "translations" : { + "en" : "Columns" + } + }, + "workspace.options.grid.params.columns" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:164" ], + "translations" : { + "en" : "Columns" + } + }, + "workspace.options.grid.params.gutter" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:191" ], + "translations" : { + "en" : "Gutter" + } + }, + "workspace.options.grid.params.height" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:184" ], + "translations" : { + "en" : "Height" + } + }, + "workspace.options.grid.params.margin" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:196" ], + "translations" : { + "en" : "Margin" + } + }, + "workspace.options.grid.params.rows" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:156" ], + "translations" : { + "en" : "Rows" + } + }, + "workspace.options.grid.params.set-default" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:208" ], + "translations" : { + "en" : "Set as default" + } + }, + "workspace.options.grid.params.size" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:149" ], + "translations" : { + "en" : "Size" + } + }, + "workspace.options.grid.params.type" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:172" ], + "translations" : { + "en" : "Type" + } + }, + "workspace.options.grid.params.type.center" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:176" ], + "translations" : { + "en" : "Center" + } + }, + "workspace.options.grid.params.type.left" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:175" ], + "translations" : { + "en" : "Left" + } + }, + "workspace.options.grid.params.type.right" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:177" ], + "translations" : { + "en" : "Right" + } + }, + "workspace.options.grid.params.type.stretch" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:174" ], + "translations" : { + "en" : "Stretch" + } + }, + "workspace.options.grid.params.use-default" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:206" ], + "translations" : { + "en" : "Use default" + } + }, + "workspace.options.grid.params.width" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:183" ], + "translations" : { + "en" : "Width" + } + }, + "workspace.options.grid.row" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:126" ], + "translations" : { + "en" : "Rows" + } + }, + "workspace.options.grid.square" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:124" ], + "translations" : { + "en" : "Square" + } + }, + "workspace.options.grid.title" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs:220" ], + "translations" : { + "en" : "Grid & Layouts" + } + }, "workspace.options.line-height-letter-spacing" : { "translations" : { "en" : "Line height and Letter spacing", diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index 937432c47e..6aec7138c1 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -285,7 +285,6 @@ position: absolute; left: 0; z-index: 12; - // width: 200px; max-height: 30rem; min-width: 7rem; overflow-y: auto; diff --git a/frontend/src/uxbox/main/ui/workspace/header.cljs b/frontend/src/uxbox/main/ui/workspace/header.cljs index ac38e9efb7..fb7ab40b47 100644 --- a/frontend/src/uxbox/main/ui/workspace/header.cljs +++ b/frontend/src/uxbox/main/ui/workspace/header.cljs @@ -21,7 +21,7 @@ [uxbox.main.ui.workspace.images :refer [import-image-modal]] [uxbox.main.ui.components.dropdown :refer [dropdown]] [uxbox.main.ui.workspace.presence :as presence] - [uxbox.util.i18n :as i18n :refer [tr t]] + [uxbox.util.i18n :as i18n :refer [t]] [uxbox.util.data :refer [classnames]] [uxbox.util.math :as mth] [uxbox.util.router :as rt])) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs index 5e835f44bf..29588bad5c 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs @@ -23,7 +23,8 @@ [uxbox.main.ui.workspace.sidebar.options.rows.input-row :refer [input-row]] [uxbox.main.ui.components.select :refer [select]] [uxbox.main.ui.components.editable-select :refer [editable-select]] - [uxbox.main.ui.components.dropdown :refer [dropdown]])) + [uxbox.main.ui.components.dropdown :refer [dropdown]] + [uxbox.util.i18n :as i18n :refer [tr t]])) (mf/defc advanced-options [{:keys [visible? on-close children]}] (when visible? @@ -36,12 +37,13 @@ children]])) (def ^:private size-options - [{:value :auto :label "Auto"} + [{:value :auto :label (tr "workspace.options.grid.auto")} :separator 18 12 10 8 6 4 3 2]) (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 + (let [locale (i18n/use-locale) + state (mf/use-state {:show-advanced-options false :changes {}}) {:keys [type display params] :as grid} (d/deep-merge grid (:changes @state)) @@ -119,9 +121,9 @@ [:& select {:class "flex-grow" :default-value type - :options [{:value :square :label "Square"} - {:value :column :label "Columns"} - {:value :row :label "Rows"}] + :options [{:value :square :label (t locale "workspace.options.grid.square")} + {:value :column :label (t locale "workspace.options.grid.column")} + {:value :row :label (t locale "workspace.options.grid.row")}] :on-change handle-change-type}] (if (= type :square) @@ -144,14 +146,14 @@ [:& advanced-options {:visible? (:show-advanced-options @state) :on-close toggle-advanced-options} (when (= :square type) - [:& input-row {:label "Size" + [:& input-row {:label (t locale "workspace.options.grid.params.size") :class "pixels" :min 1 :value (:size params) :on-change (handle-change :params :size)}]) (when (= :row type) - [:& input-row {:label "Rows" + [:& input-row {:label (t locale "workspace.options.grid.params.rows") :type :editable-select :options size-options :value (:size params) @@ -159,7 +161,7 @@ :on-change handle-change-size}]) (when (= :column type) - [:& input-row {:label "Columns" + [:& input-row {:label (t locale "workspace.options.grid.params.columns") :type :editable-select :options size-options :value (:size params) @@ -167,29 +169,31 @@ :on-change handle-change-size}]) (when (#{:row :column} type) - [:& input-row {:label "Type" + [:& input-row {:label (t locale "workspace.options.grid.params.type") :type :select - :options [{:value :stretch :label "Stretch"} - {:value :left :label "Left"} - {:value :center :label "Center"} - {:value :right :label "Right"}] + :options [{:value :stretch :label (t locale "workspace.options.grid.params.type.stretch")} + {:value :left :label (t locale "workspace.options.grid.params.type.left")} + {:value :center :label (t locale "workspace.options.grid.params.type.center")} + {:value :right :label (t locale "workspace.options.grid.params.type.right")}] :value (:type params) :on-change (handle-change :params :type)}]) (when (#{:row :column} type) - [:& input-row {:label (if (= :row type) "Height" "Width") + [:& input-row {:label (if (= :row type) + (t locale "workspace.options.grid.params.width") + (t locale "workspace.options.grid.params.height")) :class "pixels" :value (or (:item-length params) "") :on-change handle-change-item-length}]) (when (#{:row :column} type) [:* - [:& input-row {:label "Gutter" + [:& input-row {:label (t locale "workspace.options.grid.params.gutter") :class "pixels" :value (:gutter params) :min 0 :on-change (handle-change :params :gutter)}] - [:& input-row {:label "Margin" + [:& input-row {:label (t locale "workspace.options.grid.params.margin") :class "pixels" :min 0 :value (:margin params) @@ -199,12 +203,13 @@ :on-change (handle-change :params :color)}] [:div.row-flex [:button.btn-options {:disabled is-default - :on-click handle-use-default} "Use default"] + :on-click handle-use-default} (t locale "workspace.options.grid.params.use-default")] [:button.btn-options {:disabled is-default - :on-click handle-set-as-default} "Set as default"]]]])) + :on-click handle-set-as-default} (t locale "workspace.options.grid.params.set-default")]]]])) (mf/defc frame-grid [{:keys [shape]}] - (let [id (:id shape) + (let [locale (i18n/use-locale) + id (:id shape) 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))) @@ -212,7 +217,7 @@ handle-save-grid (fn [grid] (st/emit! (dw/set-default-grid (:type grid) (:params grid))))] [:div.element-set [:div.element-set-title - [:span "Grids"] + [:span (t locale "workspace.options.grid.title")] [:div.add-page {:on-click handle-create-grid} i/close]] (when (not (empty? (:grids shape))) From 2a4573842d20b1acefc7a9b8c0dfd0cfdacd0af3 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 19 May 2020 14:25:31 +0200 Subject: [PATCH 10/11] :sparkles: Small change to frame labels --- frontend/src/uxbox/main/refs.cljs | 1 - frontend/src/uxbox/main/ui/components/dropdown.cljs | 4 ++-- .../src/uxbox/main/ui/components/editable_select.cljs | 2 +- frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs | 2 +- .../uxbox/main/ui/workspace/sidebar/options/frame.cljs | 8 ++++---- .../main/ui/workspace/sidebar/options/frame_grid.cljs | 4 ++-- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/frontend/src/uxbox/main/refs.cljs b/frontend/src/uxbox/main/refs.cljs index 84d72442f4..2437461b37 100644 --- a/frontend/src/uxbox/main/refs.cljs +++ b/frontend/src/uxbox/main/refs.cljs @@ -74,7 +74,6 @@ (def workspace-saved-grids (l/derived :saved-grids workspace-page-options)) - (def workspace-objects (l/derived :objects workspace-data)) diff --git a/frontend/src/uxbox/main/ui/components/dropdown.cljs b/frontend/src/uxbox/main/ui/components/dropdown.cljs index eae0f817d0..48f8f6d867 100644 --- a/frontend/src/uxbox/main/ui/components/dropdown.cljs +++ b/frontend/src/uxbox/main/ui/components/dropdown.cljs @@ -43,8 +43,8 @@ (mf/defc dropdown {::mf/wrap-props false} [props] - #_(assert (fn? (gobj/get props "on-close")) "missing `on-close` prop") - #_(assert (boolean? (gobj/get props "show")) "missing `show` prop") + (assert (fn? (gobj/get props "on-close")) "missing `on-close` prop") + (assert (boolean? (gobj/get props "show")) "missing `show` prop") (when (gobj/get props "show") (mf/element dropdown' props))) diff --git a/frontend/src/uxbox/main/ui/components/editable_select.cljs b/frontend/src/uxbox/main/ui/components/editable_select.cljs index 57093c6cb4..5d2994d4c9 100644 --- a/frontend/src/uxbox/main/ui/components/editable_select.cljs +++ b/frontend/src/uxbox/main/ui/components/editable_select.cljs @@ -55,7 +55,7 @@ :type type}] [:span.dropdown-button {:on-click open-dropdown} i/arrow-down] - [:& dropdown {:show (:is-open? @state) + [:& dropdown {:show (get @state :is-open? false) :on-close close-dropdown} [:ul.custom-select-dropdown (for [[index item] (map-indexed vector options)] diff --git a/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs b/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs index 28cadea2c0..e39abfe6c1 100644 --- a/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs @@ -71,7 +71,7 @@ childs (mapv #(get objects %) (:shapes shape)) ds-modifier (get-in shape [:modifiers :displacement]) - label-pos (gpt/point x (- y 10)) + label-pos (gpt/point x (- y (/ 10 zoom))) on-double-click (mf/use-callback diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs index 13d525922b..1dabf3e554 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs @@ -38,10 +38,10 @@ on-orientation-clicked (fn [orientation] - (let [width (:width shape) - height (:height shape) - new-width (if (= orientation :horiz) (max width height) (min width height)) - new-height (if (= orientation :horiz) (min width height) (max width height))] + (let [width (:width shape) + height (:height shape) + new-width (if (= orientation :horiz) (max width height) (min width height)) + new-height (if (= orientation :horiz) (min width height) (max width height))] (st/emit! (udw/update-rect-dimensions (:id shape) :width new-width) (udw/update-rect-dimensions (:id shape) :height new-height)))) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs index 29588bad5c..5e2fc522f5 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame_grid.cljs @@ -180,8 +180,8 @@ (when (#{:row :column} type) [:& input-row {:label (if (= :row type) - (t locale "workspace.options.grid.params.width") - (t locale "workspace.options.grid.params.height")) + (t locale "workspace.options.grid.params.height") + (t locale "workspace.options.grid.params.width")) :class "pixels" :value (or (:item-length params) "") :on-change handle-change-item-length}]) From 9681a4b32ed6356ee2401c4bf4ef8460976d9c2a Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 19 May 2020 15:22:39 +0200 Subject: [PATCH 11/11] :sparkles: Fixed tab space --- .../partials/sidebar-element-options.scss | 443 +++++++++--------- .../ui/workspace/sidebar/options/frame.cljs | 2 +- 2 files changed, 227 insertions(+), 218 deletions(-) diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index 6aec7138c1..183a68e616 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -279,155 +279,159 @@ font-size: $fs13; } - + } - .custom-select-dropdown { - position: absolute; - left: 0; - z-index: 12; - max-height: 30rem; - min-width: 7rem; - overflow-y: auto; + .custom-select-dropdown { + position: absolute; + left: 0; + z-index: 12; + max-height: 30rem; + min-width: 7rem; + overflow-y: auto; - background-color: $color-white; - border-radius: $br-small; - box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25); + background-color: $color-white; + border-radius: $br-small; + box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25); - hr { - margin: 0; - border-color: $color-gray-20; + .presets { + width: 200px; + } + + hr { + margin: 0; + border-color: $color-gray-20; + } + + li { + color: $color-gray-60; + cursor: pointer; + font-size: $fs14; + display: flex; + padding: $small; + + span { + color: $color-gray-20; + margin-left: auto; } - li { + &.dropdown-separator:not(:last-child) { + border-bottom: 1px solid $color-gray-10; + } + + &.dropdown-label:not(:first-child) { + border-top: 1px solid $color-gray-10; + } + + &.dropdown-label span { + margin-left: 0; + } + + &:hover { + background-color: $color-primary-lighter; + } + } + } + + & li.checked-element { + padding-left: 0; + + & span { + margin: 0; + color: $color-black; + } + + & svg { + visibility: hidden; + width: 8px; + height: 8px; + background: none; + margin: 0.25rem; + fill: $color-black; + } + + &.is-selected { + & svg { + visibility: visible; + } + } + } + + .editable-select { + position: relative; + height: 38px; + margin-right: $small; + position: relative; + width: 60%; + + svg { + fill: $color-gray-40; + height: 10px; + width: 10px; + } + + .input-text { + left: 0; + position: absolute; + top: -1px; + width: 60%; + } + + .input-select { + background-color: transparent; + border: none; + border-bottom: 1px solid $color-gray-40; + color: transparent; + left: 0; + position: absolute; + top: 0; + width: 100%; + + option { color: $color-gray-60; - cursor: pointer; - font-size: $fs14; - display: flex; - padding: $small; - - span { - color: $color-gray-20; - margin-left: auto; - } - - &.dropdown-separator:not(:last-child) { - border-bottom: 1px solid $color-gray-10; - } - - &.dropdown-label:not(:first-child) { - border-top: 1px solid $color-gray-10; - } - - &.dropdown-label span { - margin-left: 0; - } - - &:hover { - background-color: $color-primary-lighter; - } + background: $color-white; + font-size: $fs12; } } - & li.checked-element { - padding-left: 0; + .dropdown-button { + position: absolute; + top: 7px; + right: 0; + } - & span { - margin: 0; - color: $color-black; - } - - & svg { - visibility: hidden; - width: 8px; - height: 8px; - background: none; - margin: 0.25rem; - fill: $color-black; - } - - &.is-selected { - & svg { - visibility: visible; - } - } - } - - .editable-select { - position: relative; - height: 38px; - margin-right: $small; - position: relative; - width: 60%; - - svg { - fill: $color-gray-40; - height: 10px; - width: 10px; - } + &.input-option { + height: 2rem; + border-bottom: 1px solid #64666A; + width: 100%; + margin-left: 0.25rem; .input-text { - left: 0; - position: absolute; - top: -1px; - width: 60%; - } - - .input-select { - background-color: transparent; - border: none; - border-bottom: 1px solid $color-gray-40; - color: transparent; - left: 0; - position: absolute; - top: 0; - width: 100%; - - option { - color: $color-gray-60; - background: $color-white; - font-size: $fs12; - } - } - - .dropdown-button { - position: absolute; - top: 7px; - right: 0; - } - - &.input-option { - height: 2rem; - border-bottom: 1px solid #64666A; - width: 100%; - margin-left: 0.25rem; - - .input-text { - border: none; - margin: 0; - width: calc(100% - 12px); - height: 100%; - top: auto; - color: #b1b2b5; - } + border: none; + margin: 0; + width: calc(100% - 12px); + height: 100%; + top: auto; + color: #b1b2b5; } + } } } .element-set-content .grid-option-main { - .editable-select.input-option .input-text { - padding: 0; - padding-top: 0.18rem; - } + .editable-select.input-option .input-text { + padding: 0; + padding-top: 0.18rem; + } - .input-element { - width: 55px; - margin: 0 0.2rem; - } + .input-element { + width: 55px; + margin: 0 0.2rem; + } - .input-text { - padding-left: 0; - color: #b1b2b5; - } + .input-text { + padding-left: 0; + color: #b1b2b5; + } } .color-th { @@ -461,7 +465,12 @@ } } +} +.presets { + .custom-select-dropdown { + width: 200px; + } } .row-flex.align-icons { @@ -602,7 +611,7 @@ height: 18px; position: relative; width: 18px; - + svg { fill: $color-gray-30; height: 16px; @@ -612,127 +621,127 @@ } .custom-button { - cursor: pointer; - background: none; - border: none; + cursor: pointer; + background: none; + border: none; - & svg { - width: 1rem; - height: 1rem; - fill: $color-gray-20; - } + & svg { + width: 1rem; + height: 1rem; + fill: $color-gray-20; + } - &:hover svg, &.is-active svg { - fill: $color-primary; - } + &:hover svg, &.is-active svg { + fill: $color-primary; + } } .element-set-content .input-row { - & .element-set-subtitle { - width: 5.5rem; - } + & .element-set-subtitle { + width: 5.5rem; + } } .grid-option { - margin-bottom: 0.5rem; + margin-bottom: 0.5rem; } .element-set-content .custom-select.input-option { - border-top: none; - border-left: none; - border-right: none; - margin-left: 0.25rem; + border-top: none; + border-left: none; + border-right: none; + margin-left: 0.25rem; } .element-set-content .grid-option-main { - display: flex; - padding: 0.5rem 0; - border: 1px solid $color-black; - border-radius: 4px; + display: flex; + padding: 0.5rem 0; + border: 1px solid $color-black; + border-radius: 4px; - &:hover { - background: #1F1F1F; - } + &:hover { + background: #1F1F1F; + } - & .custom-select { - min-width: 4.75rem; - height: 2rem; - border: none; - border-bottom: 1px solid #65666A; - } + & .custom-select { + min-width: 4.75rem; + height: 2rem; + border: none; + border-bottom: 1px solid #65666A; + } - & .input-element { - width: 50px; - overflow: hidden; - } + & .input-element { + width: 50px; + overflow: hidden; + } + + & .custom-select-dropdown { + width: 96px; + } + + & .input-option { + margin-left: 0.5rem; & .custom-select-dropdown { - width: 96px; + width: 56px; + min-width: 56px; + max-height: 10rem; } - & .input-option { - margin-left: 0.5rem; - - & .custom-select-dropdown { - width: 56px; - min-width: 56px; - max-height: 10rem; - } - - } + } } .grid-option-main-actions { - display: flex; - visibility: hidden; + display: flex; + visibility: hidden; - .grid-option:hover & { - visibility: visible; - } + .grid-option:hover & { + visibility: visible; + } } .focus-overlay { - background: $color-black; - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; - opacity: 0.4; + background: $color-black; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; + opacity: 0.4; } .element-set-content .advanced-options { - background-color: #303236; - border-radius: 4px; - left: -8px; - padding: 0.5rem; - position: relative; - top: 2px; - width: calc(100% + 16px); + background-color: #303236; + border-radius: 4px; + left: -8px; + padding: 0.5rem; + position: relative; + top: 2px; + width: calc(100% + 16px); } .btn-options { - cursor: pointer; - border: 1px solid $color-black; - background: $color-gray-60; - border-radius: 2px; - color: $color-gray-20; - font-size: 11px; - line-height: 16px; - flex-grow: 1; - padding: 0.25rem 0; + cursor: pointer; + border: 1px solid $color-black; + background: $color-gray-60; + border-radius: 2px; + color: $color-gray-20; + font-size: 11px; + line-height: 16px; + flex-grow: 1; + padding: 0.25rem 0; - &:first-child { - margin-right: 0.5rem; - } + &:first-child { + margin-right: 0.5rem; + } - &:not([disabled]):hover { - background: $color-primary; - color: $color-black; - } + &:not([disabled]):hover { + background: $color-primary; + color: $color-black; + } - &[disabled] { - opacity: 0.4; - cursor: auto; - } + &[disabled] { + opacity: 0.4; + cursor: auto; + } } diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs index 1dabf3e554..1c49bb35f1 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/frame.cljs @@ -80,7 +80,7 @@ [:div.element-set-content [:div.row-flex - [:div.custom-select.flex-grow {:on-click #(reset! show-presets-dropdown? true)} + [:div.presets.custom-select.flex-grow {:on-click #(reset! show-presets-dropdown? true)} [:span (tr "workspace.options.size-presets")] [:span.dropdown-button i/arrow-down] [:& dropdown {:show @show-presets-dropdown?