Merge remote-tracking branch 'origin/staging' into develop

This commit is contained in:
Andrey Antukh 2024-03-04 10:29:11 +01:00
commit 481058b8d4
142 changed files with 1358 additions and 792 deletions

View file

@ -7,6 +7,7 @@
(ns app.main.data.dashboard.shortcuts
(:require
[app.main.data.dashboard :as dd]
[app.main.data.events :as ev]
[app.main.data.shortcuts :as ds]
[app.main.data.users :as du]
[app.main.store :as st]))
@ -35,7 +36,10 @@
:toggle-theme {:tooltip (ds/alt "M")
:command (ds/a-mod "m")
:subsections [:general-dashboard]
:fn #(st/emit! (du/toggle-theme))}})
:fn #(st/emit! (with-meta (du/toggle-theme)
{::ev/origin "dashboard:shortcuts"}))}})
(defn get-tooltip [shortcut]
(assert (contains? shortcuts shortcut) (str shortcut))

View file

@ -121,26 +121,10 @@
(derive :app.main.data.workspace/set-workspace-layout ::generic-action)
(derive :app.main.data.workspace/toggle-layout-flag ::generic-action)
(defmulti process-event ptk/type)
(defmethod process-event :default [_] nil)
(defprotocol Event
(-data [_] "Get event data"))
(defmethod process-event ::event
[event]
(let [data (deref event)
origin (::origin data)]
(when (::name data)
(d/without-nils
{:type (::type data "action")
:name (::name data)
:context (::context data)
:props (-> data
(dissoc ::name)
(dissoc ::type)
(dissoc ::origin)
(dissoc ::context)
(cond-> origin (assoc :origin origin)))}))))
(defn- normalize-props
(defn- simplify-props
"Removes complex data types from props."
[data]
(into {}
@ -156,24 +140,65 @@
:else kv))))
data))
(defmethod process-event ::generic-action
(defmulti process-event-by-type ptk/type)
(defn- process-event-by-proto
[event]
(let [data (d/deep-merge (-data event) (meta event))
type (ptk/type event)
ev-name (name type)
context (-> (::context data)
(assoc :event-origin (::origin data))
(assoc :event-namespace (namespace type))
(assoc :event-symbol ev-name)
(d/without-nils))
props (-> data d/without-qualified simplify-props)]
{:type (::type data "action")
:name (::name data ev-name)
:context context
:props props}))
(defn- process-event
[event]
(if (satisfies? Event event)
(process-event-by-proto event)
(process-event-by-type event)))
(defmethod process-event-by-type :default [_] nil)
(defmethod process-event-by-type ::event
[event]
(let [data (deref event)
context (-> (::context data)
(assoc :event-origin (::origin data))
(d/without-nils))
props (-> data d/without-qualified simplify-props)]
{:type (::type data "action")
:name (::name data "unnamed")
:context context
:props props}))
(defmethod process-event-by-type ::generic-action
[event]
(let [type (ptk/type event)
mdata (meta event)
data (if (satisfies? IDeref event)
(deref event)
{})]
{})
data (d/deep-merge data (meta event))]
{:type "action"
:name (or (::name mdata) (name type))
:props (-> (merge data (::props mdata))
(normalize-props))
:name (or (::name data) (name type))
:props (-> (d/without-qualified data)
(simplify-props))
:context (d/without-nils
{:event-origin (::origin mdata)
{:event-origin (::origin data)
:event-namespace (namespace type)
:event-symbol (name type)})}))
(defmethod process-event :app.util.router/navigated
(defmethod process-event-by-type :app.util.router/navigated
[event]
(let [match (deref event)
route (get-in match [:data :name])
@ -183,9 +208,9 @@
:project-id (get-in match [:path-params :project-id])}]
{:name "navigate"
:type "action"
:props (normalize-props props)}))
:props (simplify-props props)}))
(defmethod process-event :app.main.data.users/logged-in
(defmethod process-event-by-type :app.main.data.users/logged-in
[event]
(let [data (deref event)
mdata (meta data)
@ -196,10 +221,11 @@
:is-muted (:is-muted data)
:default-team-id (str (:default-team-id data))
:default-project-id (str (:default-project-id data))}]
{:name "signin"
:type "identify"
:profile-id (:id data)
:props (normalize-props props)}))
:props (simplify-props props)}))
;; --- MAIN LOOP

View file

@ -53,7 +53,9 @@
(defn set-current-team!
[team-id]
(swap! storage assoc ::current-team-id team-id))
(if (nil? team-id)
(swap! storage dissoc ::current-team-id)
(swap! storage assoc ::current-team-id team-id)))
;; --- EVENT: fetch-teams
@ -132,7 +134,7 @@
(swap! storage assoc :profile profile)
(i18n/set-locale! (:lang profile))
(when (not= previous-email email)
(swap! storage dissoc ::current-team-id)))))))
(set-current-team! nil)))))))
(defn fetch-profile
[]
@ -295,6 +297,19 @@
;; --- Update Profile
(defn persist-profile
[& {:as opts}]
(ptk/reify ::persist-profile
ptk/WatchEvent
(watch [_ state _]
(let [on-success (:on-success opts identity)
on-error (:on-error opts rx/throw)
profile (:profile state)]
(->> (rp/cmd! :update-profile (dissoc profile :props))
(rx/tap on-success)
(rx/catch on-error))))))
(defn update-profile
[data]
(dm/assert!
@ -303,21 +318,19 @@
(ptk/reify ::update-profile
ptk/WatchEvent
(watch [_ _ stream]
(let [mdata (meta data)
on-success (:on-success mdata identity)
on-error (:on-error mdata rx/throw)]
(->> (rp/cmd! :update-profile (dissoc data :props))
(rx/mapcat
(fn [_]
(rx/merge
(->> stream
(rx/filter (ptk/type? ::profile-fetched))
(rx/take 1)
(rx/tap on-success)
(rx/ignore))
(rx/of (profile-fetched data)))))
(rx/catch on-error))))))
(watch [_ state _]
(let [data (dissoc data :props)
profile (:profile state)
profile' (d/deep-merge profile data)]
(rx/concat
(rx/of #(assoc % :profile profile'))
(when (not= (:theme profile) (:theme profile'))
(rx/of (ptk/data-event ::ev/event
{::ev/name "activate-theme"
::ev/origin "settings"
:theme (:theme profile')}))))))))
;; --- Toggle Theme
@ -327,18 +340,19 @@
ptk/UpdateEvent
(update [_ state]
(update-in state [:profile :theme]
(fn [theme]
(cond
(= theme "default")
(fn [current]
(if (= current "default")
"light"
:else
"default"))))
ptk/WatchEvent
(watch [_ state _]
(rx/of (update-profile (:profile state))))))
(watch [it state _]
(let [profile (get state :profile)
origin (::ev/origin (meta it))]
(rx/of (ptk/data-event ::ev/event {:theme (:theme profile)
::ev/name "activate-theme"
::ev/origin origin})
(persist-profile))))))
;; --- Request Email Change

View file

@ -16,6 +16,7 @@
[app.common.types.shape-tree :as ctt]
[app.common.types.shape.interactions :as ctsi]
[app.main.data.comments :as dcm]
[app.main.data.events :as ev]
[app.main.data.fonts :as df]
[app.main.features :as features]
[app.main.repo :as rp]
@ -546,6 +547,11 @@
(defn go-to-section
[section]
(ptk/reify ::go-to-section
ev/Event
(-data [_]
{::ev/origin "viewer"
:section (name section)})
ptk/UpdateEvent
(update [_ state]
(assoc state :viewer-overlays []))

View file

@ -2210,14 +2210,12 @@
(ptk/reify ::update-component-annotation
ptk/WatchEvent
(watch [it state _]
(let [data (get state :workspace-data)
update-fn
(fn [component]
;; NOTE: we need to ensure the component exists,
;; because there are small possibilities of race
;; conditions with component deletion.
;; NOTE: we need to ensure the component exists,
;; because there are small possibilities of race
;; conditions with component deletion.
(when component
(if (nil? annotation)
(dissoc component :annotation)
@ -2227,14 +2225,17 @@
(pcb/with-library-data data)
(pcb/update-component id update-fn))]
(rx/of (dch/commit-changes changes))))))
(rx/concat
(rx/of (dch/commit-changes changes))
(when (nil? annotation)
(rx/of (ptk/data-event ::ev/event {::ev/name "delete-component-annotation"}))))))))
(defn set-annotations-expanded
[expanded?]
[expanded]
(ptk/reify ::set-annotations-expanded
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-annotations :expanded?] expanded?))))
(assoc-in state [:workspace-annotations :expanded] expanded))))
(defn set-annotations-id-for-create
[id]
@ -2243,8 +2244,13 @@
(update [_ state]
(if id
(-> (assoc-in state [:workspace-annotations :id-for-create] id)
(assoc-in [:workspace-annotations :expanded?] true))
(d/dissoc-in state [:workspace-annotations :id-for-create])))))
(assoc-in [:workspace-annotations :expanded] true))
(d/dissoc-in state [:workspace-annotations :id-for-create])))
ptk/WatchEvent
(watch [_ _ _]
(when (some? id)
(rx/of (ptk/data-event ::ev/event {::ev/name "create-component-annotation"}))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Preview blend modes

View file

@ -8,6 +8,7 @@
"Workspace layout management events and helpers."
(:require
[app.common.data.macros :as dm]
[app.main.data.events :as ev]
[app.util.storage :refer [storage]]
[clojure.set :as set]
[potok.v2.core :as ptk]))
@ -114,8 +115,16 @@
(defn set-options-mode
[mode]
(dm/assert! (contains? valid-options-mode mode))
(dm/assert!
"expected valid options mode"
(contains? valid-options-mode mode))
(ptk/reify ::set-options-mode
ev/Event
(-data [_]
{::ev/origin "workspace:sidebar"
:mode (name mode)})
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-global :options-mode] mode))))

View file

