mirror of
https://github.com/penpot/penpot.git
synced 2025-03-19 19:21:23 -05:00
♻️ Refactor: shape data structure, dashboard data loading...
This commit is contained in:
parent
9f8936ea40
commit
1e058463b2
56 changed files with 1785 additions and 2316 deletions
backend/src
common/uxbox/common
frontend
deps.edn
resources/styles/main/partials
src/uxbox
|
@ -56,7 +56,6 @@
|
|||
(let [sql create-project-user-sql
|
||||
project-id (mk-uuid "project" project-index user-index)
|
||||
user-id (mk-uuid "user" (dec user-index))]
|
||||
(println sql project-id user-id)
|
||||
(db/query-one conn [sql project-id user-id])))
|
||||
|
||||
;; --- Projects creation
|
||||
|
@ -114,10 +113,10 @@
|
|||
(let [canvas {:id (mk-uuid "canvas" 1)
|
||||
:name "Canvas-1"
|
||||
:type :canvas
|
||||
:x1 200
|
||||
:y1 200
|
||||
:x2 1224
|
||||
:y2 968}
|
||||
:x 200
|
||||
:y 200
|
||||
:width 1024
|
||||
:height 768}
|
||||
data {:shapes []
|
||||
:canvas [(:id canvas)]
|
||||
:shapes-by-id {(:id canvas) canvas}}
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
[conn {:keys [name] :as item}]
|
||||
(log/info "Creating or updating icons collection:" name)
|
||||
(let [id (uuid/namespaced +icons-uuid-ns+ name)
|
||||
sql "insert into icons_collections (id, user_id, name)
|
||||
sql "insert into icon_collections (id, user_id, name)
|
||||
values ($1, '00000000-0000-0000-0000-000000000000'::uuid, $2)
|
||||
on conflict (id)
|
||||
do update set name = $2
|
||||
|
@ -122,8 +122,8 @@
|
|||
"Create or replace image collection by its name."
|
||||
[conn {:keys [name] :as item}]
|
||||
(log/info "Creating or updating image collection:" name)
|
||||
(let [id (uuid/namespaced +icons-uuid-ns+ name)
|
||||
sql "insert into images_collections (id, user_id, name)
|
||||
(let [id (uuid/namespaced +images-uuid-ns+ name)
|
||||
sql "insert into image_collections (id, user_id, name)
|
||||
values ($1, '00000000-0000-0000-0000-000000000000'::uuid, $2)
|
||||
on conflict (id)
|
||||
do update set name = $2
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
(sm/defmutation ::create-icons-collection
|
||||
[{:keys [id user name] :as params}]
|
||||
(let [id (or id (uuid/next))
|
||||
sql "insert into icons_collections (id, user_id, name)
|
||||
sql "insert into icon_collections (id, user_id, name)
|
||||
values ($1, $2, $3) returning *"]
|
||||
(db/query-one db/pool [sql id user name])))
|
||||
|
||||
|
@ -56,7 +56,7 @@
|
|||
|
||||
(sm/defmutation ::update-icons-collection
|
||||
[{:keys [id user name] :as params}]
|
||||
(let [sql "update icons_collections
|
||||
(let [sql "update icon_collections
|
||||
set name = $3
|
||||
where id = $1
|
||||
and user_id = $2
|
||||
|
@ -97,7 +97,7 @@
|
|||
|
||||
(sm/defmutation ::delete-icons-collection
|
||||
[{:keys [user id] :as params}]
|
||||
(let [sql "update icons_collections
|
||||
(let [sql "update icon_collections
|
||||
set deleted_at = clock_timestamp()
|
||||
where id = $1
|
||||
and user_id = $2
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
|
||||
(sm/defmutation ::create-image-collection
|
||||
[{:keys [id user name] :as params}]
|
||||
(let [sql "insert into images_collections (id, user_id, name)
|
||||
(let [sql "insert into image_collections (id, user_id, name)
|
||||
values ($1, $2, $3) returning *;"]
|
||||
(db/query-one db/pool [sql (or id (uuid/next)) user name])))
|
||||
|
||||
|
@ -71,7 +71,7 @@
|
|||
|
||||
(sm/defmutation ::update-images-collection
|
||||
[{:keys [id user name] :as params}]
|
||||
(let [sql "update images_collections
|
||||
(let [sql "update image_collections
|
||||
set name = $3
|
||||
where id = $1
|
||||
and user_id = $2
|
||||
|
@ -85,7 +85,7 @@
|
|||
|
||||
(sm/defmutation ::delete-images-collection
|
||||
[{:keys [id user] :as params}]
|
||||
(let [sql "update images_collections
|
||||
(let [sql "update image_collections
|
||||
set deleted_at = clock_timestamp()
|
||||
where id = $1
|
||||
and user_id = $2
|
||||
|
@ -102,13 +102,13 @@
|
|||
(-> (ds/save storage filename path)
|
||||
(su/handle-on-context))))
|
||||
|
||||
(def ^:private create-image-sql
|
||||
(su/defstr sql:create-image
|
||||
"insert into images (user_id, name, collection_id, path, width, height, mimetype)
|
||||
values ($1, $2, $3, $4, $5, $6, $7) returning *")
|
||||
|
||||
(defn- store-image-in-db
|
||||
[conn {:keys [id user name path collection-id height width mimetype]}]
|
||||
(let [sqlv [create-image-sql user name collection-id
|
||||
(let [sqlv [sql:create-image user name collection-id
|
||||
path width height mimetype]]
|
||||
(-> (db/query-one conn sqlv)
|
||||
(p/then populate-thumbnail)
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
(ex/raise :type :validation
|
||||
:code :not-authorized))))))
|
||||
|
||||
;; --- Mutation: Create Project
|
||||
;; --- Mutation: Create Project File
|
||||
|
||||
(declare create-file)
|
||||
(declare create-page)
|
||||
|
@ -72,9 +72,9 @@
|
|||
[{:keys [user project-id] :as params}]
|
||||
(db/with-atomic [conn db/pool]
|
||||
(proj/check-edition-permissions! conn user project-id)
|
||||
(p/let [file (create-file conn params)]
|
||||
(create-page conn (assoc params :file-id (:id file)))
|
||||
file)))
|
||||
(p/let [file (create-file conn params)
|
||||
page (create-page conn (assoc params :file-id (:id file)))]
|
||||
(assoc file :pages [(:id page)]))))
|
||||
|
||||
(defn create-file
|
||||
[conn {:keys [id user name project-id] :as params}]
|
||||
|
@ -88,7 +88,10 @@
|
|||
[conn {:keys [user file-id] :as params}]
|
||||
(let [id (uuid/next)
|
||||
name "Page 1"
|
||||
data (blob/encode {})
|
||||
data (blob/encode
|
||||
{:shapes []
|
||||
:canvas []
|
||||
:shapes-by-id {}})
|
||||
sql "insert into project_pages (id, user_id, file_id, name, version,
|
||||
ordering, data)
|
||||
values ($1, $2, $3, $4, 0, 1, $5) returning id"]
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
(def ^:private icons-collections-sql
|
||||
"select *,
|
||||
(select count(*) from icons where collection_id = ic.id) as num_icons
|
||||
from icons_collections as ic
|
||||
from icon_collections as ic
|
||||
where (ic.user_id = $1 or
|
||||
ic.user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and ic.deleted_at is null
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
(def ^:private images-collections-sql
|
||||
"select *,
|
||||
(select count(*) from images where collection_id = ic.id) as num_images
|
||||
from images_collections as ic
|
||||
from image_collections as ic
|
||||
where (ic.user_id = $1 or
|
||||
ic.user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and ic.deleted_at is null
|
||||
|
@ -86,23 +86,32 @@
|
|||
|
||||
;; --- Query Images by Collection (id)
|
||||
|
||||
(def images-by-collection-sql
|
||||
(su/defstr sql:images-by-collection
|
||||
"select * from images
|
||||
where (user_id = $1 or
|
||||
user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and deleted_at is null
|
||||
and case when $2::uuid is null then collection_id is null
|
||||
else collection_id = $2::uuid
|
||||
end
|
||||
order by created_at desc;")
|
||||
order by created_at desc")
|
||||
|
||||
(s/def ::images-by-collection-query
|
||||
(su/defstr sql:images-by-collection1
|
||||
"with images as (~{sql:images-by-collection})
|
||||
select im.* from images as im
|
||||
where im.collection_id is null")
|
||||
|
||||
(su/defstr sql:images-by-collection2
|
||||
"with images as (~{sql:images-by-collection})
|
||||
select im.* from images as im
|
||||
where im.collection_id = $2")
|
||||
|
||||
(s/def ::images-by-collection
|
||||
(s/keys :req-un [::user]
|
||||
:opt-un [::collection-id]))
|
||||
|
||||
(sq/defquery ::images-by-collection
|
||||
[{:keys [user collection-id] :as params}]
|
||||
(let [sqlv [images-by-collection-sql user collection-id]]
|
||||
(let [sqlv (if (nil? collection-id)
|
||||
[sql:images-by-collection1 user]
|
||||
[sql:images-by-collection2 user collection-id])]
|
||||
(-> (db/query db/pool sqlv)
|
||||
(p/then populate-thumbnails)
|
||||
(p/then #(mapv populate-urls %)))))
|
||||
|
|
|
@ -99,13 +99,13 @@
|
|||
{:enter (fn [data]
|
||||
(let [context (get-in data [:request ::vw/routing-context])
|
||||
uploads (reduce (fn [acc ^FileUpload upload]
|
||||
(assoc acc
|
||||
(keyword (.name upload))
|
||||
{:type :uploaded-file
|
||||
:mtype (.contentType upload)
|
||||
:path (.uploadedFileName upload)
|
||||
:name (.fileName upload)
|
||||
:size (.size upload)}))
|
||||
(assoc! acc
|
||||
(keyword (.name upload))
|
||||
{:type :uploaded-file
|
||||
:mtype (.contentType upload)
|
||||
:path (.uploadedFileName upload)
|
||||
:name (.fileName upload)
|
||||
:size (.size upload)}))
|
||||
(transient {})
|
||||
(.fileUploads ^RoutingContext context))]
|
||||
(update data :request assoc attr (persistent! uploads))))}))
|
||||
|
|
|
@ -7,7 +7,13 @@
|
|||
(ns uxbox.common.data
|
||||
"Data manipulation and query helper functions."
|
||||
(:refer-clojure :exclude [concat])
|
||||
(:require [clojure.set :as set]))
|
||||
(:require [clojure.set :as set]
|
||||
#?(:cljs [cljs.reader :as r]
|
||||
:clj [clojure.edn :as r])))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Data Structures Manipulation
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn concat
|
||||
[& colls]
|
||||
|
@ -18,6 +24,18 @@
|
|||
(rest colls))
|
||||
result)))
|
||||
|
||||
(defn enumerate
|
||||
([items] (enumerate items 0))
|
||||
([items start]
|
||||
(loop [idx start
|
||||
items items
|
||||
res []]
|
||||
(if (empty? items)
|
||||
res
|
||||
(recur (inc idx)
|
||||
(rest items)
|
||||
(conj res [idx (first items)]))))))
|
||||
|
||||
(defn seek
|
||||
([pred coll]
|
||||
(seek pred coll nil))
|
||||
|
@ -48,3 +66,75 @@
|
|||
(recur (first r) (rest r) rs)
|
||||
(recur (first r) (rest r) (conj rs [:mod k vmb]))))
|
||||
rs)))))
|
||||
|
||||
(defn index-by
|
||||
"Return a indexed map of the collection keyed by the result of
|
||||
executing the getter over each element of the collection."
|
||||
[getter coll]
|
||||
(persistent!
|
||||
(reduce #(assoc! %1 (getter %2) %2) (transient {}) coll)))
|
||||
|
||||
(defn remove-nil-vals
|
||||
"Given a map, return a map removing key-value
|
||||
pairs when value is `nil`."
|
||||
[data]
|
||||
(into {} (remove (comp nil? second) data)))
|
||||
|
||||
(defn without-keys
|
||||
"Return a map without the keys provided
|
||||
in the `keys` parameter."
|
||||
[data keys]
|
||||
(persistent!
|
||||
(reduce #(dissoc! %1 %2) (transient data) keys)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Data Parsing / Conversion
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn- nan?
|
||||
[v]
|
||||
(not= v v))
|
||||
|
||||
(defn- impl-parse-integer
|
||||
[v]
|
||||
#?(:cljs (js/parseInt v 10)
|
||||
:clj (try
|
||||
(Integer/parseInt v)
|
||||
(catch Throwable e
|
||||
nil))))
|
||||
|
||||
(defn- impl-parse-double
|
||||
[v]
|
||||
#?(:cljs (js/parseFloat v)
|
||||
:clj (try
|
||||
(Double/parseDouble v)
|
||||
(catch Throwable e
|
||||
nil))))
|
||||
|
||||
(defn parse-integer
|
||||
([v]
|
||||
(parse-integer v nil))
|
||||
([v default]
|
||||
(let [v (impl-parse-integer v)]
|
||||
(if (or (nil? v) (nan? v))
|
||||
default
|
||||
v))))
|
||||
|
||||
(defn parse-double
|
||||
([v]
|
||||
(parse-double v nil))
|
||||
([v default]
|
||||
(let [v (impl-parse-double v)]
|
||||
(if (or (nil? v) (nan? v))
|
||||
default
|
||||
v))))
|
||||
|
||||
(defn read-string
|
||||
[v]
|
||||
(r/read-string v))
|
||||
|
||||
(defn coalesce-str
|
||||
[val default]
|
||||
(if (or (nil? val) (nan? val))
|
||||
default
|
||||
(str val)))
|
||||
|
|
|
@ -16,13 +16,12 @@
|
|||
(s/def ::background string?)
|
||||
(s/def ::background-opacity number?)
|
||||
|
||||
;; Page related
|
||||
(s/def ::file-id uuid?)
|
||||
(s/def ::user uuid?)
|
||||
(s/def ::created-at inst?)
|
||||
(s/def ::modified-at inst?)
|
||||
(s/def ::version number?)
|
||||
(s/def ::ordering number?)
|
||||
(s/def ::metadata
|
||||
(s/keys :opt-un [::grid-y-axis
|
||||
::grid-x-axis
|
||||
::grid-color
|
||||
::background
|
||||
::background-opacity]))
|
||||
|
||||
;; Page Data related
|
||||
(s/def ::shape
|
||||
|
@ -35,19 +34,9 @@
|
|||
(s/def ::shapes-by-id
|
||||
(s/map-of uuid? ::shape))
|
||||
|
||||
;; Main
|
||||
|
||||
(s/def ::data
|
||||
(s/keys :req-un [::shapes ::canvas ::shapes-by-id]))
|
||||
|
||||
|
||||
(s/def ::metadata
|
||||
(s/keys :opt-un [::grid-y-axis
|
||||
::grid-x-axis
|
||||
::grid-color
|
||||
::background
|
||||
::background-opacity]))
|
||||
|
||||
(s/def ::shape-change
|
||||
(s/tuple #{:add :mod :del} keyword? any?))
|
||||
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
org.clojure/clojure {:mvn/version "1.10.1"}
|
||||
com.cognitect/transit-cljs {:mvn/version "0.8.256"}
|
||||
|
||||
cljsjs/react {:mvn/version "16.11.0-0"}
|
||||
cljsjs/react-dom {:mvn/version "16.11.0-0"}
|
||||
cljsjs/react-dom-server {:mvn/version "16.11.0-0"}
|
||||
cljsjs/react {:mvn/version "16.12.0-1"}
|
||||
cljsjs/react-dom {:mvn/version "16.12.0-1"}
|
||||
cljsjs/react-dom-server {:mvn/version "16.12.0-1"}
|
||||
|
||||
environ/environ {:mvn/version "1.1.0"}
|
||||
metosin/reitit-core {:mvn/version "0.3.10"}
|
||||
expound/expound {:mvn/version "0.7.2"}
|
||||
|
||||
funcool/beicon {:mvn/version "5.1.0"}
|
||||
funcool/beicon {:mvn/version "6.0.0-SNAPSHOT"}
|
||||
funcool/cuerdas {:mvn/version "2.2.0"}
|
||||
funcool/lentes {:mvn/version "1.3.3"}
|
||||
funcool/potok {:mvn/version "2.8.0-SNAPSHOT"}
|
||||
|
|
|
@ -242,7 +242,7 @@
|
|||
|
||||
// IMAGES SECTION
|
||||
&.images-th {
|
||||
background-color: $primary-ui-bg;
|
||||
border: 1px dashed $color-gray-light;
|
||||
border-bottom: 2px solid lighten($color-gray-light, 12%);
|
||||
|
||||
&:hover {
|
||||
|
|
|
@ -17,21 +17,14 @@
|
|||
[uxbox.util.time :as dt]
|
||||
[uxbox.util.uuid :as uuid]))
|
||||
|
||||
;; TODO: need a good refactor
|
||||
|
||||
;; --- Initialize
|
||||
|
||||
(declare fetch-collections)
|
||||
(declare persist-collections)
|
||||
(declare collections-fetched?)
|
||||
|
||||
(defrecord Initialize []
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:dashboard :colors] {:selected #{}})))
|
||||
|
||||
(defn initialize
|
||||
[]
|
||||
(Initialize.))
|
||||
|
||||
;; --- Collections Fetched
|
||||
|
||||
(defrecord CollectionsFetched [data]
|
||||
|
@ -56,7 +49,7 @@
|
|||
(defrecord FetchCollections []
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
(->> (rp/query! :kvstore-entry {:key "color-collections"})
|
||||
(->> (rp/query! :user-attr {:key "color-collections"})
|
||||
(rx/map collections-fetched)
|
||||
(rx/catch (fn [{:keys [type] :as error}]
|
||||
(if (= type :not-found)
|
||||
|
@ -99,10 +92,9 @@
|
|||
version (or (get state ::version) -1)
|
||||
value (->> (get state :colors-collections)
|
||||
(into {} xform))
|
||||
data {:id "color-collections"
|
||||
:version version
|
||||
:value value}]
|
||||
(->> (rp/mutation! :upsert-kvstore data)
|
||||
data {:key "color-collections"
|
||||
:val value}]
|
||||
(->> (rp/mutation! :upsert-user-attr data)
|
||||
(rx/map collections-fetched)))))
|
||||
|
||||
(defn persist-collections
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
(ns uxbox.main.data.icons
|
||||
(:require
|
||||
[cljs.spec.alpha :as s]
|
||||
[beicon.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]
|
||||
|
@ -18,30 +19,32 @@
|
|||
[uxbox.util.router :as r]
|
||||
[uxbox.util.uuid :as uuid]))
|
||||
|
||||
;; --- Initialize
|
||||
(s/def ::id uuid?)
|
||||
(s/def ::name string?)
|
||||
(s/def ::created-at inst?)
|
||||
(s/def ::modified-at inst?)
|
||||
(s/def ::user-id uuid?)
|
||||
|
||||
(def initialize
|
||||
(ptk/reify ::initialize
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:dashboard :icons] {:selected #{}}))))
|
||||
;; (s/def ::collection-id (s/nilable ::us/uuid))
|
||||
|
||||
;; --- Select a Collection
|
||||
;; (s/def ::mimetype string?)
|
||||
;; (s/def ::thumbnail us/url-str?)
|
||||
;; (s/def ::width number?)
|
||||
;; (s/def ::height number?)
|
||||
;; (s/def ::url us/url-str?)
|
||||
|
||||
(defn select-collection
|
||||
([type]
|
||||
(select-collection type nil))
|
||||
([type id]
|
||||
{:pre [(keyword? type)]}
|
||||
(ptk/reify ::select-collection
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(rx/of (r/navigate :dashboard/icons {:type type :id id}))))))
|
||||
(s/def ::collection
|
||||
(s/keys :req-un [::id
|
||||
::name
|
||||
::created-at
|
||||
::modified-at
|
||||
::user-id]))
|
||||
|
||||
;; --- Collections Fetched
|
||||
|
||||
(defn collections-fetched
|
||||
[items]
|
||||
(s/assert (s/every ::collection) items)
|
||||
(ptk/reify ::collections-fetched
|
||||
cljs.core/IDeref
|
||||
(-deref [_] items)
|
||||
|
@ -55,10 +58,6 @@
|
|||
state
|
||||
items))))
|
||||
|
||||
(defn collections-fetched?
|
||||
[v]
|
||||
(= ::collections-fetched (ptk/type v)))
|
||||
|
||||
;; --- Fetch Collections
|
||||
|
||||
(def fetch-collections
|
||||
|
@ -72,15 +71,12 @@
|
|||
|
||||
(defn collection-created
|
||||
[item]
|
||||
(s/assert ::collection item)
|
||||
(ptk/reify ::collection-created
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [{:keys [id] :as item} (assoc item :type :own)]
|
||||
(update state :icons-collections assoc id item)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(rx/of (select-collection :own (:id item))))))
|
||||
(update state :icons-collections assoc id item)))))
|
||||
|
||||
;; --- Create Collection
|
||||
|
||||
|
@ -100,7 +96,7 @@
|
|||
(ptk/reify ::collection-updated
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:icons-collections (:id item)] merge item))))
|
||||
(update-in state [:icons-collections (:id item)] merge item))))
|
||||
|
||||
;; --- Update Collection
|
||||
|
||||
|
@ -141,7 +137,7 @@
|
|||
(watch [_ state s]
|
||||
(let [type (get-in state [:dashboard :icons :type])]
|
||||
(->> (rp/mutation! :delete-icons-collection {:id id})
|
||||
(rx/map #(select-collection type))))))
|
||||
(rx/map #(r/nav :dashboard-icons {:type type}))))))
|
||||
|
||||
(defn delete-collection
|
||||
[id]
|
||||
|
@ -179,44 +175,42 @@
|
|||
(dom/append-child! gc child))
|
||||
(recur (dom/get-first-child svg)))
|
||||
(let [width (.. svg -width -baseVal -value)
|
||||
header (.. svg -height -baseVal -value)
|
||||
height (.. svg -height -baseVal -value)
|
||||
view-box [(.. svg -viewBox -baseVal -x)
|
||||
(.. svg -viewBox -baseVal -y)
|
||||
(.. svg -viewBox -baseVal -width)
|
||||
(.. svg -viewBox -baseVal -height)]
|
||||
props {:width width
|
||||
:mimetype "image/svg+xml"
|
||||
:height header
|
||||
:height height
|
||||
:view-box view-box}]
|
||||
[(dom/get-outer-html g) props])))))
|
||||
|
||||
(defrecord CreateIcons [id files]
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
(letfn [(parse [file]
|
||||
(->> (files/read-as-text file)
|
||||
(rx/map parse-svg)))
|
||||
(allowed? [file]
|
||||
(= (.-type file) "image/svg+xml"))
|
||||
(prepare [[content metadata]]
|
||||
{:collection-id id
|
||||
:content content
|
||||
:id (uuid/random)
|
||||
;; TODO Keep the name of the original icon
|
||||
:name (str "Icon " (gensym "i"))
|
||||
:metadata metadata})]
|
||||
(->> (rx/from-coll files)
|
||||
(rx/filter allowed?)
|
||||
(rx/flat-map parse)
|
||||
(rx/map prepare)
|
||||
(rx/flat-map #(rp/mutation! :create-icon %))
|
||||
(rx/map :payload)
|
||||
(rx/map icon-created)))))
|
||||
|
||||
(defn create-icons
|
||||
[id files]
|
||||
{:pre [(or (uuid? id) (nil? id))]}
|
||||
(CreateIcons. id files))
|
||||
(s/assert (s/nilable uuid?) id)
|
||||
(ptk/reify ::create-icons
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
(letfn [(parse [file]
|
||||
(->> (files/read-as-text file)
|
||||
(rx/map parse-svg)))
|
||||
(allowed? [file]
|
||||
(= (.-type file) "image/svg+xml"))
|
||||
(prepare [[content metadata]]
|
||||
{:collection-id id
|
||||
:content content
|
||||
:id (uuid/random)
|
||||
;; TODO Keep the name of the original icon
|
||||
:name (str "Icon " (gensym "i"))
|
||||
:metadata metadata})]
|
||||
(->> (rx/from files)
|
||||
(rx/filter allowed?)
|
||||
(rx/flat-map parse)
|
||||
(rx/map prepare)
|
||||
(rx/flat-map #(rp/mutation! :create-icon %))
|
||||
(rx/map icon-created))))))
|
||||
|
||||
;; --- Icon Persisted
|
||||
|
||||
|
@ -345,14 +339,14 @@
|
|||
(watch [_ state stream]
|
||||
(let [selected (get-in state [:dashboard :icons :selected])]
|
||||
(rx/merge
|
||||
(->> (rx/from-coll selected)
|
||||
(->> (rx/from selected)
|
||||
(rx/map #(get-in state [:icons %]))
|
||||
(rx/map #(dissoc % :id))
|
||||
(rx/map #(assoc % :collection-id id))
|
||||
(rx/flat-map #(rp/mutation :create-icon %))
|
||||
(rx/map :payload)
|
||||
(rx/map icon-created))
|
||||
(->> (rx/from-coll selected)
|
||||
(->> (rx/from selected)
|
||||
(rx/map deselect-icon))))))
|
||||
|
||||
(defn copy-selected
|
||||
|
@ -375,9 +369,9 @@
|
|||
(watch [_ state stream]
|
||||
(let [selected (get-in state [:dashboard :icons :selected])]
|
||||
(rx/merge
|
||||
(->> (rx/from-coll selected)
|
||||
(->> (rx/from selected)
|
||||
(rx/map persist-icon))
|
||||
(->> (rx/from-coll selected)
|
||||
(->> (rx/from selected)
|
||||
(rx/map deselect-icon))))))
|
||||
|
||||
(defn move-selected
|
||||
|
@ -391,7 +385,7 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [selected (get-in state [:dashboard :icons :selected])]
|
||||
(->> (rx/from-coll selected)
|
||||
(->> (rx/from selected)
|
||||
(rx/map delete-icon)))))
|
||||
|
||||
(defn delete-selected
|
||||
|
|
|
@ -55,80 +55,50 @@
|
|||
::url
|
||||
::user-id]))
|
||||
|
||||
;; --- Initialize
|
||||
|
||||
(defrecord Initialize []
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:dashboard :images] {:selected #{}})))
|
||||
|
||||
(defn initialize
|
||||
[]
|
||||
(Initialize.))
|
||||
|
||||
;; --- Color Collections Fetched
|
||||
|
||||
(defrecord CollectionsFetched [items]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(reduce (fn [state {:keys [id user] :as item}]
|
||||
(let [type (if (uuid/zero? (:user-id item)) :builtin :own)
|
||||
item (assoc item :type type)]
|
||||
(assoc-in state [:images-collections id] item)))
|
||||
state
|
||||
items)))
|
||||
;; --- Collections Fetched
|
||||
|
||||
(defn collections-fetched
|
||||
[items]
|
||||
{:pre [(us/valid? (s/every ::collection-entity) items)]}
|
||||
(CollectionsFetched. items))
|
||||
(s/assert (s/every ::collection-entity) items)
|
||||
(ptk/reify ::collections-fetched
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(reduce (fn [state {:keys [id user] :as item}]
|
||||
(let [type (if (uuid/zero? (:user-id item)) :builtin :own)
|
||||
item (assoc item :type type)]
|
||||
(assoc-in state [:images-collections id] item)))
|
||||
state
|
||||
items))))
|
||||
|
||||
;; --- Fetch Color Collections
|
||||
|
||||
(defrecord FetchCollections []
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
(->> (rp/query! :images-collections)
|
||||
(rx/map collections-fetched))))
|
||||
|
||||
(defn fetch-collections
|
||||
[]
|
||||
(FetchCollections.))
|
||||
(def fetch-collections
|
||||
(ptk/reify ::fetch-collections
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
(->> (rp/query! :images-collections)
|
||||
(rx/map collections-fetched)))))
|
||||
|
||||
;; --- Collection Created
|
||||
|
||||
(defrecord CollectionCreated [item]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [{:keys [id] :as item} (assoc item :type :own)]
|
||||
(update state :images-collections assoc id item)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(rx/of (rt/nav :dashboard/images nil {:type :own :id (:id item)}))))
|
||||
|
||||
(defn collection-created
|
||||
[item]
|
||||
{:pre [(us/valid? ::collection-entity item)]}
|
||||
(CollectionCreated. item))
|
||||
(s/assert ::collection-entity item)
|
||||
(ptk/reify ::collection-created
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [{:keys [id] :as item} (assoc item :type :own)]
|
||||
(update state :images-collections assoc id item)))))
|
||||
|
||||
;; --- Create Collection
|
||||
|
||||
(defrecord CreateCollection []
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
(let [data {:name (tr "ds.default-library-title" (gensym "c"))}]
|
||||
(->> (rp/mutation! :create-image-collection data)
|
||||
(rx/map :payload)
|
||||
(rx/map collection-created)))))
|
||||
|
||||
(defn create-collection
|
||||
[]
|
||||
(CreateCollection.))
|
||||
|
||||
(defn collections-fetched?
|
||||
[v]
|
||||
(instance? CollectionsFetched v))
|
||||
(def create-collection
|
||||
(ptk/reify ::create-collection
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
(let [data {:name (tr "ds.default-library-title" (gensym "c"))}]
|
||||
(->> (rp/mutation! :create-image-collection data)
|
||||
(rx/map collection-created))))))
|
||||
|
||||
;; --- Collection Updated
|
||||
|
||||
|
@ -189,61 +159,55 @@
|
|||
|
||||
;; --- Image Created
|
||||
|
||||
(defrecord ImageCreated [item]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :images assoc (:id item) item)))
|
||||
|
||||
(defn image-created
|
||||
[item]
|
||||
{:pre [(us/valid? ::image-entity item)]}
|
||||
(ImageCreated. item))
|
||||
(s/assert ::image-entity item)
|
||||
(ptk/reify ::image-created
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :images assoc (:id item) item))))
|
||||
|
||||
;; --- Create Image
|
||||
|
||||
(def allowed-file-types #{"image/jpeg" "image/png"})
|
||||
|
||||
(defrecord CreateImages [id files on-uploaded]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:dashboard :images :uploading] true))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(letfn [(image-size [file]
|
||||
(->> (files/get-image-size file)
|
||||
(rx/map (partial vector file))))
|
||||
(allowed-file? [file]
|
||||
(contains? allowed-file-types (.-type file)))
|
||||
(finalize-upload [state]
|
||||
(assoc-in state [:dashboard :images :uploading] false))
|
||||
(prepare [[file [width height]]]
|
||||
(cond-> {:name (.-name file)
|
||||
:mimetype (.-type file)
|
||||
:id (uuid/random)
|
||||
:file file
|
||||
:width width
|
||||
:height height}
|
||||
id (assoc :collection-id id)))]
|
||||
(->> (rx/from-coll files)
|
||||
(rx/filter allowed-file?)
|
||||
(rx/mapcat image-size)
|
||||
(rx/map prepare)
|
||||
(rx/mapcat #(rp/mutation! :create-image %))
|
||||
(rx/map :payload)
|
||||
(rx/reduce conj [])
|
||||
(rx/do #(st/emit! finalize-upload))
|
||||
(rx/do on-uploaded)
|
||||
(rx/mapcat identity)
|
||||
(rx/map image-created)))))
|
||||
|
||||
(defn create-images
|
||||
([id files]
|
||||
{:pre [(or (uuid? id) (nil? id))]}
|
||||
(CreateImages. id files (constantly nil)))
|
||||
([id files] (create-images id files identity))
|
||||
([id files on-uploaded]
|
||||
{:pre [(or (uuid? id) (nil? id)) (fn? on-uploaded)]}
|
||||
(CreateImages. id files on-uploaded)))
|
||||
(s/assert (s/nilable ::us/uuid) id)
|
||||
(s/assert fn? on-uploaded)
|
||||
(ptk/reify ::create-images
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:dashboard :images :uploading] true))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(letfn [(image-size [file]
|
||||
(->> (files/get-image-size file)
|
||||
(rx/map (partial vector file))))
|
||||
(allowed-file? [file]
|
||||
(contains? allowed-file-types (.-type file)))
|
||||
(finalize-upload [state]
|
||||
(assoc-in state [:dashboard :images :uploading] false))
|
||||
(prepare [[file [width height]]]
|
||||
(cond-> {:name (.-name file)
|
||||
:mimetype (.-type file)
|
||||
:id (uuid/random)
|
||||
:file file
|
||||
:width width
|
||||
:height height}
|
||||
id (assoc :collection-id id)))]
|
||||
(->> (rx/from files)
|
||||
(rx/filter allowed-file?)
|
||||
(rx/mapcat image-size)
|
||||
(rx/map prepare)
|
||||
(rx/mapcat #(rp/mutation! :create-image %))
|
||||
(rx/reduce conj [])
|
||||
(rx/do #(st/emit! finalize-upload))
|
||||
(rx/do on-uploaded)
|
||||
(rx/mapcat identity)
|
||||
(rx/map image-created)))))))
|
||||
|
||||
;; --- Update Image
|
||||
|
||||
|
@ -259,32 +223,29 @@
|
|||
|
||||
;; --- Images Fetched
|
||||
|
||||
(defrecord ImagesFetched [items]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(reduce (fn [state {:keys [id] :as image}]
|
||||
(assoc-in state [:images id] image))
|
||||
state
|
||||
items)))
|
||||
|
||||
(defn images-fetched
|
||||
[items]
|
||||
(ImagesFetched. items))
|
||||
(s/assert (s/every ::image-entity) items)
|
||||
(ptk/reify ::images-fetched
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(reduce (fn [state {:keys [id] :as image}]
|
||||
(assoc-in state [:images id] image))
|
||||
state
|
||||
items))))
|
||||
|
||||
;; --- Fetch Images
|
||||
|
||||
(defrecord FetchImages [id]
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
(let [params (cond-> {} id (assoc :collection-id id))]
|
||||
(->> (rp/query! :images-by-collection params)
|
||||
(rx/map images-fetched)))))
|
||||
|
||||
(defn fetch-images
|
||||
"Fetch a list of images of the selected collection"
|
||||
[id]
|
||||
{:pre [(or (uuid? id) (nil? id))]}
|
||||
(FetchImages. id))
|
||||
(s/assert (s/nilable ::us/uuid) id)
|
||||
(ptk/reify ::fetch-images
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
(let [params (cond-> {} id (assoc :collection-id id))]
|
||||
(->> (rp/query! :images-by-collection params)
|
||||
(rx/map images-fetched))))))
|
||||
|
||||
;; --- Fetch Image
|
||||
|
||||
|
@ -392,10 +353,10 @@
|
|||
(watch [_ state stream]
|
||||
(let [selected (get-in state [:dashboard :images :selected])]
|
||||
(rx/merge
|
||||
(->> (rx/from-coll selected)
|
||||
(->> (rx/from selected)
|
||||
(rx/flat-map #(rp/mutation! :copy-image {:id % :collection-id id}))
|
||||
(rx/map image-created))
|
||||
(->> (rx/from-coll selected)
|
||||
(->> (rx/from selected)
|
||||
(rx/map deselect-image))))))
|
||||
|
||||
(defn copy-selected
|
||||
|
@ -418,9 +379,9 @@
|
|||
(watch [_ state stream]
|
||||
(let [selected (get-in state [:dashboard :images :selected])]
|
||||
(rx/merge
|
||||
(->> (rx/from-coll selected)
|
||||
(->> (rx/from selected)
|
||||
(rx/map persist-image))
|
||||
(->> (rx/from-coll selected)
|
||||
(->> (rx/from selected)
|
||||
(rx/map deselect-image))))))
|
||||
|
||||
(defn move-selected
|
||||
|
@ -434,7 +395,7 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [selected (get-in state [:dashboard :images :selected])]
|
||||
(->> (rx/from-coll selected)
|
||||
(->> (rx/from selected)
|
||||
(rx/map delete-image)))))
|
||||
|
||||
(defn delete-selected
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
[cljs.spec.alpha :as s]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]
|
||||
[uxbox.common.pages :as cp]
|
||||
[uxbox.main.repo.core :as rp]
|
||||
[uxbox.util.data :refer [index-by-id concatv]]
|
||||
[uxbox.util.router :as rt]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.util.time :as dt]
|
||||
|
@ -25,12 +25,15 @@
|
|||
(s/def ::user ::us/uuid)
|
||||
(s/def ::type ::us/keyword)
|
||||
(s/def ::file-id ::us/uuid)
|
||||
(s/def ::project-id ::us/uuid)
|
||||
(s/def ::created-at ::us/inst)
|
||||
(s/def ::modified-at ::us/inst)
|
||||
(s/def ::version ::us/number)
|
||||
(s/def ::ordering ::us/number)
|
||||
(s/def ::metadata (s/nilable ::cp/metadata))
|
||||
(s/def ::data ::cp/data)
|
||||
|
||||
(s/def ::project-entity
|
||||
(s/def ::project
|
||||
(s/keys ::req-un [::id
|
||||
::name
|
||||
::version
|
||||
|
@ -38,38 +41,18 @@
|
|||
::created-at
|
||||
::modified-at]))
|
||||
|
||||
(s/def ::grid-x-axis ::us/number)
|
||||
(s/def ::grid-y-axis ::us/number)
|
||||
(s/def ::grid-color ::us/string)
|
||||
(s/def ::background ::us/string)
|
||||
(s/def ::background-opacity ::us/number)
|
||||
|
||||
(s/def ::metadata
|
||||
(s/keys :opt-un [::grid-y-axis
|
||||
::grid-x-axis
|
||||
::grid-color
|
||||
::background
|
||||
::background-opacity]))
|
||||
|
||||
;; TODO: start using uxbox.common.pagedata/data spec ...
|
||||
|
||||
(s/def ::minimal-shape
|
||||
(s/keys :req-un [::type ::name]
|
||||
:opt-un [::id]))
|
||||
|
||||
(s/def ::shapes (s/coll-of ::us/uuid :kind vector?))
|
||||
(s/def ::canvas (s/coll-of ::us/uuid :kind vector?))
|
||||
|
||||
(s/def ::shapes-by-id
|
||||
(s/map-of ::us/uuid ::minimal-shape))
|
||||
|
||||
(s/def ::data
|
||||
(s/keys :req-un [::shapes ::canvas ::shapes-by-id]))
|
||||
(s/def ::file
|
||||
(s/keys :req-un [::id
|
||||
::name
|
||||
::created-at
|
||||
::modified-at
|
||||
::project-id]))
|
||||
|
||||
(s/def ::page
|
||||
(s/keys :req-un [::id
|
||||
::name
|
||||
::file-id
|
||||
::version
|
||||
::created-at
|
||||
::modified-at
|
||||
::user-id
|
||||
|
@ -77,23 +60,8 @@
|
|||
::metadata
|
||||
::data]))
|
||||
|
||||
(s/def ::pages
|
||||
(s/every ::page :kind vector?))
|
||||
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
(defn assoc-project
|
||||
"A reduce function for assoc the project to the state map."
|
||||
[state {:keys [id] :as project}]
|
||||
(s/assert ::project-entity project)
|
||||
(update-in state [:projects id] merge project))
|
||||
|
||||
(defn dissoc-project
|
||||
"A reduce function for dissoc the project from the state map."
|
||||
[state id]
|
||||
(update state :projects dissoc id))
|
||||
|
||||
(defn unpack-page
|
||||
[state {:keys [id data metadata] :as page}]
|
||||
(-> state
|
||||
|
@ -114,13 +82,10 @@
|
|||
;; --- Initialize Dashboard
|
||||
|
||||
(declare fetch-projects)
|
||||
(declare projects-fetched?)
|
||||
|
||||
(declare fetch-files)
|
||||
(declare initialized)
|
||||
|
||||
;; TODO: rename to initialize dashboard
|
||||
|
||||
(defn initialize
|
||||
[id]
|
||||
(ptk/reify ::initialize
|
||||
|
@ -156,21 +121,8 @@
|
|||
(when order {:order order})
|
||||
(when filter {:filter filter})))))
|
||||
|
||||
;; --- Projects Fetched
|
||||
|
||||
(defn projects-fetched
|
||||
[projects]
|
||||
(s/assert (s/every ::project-entity) projects)
|
||||
(ptk/reify ::projects-fetched
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(reduce assoc-project state projects))))
|
||||
|
||||
(defn projects-fetched?
|
||||
[v]
|
||||
(= ::projects-fetched (ptk/type v)))
|
||||
|
||||
;; --- Fetch Projects
|
||||
(declare projects-fetched)
|
||||
|
||||
(def fetch-projects
|
||||
(ptk/reify ::fetch-projects
|
||||
|
@ -179,6 +131,17 @@
|
|||
(->> (rp/query :projects)
|
||||
(rx/map projects-fetched)))))
|
||||
|
||||
;; --- Projects Fetched
|
||||
|
||||
(defn projects-fetched
|
||||
[projects]
|
||||
(s/assert (s/every ::project) projects)
|
||||
(ptk/reify ::projects-fetched
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [assoc-project #(update-in %1 [:projects (:id %2)] merge %2)]
|
||||
(reduce assoc-project state projects)))))
|
||||
|
||||
;; --- Fetch Files
|
||||
|
||||
(declare files-fetched)
|
||||
|
@ -205,11 +168,9 @@
|
|||
|
||||
;; --- Files Fetched
|
||||
|
||||
(s/def ::files any?)
|
||||
|
||||
(defn files-fetched
|
||||
[files]
|
||||
(s/assert ::files files)
|
||||
(s/assert (s/every ::file) files)
|
||||
(ptk/reify ::files-fetched
|
||||
cljs.core/IDeref
|
||||
(-deref [_] files)
|
||||
|
@ -219,6 +180,34 @@
|
|||
(let [assoc-file #(assoc-in %1 [:files (:id %2)] %2)]
|
||||
(reduce assoc-file state files)))))
|
||||
|
||||
;; --- Create Project
|
||||
|
||||
(declare project-created)
|
||||
|
||||
(def create-project
|
||||
(ptk/reify ::create-project
|
||||
ptk/WatchEvent
|
||||
(watch [this state stream]
|
||||
(let [name (str "Project Name " (gensym "p"))]
|
||||
(->> (rp/mutation! :create-project {:name name})
|
||||
(rx/map (fn [data]
|
||||
(projects-fetched [data]))))))))
|
||||
|
||||
;; --- Create File
|
||||
|
||||
(defn create-file
|
||||
[{:keys [project-id] :as params}]
|
||||
(ptk/reify ::create-file
|
||||
ptk/WatchEvent
|
||||
(watch [this state stream]
|
||||
(let [name (str "File Name " (gensym "p"))
|
||||
params {:name name :project-id project-id}]
|
||||
(->> (rp/mutation! :create-project-file params)
|
||||
(rx/mapcat
|
||||
(fn [data]
|
||||
(rx/of (files-fetched [data])
|
||||
#(update-in % [:dashboard-projects :files project-id] conj (:id data))))))))))
|
||||
|
||||
;; --- Rename Project
|
||||
|
||||
(defn rename-project
|
||||
|
@ -243,7 +232,7 @@
|
|||
(ptk/reify ::delete-project
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(dissoc-project state id))
|
||||
(update state :projects dissoc id))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
|
@ -265,32 +254,6 @@
|
|||
(->> (rp/mutation :delete-project-file {:id id})
|
||||
(rx/ignore)))))
|
||||
|
||||
;; --- Create Project
|
||||
|
||||
(declare project-created)
|
||||
|
||||
(s/def ::create-project
|
||||
(s/keys :req-un [::name]))
|
||||
|
||||
(defn create-project
|
||||
[{:keys [name] :as params}]
|
||||
(s/assert ::create-project params)
|
||||
(ptk/reify ::create-project
|
||||
ptk/WatchEvent
|
||||
(watch [this state stream]
|
||||
(->> (rp/mutation :create-project {:name name})
|
||||
(rx/map project-created)))))
|
||||
|
||||
;; --- Project Created
|
||||
|
||||
(defn project-created
|
||||
[data]
|
||||
(ptk/reify ::project-created
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-project state data))))
|
||||
|
||||
|
||||
;; --- Rename Project
|
||||
|
||||
(defn rename-file
|
||||
|
@ -348,7 +311,7 @@
|
|||
|
||||
(defn pages-fetched
|
||||
[pages]
|
||||
(s/assert ::pages pages)
|
||||
(s/assert (s/every ::page) pages)
|
||||
(ptk/reify ::pages-fetched
|
||||
IDeref
|
||||
(-deref [_] pages)
|
||||
|
|
|
@ -50,7 +50,6 @@
|
|||
(s/def ::font-size number?)
|
||||
(s/def ::font-style string?)
|
||||
(s/def ::font-weight string?)
|
||||
(s/def ::height number?)
|
||||
(s/def ::hidden boolean?)
|
||||
(s/def ::id uuid?)
|
||||
(s/def ::letter-spacing number?)
|
||||
|
@ -67,12 +66,13 @@
|
|||
(s/def ::stroke-style #{:none :solid :dotted :dashed :mixed})
|
||||
(s/def ::stroke-width number?)
|
||||
(s/def ::text-align #{"left" "right" "center" "justify"})
|
||||
(s/def ::type #{:rect :path :circle :image :text})
|
||||
(s/def ::type #{:rect :path :circle :image :text :canvas})
|
||||
(s/def ::x number?)
|
||||
(s/def ::y number?)
|
||||
(s/def ::cx number?)
|
||||
(s/def ::cy number?)
|
||||
(s/def ::width number?)
|
||||
(s/def ::x1 number?)
|
||||
(s/def ::x2 number?)
|
||||
(s/def ::y1 number?)
|
||||
(s/def ::y2 number?)
|
||||
(s/def ::height number?)
|
||||
|
||||
(s/def ::attributes
|
||||
(s/keys :opt-un [::blocked
|
||||
|
@ -91,13 +91,14 @@
|
|||
::proportion
|
||||
::proportion-lock
|
||||
::rx ::ry
|
||||
::cx ::cy
|
||||
::x ::y
|
||||
::stroke-color
|
||||
::stroke-opacity
|
||||
::stroke-style
|
||||
::stroke-width
|
||||
::text-align
|
||||
::x1 ::x2
|
||||
::y1 ::y2]))
|
||||
::width ::height]))
|
||||
|
||||
(s/def ::minimal-shape
|
||||
(s/keys :req-un [::id ::page ::type ::name]))
|
||||
|
@ -169,28 +170,6 @@
|
|||
(ws/-close (get-in state [:ws file-id]))
|
||||
(rx/of ::finalize))))
|
||||
|
||||
;; --- Fetch Workspace Users
|
||||
|
||||
(declare users-fetched)
|
||||
|
||||
(defn fetch-users
|
||||
[file-id]
|
||||
(ptk/reify ::fetch-users
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(->> (rp/query :project-file-users {:file-id file-id})
|
||||
(rx/map users-fetched)))))
|
||||
|
||||
(defn users-fetched
|
||||
[users]
|
||||
(ptk/reify ::users-fetched
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(reduce (fn [state user]
|
||||
(update-in state [:workspace-users :by-id (:id user)] merge user))
|
||||
state
|
||||
users))))
|
||||
|
||||
;; --- Handle: Who
|
||||
|
||||
;; TODO: assign color
|
||||
|
@ -256,66 +235,119 @@
|
|||
|
||||
(defn initialize
|
||||
"Initialize the workspace state."
|
||||
[file-id page-id]
|
||||
[file-id]
|
||||
(s/assert ::us/uuid file-id)
|
||||
(s/assert ::us/uuid page-id)
|
||||
(ptk/reify ::initialize
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [local (assoc workspace-default
|
||||
:file-id file-id
|
||||
:page-id page-id)]
|
||||
:file-id file-id)
|
||||
;; :page-id page-id)
|
||||
;; TODO: this need to be parametrized
|
||||
uri (str "ws://localhost:6060/sub/" file-id)]
|
||||
(-> state
|
||||
(assoc :workspace-layout default-layout)
|
||||
(assoc :workspace-local local))))
|
||||
(assoc :workspace-local local)
|
||||
(assoc-in [:ws file-id] (ws/open uri)))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
#_(when-not (get-in state [:pages page-id])
|
||||
(reset! st/loader true))
|
||||
(let [wsession (get-in state [:ws file-id])]
|
||||
(rx/merge
|
||||
;; Stop possible previous watchers and re-fetch the main page
|
||||
;; and all project related pages.
|
||||
(rx/of ::stop-watcher
|
||||
(dp/fetch-file file-id)
|
||||
(dp/fetch-pages file-id)
|
||||
(fetch-users file-id))
|
||||
|
||||
(rx/merge
|
||||
;; Stop possible previous watchers and re-fetch the main page
|
||||
;; and all project related pages.
|
||||
(rx/of ::stop-watcher
|
||||
(dp/fetch-file file-id)
|
||||
(dp/fetch-pages file-id))
|
||||
;; When main page is fetched, schedule the main initialization.
|
||||
(->> (rx/zip (rx/filter (ptk/type? ::dp/pages-fetched) stream)
|
||||
(rx/filter (ptk/type? ::dp/files-fetched) stream))
|
||||
(rx/take 1)
|
||||
(rx/do #(reset! st/loader false))
|
||||
(rx/mapcat #(rx/of (initialized file-id)
|
||||
#_(initialize-alignment page-id))))
|
||||
|
||||
;; When main page is fetched, schedule the main initialization.
|
||||
(->> (rx/zip (rx/filter (ptk/type? ::dp/pages-fetched) stream)
|
||||
(rx/filter (ptk/type? ::dp/files-fetched) stream))
|
||||
(rx/take 1)
|
||||
(rx/do #(reset! st/loader false))
|
||||
(rx/mapcat #(rx/of (initialized file-id page-id)
|
||||
#_(initialize-alignment page-id))))
|
||||
|
||||
(->> stream
|
||||
(rx/filter uxbox.main.ui.workspace.streams/pointer-event?)
|
||||
(rx/sample 150)
|
||||
(rx/tap (fn [{:keys [pt] :as event}]
|
||||
(let [msg {:type :pointer-update
|
||||
:page-id page-id
|
||||
:x (:x pt)
|
||||
:y (:y pt)}]
|
||||
(ws/-send (get-in state [:ws file-id]) (t/encode msg)))))
|
||||
(rx/ignore)
|
||||
(rx/take-until (rx/filter #(= ::stop-watcher %) stream)))))))
|
||||
;; WebSocket Incoming Messages Handling
|
||||
(->> (ws/-stream wsession)
|
||||
(rx/filter #(= :message (:type %)))
|
||||
(rx/map (comp t/decode :payload))
|
||||
(rx/filter #(s/valid? ::message %))
|
||||
(rx/map (fn [{:keys [type] :as msg}]
|
||||
(case type
|
||||
:who (handle-who msg)
|
||||
:pointer-update (handle-pointer-update msg)
|
||||
:page-snapshot (handle-page-snapshot msg)
|
||||
::unknown))))
|
||||
|
||||
#_(->> stream
|
||||
;; TODO: this need to be rethinked
|
||||
(rx/filter uxbox.main.ui.workspace.streams/pointer-event?)
|
||||
(rx/sample 150)
|
||||
(rx/tap (fn [{:keys [pt] :as event}]
|
||||
(let [msg {:type :pointer-update
|
||||
:page-id page-id
|
||||
:x (:x pt)
|
||||
:y (:y pt)}]
|
||||
(ws/-send (get-in state [:ws file-id]) (t/encode msg)))))
|
||||
(rx/ignore)
|
||||
(rx/take-until (rx/filter #(= ::finalize %) stream))))))))
|
||||
|
||||
(defn- initialized
|
||||
[file-id page-id]
|
||||
[file-id]
|
||||
(s/assert ::us/uuid file-id)
|
||||
(s/assert ::us/uuid page-id)
|
||||
(ptk/reify ::initialized
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [file (get-in state [:files file-id])
|
||||
page (get-in state [:pages page-id])
|
||||
(let [file (get-in state [:files file-id])]
|
||||
(assoc state :workspace-file file)))))
|
||||
|
||||
(defn finalize
|
||||
[file-id]
|
||||
(ptk/reify ::finalize
|
||||
cljs.core/IDeref
|
||||
(-deref [_] file-id)
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ state stream]
|
||||
(ws/-close (get-in state [:ws file-id])))))
|
||||
|
||||
(defn initialize-page
|
||||
[page-id]
|
||||
(ptk/reify ::initialize-page
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [page (get-in state [:pages page-id])
|
||||
data (get-in state [:pages-data page-id])]
|
||||
(assoc state
|
||||
:workspace-file file
|
||||
:workspace-data data
|
||||
:workspace-page page)))))
|
||||
:workspace-page page)))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ state stream])))
|
||||
|
||||
;; --- Fetch Workspace Users
|
||||
|
||||
(declare users-fetched)
|
||||
|
||||
(defn fetch-users
|
||||
[file-id]
|
||||
(ptk/reify ::fetch-users
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(->> (rp/query :project-file-users {:file-id file-id})
|
||||
(rx/map users-fetched)))))
|
||||
|
||||
(defn users-fetched
|
||||
[users]
|
||||
(ptk/reify ::users-fetched
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(reduce (fn [state user]
|
||||
(update-in state [:workspace-users :by-id (:id user)] merge user))
|
||||
state
|
||||
users))))
|
||||
|
||||
;; --- Toggle layout flag
|
||||
|
||||
|
@ -576,14 +608,22 @@
|
|||
(declare select-shape)
|
||||
(declare recalculate-shape-canvas-relation)
|
||||
|
||||
(def shape-default-attrs
|
||||
{:stroke-color "#000000"
|
||||
:stroke-opacity 1
|
||||
:fill-color "#000000"
|
||||
:fill-opacity 1})
|
||||
|
||||
(defn add-shape
|
||||
[data]
|
||||
(s/assert ::attributes data)
|
||||
(let [id (uuid/random)]
|
||||
(ptk/reify ::add-shape
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [shape (-> (geom/setup-proportions data)
|
||||
(assoc :id id))
|
||||
shape (merge shape-default-attrs shape)
|
||||
shape (recalculate-shape-canvas-relation state shape)]
|
||||
(impl-assoc-shape state shape)))
|
||||
|
||||
|
@ -593,6 +633,32 @@
|
|||
(rx/of (commit-shapes-changes [[:add-shape id shape]])
|
||||
(select-shape id)))))))
|
||||
|
||||
(def canvas-default-attrs
|
||||
{:stroke-color "#000000"
|
||||
:stroke-opacity 1
|
||||
:fill-color "#ffffff"
|
||||
:fill-opacity 1})
|
||||
|
||||
(defn add-canvas
|
||||
[data]
|
||||
(s/assert ::attributes data)
|
||||
(let [id (uuid/random)]
|
||||
(ptk/reify ::add-canvas
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [shape (-> (geom/setup-proportions data)
|
||||
(assoc :id id))
|
||||
shape (merge canvas-default-attrs shape)
|
||||
shape (recalculate-shape-canvas-relation state shape)]
|
||||
(impl-assoc-shape state shape)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [shape (get-in state [:workspace-data :shapes-by-id id])]
|
||||
(rx/of (commit-shapes-changes [[:add-canvas id shape]])
|
||||
(select-shape id)))))))
|
||||
|
||||
|
||||
;; --- Duplicate Selected
|
||||
|
||||
(defn impl-duplicate-shape
|
||||
|
@ -608,7 +674,7 @@
|
|||
duplicate (partial impl-duplicate-shape state)
|
||||
shapes (map duplicate selected)]
|
||||
(rx/merge
|
||||
(rx/from-coll (map (fn [s] #(impl-assoc-shape % s)) shapes))
|
||||
(rx/from (map (fn [s] #(impl-assoc-shape % s)) shapes))
|
||||
(rx/of (commit-shapes-changes (mapv #(vector :add-shape (:id %) %) shapes))))))))
|
||||
|
||||
;; --- Toggle shape's selection status (selected or deselected)
|
||||
|
@ -675,41 +741,35 @@
|
|||
|
||||
;; --- Update Shape Attrs
|
||||
|
||||
(defn update-shape-attrs
|
||||
[id attrs]
|
||||
(s/assert ::us/uuid id)
|
||||
(let [atts (s/conform ::attributes attrs)]
|
||||
(ptk/reify ::update-shape-attrs
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(if (map? attrs)
|
||||
(update-in state [:workspace-data :shapes-by-id id] merge attrs)
|
||||
state)))))
|
||||
|
||||
(defn update-shape
|
||||
[id & attrs]
|
||||
(let [attrs' (->> (apply hash-map attrs)
|
||||
(s/conform ::attributes))]
|
||||
(ptk/reify ::update-shape
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(cond-> state
|
||||
(not= attrs' ::s/invalid)
|
||||
(update-in [:workspace-data :shapes-by-id id] merge attrs'))))))
|
||||
[id attrs]
|
||||
(s/assert ::attributes attrs)
|
||||
(ptk/reify ::update-shape
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [shape-old (get-in state [:workspace-data :shapes-by-id id])
|
||||
shape-new (merge shape-old attrs)
|
||||
diff (d/diff-maps shape-old shape-new)]
|
||||
(-> state
|
||||
(assoc-in [:workspace-data :shapes-by-id id] shape-new)
|
||||
(assoc ::tmp-change (into [:mod-shape id] diff)))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [change (::tmp-change state)]
|
||||
(prn "update-shape" change)
|
||||
(rx/of (commit-shapes-changes [change])
|
||||
#(dissoc state ::tmp-change))))))
|
||||
|
||||
;; --- Update Selected Shapes attrs
|
||||
|
||||
;; TODO: improve performance of this event
|
||||
|
||||
(defn update-selected-shapes-attrs
|
||||
[attrs]
|
||||
(s/assert ::attributes attrs)
|
||||
(defn update-selected-shapes
|
||||
[& attrs]
|
||||
(ptk/reify ::update-selected-shapes-attrs
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [selected (get-in state [:workspace-local :selected])]
|
||||
(rx/from-coll (map #(update-shape-attrs % attrs) selected))))))
|
||||
(rx/from (map #(apply update-shape % attrs) selected))))))
|
||||
|
||||
;; --- Move Selected
|
||||
|
||||
|
@ -763,19 +823,6 @@
|
|||
(rx/of (apply-temporal-displacement-in-bulk selected displacement))
|
||||
(rx/of (materialize-temporal-modifier-in-bulk selected)))))))
|
||||
|
||||
;; --- Update Shape Position
|
||||
|
||||
(deftype UpdateShapePosition [id point]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:shapes id] geom/absolute-move point)))
|
||||
|
||||
(defn update-position
|
||||
"Update the start position coordenate of the shape."
|
||||
[id point]
|
||||
{:pre [(uuid? id) (gpt/point? point)]}
|
||||
(UpdateShapePosition. id point))
|
||||
|
||||
;; --- Delete Selected
|
||||
|
||||
(defn impl-dissoc-shape
|
||||
|
@ -923,9 +970,10 @@
|
|||
|
||||
(defn- recalculate-shape-canvas-relation
|
||||
[state shape]
|
||||
(let [xfmt (comp (map #(get-in state [:workspace-data :shapes-by-id %]))
|
||||
(let [shape' (geom/shape->rect-shape shape)
|
||||
xfmt (comp (map #(get-in state [:workspace-data :shapes-by-id %]))
|
||||
(map geom/shape->rect-shape)
|
||||
(filter #(geom/overlaps? % shape))
|
||||
(filter #(geom/overlaps? % shape'))
|
||||
(map :id))
|
||||
|
||||
id (->> (get-in state [:workspace-data :canvas])
|
||||
|
@ -1035,59 +1083,16 @@
|
|||
(rx/map (constantly clear-drawing))
|
||||
(rx/take-until stoper)))))))
|
||||
|
||||
;; --- Shape Proportions
|
||||
|
||||
;; (defn toggle-shape-proportion-lock
|
||||
;; [id]
|
||||
;; (ptk/reify ::toggle-shape-proportion-lock
|
||||
;; ptk/UpdateEvent
|
||||
;; (update [_ state]
|
||||
;; (let [shape (-> (get-in state [:workspace-data :shapes-by-id id])
|
||||
;; (geom/size)
|
||||
|
||||
|
||||
;; TODO: revisit
|
||||
|
||||
(deftype LockShapeProportions [id]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [[width height] (-> (get-in state [:shapes id])
|
||||
(geom/size)
|
||||
(keep [:width :height]))
|
||||
proportion (/ width height)]
|
||||
(update-in state [:shapes id] assoc
|
||||
:proportion proportion
|
||||
:proportion-lock true))))
|
||||
|
||||
(defn lock-proportions
|
||||
"Mark proportions of the shape locked and save the current
|
||||
proportion as additional precalculated property."
|
||||
[id]
|
||||
{:pre [(uuid? id)]}
|
||||
(LockShapeProportions. id))
|
||||
|
||||
;; TODO: revisit
|
||||
|
||||
(deftype UnlockShapeProportions [id]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:shapes id :proportion-lock] false)))
|
||||
|
||||
(defn unlock-proportions
|
||||
[id]
|
||||
{:pre [(uuid? id)]}
|
||||
(UnlockShapeProportions. id))
|
||||
|
||||
;; --- Update Dimensions
|
||||
|
||||
;; TODO: revisit
|
||||
|
||||
(s/def ::width (s/and ::us/number ::us/positive))
|
||||
(s/def ::height (s/and ::us/number ::us/positive))
|
||||
(s/def ::width ::us/number)
|
||||
(s/def ::height ::us/number)
|
||||
|
||||
(s/def ::update-dimensions
|
||||
(s/keys :opt-un [::width ::height]))
|
||||
|
||||
;; TODO: emit commit-changes
|
||||
|
||||
(defn update-dimensions
|
||||
"A helper event just for update the position
|
||||
of the shape using the width and height attrs
|
||||
|
@ -1098,35 +1103,31 @@
|
|||
(ptk/reify ::update-dimensions
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:shapes id] geom/resize-dim dimensions))))
|
||||
(update-in state [:workspace-data :shapes-by-id id] geom/resize-dim dimensions))))
|
||||
|
||||
;; --- Update Interaction
|
||||
;; --- Shape Proportions
|
||||
|
||||
;; TODO: revisit
|
||||
(deftype UpdateInteraction [shape interaction]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (or (:id interaction)
|
||||
(uuid/random))
|
||||
data (assoc interaction :id id)]
|
||||
(assoc-in state [:shapes shape :interactions id] data))))
|
||||
(defn toggle-shape-proportion-lock
|
||||
[id]
|
||||
(ptk/reify ::toggle-shape-proportion-lock
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [shape (get-in state [:workspace-data :shapes-by-id id])]
|
||||
(if (:proportion-lock shape)
|
||||
(assoc-in state [:workspace-data :shapes-by-id id :proportion-lock] false)
|
||||
(->> (geom/assign-proportions (assoc shape :proportion-lock true))
|
||||
(assoc-in state [:workspace-data :shapes-by-id id])))))))
|
||||
|
||||
(defn update-interaction
|
||||
[shape interaction]
|
||||
(UpdateInteraction. shape interaction))
|
||||
;; --- Update Shape Position
|
||||
|
||||
;; --- Delete Interaction
|
||||
|
||||
;; TODO: revisit
|
||||
(deftype DeleteInteracton [shape id]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:shapes shape :interactions] dissoc id)))
|
||||
|
||||
(defn delete-interaction
|
||||
[shape id]
|
||||
{:pre [(uuid? id) (uuid? shape)]}
|
||||
(DeleteInteracton. shape id))
|
||||
(defn update-position
|
||||
[id point]
|
||||
(s/assert ::us/uuid id)
|
||||
(s/assert gpt/point? point)
|
||||
(ptk/reify ::update-position
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:workspace-data :shapes-by-id id] geom/absolute-move point))))
|
||||
|
||||
;; --- Path Modifications
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
(declare move-rect)
|
||||
(declare move-path)
|
||||
(declare move-circle)
|
||||
(declare move-group)
|
||||
|
||||
(defn move
|
||||
"Move the shape relativelly to its current
|
||||
|
@ -28,18 +27,15 @@
|
|||
:text (move-rect shape dpoint)
|
||||
:curve (move-path shape dpoint)
|
||||
:path (move-path shape dpoint)
|
||||
:circle (move-circle shape dpoint)
|
||||
:group (move-group shape dpoint)))
|
||||
:circle (move-circle shape dpoint)))
|
||||
|
||||
(defn- move-rect
|
||||
"A specialized function for relative movement
|
||||
for rect-like shapes."
|
||||
[shape {dx :x dy :y}]
|
||||
(assoc shape
|
||||
:x1 (mth/round (+ (:x1 shape) dx))
|
||||
:y1 (mth/round (+ (:y1 shape) dy))
|
||||
:x2 (mth/round (+ (:x2 shape) dx))
|
||||
:y2 (mth/round (+ (:y2 shape) dy))))
|
||||
:x (mth/round (+ (:x shape) dx))
|
||||
:y (mth/round (+ (:y shape) dy))))
|
||||
|
||||
(defn- move-circle
|
||||
"A specialized function for relative movement
|
||||
|
@ -49,14 +45,6 @@
|
|||
:cx (mth/round (+ (:cx shape) dx))
|
||||
:cy (mth/round (+ (:cy shape) dy))))
|
||||
|
||||
(defn- move-group
|
||||
"A specialized function for relative movement
|
||||
for group shapes."
|
||||
[shape {dx :x dy :y}]
|
||||
(assoc shape
|
||||
:dx (mth/round (+ (:dx shape 0) dx))
|
||||
:dy (mth/round (+ (:dy shape 0) dy))))
|
||||
|
||||
(defn- move-path
|
||||
"A specialized function for relative movement
|
||||
for path shapes."
|
||||
|
@ -71,7 +59,6 @@
|
|||
|
||||
(declare absolute-move-rect)
|
||||
(declare absolute-move-circle)
|
||||
(declare absolute-move-group)
|
||||
|
||||
(defn absolute-move
|
||||
"Move the shape to the exactly specified position."
|
||||
|
@ -80,31 +67,24 @@
|
|||
:icon (absolute-move-rect shape point)
|
||||
:image (absolute-move-rect shape point)
|
||||
:rect (absolute-move-rect shape point)
|
||||
:circle (absolute-move-circle shape point)
|
||||
:group (absolute-move-group shape point)))
|
||||
:circle (absolute-move-circle shape point)))
|
||||
|
||||
(defn- absolute-move-rect
|
||||
"A specialized function for absolute moviment
|
||||
for rect-like shapes."
|
||||
[shape {:keys [x y] :as pos}]
|
||||
(let [dx (if x (- x (:x1 shape)) 0)
|
||||
dy (if y (- y (:y1 shape)) 0)]
|
||||
(let [dx (if x (- x (:x shape)) 0)
|
||||
dy (if y (- y (:y shape)) 0)]
|
||||
(move shape (gpt/point dx dy))))
|
||||
|
||||
(defn- absolute-move-circle
|
||||
"A specialized function for absolute moviment
|
||||
for rect-like shapes."
|
||||
[shape {:keys [x y] :as pos}]
|
||||
(let [dx (if x (- x(:cx shape)) 0)
|
||||
(let [dx (if x (- x (:cx shape)) 0)
|
||||
dy (if y (- y (:cy shape)) 0)]
|
||||
(move shape (gpt/point dx dy))))
|
||||
|
||||
(defn- absolute-move-group
|
||||
"A specialized function for absolute moviment
|
||||
for rect-like shapes."
|
||||
[shape {:keys [x y] :as pos}]
|
||||
(throw (ex-info "Not implemented (TODO)" {})))
|
||||
|
||||
;; --- Rotation
|
||||
|
||||
;; TODO: maybe we can consider apply the rotation
|
||||
|
@ -159,6 +139,30 @@
|
|||
(merge shape {:width (* rx 2)
|
||||
:height (* ry 2)}))
|
||||
|
||||
;; --- Proportions
|
||||
|
||||
(declare assign-proportions-path)
|
||||
(declare assign-proportions-circle)
|
||||
(declare assign-proportions-rect)
|
||||
|
||||
(defn assign-proportions
|
||||
[{:keys [type] :as shape}]
|
||||
(case type
|
||||
:circle (assign-proportions-circle shape)
|
||||
:path (assign-proportions-path shape)
|
||||
(assign-proportions-rect shape)))
|
||||
|
||||
(defn- assign-proportions-rect
|
||||
[{:keys [width height] :as shape}]
|
||||
(assoc shape :proportion (/ width height)))
|
||||
|
||||
(defn- assign-proportions-circle
|
||||
[{:as shape}]
|
||||
(prn "assign-proportions-circle" shape)
|
||||
(assoc shape :proportion 1))
|
||||
|
||||
;; TODO: implement the rest of shapes
|
||||
|
||||
;; --- Paths
|
||||
|
||||
(defn update-path-point
|
||||
|
@ -171,20 +175,16 @@
|
|||
|
||||
;; --- Setup Proportions
|
||||
|
||||
(declare setup-proportions-rect)
|
||||
(declare setup-proportions-const)
|
||||
(declare setup-proportions-image)
|
||||
|
||||
(defn setup-proportions
|
||||
[shape]
|
||||
(case (:type shape)
|
||||
:canvas (setup-proportions-rect shape)
|
||||
:rect (setup-proportions-rect shape)
|
||||
:circle (setup-proportions-rect shape)
|
||||
:icon (setup-proportions-image shape)
|
||||
:image (setup-proportions-image shape)
|
||||
:text shape
|
||||
:curve (setup-proportions-rect shape)
|
||||
:path (setup-proportions-rect shape)))
|
||||
(setup-proportions-const shape)))
|
||||
|
||||
(defn setup-proportions-image
|
||||
[{:keys [metadata] :as shape}]
|
||||
|
@ -193,12 +193,11 @@
|
|||
:proportion (/ width height)
|
||||
:proportion-lock false)))
|
||||
|
||||
(defn setup-proportions-rect
|
||||
(defn setup-proportions-const
|
||||
[shape]
|
||||
(let [{:keys [width height]} (size shape)]
|
||||
(assoc shape
|
||||
:proportion (/ width height)
|
||||
:proportion-lock false)))
|
||||
(assoc shape
|
||||
:proportion 1
|
||||
:proportion-lock false))
|
||||
|
||||
;; --- Resize (Dimentsions)
|
||||
|
||||
|
@ -216,20 +215,19 @@
|
|||
:circle (resize-dim-circle shape opts)))
|
||||
|
||||
(defn- resize-dim-rect
|
||||
[{:keys [proportion proportion-lock x1 y1] :as shape}
|
||||
{:keys [width height]}]
|
||||
{:pre [(not (and width height))]}
|
||||
[{:keys [proportion proportion-lock x y] :as shape}
|
||||
{:keys [width height] :as dimensions}]
|
||||
(if-not proportion-lock
|
||||
(if width
|
||||
(assoc shape :x2 (+ x1 width))
|
||||
(assoc shape :y2 (+ y1 height)))
|
||||
(assoc shape :width width)
|
||||
(assoc shape :height height))
|
||||
(if width
|
||||
(-> shape
|
||||
(assoc :x2 (+ x1 width))
|
||||
(assoc :y2 (+ y1 (/ width proportion))))
|
||||
(assoc :width width)
|
||||
(assoc :height (/ width proportion)))
|
||||
(-> shape
|
||||
(assoc :y2 (+ y1 height))
|
||||
(assoc :x2 (+ x1 (* height proportion)))))))
|
||||
(assoc :height height)
|
||||
(assoc :width (* height proportion))))))
|
||||
|
||||
(defn- resize-dim-circle
|
||||
[{:keys [proportion proportion-lock] :as shape}
|
||||
|
@ -299,11 +297,11 @@
|
|||
|
||||
:bottom-right
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x1 shape))
|
||||
(+ (:y1 shape)))
|
||||
(gmt/translate (+ (:x shape))
|
||||
(+ (:y shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x1 shape))
|
||||
(- (:y1 shape))))
|
||||
(gmt/translate (- (:x shape))
|
||||
(- (:y shape))))
|
||||
|
||||
:bottom
|
||||
(-> (gmt/matrix)
|
||||
|
@ -373,8 +371,8 @@
|
|||
:height (if lock? (/ width proportion) height)))
|
||||
|
||||
:bottom-right
|
||||
(let [width (- x (:x1 shape))
|
||||
height (- y (:y1 shape))
|
||||
(let [width (- x (:x shape))
|
||||
height (- y (:y shape))
|
||||
proportion (:proportion shape 1)]
|
||||
(assoc shape
|
||||
:width width
|
||||
|
@ -421,38 +419,39 @@
|
|||
|
||||
(defn- setup-rect
|
||||
"A specialized function for setup rect-like shapes."
|
||||
[shape {:keys [x1 y1 x2 y2]}]
|
||||
[shape {:keys [x y width height]}]
|
||||
(assoc shape
|
||||
:x1 x1
|
||||
:y1 y1
|
||||
:x2 x2
|
||||
:y2 y2))
|
||||
:x x
|
||||
:y y
|
||||
:width width
|
||||
:height height))
|
||||
|
||||
(defn- setup-circle
|
||||
"A specialized function for setup circle shapes."
|
||||
[shape {:keys [x1 y1 x2 y2]}]
|
||||
[shape {:keys [x y width height]}]
|
||||
(assoc shape
|
||||
:cx x1
|
||||
:cy y1
|
||||
:rx (mth/abs (- x2 x1))
|
||||
:ry (mth/abs (- y2 y1))))
|
||||
:cx x
|
||||
:cy y
|
||||
:rx (mth/abs width)
|
||||
:ry (mth/abs height)))
|
||||
|
||||
(defn- setup-image
|
||||
[{:keys [metadata] :as shape} {:keys [x1 y1 x2 y2] :as props}]
|
||||
(let [{:keys [width height]} metadata]
|
||||
(assoc shape
|
||||
:x1 x1
|
||||
:y1 y1
|
||||
:x2 x2
|
||||
:y2 y2
|
||||
:proportion (/ width height)
|
||||
:proportion-lock true)))
|
||||
[{:keys [metadata] :as shape} {:keys [x y width height] :as props}]
|
||||
(assoc shape
|
||||
:x x
|
||||
:y y
|
||||
:width width
|
||||
:height height
|
||||
:proportion (/ (:width metadata)
|
||||
(:height metadata))
|
||||
:proportion-lock true))
|
||||
|
||||
;; --- Coerce to Rect-like shape.
|
||||
|
||||
(declare circle->rect-shape)
|
||||
(declare path->rect-shape)
|
||||
(declare group->rect-shape)
|
||||
(declare rect->rect-shape)
|
||||
|
||||
(defn shape->rect-shape
|
||||
"Coerce shape to rect like shape."
|
||||
|
@ -461,7 +460,7 @@
|
|||
:circle (circle->rect-shape shape)
|
||||
:path (path->rect-shape shape)
|
||||
:curve (path->rect-shape shape)
|
||||
shape))
|
||||
(rect->rect-shape shape)))
|
||||
|
||||
(defn shapes->rect-shape
|
||||
[shapes]
|
||||
|
@ -474,29 +473,41 @@
|
|||
:y1 miny
|
||||
:x2 maxx
|
||||
:y2 maxy
|
||||
:x minx
|
||||
:y miny
|
||||
:width (- maxx minx)
|
||||
:height (- maxy miny)
|
||||
:type :rect}))
|
||||
|
||||
(defn shapes->rect-shape'
|
||||
[shapes]
|
||||
(let [shapes (mapv shape->rect-shape shapes)
|
||||
total (count shapes)]
|
||||
(loop [idx (int 0)
|
||||
minx js/Number.POSITIVE_INFINITY
|
||||
miny js/Number.POSITIVE_INFINITY
|
||||
maxx js/Number.NEGATIVE_INFINITY
|
||||
maxy js/Number.NEGATIVE_INFINITY]
|
||||
(if (> total idx)
|
||||
(let [{:keys [x1 y1 x2 y2]} (nth shapes idx)]
|
||||
(recur (inc idx)
|
||||
(min minx x1)
|
||||
(min miny y1)
|
||||
(max maxx x2)
|
||||
(max maxy y2)))
|
||||
{:x1 minx
|
||||
:y1 miny
|
||||
:x2 maxx
|
||||
:y2 maxy
|
||||
:type :rect}))))
|
||||
;; (defn shapes->rect-shape'
|
||||
;; [shapes]
|
||||
;; (let [shapes (mapv shape->rect-shape shapes)
|
||||
;; total (count shapes)]
|
||||
;; (loop [idx (int 0)
|
||||
;; minx js/Number.POSITIVE_INFINITY
|
||||
;; miny js/Number.POSITIVE_INFINITY
|
||||
;; maxx js/Number.NEGATIVE_INFINITY
|
||||
;; maxy js/Number.NEGATIVE_INFINITY]
|
||||
;; (if (> total idx)
|
||||
;; (let [{:keys [x1 y1 x2 y2]} (nth shapes idx)]
|
||||
;; (recur (inc idx)
|
||||
;; (min minx x1)
|
||||
;; (min miny y1)
|
||||
;; (max maxx x2)
|
||||
;; (max maxy y2)))
|
||||
;; {:x1 minx
|
||||
;; :y1 miny
|
||||
;; :x2 maxx
|
||||
;; :y2 maxy
|
||||
;; :type :rect}))))
|
||||
|
||||
(defn- rect->rect-shape
|
||||
[{:keys [x y width height] :as shape}]
|
||||
(assoc shape
|
||||
:x1 x
|
||||
:y1 y
|
||||
:x2 (+ x width)
|
||||
:y2 (+ y height)))
|
||||
|
||||
(defn- path->rect-shape
|
||||
[{:keys [segments] :as shape}]
|
||||
|
@ -508,7 +519,11 @@
|
|||
:x1 minx
|
||||
:y1 miny
|
||||
:x2 maxx
|
||||
:y2 maxy)))
|
||||
:y2 maxy
|
||||
:x minx
|
||||
:y miny
|
||||
:width (- maxx minx)
|
||||
:height (- maxy miny))))
|
||||
|
||||
(defn- circle->rect-shape
|
||||
[{:keys [cx cy rx ry] :as shape}]
|
||||
|
@ -520,7 +535,11 @@
|
|||
:x1 x1
|
||||
:y1 y1
|
||||
:x2 (+ x1 width)
|
||||
:y2 (+ y1 height))))
|
||||
:y2 (+ y1 height)
|
||||
:x x1
|
||||
:y y1
|
||||
:width width
|
||||
:height height)))
|
||||
|
||||
;; --- Transform Shape
|
||||
|
||||
|
@ -542,21 +561,23 @@
|
|||
:circle (transform-circle shape xfmt)))
|
||||
|
||||
(defn- transform-rect
|
||||
[{:keys [x1 y1] :as shape} mx]
|
||||
(let [{:keys [width height]} (size shape)
|
||||
tl (gpt/transform [x1 y1] mx)
|
||||
tr (gpt/transform [(+ x1 width) y1] mx)
|
||||
bl (gpt/transform [x1 (+ y1 height)] mx)
|
||||
br (gpt/transform [(+ x1 width) (+ y1 height)] mx)
|
||||
[{:keys [x y width height] :as shape} mx]
|
||||
(let [tl (gpt/transform [x y] mx)
|
||||
tr (gpt/transform [(+ x width) y] mx)
|
||||
bl (gpt/transform [x (+ y height)] mx)
|
||||
br (gpt/transform [(+ x width) (+ y height)] mx)
|
||||
minx (apply min (map :x [tl tr bl br]))
|
||||
maxx (apply max (map :x [tl tr bl br]))
|
||||
miny (apply min (map :y [tl tr bl br]))
|
||||
maxy (apply max (map :y [tl tr bl br]))]
|
||||
(assoc shape
|
||||
:x1 minx
|
||||
:y1 miny
|
||||
:x2 (+ minx (- maxx minx))
|
||||
:y2 (+ miny (- maxy miny)))))
|
||||
:x minx
|
||||
:y miny
|
||||
:width (- maxx minx)
|
||||
:height (- maxy miny))))
|
||||
|
||||
;; :x2 (+ minx (- maxx minx))
|
||||
;; :y2 (+ miny (- maxy miny)))))
|
||||
|
||||
(defn- transform-circle
|
||||
[{:keys [cx cy rx ry] :as shape} xfmt]
|
||||
|
@ -585,8 +606,6 @@
|
|||
|
||||
;; --- Outer Rect
|
||||
|
||||
(declare selection-rect-generic)
|
||||
|
||||
(defn rotation-matrix
|
||||
"Generate a rotation matrix from shape."
|
||||
[{:keys [x1 y1 rotation] :as shape}]
|
||||
|
@ -607,15 +626,12 @@
|
|||
|
||||
(defn selection-rect
|
||||
"Return the selection rect for the shape."
|
||||
([shape]
|
||||
(selection-rect @st/state shape))
|
||||
([state shape]
|
||||
(let [modifier (:modifier-mtx shape)]
|
||||
(-> (shape->rect-shape shape)
|
||||
(assoc :type :rect :id (:id shape))
|
||||
(transform (or modifier (gmt/matrix)))
|
||||
(rotate-shape)
|
||||
(size)))))
|
||||
[shape]
|
||||
(let [modifier (:modifier-mtx shape)]
|
||||
(-> (shape->rect-shape shape)
|
||||
(assoc :type :rect :id (:id shape))
|
||||
(transform (or modifier (gmt/matrix)))
|
||||
#_(rotate-shape))))
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
|
@ -623,8 +639,8 @@
|
|||
"Check if a shape is contained in the
|
||||
provided selection rect."
|
||||
[shape selrect]
|
||||
(let [{sx1 :x1 sx2 :x2 sy1 :y1 sy2 :y2} selrect
|
||||
{rx1 :x1 rx2 :x2 ry1 :y1 ry2 :y2} shape]
|
||||
(let [{sx1 :x1 sx2 :x2 sy1 :y1 sy2 :y2} (shape->rect-shape selrect)
|
||||
{rx1 :x1 rx2 :x2 ry1 :y1 ry2 :y2} (shape->rect-shape shape)]
|
||||
(and (neg? (- sy1 ry1))
|
||||
(neg? (- sx1 rx1))
|
||||
(pos? (- sy2 ry2))
|
||||
|
@ -633,8 +649,8 @@
|
|||
(defn overlaps?
|
||||
"Check if a shape overlaps with provided selection rect."
|
||||
[shape selrect]
|
||||
(let [{sx1 :x1 sx2 :x2 sy1 :y1 sy2 :y2} selrect
|
||||
{rx1 :x1 rx2 :x2 ry1 :y1 ry2 :y2} shape]
|
||||
(let [{sx1 :x1 sx2 :x2 sy1 :y1 sy2 :y2} (shape->rect-shape selrect)
|
||||
{rx1 :x1 rx2 :x2 ry1 :y1 ry2 :y2} (shape->rect-shape shape)]
|
||||
(and (< rx1 sx2)
|
||||
(> rx2 sx1)
|
||||
(< ry1 sy2)
|
||||
|
|
|
@ -80,33 +80,8 @@
|
|||
"ds.project.placeholder" "New project name"
|
||||
"ds.project.new" "New project"
|
||||
|
||||
"ds.radius" "Radius"
|
||||
"ds.size" "Size"
|
||||
"ds.width" "Width"
|
||||
"ds.height" "Height"
|
||||
"ds.style" "Style"
|
||||
"ds.none" "None"
|
||||
"ds.solid" "Solid"
|
||||
"ds.dotted" "Dotted"
|
||||
"ds.dashed" "Dashed"
|
||||
"ds.mixed" "Mixed"
|
||||
"ds.position" "Position"
|
||||
"ds.rotation" "Rotation"
|
||||
"ds.opacity" "Opacity"
|
||||
"ds.color" "Color"
|
||||
"ds.background-color" "Background color"
|
||||
"ds.font-family" "Font family"
|
||||
"ds.size-weight" "Size and Weight"
|
||||
"ds.font-size" "Font Size"
|
||||
"ds.line-height-letter-spacing" "Line height and Letter spacing"
|
||||
"ds.line-height" "Line height"
|
||||
"ds.letter-spacing" "Letter spacing"
|
||||
"ds.text-align" "Text align"
|
||||
"ds.name" "Name"
|
||||
"ds.go" "Go go go!"
|
||||
|
||||
"ds.accept" "Accept"
|
||||
"ds.cancel" "Cancel"
|
||||
"common.accept" "Accept"
|
||||
"common.cancel" "Cancel"
|
||||
|
||||
"ds.settings.icons" "Icons"
|
||||
"ds.settings.element-options" "Element options"
|
||||
|
@ -122,43 +97,69 @@
|
|||
"ds.history.versions" "History"
|
||||
"ds.history.pinned" "Pinned"
|
||||
|
||||
"ds.help.rect" "Box (Ctrl + B)"
|
||||
"ds.help.circle" "Circle (Ctrl + E)"
|
||||
"ds.help.line" "Line (Ctrl + L)"
|
||||
"ds.help.text" "Text"
|
||||
"ds.help.path" "Path"
|
||||
"ds.help.curve" "Curve"
|
||||
"ds.help.ruler" "Ruler"
|
||||
"ds.help.canvas" "Canvas"
|
||||
"workspace.header.rect" "Box (Ctrl + B)"
|
||||
"workspace.header.circle" "Circle (Ctrl + E)"
|
||||
"workspace.header.line" "Line (Ctrl + L)"
|
||||
"workspace.header.text" "Text"
|
||||
"workspace.header.path" "Path"
|
||||
"workspace.header.curve" "Curve"
|
||||
"workspace.header.ruler" "Ruler"
|
||||
"workspace.header.canvas" "Canvas"
|
||||
|
||||
"ds.user.profile" "Profile"
|
||||
"ds.user.password" "Password"
|
||||
"ds.user.notifications" "Notifications"
|
||||
"ds.user.exit" "Exit"
|
||||
|
||||
"header.sitemap" "Sitemap (Ctrl + Shift + M)"
|
||||
"header.draw-tools" "Draw tools (Ctrl + Shift + S)"
|
||||
"header.color-palette" "Color Palette (---)"
|
||||
"header.icons" "Icons (Ctrl + Shift + I)"
|
||||
"header.layers" "Layers (Ctrl + Shift + L)"
|
||||
"header.element-options" "Element options (Ctrl + Shift + O)"
|
||||
"header.document-history" "History (Ctrl + Shift + H)"
|
||||
"header.undo" "Undo (Ctrl + Z)"
|
||||
"header.redo" "Redo (Ctrl + Shift + Z)"
|
||||
"header.download" "Download (Ctrl + E)"
|
||||
"header.image" "Image (Ctrl + I)"
|
||||
"header.rules" "Rules"
|
||||
"header.grid" "Grid (Ctrl + G)"
|
||||
"header.grid-snap" "Snap to grid"
|
||||
"header.align" "Align (Ctrl + A)"
|
||||
"header.view-mode" "View mode (Ctrl + P)"
|
||||
"workspace.header.sitemap" "Sitemap (Ctrl + Shift + M)"
|
||||
"workspace.header.draw-tools" "Draw tools (Ctrl + Shift + S)"
|
||||
"workspace.header.color-palette" "Color Palette (---)"
|
||||
"workspace.header.icons" "Icons (Ctrl + Shift + I)"
|
||||
"workspace.header.layers" "Layers (Ctrl + Shift + L)"
|
||||
"workspace.header.element-options" "Element options (Ctrl + Shift + O)"
|
||||
"workspace.header.document-history" "History (Ctrl + Shift + H)"
|
||||
"workspace.header.undo" "Undo (Ctrl + Z)"
|
||||
"workspace.header.redo" "Redo (Ctrl + Shift + Z)"
|
||||
"workspace.header.download" "Download (Ctrl + E)"
|
||||
"workspace.header.image" "Image (Ctrl + I)"
|
||||
"workspace.header.rules" "Rules"
|
||||
"workspace.header.grid" "Grid (Ctrl + G)"
|
||||
"workspace.header.grid-snap" "Snap to grid"
|
||||
"workspace.header.align" "Align (Ctrl + A)"
|
||||
"workspace.header.view-mode" "View mode (Ctrl + P)"
|
||||
|
||||
"workspace.options.radius" "Radius"
|
||||
"workspace.options.size" "Size"
|
||||
"workspace.options.width" "Width"
|
||||
"workspace.options.height" "Height"
|
||||
"workspace.options.stroke.style" "Style"
|
||||
"workspace.options.stroke.none" "None"
|
||||
"workspace.options.stroke.solid" "Solid"
|
||||
"workspace.options.stroke.dotted" "Dotted"
|
||||
"workspace.options.stroke.dashed" "Dashed"
|
||||
"workspace.options.stroke.mixed" "Mixed"
|
||||
"workspace.options.position" "Position"
|
||||
"workspace.options.rotation" "Rotation"
|
||||
"workspace.options.opacity" "Opacity"
|
||||
"workspace.options.color" "Color"
|
||||
"workspace.options.background-color" "Background color"
|
||||
"workspace.options.font-family" "Font family"
|
||||
"workspace.options.size-weight" "Size and Weight"
|
||||
"workspace.options.font-size" "Font Size"
|
||||
"workspace.options.line-height-letter-spacing" "Line height and Letter spacing"
|
||||
"workspace.options.line-height" "Line height"
|
||||
"workspace.options.letter-spacing" "Letter spacing"
|
||||
"workspace.options.text-align" "Text align"
|
||||
"workspace.options.name" "Name"
|
||||
"workspace.options.go" "Go go go!"
|
||||
"workspace.options.measures" "Size, position & rotation"
|
||||
"workspace.options.rotation-radius" "Rotation & Radius"
|
||||
|
||||
"element.measures" "Size, position & rotation"
|
||||
"element.fill" "Fill"
|
||||
"element.stroke" "Stroke"
|
||||
"workspace.options.stroke" "Stroke"
|
||||
"element.text" "Text"
|
||||
"element.interactions" "Interactions"
|
||||
"element.page-measures" "Page settings"
|
||||
"workspace.options.page-measures" "Page settings"
|
||||
"element.page-grid-options" "Grid settings"
|
||||
|
||||
"image.new" "New image"
|
||||
|
|
|
@ -82,9 +82,6 @@
|
|||
;; Something else
|
||||
:else
|
||||
(do
|
||||
(js/console.error "Unhandled Error:"
|
||||
"\n - message:" (ex-message error)
|
||||
"\n - data:" (pr-str (ex-data error)))
|
||||
(js/console.error error)
|
||||
(ts/schedule 100 #(st/emit! (uum/error (tr "errors.generic")))))))
|
||||
|
||||
|
|
|
@ -17,28 +17,28 @@
|
|||
id (when (uuid-str? id) (uuid id))]
|
||||
[:main.dashboard-main
|
||||
[:& messages-widget]
|
||||
[:& header {:section :dashboard/projects}]
|
||||
[:& header {:section :dashboard-projects}]
|
||||
[:& projects/projects-page {:id id}]]))
|
||||
|
||||
(mf/defc dashboard-assets
|
||||
[{:keys [route] :as props}]
|
||||
(let [section (:name route)
|
||||
(let [section (get-in route [:data :name])
|
||||
{:keys [id type]} (get-in route [:params :query])
|
||||
id (cond
|
||||
;; (str/digits? id) (parse-int id)
|
||||
(uuid-str? id) (uuid id)
|
||||
(str/empty-or-nil? id) nil
|
||||
:else id)
|
||||
type (when (str/alpha? type) (keyword type))]
|
||||
type (if (str/alpha? type) (keyword type) :own)]
|
||||
[:main.dashboard-main
|
||||
[:& messages-widget]
|
||||
[:& header {:section section}]
|
||||
(case section
|
||||
:dashboard/icons
|
||||
:dashboard-icons
|
||||
[:& icons/icons-page {:type type :id id}]
|
||||
|
||||
:dashboard/images
|
||||
:dashboard-images
|
||||
[:& images/images-page {:type type :id id}]
|
||||
|
||||
:dashboard/colors
|
||||
:dashboard-colors
|
||||
[:& colors/colors-page {:type type :id id}])]))
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
(delete []
|
||||
(st/emit!
|
||||
(dc/delete-collection (:id coll))
|
||||
(rt/nav :dashboard/colors nil {:type (:type coll)})))
|
||||
(rt/nav :dashboard-colors nil {:type (:type coll)})))
|
||||
|
||||
(on-delete []
|
||||
(modal/show! confirm-dialog {:on-accept delete}))]
|
||||
|
@ -79,7 +79,7 @@
|
|||
editable? (= type :own)]
|
||||
(letfn [(on-click [event]
|
||||
(let [type (or type :own)]
|
||||
(st/emit! (rt/nav :dashboard/colors nil {:type type :id id}))))
|
||||
(st/emit! (rt/nav :dashboard-colors nil {:type type :id id}))))
|
||||
(on-input-change [event]
|
||||
(let [value (dom/get-target event)
|
||||
value (dom/get-value value)]
|
||||
|
@ -107,14 +107,14 @@
|
|||
:on-key-down on-input-keyup}]
|
||||
[:span.close {:on-click on-cancel} i/close]]
|
||||
[:span.element-title name])
|
||||
[:span.element-subtitle
|
||||
#_[:span.element-subtitle
|
||||
(tr "ds.num-elements" (t/c colors))]])))
|
||||
|
||||
(mf/defc nav
|
||||
[{:keys [id type colls selected-coll] :as props}]
|
||||
(let [own? (= type :own)
|
||||
builtin? (= type :builtin)
|
||||
select-tab #(st/emit! (rt/nav :dashboard/colors nil {:type %}))]
|
||||
select-tab #(st/emit! (rt/nav :dashboard-colors nil {:type %}))]
|
||||
[:div.library-bar
|
||||
[:div.library-bar-inside
|
||||
[:ul.library-tabs
|
||||
|
@ -259,7 +259,8 @@
|
|||
[{:keys [id type coll] :as props}]
|
||||
(let [selected (mf/deref selected-colors-iref)]
|
||||
[:section.dashboard-grid.library
|
||||
[:& grid-header {:coll coll}]
|
||||
(when coll
|
||||
[:& grid-header {:coll coll}])
|
||||
[:& grid {:coll coll :id id :type type :selected selected}]
|
||||
(when (seq selected)
|
||||
[:& grid-options {:id id :type type
|
||||
|
@ -282,14 +283,12 @@
|
|||
(first colls))
|
||||
id (:id selected-coll)]
|
||||
|
||||
(mf/use-effect #(st/emit! (dc/initialize)) #js [id type])
|
||||
(mf/use-effect #(st/emit! (dc/fetch-collections)))
|
||||
|
||||
[:section.dashboard-content
|
||||
[:& nav {:type type
|
||||
:id id
|
||||
:colls colls
|
||||
:selected-coll selected-coll}]
|
||||
:colls colls}]
|
||||
[:& content {:type type
|
||||
:id id
|
||||
:coll selected-coll}]]))
|
||||
|
|
|
@ -25,26 +25,26 @@
|
|||
|
||||
(mf/defc header
|
||||
[{:keys [section] :as props}]
|
||||
(let [projects? (= section :dashboard/projects)
|
||||
icons? (= section :dashboard/icons)
|
||||
images? (= section :dashboard/images)
|
||||
colors? (= section :dashboard/colors)]
|
||||
(let [projects? (= section :dashboard-projects)
|
||||
icons? (= section :dashboard-icons)
|
||||
images? (= section :dashboard-images)
|
||||
colors? (= section :dashboard-colors)]
|
||||
[:header#main-bar.main-bar
|
||||
[:div.main-logo
|
||||
[:& header-link {:section :dashboard/projects
|
||||
[:& header-link {:section :dashboard-projects
|
||||
:content i/logo}]]
|
||||
[:ul.main-nav
|
||||
[:li {:class (when projects? "current")}
|
||||
[:& header-link {:section :dashboard/projects
|
||||
[:& header-link {:section :dashboard-projects
|
||||
:content (tr "ds.projects")}]]
|
||||
[:li {:class (when icons? "current")}
|
||||
[:& header-link {:section :dashboard/icons
|
||||
[:& header-link {:section :dashboard-icons
|
||||
:content (tr "ds.icons")}]]
|
||||
[:li {:class (when images? "current")}
|
||||
[:& header-link {:section :dashboard/images
|
||||
[:& header-link {:section :dashboard-images
|
||||
:content (tr "ds.images")}]]
|
||||
[:li {:class (when colors? "current")}
|
||||
[:& header-link {:section :dashboard/colors
|
||||
[:& header-link {:section :dashboard-colors
|
||||
:content (tr "ds.colors")}]]]
|
||||
[:& user]]))
|
||||
|
||||
|
|
|
@ -79,29 +79,14 @@
|
|||
|
||||
;; --- Nav
|
||||
|
||||
(defn- make-num-icons-iref
|
||||
[id]
|
||||
(letfn [(selector [icons]
|
||||
(->> (vals icons)
|
||||
(filter #(= id (:collection-id %)))
|
||||
(count)))]
|
||||
(-> (comp (l/key :icons)
|
||||
(l/lens selector))
|
||||
(l/derive st/state))))
|
||||
|
||||
(mf/defc nav-item
|
||||
[{:keys [coll selected?] :as props}]
|
||||
(let [local (mf/use-state {})
|
||||
{:keys [id type name num-icons]} coll
|
||||
;; TODO: recalculate the num-icons on crud operations for
|
||||
;; avod doing this on UI.
|
||||
;; num-icons-iref (mf/use-memo {:deps #js [id]
|
||||
;; :fn #(make-num-icons-iref (:id coll))})
|
||||
;; num-icons (mf/deref num-icons-iref)
|
||||
{:keys [id type name]} coll
|
||||
editable? (= type :own)]
|
||||
(letfn [(on-click [event]
|
||||
(let [type (or type :own)]
|
||||
(st/emit! (rt/nav :dashboard/icons {} {:type type :id id}))))
|
||||
(st/emit! (rt/nav :dashboard-icons {} {:type type :id id}))))
|
||||
(on-input-change [event]
|
||||
(-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
|
@ -127,15 +112,14 @@
|
|||
:on-change on-input-change
|
||||
:on-key-down on-input-keyup}]
|
||||
[:span.close {:on-click on-cancel} i/close]]
|
||||
[:span.element-title (if id name "Storage")])
|
||||
[:span.element-subtitle (tr "ds.num-elements" (t/c num-icons))]])))
|
||||
[:span.element-title (if id name "Storage")])])))
|
||||
|
||||
|
||||
(mf/defc nav
|
||||
[{:keys [id type colls selected-coll] :as props}]
|
||||
(let [own? (= type :own)
|
||||
builtin? (= type :builtin)
|
||||
select-tab #(st/emit! (rt/nav :dashboard/icons nil {:type %}))]
|
||||
select-tab #(st/emit! (rt/nav :dashboard-icons nil {:type %}))]
|
||||
[:div.library-bar
|
||||
[:div.library-bar-inside
|
||||
[:ul.library-tabs
|
||||
|
@ -350,54 +334,12 @@
|
|||
:selected (contains? (:selected opts) (:id icon))
|
||||
:edition? (= (:edition opts) (:id icon))}])]]]))
|
||||
|
||||
;; --- Menu
|
||||
|
||||
(mf/defc menu
|
||||
[{:keys [opts coll] :as props}]
|
||||
(let [ordering (:order opts :name)
|
||||
filtering (:filter opts "")
|
||||
icount (count (:icons coll))]
|
||||
(letfn [(on-term-change [event]
|
||||
(let [term (-> (dom/get-target event)
|
||||
(dom/get-value))]
|
||||
(st/emit! (di/update-opts :filter term))))
|
||||
(on-ordering-change [event]
|
||||
(let [value (dom/event->value event)
|
||||
value (read-string value)]
|
||||
(st/emit! (di/update-opts :order value))))
|
||||
(on-clear [event]
|
||||
(st/emit! (di/update-opts :filter "")))]
|
||||
[:section.dashboard-bar.library-gap
|
||||
[:div.dashboard-info
|
||||
|
||||
;; Counter
|
||||
[:span.dashboard-icons (tr "ds.num-icons" (t/c icount))]
|
||||
|
||||
;; Sorting
|
||||
[:div
|
||||
[:span (tr "ds.ordering")]
|
||||
[:select.input-select {:on-change on-ordering-change
|
||||
:value (pr-str ordering)}
|
||||
(for [[key value] (seq +ordering-options+)]
|
||||
[:option {:key key :value (pr-str key)} (tr value)])]]
|
||||
|
||||
;; Search
|
||||
[:form.dashboard-search
|
||||
[:input.input-text {:key :icons-search-box
|
||||
:type "text"
|
||||
:on-change on-term-change
|
||||
:auto-focus true
|
||||
:placeholder (tr "ds.search.placeholder")
|
||||
:value filtering}]
|
||||
[:div.clear-search {:on-click on-clear} i/close]]]])))
|
||||
|
||||
;; --- Content
|
||||
|
||||
(mf/defc content
|
||||
[{:keys [id type coll] :as props}]
|
||||
(let [opts (mf/deref opts-iref)]
|
||||
[:*
|
||||
[:& menu {:opts opts :coll coll}]
|
||||
[:section.dashboard-grid.library
|
||||
(when coll
|
||||
[:& grid-header {:coll coll}])
|
||||
|
@ -425,15 +367,13 @@
|
|||
:else (first colls))
|
||||
id (:id selected-coll)]
|
||||
(mf/use-effect #(st/emit! di/fetch-collections))
|
||||
(mf/use-effect {:fn #(st/emit! di/initialize
|
||||
(di/fetch-icons id))
|
||||
:deps #js [id type]})
|
||||
(mf/use-effect {:fn #(st/emit! (di/fetch-icons id))
|
||||
:deps #js [(str id)]})
|
||||
|
||||
[:section.dashboard-content
|
||||
[:& nav {:type type
|
||||
:id id
|
||||
:colls colls
|
||||
:selected-coll selected-coll}]
|
||||
:colls colls}]
|
||||
[:& content {:type type
|
||||
:id id
|
||||
:coll selected-coll}]]))
|
||||
|
|
|
@ -26,30 +26,6 @@
|
|||
[uxbox.util.router :as rt]
|
||||
[uxbox.util.time :as dt]))
|
||||
|
||||
;; --- Helpers & Constants
|
||||
|
||||
(def +ordering-options+
|
||||
{:name "ds.ordering.by-name"
|
||||
:created "ds.ordering.by-creation-date"})
|
||||
|
||||
(defn- sort-images-by
|
||||
[ordering images]
|
||||
(case ordering
|
||||
:name (sort-by :name images)
|
||||
:created (reverse (sort-by :created-at images))
|
||||
images))
|
||||
|
||||
(defn- contains-term?
|
||||
[phrase term]
|
||||
(let [term (name term)]
|
||||
(str/includes? (str/lower phrase) (str/trim (str/lower term)))))
|
||||
|
||||
(defn- filter-images-by
|
||||
[term images]
|
||||
(if (str/blank? term)
|
||||
images
|
||||
(filter #(contains-term? (:name %) term) images)))
|
||||
|
||||
;; --- Refs
|
||||
|
||||
(def collections-iref
|
||||
|
@ -80,61 +56,41 @@
|
|||
|
||||
;; --- Nav
|
||||
|
||||
(defn- make-num-images-iref
|
||||
[id]
|
||||
(letfn [(selector [images]
|
||||
(->> (vals images)
|
||||
(filter #(= id (:collection-id %)))
|
||||
(count)))]
|
||||
(-> (comp (l/key :images)
|
||||
(l/lens selector))
|
||||
(l/derive st/state))))
|
||||
|
||||
(mf/defc nav-item
|
||||
[{:keys [coll selected?] :as props}]
|
||||
(let [local (mf/use-state {})
|
||||
{:keys [id type name num-images]} coll
|
||||
;; TODO: recalculate the num-images on crud operations for
|
||||
;; avod doing this on UI.
|
||||
num-images-iref (mf/use-memo #(make-num-images-iref (:id coll)) #js [id])
|
||||
num-images (mf/deref num-images-iref)
|
||||
editable? (= type :own)]
|
||||
(letfn [(on-click [event]
|
||||
(let [type (or type :own)]
|
||||
(st/emit! (rt/nav :dashboard/images {} {:type type :id id}))))
|
||||
(on-input-change [event]
|
||||
(-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(swap! local assoc :name)))
|
||||
(on-cancel [event]
|
||||
(swap! local dissoc :name :edit))
|
||||
(on-double-click [event]
|
||||
(when editable?
|
||||
(swap! local assoc :edit true)))
|
||||
(on-input-keyup [event]
|
||||
(when (kbd/enter? event)
|
||||
(let [value (-> (dom/get-target event) (dom/get-value))]
|
||||
(st/emit! (di/rename-collection id (str/trim (:name @local))))
|
||||
(swap! local assoc :edit false))))]
|
||||
[:li {:on-click on-click
|
||||
:on-double-click on-double-click
|
||||
:class-name (when selected? "current")}
|
||||
(if (:edit @local)
|
||||
[:div
|
||||
[:input.element-title {:value (if (:name @local)
|
||||
(:name @local)
|
||||
(if id name "Storage"))
|
||||
:on-change on-input-change
|
||||
:on-key-down on-input-keyup}]
|
||||
[:span.close {:on-click on-cancel} i/close]]
|
||||
[:span.element-title (if id name "Storage")])
|
||||
[:span.element-subtitle (tr "ds.num-elements" (t/c num-images))]])))
|
||||
editable? (= type :own)
|
||||
on-click
|
||||
(fn [event]
|
||||
(let [type (or type :own)]
|
||||
(st/emit! (rt/nav :dashboard-images {} {:type type :id id}))))
|
||||
|
||||
on-cancel-edition #(swap! local dissoc :edit)
|
||||
on-double-click #(when editable? (swap! local assoc :edit true))
|
||||
on-input-keyup
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(str/trim))]
|
||||
(st/emit! (di/rename-collection id value))
|
||||
(swap! local assoc :edit false))))]
|
||||
[:li {:on-click on-click
|
||||
:on-double-click on-double-click
|
||||
:class-name (when selected? "current")}
|
||||
(if (:edit @local)
|
||||
[:div
|
||||
[:input.element-title {:default-value name
|
||||
:on-key-down on-input-keyup}]
|
||||
[:span.close {:on-click on-cancel-edition} i/close]]
|
||||
[:span.element-title (if id name "Storage")])]))
|
||||
|
||||
(mf/defc nav
|
||||
[{:keys [id type colls selected-coll] :as props}]
|
||||
[{:keys [id type colls] :as props}]
|
||||
(let [own? (= type :own)
|
||||
builtin? (= type :builtin)
|
||||
select-tab #(st/emit! (rt/nav :dashboard/images nil {:type %}))]
|
||||
select-tab #(st/emit! (rt/nav :dashboard-images nil {:type %}))]
|
||||
[:div.library-bar
|
||||
[:div.library-bar-inside
|
||||
[:ul.library-tabs
|
||||
|
@ -148,7 +104,7 @@
|
|||
[:ul.library-elements
|
||||
(when own?
|
||||
[:li
|
||||
[:a.btn-primary {:on-click #(st/emit! (di/create-collection))}
|
||||
[:a.btn-primary {:on-click #(st/emit! di/create-collection)}
|
||||
(tr "ds.images-collection.new")]])
|
||||
(when own?
|
||||
[:& nav-item {:selected? (nil? id)}])
|
||||
|
@ -168,7 +124,7 @@
|
|||
colls (->> (vals colls)
|
||||
(filter #(= :own (:type %)))
|
||||
(remove #(= selected (:id %)))
|
||||
(sort-by :name colls))
|
||||
#_(sort-by :name colls))
|
||||
on-select (fn [event id]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
|
@ -306,20 +262,21 @@
|
|||
;; --- Grid
|
||||
|
||||
(defn- make-images-iref
|
||||
[id]
|
||||
(-> (comp (l/key :images)
|
||||
(l/lens (fn [images]
|
||||
(->> (vals images)
|
||||
(filter #(= id (:collection-id %)))))))
|
||||
(l/derive st/state)))
|
||||
[id type]
|
||||
(letfn [(selector-fn [state]
|
||||
(let [images (vals (:images state))]
|
||||
(filterv #(= id (:collection-id %)) images)))]
|
||||
(-> (l/lens selector-fn)
|
||||
(l/derive st/state))))
|
||||
|
||||
(mf/defc grid
|
||||
[{:keys [id type coll opts] :as props}]
|
||||
(let [editable? (or (= type :own) (nil? id))
|
||||
images-iref (mf/use-memo #(make-images-iref id) #js [id])
|
||||
images-iref (mf/use-memo
|
||||
{:fn #(make-images-iref id type)
|
||||
:deps (mf/deps id type)})
|
||||
images (->> (mf/deref images-iref)
|
||||
(filter-images-by (:filter opts ""))
|
||||
(sort-images-by (:order opts :name)))]
|
||||
(sort-by :created-at))]
|
||||
[:div.dashboard-grid-content
|
||||
[:div.dashboard-grid-row
|
||||
(when editable?
|
||||
|
@ -332,53 +289,53 @@
|
|||
|
||||
;; --- Menu
|
||||
|
||||
(mf/defc menu
|
||||
[{:keys [opts coll] :as props}]
|
||||
(let [ordering (:order opts :name)
|
||||
filtering (:filter opts "")
|
||||
icount (count (:images coll))]
|
||||
(letfn [(on-term-change [event]
|
||||
(let [term (-> (dom/get-target event)
|
||||
(dom/get-value))]
|
||||
(st/emit! (di/update-opts :filter term))))
|
||||
(on-ordering-change [event]
|
||||
(let [value (dom/event->value event)
|
||||
value (read-string value)]
|
||||
(st/emit! (di/update-opts :order value))))
|
||||
(on-clear [event]
|
||||
(st/emit! (di/update-opts :filter "")))]
|
||||
[:section.dashboard-bar.library-gap
|
||||
[:div.dashboard-info
|
||||
;; (mf/defc menu
|
||||
;; [{:keys [opts coll] :as props}]
|
||||
;; (let [ordering (:order opts :name)
|
||||
;; filtering (:filter opts "")
|
||||
;; icount (count (:images coll))]
|
||||
;; (letfn [(on-term-change [event]
|
||||
;; (let [term (-> (dom/get-target event)
|
||||
;; (dom/get-value))]
|
||||
;; (st/emit! (di/update-opts :filter term))))
|
||||
;; (on-ordering-change [event]
|
||||
;; (let [value (dom/event->value event)
|
||||
;; value (read-string value)]
|
||||
;; (st/emit! (di/update-opts :order value))))
|
||||
;; (on-clear [event]
|
||||
;; (st/emit! (di/update-opts :filter "")))]
|
||||
;; [:section.dashboard-bar.library-gap
|
||||
;; [:div.dashboard-info
|
||||
|
||||
;; Counter
|
||||
[:span.dashboard-images (tr "ds.num-images" (t/c icount))]
|
||||
;; ;; Counter
|
||||
;; [:span.dashboard-images (tr "ds.num-images" (t/c icount))]
|
||||
|
||||
;; Sorting
|
||||
[:div
|
||||
[:span (tr "ds.ordering")]
|
||||
[:select.input-select {:on-change on-ordering-change
|
||||
:value (pr-str ordering)}
|
||||
(for [[key value] (seq +ordering-options+)]
|
||||
[:option {:key key :value (pr-str key)} (tr value)])]]
|
||||
;; ;; Sorting
|
||||
;; [:div
|
||||
;; [:span (tr "ds.ordering")]
|
||||
;; [:select.input-select {:on-change on-ordering-change
|
||||
;; :value (pr-str ordering)}
|
||||
;; (for [[key value] (seq +ordering-options+)]
|
||||
;; [:option {:key key :value (pr-str key)} (tr value)])]]
|
||||
|
||||
;; Search
|
||||
[:form.dashboard-search
|
||||
[:input.input-text {:key :images-search-box
|
||||
:type "text"
|
||||
:on-change on-term-change
|
||||
:auto-focus true
|
||||
:placeholder (tr "ds.search.placeholder")
|
||||
:value filtering}]
|
||||
[:div.clear-search {:on-click on-clear} i/close]]]])))
|
||||
;; ;; Search
|
||||
;; [:form.dashboard-search
|
||||
;; [:input.input-text {:key :images-search-box
|
||||
;; :type "text"
|
||||
;; :on-change on-term-change
|
||||
;; :auto-focus true
|
||||
;; :placeholder (tr "ds.search.placeholder")
|
||||
;; :value filtering}]
|
||||
;; [:div.clear-search {:on-click on-clear} i/close]]]])))
|
||||
|
||||
(mf/defc content
|
||||
[{:keys [id type coll] :as props}]
|
||||
(let [opts (mf/deref opts-iref)]
|
||||
[:*
|
||||
[:& menu {:opts opts :coll coll}]
|
||||
[:section.dashboard-grid.library
|
||||
(when coll
|
||||
[:& grid-header {:coll coll}])
|
||||
|
||||
[:& grid {:id id
|
||||
:type type
|
||||
:coll coll
|
||||
|
@ -390,28 +347,26 @@
|
|||
|
||||
(mf/defc images-page
|
||||
[{:keys [id type] :as props}]
|
||||
(let [type (or type :own)
|
||||
colls (mf/deref collections-iref)
|
||||
(let [colls (mf/deref collections-iref)
|
||||
colls (cond->> (vals colls)
|
||||
(= type :own) (filter #(= :own (:type %)))
|
||||
(= type :builtin) (filter #(= :builtin (:type %)))
|
||||
true (sort-by :created-at))
|
||||
selected-coll (cond
|
||||
(and (= type :own) (nil? id)) nil
|
||||
(uuid? id) (seek #(= id (:id %)) colls)
|
||||
:else (first colls))
|
||||
id (:id selected-coll)]
|
||||
|
||||
(mf/use-effect #(st/emit! (di/fetch-collections)))
|
||||
(mf/use-effect {:fn #(st/emit! (di/initialize)
|
||||
(di/fetch-images id))
|
||||
:deps #js [id type]})
|
||||
coll (cond
|
||||
(and (= type :own) (nil? id)) nil
|
||||
(uuid? id) (seek #(= id (:id %)) colls)
|
||||
:else (first colls))
|
||||
id (:id coll)]
|
||||
|
||||
(mf/use-effect #(st/emit! di/fetch-collections))
|
||||
(mf/use-effect {:fn #(st/emit! (di/fetch-images (:id coll)))
|
||||
:deps #js [(str (:id coll))]})
|
||||
|
||||
[:section.dashboard-content
|
||||
[:& nav {:type type
|
||||
:id id
|
||||
:colls colls
|
||||
:selected-coll selected-coll}]
|
||||
:colls colls}]
|
||||
[:& content {:type type
|
||||
:id id
|
||||
:coll selected-coll}]]))
|
||||
:coll coll}]]))
|
||||
|
|
|
@ -64,60 +64,6 @@
|
|||
files
|
||||
(filter #(contains-term? (:name %) term) files)))
|
||||
|
||||
;; --- Menu (Filter & Sort)
|
||||
|
||||
(mf/defc menu
|
||||
[{:keys [id opts files] :as props}]
|
||||
(let [ordering (:order opts :modified)
|
||||
filtering (:filter opts "")
|
||||
|
||||
on-term-change
|
||||
(fn [event]
|
||||
(let [term (-> (dom/get-target event)
|
||||
(dom/get-value))]
|
||||
(st/emit! (udp/update-opts :filter term))))
|
||||
|
||||
on-order-change
|
||||
(fn [event]
|
||||
(let [value (dom/event->value event)
|
||||
value (read-string value)]
|
||||
(st/emit! (udp/update-opts :order value))))
|
||||
|
||||
on-clear
|
||||
(fn [event]
|
||||
(st/emit! (udp/update-opts :filter "")))]
|
||||
|
||||
[:section.dashboard-bar.library-gap
|
||||
[:div.dashboard-info
|
||||
|
||||
;; Counter
|
||||
[:span.dashboard-images (tr "ds.num-files" (t/c (count files)))]
|
||||
|
||||
[:div
|
||||
;; Sorting
|
||||
;; TODO: convert to separate component?
|
||||
(when id
|
||||
[:*
|
||||
[:span (tr "ds.ordering")]
|
||||
[:select.input-select {:on-change on-order-change
|
||||
:value (pr-str ordering)}
|
||||
(for [[key value] (seq +ordering-options+)]
|
||||
(let [key (pr-str key)]
|
||||
[:option {:key key :value key} (tr value)]))]])]
|
||||
|
||||
;; Search
|
||||
;; TODO: convert to separate component?
|
||||
; [:form.dashboard-search
|
||||
; [:input.input-text
|
||||
; {:key :images-search-box
|
||||
; :type "text"
|
||||
; :on-change on-term-change
|
||||
; :auto-focus true
|
||||
; :placeholder (tr "ds.search.placeholder")
|
||||
; :value (or filtering "")}]
|
||||
; [:div.clear-search {:on-click on-clear} i/close]]
|
||||
]]))
|
||||
|
||||
;; --- Grid Item Thumbnail
|
||||
|
||||
(mf/defc grid-item-thumbnail
|
||||
|
@ -164,9 +110,9 @@
|
|||
;; [:div.project-th-icon.pages
|
||||
;; i/page
|
||||
;; #_[:span (:total-pages project)]]
|
||||
#_[:div.project-th-icon.comments
|
||||
i/chat
|
||||
[:span "0"]]
|
||||
;; [:div.project-th-icon.comments
|
||||
;; i/chat
|
||||
;; [:span "0"]]
|
||||
[:div.project-th-icon.edit
|
||||
{:on-click on-edit}
|
||||
i/pencil]
|
||||
|
@ -177,7 +123,7 @@
|
|||
;; --- Grid
|
||||
|
||||
(mf/defc grid
|
||||
[{:keys [opts files] :as props}]
|
||||
[{:keys [id opts files] :as props}]
|
||||
(let [order (:order opts :modified)
|
||||
filter (:filter opts "")
|
||||
files (->> files
|
||||
|
@ -185,15 +131,13 @@
|
|||
(sort-by order))
|
||||
on-click #(do
|
||||
(dom/prevent-default %)
|
||||
#_(modal/show! create-project-dialog {})
|
||||
#_(udl/open! :create-project))
|
||||
]
|
||||
(st/emit! (udp/create-file {:project-id id})))]
|
||||
[:section.dashboard-grid
|
||||
;; [:h2 (tr "ds.projects.file-name")]
|
||||
[:div.dashboard-grid-content
|
||||
[:div.dashboard-grid-row
|
||||
[:div.grid-item.add-project #_{:on-click on-click}
|
||||
[:span (tr "ds.new-file")]]
|
||||
(when id
|
||||
[:div.grid-item.add-project {:on-click on-click}
|
||||
[:span (tr "ds.new-file")]])
|
||||
(for [item files]
|
||||
[:& grid-item {:file item :key (:id item)}])]]]))
|
||||
|
||||
|
@ -245,10 +189,6 @@
|
|||
:placeholder (tr "ds.search.placeholder")}]
|
||||
[:div.clear-search i/close]]
|
||||
[:ul.library-elements
|
||||
; [:li
|
||||
; [:a.btn-primary #_{:on-click #(st/emit! di/create-collection)}
|
||||
; "new project +"]]
|
||||
|
||||
[:li.recent-projects {:on-click #(st/emit! (udp/go-to-project nil))
|
||||
:class-name (when (nil? id) "current")}
|
||||
[:span.element-title "Recent"]]
|
||||
|
@ -280,10 +220,8 @@
|
|||
[{:keys [id] :as props}]
|
||||
(let [opts (mf/deref opts-iref)
|
||||
files (mf/deref files-ref)]
|
||||
[:*
|
||||
#_[:& menu {:id id :opts opts :files files}]
|
||||
[:section.dashboard-grid.library
|
||||
[:& grid {:id id :opts opts :files files}]]]))
|
||||
[:section.dashboard-grid.library
|
||||
[:& grid {:id id :opts opts :files files}]]))
|
||||
|
||||
;; --- Projects Page
|
||||
|
||||
|
|
|
@ -4,7 +4,43 @@
|
|||
;;
|
||||
;; Copyright (c) 2016-2017 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.main.ui.shapes.attrs)
|
||||
(ns uxbox.main.ui.shapes.attrs
|
||||
(:require [cuerdas.core :as str]))
|
||||
|
||||
|
||||
;; (defn camel-case
|
||||
;; "Returns camel case version of the key, e.g. :http-equiv becomes :httpEquiv."
|
||||
;; [k]
|
||||
;; (if (or (keyword? k)
|
||||
;; (string? k)
|
||||
;; (symbol? k))
|
||||
;; (let [[first-word & words] (str/split (name k) #"-")]
|
||||
;; (if (or (empty? words)
|
||||
;; (= "aria" first-word)
|
||||
;; (= "data" first-word))
|
||||
;; k
|
||||
;; (-> (map str/capital words)
|
||||
;; (conj first-word)
|
||||
;; str/join
|
||||
;; keyword)))
|
||||
;; k))
|
||||
|
||||
(defn- process-key
|
||||
[k]
|
||||
(if (keyword? k)
|
||||
(cond
|
||||
(keyword-identical? k :stroke-color) :stroke
|
||||
(keyword-identical? k :fill-color) :fill
|
||||
(str/includes? (name k) "-") (str/camel k)
|
||||
:else k)))
|
||||
|
||||
(defn- process-attrs
|
||||
[m]
|
||||
(persistent!
|
||||
(reduce-kv (fn [m k v]
|
||||
(assoc! m (process-key k) v))
|
||||
(transient {})
|
||||
m)))
|
||||
|
||||
(def shape-style-attrs
|
||||
#{:fill-color
|
||||
|
@ -17,12 +53,6 @@
|
|||
:rx
|
||||
:ry})
|
||||
|
||||
(def shape-default-attrs
|
||||
{:stroke-color "#000000"
|
||||
:stroke-opacity 1
|
||||
:fill-color "#000000"
|
||||
:fill-opacity 1})
|
||||
|
||||
(defn- stroke-type->dasharray
|
||||
[style]
|
||||
(case style
|
||||
|
@ -30,24 +60,23 @@
|
|||
:dotted "5,5"
|
||||
:dashed "10,10"))
|
||||
|
||||
(defn- rename-attr
|
||||
[[key value :as pair]]
|
||||
(case key
|
||||
:stroke-color [:stroke value]
|
||||
:fill-color [:fill value]
|
||||
pair))
|
||||
;; (defn- rename-attr
|
||||
;; [[key value :as pair]]
|
||||
;; (case key
|
||||
;; :stroke-color [:stroke value]
|
||||
;; :fill-color [:fill value]
|
||||
;; pair))
|
||||
|
||||
(defn- rename-attrs
|
||||
[attrs]
|
||||
(into {} (map rename-attr) attrs))
|
||||
;; (defn- rename-attrs
|
||||
;; [attrs]
|
||||
;; (into {} (map rename-attr) attrs))
|
||||
|
||||
(defn- transform-stroke-attrs
|
||||
[{:keys [stroke-style] :or {stroke-style :none} :as attrs}]
|
||||
(case stroke-style
|
||||
:none (dissoc attrs :stroke-style :stroke-width :stroke-opacity :stroke-color)
|
||||
:solid (-> (merge shape-default-attrs attrs)
|
||||
(dissoc :stroke-style))
|
||||
(-> (merge shape-default-attrs attrs)
|
||||
:solid (dissoc attrs :stroke-style)
|
||||
(-> attrs
|
||||
(assoc :stroke-dasharray (stroke-type->dasharray stroke-style))
|
||||
(dissoc :stroke-style))))
|
||||
|
||||
|
@ -56,4 +85,4 @@
|
|||
[shape]
|
||||
(-> (select-keys shape shape-style-attrs)
|
||||
(transform-stroke-attrs)
|
||||
(rename-attrs)))
|
||||
(process-attrs)))
|
||||
|
|
|
@ -47,18 +47,19 @@
|
|||
|
||||
(mf/defc image-shape
|
||||
[{:keys [shape image] :as props}]
|
||||
(let [{:keys [id x1 y1 width height modifier-mtx]} (geom/size shape)
|
||||
(let [{:keys [id x y width height modifier-mtx]} shape
|
||||
moving? (boolean modifier-mtx)
|
||||
transform (when (gmt/matrix? modifier-mtx)
|
||||
(str modifier-mtx))
|
||||
|
||||
props {:x x1 :y y1
|
||||
:id (str "shape-" id)
|
||||
:preserve-aspect-ratio "none"
|
||||
:class (classnames :move-cursor moving?)
|
||||
:xlink-href (:url image)
|
||||
:transform transform
|
||||
:width width
|
||||
:height height}
|
||||
attrs (merge props (attrs/extract-style-attrs shape))]
|
||||
[:> :image (normalize-props attrs)]))
|
||||
props (-> (attrs/extract-style-attrs shape)
|
||||
(assoc :x x
|
||||
:y y
|
||||
:id (str "shape-" id)
|
||||
:preserveAspectRatio "none"
|
||||
:class (classnames :move-cursor moving?)
|
||||
:xlinkHref (:url image)
|
||||
:transform transform
|
||||
:width width
|
||||
:height height))]
|
||||
[:& "image" props]))
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
(ns uxbox.main.ui.shapes.rect
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[cuerdas.core :as str]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
||||
|
@ -46,18 +47,19 @@
|
|||
(gmt/matrix? modifier-mtx) (geom/transform shape modifier-mtx)
|
||||
:else shape)
|
||||
|
||||
{:keys [x1 y1 width height] :as shape} (geom/size shape)
|
||||
{:keys [x y width height]} shape
|
||||
|
||||
transform (when (pos? rotation)
|
||||
(str (rotate (gmt/matrix) shape)))
|
||||
|
||||
moving? (boolean modifier-mtx)
|
||||
|
||||
props {:x x1 :y y1
|
||||
:id (str "shape-" id)
|
||||
:className (classnames :move-cursor moving?)
|
||||
:width width
|
||||
:height height
|
||||
:transform transform}
|
||||
attrs (merge (attrs/extract-style-attrs shape) props)]
|
||||
[:& "rect" attrs]))
|
||||
props (-> (attrs/extract-style-attrs shape)
|
||||
(assoc :x x
|
||||
:y y
|
||||
:id (str "shape-" id)
|
||||
:className (classnames :move-cursor moving?)
|
||||
:width width
|
||||
:height height
|
||||
:transform transform))]
|
||||
[:& "rect" props]))
|
||||
|
|
|
@ -122,7 +122,7 @@
|
|||
style (make-style shape)
|
||||
on-input (fn [ev]
|
||||
(let [content (dom/event->inner-text ev)]
|
||||
(st/emit! (udw/update-shape-attrs id {:content content}))))]
|
||||
#_(st/emit! (udw/update-shape-attrs id {:content content}))))]
|
||||
[:foreignObject {:x x1 :y y1 :width width :height height}
|
||||
[:div {:style (normalize-props style)
|
||||
:ref (::container own)
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
(scroll/scroll-to-point dom mouse-point scroll-position))))
|
||||
|
||||
(mf/defc workspace-content
|
||||
[{:keys [layout page file] :as params}]
|
||||
[{:keys [layout page file flags] :as params}]
|
||||
(let [canvas (mf/use-ref nil)
|
||||
left-sidebar? (not (empty? (keep layout [:layers :sitemap
|
||||
:document-history])))
|
||||
|
@ -93,28 +93,13 @@
|
|||
(when right-sidebar?
|
||||
[:& right-sidebar {:page page :layout layout}])]))
|
||||
|
||||
(mf/defc workspace
|
||||
[{:keys [file-id page-id] :as props}]
|
||||
|
||||
(mf/defc workspace-page
|
||||
[{:keys [file-id page-id layout file flags] :as props}]
|
||||
(mf/use-effect
|
||||
{:deps #js [(str file-id)]
|
||||
:fn (fn []
|
||||
(st/emit! (dw/initialize-ws file-id))
|
||||
#(st/emit! (dw/finalize-ws file-id)))})
|
||||
|
||||
(mf/use-effect
|
||||
{:deps #js [(str file-id)
|
||||
(str page-id)]
|
||||
:fn (fn []
|
||||
(let [sub (shortcuts/init)]
|
||||
(st/emit! (dw/initialize file-id page-id))
|
||||
#(rx/cancel! sub)))})
|
||||
|
||||
(let [layout (mf/deref refs/workspace-layout)
|
||||
file (mf/deref refs/workspace-file)
|
||||
page (mf/deref refs/workspace-page)
|
||||
flags (mf/deref refs/selected-flags)]
|
||||
{:deps (mf/deps file-id page-id)
|
||||
:fn #(st/emit! (dw/initialize-page page-id))})
|
||||
|
||||
(let [page (mf/deref refs/workspace-page)]
|
||||
[:> rdnd/provider {:backend rdnd/html5}
|
||||
[:& messages-widget]
|
||||
[:& header {:page page :layout layout :flags flags}]
|
||||
|
@ -124,5 +109,34 @@
|
|||
|
||||
(when (and layout page)
|
||||
[:& workspace-content {:layout layout
|
||||
:flags flags
|
||||
:file file
|
||||
:page page}])]))
|
||||
|
||||
|
||||
|
||||
(mf/defc workspace
|
||||
[{:keys [file-id page-id] :as props}]
|
||||
(mf/use-effect
|
||||
{:deps (mf/deps file-id)
|
||||
:fn (fn []
|
||||
(st/emit! (dw/initialize file-id))
|
||||
#(st/emit! (dw/finalize file-id)))})
|
||||
|
||||
(mf/use-effect
|
||||
{:deps (mf/deps file-id page-id)
|
||||
:fn (fn []
|
||||
(let [sub (shortcuts/init)]
|
||||
#(rx/cancel! sub)))})
|
||||
|
||||
(let [layout (mf/deref refs/workspace-layout)
|
||||
file (mf/deref refs/workspace-file)
|
||||
flags (mf/deref refs/selected-flags)]
|
||||
|
||||
;; TODO: maybe loading state?
|
||||
(when file
|
||||
[:& workspace-page {:layout layout
|
||||
:file file
|
||||
:flags flags
|
||||
:page-id page-id
|
||||
:file-id file-id}])))
|
||||
|
|
|
@ -30,10 +30,9 @@
|
|||
(mf/defc palette-item
|
||||
[{:keys [color] :as props}]
|
||||
(letfn [(select-color [event]
|
||||
(let [attrs (if (kbd/shift? event)
|
||||
{:stroke-color color}
|
||||
{:fill-color color})]
|
||||
(st/emit! (udw/update-selected-shapes-attrs attrs))))]
|
||||
(if (kbd/shift? event)
|
||||
(st/emit! (udw/update-selected-shapes :stroke-color color))
|
||||
(st/emit! (udw/update-selected-shapes :fill-color color))))]
|
||||
(let [rgb-vec (hex->rgb color)
|
||||
rgb-color (apply str "" (interpose ", " rgb-vec))]
|
||||
[:div.color-cell {:key (str color)
|
||||
|
|
|
@ -75,7 +75,7 @@
|
|||
(let [event (js/MouseEvent. "click")
|
||||
link (.createElement js/document "a")
|
||||
now (dt/now)
|
||||
stream (->> (rx/from-coll (generate-files pages))
|
||||
stream (->> (rx/from (generate-files pages))
|
||||
(rx/reduce conj [])
|
||||
(rx/mapcat zip/build)
|
||||
(rx/map blob/create-uri)
|
||||
|
|
|
@ -51,8 +51,7 @@
|
|||
:fill-opacity 0
|
||||
:segments []}
|
||||
{:type :canvas
|
||||
:name "Canvas"
|
||||
:stroke-color "#000000"}
|
||||
:name "Canvas"}
|
||||
{:type :curve
|
||||
:name "Path"
|
||||
:stroke-style :solid
|
||||
|
@ -109,18 +108,17 @@
|
|||
(def handle-drawing-generic
|
||||
(letfn [(initialize-drawing [state point]
|
||||
(let [shape (get-in state [:workspace-local :drawing])
|
||||
shape (geom/setup shape {:x1 (:x point)
|
||||
:y1 (:y point)
|
||||
:x2 (+ (:x point) 2)
|
||||
:y2 (+ (:y point) 2)})]
|
||||
shape (geom/setup shape {:x (:x point)
|
||||
:y (:y point)
|
||||
:width 2
|
||||
:height 2})]
|
||||
(assoc-in state [:workspace-local :drawing] (assoc shape ::initialized? true))))
|
||||
|
||||
(resize-shape [shape point lock?]
|
||||
(let [shape (-> (geom/shape->rect-shape shape)
|
||||
(geom/size))
|
||||
result (geom/resize-shape :bottom-right shape point lock?)
|
||||
scale (geom/calculate-scale-ratio shape result)
|
||||
mtx (geom/generate-resize-matrix :bottom-right shape scale)]
|
||||
(let [shape' (geom/shape->rect-shape shape)
|
||||
result (geom/resize-shape :bottom-right shape' point lock?)
|
||||
scale (geom/calculate-scale-ratio shape' result)
|
||||
mtx (geom/generate-resize-matrix :bottom-right shape' scale)]
|
||||
(assoc shape :modifier-mtx mtx)))
|
||||
|
||||
(update-drawing [state point lock?]
|
||||
|
@ -150,13 +148,13 @@
|
|||
|
||||
(def handle-drawing-path
|
||||
(letfn [(stoper-event? [{:keys [type shift] :as event}]
|
||||
(or (= event :interrupt)
|
||||
(and (uws/mouse-event? event)
|
||||
(or (and (= type :double-click) shift)
|
||||
(= type :context-menu)))
|
||||
(and (uws/keyboard-event? event)
|
||||
(= type :down)
|
||||
(= 13 (:key event)))))
|
||||
(or (= event ::end-path-drawing)
|
||||
(and (uws/mouse-event? event)
|
||||
(or (and (= type :double-click) shift)
|
||||
(= type :context-menu)))
|
||||
(and (uws/keyboard-event? event)
|
||||
(= type :down)
|
||||
(= 13 (:key event)))))
|
||||
|
||||
(initialize-drawing [state point]
|
||||
(-> state
|
||||
|
@ -238,8 +236,7 @@
|
|||
|
||||
(def handle-drawing-curve
|
||||
(letfn [(stoper-event? [{:keys [type shift] :as event}]
|
||||
(or (= event :interrupt)
|
||||
(and (uws/mouse-event? event) (= type :up))))
|
||||
(uws/mouse-event? event) (= type :up))
|
||||
|
||||
(initialize-drawing [state]
|
||||
(assoc-in state [:workspace-local :drawing ::initialized?] true))
|
||||
|
@ -282,7 +279,9 @@
|
|||
shape (dissoc shape ::initialized? :modifier-mtx)]
|
||||
;; Add & select the created shape to the workspace
|
||||
(rx/of dw/deselect-all
|
||||
(dw/add-shape shape)))))))))
|
||||
(if (= :canvas (:type shape))
|
||||
(dw/add-canvas shape)
|
||||
(dw/add-shape shape))))))))))
|
||||
|
||||
(def close-drawing-path
|
||||
(ptk/reify ::close-drawing-path
|
||||
|
@ -322,7 +321,8 @@
|
|||
(dom/stop-propagation event)
|
||||
(st/emit! (dw/set-tooltip nil)
|
||||
close-drawing-path
|
||||
:interrupt))
|
||||
::end-path-drawing))
|
||||
|
||||
(on-mouse-enter [event]
|
||||
(st/emit! (dw/set-tooltip "Click to close the path")))
|
||||
(on-mouse-leave [event]
|
||||
|
|
|
@ -7,22 +7,19 @@
|
|||
|
||||
(ns uxbox.main.ui.workspace.header
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[lentes.core :as l]
|
||||
[rumext.alpha :as mf]
|
||||
[uxbox.builtins.icons :as i]
|
||||
[uxbox.config :as cfg]
|
||||
[uxbox.main.data.history :as udh]
|
||||
[uxbox.main.data.undo :as udu]
|
||||
[uxbox.main.data.workspace :as dw]
|
||||
[uxbox.main.ui.workspace.images :refer [import-image-modal]]
|
||||
[uxbox.main.ui.modal :as modal]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.ui.modal :as modal]
|
||||
[uxbox.main.ui.users :refer [user]]
|
||||
[uxbox.main.ui.workspace.clipboard]
|
||||
[uxbox.util.data :refer [index-of]]
|
||||
[uxbox.util.i18n :refer (tr)]
|
||||
[uxbox.util.geom.point :as gpt]
|
||||
[uxbox.main.ui.workspace.images :refer [import-image-modal]]
|
||||
[uxbox.util.i18n :refer [tr]]
|
||||
[uxbox.util.math :as mth]
|
||||
[uxbox.util.router :as rt]))
|
||||
|
||||
|
@ -66,6 +63,7 @@
|
|||
(mf/defc header
|
||||
[{:keys [page layout flags] :as props}]
|
||||
(let [toggle #(st/emit! (dw/toggle-flag %))
|
||||
toggle-layout #(st/emit! (dw/toggle-layout-flag %))
|
||||
on-undo #(st/emit! (udu/undo))
|
||||
on-redo #(st/emit! (udu/redo))
|
||||
on-image #(modal/show! import-image-modal {})
|
||||
|
@ -91,100 +89,100 @@
|
|||
[:div.workspace-options
|
||||
[:ul.options-btn
|
||||
[:li.tooltip.tooltip-bottom
|
||||
{:alt (tr "ds.help.canvas")
|
||||
{:alt (tr "workspace.header.canvas")
|
||||
:class (when (= selected-drawtool :canvas) "selected")
|
||||
:on-click (partial select-drawtool :canvas)}
|
||||
i/artboard]
|
||||
[:li.tooltip.tooltip-bottom
|
||||
{:alt (tr "ds.help.rect")
|
||||
{:alt (tr "workspace.header.rect")
|
||||
:class (when (= selected-drawtool :rect) "selected")
|
||||
:on-click (partial select-drawtool :rect)}
|
||||
i/box]
|
||||
[:li.tooltip.tooltip-bottom
|
||||
{:alt (tr "ds.help.circle")
|
||||
{:alt (tr "workspace.header.circle")
|
||||
:class (when (= selected-drawtool :circle) "selected")
|
||||
:on-click (partial select-drawtool :circle)}
|
||||
i/circle]
|
||||
[:li.tooltip.tooltip-bottom
|
||||
{:alt (tr "ds.help.text")
|
||||
{:alt (tr "workspace.header.text")
|
||||
:class (when (= selected-drawtool :text) "selected")
|
||||
:on-click (partial select-drawtool :text)}
|
||||
i/text]
|
||||
[:li.tooltip.tooltip-bottom
|
||||
{:alt (tr "ds.help.path")
|
||||
{:alt (tr "workspace.header.path")
|
||||
:class (when (= selected-drawtool :path) "selected")
|
||||
:on-click (partial select-drawtool :path)}
|
||||
i/curve]
|
||||
[:li.tooltip.tooltip-bottom
|
||||
{:alt (tr "ds.help.curve")
|
||||
{:alt (tr "workspace.header.curve")
|
||||
:class (when (= selected-drawtool :curve) "selected")
|
||||
:on-click (partial select-drawtool :curve)}
|
||||
i/pencil]
|
||||
[:li.tooltip.tooltip-bottom
|
||||
{:alt (tr "header.color-palette")
|
||||
{:alt (tr "workspace.header.color-palette")
|
||||
:class (when (contains? layout :colorpalette) "selected")
|
||||
:on-click #(st/emit! (dw/toggle-layout-flag :colorpalette))}
|
||||
i/palette]
|
||||
[:li.tooltip.tooltip-bottom
|
||||
{:alt (tr "header.icons")
|
||||
{:alt (tr "workspace.header.icons")
|
||||
:class (when (contains? layout :icons) "selected")
|
||||
:on-click #(st/emit! (dw/toggle-layout-flag :icons))}
|
||||
i/icon-set]
|
||||
; [:li.tooltip.tooltip-bottom
|
||||
; {:alt (tr "header.layers")
|
||||
; :class (when (contains? layout :layers) "selected")
|
||||
; :on-click #(st/emit! (dw/toggle-layout-flag :layers))}
|
||||
; i/layers]
|
||||
; [:li.tooltip.tooltip-bottom
|
||||
; {:alt (tr "header.element-options")
|
||||
; :class (when (contains? layout :element-options) "selected")
|
||||
; :on-click #(st/emit! (dw/toggle-layout-flag :element-options))}
|
||||
; i/options]
|
||||
;; [:li.tooltip.tooltip-bottom
|
||||
;; {:alt (tr "header.layers")
|
||||
;; :class (when (contains? layout :layers) "selected")
|
||||
;; :on-click #(st/emit! (dw/toggle-layout-flag :layers))}
|
||||
;; i/layers]
|
||||
;; [:li.tooltip.tooltip-bottom
|
||||
;; {:alt (tr "header.element-options")
|
||||
;; :class (when (contains? layout :element-options) "selected")
|
||||
;; :on-click #(st/emit! (dw/toggle-layout-flag :element-options))}
|
||||
;; i/options]
|
||||
[:li.tooltip.tooltip-bottom
|
||||
{:alt (tr "header.document-history")
|
||||
{:alt (tr "workspace.header.document-history")
|
||||
:class (when (contains? layout :document-history) "selected")
|
||||
:on-click #(st/emit! (dw/toggle-layout-flag :document-history))}
|
||||
i/undo-history]
|
||||
; [:li.tooltip.tooltip-bottom
|
||||
; {:alt (tr "header.undo")
|
||||
; :on-click on-undo}
|
||||
; i/undo]
|
||||
; [:li.tooltip.tooltip-bottom
|
||||
; {:alt (tr "header.redo")
|
||||
; :on-click on-redo}
|
||||
; i/redo]
|
||||
;; [:li.tooltip.tooltip-bottom
|
||||
;; {:alt (tr "header.undo")
|
||||
;; :on-click on-undo}
|
||||
;; i/undo]
|
||||
;; [:li.tooltip.tooltip-bottom
|
||||
;; {:alt (tr "header.redo")
|
||||
;; :on-click on-redo}
|
||||
;; i/redo]
|
||||
[:li.tooltip.tooltip-bottom
|
||||
{:alt (tr "header.download")
|
||||
{:alt (tr "workspace.header.download")
|
||||
;; :on-click on-download
|
||||
}
|
||||
i/download]
|
||||
[:li.tooltip.tooltip-bottom
|
||||
{:alt (tr "header.image")
|
||||
{:alt (tr "workspace.header.image")
|
||||
:on-click on-image}
|
||||
i/image]
|
||||
[:li.tooltip.tooltip-bottom
|
||||
{:alt (tr "header.rules")
|
||||
:class (when (contains? flags :rules) "selected")
|
||||
:on-click (partial toggle :rules)}
|
||||
{:alt (tr "workspace.header.rules")
|
||||
:class (when (contains? layout :rules) "selected")
|
||||
:on-click (partial toggle-layout :rules)}
|
||||
i/ruler]
|
||||
[:li.tooltip.tooltip-bottom
|
||||
{:alt (tr "header.grid")
|
||||
{:alt (tr "workspace.header.grid")
|
||||
:class (when (contains? flags :grid) "selected")
|
||||
:on-click (partial toggle :grid)}
|
||||
i/grid]
|
||||
[:li.tooltip.tooltip-bottom
|
||||
{:alt (tr "header.grid-snap")
|
||||
{:alt (tr "workspace.header.grid-snap")
|
||||
:class (when (contains? flags :grid-snap) "selected")
|
||||
:on-click (partial toggle :grid-snap)}
|
||||
i/grid-snap]]]
|
||||
;; [:li.tooltip.tooltip-bottom
|
||||
;; {:alt (tr "header.align")}
|
||||
;; i/alignment]]
|
||||
;;[:& user]
|
||||
;; [:li.tooltip.tooltip-bottom
|
||||
;; {:alt (tr "header.align")}
|
||||
;; i/alignment]]
|
||||
;; [:& user]
|
||||
[:div.secondary-options
|
||||
[:& zoom-widget]
|
||||
[:a.tooltip.tooltip-bottom.view-mode
|
||||
{:alt (tr "header.view-mode")
|
||||
{:alt (tr "workspace.header.view-mode")
|
||||
;; :on-click #(st/emit! (dw/->OpenView (:id page)))
|
||||
}
|
||||
i/play]]
|
||||
|
|
|
@ -129,13 +129,9 @@
|
|||
(read-string)
|
||||
(swap! local assoc :id))]
|
||||
|
||||
(mf/use-effect
|
||||
{:fn #(do (st/emit! (udi/fetch-collections))
|
||||
(st/emit! (udi/fetch-images nil)))})
|
||||
|
||||
(mf/use-effect
|
||||
{:deps #js [type id]
|
||||
:fn #(st/emit! (udi/fetch-images id))})
|
||||
(mf/use-effect #(st/emit! udi/fetch-collections))
|
||||
(mf/use-effect {:deps #js [(str id)]
|
||||
:fn #(st/emit! (udi/fetch-images id))})
|
||||
|
||||
[:div.lightbox-body.big-lightbox
|
||||
[:h3 (tr "image.import-library")]
|
||||
|
|
|
@ -99,10 +99,10 @@
|
|||
|
||||
(mf/defc controls
|
||||
[{:keys [shape zoom on-click] :as props}]
|
||||
(let [{:keys [x1 y1 width height]} shape
|
||||
(let [{:keys [x y width height]} shape
|
||||
radius (if (> (max width height) handler-size-threshold) 6.0 4.0)]
|
||||
[:g.controls
|
||||
[:rect.main {:x x1 :y y1
|
||||
[:rect.main {:x x :y y
|
||||
:width width
|
||||
:height height
|
||||
:stroke-dasharray (str (/ 8.0 zoom) "," (/ 5 zoom))
|
||||
|
@ -111,42 +111,42 @@
|
|||
[:& control-item {:class "top"
|
||||
:on-click #(on-click :top %)
|
||||
:r (/ radius zoom)
|
||||
:cx (+ x1 (/ width 2))
|
||||
:cy (- y1 2)}]
|
||||
:cx (+ x (/ width 2))
|
||||
:cy (- y 2)}]
|
||||
[:& control-item {:on-click #(on-click :right %)
|
||||
:r (/ radius zoom)
|
||||
:cy (+ y1 (/ height 2))
|
||||
:cx (+ x1 width 1)
|
||||
:cy (+ y (/ height 2))
|
||||
:cx (+ x width 1)
|
||||
:class "right"}]
|
||||
[:& control-item {:on-click #(on-click :bottom %)
|
||||
:r (/ radius zoom)
|
||||
:cx (+ x1 (/ width 2))
|
||||
:cy (+ y1 height 2)
|
||||
:cx (+ x (/ width 2))
|
||||
:cy (+ y height 2)
|
||||
:class "bottom"}]
|
||||
[:& control-item {:on-click #(on-click :left %)
|
||||
:r (/ radius zoom)
|
||||
:cy (+ y1 (/ height 2))
|
||||
:cx (- x1 3)
|
||||
:cy (+ y (/ height 2))
|
||||
:cx (- x 3)
|
||||
:class "left"}]
|
||||
[:& control-item {:on-click #(on-click :top-left %)
|
||||
:r (/ radius zoom)
|
||||
:cx x1
|
||||
:cy y1
|
||||
:cx x
|
||||
:cy y
|
||||
:class "top-left"}]
|
||||
[:& control-item {:on-click #(on-click :top-right %)
|
||||
:r (/ radius zoom)
|
||||
:cx (+ x1 width)
|
||||
:cy y1
|
||||
:cx (+ x width)
|
||||
:cy y
|
||||
:class "top-right"}]
|
||||
[:& control-item {:on-click #(on-click :bottom-left %)
|
||||
:r (/ radius zoom)
|
||||
:cx x1
|
||||
:cy (+ y1 height)
|
||||
:cx x
|
||||
:cy (+ y height)
|
||||
:class "bottom-left"}]
|
||||
[:& control-item {:on-click #(on-click :bottom-right %)
|
||||
:r (/ radius zoom)
|
||||
:cx (+ x1 width)
|
||||
:cy (+ y1 height)
|
||||
:cx (+ x width)
|
||||
:cy (+ y height)
|
||||
:class "bottom-right"}]]))
|
||||
|
||||
;; --- Selection Handlers (Component)
|
||||
|
@ -203,9 +203,9 @@
|
|||
|
||||
(mf/defc text-edition-selection-handlers
|
||||
[{:keys [shape zoom] :as props}]
|
||||
(let [{:keys [x1 y1 width height] :as shape} (geom/selection-rect shape)]
|
||||
(let [{:keys [x y width height] :as shape} (geom/selection-rect shape)]
|
||||
[:g.controls
|
||||
[:rect.main {:x x1 :y y1
|
||||
[:rect.main {:x x :y y
|
||||
:width width
|
||||
:height height
|
||||
;; :stroke-dasharray (str (/ 5.0 zoom) "," (/ 5 zoom))
|
||||
|
|
|
@ -205,7 +205,7 @@
|
|||
:class (when-not collapsed? "inverse")}
|
||||
i/arrow-slide]]
|
||||
[:ul
|
||||
(for [[index shape] shapes]
|
||||
(for [[index shape] (reverse shapes)]
|
||||
[:& layer-item {:shape shape
|
||||
:selected selected
|
||||
:index index
|
||||
|
|
|
@ -9,28 +9,15 @@
|
|||
(:require
|
||||
[lentes.core :as l]
|
||||
[rumext.alpha :as mf]
|
||||
[rumext.core :as mx]
|
||||
[uxbox.builtins.icons :as i]
|
||||
[uxbox.main.data.workspace :as udw]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.ui.shapes.attrs :refer [shape-default-attrs]]
|
||||
|
||||
[uxbox.main.ui.workspace.sidebar.options.rect :as rect]
|
||||
[uxbox.main.ui.workspace.sidebar.options.circle :as circle]
|
||||
[uxbox.main.ui.workspace.sidebar.options.path :as path]
|
||||
[uxbox.main.ui.workspace.sidebar.options.image :as image]
|
||||
[uxbox.main.ui.workspace.sidebar.options.page :as page]
|
||||
|
||||
;; [uxbox.main.ui.workspace.sidebar.options.circle-measures :as options-circlem]
|
||||
;; [uxbox.main.ui.workspace.sidebar.options.fill :as options-fill]
|
||||
;; [uxbox.main.ui.workspace.sidebar.options.icon-measures :as options-iconm]
|
||||
;; [uxbox.main.ui.workspace.sidebar.options.image-measures :as options-imagem]
|
||||
;; [uxbox.main.ui.workspace.sidebar.options.interactions :as options-interactions]
|
||||
;; [uxbox.main.ui.workspace.sidebar.options.rect-measures :as options-rectm]
|
||||
;; [uxbox.main.ui.workspace.sidebar.options.stroke :as options-stroke]
|
||||
;; [uxbox.main.ui.workspace.sidebar.options.text :as options-text]
|
||||
[uxbox.util.data :as data]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.i18n :refer [tr]]))
|
||||
|
||||
;; --- Constants
|
||||
|
@ -44,43 +31,6 @@
|
|||
;; :image [::image-measures]
|
||||
;; ::page [::page-measures ::page-grid-options]})
|
||||
|
||||
;; (def ^:private +menus+
|
||||
;; [{:name "element.measures"
|
||||
;; :id ::icon-measures
|
||||
;; :icon i/infocard
|
||||
;; :comp options-iconm/icon-measures-menu}
|
||||
;; {:name "element.measures"
|
||||
;; :id ::image-measures
|
||||
;; :icon i/infocard
|
||||
;; :comp options-imagem/image-measures-menu}
|
||||
;; {:name "element.measures"
|
||||
;; :id ::rect-measures
|
||||
;; :icon i/infocard
|
||||
;; :comp options-rectm/rect-measures-menu}
|
||||
;; {:name "element.measures"
|
||||
;; :id ::circle-measures
|
||||
;; :icon i/infocard
|
||||
;; :comp options-circlem/circle-measures-menu}
|
||||
;; {:name "element.fill"
|
||||
;; :id ::fill
|
||||
;; :icon i/fill
|
||||
;; :comp options-fill/fill-menu}
|
||||
;; {:name "element.stroke"
|
||||
;; :id ::stroke
|
||||
;; :icon i/stroke
|
||||
;; :comp options-stroke/stroke-menu}
|
||||
;; {:name "element.text"
|
||||
;; :id ::text
|
||||
;; :icon i/text
|
||||
;; :comp options-text/text-menu}
|
||||
;; {:name "element.interactions"
|
||||
;; :id ::interactions
|
||||
;; :icon i/action
|
||||
;; :comp options-interactions/interactions-menu}])
|
||||
|
||||
;; (def ^:private +menus-by-id+
|
||||
;; (data/index-by-id +menus+))
|
||||
|
||||
;; --- Options
|
||||
|
||||
(mf/defc shape-options
|
||||
|
@ -93,6 +43,9 @@
|
|||
(case (:type shape)
|
||||
:rect [:& rect/options {:shape shape}]
|
||||
:circle [:& circle/options {:shape shape}]
|
||||
:path [:& path/options {:shape shape}]
|
||||
:curve [:& path/options {:shape shape}]
|
||||
:image [:& image/options {:shape shape}]
|
||||
nil)]))
|
||||
|
||||
(mf/defc options-toolbox
|
||||
|
@ -102,9 +55,9 @@
|
|||
selected (mf/deref refs/selected-shapes)]
|
||||
[:div.elementa-options.tool-window
|
||||
;; [:div.tool-window-bar
|
||||
;; [:div.tool-window-icon i/options]
|
||||
;; [:span (tr "ds.settings.element-options")]
|
||||
;; [:div.tool-window-close {:on-click close} i/close]]
|
||||
;; [:div.tool-window-icon i/options]
|
||||
;; [:span (tr "ds.settings.element-options")]
|
||||
;; [:div.tool-window-close {:on-click close} i/close]]
|
||||
[:div.tool-window-content
|
||||
[:div.element-options
|
||||
(if (= (count selected) 1)
|
||||
|
|
|
@ -8,126 +8,125 @@
|
|||
(ns uxbox.main.ui.workspace.sidebar.options.circle
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[uxbox.common.data :as d]
|
||||
[uxbox.builtins.icons :as i]
|
||||
[uxbox.main.data.workspace :as udw]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.ui.workspace.sidebar.options.fill :refer [fill-menu]]
|
||||
[uxbox.main.ui.workspace.sidebar.options.stroke :refer [stroke-menu]]
|
||||
[uxbox.util.data :refer (parse-int parse-float read-string)]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.geom.point :as gpt]
|
||||
[uxbox.util.i18n :refer (tr)]
|
||||
[uxbox.util.math :refer (precision-or-0)]))
|
||||
[uxbox.util.i18n :refer [tr]]
|
||||
[uxbox.util.math :as math :refer (precision-or-0)]))
|
||||
|
||||
(mf/defc size-options
|
||||
(mf/defc measures-menu
|
||||
[{:keys [shape] :as props}]
|
||||
[:*
|
||||
[:span (tr "ds.size")]
|
||||
[:div.row-flex
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:placeholder (tr "ds.width")
|
||||
:type "number"
|
||||
:min "0"
|
||||
;; :on-change #(on-size-change % shape :rx)
|
||||
:value (precision-or-0 (:rx shape 0) 2)}]]
|
||||
[:div.lock-size {:class (when (:proportion-lock shape) "selected")
|
||||
;; :on-click #(on-proportion-lock-change % shape)
|
||||
}
|
||||
(if (:proportion-lock shape) i/lock i/unlock)]
|
||||
(let [on-size-change
|
||||
(fn [event attr]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/parse-integer 0))]
|
||||
(st/emit! (udw/update-dimensions (:id shape) {attr value}))))
|
||||
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder (tr "ds.height")
|
||||
:type "number"
|
||||
:min "0"
|
||||
;; :on-change #(on-size-change % shape :ry)
|
||||
:value (precision-or-0 (:ry shape 0) 2)}]]]])
|
||||
on-proportion-lock-change
|
||||
(fn [event]
|
||||
(st/emit! (udw/toggle-shape-proportion-lock (:id shape))))
|
||||
|
||||
(mf/defc position-options
|
||||
[{:keys [shape] :as props}]
|
||||
[:*
|
||||
[:span (tr "ds.position")]
|
||||
[:div.row-flex
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder "cx"
|
||||
:type "number"
|
||||
;; :on-change #(on-position-change % shape :x)
|
||||
:value (precision-or-0 (:cx shape 0) 2)}]]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder "cy"
|
||||
:type "number"
|
||||
;; :on-change #(on-position-change % shape :y)
|
||||
:value (precision-or-0 (:cy shape 0) 2)}]]]])
|
||||
on-size-rx-change #(on-size-change % :rx)
|
||||
on-size-ry-change #(on-size-change % :ry)
|
||||
|
||||
(mf/defc rotation-options
|
||||
[{:keys [shape] :as props}]
|
||||
[:*
|
||||
[:span (tr "ds.rotation")]
|
||||
[:div.row-flex
|
||||
[:input.slidebar
|
||||
{:type "range"
|
||||
:min 0
|
||||
:max 360
|
||||
;; :on-change #(on-rotation-change % shape)
|
||||
:value (:rotation shape 0)}]]
|
||||
on-position-change
|
||||
(fn [event attr]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/parse-integer))
|
||||
point (gpt/point {attr value})]
|
||||
(st/emit! (udw/update-position (:id shape) point))))
|
||||
|
||||
[:div.row-flex
|
||||
[:div.input-element.degrees
|
||||
[:input.input-text
|
||||
{:placeholder ""
|
||||
:type "number"
|
||||
:min 0
|
||||
:max 360
|
||||
;; :on-change #(on-rotation-change % shape)
|
||||
:value (precision-or-0 (:rotation shape 0) 2)}]]
|
||||
[:input.input-text
|
||||
{:style {:visibility "hidden"}}]]])
|
||||
on-pos-cx-change #(on-position-change % :x)
|
||||
on-pos-cy-change #(on-position-change % :y)
|
||||
|
||||
(mf/defc measures-options
|
||||
[{:keys [shape] :as props}]
|
||||
[:div.element-set
|
||||
[:div.element-set-title (tr "element.measures")]
|
||||
[:div.element-set-content
|
||||
[:& size-options {:shape shape}]
|
||||
[:& position-options {:shape shape}]
|
||||
[:& rotation-options {:shape shape}]]])
|
||||
on-rotation-change
|
||||
(fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/parse-integer 0))]
|
||||
(st/emit! (udw/update-shape (:id shape) {:rotation value}))))
|
||||
|
||||
;; (defn- on-size-change
|
||||
;; [event shape attr]
|
||||
;; (let [value (dom/event->value event)
|
||||
;; value (parse-int value 0)
|
||||
;; sid (:id shape)
|
||||
;; props {attr value}]
|
||||
;; (st/emit! (udw/update-dimensions sid props))))
|
||||
on-radius-change
|
||||
(fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/parse-double 0))]
|
||||
(st/emit! (udw/update-shape (:id shape) {:rx value :ry value}))))]
|
||||
|
||||
;; (defn- on-rotation-change
|
||||
;; [event shape]
|
||||
;; (let [value (dom/event->value event)
|
||||
;; value (parse-int value 0)
|
||||
;; sid (:id shape)]
|
||||
;; (st/emit! (udw/update-shape-attrs sid {:rotation value}))))
|
||||
[:div.element-set
|
||||
[:div.element-set-title (tr "workspace.options.measures")]
|
||||
[:div.element-set-content
|
||||
|
||||
;; (defn- on-position-change
|
||||
;; [event shape attr]
|
||||
;; (let [value (dom/event->value event)
|
||||
;; value (parse-int value nil)
|
||||
;; sid (:id shape)
|
||||
;; point (gpt/point {attr value})]
|
||||
;; (st/emit! (udw/update-position sid point))))
|
||||
;; SIZE
|
||||
[:span (tr "workspace.options.size")]
|
||||
[:div.row-flex
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:type "number"
|
||||
:min "0"
|
||||
:on-change on-size-rx-change
|
||||
:value (-> (:rx shape)
|
||||
(math/precision 2)
|
||||
(d/coalesce-str "0"))}]]
|
||||
[:div.lock-size {:class (when (:proportion-lock shape) "selected")
|
||||
:on-click on-proportion-lock-change}
|
||||
(if (:proportion-lock shape)
|
||||
i/lock
|
||||
i/unlock)]
|
||||
|
||||
;; (defn- on-proportion-lock-change
|
||||
;; [event shape]
|
||||
;; (if (:proportion-lock shape)
|
||||
;; (st/emit! (udw/unlock-proportions (:id shape)))
|
||||
;; (st/emit! (udw/lock-proportions (:id shape)))))
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:type "number"
|
||||
:min "0"
|
||||
:on-change on-size-ry-change
|
||||
:value (-> (:ry shape)
|
||||
(math/precision 2)
|
||||
(d/coalesce-str "0"))}]]]
|
||||
|
||||
;; POSITION
|
||||
[:span (tr "workspace.options.position")]
|
||||
[:div.row-flex
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:type "number"
|
||||
:on-change on-pos-cx-change
|
||||
:value (-> (:cx shape)
|
||||
(math/precision 2)
|
||||
(d/coalesce-str "0"))}]]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:type "number"
|
||||
:on-change on-pos-cy-change
|
||||
:value (-> (:cy shape)
|
||||
(math/precision 2)
|
||||
(d/coalesce-str "0"))}]]]
|
||||
;; ROTATION & RADIUS
|
||||
[:span (tr "workspace.options.rotation-radius")]
|
||||
[:div.row-flex
|
||||
[:div.input-element.degrees
|
||||
[:input.input-text {:placeholder ""
|
||||
:type "number"
|
||||
:min 0
|
||||
:max 360
|
||||
:on-change on-rotation-change
|
||||
:value (-> (:rotation shape)
|
||||
(math/precision 2)
|
||||
(d/coalesce-str "0"))}]]
|
||||
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:type "number"
|
||||
:on-change on-radius-change
|
||||
:value (-> (:rx shape)
|
||||
(math/precision 2)
|
||||
(d/coalesce-str "0"))}]]]]]))
|
||||
|
||||
(mf/defc options
|
||||
[{:keys [shape] :as props}]
|
||||
[:div
|
||||
[:& measures-options {:shape shape}]
|
||||
[:& measures-menu {:shape shape}]
|
||||
[:& fill-menu {:shape shape}]
|
||||
[:& stroke-menu {:shape shape}]])
|
||||
|
|
|
@ -1,117 +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-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
;; Copyright (c) 2015-2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.main.ui.workspace.sidebar.options.circle-measures
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[uxbox.builtins.icons :as i]
|
||||
[uxbox.main.data.workspace :as udw]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.util.data :refer (parse-int parse-float read-string)]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.geom.point :as gpt]
|
||||
[uxbox.util.i18n :refer (tr)]
|
||||
[uxbox.util.math :refer (precision-or-0)]))
|
||||
|
||||
(declare on-size-change)
|
||||
(declare on-rotation-change)
|
||||
(declare on-position-change)
|
||||
(declare on-proportion-lock-change)
|
||||
|
||||
(mf/defc circle-measures-menu
|
||||
[{:keys [menu shape] :as props}]
|
||||
[:div.element-set {:key (str (:id menu))}
|
||||
[:div.element-set-title (:name menu)]
|
||||
[:div.element-set-content
|
||||
;; SLIDEBAR FOR ROTATION AND OPACITY
|
||||
[:span (tr "ds.size")]
|
||||
[:div.row-flex
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder (tr "ds.width")
|
||||
:type "number"
|
||||
:min "0"
|
||||
:value (precision-or-0 (:rx shape 0) 2)
|
||||
:on-change #(on-size-change % shape :rx)}]]
|
||||
[:div.lock-size {:class (when (:proportion-lock shape) "selected")
|
||||
:on-click #(on-proportion-lock-change % shape)}
|
||||
(if (:proportion-lock shape) i/lock i/unlock)]
|
||||
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder (tr "ds.height")
|
||||
:type "number"
|
||||
:min "0"
|
||||
:value (precision-or-0 (:ry shape 0) 2)
|
||||
:on-change #(on-size-change % shape :ry)}]]]
|
||||
|
||||
[:span (tr "ds.position")]
|
||||
[:div.row-flex
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder "cx"
|
||||
:type "number"
|
||||
:value (precision-or-0 (:cx shape 0) 2)
|
||||
:on-change #(on-position-change % shape :x)}]]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder "cy"
|
||||
:type "number"
|
||||
:value (precision-or-0 (:cy shape 0) 2)
|
||||
:on-change #(on-position-change % shape :y)}]]]
|
||||
|
||||
[:span (tr "ds.rotation")]
|
||||
[:div.row-flex
|
||||
[:input.slidebar
|
||||
{:type "range"
|
||||
:min 0
|
||||
:max 360
|
||||
:value (:rotation shape 0)
|
||||
:on-change #(on-rotation-change % shape)}]]
|
||||
|
||||
[:div.row-flex
|
||||
[:div.input-element.degrees
|
||||
[:input.input-text
|
||||
{:placeholder ""
|
||||
:type "number"
|
||||
:min 0
|
||||
:max 360
|
||||
:value (precision-or-0 (:rotation shape 0) 2)
|
||||
:on-change #(on-rotation-change % shape)}]]
|
||||
[:input.input-text
|
||||
{:style {:visibility "hidden"}}]]]])
|
||||
|
||||
(defn- on-size-change
|
||||
[event shape attr]
|
||||
(let [value (dom/event->value event)
|
||||
value (parse-int value 0)
|
||||
sid (:id shape)
|
||||
props {attr value}]
|
||||
(st/emit! (udw/update-dimensions sid props))))
|
||||
|
||||
(defn- on-rotation-change
|
||||
[event shape]
|
||||
(let [value (dom/event->value event)
|
||||
value (parse-int value 0)
|
||||
sid (:id shape)]
|
||||
(st/emit! (udw/update-shape-attrs sid {:rotation value}))))
|
||||
|
||||
(defn- on-position-change
|
||||
[event shape attr]
|
||||
(let [value (dom/event->value event)
|
||||
value (parse-int value nil)
|
||||
sid (:id shape)
|
||||
point (gpt/point {attr value})]
|
||||
(st/emit! (udw/update-position sid point))))
|
||||
|
||||
(defn- on-proportion-lock-change
|
||||
[event shape]
|
||||
(if (:proportion-lock shape)
|
||||
(st/emit! (udw/unlock-proportions (:id shape)))
|
||||
(st/emit! (udw/lock-proportions (:id shape)))))
|
||||
|
|
@ -9,31 +9,33 @@
|
|||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[uxbox.builtins.icons :as i]
|
||||
[uxbox.common.data :as d]
|
||||
[uxbox.main.data.workspace :as udw]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.ui.modal :as modal]
|
||||
[uxbox.main.ui.workspace.colorpicker :refer [colorpicker-modal]]
|
||||
[uxbox.util.data :refer [parse-float]]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.i18n :refer [tr]]))
|
||||
|
||||
(mf/defc fill-menu
|
||||
[{:keys [shape] :as props}]
|
||||
(letfn [(change-attrs [attrs]
|
||||
(st/emit! (udw/update-shape-attrs (:id shape) attrs)))
|
||||
(letfn [(update-shape! [attr value]
|
||||
(st/emit! (udw/update-shape (:id shape) {attr value})))
|
||||
(on-color-change [event]
|
||||
(let [value (dom/event->value event)]
|
||||
(change-attrs {:fill-color value})))
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value))]
|
||||
(update-shape! :fill-color value)))
|
||||
(on-opacity-change [event]
|
||||
(let [value (dom/event->value event)
|
||||
value (parse-float value 1)
|
||||
value (/ value 10000)]
|
||||
(change-attrs {:fill-opacity value})))
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/parse-double 1)
|
||||
(/ 10000))]
|
||||
(update-shape! :fill-opacity value)))
|
||||
(show-color-picker [event]
|
||||
(let [x (.-clientX event)
|
||||
y (.-clientY event)
|
||||
props {:x x :y y
|
||||
:on-change #(change-attrs {:fill-color %})
|
||||
:on-change #(update-shape! :fill-color %)
|
||||
:default "#ffffff"
|
||||
:value (:fill-color shape)
|
||||
:transparent? true}]
|
||||
|
@ -42,10 +44,10 @@
|
|||
[:div.element-set-title (tr "element.fill")]
|
||||
[:div.element-set-content
|
||||
|
||||
[:span (tr "ds.color")]
|
||||
[:span (tr "workspace.options.color")]
|
||||
[:div.row-flex.color-data
|
||||
[:span.color-th
|
||||
{:style {:background-color (:fill-color shape)}
|
||||
{:style {:background-color (:fill-color shape "#000000")}
|
||||
:on-click show-color-picker}]
|
||||
[:div.color-info
|
||||
[:input
|
||||
|
@ -53,7 +55,7 @@
|
|||
:value (:fill-color shape "")}]]]
|
||||
|
||||
;; SLIDEBAR FOR ROTATION AND OPACITY
|
||||
[:span (tr "ds.opacity")]
|
||||
[:span (tr "workspace.options.opacity")]
|
||||
[:div.row-flex
|
||||
[:input.slidebar
|
||||
{:type "range"
|
||||
|
|
|
@ -95,7 +95,7 @@
|
|||
[event shape]
|
||||
(let [value (dom/event->value event)
|
||||
value (parse-int value 0)]
|
||||
(st/emit! (udw/update-shape-attrs (:id shape) {:rotation value}))))
|
||||
#_(st/emit! (udw/update-shape-attrs (:id shape) {:rotation value}))))
|
||||
|
||||
(defn- on-position-change
|
||||
[event shape attr]
|
||||
|
@ -107,7 +107,7 @@
|
|||
|
||||
(defn- on-proportion-lock-change
|
||||
[event shape]
|
||||
(if (:proportion-lock shape)
|
||||
#_(if (:proportion-lock shape)
|
||||
(st/emit! (udw/unlock-proportions (:id shape)))
|
||||
(st/emit! (udw/lock-proportions (:id shape)))))
|
||||
|
||||
|
|
|
@ -1,134 +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 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.main.ui.workspace.sidebar.options.image-measures
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[uxbox.builtins.icons :as i]
|
||||
[uxbox.main.data.workspace :as dw]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.util.data :refer (parse-int parse-float read-string)]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.geom.point :as gpt]
|
||||
[uxbox.util.i18n :refer (tr)]
|
||||
[uxbox.util.math :refer (precision-or-0)]))
|
||||
|
||||
(declare on-size-change)
|
||||
(declare on-rotation-change)
|
||||
(declare on-opacity-change)
|
||||
(declare on-position-change)
|
||||
(declare on-proportion-lock-change)
|
||||
|
||||
(mf/defc image-measures-menu
|
||||
[{:keys [menu shape] :as props}]
|
||||
(let [size (geom/size shape)]
|
||||
[:div.element-set
|
||||
[:div.element-set-title (:name menu)]
|
||||
[:div.element-set-content
|
||||
;; SLIDEBAR FOR ROTATION AND OPACITY
|
||||
[:span (tr "ds.size")]
|
||||
[:div.row-flex
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder (tr "ds.width")
|
||||
:type "number"
|
||||
:min "0"
|
||||
:value (precision-or-0 (:width size) 2)
|
||||
:on-change #(on-size-change % shape :width)}]]
|
||||
[:div.lock-size
|
||||
{:class (when (:proportion-lock shape) "selected")
|
||||
:on-click #(on-proportion-lock-change % shape)}
|
||||
(if (:proportion-lock shape) i/lock i/unlock)]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder (tr "ds.height")
|
||||
:type "number"
|
||||
:min "0"
|
||||
:value (precision-or-0 (:height size) 2)
|
||||
:on-change #(on-size-change % shape :height)}]]]
|
||||
|
||||
[:span (tr "ds.position")]
|
||||
[:div.row-flex
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder "X"
|
||||
:type "number"
|
||||
:value (precision-or-0 (:x1 shape 0) 2)
|
||||
:on-change #(on-position-change % shape :x)}]]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder "Y"
|
||||
:type "number"
|
||||
:value (precision-or-0 (:y1 shape 0) 2)
|
||||
:on-change #(on-position-change % shape :y)}]]]
|
||||
|
||||
;; [:span (tr "ds.rotation")]
|
||||
;; [:div.row-flex
|
||||
;; [:input.slidebar
|
||||
;; {:type "range"
|
||||
;; :min 0
|
||||
;; :max 360
|
||||
;; :value (:rotation shape 0)
|
||||
;; :on-change on-rotation-change}]]
|
||||
|
||||
;; [:div.row-flex
|
||||
;; [:div.input-element.degrees
|
||||
;; [:input.input-text
|
||||
;; {:placeholder ""
|
||||
;; :type "number"
|
||||
;; :min 0
|
||||
;; :max 360
|
||||
;; :value (precision-or-0 (:rotation shape 0) 2)
|
||||
;; :on-change on-rotation-change
|
||||
;; }]]
|
||||
;; [:input.input-text
|
||||
;; {:style {:visibility "hidden"}}]]
|
||||
|
||||
|
||||
[:span (tr "ds.opacity")]
|
||||
[:div.row-flex
|
||||
[:input.slidebar
|
||||
{:type "range"
|
||||
:min "0"
|
||||
:max "10000"
|
||||
:value (* 10000 (:opacity shape 1))
|
||||
:step "1"
|
||||
:on-change #(on-opacity-change % shape)}]]]]))
|
||||
|
||||
(defn- on-size-change
|
||||
[event shape attr]
|
||||
(let [value (dom/event->value event)
|
||||
value (parse-int value 0)
|
||||
props {attr value}]
|
||||
(st/emit! (dw/update-dimensions (:id shape) props))))
|
||||
|
||||
(defn- on-rotation-change
|
||||
[event shape]
|
||||
(let [value (dom/event->value event)
|
||||
value (parse-int value 0)]
|
||||
(st/emit! (dw/update-shape-attrs (:id shape) {:rotation value}))))
|
||||
|
||||
(defn- on-opacity-change
|
||||
[event shape]
|
||||
(let [value (dom/event->value event)
|
||||
value (parse-float value 1)
|
||||
value (/ value 10000)]
|
||||
(st/emit! (dw/update-shape-attrs (:id shape) {:opacity value}))))
|
||||
|
||||
(defn- on-position-change
|
||||
[event shape attr]
|
||||
(let [value (dom/event->value event)
|
||||
value (parse-int value nil)
|
||||
point (gpt/point {attr value})]
|
||||
(st/emit! (dw/update-position (:id shape) point))))
|
||||
|
||||
(defn- on-proportion-lock-change
|
||||
[event shape]
|
||||
(if (:proportion-lock shape)
|
||||
(st/emit! (dw/unlock-proportions (:id shape)))
|
||||
(st/emit! (dw/lock-proportions (:id shape)))))
|
|
@ -22,452 +22,452 @@
|
|||
|
||||
;; --- Helpers
|
||||
|
||||
(defn- on-change
|
||||
([form attr event]
|
||||
(dom/prevent-default event)
|
||||
(let [value (dom/event->value event)
|
||||
value (read-string value)]
|
||||
(swap! form assoc attr value)))
|
||||
([form attr keep event]
|
||||
(let [data (select-keys @form keep)]
|
||||
(reset! form data)
|
||||
(on-change form attr event))))
|
||||
;; (defn- on-change
|
||||
;; ([form attr event]
|
||||
;; (dom/prevent-default event)
|
||||
;; (let [value (dom/event->value event)
|
||||
;; value (read-string value)]
|
||||
;; (swap! form assoc attr value)))
|
||||
;; ([form attr keep event]
|
||||
;; (let [data (select-keys @form keep)]
|
||||
;; (reset! form data)
|
||||
;; (on-change form attr event))))
|
||||
|
||||
;; --- Interactions List
|
||||
;; ;; --- Interactions List
|
||||
|
||||
(defn- translate-trigger-name
|
||||
[trigger]
|
||||
(case trigger
|
||||
:click "Click"
|
||||
:doubleclick "Double Click"
|
||||
:rightclick "Right Click"
|
||||
:hover "Hover"
|
||||
:mousein "Mouse In"
|
||||
:mouseout "Mouse Out"
|
||||
;; :swiperight "Swipe Right"
|
||||
;; :swipeleft "Swipe Left"
|
||||
;; :swipedown "Swipe Down"
|
||||
;; :touchandhold "Touch and Hold"
|
||||
;; :holdrelease "Hold release"
|
||||
(pr-str trigger)))
|
||||
;; (defn- translate-trigger-name
|
||||
;; [trigger]
|
||||
;; (case trigger
|
||||
;; :click "Click"
|
||||
;; :doubleclick "Double Click"
|
||||
;; :rightclick "Right Click"
|
||||
;; :hover "Hover"
|
||||
;; :mousein "Mouse In"
|
||||
;; :mouseout "Mouse Out"
|
||||
;; ;; :swiperight "Swipe Right"
|
||||
;; ;; :swipeleft "Swipe Left"
|
||||
;; ;; :swipedown "Swipe Down"
|
||||
;; ;; :touchandhold "Touch and Hold"
|
||||
;; ;; :holdrelease "Hold release"
|
||||
;; (pr-str trigger)))
|
||||
|
||||
(mf/defc interactions-list
|
||||
[{:keys [shape form] :as props}]
|
||||
(letfn [(on-edit [item event]
|
||||
(dom/prevent-default event)
|
||||
(reset! form item))
|
||||
(delete [item]
|
||||
(let [sid (:id shape)
|
||||
id (:id item)]
|
||||
(st/emit! (dw/delete-interaction sid id))))
|
||||
(on-delete [item event]
|
||||
(dom/prevent-default event)
|
||||
(let [delete (partial delete item)]
|
||||
(udl/open! :confirm {:on-accept delete})))]
|
||||
[:ul.element-list
|
||||
(for [item (vals (:interactions shape))
|
||||
:let [key (pr-str (:id item))]]
|
||||
[:li {:key key}
|
||||
[:div.list-icon i/action]
|
||||
[:span (translate-trigger-name (:trigger item))]
|
||||
[:div.list-actions
|
||||
[:a {:on-click (partial on-edit item)} i/pencil]
|
||||
[:a {:on-click (partial on-delete item)} i/trash]]])]))
|
||||
;; (mf/defc interactions-list
|
||||
;; [{:keys [shape form] :as props}]
|
||||
;; (letfn [(on-edit [item event]
|
||||
;; (dom/prevent-default event)
|
||||
;; (reset! form item))
|
||||
;; (delete [item]
|
||||
;; (let [sid (:id shape)
|
||||
;; id (:id item)]
|
||||
;; (st/emit! (dw/delete-interaction sid id))))
|
||||
;; (on-delete [item event]
|
||||
;; (dom/prevent-default event)
|
||||
;; (let [delete (partial delete item)]
|
||||
;; (udl/open! :confirm {:on-accept delete})))]
|
||||
;; [:ul.element-list
|
||||
;; (for [item (vals (:interactions shape))
|
||||
;; :let [key (pr-str (:id item))]]
|
||||
;; [:li {:key key}
|
||||
;; [:div.list-icon i/action]
|
||||
;; [:span (translate-trigger-name (:trigger item))]
|
||||
;; [:div.list-actions
|
||||
;; [:a {:on-click (partial on-edit item)} i/pencil]
|
||||
;; [:a {:on-click (partial on-delete item)} i/trash]]])]))
|
||||
|
||||
;; --- Trigger Input
|
||||
;; ;; --- Trigger Input
|
||||
|
||||
(mf/defc trigger-input
|
||||
[{:keys [form] :as props}]
|
||||
;; (mf/use-effect
|
||||
;; {:init #(when-not (:trigger @form) (swap! form assoc :trigger :click))
|
||||
;; :deps true})
|
||||
[:div
|
||||
[:span "Trigger"]
|
||||
[:div.row-flex
|
||||
[:select.input-select {:placeholder "Choose a trigger"
|
||||
:on-change (partial on-change form :trigger)
|
||||
:value (pr-str (:trigger @form))}
|
||||
[:option {:value ":click"} "Click"]
|
||||
[:option {:value ":doubleclick"} "Double-click"]
|
||||
[:option {:value ":rightclick"} "Right-click"]
|
||||
[:option {:value ":hover"} "Hover"]
|
||||
[:option {:value ":mousein"} "Mouse in"]
|
||||
[:option {:value ":mouseout"} "Mouse out"]
|
||||
#_[:option {:value ":swiperight"} "Swipe right"]
|
||||
#_[:option {:value ":swipeleft"} "Swipe left"]
|
||||
#_[:option {:value ":swipedown"} "Swipe dpwn"]
|
||||
#_[:option {:value ":touchandhold"} "Touch and hold"]
|
||||
#_[:option {:value ":holdrelease"} "Hold release"]
|
||||
#_[:option {:value ":keypress"} "Key press"]
|
||||
#_[:option {:value ":pageisloaded"} "Page is loaded"]
|
||||
#_[:option {:value ":windowscroll"} "Window is scrolled to"]]]])
|
||||
;; (mf/defc trigger-input
|
||||
;; [{:keys [form] :as props}]
|
||||
;; ;; (mf/use-effect
|
||||
;; ;; {:init #(when-not (:trigger @form) (swap! form assoc :trigger :click))
|
||||
;; ;; :deps true})
|
||||
;; [:div
|
||||
;; [:span "Trigger"]
|
||||
;; [:div.row-flex
|
||||
;; [:select.input-select {:placeholder "Choose a trigger"
|
||||
;; :on-change (partial on-change form :trigger)
|
||||
;; :value (pr-str (:trigger @form))}
|
||||
;; [:option {:value ":click"} "Click"]
|
||||
;; [:option {:value ":doubleclick"} "Double-click"]
|
||||
;; [:option {:value ":rightclick"} "Right-click"]
|
||||
;; [:option {:value ":hover"} "Hover"]
|
||||
;; [:option {:value ":mousein"} "Mouse in"]
|
||||
;; [:option {:value ":mouseout"} "Mouse out"]
|
||||
;; #_[:option {:value ":swiperight"} "Swipe right"]
|
||||
;; #_[:option {:value ":swipeleft"} "Swipe left"]
|
||||
;; #_[:option {:value ":swipedown"} "Swipe dpwn"]
|
||||
;; #_[:option {:value ":touchandhold"} "Touch and hold"]
|
||||
;; #_[:option {:value ":holdrelease"} "Hold release"]
|
||||
;; #_[:option {:value ":keypress"} "Key press"]
|
||||
;; #_[:option {:value ":pageisloaded"} "Page is loaded"]
|
||||
;; #_[:option {:value ":windowscroll"} "Window is scrolled to"]]]])
|
||||
|
||||
;; --- URL Input
|
||||
;; ;; --- URL Input
|
||||
|
||||
(mf/defc url-input
|
||||
[form]
|
||||
[:div
|
||||
[:span "Url"]
|
||||
[:div.row-flex
|
||||
[:input.input-text
|
||||
{:placeholder "http://"
|
||||
:on-change (partial on-change form :url)
|
||||
:value (:url @form "")
|
||||
:type "url"}]]])
|
||||
|
||||
;; --- Elements Input
|
||||
|
||||
(defn- collect-shapes
|
||||
[state page]
|
||||
(let [shapes-by-id (:shapes state)
|
||||
shapes (get-in state [:pages page :shapes])]
|
||||
(letfn [(resolve-shape [acc id]
|
||||
(let [shape (get shapes-by-id id)]
|
||||
(if (= (:type shape) :group)
|
||||
(reduce resolve-shape (conj acc shape) (:items shape))
|
||||
(conj acc shape))))]
|
||||
(reduce resolve-shape [] shapes))))
|
||||
|
||||
(mf/defc elements-input
|
||||
[{:keys [page-id form] :as props}]
|
||||
(let [shapes (collect-shapes @st/state page-id)]
|
||||
[:div
|
||||
[:span "Element"]
|
||||
[:div.row-flex
|
||||
[:select.input-select
|
||||
{:placeholder "Choose an element"
|
||||
:on-change (partial on-change form :element)
|
||||
:value (pr-str (:element @form))}
|
||||
[:option {:value "nil"} "---"]
|
||||
(for [shape shapes
|
||||
:let [key (pr-str (:id shape))]]
|
||||
[:option {:key key :value key} (:name shape)])]]]))
|
||||
|
||||
;; --- Page Input
|
||||
|
||||
(mf/defc pages-input
|
||||
[form-ref path]
|
||||
;; FIXME: react on ref
|
||||
#_(let [pages (mx/react refs/selected-project-pages)]
|
||||
(when (and (not (:page @form-ref))
|
||||
(pos? (count pages)))
|
||||
(swap! form-ref assoc :page (:id (first pages))))
|
||||
[:div
|
||||
[:span "Page"]
|
||||
[:div.row-flex
|
||||
[:select.input-select {:placeholder "Choose a page"
|
||||
:on-change (partial on-change form-ref :page)
|
||||
:value (pr-str (:page @form-ref))}
|
||||
(for [page pages
|
||||
:let [key (pr-str (:id page))]]
|
||||
[:option {:key key :value key} (:name page)])]]]))
|
||||
|
||||
;; --- Animation
|
||||
|
||||
(mf/defc animation-input
|
||||
[{:keys [form] :as props}]
|
||||
(when-not (:action @form)
|
||||
(swap! form assoc :animation :none))
|
||||
[:div
|
||||
[:span "Animation"]
|
||||
[:div.row-flex
|
||||
[:select.input-select
|
||||
{:placeholder "Animation"
|
||||
:on-change (partial on-change form :animation)
|
||||
:value (pr-str (:animation @form))}
|
||||
[:option {:value ":none"} "None"]
|
||||
[:option {:value ":fade"} "Fade"]
|
||||
[:option {:value ":slide"} "Slide"]]]])
|
||||
|
||||
;; --- MoveTo Input
|
||||
|
||||
(mf/defc moveto-input
|
||||
[{:keys [form] :as props}]
|
||||
(when-not (:moveto-x @form)
|
||||
(swap! form assoc :moveto-x 0))
|
||||
(when-not (:moveto-y @form)
|
||||
(swap! form assoc :moveto-y 0))
|
||||
[:div
|
||||
[:span "Move to position"]
|
||||
[:div.row-flex
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder "X"
|
||||
:on-change (partial on-change form :moveto-x)
|
||||
:type "number"
|
||||
:value (:moveto-x @form "")}]]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder "Y"
|
||||
:on-change (partial on-change form :moveto-y)
|
||||
:type "number"
|
||||
:value (:moveto-y @form "")}]]]])
|
||||
|
||||
;; --- MoveBy Input
|
||||
|
||||
(mf/defc moveby-input
|
||||
[{:keys [form] :as props}]
|
||||
(when-not (:moveby-x @form)
|
||||
(swap! form assoc :moveby-x 0))
|
||||
(when-not (:moveby-y @form)
|
||||
(swap! form assoc :moveby-y 0))
|
||||
[:div
|
||||
[:span "Move to position"]
|
||||
[:div.row-flex
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder "X"
|
||||
:on-change (partial on-change form :moveby-x)
|
||||
:type "number"
|
||||
:value (:moveby-x @form "")}]]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder "Y"
|
||||
:on-change (partial on-change form :moveby-y)
|
||||
:type "number"
|
||||
:value (:moveby-y @form "")}]]]])
|
||||
|
||||
;; --- Opacity Input
|
||||
|
||||
(mf/defc opacity-input
|
||||
[{:keys [form] :as props}]
|
||||
(when-not (:opacity @form)
|
||||
(swap! form assoc :opacity 100))
|
||||
[:div
|
||||
[:span "Opacity"]
|
||||
[:div.row-flex
|
||||
[:div.input-element.percentail
|
||||
[:input.input-text
|
||||
{:placeholder "%"
|
||||
:on-change (partial on-change form :opacity)
|
||||
:min "0"
|
||||
:max "100"
|
||||
:type "number"
|
||||
:value (:opacity @form "")}]]]])
|
||||
|
||||
;; --- Rotate Input
|
||||
|
||||
;; (mx/defc rotate-input
|
||||
;; (mf/defc url-input
|
||||
;; [form]
|
||||
;; [:div
|
||||
;; [:span "Rotate (dg)"]
|
||||
;; [:span "Url"]
|
||||
;; [:div.row-flex
|
||||
;; [:div.input-element.degrees
|
||||
;; [:input.input-text
|
||||
;; {:placeholder "http://"
|
||||
;; :on-change (partial on-change form :url)
|
||||
;; :value (:url @form "")
|
||||
;; :type "url"}]]])
|
||||
|
||||
;; ;; --- Elements Input
|
||||
|
||||
;; (defn- collect-shapes
|
||||
;; [state page]
|
||||
;; (let [shapes-by-id (:shapes state)
|
||||
;; shapes (get-in state [:pages page :shapes])]
|
||||
;; (letfn [(resolve-shape [acc id]
|
||||
;; (let [shape (get shapes-by-id id)]
|
||||
;; (if (= (:type shape) :group)
|
||||
;; (reduce resolve-shape (conj acc shape) (:items shape))
|
||||
;; (conj acc shape))))]
|
||||
;; (reduce resolve-shape [] shapes))))
|
||||
|
||||
;; (mf/defc elements-input
|
||||
;; [{:keys [page-id form] :as props}]
|
||||
;; (let [shapes (collect-shapes @st/state page-id)]
|
||||
;; [:div
|
||||
;; [:span "Element"]
|
||||
;; [:div.row-flex
|
||||
;; [:select.input-select
|
||||
;; {:placeholder "Choose an element"
|
||||
;; :on-change (partial on-change form :element)
|
||||
;; :value (pr-str (:element @form))}
|
||||
;; [:option {:value "nil"} "---"]
|
||||
;; (for [shape shapes
|
||||
;; :let [key (pr-str (:id shape))]]
|
||||
;; [:option {:key key :value key} (:name shape)])]]]))
|
||||
|
||||
;; ;; --- Page Input
|
||||
|
||||
;; (mf/defc pages-input
|
||||
;; [form-ref path]
|
||||
;; ;; FIXME: react on ref
|
||||
;; #_(let [pages (mx/react refs/selected-project-pages)]
|
||||
;; (when (and (not (:page @form-ref))
|
||||
;; (pos? (count pages)))
|
||||
;; (swap! form-ref assoc :page (:id (first pages))))
|
||||
;; [:div
|
||||
;; [:span "Page"]
|
||||
;; [:div.row-flex
|
||||
;; [:select.input-select {:placeholder "Choose a page"
|
||||
;; :on-change (partial on-change form-ref :page)
|
||||
;; :value (pr-str (:page @form-ref))}
|
||||
;; (for [page pages
|
||||
;; :let [key (pr-str (:id page))]]
|
||||
;; [:option {:key key :value key} (:name page)])]]]))
|
||||
|
||||
;; ;; --- Animation
|
||||
|
||||
;; (mf/defc animation-input
|
||||
;; [{:keys [form] :as props}]
|
||||
;; (when-not (:action @form)
|
||||
;; (swap! form assoc :animation :none))
|
||||
;; [:div
|
||||
;; [:span "Animation"]
|
||||
;; [:div.row-flex
|
||||
;; [:select.input-select
|
||||
;; {:placeholder "Animation"
|
||||
;; :on-change (partial on-change form :animation)
|
||||
;; :value (pr-str (:animation @form))}
|
||||
;; [:option {:value ":none"} "None"]
|
||||
;; [:option {:value ":fade"} "Fade"]
|
||||
;; [:option {:value ":slide"} "Slide"]]]])
|
||||
|
||||
;; ;; --- MoveTo Input
|
||||
|
||||
;; (mf/defc moveto-input
|
||||
;; [{:keys [form] :as props}]
|
||||
;; (when-not (:moveto-x @form)
|
||||
;; (swap! form assoc :moveto-x 0))
|
||||
;; (when-not (:moveto-y @form)
|
||||
;; (swap! form assoc :moveto-y 0))
|
||||
;; [:div
|
||||
;; [:span "Move to position"]
|
||||
;; [:div.row-flex
|
||||
;; [:div.input-element.pixels
|
||||
;; [:input.input-text
|
||||
;; {:placeholder "dg"
|
||||
;; :on-change (partial on-change form :rotation)
|
||||
;; {:placeholder "X"
|
||||
;; :on-change (partial on-change form :moveto-x)
|
||||
;; :type "number"
|
||||
;; :value (:rotation @form "")}]]]])
|
||||
;; :value (:moveto-x @form "")}]]
|
||||
;; [:div.input-element.pixels
|
||||
;; [:input.input-text
|
||||
;; {:placeholder "Y"
|
||||
;; :on-change (partial on-change form :moveto-y)
|
||||
;; :type "number"
|
||||
;; :value (:moveto-y @form "")}]]]])
|
||||
|
||||
;; --- Resize Input
|
||||
;; ;; --- MoveBy Input
|
||||
|
||||
(mf/defc resize-input
|
||||
[{:keys [form] :as props}]
|
||||
[:div
|
||||
[:span "Resize"]
|
||||
[:div.row-flex
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder "Width"
|
||||
:on-change (partial on-change form :resize-width)
|
||||
:type "number"
|
||||
:value (:resize-width @form "")}]]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder "Height"
|
||||
:on-change (partial on-change form :resize-height)
|
||||
:type "number"
|
||||
:value (:resize-height @form "")}]]]])
|
||||
;; (mf/defc moveby-input
|
||||
;; [{:keys [form] :as props}]
|
||||
;; (when-not (:moveby-x @form)
|
||||
;; (swap! form assoc :moveby-x 0))
|
||||
;; (when-not (:moveby-y @form)
|
||||
;; (swap! form assoc :moveby-y 0))
|
||||
;; [:div
|
||||
;; [:span "Move to position"]
|
||||
;; [:div.row-flex
|
||||
;; [:div.input-element.pixels
|
||||
;; [:input.input-text
|
||||
;; {:placeholder "X"
|
||||
;; :on-change (partial on-change form :moveby-x)
|
||||
;; :type "number"
|
||||
;; :value (:moveby-x @form "")}]]
|
||||
;; [:div.input-element.pixels
|
||||
;; [:input.input-text
|
||||
;; {:placeholder "Y"
|
||||
;; :on-change (partial on-change form :moveby-y)
|
||||
;; :type "number"
|
||||
;; :value (:moveby-y @form "")}]]]])
|
||||
|
||||
;; --- Color Input
|
||||
;; ;; --- Opacity Input
|
||||
|
||||
(mf/defc colorpicker
|
||||
[{:keys [x y on-change value]}]
|
||||
(let [left (- x 260)
|
||||
top (- y 50)]
|
||||
[:div.colorpicker-tooltip
|
||||
{:style {:left (str left "px")
|
||||
:top (str top "px")}}
|
||||
;; (mf/defc opacity-input
|
||||
;; [{:keys [form] :as props}]
|
||||
;; (when-not (:opacity @form)
|
||||
;; (swap! form assoc :opacity 100))
|
||||
;; [:div
|
||||
;; [:span "Opacity"]
|
||||
;; [:div.row-flex
|
||||
;; [:div.input-element.percentail
|
||||
;; [:input.input-text
|
||||
;; {:placeholder "%"
|
||||
;; :on-change (partial on-change form :opacity)
|
||||
;; :min "0"
|
||||
;; :max "100"
|
||||
;; :type "number"
|
||||
;; :value (:opacity @form "")}]]]])
|
||||
|
||||
(cp/colorpicker
|
||||
:theme :small
|
||||
:value value
|
||||
:on-change on-change)]))
|
||||
;; ;; --- Rotate Input
|
||||
|
||||
(defmethod lbx/render-lightbox :interactions/colorpicker
|
||||
[params]
|
||||
(colorpicker params))
|
||||
;; ;; (mx/defc rotate-input
|
||||
;; ;; [form]
|
||||
;; ;; [:div
|
||||
;; ;; [:span "Rotate (dg)"]
|
||||
;; ;; [:div.row-flex
|
||||
;; ;; [:div.input-element.degrees
|
||||
;; ;; [:input.input-text
|
||||
;; ;; {:placeholder "dg"
|
||||
;; ;; :on-change (partial on-change form :rotation)
|
||||
;; ;; :type "number"
|
||||
;; ;; :value (:rotation @form "")}]]]])
|
||||
|
||||
(mf/defc color-input
|
||||
[{:keys [form] :as props}]
|
||||
(when-not (:fill-color @form)
|
||||
(swap! form assoc :fill-color "#000000"))
|
||||
(when-not (:stroke-color @form)
|
||||
(swap! form assoc :stroke-color "#000000"))
|
||||
(letfn [(on-change [attr color]
|
||||
(swap! form assoc attr color))
|
||||
(on-change-fill-color [event]
|
||||
(let [value (dom/event->value event)]
|
||||
(when (color? value)
|
||||
(on-change :fill-color value))))
|
||||
(on-change-stroke-color [event]
|
||||
(let [value (dom/event->value event)]
|
||||
(when (color? value)
|
||||
(on-change :stroke-color value))))
|
||||
(show-picker [attr event]
|
||||
(let [x (.-clientX event)
|
||||
y (.-clientY event)
|
||||
opts {:x x :y y
|
||||
:on-change (partial on-change attr)
|
||||
:value (get @form attr)
|
||||
:transparent? true}]
|
||||
(udl/open! :interactions/colorpicker opts)))]
|
||||
(let [stroke-color (:stroke-color @form)
|
||||
fill-color (:fill-color @form)]
|
||||
[:div
|
||||
[:div.row-flex
|
||||
[:div.column-half
|
||||
[:span "Fill"]
|
||||
[:div.color-data
|
||||
[:span.color-th
|
||||
{:style {:background-color fill-color}
|
||||
:on-click (partial show-picker :fill-color)}]
|
||||
[:div.color-info
|
||||
[:input
|
||||
{:on-change on-change-fill-color
|
||||
:value fill-color}]]]]
|
||||
[:div.column-half
|
||||
[:span "Stroke"]
|
||||
[:div.color-data
|
||||
[:span.color-th
|
||||
{:style {:background-color stroke-color}
|
||||
:on-click (partial show-picker :stroke-color)}]
|
||||
[:div.color-info
|
||||
[:input
|
||||
{:on-change on-change-stroke-color
|
||||
:value stroke-color}]]]]]])))
|
||||
;; ;; --- Resize Input
|
||||
|
||||
;; --- Easing Input
|
||||
;; (mf/defc resize-input
|
||||
;; [{:keys [form] :as props}]
|
||||
;; [:div
|
||||
;; [:span "Resize"]
|
||||
;; [:div.row-flex
|
||||
;; [:div.input-element.pixels
|
||||
;; [:input.input-text
|
||||
;; {:placeholder "Width"
|
||||
;; :on-change (partial on-change form :resize-width)
|
||||
;; :type "number"
|
||||
;; :value (:resize-width @form "")}]]
|
||||
;; [:div.input-element.pixels
|
||||
;; [:input.input-text
|
||||
;; {:placeholder "Height"
|
||||
;; :on-change (partial on-change form :resize-height)
|
||||
;; :type "number"
|
||||
;; :value (:resize-height @form "")}]]]])
|
||||
|
||||
(mf/defc easing-input
|
||||
[{:keys [form] :as props}]
|
||||
(when-not (:easing @form)
|
||||
(swap! form assoc :easing :linear))
|
||||
[:div
|
||||
[:span "Easing"]
|
||||
[:div.row-flex
|
||||
[:select.input-select
|
||||
{:placeholder "Easing"
|
||||
:on-change (partial on-change form :easing)
|
||||
:value (pr-str (:easing @form))}
|
||||
[:option {:value ":linear"} "Linear"]
|
||||
[:option {:value ":easein"} "Ease in"]
|
||||
[:option {:value ":easeout"} "Ease out"]
|
||||
[:option {:value ":easeinout"} "Ease in out"]]]])
|
||||
;; ;; --- Color Input
|
||||
|
||||
;; --- Duration Input
|
||||
;; (mf/defc colorpicker
|
||||
;; [{:keys [x y on-change value]}]
|
||||
;; (let [left (- x 260)
|
||||
;; top (- y 50)]
|
||||
;; [:div.colorpicker-tooltip
|
||||
;; {:style {:left (str left "px")
|
||||
;; :top (str top "px")}}
|
||||
|
||||
(mf/defc duration-input
|
||||
[{:keys [form] :as props}]
|
||||
(when-not (:duration @form)
|
||||
(swap! form assoc :duration 300))
|
||||
(when-not (:delay @form)
|
||||
(swap! form assoc :delay 0))
|
||||
[:div
|
||||
[:span "Duration | Delay"]
|
||||
[:div.row-flex
|
||||
[:div.input-element.miliseconds
|
||||
[:input.input-text
|
||||
{:placeholder "Duration"
|
||||
:type "number"
|
||||
:on-change (partial on-change form :duration)
|
||||
:value (pr-str (:duration @form))}]]
|
||||
[:div.input-element.miliseconds
|
||||
[:input.input-text {:placeholder "Delay"
|
||||
:type "number"
|
||||
:on-change (partial on-change form :delay)
|
||||
:value (pr-str (:delay @form))}]]]])
|
||||
;; (cp/colorpicker
|
||||
;; :theme :small
|
||||
;; :value value
|
||||
;; :on-change on-change)]))
|
||||
|
||||
;; --- Action Input
|
||||
;; (defmethod lbx/render-lightbox :interactions/colorpicker
|
||||
;; [params]
|
||||
;; (colorpicker params))
|
||||
|
||||
(mf/defc action-input
|
||||
[{:keys [shape form] :as props}]
|
||||
;; (when-not (:action @form)
|
||||
;; (swap! form assoc :action :show))
|
||||
(let [form-data (deref form)
|
||||
simple? #{:gotourl :gotopage}
|
||||
elements? (complement simple?)
|
||||
animation? #{:show :hide :toggle}
|
||||
only-easing? (complement animation?)]
|
||||
[:div
|
||||
[:span "Action"]
|
||||
[:div.row-flex
|
||||
[:select.input-select
|
||||
{:placeholder "Choose an action"
|
||||
:on-change (partial on-change form :action [:trigger])
|
||||
:value (pr-str (:action form-data))}
|
||||
[:option {:value ":show"} "Show"]
|
||||
[:option {:value ":hide"} "Hide"]
|
||||
[:option {:value ":toggle"} "Toggle"]
|
||||
;; [:option {:value ":moveto"} "Move to"]
|
||||
[:option {:value ":moveby"} "Move by"]
|
||||
[:option {:value ":opacity"} "Opacity"]
|
||||
[:option {:value ":size"} "Size"]
|
||||
[:option {:value ":color"} "Color"]
|
||||
;; [:option {:value ":rotate"} "Rotate"]
|
||||
[:option {:value ":gotopage"} "Go to page"]
|
||||
[:option {:value ":gotourl"} "Go to URL"]
|
||||
#_[:option {:value ":goback"} "Go back"]
|
||||
[:option {:value ":scrolltoelement"} "Scroll to element"]]]
|
||||
;; (mf/defc color-input
|
||||
;; [{:keys [form] :as props}]
|
||||
;; (when-not (:fill-color @form)
|
||||
;; (swap! form assoc :fill-color "#000000"))
|
||||
;; (when-not (:stroke-color @form)
|
||||
;; (swap! form assoc :stroke-color "#000000"))
|
||||
;; (letfn [(on-change [attr color]
|
||||
;; (swap! form assoc attr color))
|
||||
;; (on-change-fill-color [event]
|
||||
;; (let [value (dom/event->value event)]
|
||||
;; (when (color? value)
|
||||
;; (on-change :fill-color value))))
|
||||
;; (on-change-stroke-color [event]
|
||||
;; (let [value (dom/event->value event)]
|
||||
;; (when (color? value)
|
||||
;; (on-change :stroke-color value))))
|
||||
;; (show-picker [attr event]
|
||||
;; (let [x (.-clientX event)
|
||||
;; y (.-clientY event)
|
||||
;; opts {:x x :y y
|
||||
;; :on-change (partial on-change attr)
|
||||
;; :value (get @form attr)
|
||||
;; :transparent? true}]
|
||||
;; (udl/open! :interactions/colorpicker opts)))]
|
||||
;; (let [stroke-color (:stroke-color @form)
|
||||
;; fill-color (:fill-color @form)]
|
||||
;; [:div
|
||||
;; [:div.row-flex
|
||||
;; [:div.column-half
|
||||
;; [:span "Fill"]
|
||||
;; [:div.color-data
|
||||
;; [:span.color-th
|
||||
;; {:style {:background-color fill-color}
|
||||
;; :on-click (partial show-picker :fill-color)}]
|
||||
;; [:div.color-info
|
||||
;; [:input
|
||||
;; {:on-change on-change-fill-color
|
||||
;; :value fill-color}]]]]
|
||||
;; [:div.column-half
|
||||
;; [:span "Stroke"]
|
||||
;; [:div.color-data
|
||||
;; [:span.color-th
|
||||
;; {:style {:background-color stroke-color}
|
||||
;; :on-click (partial show-picker :stroke-color)}]
|
||||
;; [:div.color-info
|
||||
;; [:input
|
||||
;; {:on-change on-change-stroke-color
|
||||
;; :value stroke-color}]]]]]])))
|
||||
|
||||
(case (:action form-data)
|
||||
:gotourl [:& url-input {:form form}]
|
||||
;; :gotopage (pages-input form)
|
||||
:color [:& color-input {:form form}]
|
||||
;; :rotate (rotate-input form)
|
||||
:size [:& resize-input {:form form}]
|
||||
:moveto [:& moveto-input {:form form}]
|
||||
:moveby [:& moveby-input {:form form}]
|
||||
:opacity [:& opacity-input {:form form}]
|
||||
nil)
|
||||
;; ;; --- Easing Input
|
||||
|
||||
(when (elements? (:action form-data))
|
||||
[:& elements-input {:page-id (:page shape)
|
||||
:form form}])
|
||||
;; (mf/defc easing-input
|
||||
;; [{:keys [form] :as props}]
|
||||
;; (when-not (:easing @form)
|
||||
;; (swap! form assoc :easing :linear))
|
||||
;; [:div
|
||||
;; [:span "Easing"]
|
||||
;; [:div.row-flex
|
||||
;; [:select.input-select
|
||||
;; {:placeholder "Easing"
|
||||
;; :on-change (partial on-change form :easing)
|
||||
;; :value (pr-str (:easing @form))}
|
||||
;; [:option {:value ":linear"} "Linear"]
|
||||
;; [:option {:value ":easein"} "Ease in"]
|
||||
;; [:option {:value ":easeout"} "Ease out"]
|
||||
;; [:option {:value ":easeinout"} "Ease in out"]]]])
|
||||
|
||||
(when (and (animation? (:action form-data))
|
||||
(:element form-data))
|
||||
[:& animation-input {:form form}])
|
||||
;; ;; --- Duration Input
|
||||
|
||||
(when (or (not= (:animation form-data :none) :none)
|
||||
(and (only-easing? (:action form-data))
|
||||
(:element form-data)))
|
||||
[:*
|
||||
[:& easing-input {:form form}]
|
||||
[:& duration-input {:form form}]])]))
|
||||
;; (mf/defc duration-input
|
||||
;; [{:keys [form] :as props}]
|
||||
;; (when-not (:duration @form)
|
||||
;; (swap! form assoc :duration 300))
|
||||
;; (when-not (:delay @form)
|
||||
;; (swap! form assoc :delay 0))
|
||||
;; [:div
|
||||
;; [:span "Duration | Delay"]
|
||||
;; [:div.row-flex
|
||||
;; [:div.input-element.miliseconds
|
||||
;; [:input.input-text
|
||||
;; {:placeholder "Duration"
|
||||
;; :type "number"
|
||||
;; :on-change (partial on-change form :duration)
|
||||
;; :value (pr-str (:duration @form))}]]
|
||||
;; [:div.input-element.miliseconds
|
||||
;; [:input.input-text {:placeholder "Delay"
|
||||
;; :type "number"
|
||||
;; :on-change (partial on-change form :delay)
|
||||
;; :value (pr-str (:delay @form))}]]]])
|
||||
|
||||
;; ;; --- Action Input
|
||||
|
||||
;; (mf/defc action-input
|
||||
;; [{:keys [shape form] :as props}]
|
||||
;; ;; (when-not (:action @form)
|
||||
;; ;; (swap! form assoc :action :show))
|
||||
;; (let [form-data (deref form)
|
||||
;; simple? #{:gotourl :gotopage}
|
||||
;; elements? (complement simple?)
|
||||
;; animation? #{:show :hide :toggle}
|
||||
;; only-easing? (complement animation?)]
|
||||
;; [:div
|
||||
;; [:span "Action"]
|
||||
;; [:div.row-flex
|
||||
;; [:select.input-select
|
||||
;; {:placeholder "Choose an action"
|
||||
;; :on-change (partial on-change form :action [:trigger])
|
||||
;; :value (pr-str (:action form-data))}
|
||||
;; [:option {:value ":show"} "Show"]
|
||||
;; [:option {:value ":hide"} "Hide"]
|
||||
;; [:option {:value ":toggle"} "Toggle"]
|
||||
;; ;; [:option {:value ":moveto"} "Move to"]
|
||||
;; [:option {:value ":moveby"} "Move by"]
|
||||
;; [:option {:value ":opacity"} "Opacity"]
|
||||
;; [:option {:value ":size"} "Size"]
|
||||
;; [:option {:value ":color"} "Color"]
|
||||
;; ;; [:option {:value ":rotate"} "Rotate"]
|
||||
;; [:option {:value ":gotopage"} "Go to page"]
|
||||
;; [:option {:value ":gotourl"} "Go to URL"]
|
||||
;; #_[:option {:value ":goback"} "Go back"]
|
||||
;; [:option {:value ":scrolltoelement"} "Scroll to element"]]]
|
||||
|
||||
;; (case (:action form-data)
|
||||
;; :gotourl [:& url-input {:form form}]
|
||||
;; ;; :gotopage (pages-input form)
|
||||
;; :color [:& color-input {:form form}]
|
||||
;; ;; :rotate (rotate-input form)
|
||||
;; :size [:& resize-input {:form form}]
|
||||
;; :moveto [:& moveto-input {:form form}]
|
||||
;; :moveby [:& moveby-input {:form form}]
|
||||
;; :opacity [:& opacity-input {:form form}]
|
||||
;; nil)
|
||||
|
||||
;; (when (elements? (:action form-data))
|
||||
;; [:& elements-input {:page-id (:page shape)
|
||||
;; :form form}])
|
||||
|
||||
;; (when (and (animation? (:action form-data))
|
||||
;; (:element form-data))
|
||||
;; [:& animation-input {:form form}])
|
||||
|
||||
;; (when (or (not= (:animation form-data :none) :none)
|
||||
;; (and (only-easing? (:action form-data))
|
||||
;; (:element form-data)))
|
||||
;; [:*
|
||||
;; [:& easing-input {:form form}]
|
||||
;; [:& duration-input {:form form}]])]))
|
||||
|
||||
|
||||
;; --- Form
|
||||
;; ;; --- Form
|
||||
|
||||
(mf/defc interactions-form
|
||||
[{:keys [shape form] :as props}]
|
||||
(letfn [(on-submit [event]
|
||||
(dom/prevent-default event)
|
||||
(let [sid (:id shape)
|
||||
data (deref form)]
|
||||
(st/emit! (dw/update-interaction sid data))
|
||||
(reset! form nil)))
|
||||
(on-cancel [event]
|
||||
(dom/prevent-default event)
|
||||
(reset! form nil))]
|
||||
[:form {:on-submit on-submit}
|
||||
[:& trigger-input {:form form}]
|
||||
[:& action-input {:shape shape :form form}]
|
||||
[:div.row-flex
|
||||
[:input.btn-primary.btn-small.save-btn
|
||||
{:value "Save" :type "submit"}]
|
||||
[:a.cancel-btn {:on-click on-cancel}
|
||||
"Cancel"]]]))
|
||||
;; (mf/defc interactions-form
|
||||
;; [{:keys [shape form] :as props}]
|
||||
;; (letfn [(on-submit [event]
|
||||
;; (dom/prevent-default event)
|
||||
;; (let [sid (:id shape)
|
||||
;; data (deref form)]
|
||||
;; (st/emit! (dw/update-interaction sid data))
|
||||
;; (reset! form nil)))
|
||||
;; (on-cancel [event]
|
||||
;; (dom/prevent-default event)
|
||||
;; (reset! form nil))]
|
||||
;; [:form {:on-submit on-submit}
|
||||
;; [:& trigger-input {:form form}]
|
||||
;; [:& action-input {:shape shape :form form}]
|
||||
;; [:div.row-flex
|
||||
;; [:input.btn-primary.btn-small.save-btn
|
||||
;; {:value "Save" :type "submit"}]
|
||||
;; [:a.cancel-btn {:on-click on-cancel}
|
||||
;; "Cancel"]]]))
|
||||
|
||||
;; --- Interactions Menu
|
||||
|
||||
|
@ -477,7 +477,7 @@
|
|||
|
||||
(mf/defc interactions-menu
|
||||
[{:keys [menu shape] :as props}]
|
||||
(let [form (mf/use-state nil)
|
||||
#_(let [form (mf/use-state nil)
|
||||
interactions (:interactions shape)]
|
||||
[:div.element-set {:key (str (:id menu))}
|
||||
[:div.element-set-title (:name menu)]
|
||||
|
|
|
@ -46,9 +46,9 @@
|
|||
(modal/show! colorpicker-modal props)))]
|
||||
|
||||
[:div.element-set
|
||||
[:div.element-set-title (tr "element.page-measures")]
|
||||
[:div.element-set-title (tr "workspace.options.page-measures")]
|
||||
[:div.element-set-content
|
||||
[:span (tr "ds.background-color")]
|
||||
[:span (tr "workspace.options.background-color")]
|
||||
[:div.row-flex.color-data
|
||||
[:span.color-th
|
||||
{:style {:background-color (:background metadata "#ffffff")}
|
||||
|
@ -92,7 +92,7 @@
|
|||
[:div.element-set
|
||||
[:div.element-set-title (tr "element.page-grid-options")]
|
||||
[:div.element-set-content
|
||||
[:span (tr "ds.size")]
|
||||
[:span (tr "workspace.options.size")]
|
||||
[:div.row-flex
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
|
@ -106,7 +106,7 @@
|
|||
:value (:grid-y-axis metadata)
|
||||
:on-change on-y-change
|
||||
:placeholder "y"}]]]
|
||||
[:span (tr "ds.color")]
|
||||
[:span (tr "workspace.options.color")]
|
||||
[:div.row-flex.color-data
|
||||
[:span.color-th
|
||||
{:style {:background-color (:grid-color metadata)}
|
||||
|
|
|
@ -8,111 +8,130 @@
|
|||
(ns uxbox.main.ui.workspace.sidebar.options.rect
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[uxbox.common.data :as d]
|
||||
[uxbox.builtins.icons :as i]
|
||||
[uxbox.main.data.workspace :as udw]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.util.data :refer [parse-int parse-float read-string]]
|
||||
[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 :refer [precision-or-0]]))
|
||||
[uxbox.util.math :as math]))
|
||||
|
||||
(declare on-size-change)
|
||||
(declare on-rotation-change)
|
||||
(declare on-position-change)
|
||||
(declare on-proportion-lock-change)
|
||||
(mf/defc measures-menu
|
||||
[{:keys [shape] :as props}]
|
||||
(let [on-size-change
|
||||
(fn [event attr]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/parse-integer 0))]
|
||||
(st/emit! (udw/update-dimensions (:id shape) {attr value}))))
|
||||
|
||||
on-proportion-lock-change
|
||||
(fn [event]
|
||||
(st/emit! (udw/toggle-shape-proportion-lock (:id shape))))
|
||||
|
||||
on-position-change
|
||||
(fn [event attr]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/parse-integer))
|
||||
point (gpt/point {attr value})]
|
||||
(st/emit! (udw/update-position (:id shape) point))))
|
||||
|
||||
on-rotation-change
|
||||
(fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/parse-integer 0))]
|
||||
(st/emit! (udw/update-shape (:id shape) {:rotation value}))))
|
||||
|
||||
on-radius-change
|
||||
(fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/parse-double 0))]
|
||||
(st/emit! (udw/update-shape (:id shape) {:rx value :ry value}))))
|
||||
|
||||
on-width-change #(on-size-change % :width)
|
||||
on-height-change #(on-size-change % :height)
|
||||
on-pos-x-change #(on-position-change % :x)
|
||||
on-pos-y-change #(on-position-change % :y)]
|
||||
|
||||
(mf/defc measures
|
||||
[{:keys [menu shape] :as props}]
|
||||
(let [size (geom/size shape)]
|
||||
[:div.element-set
|
||||
[:div.element-set-title (tr "element.measures")]
|
||||
[:div.element-set-title (tr "workspace.options.measures")]
|
||||
[:div.element-set-content
|
||||
[:span (tr "ds.size")]
|
||||
[:span (tr "workspace.options.size")]
|
||||
|
||||
;; WIDTH & HEIGHT
|
||||
[:div.row-flex
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:placeholder (tr "ds.width")
|
||||
:type "number"
|
||||
[:input.input-text {:type "number"
|
||||
:min "0"
|
||||
:value (precision-or-0 (:width size) 2)}]]
|
||||
:on-change on-width-change
|
||||
:value (-> (:width shape)
|
||||
(math/precision 2)
|
||||
(d/coalesce-str "0"))}]]
|
||||
|
||||
[:div.lock-size {:class (when (:proportion-lock shape) "selected")
|
||||
:on-click #(on-proportion-lock-change % shape)}
|
||||
(if (:proportion-lock shape) i/lock i/unlock)]
|
||||
:on-click on-proportion-lock-change}
|
||||
(if (:proportion-lock shape)
|
||||
i/lock
|
||||
i/unlock)]
|
||||
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:placeholder (tr "ds.height")
|
||||
:type "number"
|
||||
[:input.input-text {:type "number"
|
||||
:min "0"
|
||||
:value (precision-or-0 (:height size) 2)}]]]
|
||||
:on-change on-height-change
|
||||
:value (-> (:height shape)
|
||||
(math/precision 2)
|
||||
(d/coalesce-str "0"))}]]]
|
||||
|
||||
;; POSITION
|
||||
[:span (tr "ds.position")]
|
||||
[:span (tr "workspace.options.position")]
|
||||
[:div.row-flex
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:placeholder "x"
|
||||
:type "number"
|
||||
:value (precision-or-0 (:x1 shape 0) 2)}]]
|
||||
:on-change on-pos-x-change
|
||||
:value (-> (:x shape)
|
||||
(math/precision 2)
|
||||
(d/coalesce-str "0"))}]]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:placeholder "y"
|
||||
:type "number"
|
||||
:value (precision-or-0 (:y1 shape 0) 2)}]]]
|
||||
|
||||
;; ROTATION
|
||||
[:span (tr "ds.rotation")]
|
||||
[:div.row-flex
|
||||
[:input.slidebar {:type "range"
|
||||
:min 0
|
||||
:max 360
|
||||
;; :on-change #(on-rotation-change % shape)
|
||||
:value (:rotation shape 0)}]]
|
||||
:on-change on-pos-y-change
|
||||
:value (-> (:y shape)
|
||||
(math/precision 2)
|
||||
(d/coalesce-str "0"))}]]]
|
||||
|
||||
[:span (tr "workspace.options.rotation-radius")]
|
||||
[:div.row-flex
|
||||
[:div.input-element.degrees
|
||||
[:input.input-text {:placeholder ""
|
||||
:type "number"
|
||||
:min 0
|
||||
:max 360
|
||||
:on-change #(on-rotation-change % shape)
|
||||
:value (precision-or-0 (:rotation shape "0") 2)}]]
|
||||
[:input.input-text {:style {:visibility "hidden"}}]]]]))
|
||||
:on-change on-rotation-change
|
||||
:value (-> (:rotation shape 0)
|
||||
(math/precision 2)
|
||||
(d/coalesce-str "0"))}]]
|
||||
|
||||
;; (defn- on-size-change
|
||||
;; [event shape attr]
|
||||
;; (let [value (-> (dom/event->value event)
|
||||
;; (parse-int 0))]
|
||||
;; (st/emit! (udw/update-dimensions (:id shape) {attr value}))))
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder "rx"
|
||||
:type "number"
|
||||
:on-change on-radius-change
|
||||
:value (-> (:rx shape)
|
||||
(math/precision 2)
|
||||
(d/coalesce-str "0"))}]]]]]))
|
||||
|
||||
;; (defn- on-rotation-change
|
||||
;; [event shape]
|
||||
;; (let [value (dom/event->value event)
|
||||
;; value (parse-int value 0)]
|
||||
;; (st/emit! (udw/update-shape-attrs (:id shape) {:rotation value}))))
|
||||
|
||||
;; (defn- on-position-change
|
||||
;; [event shape attr]
|
||||
;; (let [value (-> (dom/event->value event)
|
||||
;; (parse-int nil))
|
||||
;; point (gpt/point {attr value})]
|
||||
;; (st/emit! (udw/update-position (:id shape) point))))
|
||||
|
||||
;; (defn- on-proportion-lock-change
|
||||
;; [event shape]
|
||||
;; (if (:proportion-lock shape)
|
||||
;; (st/emit! (udw/unlock-proportions (:id shape)))
|
||||
;; (st/emit! (udw/lock-proportions (:id shape)))))
|
||||
|
||||
|
||||
;; :rect [::rect-measures ::fill ::stroke]
|
||||
|
||||
(mf/defc options
|
||||
[{:keys [shape] :as props}]
|
||||
[:div
|
||||
[:& measures {:shape shape}]
|
||||
[:& measures-menu {:shape shape}]
|
||||
[:& fill-menu {:shape shape}]
|
||||
[:& stroke-menu {:shape shape}]])
|
||||
|
|
|
@ -1,108 +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 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.main.ui.workspace.sidebar.options.rect-measures
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[uxbox.builtins.icons :as i]
|
||||
[uxbox.main.data.workspace :as udw]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.util.data :refer [parse-int parse-float read-string]]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.geom.point :as gpt]
|
||||
[uxbox.util.i18n :refer [tr]]
|
||||
[uxbox.util.math :refer [precision-or-0]]))
|
||||
|
||||
(declare on-size-change)
|
||||
(declare on-rotation-change)
|
||||
(declare on-position-change)
|
||||
(declare on-proportion-lock-change)
|
||||
|
||||
(mf/defc rect-measures-menu
|
||||
[{:keys [menu shape] :as props}]
|
||||
(let [size (geom/size shape)]
|
||||
[:div.element-set
|
||||
[:div.element-set-title (:name menu)]
|
||||
[:div.element-set-content
|
||||
;; SLIDEBAR FOR ROTATION AND OPACITY
|
||||
[:span (tr "ds.size")]
|
||||
[:div.row-flex
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:placeholder (tr "ds.width")
|
||||
:type "number"
|
||||
:min "0"
|
||||
:value (precision-or-0 (:width size) 2)
|
||||
:on-change #(on-size-change % shape :width)}]]
|
||||
|
||||
[:div.lock-size {:class (when (:proportion-lock shape) "selected")
|
||||
:on-click #(on-proportion-lock-change % shape)}
|
||||
(if (:proportion-lock shape) i/lock i/unlock)]
|
||||
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:placeholder (tr "ds.height")
|
||||
:type "number"
|
||||
:min "0"
|
||||
:value (precision-or-0 (:height size) 2)
|
||||
:on-change #(on-size-change % shape :height)}]]]
|
||||
|
||||
[:span (tr "ds.position")]
|
||||
[:div.row-flex
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:placeholder "x"
|
||||
:type "number"
|
||||
:value (precision-or-0 (:x1 shape 0) 2)
|
||||
:on-change #(on-position-change % shape :x)}]]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:placeholder "y"
|
||||
:type "number"
|
||||
:value (precision-or-0 (:y1 shape 0) 2)
|
||||
:on-change #(on-position-change % shape :y)}]]]
|
||||
|
||||
[:span (tr "ds.rotation")]
|
||||
[:div.row-flex
|
||||
[:input.slidebar {:type "range"
|
||||
:min 0
|
||||
:max 360
|
||||
:value (:rotation shape 0)
|
||||
:on-change #(on-rotation-change % shape)}]]
|
||||
|
||||
[:div.row-flex
|
||||
[:div.input-element.degrees
|
||||
[:input.input-text {:placeholder ""
|
||||
:type "number"
|
||||
:min 0
|
||||
:max 360
|
||||
:value (precision-or-0 (:rotation shape "0") 2)
|
||||
:on-change #(on-rotation-change % shape)}]]
|
||||
[:input.input-text {:style {:visibility "hidden"}}]]]]))
|
||||
|
||||
|
||||
(defn- on-size-change
|
||||
[event shape attr]
|
||||
(let [value (-> (dom/event->value event)
|
||||
(parse-int 0))]
|
||||
(st/emit! (udw/update-dimensions (:id shape) {attr value}))))
|
||||
|
||||
(defn- on-rotation-change
|
||||
[event shape]
|
||||
(let [value (dom/event->value event)
|
||||
value (parse-int value 0)]
|
||||
(st/emit! (udw/update-shape-attrs (:id shape) {:rotation value}))))
|
||||
|
||||
(defn- on-position-change
|
||||
[event shape attr]
|
||||
(let [value (-> (dom/event->value event)
|
||||
(parse-int nil))
|
||||
point (gpt/point {attr value})]
|
||||
(st/emit! (udw/update-position (:id shape) point))))
|
||||
|
||||
(defn- on-proportion-lock-change
|
||||
[event shape]
|
||||
(if (:proportion-lock shape)
|
||||
(st/emit! (udw/unlock-proportions (:id shape)))
|
||||
(st/emit! (udw/lock-proportions (:id shape)))))
|
|
@ -8,6 +8,7 @@
|
|||
(ns uxbox.main.ui.workspace.sidebar.options.stroke
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[uxbox.common.data :as d]
|
||||
[uxbox.builtins.icons :as i]
|
||||
[uxbox.main.data.workspace :as udw]
|
||||
[uxbox.main.store :as st]
|
||||
|
@ -16,126 +17,82 @@
|
|||
[uxbox.util.data :refer [parse-int parse-float read-string]]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.i18n :refer [tr]]
|
||||
[uxbox.util.math :refer [precision-or-0]]))
|
||||
|
||||
(declare on-width-change)
|
||||
(declare on-opacity-change)
|
||||
(declare on-stroke-style-change)
|
||||
(declare on-stroke-color-change)
|
||||
(declare on-border-change)
|
||||
(declare show-color-picker)
|
||||
[uxbox.util.math :as math]))
|
||||
|
||||
(mf/defc stroke-menu
|
||||
[{:keys [shape] :as props}]
|
||||
(let [local (mf/use-state {})
|
||||
on-border-lock #(swap! local update :border-lock not)
|
||||
on-stroke-style-change #(on-stroke-style-change % shape)
|
||||
on-width-change #(on-width-change % shape)
|
||||
on-stroke-color-change #(on-stroke-color-change % shape)
|
||||
on-border-change-rx #(on-border-change % shape local :rx)
|
||||
on-border-change-ry #(on-border-change % shape local :ry)
|
||||
on-opacity-change #(on-opacity-change % shape)
|
||||
show-color-picker #(show-color-picker % shape)]
|
||||
(let [on-stroke-style-change
|
||||
(fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/read-string))]
|
||||
(st/emit! (udw/update-shape (:id shape) {:stroke-style value}))))
|
||||
|
||||
on-stroke-width-change
|
||||
(fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/parse-double 1))]
|
||||
(st/emit! (udw/update-shape (:id shape) {:stroke-width value}))))
|
||||
|
||||
on-stroke-opacity-change
|
||||
(fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/parse-double 1)
|
||||
(/ 10000))]
|
||||
(st/emit! (udw/update-shape (:id shape) {:stroke-opacity value}))))
|
||||
|
||||
show-color-picker
|
||||
(fn [event]
|
||||
(let [x (.-clientX event)
|
||||
y (.-clientY event)
|
||||
props {:x x :y y
|
||||
:default "#ffffff"
|
||||
:value (:stroke-color shape)
|
||||
:on-change #(st/emit! (udw/update-shape (:id shape) {:stroke-color %}))
|
||||
:transparent? true}]
|
||||
(modal/show! colorpicker-modal props)))]
|
||||
|
||||
[:div.element-set
|
||||
[:div.element-set-title (tr "element.stroke")]
|
||||
[:div.element-set-title (tr "workspace.options.stroke")]
|
||||
[:div.element-set-content
|
||||
[:span (tr "ds.style")]
|
||||
|
||||
;; Stroke Style & Width
|
||||
[:span (tr "workspace.options.stroke.style")]
|
||||
[:div.row-flex
|
||||
[:select#style.input-select {:placeholder (tr "ds.style")
|
||||
:value (pr-str (:stroke-style shape))
|
||||
[:select#style.input-select {:value (pr-str (:stroke-style shape))
|
||||
:on-change on-stroke-style-change}
|
||||
[:option {:value ":none"} (tr "ds.none")]
|
||||
[:option {:value ":solid"} (tr "ds.solid")]
|
||||
[:option {:value ":dotted"} (tr "ds.dotted")]
|
||||
[:option {:value ":dashed"} (tr "ds.dashed")]
|
||||
[:option {:value ":mixed"} (tr "ds.mixed")]]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder (tr "ds.width")
|
||||
:type "number"
|
||||
:min "0"
|
||||
:value (precision-or-0 (:stroke-width shape 1) 2)
|
||||
:on-change on-width-change}]]]
|
||||
[:option {:value ":none"} (tr "workspace.options.stroke.none")]
|
||||
[:option {:value ":solid"} (tr "workspace.options.stroke.solid")]
|
||||
[:option {:value ":dotted"} (tr "workspace.options.stroke.dotted")]
|
||||
[:option {:value ":dashed"} (tr "workspace.options.stroke.dashed")]
|
||||
[:option {:value ":mixed"} (tr "workspace.options.stroke.mixed")]]
|
||||
|
||||
[:span (tr "ds.color")]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:type "number"
|
||||
:min "0"
|
||||
:value (-> (:stroke-width shape)
|
||||
(math/precision 2)
|
||||
(d/coalesce-str "1"))
|
||||
:on-change on-stroke-width-change}]]]
|
||||
|
||||
;; Stroke Color
|
||||
[:span (tr "workspace.options.color")]
|
||||
[:div.row-flex.color-data
|
||||
[:span.color-th
|
||||
{:style {:background-color (:stroke-color shape)}
|
||||
:on-click show-color-picker}]
|
||||
[:span.color-th {:style {:background-color (:stroke-color shape)}
|
||||
:on-click show-color-picker}]
|
||||
[:div.color-info
|
||||
[:input
|
||||
{:on-change on-stroke-color-change
|
||||
:value (:stroke-color shape "")}]]]
|
||||
[:input {:read-only true
|
||||
:default-value (:stroke-color shape "")}]]]
|
||||
|
||||
[:span (tr "ds.radius")]
|
||||
[:span (tr "workspace.options.opacity")]
|
||||
[:div.row-flex
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder "rx"
|
||||
:type "number"
|
||||
:value (precision-or-0 (:rx shape 0) 2)
|
||||
:on-change on-border-change-rx}]]
|
||||
[:div.lock-size
|
||||
{:class (when (:border-lock @local) "selected")
|
||||
:on-click on-border-lock}
|
||||
i/lock]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder "ry"
|
||||
:type "number"
|
||||
:value (precision-or-0 (:ry shape 0) 2)
|
||||
:on-change on-border-change-ry}]]]
|
||||
|
||||
[:span (tr "ds.opacity")]
|
||||
[:div.row-flex
|
||||
[:input.slidebar
|
||||
{:type "range"
|
||||
:min "0"
|
||||
:max "10000"
|
||||
:value (* 10000 (:stroke-opacity shape 1))
|
||||
:step "1"
|
||||
:on-change on-opacity-change}]]]]))
|
||||
|
||||
(defn- on-width-change
|
||||
[event shape]
|
||||
(let [value (-> (dom/event->value event)
|
||||
(parse-float 1))]
|
||||
(st/emit! (udw/update-shape-attrs (:id shape) {:stroke-width value}))))
|
||||
|
||||
(defn- on-opacity-change
|
||||
[event shape]
|
||||
(let [value (-> (dom/event->value event)
|
||||
(parse-float 1)
|
||||
(/ 10000))]
|
||||
(st/emit! (udw/update-shape-attrs (:id shape) {:stroke-opacity value}))))
|
||||
|
||||
(defn- on-stroke-style-change
|
||||
[event shape]
|
||||
(let [value (-> (dom/event->value event)
|
||||
(read-string))]
|
||||
(st/emit! (udw/update-shape-attrs (:id shape) {:stroke-style value}))))
|
||||
|
||||
(defn- on-stroke-color-change
|
||||
[event shape]
|
||||
(let [value (dom/event->value event)]
|
||||
(st/emit! (udw/update-shape-attrs (:id shape) {:stroke-color value}))))
|
||||
|
||||
(defn- on-border-change
|
||||
[event shape local attr]
|
||||
(let [value (-> (dom/event->value event)
|
||||
(parse-int nil))
|
||||
id (:id shape)]
|
||||
(if (:border-lock @local)
|
||||
(st/emit! (udw/update-shape-attrs id {:rx value :ry value}))
|
||||
(st/emit! (udw/update-shape-attrs id {attr value})))))
|
||||
|
||||
(defn- show-color-picker
|
||||
[event shape]
|
||||
(let [x (.-clientX event)
|
||||
y (.-clientY event)
|
||||
props {:x x :y y
|
||||
:default "#ffffff"
|
||||
:value (:stroke-color shape)
|
||||
:on-change #(st/emit! (udw/update-shape-attrs (:id shape) {:stroke-color %}))
|
||||
:transparent? true}]
|
||||
(modal/show! colorpicker-modal props)))
|
||||
[:input.slidebar {:type "range"
|
||||
:min "0"
|
||||
:max "10000"
|
||||
:value (-> (:stroke-opacity shape 1)
|
||||
(* 10000)
|
||||
(d/coalesce-str "1"))
|
||||
:step "1"
|
||||
:on-change on-stroke-opacity-change}]]]]))
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
{:mixins [mx/static]}
|
||||
[menu {:keys [id] :as shape}]
|
||||
(letfn [(update-attrs [attrs]
|
||||
(st/emit! (udw/update-shape-attrs id attrs)))
|
||||
#_(st/emit! (udw/update-shape-attrs id attrs)))
|
||||
(on-font-family-change [event]
|
||||
(let [value (dom/event->value event)
|
||||
attrs {:font-family (read-string value)
|
||||
|
|
|
@ -82,11 +82,11 @@
|
|||
end-x (max (:x start) (:x stop))
|
||||
end-y (max (:y start) (:y stop))]
|
||||
(assoc data
|
||||
:x1 start-x
|
||||
:y1 start-y
|
||||
:x2 end-x
|
||||
:y2 end-y
|
||||
:type :rect)))
|
||||
:type :rect
|
||||
:x start-x
|
||||
:y start-y
|
||||
:width (- end-x start-x)
|
||||
:height (- end-y start-y))))
|
||||
|
||||
(def ^:private handle-selrect
|
||||
(letfn [(update-state [state position]
|
||||
|
@ -115,13 +115,11 @@
|
|||
{:wrap [mf/wrap-memo]}
|
||||
[{:keys [data] :as props}]
|
||||
(when data
|
||||
(let [{:keys [x1 y1 width height]} (geom/size data)]
|
||||
[:rect.selection-rect
|
||||
{:x x1
|
||||
:y y1
|
||||
:width width
|
||||
:height height}])))
|
||||
|
||||
[:rect.selection-rect
|
||||
{:x (:x data)
|
||||
:y (:y data)
|
||||
:width (:width data)
|
||||
:height (:height data)}]))
|
||||
|
||||
;; --- Viewport Positioning
|
||||
|
||||
|
@ -155,7 +153,7 @@
|
|||
[:*
|
||||
(for [item canvas]
|
||||
[:& shape-wrapper {:shape item :key (:id item)}])
|
||||
(for [item (reverse shapes)]
|
||||
(for [item shapes]
|
||||
[:& shape-wrapper {:shape item :key (:id item)}])]))
|
||||
|
||||
(mf/defc viewport
|
||||
|
|
|
@ -201,6 +201,14 @@
|
|||
(str/camel (name key))))))
|
||||
|
||||
|
||||
;; (defn coalesce
|
||||
;; [^number v ^number n]
|
||||
;; (if (.-toFixed v)
|
||||
;; (js/parseFloat (.toFixed v n))
|
||||
;; 0))
|
||||
|
||||
|
||||
|
||||
;; (defmacro mirror-map [& fields]
|
||||
;; (let [keys# (map #(keyword (name %)) fields)
|
||||
;; vals# fields]
|
||||
|
|
|
@ -81,7 +81,8 @@
|
|||
|
||||
(defn precision
|
||||
[^number v ^number n]
|
||||
(js/parseFloat (.toFixed v n)))
|
||||
(when (and (number? v) (number? n))
|
||||
(js/parseFloat (.toFixed v n))))
|
||||
|
||||
(defn precision-or-0
|
||||
[^number v ^number n]
|
||||
|
|
|
@ -5,12 +5,15 @@
|
|||
;; Copyright (c) 2015-2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.util.router
|
||||
(:require [reitit.core :as r]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]
|
||||
[uxbox.util.html.history :as html-history])
|
||||
(:import goog.Uri
|
||||
goog.Uri.QueryData))
|
||||
(:require
|
||||
[reitit.core :as r]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]
|
||||
[uxbox.common.data :as d]
|
||||
[uxbox.util.html.history :as html-history])
|
||||
(:import
|
||||
goog.Uri
|
||||
goog.Uri.QueryData))
|
||||
|
||||
(defonce +router+ nil)
|
||||
|
||||
|
@ -42,7 +45,9 @@
|
|||
(if (empty? qparams)
|
||||
(r/match->path match)
|
||||
(let [uri (.parse goog.Uri (r/match->path match))
|
||||
qdt (.createFromMap QueryData (clj->js qparams))]
|
||||
qdt (.createFromMap QueryData (-> qparams
|
||||
(d/remove-nil-vals)
|
||||
(clj->js)))]
|
||||
(.setQueryData uri qdt)
|
||||
(.toString uri))))))
|
||||
|
||||
|
|
|
@ -12,20 +12,20 @@
|
|||
(schedule 0 func))
|
||||
([ms func]
|
||||
(let [sem (js/setTimeout #(func) ms)]
|
||||
(reify rx/ICancellable
|
||||
(-cancel [_]
|
||||
(reify rx/IDisposable
|
||||
(-dispose [_]
|
||||
(js/clearTimeout sem))))))
|
||||
|
||||
(defn interval
|
||||
[ms func]
|
||||
(let [sem (js/setInterval #(func) ms)]
|
||||
(reify rx/ICancellable
|
||||
(-cancel [_]
|
||||
(reify rx/IDisposable
|
||||
(-dispose [_]
|
||||
(js/clearInterval sem)))))
|
||||
|
||||
(defn schedule-on-idle
|
||||
[func]
|
||||
(let [sem (js/requestIdleCallback #(func))]
|
||||
(reify rx/ICancellable
|
||||
(-cancel [_]
|
||||
(reify rx/IDisposable
|
||||
(-dispose [_]
|
||||
(js/cancelIdleCallback sem)))))
|
||||
|
|
|
@ -16,4 +16,4 @@
|
|||
(let [zobj (js/JSZip.)]
|
||||
(run! (partial attach-file zobj) files)
|
||||
(->> (.generateAsync zobj #js {:type "blob"})
|
||||
(rx/from-promise)))))
|
||||
(rx/from)))))
|
||||
|
|
Loading…
Add table
Reference in a new issue