0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-24 15:39:50 -05:00

Adds typography to libraries

This commit is contained in:
alonso.torres 2020-09-29 16:17:45 +02:00 committed by Hirunatan
parent 4a4cff74e8
commit 718a676fa8
22 changed files with 831 additions and 642 deletions

View file

@ -15,7 +15,6 @@
(defn- load-query-services
[]
(require 'app.services.queries.media)
(require 'app.services.queries.colors)
(require 'app.services.queries.projects)
(require 'app.services.queries.files)
(require 'app.services.queries.profile)
@ -26,7 +25,6 @@
[]
(require 'app.services.mutations.demo)
(require 'app.services.mutations.media)
(require 'app.services.mutations.colors)
(require 'app.services.mutations.projects)
(require 'app.services.mutations.files)
(require 'app.services.mutations.profile)

View file

@ -1,150 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns app.services.mutations.colors
(:require
[clojure.spec.alpha :as s]
[app.common.exceptions :as ex]
[app.common.spec :as us]
[app.common.uuid :as uuid]
[app.config :as cfg]
[app.db :as db]
[app.services.mutations :as sm]
[app.services.queries.teams :as teams]
[app.tasks :as tasks]
[app.util.time :as dt]))
;; --- Helpers & Specs
(s/def ::id ::us/uuid)
(s/def ::name ::us/string)
(s/def ::profile-id ::us/uuid)
(s/def ::team-id ::us/uuid)
(s/def ::library-id ::us/uuid)
(s/def ::content ::us/string)
;; --- Mutation: Create Color
(declare select-file-for-update)
(declare create-color)
(s/def ::create-color
(s/keys :req-un [::profile-id ::name ::content ::file-id]
:opt-un [::id]))
(sm/defmutation ::create-color
[{:keys [profile-id file-id] :as params}]
(db/with-atomic [conn db/pool]
(let [file (select-file-for-update conn file-id)]
(teams/check-edition-permissions! conn profile-id (:team-id file))
(create-color conn params))))
(def ^:private sql:create-color
"insert into color (id, name, file_id, content)
values ($1, $2, $3, $4) returning *")
(defn create-color
[conn {:keys [id name file-id content]}]
(let [id (or id (uuid/next))]
(db/insert! conn :color {:id id
:name name
:file-id file-id
:content content})))
(def ^:private sql:select-file-for-update
"select file.*,
project.team_id as team_id
from file
inner join project on (project.id = file.project_id)
where file.id = ?
for update of file")
(defn- select-file-for-update
[conn id]
(let [row (db/exec-one! conn [sql:select-file-for-update id])]
(when-not row
(ex/raise :type :not-found))
row))
;; --- Mutation: Rename Color
(declare select-color-for-update)
(s/def ::rename-color
(s/keys :req-un [::id ::profile-id ::name]))
(sm/defmutation ::rename-color
[{:keys [id profile-id name] :as params}]
(db/with-atomic [conn db/pool]
(let [clr (select-color-for-update conn id)]
(teams/check-edition-permissions! conn profile-id (:team-id clr))
(db/update! conn :color
{:name name}
{:id id}))))
(def ^:private sql:select-color-for-update
"select c.*,
p.team_id as team_id
from color as c
inner join file as f on f.id = c.file_id
inner join project as p on p.id = f.project_id
where c.id = ?
for update of c")
(defn- select-color-for-update
[conn id]
(let [row (db/exec-one! conn [sql:select-color-for-update id])]
(when-not row
(ex/raise :type :not-found))
row))
;; --- Mutation: Update Color
(s/def ::update-color
(s/keys :req-un [::profile-id ::id ::content]))
(sm/defmutation ::update-color
[{:keys [profile-id id content] :as params}]
(db/with-atomic [conn db/pool]
(let [clr (select-color-for-update conn id)
;; IMPORTANT: if the previous name was equal to the hex content,
;; we must rename it in addition to changing the value.
new-name (if (= (:name clr) (:content clr))
content
(:name clr))]
(teams/check-edition-permissions! conn profile-id (:team-id clr))
(db/update! conn :color
{:name new-name
:content content}
{:id id}))))
;; --- Delete Color
(declare delete-color)
(s/def ::delete-color
(s/keys :req-un [::id ::profile-id]))
(sm/defmutation ::delete-color
[{:keys [profile-id id] :as params}]
(db/with-atomic [conn db/pool]
(let [clr (select-color-for-update conn id)]
(teams/check-edition-permissions! conn profile-id (:team-id clr))
;; Schedule object deletion
(tasks/submit! conn {:name "delete-object"
:delay cfg/default-deletion-delay
:props {:id id :type :color}})
(db/update! conn :color
{:deleted-at (dt/now)}
{:id id})
nil)))

View file

@ -245,7 +245,8 @@
[change]
(or (#{:add-color :mod-color :del-color
:add-media :mod-media :del-media
:add-component :mod-component :del-component} (:type change))
:add-component :mod-component :del-component
:add-typography :mod-typography :del-typography} (:type change))
(and (= (:type change) :mod-obj)
(some? (:component-id change)))))

View file

@ -1,104 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
(ns app.services.queries.colors
(:require
[clojure.spec.alpha :as s]
[promesa.core :as p]
[promesa.exec :as px]
[app.common.exceptions :as ex]
[app.common.spec :as us]
[app.common.uuid :as uuid]
[app.db :as db]
[app.services.queries :as sq]
[app.services.queries.teams :as teams]
[app.util.blob :as blob]
[app.util.data :as data]))
;; --- Helpers & Specs
(s/def ::id ::us/uuid)
(s/def ::profile-id ::us/uuid)
(s/def ::team-id ::us/uuid)
(s/def ::file-id ::us/uuid)
;; --- Query: Colors (by file)
(declare retrieve-colors)
(declare retrieve-file)
(s/def ::colors
(s/keys :req-un [::profile-id ::file-id]))
(sq/defquery ::colors
[{:keys [profile-id file-id] :as params}]
(db/with-atomic [conn db/pool]
(let [file (retrieve-file conn file-id)]
(teams/check-read-permissions! conn profile-id (:team-id file))
(retrieve-colors conn file-id))))
(def ^:private sql:colors
"select *
from color
where color.deleted_at is null
and color.file_id = ?
order by created_at desc")
(defn- retrieve-colors
[conn file-id]
(db/exec! conn [sql:colors file-id]))
(def ^:private sql:retrieve-file
"select file.*,
project.team_id as team_id
from file
inner join project on (project.id = file.project_id)
where file.id = ?")
(defn- retrieve-file
[conn id]
(let [row (db/exec-one! conn [sql:retrieve-file id])]
(when-not row
(ex/raise :type :not-found))
row))
;; --- Query: Color (by ID)
(declare retrieve-color)
(s/def ::id ::us/uuid)
(s/def ::color
(s/keys :req-un [::profile-id ::id]))
(sq/defquery ::color
[{:keys [profile-id id] :as params}]
(db/with-atomic [conn db/pool]
(let [color (retrieve-color conn id)]
(teams/check-read-permissions! conn profile-id (:team-id color))
color)))
(def ^:private sql:single-color
"select color.*,
p.team_id as team_id
from color as color
inner join file as f on (color.file_id = f.id)
inner join project as p on (p.id = f.project_id)
where color.deleted_at is null
and color.id = ?
order by created_at desc")
(defn retrieve-color
[conn id]
(let [row (db/exec-one! conn [sql:single-color id])]
(when-not row
(ex/raise :type :not-found))
row))

View file

@ -299,6 +299,31 @@
(s/def :internal.file/recent-colors
(s/coll-of ::string :kind vector?))
(s/def :internal.typography/id ::id)
(s/def :internal.typography/name ::string)
(s/def :internal.typography/font-id ::string)
(s/def :internal.typography/font-family ::string)
(s/def :internal.typography/font-variant-id ::string)
(s/def :internal.typography/font-size ::string)
(s/def :internal.typography/font-weight ::string)
(s/def :internal.typography/font-style ::string)
(s/def :internal.typography/line-height ::string)
(s/def :internal.typography/letter-spacing ::string)
(s/def :internal.typography/text-transform ::string)
(s/def ::typography
(s/keys :req-un [:internal.typography/id
:internal.typography/name
:internal.typography/font-id
:internal.typography/font-family
:internal.typography/font-variant-id
:internal.typography/font-size
:internal.typography/font-weight
:internal.typography/font-style
:internal.typography/line-height
:internal.typography/letter-spacing
:internal.typography/text-transform]))
(s/def :internal.file/pages
(s/coll-of ::uuid :kind vector?))
@ -412,6 +437,17 @@
(defmethod change-spec :del-component [_]
(s/keys :req-un [::id]))
(s/def :internal.changes.typography/typography ::typography)
(defmethod change-spec :add-typography [_]
(s/keys :req-un [:internal.changes.typography/typography]))
(defmethod change-spec :mod-typography [_]
(s/keys :req-un [:internal.changes.typography/typography]))
(defmethod change-spec :del-typography [_]
(s/keys :req-un [:internal.typography/id]))
(s/def ::change (s/multi-spec change-spec :type))
(s/def ::changes (s/coll-of ::change))
@ -803,6 +839,8 @@
(subvec rc 1)
rc)))))
;; -- Media
(defmethod process-change :add-media
[data {:keys [object]}]
(update data :media assoc (:id object) object))
@ -815,6 +853,8 @@
[data {:keys [id]}]
(update data :media dissoc id))
;; -- Components
(defmethod process-change :add-component
[data {:keys [id name shapes]}]
(assoc-in data [:components id]
@ -833,6 +873,22 @@
[data {:keys [id]}]
(d/dissoc-in data [:components id]))
;; -- Typography
(defmethod process-change :add-typography
[data {:keys [typography]}]
(update data :typography assoc (:id typography) typography))
(defmethod process-change :mod-typography
[data {:keys [typography]}]
(d/update-in-when data [:typography (:id typography)] merge typography))
(defmethod process-change :del-typography
[data {:keys [id]}]
(update data :typography dissoc id))
;; -- Operations
(defmethod process-operation :set
[shape op]
(let [attr (:attr op)

View file

@ -0,0 +1,7 @@
<svg width="40" height="41" viewBox="0 0 40 41" xmlns="http://www.w3.org/2000/svg">
<path d="M27.3455 0.855913C24.5854 1.57398 22.3027 3.61844 21.0431 6.14402C20.1365 7.69681 19.033 9.20989 18.595 10.9747C18.5791 12.5077 20.6042 13.6713 21.8504 12.6729C22.6753 11.9726 23.1234 10.9544 23.7106 10.0663C24.635 8.50138 25.4362 6.73624 26.9321 5.62399C29.1019 4.25506 32.2456 4.42792 34.0446 6.34338C35.8577 8.1232 36.2905 11.1996 34.8418 13.3314C32.9321 16.6169 31.1617 19.9943 29.0399 23.1496C27.4127 25.0133 24.4999 25.5226 22.3148 24.3806C21.3889 23.9825 20.1292 23.8473 19.4164 24.7113C18.5786 25.6499 18.7897 27.3327 19.9352 27.9313C21.5038 28.8608 23.3602 29.3035 25.1804 29.2411C28.4779 29.143 31.7684 27.3471 33.3539 24.3955C35.2416 21.2163 37.1745 18.059 38.8823 14.7782C40.1672 12.4492 40.2813 9.54151 39.3961 7.06543C38.2811 3.73956 35.2239 1.14689 31.7426 0.612769C30.2794 0.366248 28.7587 0.444615 27.3455 0.855913Z"/>
<path d="M21.5425 15.5518C18.5342 14.718 15.2111 15.4777 12.7177 17.3188C9.61691 19.2557 6.23319 20.7461 3.34262 23.0045C0.406478 25.6204 -0.667 30.0993 0.749192 33.7793C2.05062 37.3431 5.55324 39.9812 9.32072 40.1837C11.3238 40.3507 13.3742 39.8349 15.0901 38.8156C16.7842 37.9099 18.4823 36.9731 20.0268 35.832C21.0495 34.7451 20.3594 32.7262 18.9072 32.4378C17.9352 32.2308 17.0769 32.9029 16.2329 33.269C14.3839 34.2493 12.6424 35.605 10.5346 35.969C7.95596 36.1495 5.28979 34.4567 4.60879 31.9141C3.8766 29.5283 4.92467 26.6732 7.15855 25.4846C10.4996 23.5458 13.7869 21.5028 17.2426 19.7703C19.6534 18.9028 22.5372 20.0004 23.8578 22.1732C24.4664 22.9945 25.6092 23.6365 26.6126 23.1317C27.7622 22.6301 28.2785 21.0176 27.4862 19.9962C26.1927 17.801 23.9992 16.1919 21.5425 15.5518Z"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.06066 4.93934C5.64645 4.35355 6.59619 4.35355 7.18198 4.93934L11.0607 8.81802C11.6464 9.40381 11.6464 10.3536 11.0607 10.9393C10.4749 11.5251 9.52513 11.5251 8.93934 10.9393L5.06066 7.06066C4.47487 6.47487 4.47487 5.52513 5.06066 4.93934Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.5 0C14.3284 3.62117e-08 15 0.671573 15 1.5V6.98528C15 7.81371 14.3284 8.48528 13.5 8.48528C12.6716 8.48528 12 7.81371 12 6.98528V1.5C12 0.671573 12.6716 -3.62117e-08 13.5 0Z"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 13.5C0 12.6716 0.671573 12 1.5 12H6.98528C7.81371 12 8.48528 12.6716 8.48528 13.5C8.48528 14.3284 7.81371 15 6.98528 15H1.5C0.671573 15 0 14.3284 0 13.5Z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -2801,5 +2801,8 @@
"ru" : "Кликни чтобы закончить фигуру",
"es" : "Pulsar para cerrar la ruta"
}
}
},
"workspace.assets.typography": "Typographies",
"workspace.libraries.typography": "%s typographies"
}

View file

@ -69,8 +69,8 @@
font-size: $fs13;
padding: $small $x-small;
width: 100%;
align-items: center;
}
}
.element-list {
@ -768,7 +768,7 @@
z-index: 10;
}
.element-set-content .advanced-options {
.advanced-options {
background-color: #303236;
border-radius: 4px;
left: -8px;
@ -876,3 +876,119 @@
.element-set-options-group:hover .element-set-actions {
visibility: visible;
}
.typography-entry {
margin: 0.5rem 0.3rem;
display: flex;
flex-direction: row;
align-items: center;
.typography-selection-wrapper {
display: flex;
flex-direction: row;
align-items: center;
flex: 1;
height: 100%;
&.is-selectable {
cursor: pointer;
}
}
.typography-sample {
font-size: 17px;
color: $color-white;
margin: 0 0.5rem;
font-family: sourcesanspro;
font-style: normal;
font-weight: normal;
}
.typography-name {
flex-grow: 1;
font-size: 11px;
margin-top: 4px;
}
.element-set-actions-button svg {
width: 10px;
height: 10px;
}
}
.asset-group {
.typography-entry {
margin: 0.25rem 0;
}
.element-set-content .font-option,
.element-set-content .size-option {
margin: 0.5rem 0;
}
.element-set-content .variant-option {
margin-left: 0.5rem;
}
}
.row-flex input.adv-typography-name {
font-size: 14px;
color: $color-gray-10;
width: 100%;
max-width: none;
margin: 0;
background: #303236;
border-top: none;
border-left: none;
border-right: none;
}
.size-option .custom-select-dropdown {
position: fixed;
max-height: 15rem;
min-width: 6rem;
margin-top: 25px;
left: initial;
}
.typography-read-only-data {
font-size: 12px;
color: $color-white;
.typography-name {
font-size: 14px;
}
.row-flex {
padding: 0.5rem 0;
}
.label {
color: $color-gray-30;
&::after {
content: ':';
margin-right: 0.25rem;
}
}
.go-to-lib-button {
transition: border 0.3s, color 0.3s;
text-align: center;
background: $color-gray-60;
padding: 0.5rem;
border-radius: 2px;
cursor: pointer;
font-size: 14px;
margin-top: 1rem;
border: 1px solid $color-gray-60;
&:hover {
border: 1px solid $color-primary;
color: $color-primary;
}
}
}

View file

@ -25,29 +25,6 @@
[app.main.data.modal :as md]
[app.common.pages-helpers :as cph]))
(declare create-color-result)
(defn create-color
[file-id color]
(s/assert (s/nilable uuid?) file-id)
(ptk/reify ::create-color
ptk/WatchEvent
(watch [_ state s]
(->> (rp/mutation! :create-color {:file-id file-id
:content color
:name color})
(rx/map (partial create-color-result file-id))))))
(defn create-color-result
[file-id color]
(ptk/reify ::create-color-result
ptk/UpdateEvent
(update [_ state]
(-> state
(update-in [:workspace-file :colors] #(conj % color))
(assoc-in [:workspace-local :color-for-rename] (:id color))))))
(def clear-color-for-rename
(ptk/reify ::clear-color-for-rename
ptk/UpdateEvent
@ -73,44 +50,6 @@
(-> state
(update-in [:workspace-file :colors] #(d/replace-by-id % color))))))
(declare update-color-result)
(defn update-color
[file-id color-id content]
(ptk/reify ::update-color
ptk/WatchEvent
(watch [_ state stream]
(->> (rp/mutation! :update-color {:id color-id
:content content})
(rx/map (partial update-color-result file-id))))))
(defn update-color-result
[file-id color]
(ptk/reify ::update-color-result
ptk/UpdateEvent
(update [_ state]
(-> state
(update-in [:workspace-file :colors] #(d/replace-by-id % color))))))
(declare delete-color-result)
(defn delete-color
[file-id color-id]
(ptk/reify ::delete-color
ptk/WatchEvent
(watch [_ state stream]
(->> (rp/mutation! :delete-color {:id color-id})
(rx/map #(delete-color-result file-id color-id))))))
(defn delete-color-result
[file-id color-id]
(ptk/reify ::delete-color-result
ptk/UpdateEvent
(update [_ state]
(-> state
(update-in [:workspace-file :colors]
(fn [colors] (filter #(not= (:id %) color-id) colors)))))))
(defn change-palette-size [size]
(s/assert #{:big :small} size)
(ptk/reify ::change-palette-size

View file

@ -73,9 +73,10 @@
(s/def ::layout-flags (s/coll-of ::layout-flag))
(def default-layout
#{:sitemap
:sitemap-pages
:layers
#{;; :sitemap
;; :sitemap-pages
;; :layers
:assets
:element-options
:rules
:display-grid

View file

@ -519,3 +519,56 @@
:callback do-dismiss}]
:sync-dialog))))))
(def default-typography
{:name "Source Sans Pro Regular"
:font-id "sourcesanspro"
:font-family "sourcesanspro"
:font-variant-id "regular"
:font-size "14"
:font-weight "400"
:font-style "normal"
:line-height "1.2"
:letter-spacing "0"
:text-transform "none"})
(defn add-typography
[typography]
(let [typography (update typography :id #(or % (uuid/next)))]
(us/assert ::cp/typography typography)
(ptk/reify ::add-typography
ptk/WatchEvent
(watch [_ state s]
(let [rchg {:type :add-typography
:typography typography}
uchg {:type :del-typography
:id (:id typography)}]
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true})))))))
(defn update-typography
[typography]
(us/assert ::cp/typography typography)
(ptk/reify ::update-typography
ptk/WatchEvent
(watch [_ state stream]
(let [prev (get-in state [:workspace-data :typography (:id typography)])
rchg {:type :mod-typography
:typography typography}
uchg {:type :mod-typography
:typography prev}]
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true})
(sync-file nil))))))
(defn delete-typography
[id]
(us/assert ::us/uuid id)
(ptk/reify ::delete-typography
ptk/WatchEvent
(watch [_ state stream]
(let [prev (get-in state [:workspace-data :typography id])
rchg {:type :del-typography
:id id}
uchg {:type :add-typography
:typography prev}]
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))

View file

@ -300,8 +300,7 @@
(rx/mapcat
#(rx/zip (rp/query :file-library {:file-id library-id})
(rp/query :media-objects {:file-id library-id
:is-local false})
(rp/query :colors {:file-id library-id}))))
:is-local false}))))
(rx/map file-linked))))))
(defn file-linked

View file

@ -96,6 +96,9 @@
(register! :builtin local-fonts)
(register! :google google-fonts)
(defn get-font-data [id]
(get @fontsdb id))
(defn resolve-variants
[id]
(get-in @fontsdb [id :variants]))
@ -164,3 +167,8 @@
(defn ready [cb]
(-> (obj/get-in js/document ["fonts" "ready"])
(p/then cb)))
(defn get-default-variant [{:keys [variants]}]
(or
(d/seek #(or (= (:id %) "regular") (= (:name %) "regular")) variants)
(first variants)))

View file

@ -97,6 +97,12 @@
(get-in state [:workspace-data :recent-colors] []))
st/state))
(def workspace-file-typography
(l/derived (fn [state]
(when-let [file (:workspace-file state)]
(get-in file [:data :typography])))
st/state))
(def workspace-project
(l/derived :workspace-project st/state))

View file

@ -28,6 +28,7 @@
(def auto-width (icon-xref :auto-width))
(def box (icon-xref :box))
(def chain (icon-xref :chain))
(def unchain (icon-xref :unchain))
(def chat (icon-xref :chat))
(def circle (icon-xref :circle))
(def close (icon-xref :close))

View file

@ -27,7 +27,8 @@
[library]
(let [components-count (count (get-in library [:data :components] []))
graphics-count (count (get-in library [:data :media] []))
colors-count (count (get-in library [:data :colors] []))]
colors-count (count (get-in library [:data :colors] []))
typography-count (count (get-in library [:data :typography] []))]
;; Include a &nbsp; so this block has always some content
(str
(str/join " · "
@ -39,7 +40,10 @@
(conj (tr "workspace.libraries.graphics" graphics-count))
(< 0 colors-count)
(conj (tr "workspace.libraries.colors" colors-count))))
(conj (tr "workspace.libraries.colors" colors-count))
(< 0 typography-count)
(conj (tr "workspace.libraries.typography" typography-count))))
"\u00A0")))
(mf/defc libraries-tab

View file

@ -289,12 +289,15 @@
(dom/prevent-default event)
(dom/stop-propagation event)
(let [sidebar (dom/get-element "settings-bar")
assets (dom/get-element-by-class "assets-bar")
cpicker (dom/get-element-by-class "colorpicker-tooltip")
self (mf/ref-val self-ref)
target (dom/get-target event)
selecting? (mf/ref-val selecting-ref)]
(when-not (or (.contains sidebar target)
(.contains assets target)
(.contains self target)
(and cpicker (.contains cpicker target)))
(if selecting?
@ -340,7 +343,8 @@
(when (not read-only?)
(let [content (js->clj val :keywordize-keys true)
content (first content)]
(st/emit! (dw/update-shape id {:content content}))
;; Append timestamp so we can react to cursor change events
(st/emit! (dw/update-shape id {:content (assoc content :ts (js->clj (.now js/Date)))}))
(reset! state val)
(reset! content-var content)))))]

View file

@ -19,6 +19,7 @@
[app.config :as cfg]
[app.main.data.workspace :as dw]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.texts :as dwt]
[app.main.data.colors :as dc]
[app.main.refs :as refs]
[app.main.store :as st]
@ -26,6 +27,7 @@
[app.main.ui.components.context-menu :refer [context-menu]]
[app.main.ui.components.file-uploader :refer [file-uploader]]
[app.main.ui.components.tab-container :refer [tab-container tab-element]]
[app.main.ui.workspace.sidebar.options.typography :refer [typography-entry]]
[app.main.ui.icons :as i]
[app.main.ui.keyboard :as kbd]
[app.main.ui.modal :as modal]
@ -41,7 +43,7 @@
[rumext.alpha :as mf]))
(mf/defc components-box
[{:keys [file-id local? components] :as props}]
[{:keys [file-id local? components open? on-open on-close] :as props}]
(let [state (mf/use-state {:menu-open false
:top nil
:left nil
@ -75,27 +77,28 @@
(dnd/set-allowed-effect! event "move")))]
[:div.asset-group
[:div.group-title
(tr "workspace.assets.components")
[:div.group-title {:class (when (not open?) "closed")}
[:span {:on-click #(if open? (on-close) (on-open))} i/arrow-slide (tr "workspace.assets.components")]
[:span (str "\u00A0(") (count components) ")"]] ;; Unicode 00A0 is non-breaking space
[:div.group-grid.big
(for [component components]
[:div.grid-cell {:key (:id component)
:draggable true
:on-context-menu (on-context-menu (:id component))
:on-drag-start (partial on-drag-start component)}
[:& exports/component-svg {:group (get-in component [:objects (:id component)])
:objects (:objects component)}]
[:div.cell-name (:name component)]])
(when open?
[:div.group-grid.big
(for [component components]
[:div.grid-cell {:key (:id component)
:draggable true
:on-context-menu (on-context-menu (:id component))
:on-drag-start (partial on-drag-start component)}
[:& exports/component-svg {:group (get-in component [:objects (:id component)])
:objects (:objects component)}]
[:div.cell-name (:name component)]])])
(when local?
[:& context-menu
{:selectable false
:show (:menu-open @state)
:on-close #(swap! state assoc :menu-open false)
:top (:top @state)
:left (:left @state)
:options [[(tr "workspace.assets.delete") on-delete]]}])]]))
(when local?
[:& context-menu
{:selectable false
:show (:menu-open @state)
:on-close #(swap! state assoc :menu-open false)
:top (:top @state)
:left (:left @state)
:options [[(tr "workspace.assets.delete") on-delete]]}])]))
(mf/defc graphics-box
[{:keys [file-id local? objects open? on-open on-close] :as props}]
@ -326,6 +329,88 @@
:local? local?
:locale locale}])])]))
(mf/defc typography-box
[{:keys [file-id local? typographies locale open? on-open on-close] :as props}]
(let [state (mf/use-state {:detail-open? false
:menu-open? false
:top nil
:left nil})
selected (mf/deref refs/selected-shapes)
add-typography
(mf/use-callback
(mf/deps file-id)
(fn [value opacity]
(st/emit! (dwl/add-typography dwl/default-typography))))
handle-change
(mf/use-callback
(mf/deps file-id)
(fn [typography changes]
(st/emit! (dwl/update-typography (merge typography changes)))))
handle-typography-selection
(fn [typography]
(let [attrs (merge
{:typography-ref-file (when-not local? file-id)
:typography-ref-id (:id typography)}
(d/without-keys typography [:id :name]))]
(run! #(st/emit! (dwt/update-text-attrs {:id % :editor nil :attrs attrs}))
selected)))
on-context-menu
(fn [id event]
(when local?
(let [pos (dom/get-client-position event)
top (:y pos)
left (- (:x pos) 20)]
(dom/prevent-default event)
(swap! state assoc
:menu-open? true
:top top
:left left
:id id))))
closed-typography-edit
(mf/use-callback
(mf/deps file-id)
(fn [event] ))
handle-rename-typography-clicked (fn [])
handle-edit-typography-clicked (fn [] )
handle-delete-typography (fn []
(st/emit! (dwl/delete-typography (:id @state))))]
[:div.asset-group
[:div.group-title {:class (when (not open?) "closed")}
[:span {:on-click #(if open? (on-close) (on-open))} i/arrow-slide "Typography" #_(t locale "workspace.assets.typography")]
[:span.num-assets (str "\u00A0(") (count typographies) ")"] ;; Unicode 00A0 is non-breaking space
(when local?
[:div.group-button {:on-click add-typography} i/plus])]
[:& context-menu
{:selectable false
:show (:menu-open? @state)
:on-close #(swap! state assoc :menu-open? false)
:top (:top @state)
:left (:left @state)
:options [[(t locale "workspace.assets.rename") handle-rename-typography-clicked]
[(t locale "workspace.assets.edit") handle-edit-typography-clicked]
[(t locale "workspace.assets.delete") handle-delete-typography]]}]
(when open?
[:div.group-list
(for [typography (sort-by (comp - :ts) typographies)]
[:& typography-entry
{:key (:id typography)
:typography typography
:read-only? (not local?)
:on-context-menu #(on-context-menu (:id typography) %)
:on-change #(handle-change typography %)
:on-select #(handle-typography-selection typography)}])])]))
(defn file-colors-ref
[id]
(l/derived (fn [state]
@ -354,6 +439,15 @@
(vals (get-in state [:workspace-libraries id :data :components])))))
st/state =))
(defn file-typography-ref
[id]
(l/derived (fn [state]
(let [wfile (:workspace-file state)]
(if (= (:id wfile) id)
(vals (get-in wfile [:data :typography]))
(vals (get-in state [:workspace-libraries id :data :typography])))))
st/state =))
(defn apply-filters
[coll filters]
(->> coll
@ -369,7 +463,10 @@
router (mf/deref refs/router)
toggle-open #(swap! open? not)
toggles (mf/use-state #{:graphics :colors})
toggles (mf/use-state #{:components
:graphics
:colors
:typography})
url (rt/resolve router :workspace
{:project-id (:project-id file)
@ -379,6 +476,9 @@
colors-ref (mf/use-memo (mf/deps (:id file)) #(file-colors-ref (:id file)))
colors (apply-filters (mf/deref colors-ref) filters)
typography-ref (mf/use-memo (mf/deps (:id file)) #(file-typography-ref (:id file)))
typographies (apply-filters (mf/deref typography-ref) filters)
media-ref (mf/use-memo (mf/deps (:id file)) #(file-media-ref (:id file)))
media (apply-filters (mf/deref media-ref) filters)
@ -413,13 +513,20 @@
(str/empty? (:term filters))))
show-colors? (and (or (= (:box filters) :all)
(= (:box filters) :colors))
(or (> (count colors) 0)
(str/empty? (:term filters))))
show-typography? (and (or (= (:box filters) :all)
(= (:box filters) :typography))
(or (> (count colors) 0)
(str/empty? (:term filters))))]
[:div.tool-window-content
(when show-components?
[:& components-box {:file-id (:id file)
:local? local?
:components components}])
:components components
:open? (contains? @toggles :components)
:on-open #(swap! toggles conj :components)
:on-close #(swap! toggles disj :components)}])
(when show-graphics?
[:& graphics-box {:file-id (:id file)
:local? local?
@ -436,6 +543,15 @@
:on-open #(swap! toggles conj :colors)
:on-close #(swap! toggles disj :colors)}])
(when show-typography?
[:& typography-box {:file-id (:id file)
:local? local?
:locale locale
:typographies typographies
:open? (contains? @toggles :typography)
:on-open #(swap! toggles conj :typography)
:on-close #(swap! toggles disj :typography)}])
(when (and (not show-components?) (not show-graphics?) (not show-colors?))
[:div.asset-group
[:div.group-title (t locale "workspace.assets.not-found")]])]))]))
@ -495,8 +611,10 @@
[:select.input-select {:value (:box @filters)
:on-change on-box-filter-change}
[:option {:value ":all"} (t locale "workspace.assets.box-filter-all")]
[:option {:value ":graphics"} (t locale "workspace.assets.box-filter-graphics")]
[:option {:value ":colors"} (t locale "workspace.assets.box-filter-colors")]]]]
[:option {:value ":components"} (t locale "workspace.assets.components")]
[:option {:value ":graphics"} (t locale "workspace.assets.graphics")]
[:option {:value ":colors"} (t locale "workspace.assets.colors")]
[:option {:value ":typography"} (t locale "workspace.assets.typography")]]]]
[:div.libraries-wrapper
[:& file-library

View file

@ -144,11 +144,11 @@
[:& text-menu {:ids text-ids
:type :multiple
:editor nil
:font-values font-values
:align-values align-values
:spacing-values spacing-values
:valign-values valign-values
:decoration-values decoration-values
:transform-values transform-values
:values (merge font-values
align-values
spacing-values
valign-values
decoration-values
transform-values)
:shapes shapes}])]))

View file

@ -14,20 +14,23 @@
[okulary.core :as l]
[app.main.ui.icons :as i]
[app.common.data :as d]
[app.common.uuid :as uuid]
[app.main.data.workspace :as dw]
[app.main.data.workspace.common :as dwc]
[app.main.data.workspace.texts :as dwt]
[app.main.data.workspace.libraries :as dwl]
[app.main.store :as st]
[app.main.refs :as refs]
[app.main.ui.workspace.sidebar.options.measures :refer [measure-attrs measures-menu]]
[app.main.ui.workspace.sidebar.options.fill :refer [fill-menu]]
[app.main.ui.workspace.sidebar.options.shadow :refer [shadow-menu]]
[app.main.ui.components.editable-select :refer [editable-select]]
[app.main.ui.workspace.sidebar.options.typography :refer [typography-entry typography-options]]
[app.util.dom :as dom]
[app.main.fonts :as fonts]
[app.util.i18n :as i18n :refer [tr t]]
["slate" :refer [Transforms]]))
(def text-typography-attrs [:typography-ref-id :typography-ref-file])
(def text-fill-attrs [:fill :opacity])
(def text-font-attrs [:font-id :font-family :font-variant-id :font-size :font-weight :font-style])
(def text-align-attrs [:text-align])
@ -36,205 +39,52 @@
(def text-decoration-attrs [:text-decoration])
(def text-transform-attrs [:text-transform])
(defn- attr->string [value]
(if (= value :multiple)
""
(str value)))
(def ^:private editor-ref
(l/derived :editor refs/workspace-local))
(mf/defc font-select-optgroups
{::mf/wrap [mf/memo]}
[]
[:*
[:optgroup {:label "Local"}
(for [font fonts/local-fonts]
[:option {:value (:id font)
:key (:id font)}
(:name font)])]
[:optgroup {:label "Google"}
(for [font (fonts/resolve-fonts :google)]
[:option {:value (:id font)
:key (:id font)}
(:name font)])]])
(mf/defc font-options
[{:keys [editor ids values locale] :as props}]
(let [{:keys [font-id
font-size
font-variant-id]} values
font-id (or font-id "sourcesanspro")
font-size (or font-size "14")
font-variant-id (or font-variant-id "regular")
fonts (mf/deref fonts/fontsdb)
font (get fonts font-id)
change-font
(fn [new-font-id]
(run! #(st/emit! (dwt/update-text-attrs
{:id %
:editor editor
:attrs {:font-id new-font-id
:font-family (:family (get fonts new-font-id))
:font-variant-id nil
:font-weight nil
:font-style nil}}))
ids))
on-font-family-change
(fn [event]
(let [new-font-id (-> (dom/get-target event)
(dom/get-value))]
(when-not (str/empty? new-font-id)
(let [font (get fonts new-font-id)]
(fonts/ensure-loaded! new-font-id (partial change-font new-font-id))))))
on-font-size-change
(fn [new-font-size]
(when-not (str/empty? new-font-size)
(run! #(st/emit! (dwt/update-text-attrs
{:id %
:editor editor
:attrs {:font-size (str new-font-size)}}))
ids)))
on-font-variant-change
(fn [event]
(let [new-variant-id (-> (dom/get-target event)
(dom/get-value))
variant (d/seek #(= new-variant-id (:id %)) (:variants font))]
(run! #(st/emit! (dwt/update-text-attrs
{:id %
:editor editor
:attrs {:font-id (:id font)
:font-family (:family font)
:font-variant-id new-variant-id
:font-weight (:weight variant)
:font-style (:style variant)}}))
ids)))]
[:*
[:div.row-flex
[:select.input-select {:value (attr->string font-id)
:on-change on-font-family-change}
(when (= font-id :multiple)
[:option {:value ""} (t locale "settings.multiple")])
[:& font-select-optgroups]]]
[:div.row-flex
(let [size-options [8 9 10 11 12 14 18 24 36 48 72]
size-options (if (= font-size :multiple) (concat [{:value "" :label "--"}] size-options) size-options)]
[:& editable-select
{:value (attr->string font-size)
:class "input-option"
:options size-options
:type "number"
:placeholder "--"
:on-change on-font-size-change}])
[:select.input-select {:value (attr->string font-variant-id)
:on-change on-font-variant-change}
(when (= font-size :multiple)
[:option {:value ""} "--"])
(for [variant (:variants font)]
[:option {:value (:id variant)
:key (pr-str variant)}
(:name variant)])]]]))
(def root-attrs (d/concat text-valign-attrs
text-align-attrs))
(def paragraph-attrs text-align-attrs)
(def text-attrs (d/concat text-typography-attrs
text-font-attrs
text-align-attrs
text-spacing-attrs
text-decoration-attrs
text-transform-attrs))
(mf/defc text-align-options
[{:keys [editor ids values locale] :as props}]
[{:keys [editor ids values locale on-change] :as props}]
(let [{:keys [text-align]} values
text-align (or text-align "left")
on-change
handle-change
(fn [event new-align]
(run! #(st/emit!
(dwt/update-root-attrs
{:id %
:editor editor
:attrs {:text-align new-align}})
(dwt/update-paragraph-attrs
{:id %
:editor editor
:attrs {:text-align new-align}}))
ids))]
(on-change {:text-align new-align}))]
;; --- Align
[:div.row-flex.align-icons
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.align-left")
:class (dom/classnames :current (= "left" text-align))
:on-click #(on-change % "left")}
:on-click #(handle-change % "left")}
i/text-align-left]
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.align-center")
:class (dom/classnames :current (= "center" text-align))
:on-click #(on-change % "center")}
:on-click #(handle-change % "center")}
i/text-align-center]
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.align-right")
:class (dom/classnames :current (= "right" text-align))
:on-click #(on-change % "right")}
:on-click #(handle-change % "right")}
i/text-align-right]
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.align-justify")
:class (dom/classnames :current (= "justify" text-align))
:on-click #(on-change % "justify")}
:on-click #(handle-change % "justify")}
i/text-align-justify]]))
(mf/defc spacing-options
[{:keys [editor ids values locale] :as props}]
(let [{:keys [line-height
letter-spacing]} values
line-height (or line-height "1.2")
letter-spacing (or letter-spacing "0")
on-change
(fn [event attr]
(let [new-spacing (-> (dom/get-target event)
(dom/get-value))]
(run! #(st/emit! (dwt/update-text-attrs
{:id %
:editor editor
:attrs {attr new-spacing}}))
ids)))]
[:div.row-flex
[:div.input-icon
[:span.icon-before.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.line-height")}
i/line-height]
[:input.input-text
{:type "number"
:step "0.1"
:min "0"
:max "200"
:value (attr->string line-height)
:placeholder (t locale "settings.multiple")
:on-change #(on-change % :line-height)}]]
[:div.input-icon
[:span.icon-before.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.letter-spacing")}
i/letter-spacing]
[:input.input-text
{:type "number"
:step "0.1"
:min "0"
:max "200"
:value (attr->string letter-spacing)
:placeholder (t locale "settings.multiple")
:on-change #(on-change % :letter-spacing)}]]]))
(mf/defc additional-options
[{:keys [shapes editor ids values locale] :as props}]
[{:keys [shapes editor ids values locale on-change] :as props}]
(let [{:keys [vertical-align]} values
to-single-value (fn [coll] (if (> (count coll) 1) nil (first coll)))
@ -243,150 +93,171 @@
vertical-align (or vertical-align "top")
on-change-grow
handle-change-grow
(fn [event grow-type]
(st/emit! (dwc/update-shapes ids #(assoc % :grow-type grow-type))))
on-change
handle-change
(fn [event new-align]
(run! #(st/emit! (dwt/update-root-attrs
{:id %
:editor editor
:attrs {:vertical-align new-align}}))
ids))]
(on-change {:vertical-align new-align}))]
[:div.row-flex
[:div.align-icons
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.align-top")
:class (dom/classnames :current (= "top" vertical-align))
:on-click #(on-change % "top")}
:on-click #(handle-change % "top")}
i/align-top]
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.align-middle")
:class (dom/classnames :current (= "center" vertical-align))
:on-click #(on-change % "center")}
:on-click #(handle-change % "center")}
i/align-middle]
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.align-bottom")
:class (dom/classnames :current (= "bottom" vertical-align))
:on-click #(on-change % "bottom")}
:on-click #(handle-change % "bottom")}
i/align-bottom]]
[:div.align-icons
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.grow-fixed")
:class (dom/classnames :current (= :fixed grow-type))
:on-click #(on-change-grow % :fixed)}
:on-click #(handle-change-grow % :fixed)}
i/auto-fix]
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.grow-auto-width")
:class (dom/classnames :current (= :auto-width grow-type))
:on-click #(on-change-grow % :auto-width)}
:on-click #(handle-change-grow % :auto-width)}
i/auto-width]
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.grow-auto-height")
:class (dom/classnames :current (= :auto-height grow-type))
:on-click #(on-change-grow % :auto-height)}
:on-click #(handle-change-grow % :auto-height)}
i/auto-height]]]))
(mf/defc text-decoration-options
[{:keys [editor ids values locale] :as props}]
[{:keys [editor ids values locale on-change] :as props}]
(let [{:keys [text-decoration]} values
text-decoration (or text-decoration "none")
on-change
handle-change
(fn [event type]
(run! #(st/emit! (dwt/update-text-attrs
{:id %
:editor editor
:attrs {:text-decoration type}}))
ids))]
(on-change {:text-decoration type}))]
[:div.row-flex
[:span.element-set-subtitle (t locale "workspace.options.text-options.decoration")]
[:div.align-icons
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.none")
:class (dom/classnames :current (= "none" text-decoration))
:on-click #(on-change % "none")}
:on-click #(handle-change % "none")}
i/minus]
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.underline")
:class (dom/classnames :current (= "underline" text-decoration))
:on-click #(on-change % "underline")}
:on-click #(handle-change % "underline")}
i/underline]
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.strikethrough")
:class (dom/classnames :current (= "line-through" text-decoration))
:on-click #(on-change % "line-through")}
:on-click #(handle-change % "line-through")}
i/strikethrough]]]))
(mf/defc text-transform-options
[{:keys [editor ids values locale] :as props}]
(let [{:keys [text-transform]} values
text-transform (or text-transform "none")
on-change
(fn [event type]
(run! #(st/emit! (dwt/update-text-attrs
{:id %
:editor editor
:attrs {:text-transform type}}))
ids))]
[:div.row-flex
[:span.element-set-subtitle (t locale "workspace.options.text-options.text-case")]
[:div.align-icons
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.none")
:class (dom/classnames :current (= "none" text-transform))
:on-click #(on-change % "none")}
i/minus]
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.uppercase")
:class (dom/classnames :current (= "uppercase" text-transform))
:on-click #(on-change % "uppercase")}
i/uppercase]
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.lowercase")
:class (dom/classnames :current (= "lowercase" text-transform))
:on-click #(on-change % "lowercase")}
i/lowercase]
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.titlecase")
:class (dom/classnames :current (= "capitalize" text-transform))
:on-click #(on-change % "capitalize")}
i/titlecase]]]))
(defn generate-typography-name [{:keys [font-id font-variant-id] :as typography}]
(let [{:keys [name]} (fonts/get-font-data font-id)]
(-> typography
(assoc :name (str name " " (str/title font-variant-id))))) )
(mf/defc text-menu
{::mf/wrap [mf/memo]}
[{:keys [ids
type
editor
font-values
align-values
spacing-values
valign-values
decoration-values
transform-values
values
shapes] :as props}]
(let [locale (mf/deref i18n/locale)
typographies (mf/deref refs/workspace-file-typography)
shared-libs (mf/deref refs/workspace-libraries)
label (case type
:multiple (t locale "workspace.options.text-options.title-selection")
:group (t locale "workspace.options.text-options.title-group")
(t locale "workspace.options.text-options.title"))]
[:div.element-set
[:div.element-set-title label]
[:div.element-set-content
[:& font-options {:editor editor :ids ids :values font-values :locale locale}]
[:& text-align-options {:editor editor :ids ids :values align-values :locale locale}]
[:& spacing-options {:editor editor :ids ids :values spacing-values :locale locale}]
[:& additional-options {:shapes shapes :editor editor :ids ids :values valign-values :locale locale}]
[:& text-decoration-options {:editor editor :ids ids :values decoration-values :locale locale}]
[:& text-transform-options {:editor editor :ids ids :values transform-values :locale locale}]]]))
(t locale "workspace.options.text-options.title"))
emit-update!
(fn [id attrs]
(let [attrs (select-keys attrs root-attrs)]
(when-not (empty? attrs)
(st/emit! (dwt/update-root-attrs {:id id :editor editor :attrs attrs}))))
(let [attrs (select-keys attrs paragraph-attrs)]
(when-not (empty? attrs)
(st/emit! (dwt/update-paragraph-attrs {:id id :editor editor :attrs attrs}))))
(let [attrs (select-keys attrs text-attrs)]
(when-not (empty? attrs)
(st/emit! (dwt/update-text-attrs {:id id :editor editor :attrs attrs})))))
typography (cond
(and (:typography-ref-id values)
(:typography-ref-file values))
(-> shared-libs
(get-in [(:typography-ref-file values) :data :typography (:typography-ref-id values)])
(assoc :file-id (:typography-ref-file values)))
(:typography-ref-id values)
(get typographies (:typography-ref-id values)))
handle-click
(mf/use-callback
(mf/deps values)
(fn [event]
(let [setted-values (-> (d/without-nils values)
(select-keys
(d/concat text-font-attrs
text-spacing-attrs
text-transform-attrs)))
typography (merge dwl/default-typography setted-values)
typography (generate-typography-name typography)]
(let [id (uuid/next)]
(st/emit! (dwl/add-typography (assoc typography :id id)))
(run! #(emit-update! % {:typography-ref-id id}) ids)))))
handle-deattach-typography
(fn []
(run! #(emit-update! % {:typography-ref-file nil
:typography-ref-id nil})
ids))
handle-change-typography
(fn [changes]
(st/emit! (dwl/update-typography (merge typography changes))))
opts #js {:editor editor
:ids ids
:values values
:on-change (fn [attrs]
(run! #(emit-update! % attrs) ids))
:locale locale}]
[:div.element-set
[:div.element-set-title
[:span label]
[:div.add-page {:on-click handle-click} i/close]]
(if typography
[:& typography-entry {:typography typography
:on-deattach handle-deattach-typography
:on-change handle-change-typography}]
[:> typography-options opts])
[:div.element-set-content
[:> text-align-options opts]
[:> additional-options opts]
[:> text-decoration-options opts]]]))
(mf/defc options
[{:keys [shape] :as props}]
@ -399,42 +270,25 @@
measure-values (select-keys shape measure-attrs)
fill-values (dwt/current-text-values
{:editor editor
:shape shape
:attrs text-fill-attrs})
{:editor editor
:shape shape
:attrs text-fill-attrs})
converted-fill-values {:fill-color (:fill fill-values)
:fill-opacity (:opacity fill-values)}
font-values (dwt/current-text-values
{:editor editor
:shape shape
:attrs text-font-attrs})
align-values (dwt/current-paragraph-values
{:editor editor
:shape shape
:attrs text-align-attrs})
text-values (merge
(dwt/current-root-values
{:editor editor :shape shape
:attrs root-attrs})
(dwt/current-text-values
{:editor editor :shape shape
:attrs paragraph-attrs})
(dwt/current-text-values
{:editor editor :shape shape
:attrs text-attrs}))]
spacing-values (dwt/current-text-values
{:editor editor
:shape shape
:attrs text-spacing-attrs})
valign-values (dwt/current-root-values
{:editor editor
:shape shape
:attrs text-valign-attrs})
decoration-values (dwt/current-text-values
{:editor editor
:shape shape
:attrs text-decoration-attrs})
transform-values (dwt/current-text-values
{:editor editor
:shape shape
:attrs text-transform-attrs})]
[:*
[:& measures-menu {:ids ids
:type type
@ -448,11 +302,5 @@
:values (select-keys shape [:shadow])}]
[:& text-menu {:ids ids
:type type
:editor editor
:font-values font-values
:align-values align-values
:spacing-values spacing-values
:valign-values valign-values
:decoration-values decoration-values
:transform-values transform-values
:values text-values
:shapes [shape]}]]))

