mirror of
https://github.com/penpot/penpot.git
synced 2025-03-16 01:31:22 -05:00
🎉 Adds support for auto
This commit is contained in:
parent
1d2ae6d5eb
commit
ad99e6df9d
6 changed files with 261 additions and 105 deletions
|
@ -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;
|
||||
|
|
|
@ -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)))
|
||||
|
|
70
frontend/src/uxbox/main/ui/components/editable_select.cljs
Normal file
70
frontend/src/uxbox/main/ui/components/editable_select.cljs
Normal file
|
@ -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]])))]]]))
|
|
@ -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)}]])
|
||||
|
||||
|
|
|
@ -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}]))
|
||||
|
||||
]])
|
||||
|
|
|
@ -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))))
|
||||
|
|
Loading…
Add table
Reference in a new issue