From 0b4996b31a77589410391ed275b60d96bc515de5 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 14 May 2020 14:08:17 +0200 Subject: [PATCH] :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}])]])