View file

@ -0,0 +1,279 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns app.main.ui.workspace.sidebar.options.typography
(:require
[rumext.alpha :as mf]
[cuerdas.core :as str]
[app.main.ui.icons :as i]
[app.main.refs :as refs]
[app.main.store :as st]
[app.common.data :as d]
[app.main.data.workspace.texts :as dwt]
[app.main.ui.components.editable-select :refer [editable-select]]
[app.main.ui.workspace.sidebar.options.common :refer [advanced-options]]
[app.util.dom :as dom]
[app.main.fonts :as fonts]
[app.util.i18n :as i18n :refer [t]]))
(defn- attr->string [value]
(if (= value :multiple)
""
(str value)))
(mf/defc font-select-optgroups
{::mf/wrap [mf/memo]}
[]
[:*
[:optgroup {:label "Local"}
(for [font fonts/local-fonts]
[:option {:value (:id font)
:key (:id font)}
(:name font)])]
[:optgroup {:label "Google"}
(for [font (fonts/resolve-fonts :google)]
[:option {:value (:id font)
:key (:id font)}
(:name font)])]])
(mf/defc font-options
[{:keys [editor ids values locale on-change] :as props}]
(let [{:keys [font-id
font-size
font-variant-id]} values
font-id (or font-id "sourcesanspro")
font-size (or font-size "14")
font-variant-id (or font-variant-id "regular")
fonts (mf/deref fonts/fontsdb)
font (get fonts font-id)
change-font
(fn [new-font-id]
(let [{:keys [family] :as font} (get fonts new-font-id)
{:keys [id name weight style]} (fonts/get-default-variant font)]
(on-change {:font-id new-font-id
:font-family family
:font-variant-id (or id name)
:font-weight weight
:font-style style})))
on-font-family-change
(fn [event]
(let [new-font-id (dom/get-target-val event)]
(when-not (str/empty? new-font-id)
(let [font (get fonts new-font-id)]
(fonts/ensure-loaded! new-font-id (partial change-font new-font-id))))))
on-font-size-change
(fn [new-font-size]
(when-not (str/empty? new-font-size)
(on-change {:font-size (str new-font-size)})))
on-font-variant-change
(fn [event]
(let [new-variant-id (dom/get-target-val event)
variant (d/seek #(= new-variant-id (:id %)) (:variants font))]
(on-change {:font-id (:id font)
:font-family (:family font)
:font-variant-id new-variant-id
:font-weight (:weight variant)
:font-style (:style variant)})))]
[:*
[:div.row-flex
[:select.input-select.font-option
{:value (attr->string font-id)
:on-change on-font-family-change}
(when (= font-id :multiple)
[:option {:value ""} (t locale "settings.multiple")])
[:& font-select-optgroups]]]
[:div.row-flex
(let [size-options [8 9 10 11 12 14 18 24 36 48 72]
size-options (if (= font-size :multiple) (into [{:value "" :label "--"}] size-options) size-options)]
[:& editable-select
{:value (attr->string font-size)
:class "input-option size-option"
:options size-options
:type "number"
:placeholder "--"
:on-change on-font-size-change}])
[:select.input-select.variant-option
{:value (attr->string font-variant-id)
:on-change on-font-variant-change}
(when (= font-size :multiple)
[:option {:value ""} "--"])
(for [variant (:variants font)]
[:option {:value (:id variant)
:key (pr-str variant)}
(:name variant)])]]]))
(mf/defc spacing-options
[{:keys [editor ids values locale on-change] :as props}]
(let [{:keys [line-height
letter-spacing]} values
line-height (or line-height "1.2")
letter-spacing (or letter-spacing "0")
handle-change
(fn [event attr]
(let [new-spacing (dom/get-target-val event)]
(on-change {attr new-spacing})))]
[:div.row-flex
[:div.input-icon
[:span.icon-before.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.line-height")}
i/line-height]
[:input.input-text
{:type "number"
:step "0.1"
:min "0"
:max "200"
:value (attr->string line-height)
:placeholder (t locale "settings.multiple")
:on-change #(handle-change % :line-height)}]]
[:div.input-icon
[:span.icon-before.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.letter-spacing")}
i/letter-spacing]
[:input.input-text
{:type "number"
:step "0.1"
:min "0"
:max "200"
:value (attr->string letter-spacing)
:placeholder (t locale "settings.multiple")
:on-change #(handle-change % :letter-spacing)}]]]))
(mf/defc text-transform-options
[{:keys [editor ids values locale on-change] :as props}]
(let [{:keys [text-transform]} values
text-transform (or text-transform "none")
handle-change
(fn [event type]
(on-change {:text-transform type}))]
[:div.row-flex
[:span.element-set-subtitle (t locale "workspace.options.text-options.text-case")]
[:div.align-icons
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.none")
:class (dom/classnames :current (= "none" text-transform))
:on-click #(handle-change % "none")}
i/minus]
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.uppercase")
:class (dom/classnames :current (= "uppercase" text-transform))
:on-click #(handle-change % "uppercase")}
i/uppercase]
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.lowercase")
:class (dom/classnames :current (= "lowercase" text-transform))
:on-click #(handle-change % "lowercase")}
i/lowercase]
[:span.tooltip.tooltip-bottom
{:alt (t locale "workspace.options.text-options.titlecase")
:class (dom/classnames :current (= "capitalize" text-transform))
:on-click #(handle-change % "capitalize")}
i/titlecase]]]))
(mf/defc typography-options
[{:keys [ids editor values on-change]}]
(let [locale (mf/deref i18n/locale)
opts #js {:editor editor
:ids ids
:values values
:locale locale
:on-change on-change}]
[:div.element-set-content
[:> font-options opts]
[:> spacing-options opts]
[:> text-transform-options opts]]))
(mf/defc typography-entry
[{:keys [typography read-only? on-select on-change on-deattach on-context-menu]}]
(let [open? (mf/use-state false)
selected (mf/deref refs/selected-shapes)
hover-deattach (mf/use-state false)]
[:*
[:div.element-set-options-group.typography-entry
[:div.typography-selection-wrapper
{:class (when on-select "is-selectable")
:on-click on-select
:on-context-menu on-context-menu}
[:div.typography-sample
{:style {:font-family (:font-family typography)
:font-weight (:font-weight typography)
:font-style (:font-style typography)}}
"Ag"]
[:div.typography-name (:name typography)]]
[:div.element-set-actions
(when on-deattach
[:div.element-set-actions-button
{:on-mouse-enter #(reset! hover-deattach true)
:on-mouse-leave #(reset! hover-deattach false)
:on-click on-deattach}
(if @hover-deattach i/unchain i/chain)])
[:div.element-set-actions-button
{:on-click #(reset! open? true)}
i/actions]]]
[:& advanced-options {:visible? @open?
:on-close #(reset! open? false)}
(if read-only?
[:div.element-set-content.typography-read-only-data
[:div.row-flex.typography-name
[:spang (:name typography)]]
[:div.row-flex
[:span.label "Font"]
[:span (:font-id typography)]]
[:div.row-flex
[:span.label "Size"]
[:span (:font-size typography)]]
[:div.row-flex
[:span.label "Line Height"]
[:span (:line-height typography)]]
[:div.row-flex
[:span.label "Letter spacing"]
[:span (:letter-spacing typography)]]
[:div.row-flex
[:span.label "Text transform"]
[:span (:text-transform typography)]]
[:div.go-to-lib-button
"Go to style library file to edit"]]
[:*
[:div.element-set-content
[:div.row-flex
[:input.element-name.adv-typography-name
{:type "text"
:value (:name typography)
:on-change #(on-change {:name (dom/get-target-val %)})}]]]
[:& typography-options {:values typography
:on-change on-change}]]
)
]]))

View file

@ -76,6 +76,8 @@
[node]
(.-value node))
(def get-target-val (comp get-value get-target))
(defn click
"Click a node"
[node]