@ -953,6 +953,10 @@
(dm/assert! (uuid? id-new-component))
(dm/assert! (uuid? file-id))
(ptk/reify ::component-multi-swap
ev/Event
(-data [_]
{::ev/name "component-swap"})
ptk/WatchEvent
(watch [_ _ _]
(let [undo-id (js/Symbol)]

View file

@ -19,6 +19,7 @@
[app.common.types.modifiers :as ctm]
[app.common.types.shape.layout :as ctl]
[app.common.uuid :as uuid]
[app.main.data.events :as ev]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.colors :as cl]
[app.main.data.workspace.grid-layout.editor :as dwge]
@ -123,7 +124,10 @@
(defn create-layout-from-id
[id type from-frame?]
(assert (uuid? id) (str id))
(dm/assert!
"expected uuid for `id`"
(uuid? id))
(ptk/reify ::create-layout-from-id
ptk/WatchEvent
(watch [_ state _]
@ -192,7 +196,7 @@
(defn remove-layout
[ids]
(ptk/reify ::remove-layout
(ptk/reify ::remove-shape-layout
ptk/WatchEvent
(watch [_ _ _]
(let [undo-id (js/Symbol)]
@ -204,7 +208,11 @@
(defn create-layout
[type]
(ptk/reify ::create-layout
(ptk/reify ::create-shape-layout
ev/Event
(-data [_]
{:layout (name type)})
ptk/WatchEvent
(watch [_ state _]
(let [page-id (:current-page-id state)
@ -224,19 +232,21 @@
(defn toggle-layout
[type]
(ptk/reify ::toggle-layout-flex
(ptk/reify ::toggle-shape-layout
ptk/WatchEvent
(watch [_ state _]
(watch [it state _]
(let [objects (wsh/lookup-page-objects state)
selected (wsh/lookup-selected state)
selected-shapes (map (d/getf objects) selected)
single? (= (count selected-shapes) 1)
has-layout? (and single? (ctl/any-layout? objects (:id (first selected-shapes))))]
has-layout? (and single?
(ctl/any-layout? objects (:id (first selected-shapes))))]
(when (not= 0 (count selected))
(if has-layout?
(rx/of (remove-layout selected))
(rx/of (create-layout type))))))))
(let [event (if has-layout?
(remove-layout selected)
(create-layout type))]
(rx/of (with-meta event (meta it)))))))))
(defn update-layout
[ids changes]

View file

@ -436,9 +436,11 @@
ptk/WatchEvent
(watch [_ state _]
(let [selected (wsh/lookup-selected state)
pages (-> state :workspace-data :pages-index vals)]
pages (-> state :workspace-data :pages-index vals)
undo-id (js/Symbol)]
(rx/concat
(rx/of (dwu/start-undo-transaction undo-id))
;; First: clear the `:use-for-thumbnail` flag from all not
;; selected frames.
(rx/from
@ -456,4 +458,5 @@
(dch/update-shapes frame-ids #(dissoc % :use-for-thumbnail) {:page-id page-id})))))
;; And finally: toggle the flag value on all the selected shapes
(rx/of (dch/update-shapes selected #(update % :use-for-thumbnail not))))))))
(rx/of (dch/update-shapes selected #(update % :use-for-thumbnail not))
(dwu/commit-undo-transaction undo-id)))))))

View file

@ -222,13 +222,16 @@
:toggle-layout-flex {:tooltip (ds/shift "A")
:command "shift+a"
:subsections [:modify-layers]
:fn #(emit-when-no-readonly (dwsl/toggle-layout :flex))}
:fn #(emit-when-no-readonly
(with-meta (dwsl/toggle-layout :flex)
{::ev/origin "workspace:shortcuts"}))}
:toggle-layout-grid {:tooltip (ds/meta-shift "A")
:command (ds/c-mod "shift+a")
:subsections [:modify-layers]
:fn #(emit-when-no-readonly (dwsl/toggle-layout :grid))}
:fn #(emit-when-no-readonly
(with-meta (dwsl/toggle-layout :grid)
{::ev/origin "workspace:shortcuts"}))}
;; TOOLS
:draw-frame {:tooltip "B"
@ -292,10 +295,10 @@
:subsections [:tools]
:fn #(emit-when-no-readonly (dw/toggle-proportion-lock))}
:toggle-scale-text {:tooltip "K"
:command "k"
:subsections [:tools]
:fn #(emit-when-no-readonly (toggle-layout-flag :scale-text))}
:scale {:tooltip "K"
:command "k"
:subsections [:tools]
:fn #(emit-when-no-readonly (toggle-layout-flag :scale-text))}
:open-color-picker {:tooltip "I"
:command "i"
@ -549,12 +552,12 @@
:command (ds/c-mod "alt+enter")
:fn #(emit-when-no-readonly (dp/open-preview-selected))}
;; THEME
:toggle-theme {:tooltip (ds/alt "M")
:command (ds/a-mod "m")
:subsections [:basics]
:fn #(st/emit! (du/toggle-theme))}})
:fn #(st/emit! (with-meta (du/toggle-theme)
{::ev/origin "workspace:shortcut"}))}})
(def opacity-shortcuts
(into {} (->>

View file

@ -539,6 +539,7 @@
(every? (partial ctl/grid-layout-immediate-child? objects))))
workspace-page-objects =))
;; FIXME: move to viewer.inspect.code
(defn get-flex-child-viewer
[ids page-id]
(l/derived
@ -550,7 +551,7 @@
ids)))
st/state =))
;; FIXME: move to viewer.inspect.code
(defn get-viewer-objects
([]
(let [route (deref route)
@ -574,9 +575,6 @@
[id]
(l/derived #(get % id) workspace-grid-edition))
(def workspace-annotations
(l/derived #(get % :workspace-annotations {}) st/state))
(def current-file-id
(l/derived :current-file-id st/state))

View file

@ -100,26 +100,27 @@
:initial initial)
on-error
(fn [cause]
(cond
(and (= :restriction (:type cause))
(= :profile-blocked (:code cause)))
(reset! error (tr "errors.profile-blocked"))
(fn [err]
(let [cause (ex-data err)]
(cond
(and (= :restriction (:type cause))
(= :profile-blocked (:code cause)))
(reset! error (tr "errors.profile-blocked"))
(and (= :restriction (:type cause))
(= :admin-only-profile (:code cause)))
(reset! error (tr "errors.profile-blocked"))
(and (= :restriction (:type cause))
(= :admin-only-profile (:code cause)))
(reset! error (tr "errors.profile-blocked"))
(and (= :validation (:type cause))
(= :wrong-credentials (:code cause)))
(reset! error (tr "errors.wrong-credentials"))
(and (= :validation (:type cause))
(= :wrong-credentials (:code cause)))
(reset! error (tr "errors.wrong-credentials"))
(and (= :validation (:type cause))
(= :account-without-password (:code cause)))
(reset! error (tr "errors.wrong-credentials"))
(and (= :validation (:type cause))
(= :account-without-password (:code cause)))
(reset! error (tr "errors.wrong-credentials"))
:else
(reset! error (tr "errors.generic"))))
:else
(reset! error (tr "errors.generic")))))
on-success-default
(fn [data]

View file

@ -19,7 +19,7 @@
}
.section-title {
@include bodyMedTipography;
@include bodySmallTypography;
height: $s-32;
display: flex;
align-items: center;
@ -55,7 +55,7 @@
// Comment-thread
.comment {
@include bodyMedTipography;
@include bodySmallTypography;
display: flex;
flex-direction: column;
gap: $s-12;
@ -98,7 +98,7 @@
}
.content {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--color-foreground-primary);
}
@ -151,7 +151,7 @@
.comment-container {
position: relative;
.comment {
@include bodyMedTipography;
@include bodySmallTypography;
.author {
display: flex;
gap: $s-8;
@ -195,7 +195,7 @@
.content {
position: relative;
.text {
@include bodyMedTipography;
@include bodySmallTypography;
}
}
}

View file

@ -71,7 +71,7 @@
.color-text {
@include twoLineTextEllipsis;
@include bodyMedTipography;
@include bodySmallTypography;
width: $s-80;
text-align: center;
margin-top: $s-2;

View file

@ -46,7 +46,7 @@
.context-menu-item {
display: flex;
.context-menu-action {
@include bodyMedTipography;
@include bodySmallTypography;
display: flex;
align-items: center;
justify-content: flex-start;

View file

@ -8,32 +8,40 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.data.macros :as dm]
[app.main.data.events :as-alias ev]
[app.main.ui.icons :as i]
[app.util.timers :as timers]
[app.util.dom :as dom]
[app.util.timers :as tm]
[app.util.webapi :as wapi]
[beicon.v2.core :as rx]
[rumext.v2 :as mf]))
(mf/defc copy-button [{:keys [data on-copied children class]}]
(let [just-copied (mf/use-state false)]
(mf/use-effect
(mf/deps @just-copied)
(fn []
(when @just-copied
(when (fn? on-copied)
(on-copied))
(let [sub (timers/schedule 1000 #(reset! just-copied false))]
;; On unmount we dispose the timer
#(rx/-dispose sub)))))
[:button {:class (dm/str class " " (stl/css-case :copy-button (not (some? children))
:copy-wrapper (some? children)))
:on-click #(when-not @just-copied
(reset! just-copied true)
(wapi/write-to-clipboard (if (fn? data) (data) data)))}
(mf/defc copy-button
{::mf/props :obj}
[{:keys [data on-copied children class]}]
(let [active* (mf/use-state false)
active? (deref active*)
(when children
children)
class (dm/str class " "
(stl/css-case
:copy-button (not (some? children))
:copy-wrapper (some? children)))
on-click
(mf/use-fn
(mf/deps data)
(fn [event]
(when-not (dom/get-boolean-data event "active")
(reset! active* true)
(tm/schedule 1000 #(reset! active* false))
(when (fn? on-copied) (on-copied event))
(wapi/write-to-clipboard
(if (fn? data) (data) data)))))]
[:button {:class class
:data-active (dm/str active?)
:on-click on-click}
children
[:span {:class (stl/css :icon-btn)}
(if @just-copied
(if active?
i/tick-refactor
i/clipboard-refactor)]]))

View file

@ -8,7 +8,7 @@
.editable-label-input {
@include textEllipsis;
@include bodyMedTipography;
@include bodySmallTypography;
@include removeInputStyle;
flex-grow: 1;
height: $s-28;

View file

@ -41,7 +41,7 @@
.input-with-label {
@include flexColumn;
gap: $s-8;
@include bodyMedTipography;
@include bodySmallTypography;
justify-content: flex-start;
align-items: flex-start;
height: 100%;
@ -143,7 +143,7 @@
}
.hint {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--modal-text-foreground-color);
width: 99%;
}
@ -151,7 +151,7 @@
.checkbox {
@extend .input-checkbox;
.checkbox-label {
@include bodyMedTipography;
@include bodySmallTypography;
display: flex;
align-items: center;
flex-direction: row-reverse;
@ -186,7 +186,7 @@
background-color: var(--input-background-color);
.main-content {
@include flexColumn;
@include bodyMedTipography;
@include bodySmallTypography;
position: relative;
justify-content: center;
flex-grow: 1;
@ -231,7 +231,7 @@
select {
@extend .menu-dropdown;
@include bodyMedTipography;
@include bodySmallTypography;
box-sizing: border-box;
position: absolute;
top: 0;
@ -247,7 +247,7 @@
background-color: transparent;
cursor: pointer;
option {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--title-foreground-color-hover);
background-color: var(--menu-background-color);
appearance: none;
@ -276,7 +276,7 @@
overflow-y: hidden;
.inside-input {
@include removeInputStyle;
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
width: 100%;
max-width: calc(100% - $s-1);
@ -315,7 +315,7 @@
border: $s-1 solid var(--pill-background-color);
box-sizing: border-box;
.text {
@include bodyMedTipography;
@include bodySmallTypography;
padding-right: $s-8;
color: var(--pill-foreground-color);
}
@ -352,7 +352,7 @@
}
.radio-label {
@include bodyMedTipography;
@include bodySmallTypography;
@include flexRow;
align-items: flex-start;
gap: $s-8;

View file

@ -12,7 +12,7 @@
--icon-color: var(--icon-foreground);
--text-color: var(--menu-foreground-color);
@extend .new-scrollbar;
@include bodyMedTipography;
@include bodySmallTypography;
position: relative;
display: grid;
grid-template-columns: 1fr auto;

View file

@ -15,9 +15,13 @@
(i/icon-xref :arrow-refactor (stl/css :chevron-icon)))
(mf/defc title-bar
{::mf/wrap-props false}
[{:keys [collapsable collapsed on-collapsed title children on-btn-click btn-children class all-clickable add-icon-gap origin]}]
(let [klass (dm/str (stl/css-case :title-bar true :all-clickable all-clickable) " " class)]
{::mf/props :obj}
[{:keys [class collapsable collapsed title children
btn-children all-clickable add-icon-gap
on-collapsed on-btn-click]}]
(let [klass (stl/css-case :title-bar true
:all-clickable all-clickable)
klass (dm/str klass " " class)]
[:div {:class klass}
(if ^boolean collapsable
[:div {:class (stl/css :title-wrapper)}
@ -35,13 +39,20 @@
:collapsed collapsed)
:on-click on-collapsed}
chevron-icon]
[:div {:class (stl/css :title)} title]])]
[:div {:class (stl/css-case :title-only true
:title-only-icon-gap add-icon-gap
:title-only (not= :inspect origin)
:inspect-title (= :inspect origin))} title])
[:div {:class (stl/css :title)}
title]])]
[:div {:class (stl/css-case
:title-only true
:title-only-icon-gap add-icon-gap)}
title])
children
(when (some? on-btn-click)
[:button {:class (stl/css :title-button)
:on-click on-btn-click}
btn-children])]))
(mf/defc inspect-title-bar
{::mf/props :obj}
[{:keys [class title]}]
[:div {:class (dm/str (stl/css :title-bar) " " class)}
[:div {:class (stl/css :title-only :inspect-title)} title]])

View file

@ -28,7 +28,7 @@
}
.modal-content {
@include bodyMedTipography;
@include bodySmallTypography;
margin-bottom: $s-24;
}

View file

@ -58,9 +58,16 @@
(let [fonts* (mf/use-state {})
fonts (deref fonts*)
input-ref (mf/use-ref)
uploading (mf/use-state #{})
bad-font-family-tmp?
(mf/use-fn
(fn [font]
(and (contains? font :font-family-tmp)
(str/blank? (:font-family-tmp font)))))
disable-upload-all? (some bad-font-family-tmp? (vals fonts))
handle-click
(mf/use-fn #(dom/click (mf/ref-val input-ref)))
@ -95,7 +102,13 @@
on-blur-name
(fn [id event]
(let [name (dom/get-target-val event)]
(swap! fonts* df/rename-and-regroup id name installed-fonts)))
(when-not (str/blank? name)
(swap! fonts* df/rename-and-regroup id name installed-fonts))))
on-change-name
(fn [id event]
(let [name (dom/get-target-val event)]
(swap! fonts* update-in [id] #(assoc % :font-family-tmp name))))
on-delete
(mf/use-fn
@ -145,9 +158,11 @@
[:div {:class (stl/css :font-item :table-row)}
[:span (tr "dashboard.fonts.fonts-added" (i18n/c (count (vals fonts))))]
[:div {:class (stl/css :table-field :options)}
[:button {:class (stl/css :btn-primary)
[:button {:class (stl/css-case :btn-primary true
:disabled disable-upload-all?)
:on-click handle-upload-all
:data-test "upload-all"}
:data-test "upload-all"
:disabled disable-upload-all?}
[:span (tr "dashboard.fonts.upload-all")]]
[:button {:class (stl/css :btn-secondary)
:on-click handle-dismiss-all
@ -155,12 +170,15 @@
[:span (tr "dashboard.fonts.dismiss-all")]]]])
(for [item (sort-by :font-family (vals fonts))]
(let [uploading? (contains? @uploading (:id item))]
(let [uploading? (contains? @uploading (:id item))
disable-upload? (or uploading?
(bad-font-family-tmp? item))]
[:div {:class (stl/css :font-item :table-row)
:key (:id item)}
[:div {:class (stl/css :table-field :family)}
[:input {:type "text"
:on-blur #(on-blur-name (:id item) %)
:on-change #(on-change-name (:id item) %)
:default-value (:font-family item)}]]
[:div {:class (stl/css :table-field :variants)}
[:span {:class (stl/css :label)}
@ -177,8 +195,8 @@
[:button {:on-click #(on-upload item)
:class (stl/css-case :btn-primary true
:upload-button true
:disabled uploading?)
:disabled uploading?}
:disabled disable-upload?)
:disabled disable-upload?}
(if uploading?
(tr "labels.uploading")
(tr "labels.upload"))]

View file

@ -28,7 +28,7 @@
}
.modal-content {
@include bodyMedTipography;
@include bodySmallTypography;
display: grid;
grid-template-columns: 1fr;
gap: $s-16;
@ -52,7 +52,7 @@
.modal-scd-msg,
.modal-subtitle,
.modal-msg {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--modal-text-foreground-color);
line-height: 1.5;
}
@ -78,7 +78,7 @@
flex-grow: 1;
}
.file-name-label {
@include bodyMedTipography;
@include bodySmallTypography;
display: flex;
align-items: center;
gap: $s-12;

View file

@ -34,6 +34,7 @@
[app.util.timers :as ts]
[beicon.v2.core :as rx]
[cljs.spec.alpha :as s]
[cuerdas.core :as str]
[goog.functions :as f]
[potok.v2.core :as ptk]
[rumext.v2 :as mf]))
@ -91,8 +92,9 @@
(mf/use-callback
(mf/deps item)
(fn [name]
(st/emit! (-> (dd/rename-project (assoc item :name name))
(with-meta {::ev/origin "dashboard:sidebar"})))
(when-not (str/blank? name)
(st/emit! (-> (dd/rename-project (assoc item :name name))
(with-meta {::ev/origin "dashboard:sidebar"}))))
(swap! local* assoc :edition? false)))
on-drag-enter

View file

@ -131,8 +131,10 @@
.dropdown {
right: $s-2;
top: $s-52;
min-width: $s-160;
max-height: $s-480;
&:not(.teams-dropdown) {
min-width: $s-160;
}
}
}

View file

@ -612,7 +612,7 @@
}
}
.message {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--alert-foreground-color-error);
}
}
@ -636,7 +636,7 @@
}
}
.message {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--alert-foreground-color-warning);
}
}
@ -694,7 +694,7 @@
.modal-content {
@include flexColumn;
gap: $s-24;
@include bodyMedTipography;
@include bodySmallTypography;
margin-bottom: $s-24;
}
@ -703,7 +703,7 @@
}
.select-title {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--modal-title-foreground-color);
}

View file

@ -35,7 +35,7 @@
@extend .input-element-label;
label {
@include flexColumn;
@include bodyMedTipography;
@include bodySmallTypography;
align-items: flex-start;
width: 100%;
border: none;
@ -43,7 +43,7 @@
height: 100%;
input {
@include bodyMedTipography;
@include bodySmallTypography;
margin-top: $s-8;
}
}

View file

@ -53,7 +53,7 @@
flex-direction: column;
border-radius: $s-8;
h3 {
@include bodyMedTipography;
@include bodySmallTypography;
font-size: $fs-24;
width: 100%;
}

View file

@ -31,7 +31,7 @@
}
.modal-content {
@include bodyMedTipography;
@include bodySmallTypography;
margin-bottom: $s-24;
}

View file

@ -55,7 +55,7 @@
}
.export-progress-title {
@include bodyMedTipography;
@include bodyMediumTypography;
display: grid;
grid-template-columns: auto 1fr;
gap: $s-8;
@ -67,7 +67,7 @@
}
.progress {
@include bodyMedTipography;
@include bodyMediumTypography;
padding-left: $s-8;
margin: 0;
align-self: center;
@ -76,7 +76,7 @@
.retry-btn {
@include buttonStyle;
@include bodyMedTipography;
@include bodySmallTypography;
display: inline;
text-align: left;
color: var(--modal-link-foreground-color);
@ -128,10 +128,10 @@
.modal-content,
.no-selection {
@include bodyMedTipography;
@include bodySmallTypography;
margin-bottom: $s-24;
.modal-hint {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--modal-text-foreground-color);
}
.modal-link {

View file

@ -10,10 +10,18 @@
[cuerdas.core :as str]
[rumext.v2]))
(def exceptions #{:penpot-logo-icon})
(defmacro icon-xref
[id & [class]]
(let [href (str "#icon-" (name id))
class (or class (str "icon-" (name id)))]
class (or class (str "icon-" (name id)))
;; FIXME: Debug tool. Remove when we remove the old icons
class (cond-> class
(and (not (str/ends-with? (name id) "-refactor"))
(not (contains? exceptions id)))
(str " deprecated-icon"))]
`(rumext.v2/html
[:svg {:width 500 :height 500 :class ~class}
[:use {:href ~href}]])))

View file

@ -66,7 +66,7 @@
}
.context-text {
@include bodyMedTipography;
@include bodySmallTypography;
align-self: center;
color: var(--context-notification-fg-color);
margin: auto 0;
@ -78,7 +78,7 @@
.link,
.contain-html .context-text a {
@include bodyMedTipography;
@include bodySmallTypography;
align-self: center;
display: inline;
text-align: left;

View file

@ -32,7 +32,7 @@
}
.inline-text {
@include bodyMedTipography;
@include bodySmallTypography;
align-self: center;
}
@ -41,7 +41,7 @@
}
.link {
@include bodyMedTipography;
@include bodySmallTypography;
margin: 0;
height: 100%;
color: var(--modal-link-foreground-color);

View file

@ -69,7 +69,7 @@
}
.link {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--modal-link-foreground-color);
margin: 0;
}
@ -81,7 +81,7 @@
}
.text {
@include bodyMedTipography;
@include bodySmallTypography;
align-self: center;
}

View file

@ -45,7 +45,7 @@
}
.release {
@include bodyMedTipography;
@include bodySmallTypography;
position: absolute;
top: calc(-1 * $s-28);
right: 0;

View file

@ -26,7 +26,7 @@
}
.paginator {
@include bodyMedTipography;
@include bodySmallTypography;
position: absolute;
top: $s-40;
right: $s-100;
@ -140,7 +140,7 @@
@extend .input-element-label;
label {
@include flexColumn;
@include bodyMedTipography;
@include bodySmallTypography;
align-items: flex-start;
width: 100%;
border: none;
@ -148,7 +148,7 @@
height: 100%;
input {
@include bodyMedTipography;
@include bodySmallTypography;
margin-top: $s-8;
}
}
@ -185,7 +185,7 @@
}
.modal-hint {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--modal-text-foreground-color);
text-align: right;
}

View file

@ -26,6 +26,7 @@
[app.main.ui.releases.v1-7]
[app.main.ui.releases.v1-8]
[app.main.ui.releases.v1-9]
[app.main.ui.releases.v2-0]
[app.util.object :as obj]
[app.util.timers :as tm]
[rumext.v2 :as mf]))
@ -90,4 +91,4 @@
(defmethod rc/render-release-notes "0.0"
[params]
(rc/render-release-notes (assoc params :version "1.19")))
(rc/render-release-notes (assoc params :version "2.0")))

View file

@ -5,15 +5,16 @@
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.releases.common
(:require-macros [app.main.style :as stl])
(:require
[app.util.dom :as dom]
[rumext.v2 :as mf]))
(defmulti render-release-notes :version)
(mf/defc navigation-bullets
[{:keys [slide navigate total]}]
[:ul.step-dots
[:ul {:class (stl/css :step-dots)}
(for [i (range total)]
[:li {:class (dom/classnames :current (= slide i))
[:li {:class (stl/css-case :dot true
:current (= slide i))
:on-click #(navigate i)}])])

View file

@ -0,0 +1,32 @@
// 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) KALEIDOS INC
@import "refactor/common-refactor.scss";
.step-dots {
display: grid;
grid-template-columns: none;
grid-auto-flow: column;
gap: $s-8;
height: fit-content;
width: fit-content;
margin: 0;
padding: 0;
align-self: center;
justify-self: flex-start;
}
.dot {
height: $s-12;
width: $s-12;
border-radius: $br-circle;
background-color: var(--modal-navigator-foreground-color-rest);
cursor: pointer;
}
.current {
background-color: var(--modal-navigator-foreground-color-active);
}

View file

@ -0,0 +1,166 @@
;; 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) KALEIDOS INC
(ns app.main.ui.releases.v2-0
(:require-macros [app.main.style :as stl])
(:require
[app.common.data.macros :as dm]
[app.main.ui.releases.common :as c]
[rumext.v2 :as mf]))
;; TODO: Review all copies and alt text
(defmethod c/render-release-notes "2.0"
[{:keys [slide klass next finish navigate version]}]
(mf/html
(case slide
:start
[:div {:class (stl/css :modal-overlay)}
[:div.animated {:class klass}
[:div {:class (stl/css :modal-container)}
;; TODO: Review alt
[:img {:src "images/features/2.0-intro-image.png"
:class (stl/css :start-image)
:border "0"
:alt "Community code contributions"}]
[:div {:class (stl/css :modal-content)}
[:div {:class (stl/css :modal-header)}
[:h1 {:class (stl/css :modal-title)}
"What's new?"]
[:div {:class (stl/css :verstion-tag)}
(dm/str "Version " version)]]
[:div {:class (stl/css :features-block)}
[:div {:class (stl/css :feature)}
[:h2 {:class (stl/css :feature-title)}
"CSS Grid Layout"]
[:p {:class (stl/css :feature-content)}
"Crea una estructura flexible para componer
los elementos de tu diseño y obten el código html/css."]]
[:div {:class (stl/css :feature)}
[:h2 {:class (stl/css :feature-title)}
"New Components"]
[:p {:class (stl/css :feature-content)}
"Ahora tus main components estarán en un espacio
físico, para que los puedas ver y gestionar fácilmente."]]
[:div {:class (stl/css :feature)}
[:h2 {:class (stl/css :feature-title)}
"New User Interface"]
[:p {:class (stl/css :feature-content)}
"Hemos hecho Penpot aún más bonito, y además
ahora puedes elegir entre tema oscuro y claro."]]]
[:div {:class (stl/css :navigation)}
[:button {:class (stl/css :next-btn)
:on-click next} "Continue"]]]]]]
0
[:div {:class (stl/css :modal-overlay)}
[:div.animated {:class klass}
[:div {:class (stl/css :modal-container)}
;; TODO: Review alt
[:img {:src "images/features/2.0-css-grid.gif"
:class (stl/css :start-image)
:border "0"
:alt "Community code contributions"}]
[:div {:class (stl/css :modal-content)}
[:div {:class (stl/css :modal-header)}
[:h1 {:class (stl/css :modal-title)}
"css grid layout"]]
[:div {:class (stl/css :feature)}
[:p {:class (stl/css :feature-content)}
"¿Querías más flexibilidad para componer tus diseños?
Selecciona GridLayout para crear una estructura con los
márgenes y espacios que necesites. Los elementos de tu diseño
se adaptarán como un guante. Además tendrás en el momento el
código html y css con estándares web."]
[:p {:class (stl/css :feature-content)}
"Elige entre FlexLayout o GridLayout en tu panel lateral derecho."]]
[:div {:class (stl/css :navigation)}
[:& c/navigation-bullets
{:slide slide
:navigate navigate
:total 3}]
[:button {:on-click next
:class (stl/css :next-btn)} "Continue"]]]]]]
1
[:div {:class (stl/css :modal-overlay)}
[:div.animated {:class klass}
[:div {:class (stl/css :modal-container)}
[:img {:src "images/features/2.0-components.gif"
:class (stl/css :start-image)
:border "0"
:alt "Community code contributions"}]
[:div {:class (stl/css :modal-content)}
[:div {:class (stl/css :modal-header)}
[:h1 {:class (stl/css :modal-title)}
"New components"]]
[:div {:class (stl/css :feature)}
[:p {:class (stl/css :feature-content)}
"Os hemos escuchado y ahora los main components están
disponibles en el archivo para gestionarlos más cómodamente."]
[:p {:class (stl/css :feature-content)}
"No te preocupes por tus archivos con main componentes v1,
al abrirlos con la nueva versión los encontrarás agrupados
en una página nueva, sanos y salvos."]]
[:div {:class (stl/css :navigation)}
[:& c/navigation-bullets
{:slide slide
:navigate navigate
:total 3}]
[:button {:on-click next
:class (stl/css :next-btn)} "Continue"]]]]]]
2
[:div {:class (stl/css :modal-overlay)}
[:div.animated {:class klass}
[:div {:class (stl/css :modal-container)}
[:img {:src "images/features/2.0-new-ui.gif"
:class (stl/css :start-image)
:border "0"
:alt "Community code contributions"}]
[:div {:class (stl/css :modal-content)}
[:div {:class (stl/css :modal-header)}
[:h1 {:class (stl/css :modal-title)}
"REDISEÑO Y MEJORAS DE RENDIMIENTO"]]
[:div {:class (stl/css :feature)}
[:p {:class (stl/css :feature-content)}
"Le hemos dado una vuelta al interface y añadido
pequeñas mejoras de usabilidad.
Además, ahora puedes elegir entre tema oscuro y tema claro,
dignos de Dark Vader y Luke Skywalker."]
[:p {:class (stl/css :feature-content)}
"Aunque siempre estamos puliendo el rendimiento
y la estabilidad, en esta versión hemos
conseguido grandes mejoras en ese sentido."]
[:p {:class (stl/css :feature-content)}
"Que lo disfrutes!"]]
[:div {:class (stl/css :navigation)}
[:& c/navigation-bullets
{:slide slide
:navigate navigate
:total 3}]
[:button {:on-click finish
:class (stl/css :next-btn)} "Let's go"]]]]]])))

View file

@ -0,0 +1,90 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
@import "refactor/common-refactor.scss";
.modal-overlay {
@extend .modal-overlay-base;
}
.modal-container {
display: grid;
grid-template-columns: $s-324 1fr;
height: $s-500;
width: $s-888;
border-radius: $br-8;
background-color: var(--modal-background-color);
border: $s-2 solid var(--modal-border-color);
}
.start-image {
width: $s-324;
border-radius: $br-8 0 0 $br-8;
}
.modal-content {
padding: $s-40;
display: grid;
grid-template-rows: auto 1fr $s-32;
gap: $s-24;
}
.modal-header {
display: grid;
gap: $s-8;
}
.verstion-tag {
@include flexCenter;
@include headlineSmallTypography;
height: $s-32;
width: $s-96;
background-color: var(--communication-tag-background-color);
color: var(--communication-tag-foreground-color);
border-radius: $br-8;
}
.modal-title {
@include headlineLargeTypography;
color: var(--modal-title-foreground-color);
}
.features-block {
display: flex;
flex-direction: column;
gap: $s-16;
width: $s-440;
}
.feature {
display: flex;
flex-direction: column;
gap: $s-8;
}
.feature-title {
@include bodyLargeTypography;
color: var(--modal-title-foreground-color);
}
.feature-content {
@include bodyMediumTypography;
margin: 0;
color: var(--modal-text-foreground-color);
}
.navigation {
width: 100%;
display: grid;
grid-template-areas: "bullets button";
}
.next-btn {
@extend .button-primary;
width: $s-100;
justify-self: flex-end;
grid-area: button;
}

View file

@ -191,7 +191,7 @@
.modal-content {
@include flexColumn;
gap: $s-24;
@include bodyMedTipography;
@include bodySmallTypography;
margin-bottom: $s-24;
}
@ -200,7 +200,7 @@
}
.select-title {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--modal-title-foreground-color);
}
@ -223,7 +223,7 @@
.token-value {
@include textEllipsis;
@include bodyMedTipography;
@include bodySmallTypography;
flex-grow: 1;
}

View file

@ -30,7 +30,7 @@
.modal-content {
@include flexColumn;
@include bodyMedTipography;
@include bodySmallTypography;
gap: $s-24;
margin-bottom: $s-24;
}
@ -40,7 +40,7 @@
}
.select-title {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--modal-title-foreground-color);
}

View file

@ -30,7 +30,7 @@
.modal-content {
@include flexColumn;
@include bodyMedTipography;
@include bodySmallTypography;
gap: $s-24;
margin-bottom: $s-24;
}
@ -40,7 +40,7 @@
}
.select-title {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--modal-title-foreground-color);
}

View file

@ -24,15 +24,12 @@
(s/def ::options-form
(s/keys :opt-un [::lang ::theme]))
(defn- on-success
[_]
(st/emit! (msg/success (tr "notifications.profile-saved"))))
(defn- on-submit
[form _event]
(let [data (:clean-data @form)
mdata {:on-success (partial on-success form)}]
(st/emit! (du/update-profile (with-meta data mdata)))))
(let [data (:clean-data @form)]
(st/emit! (du/update-profile data)
(du/persist-profile)
(msg/success (tr "notifications.profile-saved")))))
(mf/defc options-form
{::mf/wrap-props false}

View file

@ -19,7 +19,7 @@
(defn- on-error
[form error]
(case (:code error)
(case (:code (ex-data error))
:old-password-not-match
(swap! form assoc-in [:errors :password-old]
{:message (tr "errors.wrong-old-password")})
@ -103,7 +103,7 @@
:label (t locale "labels.confirm-password")}]]
[:> fm/submit-button*
{:label (t locale "dashboard.update-settings")
{:label (t locale "dashboard.password-change")
:data-test "submit-password"
:class (stl/css :update-btn)}]]))

View file

@ -27,15 +27,12 @@
(s/def ::profile-form
(s/keys :req-un [::fullname ::email]))
(defn- on-success
[_]
(st/emit! (msg/success (tr "notifications.profile-saved"))))
(defn- on-submit
[form _event]
(let [data (:clean-data @form)
mdata {:on-success (partial on-success form)}]
(st/emit! (du/update-profile (with-meta data mdata)))))
(let [data (:clean-data @form)]
(st/emit! (du/update-profile data)
(du/persist-profile)
(msg/success (tr "notifications.profile-saved")))))
;; --- Profile Form

View file

@ -24,7 +24,7 @@
}
.empty-state {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--empty-message-foreground-color);
display: grid;
place-items: center;
@ -134,7 +134,7 @@
.counter {
@include flexCenter;
@include bodyMedTipography;
@include bodySmallTypography;
border-radius: $br-8;
width: $s-64;
height: $s-32;

View file

@ -8,7 +8,7 @@
// COMMENT DROPDOWN ON HEADER
.view-options {
@include bodyMedTipography;
@include bodySmallTypography;
display: flex;
align-items: center;
position: relative;
@ -29,7 +29,7 @@
}
.dropdown-title {
@include bodyMedTipography;
@include bodySmallTypography;
flex-grow: 1;
color: var(--input-foreground-color-active);
}

View file

@ -269,7 +269,8 @@
(mf/deps permissions)
(fn []
(if (:is-logged permissions)
(st/emit! dv/close-thumbnails-panel (dv/go-to-section :inspect))
(st/emit! dv/close-thumbnails-panel
(dv/go-to-section :inspect))
(open-login-dialog))))
navigate
@ -279,11 +280,11 @@
(let [section (-> (dom/get-current-target event)
(dom/get-data "value")
(keyword))]
(if (or (= section :interactions) (:is-logged permissions))
(st/emit! (dv/go-to-section section))
(open-login-dialog)))))]
[:header {:class (stl/css-case :viewer-header true
:fullscreen (mf/deref fullscreen-ref))}
[:div {:class (stl/css :nav-zone)}

View file

@ -62,7 +62,7 @@
}
.breadcrumb {
@include bodyMedTipography;
@include bodySmallTypography;
@include flexRow;
color: var(--title-foreground-color);
cursor: pointer;
@ -114,7 +114,7 @@
}
.current-frame {
@include bodyMedTipography;
@include bodySmallTypography;
@include flexRow;
flex-grow: 1;
color: var(--title-foreground-color-hover);
@ -198,7 +198,7 @@
.go-log-btn {
@extend .button-tertiary;
@include bodyMedTipography;
@include bodySmallTypography;
height: $s-32;
padding: 0 $s-8;
border-radius: $br-8;
@ -213,7 +213,7 @@
min-width: $s-64;
border-radius: $br-8;
.label {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--button-tertiary-foreground-color-rest);
}

View file

@ -15,7 +15,7 @@
}
.annotation-content {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--entry-foreground-color);
}

View file

@ -9,7 +9,7 @@
(:require
[app.common.data.macros :as dm]
[app.main.ui.components.copy-button :refer [copy-button]]
[app.main.ui.components.title-bar :refer [title-bar]]
[app.main.ui.components.title-bar :refer [inspect-title-bar]]
[app.util.code-gen.style-css :as css]
[app.util.i18n :refer [tr]]
[rumext.v2 :as mf]))
@ -22,10 +22,9 @@
(let [shapes (->> shapes (filter has-blur?))]
(when (seq shapes)
[:div {:class (stl/css :attributes-block)}
[:& title-bar {:collapsable false
:title (tr "inspect.attributes.blur")
:origin :inspect
:class (stl/css :title-spacing-blur)}
[:& inspect-title-bar
{:title (tr "inspect.attributes.blur")
:class (stl/css :title-spacing-blur)}
(when (= (count shapes) 1)
[:& copy-button {:data (css/get-css-property objects (first shapes) :filter)
:class (stl/css :copy-btn-title)}])]

View file

@ -78,7 +78,7 @@
grid-template-rows: 1fr 1fr;
}
.color-name-wrapper {
@include bodyMedTipography;
@include bodySmallTypography;
@include flexColumn;
padding: $s-8 $s-4 $s-8 $s-8;
height: $s-32;
@ -89,21 +89,21 @@
max-width: $s-124;
}
.color-name-library {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
text-align: left;
height: $s-16;
color: var(--menu-foreground-color-rest);
}
.color-value-wrapper {
@include bodyMedTipography;
@include bodySmallTypography;
height: $s-16;
color: var(--menu-foreground-color);
}
}
.opacity-info {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--menu-foreground-color);
padding: $s-8 0;
}

View file

@ -7,7 +7,7 @@
(ns app.main.ui.viewer.inspect.attributes.fill
(:require-macros [app.main.style :as stl])
(:require
[app.main.ui.components.title-bar :refer [title-bar]]
[app.main.ui.components.title-bar :refer [inspect-title-bar]]
[app.main.ui.viewer.inspect.attributes.common :refer [color-row]]
[app.util.code-gen.style-css :as css]
[app.util.i18n :refer [tr]]
@ -53,10 +53,9 @@
(let [shapes (filter has-fill? shapes)]
(when (seq shapes)
[:div {:class (stl/css :attributes-block)}
[:& title-bar {:collapsable false
:origin :inspect
:title (tr "inspect.attributes.fill")
:class (stl/css :title-spacing-fill)}]
[:& inspect-title-bar
{:title (tr "inspect.attributes.fill")
:class (stl/css :title-spacing-fill)}]
[:div {:class (stl/css :attributes-content)}
(for [shape shapes]

View file

@ -10,7 +10,7 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.main.ui.components.copy-button :refer [copy-button]]
[app.main.ui.components.title-bar :refer [title-bar]]
[app.main.ui.components.title-bar :refer [inspect-title-bar]]
[app.main.ui.viewer.inspect.attributes.common :as cmm]
[app.util.code-gen.style-css :as css]
[app.util.i18n :refer [tr]]
@ -36,10 +36,9 @@
(mf/defc geometry-panel
[{:keys [objects shapes]}]
[:div {:class (stl/css :attributes-block)}
[:& title-bar {:collapsable false
:title (tr "inspect.attributes.size")
:origin :inspect
:class (stl/css :title-spacing-geometry)}
[:& inspect-title-bar
{:title (tr "inspect.attributes.size")
:class (stl/css :title-spacing-geometry)}
(when (= (count shapes) 1)
[:& copy-button {:data (css/get-shape-properties-css objects (first shapes) properties)

View file

@ -11,7 +11,7 @@
[app.common.data.macros :as dm]
[app.common.types.shape.layout :as ctl]
[app.main.ui.components.copy-button :refer [copy-button]]
[app.main.ui.components.title-bar :refer [title-bar]]
[app.main.ui.components.title-bar :refer [inspect-title-bar]]
[app.main.ui.viewer.inspect.attributes.common :as cmm]
[app.util.code-gen.style-css :as css]
[rumext.v2 :as mf]))
@ -52,10 +52,9 @@
(when (seq shapes)
[:div {:class (stl/css :attributes-block)}
[:& title-bar {:collapsable false
:origin :inspect
:title "Layout"
:class (stl/css :title-spacing-layout)}
[:& inspect-title-bar
{:title "Layout"
:class (stl/css :title-spacing-layout)}
(when (= (count shapes) 1)
[:& copy-button {:data (css/get-shape-properties-css objects (first shapes) properties)

View file

@ -10,7 +10,7 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.main.ui.components.copy-button :refer [copy-button]]
[app.main.ui.components.title-bar :refer [title-bar]]
[app.main.ui.components.title-bar :refer [inspect-title-bar]]
[app.main.ui.viewer.inspect.attributes.common :refer [color-row]]
[app.util.code-gen.style-css :as css]
[app.util.i18n :refer [tr]]
@ -53,10 +53,9 @@
(when (and (seq shapes) (> (count shapes) 0))
[:div {:class (stl/css :attributes-block)}
[:& title-bar {:collapsable false
:origin :inspect
:title (tr "inspect.attributes.shadow")
:class (stl/css :title-spacing-shadow)}]
[:& inspect-title-bar
{:title (tr "inspect.attributes.shadow")
:class (stl/css :title-spacing-shadow)}]
[:div {:class (stl/css :attributes-content)}
(for [shape shapes]

View file

@ -10,7 +10,7 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.main.ui.components.copy-button :refer [copy-button]]
[app.main.ui.components.title-bar :refer [title-bar]]
[app.main.ui.components.title-bar :refer [inspect-title-bar]]
[app.main.ui.formats :as fmt]
[app.main.ui.viewer.inspect.attributes.common :refer [color-row]]
[app.util.code-gen.style-css-formats :as cssf]
@ -61,10 +61,9 @@
(let [shapes (->> shapes (filter has-stroke?))]
(when (seq shapes)
[:div {:class (stl/css :attributes-block)}
[:& title-bar {:collapsable false
:origin :inspect
:title (tr "inspect.attributes.stroke")
:class (stl/css :title-spacing-stroke)}]
[:& inspect-title-bar
{:title (tr "inspect.attributes.stroke")
:class (stl/css :title-spacing-stroke)}]
[:div {:class (stl/css :attributes-content)}
(for [shape shapes]

View file

@ -9,7 +9,7 @@
(:require
[app.common.data :as d]
[app.main.ui.components.copy-button :refer [copy-button]]
[app.main.ui.components.title-bar :refer [title-bar]]
[app.main.ui.components.title-bar :refer [inspect-title-bar]]
[app.util.i18n :refer [tr]]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
@ -53,8 +53,7 @@
(let [shape (first shapes)]
(when (seq (:svg-attrs shape))
[:div {:class (stl/css :attributes-block)}
[:& title-bar {:collapsable false
:origin :inspect
:title (tr "workspace.sidebar.options.svg-attrs.title")
:class (stl/css :title-spacing-svg)}]
[:& inspect-title-bar
{:title (tr "workspace.sidebar.options.svg-attrs.title")
:class (stl/css :title-spacing-svg)}]
[:& svg-block {:shape shape}]])))

View file

@ -14,7 +14,7 @@
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.copy-button :refer [copy-button]]
[app.main.ui.components.title-bar :refer [title-bar]]
[app.main.ui.components.title-bar :refer [inspect-title-bar]]
[app.main.ui.formats :as fmt]
[app.main.ui.viewer.inspect.attributes.common :refer [color-row]]
[app.util.i18n :refer [tr]]
@ -190,10 +190,9 @@
[{:keys [shapes]}]
(when-let [shapes (seq (filter has-text? shapes))]
[:div {:class (stl/css :attributes-block)}
[:& title-bar {:collapsable false
:origin :inspect
:title (tr "inspect.attributes.typography")
:class (stl/css :title-spacing-text)}]
[:& inspect-title-bar
{:title (tr "inspect.attributes.typography")
:class (stl/css :title-spacing-text)}]
(for [shape shapes]
[:& text-block {:shape shape

View file

@ -36,7 +36,7 @@
border: $s-1 solid var(--menu-border-color-disabled);
margin-top: $s-4;
.content {
@include bodyMedTipography;
@include bodySmallTypography;
width: 100%;
padding: $s-4 0;
color: var(--color-foreground-secondary);

View file

@ -20,7 +20,6 @@
[app.main.ui.components.code-block :refer [code-block]]
[app.main.ui.components.copy-button :refer [copy-button]]
[app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]]
[app.main.ui.hooks :as hooks]
[app.main.ui.hooks.resize :refer [use-resize-hook]]
[app.main.ui.icons :as i]
[app.main.ui.shapes.text.fontfaces :refer [shapes->fonts]]
@ -50,30 +49,16 @@
</body>
</html>")
(defn get-flex-elements [page-id shapes from]
(let [ids (mapv :id shapes)
ids (hooks/use-equal-memo ids)
get-layout-children-refs
(mf/use-memo
(mf/deps ids page-id from)
#(if (= from :workspace)
(refs/workspace-get-flex-child ids)
(refs/get-flex-child-viewer ids page-id)))]
(mf/deref get-layout-children-refs)))
(defn get-objects [from]
(defn- use-objects [from]
(let [page-objects-ref
(mf/use-memo
(mf/deps from)
(fn []
(if (= from :workspace)
refs/workspace-page-objects
(refs/get-viewer-objects))))]
(mf/with-memo [from]
(if (= from :workspace)
;; FIXME: fix naming consistency issues
refs/workspace-page-objects
(refs/get-viewer-objects)))]
(mf/deref page-objects-ref)))
(defn shapes->images
(defn- shapes->images
[shapes]
(->> shapes
(keep
@ -81,7 +66,7 @@
(when-let [data (or (:metadata shape) (:fill-image shape) (-> shape :fills first :fill-image))]
[(:id shape) (cfg/resolve-file-media data)])))))
(defn replace-map
(defn- replace-map
[value map]
(reduce
(fn [value [old new]]
@ -104,35 +89,39 @@
fontfaces-css* (mf/use-state nil)
images-data* (mf/use-state nil)
collapsed* (mf/use-state #{})
collapsed-css? (contains? @collapsed* :css)
style-type (deref style-type*)
markup-type (deref markup-type*)
fontfaces-css (deref fontfaces-css*)
images-data (deref images-data*)
collapsed* (mf/use-state #{})
collapsed-css? (contains? @collapsed* :css)
collapsed-markup? (contains? @collapsed* :markup)
style-type (deref style-type*)
markup-type (deref markup-type*)
fontfaces-css (deref fontfaces-css*)
images-data (deref images-data*)
objects (use-objects from)
shapes (->> shapes
(map #(gsh/translate-to-frame % frame)))
shapes
(mf/with-memo [shapes frame]
(mapv #(gsh/translate-to-frame % frame) shapes))
objects (get-objects from)
all-children
(mf/use-memo
(mf/deps shapes objects)
(fn []
(->> shapes
(map :id)
(cfh/selected-with-children objects)
(ctst/sort-z-index objects)
(map (d/getf objects)))))
all-children (->> shapes
(map :id)
(cfh/selected-with-children objects)
(ctst/sort-z-index objects)
(map (d/getf objects)))
fonts
(mf/with-memo [all-children]
(shapes->fonts all-children))
images-urls
(mf/with-memo [all-children]
(shapes->images all-children))
shapes (hooks/use-equal-memo shapes)
all-children (hooks/use-equal-memo all-children)
fonts (-> (shapes->fonts all-children)
(hooks/use-equal-memo))
images-urls (-> (shapes->images all-children)
(hooks/use-equal-memo))
style-code
(mf/use-memo
(mf/deps fontfaces-css style-type all-children cg/generate-style-code)
@ -150,20 +139,28 @@
(cb/format-code markup-type))))
on-markup-copied
(mf/use-callback
(mf/deps markup-type)
(mf/use-fn
(mf/deps markup-type from)
(fn []
(st/emit! (ptk/event ::ev/event
{::ev/name "copy-inspect-code"
:type markup-type}))))
(let [origin (if (= :workspace from)
"workspace"
"viewer")]
(st/emit! (ptk/event ::ev/event
{::ev/name "copy-inspect-code"
::ev/origin origin
:type markup-type})))))
on-style-copied
(mf/use-callback
(mf/deps style-type)
(mf/use-fn
(mf/deps style-type from)
(fn []
(st/emit! (ptk/event ::ev/event
{::ev/name "copy-inspect-style"
:type style-type}))))
(let [origin (if (= :workspace from)
"workspace"
"viewer")]
(st/emit! (ptk/event ::ev/event
{::ev/name "copy-inspect-style"
::ev/origin origin
:type style-type})))))
{on-markup-pointer-down :on-pointer-down
on-markup-lost-pointer-capture :on-lost-pointer-capture
@ -178,40 +175,50 @@
(use-resize-hook :code 400 100 800 :y false :bottom)
;; set-style
;; (mf/use-callback
;; (mf/use-fn
;; (fn [value]
;; (reset! style-type* value)))
set-markup
(mf/use-callback
(mf/use-fn
(mf/deps markup-type*)
(fn [value]
(reset! markup-type* value)))
handle-copy-all-code
(mf/use-callback
(mf/use-fn
(mf/deps style-code markup-code images-data)
(fn []
(wapi/write-to-clipboard (gen-all-code style-code markup-code images-data))))
;;handle-open-review
;;(mf/use-callback
;;(mf/use-fn
;; (fn []
;; (st/emit! (dp/open-preview-selected))))
handle-collapse
(mf/use-callback
(fn [e]
(let [panel-type (keyword (dom/get-data (dom/get-current-target e) "type"))]
(mf/use-fn
(fn [event]
(let [panel-type (-> (dom/get-current-target event)
(dom/get-data "type")
(keyword))]
(swap! collapsed*
(fn [collapsed]
(if (contains? collapsed panel-type)
(disj collapsed panel-type)
(conj collapsed panel-type)))))))]
(conj collapsed panel-type)))))))
copy-css-fn
(mf/use-fn
(mf/deps style-code images-data)
#(replace-map style-code images-data))
(mf/use-effect
(mf/deps fonts)
#(->> (rx/from fonts)
copy-html-fn
(mf/use-fn
(mf/deps markup-code images-data)
#(replace-map markup-code images-data))]
(mf/with-effect [fonts]
(->> (rx/from fonts)
(rx/merge-map fonts/fetch-font-css)
(rx/reduce conj [])
(rx/subs!
@ -219,9 +226,8 @@
(let [css (str/join "\n" result)]
(reset! fontfaces-css* css))))))
(mf/use-effect
(mf/deps images-urls)
#(->> (rx/from images-urls)
(mf/with-effect [images-urls]
(->> (rx/from images-urls)
(rx/merge-map
(fn [[_ uri]]
(->> (http/fetch-data-uri uri true)
@ -254,24 +260,24 @@
[:div {:class (stl/css :code-lang-option)}
"CSS"]
;; We will have a select when we have more than one option
;; [:& select {:default-value style-type
;; :class (stl/css :code-lang-select)
;; :on-change set-style
;; :options [{:label "CSS" :value "css"}]}]
;; We will have a select when we have more than one option
;; [:& select {:default-value style-type
;; :class (stl/css :code-lang-select)
;; :on-change set-style
;; :options [{:label "CSS" :value "css"}]}]
[:div {:class (stl/css :action-btns)}
[:button {:class (stl/css :expand-button)
:on-click on-expand}
i/code-refactor]
[:& copy-button {:data #(replace-map style-code images-data)
[:& copy-button {:data copy-css-fn
:class (stl/css :css-copy-btn)
:on-copied on-style-copied}]]]
(when-not collapsed-css?
[:div {:class (stl/css :code-row-display)
:style #js {"--code-height" (str (or style-size 400) "px")}}
:style {:--code-height (dm/str (or style-size 400) "px")}}
[:& code-block {:type style-type
:code style-code}]])
@ -306,13 +312,13 @@
:on-click on-expand}
i/code-refactor]
[:& copy-button {:data #(replace-map markup-code images-data)
[:& copy-button {:data copy-html-fn
:class (stl/css :html-copy-btn)
:on-copied on-markup-copied}]]]
(when-not collapsed-markup?
[:div {:class (stl/css :code-row-display)
:style #js {"--code-height" (str (or markup-size 400) "px")}}
:style {:--code-height (dm/str (or markup-size 400) "px")}}
[:& code-block {:type markup-type
:code markup-code}]])

View file

@ -40,16 +40,16 @@
(mf/defc right-sidebar
[{:keys [frame page objects file selected shapes page-id file-id share-id from on-change-section on-expand]
:or {from :inspect}}]
(let [section (mf/use-state :info #_:code)
objects (or objects (:objects page))
shapes (or shapes
(resolve-shapes objects selected))
first-shape (first shapes)
page-id (or page-id (:id page))
file-id (or file-id (:id file))
:or {from :viewer}}]
(let [section (mf/use-state :info #_:code)
objects (or objects (:objects page))
shapes (or shapes
(resolve-shapes objects selected))
first-shape (first shapes)
page-id (or page-id (:id page))
file-id (or file-id (:id file))
libraries (get-libraries from)
libraries (get-libraries from)
file (mf/deref refs/viewer-file)
components-v2 (dm/get-in file [:data :options :components-v2])
@ -83,7 +83,7 @@
(handle-change-tab :info))))
[:aside {:class (stl/css-case :settings-bar-right true
:viewer-code (= from :inspect))}
:viewer-code (= from :viewer))}
(if (seq shapes)
[:div {:class (stl/css :tool-windows)}
[:div {:class (stl/css :shape-row)}

View file

@ -50,7 +50,7 @@
}
.layer-title {
@include bodyMedTipography;
@include bodySmallTypography;
@include text-ellipsis;
height: $s-32;
padding: $s-8 0;
@ -79,7 +79,7 @@
}
.placeholder-label {
@include bodyMedTipography;
@include bodySmallTypography;
text-align: center;
width: $s-200;
color: var(--empty-message-foreground-color);

View file

@ -7,7 +7,7 @@
@use "common/refactor/common-refactor.scss" as *;
.view-options {
@include bodyMedTipography;
@include bodySmallTypography;
display: flex;
align-items: center;
position: relative;
@ -19,7 +19,7 @@
cursor: pointer;
}
.dropdown-title {
@include bodyMedTipography;
@include bodySmallTypography;
flex-grow: 1;
color: var(--input-foreground-color-active);
}
@ -35,10 +35,13 @@
top: calc($s-2 + $s-48);
width: $s-272;
padding: $s-6;
max-height: calc(100vh - 3 * ($s-2 + $s-48));
overflow: scroll;
}
.dropdown-element {
@extend .dropdown-element-base;
min-height: $s-32;
.icon {
@include flexCenter;
height: 100%;

View file

@ -29,7 +29,7 @@
.modal-content {
@include flexColumn;
@include bodyMedTipography;
@include bodySmallTypography;
gap: $s-24;
max-height: $s-400;
width: $s-368;

View file

@ -34,7 +34,7 @@
}
.modal-content {
@include bodyMedTipography;
@include bodySmallTypography;
@include flexColumn;
gap: $s-24;
}
@ -86,7 +86,7 @@
}
.description {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--modal-text-foreground-color);
margin-bottom: $s-24;
}
@ -181,7 +181,7 @@
}
.count-pages,
.current-tag {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--input-foreground-color);
}

View file

@ -33,7 +33,7 @@
}
.counter {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--viewer-thumbnails-control-foreground-color);
}
@ -142,7 +142,7 @@
}
.thumbnail-info {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
text-align: center;
color: var(--viewer-thumbnails-control-foreground-color);

View file

@ -97,6 +97,6 @@
}
.color-palette-empty {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--palette-text-color);
}

View file

@ -33,7 +33,7 @@
.option-wrapper {
width: 100%;
.library-name {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--context-menu-foreground-color);
display: grid;
grid-template-columns: 1fr $s-24;

View file

@ -11,6 +11,7 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.config :as cfg]
[app.main.data.events :as-alias ev]
[app.main.data.modal :as modal]
[app.main.data.workspace.colors :as dc]
[app.main.data.workspace.libraries :as dwl]
@ -32,6 +33,7 @@
[app.util.i18n :as i18n :refer [tr]]
[cuerdas.core :as str]
[okulary.core :as l]
[potok.v2.core :as ptk]
[rumext.v2 :as mf]))
;; --- Refs
@ -103,11 +105,16 @@
(mf/use-fn
(mf/deps current-color)
(fn []
(let [keep-aspect-ratio? (-> current-color :image :keep-aspect-ratio not)]
(st/emit! (dc/update-colorpicker-color
{:image (-> (:image current-color)
(assoc :keep-aspect-ratio keep-aspect-ratio?))}
true)))))
(let [keep-aspect-ratio? (-> current-color :image :keep-aspect-ratio not)
image (-> (:image current-color)
(assoc :keep-aspect-ratio keep-aspect-ratio?))]
(st/emit!
(dc/update-colorpicker-color {:image image} true)
(ptk/data-event ::ev/event {::ev/name "toggle-image-aspect-ratio"
::ev/origin "workspace:colorpicker"
:checked keep-aspect-ratio?})))))
on-change-tab
(mf/use-fn
@ -279,7 +286,8 @@
[:label {:for "keep-aspect-ratio"
:class (stl/css-case :global/checked keep-aspect-ratio?)}
[:span {:class (stl/css-case :global/checked keep-aspect-ratio?)}
(when keep-aspect-ratio? i/status-tick-refactor)]
(when keep-aspect-ratio?
i/status-tick-refactor)]
(tr "media.keep-aspect-ratio")
[:input {:type "checkbox"
:id "keep-aspect-ratio"

View file

@ -110,7 +110,7 @@
}
}
.label {
@include bodyMedTipography;
@include bodySmallTypography;
}
&:hover {
.icon svg {
@ -164,7 +164,7 @@
}
.placeholder-label {
@include bodyMedTipography;
@include bodySmallTypography;
text-align: center;
width: $s-184;
color: var(--empty-message-foreground-color);

View file

@ -48,9 +48,11 @@
(dom/stop-propagation event))
(mf/defc menu-entry
[{:keys [title shortcut on-click on-pointer-enter on-pointer-leave on-unmount children selected? icon disabled] :as props}]
{::mf/props :obj}
[{:keys [title shortcut on-click on-pointer-enter on-pointer-leave
on-unmount children selected? icon disabled value]}]
(let [submenu-ref (mf/use-ref nil)
hovering? (mf/use-ref false)
hovering? (mf/use-ref false)
on-pointer-enter
(mf/use-callback
(fn []
@ -86,6 +88,7 @@
(if icon
[:li {:class (stl/css :icon-menu-item)
:disabled disabled
:data-value value
:ref set-dom-node
:on-click on-click
:on-pointer-enter on-pointer-enter
@ -100,6 +103,7 @@
[:li {:class (stl/css :context-menu-item)
:disabled disabled
:ref set-dom-node
:data-value value
:on-click on-click
:on-pointer-enter on-pointer-enter
:on-pointer-leave on-pointer-leave}
@ -383,39 +387,56 @@
[:& menu-entry {:title (tr "workspace.shape.menu.flow-start")
:on-click do-add-flow}])))))
(mf/defc context-menu-flex
(mf/defc context-menu-layout
{::mf/props :obj}
[{:keys [shapes]}]
(let [single? (= (count shapes) 1)
has-frame? (->> shapes (d/seek cfh/frame-shape?))
is-flex-container? (and single? has-frame? (= :flex (:layout (first shapes))))
ids (->> shapes (map :id))
(let [single? (= (count shapes) 1)
add-layout
(fn [type]
(if (and single? has-frame?)
(st/emit! (dwsl/create-layout-from-id (first ids) type true))
(st/emit! (dwsl/create-layout-from-selection type))))
has-flex?
(and single? (every? ctl/flex-layout? shapes))
remove-flex
(fn []
(st/emit! (dwsl/remove-layout ids)))]
has-grid?
(and single? (every? ctl/grid-layout? shapes))
[:*
(when (not is-flex-container?)
[:div
[:& menu-separator]
[:& menu-entry {:title (tr "workspace.shape.menu.add-flex")
:shortcut (sc/get-tooltip :toggle-layout-flex)
:on-click #(add-layout :flex)}]
[:& menu-entry {:title (tr "workspace.shape.menu.add-grid")
:shortcut (sc/get-tooltip :toggle-layout-grid)
:on-click #(add-layout :grid)}]])
(when is-flex-container?
[:div
[:& menu-separator]
[:& menu-entry {:title (tr "workspace.shape.menu.remove-flex")
:shortcut (sc/get-tooltip :toggle-layout-flex)
:on-click remove-flex}]])]))
on-add-layout
(mf/use-fn
(fn [event]
(let [type (-> (dom/get-current-target event)
(dom/get-data "value")
(keyword))]
(st/emit! (with-meta (dwsl/create-layout type)
{::ev/origin "workspace:context-menu"})))))
on-remove-layout
(mf/use-fn
(mf/deps shapes)
(fn [_event]
(let [ids (map :id shapes)]
(st/emit! (dwsl/remove-layout ids)))))]
(if (or ^boolean has-flex?
^boolean has-grid?)
[:div
[:& menu-separator]
(if has-flex?
[:& menu-entry {:title (tr "workspace.shape.menu.remove-flex")
:shortcut (sc/get-tooltip :toggle-layout-flex)
:on-click on-remove-layout}]
[:& menu-entry {:title (tr "workspace.shape.menu.remove-grid")
:shortcut (sc/get-tooltip :toggle-layout-grid)
:on-click on-remove-layout}])]
[:div
[:& menu-separator]
[:& menu-entry {:title (tr "workspace.shape.menu.add-flex")
:shortcut (sc/get-tooltip :toggle-layout-flex)
:value "flex"
:on-click on-add-layout}]
[:& menu-entry {:title (tr "workspace.shape.menu.add-grid")
:shortcut (sc/get-tooltip :toggle-layout-grid)
:value "grid"
:on-click on-add-layout}]])))
(mf/defc context-menu-component
[{:keys [shapes]}]
@ -476,7 +497,7 @@
[:> context-menu-path props]
[:> context-menu-layer-options props]
[:> context-menu-prototype props]
[:> context-menu-flex props]
[:> context-menu-layout props]
[:> context-menu-component props]
[:> context-menu-delete props]])))

View file

@ -37,7 +37,7 @@
cursor: pointer;
.title {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--menu-foreground-color);
}
.shortcut {
@ -45,7 +45,7 @@
gap: $s-2;
color: var(--menu-shortcut-foreground-color);
.shortcut-key {
@include bodyMedTipography;
@include bodySmallTypography;
@include flexCenter;
height: $s-20;
padding: $s-2 $s-6;

View file

@ -184,7 +184,7 @@
(tr "workspace.header.menu.disable-scale-content")
(tr "workspace.header.menu.enable-scale-content"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-scale-text))]
(for [sc (scd/split-sc (sc/get-tooltip :scale))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:on-click toggle-flag

View file

@ -36,7 +36,7 @@
width: $s-48;
border-radius: $br-8;
.label {
@include bodyMedTipography;
@include bodySmallTypography;
height: 100%;
padding: $s-8 0;
color: var(--button-tertiary-foreground-color-rest);

View file

@ -27,7 +27,6 @@
[app.main.ui.workspace.sidebar.sitemap :refer [sitemap]]
[app.util.debug :as dbg]
[app.util.i18n :refer [tr]]
[app.util.object :as obj]
[rumext.v2 :as mf]))
;; --- Left Sidebar (Component)
@ -157,10 +156,9 @@
(set-size (if (> size 276) 276 768))))
props
(-> props
(obj/clone)
(obj/set! "on-change-section" handle-change-section)
(obj/set! "on-expand" handle-expand))]
(mf/spread props
:on-change-section handle-change-section
:on-expand handle-expand)]
[:& (mf/provider muc/sidebar) {:value :right}
[:aside {:class (stl/css-case :right-settings-bar true
@ -186,7 +184,7 @@
[:& comments-sidebar]
(true? is-history?)
[:& history-toolbox]
[:> history-toolbox {}]
:else
[:> options-toolbox props])]]]))

View file

@ -113,7 +113,7 @@
}
.section-item {
@include bodyMedTipography;
@include bodySmallTypography;
display: flex;
align-items: center;
justify-content: space-between;

View file

@ -45,7 +45,7 @@
border: $s-1 solid var(--input-border-color-focus);
input.element-name {
@include textEllipsis;
@include bodyMedTipography;
@include bodySmallTypography;
@include removeInputStyle;
flex-grow: 1;
height: $s-28;
@ -67,7 +67,7 @@
}
.name-block {
@include bodyMedTipography;
@include bodySmallTypography;
display: grid;
grid-template-columns: auto 1fr;
margin: 0;

View file

@ -29,7 +29,7 @@
cursor: pointer;
.cell-name {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
display: none;
position: absolute;
@ -44,7 +44,7 @@
color: var(--assets-item-name-foreground-color);
input {
@include textEllipsis;
@include bodyMedTipography;
@include bodySmallTypography;
@include removeInputStyle;
height: auto;
padding: 0;
@ -132,13 +132,13 @@
}
.item-name {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
order: 2;
color: var(--assets-item-name-foreground-color);
input {
@include textEllipsis;
@include bodyMedTipography;
@include bodySmallTypography;
@include removeInputStyle;
height: $s-32;
padding: $s-4;

View file

@ -14,7 +14,7 @@
}
.file-name {
@include bodyMedTipography;
@include bodySmallTypography;
display: flex;
justify-content: flex-start;
align-items: center;
@ -71,6 +71,6 @@
}
.no-found-text {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--not-found-foreground-color);
}

View file

@ -36,7 +36,7 @@
height: 10vh;
}
.cell-name {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
display: none;
position: absolute;
@ -110,7 +110,7 @@
}
.item-name {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
padding-left: $s-8;
color: var(--assets-item-name-foreground-color);

View file

@ -45,7 +45,7 @@
}
.modal-content {
@include bodyMedTipography;
@include bodySmallTypography;
margin-bottom: $s-24;
}
.input-wrapper {

View file

@ -8,12 +8,14 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.main.data.events :as ev]
[app.main.data.workspace :as dw]
[app.main.data.workspace.common :as dwc]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.icons :as i]
[app.util.dom :as dom]
[app.util.i18n :refer [t] :as i18n]
[cuerdas.core :as str]
[okulary.core :as l]
@ -283,8 +285,18 @@
nil)]))
(mf/defc history-entry [{:keys [locale entry idx-entry disabled? current?]}]
{::mf/props :obj}
(let [hover? (mf/use-state false)
show-detail? (mf/use-state false)]
show-detail? (mf/use-state false)
toggle-show-detail
(mf/use-fn
(fn [event]
(let [has-entry? (-> (dom/get-current-target event)
(dom/get-data "has-entry")
(parse-boolean))]
(dom/stop-propagation event)
(when has-entry?
(swap! show-detail? not)))))]
[:div {:class (stl/css-case :history-entry true
:disabled disabled?
:current current?
@ -301,8 +313,8 @@
(when (:detail entry)
[:div {:class (stl/css-case :history-entry-summary-button true
:button-opened @show-detail?)
:on-click #(when (:detail entry)
(swap! show-detail? not))}
:on-click toggle-show-detail
:data-has-entry (dm/str (not (nil? (:detail entry))))}
i/arrow-refactor])]
(when @show-detail?

View file

@ -59,7 +59,7 @@
}
.history-entry-empty-msg {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--empty-message-foreground-color);
}

View file

@ -8,7 +8,7 @@
.element-name {
@include textEllipsis;
@include bodyMedTipography;
@include bodySmallTypography;
flex-grow: 1;
color: var(--context-hover-color, var(--layer-row-foreground-color));
&.selected {
@ -27,7 +27,7 @@
}
.element-name-input {
@include textEllipsis;
@include bodyMedTipography;
@include bodySmallTypography;
@include removeInputStyle;
flex-grow: 1;
height: $s-28;

View file

@ -108,7 +108,7 @@
}
.focus-name {
@include bodyMedTipography;
@include bodySmallTypography;
display: flex;
align-items: center;
height: 100%;
@ -124,7 +124,7 @@
.focus-mode-tag {
@include flexCenter;
@include bodyMedTipography;
@include bodySmallTypography;
height: $s-20;
padding: $s-4 $s-6;
border: $s-1 solid var(--tag-background-color);
@ -160,7 +160,7 @@
.layer-filter-name {
@include flexCenter;
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--pill-foreground-color);
}
@ -174,7 +174,7 @@
left: $s-20;
width: $s-192;
.filter-menu-item {
@include bodyMedTipography;
@include bodySmallTypography;
display: flex;
align-items: center;
justify-content: space-between;

View file

@ -37,7 +37,6 @@
[app.main.ui.workspace.sidebar.options.shapes.svg-raw :as svg-raw]
[app.main.ui.workspace.sidebar.options.shapes.text :as text]
[app.util.i18n :as i18n :refer [tr]]
[app.util.object :as obj]
[rumext.v2 :as mf]))
;; --- Options
@ -75,7 +74,8 @@
(mf/defc options-content
{::mf/wrap [mf/memo]}
{::mf/memo true
::mf/props :obj}
[{:keys [selected section shapes shapes-with-children page-id file-id on-change-section on-expand]}]
(let [drawing (mf/deref refs/workspace-drawing)
objects (mf/deref refs/workspace-page-objects)
@ -104,7 +104,9 @@
{:on-change-tab on-change-tab
:selected section
:collapsable false
:content-class (stl/css-case :content-class true :inspect (= section :inspect))
:content-class (stl/css-case
:content-class true
:inspect (= section :inspect))
:header-class (stl/css :tab-spacing)}
[:& tab-element {:id :design
:title (tr "workspace.options.design")}
@ -172,14 +174,10 @@
;; need on multiple selection in majority of cases
(mf/defc options-toolbox
{::mf/wrap [mf/memo]
::mf/wrap-props false}
[props]
(let [section (obj/get props "section")
selected (obj/get props "selected")
on-change-section (obj/get props "on-change-section")
on-expand (obj/get props "on-expand")
page-id (mf/use-ctx ctx/current-page-id)
{::mf/memo true
::mf/props :obj}
[{:keys [section selected on-change-section on-expand]}]
(let [page-id (mf/use-ctx ctx/current-page-id)
file-id (mf/use-ctx ctx/current-file-id)
shapes (mf/deref refs/selected-objects)
shapes-with-children (mf/deref refs/selected-shapes-with-children)]

View file

@ -57,7 +57,7 @@
}
}
.label {
@include bodyMedTipography;
@include bodySmallTypography;
flex-grow: 1;
display: flex;
align-items: center;

View file

@ -7,6 +7,7 @@
(ns app.main.ui.workspace.sidebar.options.menus.component
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.helpers :as cfh]
[app.common.types.component :as ctk]
@ -31,152 +32,199 @@
[app.util.i18n :as i18n :refer [tr]]
[app.util.timers :as tm]
[cuerdas.core :as str]
[okulary.core :as l]
[rumext.v2 :as mf]))
(def ref:annotations-state
(l/derived :workspace-annotations st/state))
(mf/defc component-annotation
[{:keys [id shape component] :as props}]
(let [main-instance? (:main-instance shape)
component-id (:component-id shape)
annotation (:annotation component)
editing? (mf/use-state false)
invalid-text? (mf/use-state (or (nil? annotation) (str/blank? annotation)))
size (mf/use-state (count annotation))
textarea-ref (mf/use-ref)
{::mf/props :obj}
[{:keys [id shape component]}]
(let [main-instance? (:main-instance shape)
component-id (:component-id shape)
annotation (:annotation component)
shape-id (:id shape)
;; hack to create an autogrowing textarea
;; based on https://css-tricks.com/the-cleanest-trick-for-autogrowing-textareas/
autogrow #(let [textarea (mf/ref-val textarea-ref)
text (when textarea (.-value textarea))]
(reset! invalid-text? (str/blank? text))
(when textarea
(reset! size (count text))
(aset (.-dataset (.-parentNode textarea)) "replicatedValue" text)))
initialize #(let [textarea (mf/ref-val textarea-ref)]
(when textarea
(aset textarea "value" annotation)
(autogrow)))
editing* (mf/use-state false)
editing? (deref editing*)
discard (fn [event]
(dom/stop-propagation event)
(let [textarea (mf/ref-val textarea-ref)]
(aset textarea "value" annotation)
(reset! editing? false)
(st/emit! (dw/set-annotations-id-for-create nil))
(autogrow)))
save (fn [event]
(dom/stop-propagation event)
(let [textarea (mf/ref-val textarea-ref)
text (.-value textarea)]
(when-not (str/blank? text)
(reset! editing? false)
(st/emit!
(dw/set-annotations-id-for-create nil)
(dw/update-component-annotation component-id text)))))
workspace-annotations (mf/deref refs/workspace-annotations)
annotations-expanded? (:expanded? workspace-annotations)
creating? (= id (:id-for-create workspace-annotations))
invalid-text* (mf/use-state #(str/blank? annotation))
invalid-text? (deref invalid-text*)
expand #(when-not (or @editing? creating?)
(st/emit! (dw/set-annotations-expanded %)))
edit (fn [event]
(dom/stop-propagation event)
(when main-instance?
(let [textarea (mf/ref-val textarea-ref)]
(reset! editing? true)
(dom/focus! textarea))))
on-delete-annotation
(mf/use-callback
(mf/deps (:id shape))
size* (mf/use-state #(count annotation))
size (deref size*)
textarea-ref (mf/use-ref)
state (mf/deref ref:annotations-state)
expanded? (:expanded state)
create-id (:id-for-create state)
creating? (= id create-id)
;; hack to create an autogrowing textarea based on
;; https://css-tricks.com/the-cleanest-trick-for-autogrowing-textareas/
adjust-textarea-size
(mf/use-fn
#(when-let [textarea (mf/ref-val textarea-ref)]
(let [text (dom/get-value textarea)]
(reset! invalid-text* (str/blank? text))
(reset! size* (count text))
(let [^js parent (.-parentNode textarea)
^js dataset (.-dataset parent)]
(set! (.-replicatedValue dataset) text)))))
on-toggle-expand
(mf/use-fn
(mf/deps expanded? editing? creating?)
(fn [_]
(st/emit! (dw/set-annotations-expanded (not expanded?)))))
on-discard
(mf/use-fn
(mf/deps adjust-textarea-size creating?)
(fn [event]
(dom/stop-propagation event)
(st/emit! (modal/show
{:type :confirm
:title (tr "modals.delete-component-annotation.title")
:message (tr "modals.delete-component-annotation.message")
:accept-label (tr "ds.confirm-ok")
:on-accept (fn []
(st/emit!
(dw/set-annotations-id-for-create nil)
(dw/update-component-annotation component-id nil)))}))))]
(when-let [textarea (mf/ref-val textarea-ref)]
(dom/set-value! textarea annotation)
(reset! editing* false)
(when creating?
(st/emit! (dw/set-annotations-id-for-create nil)))
(adjust-textarea-size))))
(mf/use-effect
(mf/deps (:id shape))
(fn []
(initialize)
(when (and (not creating?) (:id-for-create workspace-annotations)) ;; cleanup set-annotations-id-for-create if we aren't on the marked component
(st/emit! (dw/set-annotations-id-for-create nil)))
(fn [] (st/emit! (dw/set-annotations-id-for-create nil))))) ;; cleanup set-annotationsid-for-create on unload
on-edit
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(when ^boolean main-instance?
(when-let [textarea (mf/ref-val textarea-ref)]
(reset! editing* true)
(dom/focus! textarea)))))
on-save
(mf/use-fn
(mf/deps creating?)
(fn [event]
(dom/stop-propagation event)
(when-let [textarea (mf/ref-val textarea-ref)]
(let [text (dom/get-value textarea)]
(when-not (str/blank? text)
(reset! editing* false)
(when ^boolean creating?
(st/emit! (dw/set-annotations-id-for-create nil)))
(dw/update-component-annotation component-id text))))))
on-delete-annotation
(mf/use-fn
(mf/deps shape-id component-id creating?)
(fn [event]
(dom/stop-propagation event)
(let [on-accept (fn []
(st/emit!
;; (ptk/data-event {::ev/name "delete-component-annotation"})
(when creating?
(dw/set-annotations-id-for-create nil))
(dw/update-component-annotation component-id nil)))]
(st/emit! (modal/show
{:type :confirm
:title (tr "modals.delete-component-annotation.title")
:message (tr "modals.delete-component-annotation.message")
:accept-label (tr "ds.confirm-ok")
:on-accept on-accept})))))]
(mf/with-effect [shape-id state create-id creating?]
(when-let [textarea (mf/ref-val textarea-ref)]
(dom/set-value! textarea annotation)
(adjust-textarea-size))
;; cleanup set-annotations-id-for-create if we aren't on the marked component
(when (and (not creating?) (some? create-id))
(st/emit! (dw/set-annotations-id-for-create nil)))
;; cleanup set-annotationsid-for-create on unload
(fn []
(when creating?
(st/emit! (dw/set-annotations-id-for-create nil)))))
(when (or creating? annotation)
[:div {:class (stl/css-case :component-annotation true
:editing @editing?
:creating creating?)}
[:div {:class (stl/css-case :annotation-title true
:expandeable (not (or @editing? creating?))
:expanded annotations-expanded?)
:on-click #(expand (not annotations-expanded?))}
[:div {:class (stl/css-case
:component-annotation true
:editing editing?
:creating creating?)}
[:div {:class (stl/css-case
:annotation-title true
:expandeable (not (or editing? creating?))
:expanded expanded?)
:on-click on-toggle-expand}
(if (or @editing? creating?)
(if (or editing? creating?)
[:span {:class (stl/css :annotation-text)}
(if @editing?
(if editing?
(tr "workspace.options.component.edit-annotation")
(tr "workspace.options.component.create-annotation"))]
[:*
[:span {:class (stl/css-case :icon-arrow true
:expanded annotations-expanded?)}
[:span {:class (stl/css-case
:icon-arrow true
:expanded expanded?)}
i/arrow-refactor]
[:span {:class (stl/css :annotation-text)}
(tr "workspace.options.component.annotation")]])
[:div {:class (stl/css :icons-wrapper)}
(when (and main-instance? annotations-expanded?)
(if (or @editing? creating?)
(when (and ^boolean main-instance?
^boolean expanded?)
(if (or ^boolean editing?
^boolean creating?)
[:*
[:div {:title (if creating? (tr "labels.create") (tr "labels.save"))
:on-click save
:class (stl/css-case :icon true
:icon-tick true
:hidden @invalid-text?)}
[:div {:title (if ^boolean creating?
(tr "labels.create")
(tr "labels.save"))
:on-click on-save
:class (stl/css-case
:icon true
:icon-tick true
:hidden invalid-text?)}
i/tick-refactor]
[:div {:class (stl/css :icon :icon-cross)
:title (tr "labels.discard")
:on-click discard}
:on-click on-discard}
i/close-refactor]]
[:*
[:div {:class (stl/css :icon :icon-edit)
:title (tr "labels.edit")
:on-click edit}
:on-click on-edit}
i/curve-refactor]
[:div {:class (stl/css :icon :icon-trash)
:title (tr "labels.delete")
:on-click on-delete-annotation}
i/delete-refactor]]))]]
[:div {:class (stl/css-case :hidden (not annotations-expanded?))}
[:div {:class (stl/css-case :hidden (not expanded?))}
[:div {:class (stl/css :grow-wrap)}
[:div {:class (stl/css :texarea-copy)}]
[:textarea
{:ref textarea-ref
:id "annotation-textarea"
:data-debug annotation
:auto-focus (or @editing? creating?)
:auto-focus (or editing? creating?)
:maxLength 300
:on-input autogrow
:on-input adjust-textarea-size
:default-value annotation
:read-only (not (or creating? @editing?))}]]
(when (or @editing? creating?)
[:div {:class (stl/css :counter)} (str @size "/300")])]])))
:read-only (not (or creating? editing?))}]]
(when (or editing? creating?)
[:div {:class (stl/css :counter)} (str size "/300")])]])))
(mf/defc component-swap-item
{::mf/wrap-props false}
[{:keys [item loop shapes file-id root-shape container component-id is-search listing-thumbs] :as props}]
(let [on-select-component
{::mf/props :obj}
[{:keys [item loop shapes file-id root-shape container component-id is-search listing-thumbs]}]
(let [on-select
(mf/use-fn
(mf/deps shapes file-id item)
#(when-not loop
(st/emit! (dwl/component-multi-swap shapes file-id (:id item)))))
item-ref (mf/use-ref)
visible? (h/use-visible item-ref :once? true)]
[:div {:ref item-ref
@ -186,7 +234,7 @@
:selected (= (:id item) component-id)
:disabled loop)
:key (str "swap-item-" (:id item))
:on-click on-select-component}
:on-click on-select}
(when visible?
[:& cmm/component-item-thumbnail {:file-id (:file-id item)
:root-shape root-shape
@ -197,12 +245,13 @@
(if is-search (:full-name item) (:name item))]]))
(mf/defc component-group-item
[{:keys [item on-enter-group] :as props}]
{::mf/props :obj}
[{:keys [item on-enter-group]}]
(let [group-name (:name item)
path (cfh/butlast-path-with-dots group-name)
on-group-click #(on-enter-group group-name)]
[:div {:class (stl/css :component-group)
:key (uuid/next) :on-click on-group-click
:on-click on-group-click
:title group-name}
[:div {:class (stl/css :path-wrapper)}
@ -215,43 +264,75 @@
[:span {:class (stl/css :arrow-icon)}
i/arrow-refactor]]))
(def ^:private ref:swap-libraries
(letfn [(get-libraries [state]
(let [file (:workspace-file state)
data (:workspace-data state)
libs (:workspace-libraries state)]
(assoc libs (:id file)
(assoc file :data data))))]
(l/derived get-libraries st/state)))
(defn- find-common-path
([components]
(let [paths (map (comp cfh/last-path :path) components)]
(find-common-path paths [] 0)))
([paths path n]
(let [current (nth (first paths) n nil)]
(if (or (nil? current)
(not (every? #(= current (nth % n nil)) paths)))
path
(find-common-path paths (conj path current) (inc n))))))
(defn- same-component-file?
[shape-a shape-b]
(= (:component-file shape-a)
(:component-file shape-b)))
(defn- same-component?
[shape-a shape-b]
(= (:component-id shape-a)
(:component-id shape-b)))
;; Get the ids of the components and its root-shapes that are parents of the current shape, to avoid loops
(defn get-parent-component-ids
[objects shape ids]
(let [shape-id (:id shape)]
(if (uuid/zero? shape-id)
ids
(let [ids (if (ctk/instance-head? shape)
(conj ids shape-id (:component-id shape))
ids)]
(get-parent-component-ids objects (get objects (:parent-id shape)) ids)))))
(mf/defc component-swap
[{:keys [shapes] :as props}]
{::mf/props :obj}
[{:keys [shapes]}]
(let [single? (= 1 (count shapes))
shape (first shapes)
current-file-id (mf/use-ctx ctx/current-file-id)
workspace-file (mf/deref refs/workspace-file)
workspace-data (mf/deref refs/workspace-data)
workspace-libraries (mf/deref refs/workspace-libraries)
libraries (mf/deref ref:swap-libraries)
objects (mf/deref refs/workspace-page-objects)
libraries (assoc workspace-libraries current-file-id (assoc workspace-file :data workspace-data))
single-comp (ctf/get-component libraries (:component-file shape) (:component-id shape))
every-same-file? (every? #(= (:component-file shape) (:component-file %)) shapes)
current-comp-id (when (every? #(= (:component-id shape) (:component-id %)) shapes)
(:component-id shape))
^boolean
every-same-file? (every? (partial same-component-file? shape) shapes)
component-id (if (every? (partial same-component? shape) shapes)
(:component-id shape)
nil)
file-id (if every-same-file?
(:component-file shape)
current-file-id)
orig-components (map #(ctf/get-component libraries (:component-file %) (:component-id %)) shapes)
paths (->> orig-components
(map :path)
(map cfh/split-path))
find-common-path (fn common-path [path n]
(let [current (nth (first paths) n nil)]
(if (or (nil? current)
(not (every? #(= current (nth % n nil)) paths)))
path
(common-path (conj path current) (inc n)))))
components (map #(ctf/get-component libraries (:component-file %) (:component-id %)) shapes)
path (if single?
(:path single-comp)
(:path (first components))
(cfh/join-path (if (not every-same-file?)
""
(find-common-path [] 0))))
(find-common-path components))))
filters* (mf/use-state
{:term ""
@ -263,13 +344,14 @@
is-search? (not (str/blank? (:term filters)))
current-library-id (if (contains? libraries (:file-id filters))
(:file-id filters)
current-file-id)
current-library-id (if (contains? libraries (:file-id filters))
(:file-id filters)
current-file-id)
current-library-name (if (= current-library-id current-file-id)
(str/upper (tr "workspace.assets.local-library"))
(get-in libraries [current-library-id :name]))
(dm/get-in libraries [current-library-id :name]))
components (->> (get-in libraries [current-library-id :data :components])
vals
@ -292,7 +374,7 @@
groups (when-not is-search?
(->> (sort (sequence xform components))
(map #(assoc {} :name %))))
(map (fn [name] {:name name}))))
components (if is-search?
(filter #(str/includes? (str/lower (:full-name %)) (str/lower (:term filters))) components)
@ -303,22 +385,16 @@
(->> (concat groups components)
(sort-by :name)))
;; Get the ids of the components and its root-shapes that are parents of the current shape, to avoid loops
get-comps-ids (fn get-comps-ids [shape ids]
(if (uuid/zero? (:id shape))
ids
(let [ids (if (ctk/instance-head? shape)
(conj ids (:id shape) (:component-id shape))
ids)]
(get-comps-ids (get objects (:parent-id shape)) ids))))
parent-components (mf/with-memo [shapes objects]
(into #{}
(comp
(map :parent-id)
(map (d/getf objects))
(mapcat #(get-parent-component-ids objects % [])))
shapes))
parent-components (->> shapes
(map :parent-id)
(map #(get objects %))
(mapcat #(get-comps-ids % []))
set)
libraries-options (map (fn [library] {:value (:id library) :label (:name library)}) (vals libraries))
libraries-options (map (fn [library] {:value (:id library) :label (:name library)})
(vals libraries))
on-library-change
(mf/use-fn
@ -409,27 +485,29 @@
:component-list (not (:listing-thumbs? filters)))}
(for [item items]
(if (:id item)
(let [data (get-in libraries [current-library-id :data])
(let [data (dm/get-in libraries [current-library-id :data])
container (ctf/get-component-page data item)
root-shape (ctf/get-component-root data item)
loop? (or (contains? parent-components (:main-instance-id item))
(contains? parent-components (:id item)))]
[:& component-swap-item {:key (:id item)
[:& component-swap-item {:key (dm/str (:id item))
:item item
:loop loop?
:shapes shapes
:file-id current-library-id
:root-shape root-shape
:container container
:component-id current-comp-id
:component-id component-id
:is-search is-search?
:listing-thumbs (:listing-thumbs? filters)}])
[:& component-group-item {:item item
:key (:id item)
:key (:name item)
:on-enter-group on-enter-group}]))]]]]))
(mf/defc component-ctx-menu
[{:keys [menu-entries on-close show main-instance] :as props}]
{::mf/props :obj}
[{:keys [menu-entries on-close show main-instance]}]
(let [do-action
(fn [action event]
(dom/stop-propagation event)
@ -438,15 +516,16 @@
[:& dropdown {:show show :on-close on-close}
[:ul {:class (stl/css-case :custom-select-dropdown true
:not-main (not main-instance))}
(for [entry menu-entries :when (not (nil? entry))]
[:li {:key (uuid/next)
:class (stl/css :dropdown-element)
:on-click (partial do-action (:action entry))}
[:span {:class (stl/css :dropdown-label)}
(tr (:msg entry))]])]]))
(for [{:keys [msg] :as entry} menu-entries]
(when (some? msg)
[:li {:key msg
:class (stl/css :dropdown-element)
:on-click (partial do-action (:action entry))}
[:span {:class (stl/css :dropdown-label)} (tr msg)]]))]]))
(mf/defc component-menu
[{:keys [shapes swap-opened?] :as props}]
{::mf/props :obj}
[{:keys [shapes swap-opened?]}]
(let [current-file-id (mf/use-ctx ctx/current-file-id)
components-v2 (mf/use-ctx ctx/components-v2)
workspace-data (deref refs/workspace-data)
@ -467,7 +546,11 @@
shape (first shapes)
id (:id shape)
shape-name (:name shape)
component (ctf/resolve-component shape {:id current-file-id :data workspace-data} workspace-libraries {:include-deleted? true})
component (ctf/resolve-component shape
{:id current-file-id
:data workspace-data}
workspace-libraries
{:include-deleted? true})
main-instance? (if components-v2 (ctk/main-instance? shape) true)
toggle-content

View file

@ -96,7 +96,7 @@
}
.component-name {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
direction: rtl;
text-align: left;
@ -104,7 +104,7 @@
}
.component-parent-name {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
direction: rtl;
text-align: left;
@ -143,7 +143,7 @@
}
.copy-text {
@include bodyMedTipography;
@include bodySmallTypography;
height: 100%;
display: flex;
align-items: center;
@ -244,7 +244,7 @@
}
.path-name {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
direction: rtl;
height: $s-32;
@ -252,7 +252,7 @@
}
.path-name-last {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
height: $s-32;
padding: $s-8 0 $s-8 $s-2;
@ -260,7 +260,7 @@
}
.component-list-empty {
@include bodyMedTipography;
@include bodySmallTypography;
margin: 0 $s-4 0 $s-8;
color: $df-secondary;
}
@ -346,7 +346,7 @@
object-fit: contain;
}
.component-name {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
display: none;
position: absolute;
@ -431,7 +431,7 @@
}
.library-name {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
color: var(--title-foreground-color);
padding: $s-8 0 $s-8 $s-2;
@ -452,7 +452,7 @@
}
.component-group {
@include bodyMedTipography;
@include bodySmallTypography;
display: grid;
grid-template-columns: 1fr $s-12;
height: $s-32;
@ -495,7 +495,7 @@
// Component annotation
.component-annotation {
@include bodyMedTipography;
@include bodySmallTypography;
color: var(--entry-foreground-color);
border-radius: $br-8;
@ -613,7 +613,7 @@
}
.counter {
@include bodyMedTipography;
@include bodySmallTypography;
text-align: right;
color: var(--entry-foreground-color);
margin: 0 $s-8 $s-8 0;

View file

@ -134,7 +134,7 @@
}
label {
@include bodyMedTipography;
@include bodySmallTypography;
display: flex;
align-items: center;
gap: $s-2;

View file

@ -55,12 +55,12 @@
}
.after {
@include bodyMedTipography;
@include bodySmallTypography;
margin-top: $s-1;
}
.interactions-help {
@include bodyMedTipography;
@include bodySmallTypography;
text-align: center;
color: var(--title-foreground-color);
}
@ -120,7 +120,7 @@
}
.interaction-name {
@include twoLineTextEllipsis;
@include bodyMedTipography;
@include bodySmallTypography;
padding-left: $s-4;
width: $s-92;
margin: auto 0;
@ -287,7 +287,7 @@
}
.flow-name-wrapper {
@include bodyMedTipography;
@include bodySmallTypography;
@include focusInput;
display: flex;
align-items: center;
@ -324,7 +324,7 @@
}
.flow-input-wrapper {
@include bodyMedTipography;
@include bodySmallTypography;
display: flex;
align-items: center;
height: $s-28;

View file

@ -12,6 +12,7 @@
[app.common.math :as mth]
[app.common.types.shape.layout :as ctl]
[app.config :as cf]
[app.main.data.events :as-alias ev]
[app.main.data.workspace :as udw]
[app.main.data.workspace.grid-layout.editor :as dwge]
[app.main.data.workspace.shape-layout :as dwsl]
@ -855,7 +856,9 @@
(let [type (-> (dom/get-current-target event)
(dom/get-data "type")
(keyword))]
(st/emit! (dwsl/create-layout type))
(st/emit! (with-meta (dwsl/create-layout type)
{::ev/origin "workspace:sidebar"}))
(reset! open* true))))
on-remove-layout

View file

@ -44,7 +44,7 @@
}
.select-name {
@include bodyMedTipography;
@include bodySmallTypography;
display: flex;
justify-content: flex-start;
align-items: center;

View file

@ -26,7 +26,7 @@
}
.attr-name {
@include bodyMedTipography;
@include bodySmallTypography;
@include twoLineTextEllipsis;
width: $s-88;
margin: auto $s-4;
@ -60,7 +60,7 @@
}
.attr-title {
@include bodyMedTipography;
@include bodySmallTypography;
font-size: $fs-10;
text-transform: uppercase;
margin-inline-start: $s-4;

View file

@ -33,7 +33,7 @@
}
.multiple-text {
@include bodyMedTipography;
@include bodySmallTypography;
flex-grow: 1;
color: var(--input-foreground-color-active);
}

View file

@ -72,7 +72,7 @@
.typography-name,
.typography-font {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
display: flex;
align-items: center;
@ -90,7 +90,7 @@
}
.font-name-wrapper {
@include bodyMedTipography;
@include bodySmallTypography;
display: flex;
align-items: center;
height: $s-32;
@ -169,7 +169,7 @@
color: var(--assets-item-name-foreground-color-hover);
}
.typography-name {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
display: flex;
align-items: center;
@ -178,7 +178,7 @@
color: var(--assets-item-name-foreground-color-hover);
}
.typography-font {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
margin-left: $s-6;
display: flex;
@ -207,14 +207,14 @@
--calcualted-width: calc(var(--width) - $s-48);
padding-left: $s-2;
.info-label {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
width: calc(var(--calcualted-width) / 2);
padding-top: $s-8;
color: var(--assets-item-name-foreground-color);
}
.info-content {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
padding-top: $s-8;
width: calc(var(--calcualted-width) / 2);
@ -254,7 +254,7 @@
position: relative;
}
.font-option {
@include bodyMedTipography;
@include bodySmallTypography;
@extend .asset-element;
padding: $s-8 0 $s-8 $s-8;
cursor: pointer;
@ -277,7 +277,7 @@
gap: $s-4;
.font-size-options {
@extend .asset-element;
@include bodyMedTipography;
@include bodySmallTypography;
flex-grow: 1;
width: $s-60;
margin: 0;
@ -331,7 +331,7 @@
.font-size-select {
@include removeInputStyle;
@include bodyMedTipography;
@include bodySmallTypography;
height: $s-32;
height: 100%;
width: 100%;
@ -410,7 +410,7 @@
}
.label {
@include bodyMedTipography;
@include bodySmallTypography;
flex-grow: 1;
}
}

View file

@ -67,7 +67,7 @@
}
}
.color-name {
@include bodyMedTipography;
@include bodySmallTypography;
@include textEllipsis;
padding-inline: $s-6;
border-radius: $br-8;
@ -86,7 +86,7 @@
stroke: var(--detach-icon-foreground-color);
}
.color-input-wrapper {
@include bodyMedTipography;
@include bodySmallTypography;
display: flex;
align-items: center;
height: $s-28;

Some files were not shown because too many files have changed in this diff Show more