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

This commit is contained in:
Andrey Antukh 2024-03-22 08:40:09 +01:00
commit a79e4d7af3
80 changed files with 563 additions and 2702 deletions

View File

@ -4,7 +4,7 @@ export PENPOT_HOST=devenv
export PENPOT_TENANT=dev export PENPOT_TENANT=dev
export PENPOT_FLAGS="\ export PENPOT_FLAGS="\
$PENPOT_FLAGS \ $PENPOT_FLAGS \
enable-registration enable-login-with-ldap \
enable-login-with-password enable-login-with-password
enable-login-with-oidc \ enable-login-with-oidc \
enable-login-with-google \ enable-login-with-google \
@ -28,9 +28,7 @@ export PENPOT_FLAGS="\
enable-access-tokens \ enable-access-tokens \
disable-feature-components-v2 \ disable-feature-components-v2 \
enable-file-validation \ enable-file-validation \
enable-file-schema-validation \ enable-file-schema-validation";
disable-soft-file-schema-validation \
disable-soft-file-validation";
# Setup default upload media file size to 100MiB # Setup default upload media file size to 100MiB

View File

@ -15,13 +15,12 @@ export PENPOT_FLAGS="\
enable-feature-fdata-pointer-map \ enable-feature-fdata-pointer-map \
enable-feature-fdata-objects-map \ enable-feature-fdata-objects-map \
disable-secure-session-cookies \ disable-secure-session-cookies \
enable-rpc-climit \
enable-smtp \ enable-smtp \
enable-access-tokens \ enable-access-tokens \
disable-feature-components-v2 \ disable-feature-components-v2 \
enable-file-validation \ enable-file-validation \
enable-file-schema-validation \ enable-file-schema-validation";
disable-soft-file-schema-validation \
disable-soft-file-validation";
export OPTIONS=" export OPTIONS="
-A:jmx-remote -A:dev \ -A:jmx-remote -A:dev \

View File

@ -215,10 +215,15 @@
(update :pages-index update-vals fix-container) (update :pages-index update-vals fix-container)
(d/update-when :components update-vals fix-container)))) (d/update-when :components update-vals fix-container))))
fix-page-invalid-options fix-invalid-page
(fn [file-data] (fn [file-data]
(letfn [(update-page [page] (letfn [(update-page [page]
(update page :options fix-options)) (-> page
(update :name (fn [name]
(if (nil? name)
"Page"
name)))
(update :options fix-options)))
(fix-background [options] (fix-background [options]
(if (and (contains? options :background) (if (and (contains? options :background)
@ -433,7 +438,8 @@
(letfn [(fix-component [components id component] (letfn [(fix-component [components id component]
(let [root-shape (ctst/get-shape component (:id component))] (let [root-shape (ctst/get-shape component (:id component))]
(if (or (empty? (:objects component)) (if (or (empty? (:objects component))
(nil? root-shape)) (nil? root-shape)
(nil? (:type root-shape)))
(dissoc components id) (dissoc components id)
components)))] components)))]
@ -731,43 +737,61 @@
(fn [file-data] (fn [file-data]
;; Remap shape-refs so that they point to the near main. ;; Remap shape-refs so that they point to the near main.
;; At the same time, if there are any dangling ref, detach the shape and its children. ;; At the same time, if there are any dangling ref, detach the shape and its children.
(letfn [(fix-container [container] (let [count (volatile! 0)
(reduce fix-shape container (ctn/shapes-seq container)))
(fix-shape [container shape] fix-shape
(if (ctk/in-component-copy? shape) (fn [container shape]
;; First look for the direct shape. (if (ctk/in-component-copy? shape)
(let [root (ctn/get-component-shape (:objects container) shape) ;; First look for the direct shape.
libraries (assoc-in libraries [(:id file-data) :data] file-data) (let [root (ctn/get-component-shape (:objects container) shape)
library (get libraries (:component-file root)) libraries (assoc-in libraries [(:id file-data) :data] file-data)
component (ctkl/get-component (:data library) (:component-id root) true) library (get libraries (:component-file root))
direct-shape (ctf/get-component-shape (:data library) component (:shape-ref shape))] component (ctkl/get-component (:data library) (:component-id root) true)
(if (some? direct-shape) direct-shape (ctf/get-component-shape (:data library) component (:shape-ref shape))]
;; If it exists, there is nothing else to do. (if (some? direct-shape)
container ;; If it exists, there is nothing else to do.
;; If not found, find the near shape. container
(let [near-shape (d/seek #(= (:shape-ref %) (:shape-ref shape)) ;; If not found, find the near shape.
(ctf/get-component-shapes (:data library) component))] (let [near-shape (d/seek #(= (:shape-ref %) (:shape-ref shape))
(if (some? near-shape) (ctf/get-component-shapes (:data library) component))]
;; If found, update the ref to point to the near shape. (if (some? near-shape)
(ctn/update-shape container (:id shape) #(assoc % :shape-ref (:id near-shape))) ;; If found, update the ref to point to the near shape.
;; If not found, it may be a fostered component. Try to locate a direct shape (do
;; in the head component. (vswap! count inc)
(let [head (ctn/get-head-shape (:objects container) shape) (ctn/update-shape container (:id shape) #(assoc % :shape-ref (:id near-shape))))
library-2 (get libraries (:component-file head)) ;; If not found, it may be a fostered component. Try to locate a direct shape
component-2 (ctkl/get-component (:data library-2) (:component-id head) true) ;; in the head component.
direct-shape-2 (ctf/get-component-shape (:data library-2) component-2 (:shape-ref shape))] (let [head (ctn/get-head-shape (:objects container) shape)
(if (some? direct-shape-2) library-2 (get libraries (:component-file head))
;; If it exists, there is nothing else to do. component-2 (ctkl/get-component (:data library-2) (:component-id head) true)
container direct-shape-2 (ctf/get-component-shape (:data library-2) component-2 (:shape-ref shape))]
;; If not found, detach shape and all children. (if (some? direct-shape-2)
;; container ;; If it exists, there is nothing else to do.
(detach-shape container shape))))))) container
container))] ;; If not found, detach shape and all children.
;; container
(do
(vswap! count inc)
(detach-shape container shape))))))))
container))
(-> file-data fix-container
(update :pages-index update-vals fix-container) (fn [container]
(d/update-when :components update-vals fix-container)))) (reduce fix-shape container (ctn/shapes-seq container)))]
[(-> file-data
(update :pages-index update-vals fix-container)
(d/update-when :components update-vals fix-container))
@count]))
remap-refs-recur
;; remapping refs can generate cascade changes so we call it until no changes are done
(fn [file-data]
(loop [f-data file-data]
(let [[f-data count] (remap-refs f-data)]
(if (= count 0)
f-data
(recur f-data)))))
fix-converted-copies fix-converted-copies
(fn [file-data] (fn [file-data]
@ -974,7 +998,7 @@
(-> file-data (-> file-data
(fix-file-data) (fix-file-data)
(fix-page-invalid-options) (fix-invalid-page)
(fix-misc-shape-issues) (fix-misc-shape-issues)
(fix-recent-colors) (fix-recent-colors)
(fix-missing-image-metadata) (fix-missing-image-metadata)
@ -993,8 +1017,8 @@
(remove-nested-roots) (remove-nested-roots)
(add-not-nested-roots) (add-not-nested-roots)
(fix-components-without-id) (fix-components-without-id)
(remap-refs)
(fix-converted-copies) (fix-converted-copies)
(remap-refs-recur)
(wrap-non-group-component-roots) (wrap-non-group-component-roots)
(detach-non-group-instance-roots) (detach-non-group-instance-roots)
(transform-to-frames) (transform-to-frames)

View File

@ -21,7 +21,6 @@
[app.worker :as-alias wrk] [app.worker :as-alias wrk]
[clojure.edn :as edn] [clojure.edn :as edn]
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
[cuerdas.core :as str]
[datoteka.fs :as fs] [datoteka.fs :as fs]
[integrant.core :as ig] [integrant.core :as ig]
[promesa.exec :as px] [promesa.exec :as px]
@ -241,16 +240,15 @@
[{:keys [::label ::profile-id ::rpc/climit ::mtx/metrics] :as cfg} f] [{:keys [::label ::profile-id ::rpc/climit ::mtx/metrics] :as cfg} f]
(let [config (get climit ::config) (let [config (get climit ::config)
cache (get climit ::cache)] cache (get climit ::cache)]
(reduce (fn [handler [limit-id limit-key :as ckey]] (reduce (fn [handler [limit-id limit-key :as ckey]]
(let [config (get config limit-id)] (if-let [config (get config limit-id)]
(when-not config
(throw (IllegalArgumentException.
(str/ffmt "config not found for: %" limit-id))))
(fn [& params] (fn [& params]
(let [limiter (cache/get cache ckey (partial create-limiter config))] (let [limiter (cache/get cache ckey (partial create-limiter config))]
(invoke limiter metrics limit-id limit-key label profile-id handler params))))) (invoke limiter metrics limit-id limit-key label profile-id handler params)))
(do
(l/wrn :hint "config not found" :label label :id limit-id)
f)))
f f
(get-limits cfg)))) (get-limits cfg))))

View File

@ -190,8 +190,8 @@
{:project-id (:id project) {:project-id (:id project)
:profile-id profile-id :profile-id profile-id
:team-id team-id :team-id team-id
:is-pinned true}) :is-pinned false})
(assoc project :is-pinned true)))) (assoc project :is-pinned false))))
;; --- MUTATION: Toggle Project Pin ;; --- MUTATION: Toggle Project Pin

View File

@ -179,7 +179,7 @@
component-child (first component-children)] component-child (first component-children)]
(if (or (nil? child) (nil? component-child)) (if (or (nil? child) (nil? component-child))
container container
(let [container (if (and (not (ctk/is-main-of? component-child child)) (let [container (if (and (not (ctk/is-main-of? component-child child true))
(nil? (ctk/get-swap-slot child)) (nil? (ctk/get-swap-slot child))
(ctk/instance-head? child)) (ctk/instance-head? child))
(let [slot (guess-swap-slot component-child component-container)] (let [slot (guess-swap-slot component-child component-container)]

View File

@ -56,7 +56,9 @@
;; migrations process, so all features referenced in migrations should ;; migrations process, so all features referenced in migrations should
;; be here. ;; be here.
(def default-enabled-features (def default-enabled-features
#{"fdata/shape-data-type"}) #{"fdata/shape-data-type"
"styles/v2"
"layout/grid"})
;; A set of features which only affects on frontend and can be enabled ;; A set of features which only affects on frontend and can be enabled
;; and disabled freely by the user any time. This features does not ;; and disabled freely by the user any time. This features does not

View File

@ -732,7 +732,8 @@
:main-instance-id (:main-instance-id new-component) :main-instance-id (:main-instance-id new-component)
:main-instance-page (:main-instance-page new-component) :main-instance-page (:main-instance-page new-component)
:annotation (:annotation new-component) :annotation (:annotation new-component)
:objects (:objects new-component)}) ;; this won't exist in components-v2 (except for deleted components) :objects (:objects new-component) ;; this won't exist in components-v2 (except for deleted components)
:modified-at (:modified-at new-component)})
(update :undo-changes conj {:type :mod-component (update :undo-changes conj {:type :mod-component
:id id :id id
:name (:name prev-component) :name (:name prev-component)

View File

@ -73,7 +73,7 @@
[{:keys [version] :as file}] [{:keys [version] :as file}]
(if (int? version) (if (int? version)
file file
(let [version (or version (-> file :data :version))] (let [version (or (-> file :data :version) 0)]
(-> file (-> file
(assoc :version version) (assoc :version version)
(update :data dissoc :version))))) (update :data dissoc :version)))))
@ -853,11 +853,11 @@
(defn migrate-up-44 (defn migrate-up-44
[data] [data]
(letfn [(fix-shadow [shadow] (letfn [(fix-shadow [shadow]
(if (string? (:color shadow)) (let [color (if (string? (:color shadow))
(let [color {:color (:color shadow) {:color (:color shadow)
:opacity 1}] :opacity 1}
(assoc shadow :color color)) (d/without-nils (:color shadow)))]
shadow)) (assoc shadow :color color)))
(update-object [object] (update-object [object]
(d/update-when object :shadow (d/update-when object :shadow

View File

@ -12,9 +12,7 @@
(def default (def default
"A common flags that affects both: backend and frontend." "A common flags that affects both: backend and frontend."
[:enable-registration [:enable-registration
:enable-login-with-password :enable-login-with-password])
:enable-login-illustration
:enable-feature-styles-v2])
(defn parse (defn parse
[& flags] [& flags]
@ -35,5 +33,3 @@
:else :else
(recur (rest flags) result))))))) (recur (rest flags) result)))))))

View File

@ -190,3 +190,8 @@
(get-rect-filter-bounds children-bounds filters blur-value)))) (get-rect-filter-bounds children-bounds filters blur-value))))
(defn get-frame-bounds
([shape]
(get-frame-bounds shape nil))
([shape {:keys [ignore-margin?] :or {ignore-margin? false}}]
(get-object-bounds [] shape {:ignore-margin? ignore-margin?})))

View File

@ -995,6 +995,9 @@
(= key :style) (= key :style)
attrs attrs
(= key :unicode)
attrs
(str/starts-with? (d/name key) "data-") (str/starts-with? (d/name key) "data-")
attrs attrs

View File

@ -383,13 +383,16 @@
(update :svg-attrs dissoc :fill) (update :svg-attrs dissoc :fill)
(assoc-in [:fills 0 :fill-color] (clr/parse color-style))) (assoc-in [:fills 0 :fill-color] (clr/parse color-style)))
(dm/get-in shape [:svg-attrs :fillOpacity]) ;; Only create an opacity if the color is setted. Othewise can create problems down the line
(and (or (clr/color-string? color-attr) (clr/color-string? color-style))
(dm/get-in shape [:svg-attrs :fillOpacity]))
(-> (update :svg-attrs dissoc :fillOpacity) (-> (update :svg-attrs dissoc :fillOpacity)
(update-in [:svg-attrs :style] dissoc :fillOpacity) (update-in [:svg-attrs :style] dissoc :fillOpacity)
(assoc-in [:fills 0 :fill-opacity] (-> (dm/get-in shape [:svg-attrs :fillOpacity]) (assoc-in [:fills 0 :fill-opacity] (-> (dm/get-in shape [:svg-attrs :fillOpacity])
(d/parse-double 1)))) (d/parse-double 1))))
(dm/get-in shape [:svg-attrs :style :fillOpacity]) (and (or (clr/color-string? color-attr) (clr/color-string? color-style))
(dm/get-in shape [:svg-attrs :style :fillOpacity]))
(-> (update-in [:svg-attrs :style] dissoc :fillOpacity) (-> (update-in [:svg-attrs :style] dissoc :fillOpacity)
(update :svg-attrs dissoc :fillOpacity) (update :svg-attrs dissoc :fillOpacity)
(assoc-in [:fills 0 :fill-opacity] (-> (dm/get-in shape [:svg-attrs :style :fillOpacity]) (assoc-in [:fills 0 :fill-opacity] (-> (dm/get-in shape [:svg-attrs :style :fillOpacity])

View File

@ -138,10 +138,10 @@
(= (:component-file shape) file-id))) (= (:component-file shape) file-id)))
(defn is-main-of? (defn is-main-of?
[shape-main shape-inst] [shape-main shape-inst components-v2]
(and (:shape-ref shape-inst) (or (= (:shape-ref shape-inst) (:id shape-main))
(or (= (:shape-ref shape-inst) (:id shape-main)) (and (= (:shape-ref shape-inst) (:shape-ref shape-main))
(= (:shape-ref shape-inst) (:shape-ref shape-main))))) (not components-v2))))
(defn main-instance? (defn main-instance?
"Check if this shape is the root of the main instance of some "Check if this shape is the root of the main instance of some

View File

@ -48,7 +48,7 @@
(wrap-object-fn))))))) (wrap-object-fn)))))))
(defn mod-component (defn mod-component
[file-data {:keys [id name path main-instance-id main-instance-page objects annotation]}] [file-data {:keys [id name path main-instance-id main-instance-page objects annotation modified-at]}]
(let [wrap-objects-fn cfeat/*wrap-with-objects-map-fn*] (let [wrap-objects-fn cfeat/*wrap-with-objects-map-fn*]
(d/update-in-when file-data [:components id] (d/update-in-when file-data [:components id]
(fn [component] (fn [component]
@ -69,6 +69,9 @@
(some? objects) (some? objects)
(assoc :objects objects) (assoc :objects objects)
(some? modified-at)
(assoc :modified-at modified-at)
(some? annotation) (some? annotation)
(assoc :annotation annotation) (assoc :annotation annotation)
@ -76,7 +79,7 @@
(dissoc :annotation)) (dissoc :annotation))
diff (set/difference diff (set/difference
(ctk/diff-components component new-comp) (ctk/diff-components component new-comp)
#{:annotation})] ;; The set of properties that doesn't mark a component as touched #{:annotation :modified-at})] ;; The set of properties that doesn't mark a component as touched
(if (empty? diff) (if (empty? diff)
new-comp new-comp

View File

@ -154,6 +154,17 @@
:else :else
(get-head-shape objects (get objects (:parent-id shape)) options)))) (get-head-shape objects (get objects (:parent-id shape)) options))))
(defn get-child-heads
"Get all recursive childs that are heads (when a head is found, do not
continue down looking for subsequent nested heads)."
[objects shape-id]
(let [shape (get objects shape-id)]
(if (nil? shape)
[]
(if (ctk/instance-head? shape)
[shape]
(mapcat #(get-child-heads objects %) (:shapes shape))))))
(defn get-parent-heads (defn get-parent-heads
"Get all component heads that are ancestors of the shape, in top-down order "Get all component heads that are ancestors of the shape, in top-down order
(include self if it's also a head)." (include self if it's also a head)."
@ -170,6 +181,20 @@
(filter #(and (ctk/instance-head? %) (ctk/in-component-copy? %))) (filter #(and (ctk/instance-head? %) (ctk/in-component-copy? %)))
(reverse))) (reverse)))
(defn get-nesting-level-delta
"Get how many levels a shape will 'go up' if moved under the new parent."
[objects shape new-parent]
(let [orig-heads (->> (get-parent-copy-heads objects shape)
(remove #(= (:id %) (:id shape))))
dest-heads (get-parent-copy-heads objects new-parent)
;; Calculate how many parent heads share in common the original
;; shape and the new parent.
pairs (map vector orig-heads dest-heads)
common-count (count (take-while (fn [a b] (= a b)) pairs))]
(- (count orig-heads) common-count)))
(defn get-instance-root (defn get-instance-root
"Get the parent shape at the top of the component instance (main or copy)." "Get the parent shape at the top of the component instance (main or copy)."
[objects shape] [objects shape]

View File

@ -216,14 +216,14 @@
(some find-ref-shape-in-head (ctn/get-parent-heads (:objects container) shape)))) (some find-ref-shape-in-head (ctn/get-parent-heads (:objects container) shape))))
(defn find-original-ref-shape (defn advance-shape-ref
"Recursively call to find-ref-shape until find the original shape of the original component" "Get the shape-ref of the near main of the shape, recursively repeated as many times
[file container libraries shape & options] as the given levels."
[file container libraries shape levels & options]
(let [ref-shape (find-ref-shape file container libraries shape options)] (let [ref-shape (find-ref-shape file container libraries shape options)]
(if (nil? (:shape-ref ref-shape)) (if (or (nil? (:shape-ref ref-shape)) (not (pos? levels)))
ref-shape (:id ref-shape)
(find-original-ref-shape file container libraries ref-shape options)))) (advance-shape-ref file container libraries ref-shape (dec levels) options))))
(defn find-ref-component (defn find-ref-component
"Locate the nearest component in the local file or libraries that is referenced by the "Locate the nearest component in the local file or libraries that is referenced by the

View File

@ -237,7 +237,7 @@
--assets-item-border-color: var(--color-accent-primary); --assets-item-border-color: var(--color-accent-primary);
--assets-item-background-color-drag: transparent; --assets-item-background-color-drag: transparent;
--assets-item-border-color-drag: var(--color-accent-primary-muted); --assets-item-border-color-drag: var(--color-accent-primary-muted);
--assets-component-background-color: var(--color-foreground-secondary); --assets-component-background-color: var(--color-canvas);
--assets-component-background-color-disabled: var(--df-secondary;); --assets-component-background-color-disabled: var(--df-secondary;);
--assets-component-border-color: var(--color-background-tertiary); --assets-component-border-color: var(--color-background-tertiary);
--assets-component-border-selected: var(--color-accent-tertiary); --assets-component-border-selected: var(--color-accent-tertiary);
@ -419,8 +419,6 @@
} }
.light { .light {
--assets-component-background-color: var(--color-background-secondary);
--tabs-background-color: var(--color-background-tertiary); --tabs-background-color: var(--color-background-tertiary);
--tab-background-color-selected: var(--color-background-primary); --tab-background-color-selected: var(--color-background-primary);
--tab-border-color: var(--color-background-tertiary); --tab-border-color: var(--color-background-tertiary);

View File

@ -4,6 +4,18 @@
// //
// Copyright (c) KALEIDOS INC // Copyright (c) KALEIDOS INC
@mixin font-face($style-name, $file, $weight: unquote("normal"), $style: unquote("normal")) {
$filepath: "/fonts/" + $file;
@font-face {
font-family: "#{$style-name}";
src:
url($filepath + ".woff2") format("woff2"),
url($filepath + ".ttf") format("truetype");
font-weight: unquote($weight);
font-style: unquote($style);
}
}
@mixin flexCenter { @mixin flexCenter {
display: flex; display: flex;
justify-content: center; justify-content: center;

View File

@ -33,34 +33,20 @@
//################################################# //#################################################
@import "common/base"; @import "common/base";
@import "main/layouts/login";
@import "main/layouts/main-layout"; @import "main/layouts/main-layout";
@import "main/layouts/not-found"; @import "main/layouts/not-found";
@import "main/layouts/viewer";
@import "main/layouts/inspect";
//################################################# //#################################################
// Commons // Commons
//################################################# //#################################################
@import "common/framework"; @import "common/framework";
@import "main/partials/forms";
@import "main/partials/texts"; @import "main/partials/texts";
@import "main/partials/context-menu";
@import "main/partials/dropdown";
//################################################# //#################################################
// Partials // Partials
//################################################# //#################################################
@import "main/partials/activity-bar";
@import "main/partials/debug-icons-preview"; @import "main/partials/debug-icons-preview";
@import "main/partials/editable-label";
@import "main/partials/loader"; @import "main/partials/loader";
@import "main/partials/project-bar";
@import "main/partials/sidebar";
@import "main/partials/tab-container";
@import "main/partials/user-settings";
@import "main/partials/workspace"; @import "main/partials/workspace";
@import "main/partials/exception-page";
@import "main/partials/signup-questions";

View File

@ -1,161 +0,0 @@
$width-left-toolbar: 48px;
$width-settings-bar: 256px;
.inspect-layout {
height: 100vh;
display: grid;
grid-template-rows: 48px auto;
grid-template-columns: 1fr;
user-select: none;
.viewer-header {
grid-column: 1 / span 1;
grid-row: 1 / span 1;
}
.viewer-content {
grid-column: 1 / span 1;
grid-row: 2 / span 1;
}
}
.fullscreen.inspect-layout.force-visible {
display: grid;
grid-template-rows: 1fr;
& .viewer-header {
position: fixed;
top: 0;
transition: top 400ms ease 300ms;
margin-bottom: 0;
z-index: 10;
}
& .viewer-bottom {
position: fixed;
bottom: 0;
transition: bottom 400ms ease 300ms;
z-index: 2;
}
}
.fullscreen.inspect-layout:not(.force-visible) {
& .viewer-header {
width: 100%;
position: fixed;
top: -48px;
left: 0;
transition: top 400ms ease 300ms;
z-index: 10;
margin-bottom: 48px;
&::after {
content: " ";
position: absolute;
width: 100%;
height: 1rem;
left: 0;
top: 48px;
}
}
& .viewer-header:hover {
top: 0;
transition: top 200ms;
}
& .viewer-bottom {
width: 100%;
position: fixed;
bottom: -48px;
left: 0;
transition: bottom 400ms ease 300ms;
z-index: 2;
&::after {
content: " ";
position: absolute;
width: 100%;
height: 1rem;
left: 0;
bottom: 0px;
}
}
& .viewer-bottom:hover {
bottom: 0px;
transition: bottom 200ms;
}
& .viewer-content {
grid-row: 1 / span 2;
}
}
.inspect-layout {
.viewer-section {
flex-wrap: nowrap;
margin-top: 0;
&.fullscreen {
.settings-bar,
.settings-bar {
padding-top: 48px;
}
}
}
.settings-bar {
width: $width-settings-bar;
&.settings-bar-right,
&.settings-bar-left {
height: 100%;
position: relative;
left: unset;
right: unset;
.settings-bar-inside {
padding-top: 0.5rem;
overflow-y: auto;
}
}
&.settings-bar-right {
width: 100%;
grid-area: right-sidebar;
}
}
.inspect-svg-wrapper {
flex: 1;
overflow: hidden;
flex-direction: column;
justify-content: flex-start;
position: relative;
}
.inspect-svg-container {
display: grid;
width: 100%;
height: 100%;
overflow: auto;
align-items: center;
justify-content: safe center;
margin: 0 auto;
}
}
.sidebar-container {
display: flex;
flex-direction: column;
width: var(--width, $width-settings-bar);
height: 100%;
overflow: hidden;
& > .resize-area {
position: absolute;
width: 8px;
height: 100%;
z-index: 10;
cursor: ew-resize;
}
}

View File

@ -1,250 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
// TODO: rename to auth.scss
.auth {
display: grid;
grid-template-rows: auto;
grid-template-columns: 33% auto;
height: 100vh;
overflow-y: scroll;
}
.auth-sidebar {
grid-column: 1 / span 1;
height: 100vh;
display: flex;
padding-top: 7vh;
flex-direction: column;
align-items: center;
justify-content: flex-start;
background-color: #151035;
background-image: url("/images/login-penpot.svg");
background-position: center 30vh;
background-size: 96%;
background-repeat: no-repeat;
.tagline {
text-align: center;
width: 280px;
font-size: $fs18;
margin-top: 2vh;
color: white;
}
.logo {
svg {
fill: white;
max-width: 11vw;
height: 80px;
}
.hidden-name {
visibility: hidden;
width: 0;
height: 0;
float: left;
}
}
}
.auth-content {
grid-column: 2 / span 1;
background-color: $color-white;
display: flex;
align-items: center;
justify-content: center;
position: relative;
.form-container {
width: 412px;
flex-direction: column;
margin-bottom: 30px;
.auth-buttons {
margin: $size-6 0 $size-4 0;
display: flex;
justify-content: center;
column-gap: 17px;
}
form {
margin: 2rem 0 0.5rem 0;
.accept-terms-and-privacy-wrapper {
position: relative;
.input-checkbox {
margin-bottom: 0;
}
.input-checkbox input[type="checkbox"] {
position: absolute;
display: block;
width: 20px;
height: 20px;
opacity: 0;
top: 22px;
}
label {
margin-left: 40px;
}
label:before {
position: absolute;
top: 15px;
left: -36px;
}
label:after {
position: absolute;
top: 15px;
left: -33px;
}
.input-checkbox input[type="checkbox"]:focus {
opacity: 100%;
}
.auth-links {
margin-left: 40px;
font-size: 0.75rem;
}
}
}
}
input {
margin-bottom: 0px;
}
.buttons-stack {
display: flex;
flex-direction: column;
width: 100%;
*:not(:last-child) {
margin-bottom: $size-4;
}
}
.btn-large {
flex-grow: 1;
font-size: $fs14;
font-style: normal;
font-weight: $fw400;
}
.btn-google-auth {
background-color: #4285f4;
color: $color-white;
margin-bottom: $size-4;
text-decoration: none;
.logo {
width: 20px;
height: 20px;
margin-right: 1rem;
}
&:hover,
&:focus {
background-color: #2065d7;
color: $color-white;
}
}
.btn-gitlab-auth {
background-color: #fc6d26;
color: $color-white;
margin-bottom: $size-4;
text-decoration: none;
.logo {
width: 20px;
height: 20px;
margin-right: 1rem;
}
&:hover,
&:focus {
background-color: #ee5f18;
color: $color-white;
}
}
.btn-github-auth {
background-color: #4c4c4c;
color: $color-white;
margin-bottom: $size-4;
text-decoration: none;
.logo {
width: 20px;
height: 20px;
margin-right: 1rem;
}
&:hover,
&:focus {
background-color: #2f2f2f;
color: $color-white;
}
}
.link-oidc {
text-align: center;
}
.separator {
display: flex;
justify-content: center;
width: 100%;
text-transform: uppercase;
text-align: center;
.text {
margin: 0 10px;
color: $color-gray-40;
}
.line {
border: 1px solid $color-gray-10;
flex-grow: 10;
margin: auto;
}
}
.links {
display: flex;
font-size: $fs14;
flex-direction: column;
justify-content: space-between;
margin-top: $size-4;
margin-bottom: $size-4;
&.demo {
justify-content: center;
margin-top: $size-5;
}
.link-entry {
font-size: $fs14;
color: $color-gray-40;
margin-bottom: 10px;
a {
font-size: $fs14;
font-weight: $fw500;
color: $color-gray-50;
&:hover,
&:focus {
text-decoration: underline;
}
}
}
}
.terms-login {
bottom: $size-5;
font-size: $fs14;
position: absolute;
span {
margin: 0 $size-2;
color: $color-gray-40;
}
}
}

View File

@ -1,101 +0,0 @@
.viewer-layout {
height: 100vh;
display: grid;
grid-template-rows: 48px auto;
grid-template-columns: 1fr;
user-select: none;
.viewer-header {
grid-column: 1 / span 1;
grid-row: 1 / span 1;
}
.viewer-content {
grid-column: 1 / span 1;
grid-row: 2 / span 1;
}
}
.fullscreen.viewer-layout.force-visible {
grid-template-rows: 1fr;
& .viewer-header {
position: fixed;
top: 0;
transition: top 400ms ease 300ms;
margin-bottom: 0;
z-index: 2;
}
& .viewer-bottom {
position: fixed;
bottom: 0;
transition: bottom 400ms ease 300ms;
z-index: 2;
}
}
.fullscreen.viewer-layout:not(.force-visible) {
grid-template-rows: 1fr;
& .viewer-header {
width: 100%;
position: fixed;
top: -48px;
left: 0;
transition: top 400ms ease 300ms;
z-index: 2;
margin-bottom: 48px;
&::after {
content: " ";
position: absolute;
width: 100%;
height: 1rem;
left: 0;
top: 48px;
}
}
& .viewer-header:hover {
top: 0;
transition: top 200ms;
}
& .viewer-bottom {
width: 100%;
position: fixed;
bottom: -48px;
left: 0;
transition: bottom 400ms ease 300ms;
z-index: 2;
&::after {
content: " ";
position: absolute;
width: 100%;
height: 1rem;
left: 0;
bottom: 0px;
}
}
& .viewer-bottom:hover {
bottom: 0px;
transition: bottom 200ms;
}
& .viewer-content {
grid-row: 1 / span 2;
}
}
.viewer-overlay {
position: absolute;
}
.viewer-overlay-background {
position: absolute;
top: 0;
left: 0;
&.visible {
background-color: rgb(0, 0, 0, 0.2);
}
}

View File

@ -1,77 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
.activity-bar {
background-color: $color-gray-50;
bottom: 0;
height: 100%;
position: fixed;
right: 0;
width: 250px;
.activity-bar-inside {
align-items: center;
display: flex;
flex-direction: column;
overflow-y: auto;
padding-top: 70px;
}
h4 {
color: $color-gray-40;
font-size: $fs16;
font-weight: $fw700;
margin-bottom: $size-1;
}
.date-ribbon {
background-color: lighten($color-gray-20, 12%);
color: $color-white;
font-size: $fs12;
font-weight: $fw700;
padding: 2px;
text-align: center;
width: 100%;
}
.activity-input {
border-bottom: 1px solid $color-gray-10;
display: flex;
font-size: $fs12;
padding: $size-2;
width: 100%;
img.activity-author {
border-radius: 50%;
flex-shrink: 0;
height: 30px;
margin-right: $size-4;
width: 30px;
}
.activity-content {
display: flex;
flex-direction: column;
.activity-project {
align-items: center;
display: flex;
flex-wrap: wrap;
a {
font-weight: $fw700;
margin: 0 3px;
}
}
.activity-time {
color: $color-gray-20;
font-size: $fs12;
font-style: italic;
}
}
}
}

View File

@ -1,104 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
.context-menu {
position: relative;
visibility: hidden;
opacity: 0;
z-index: 3;
}
.context-menu.is-open {
position: relative;
display: block;
opacity: 1;
visibility: visible;
}
.context-menu.fixed {
position: fixed;
}
.context-menu-items {
background: $color-white;
border-radius: $br3;
box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25);
left: -$size-4;
max-height: 30rem;
min-width: 7rem;
overflow: auto;
position: absolute;
top: $size-3;
& .separator {
border-top: 1px solid $color-gray-10;
padding: 0px;
margin: 2px;
}
&.min-width {
min-width: 13rem;
}
}
.context-menu-action {
color: $color-black;
display: block;
font-size: $fs14;
font-weight: $fw400;
padding: $size-2 $size-4;
text-align: left;
white-space: nowrap;
&:hover {
color: $color-black;
background-color: $color-primary-lighter;
text-decoration: none;
}
&.submenu {
display: flex;
align-items: center;
justify-content: space-between;
& span {
margin-left: 0.5rem;
}
& svg {
height: 10px;
width: 10px;
}
}
&.submenu-back {
color: $color-black;
display: flex;
font-weight: $fw700;
align-items: center;
& svg {
height: 10px;
width: 10px;
transform: rotate(180deg);
margin-right: $size-2;
}
}
}
.context-menu.is-selectable {
& .context-menu-action {
padding-left: 1.5rem;
}
& .context-menu-item.is-selected .context-menu-action {
background-image: url(/images/icons/tick.svg);
background-repeat: no-repeat;
background-position: 5% 48%;
background-size: 10px;
font-weight: $fw700;
}
}

View File

@ -1,70 +0,0 @@
.dropdown {
position: absolute;
max-height: 30rem;
background-color: $color-white;
border-radius: $br2;
box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25);
z-index: 3;
hr {
margin: 0 !important;
border-color: $color-gray-10;
}
> li {
display: flex;
align-items: center;
color: $color-gray-60;
cursor: pointer;
font-size: $fs14;
height: 40px;
padding: 5px 16px;
&.warning {
color: $color-danger;
}
svg {
fill: $color-gray-20;
height: 12px;
width: 12px;
}
&.title {
font-weight: $fw600;
cursor: default;
}
&:hover {
background-color: $color-primary-lighter;
}
&:focus {
border: 1px black solid;
}
}
&.with-check {
> li {
padding: 5px 10px;
}
> li:not(.selected) {
svg {
display: none;
}
}
svg {
fill: $color-gray-50;
}
.icon {
display: flex;
align-items: center;
width: 20px;
height: 20px;
margin-right: 7px;
}
}
}

View File

@ -1,30 +0,0 @@
.editable-label {
display: flex;
&.is-hidden {
display: none;
}
}
.editable-label-input {
border: 0;
height: 30px;
padding: 5px;
margin: 0;
width: 100%;
background-color: $color-white;
}
.editable-label-close {
background-color: $color-white;
cursor: pointer;
padding: 3px 5px;
& svg {
fill: $color-gray-30;
height: 15px;
transform: rotate(45deg) translateY(7px);
width: 15px;
margin: 0;
}
}

View File

@ -1,83 +0,0 @@
.exception-layout {
display: grid;
grid-template-rows: 120px auto;
grid-template-columns: 1fr;
}
.exception-header {
grid-column: 1 / span 1;
grid-row: 1 / span 1;
display: flex;
align-items: center;
padding: 32px;
z-index: 40;
cursor: pointer;
svg {
height: 55px;
width: 170px;
}
}
.exception-content {
grid-column: 1 / span 1;
grid-row: 1 / span 2;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
.container {
max-width: 600px;
}
.image {
align-items: center;
display: flex;
justify-content: center;
margin-bottom: 2rem;
svg {
height: 220px;
width: 220px;
}
}
.main-message {
color: $color-black;
font-size: $fs80;
line-height: $lh-188; // Original value was 150px; 150px/80px = 187.5 % => $lh-188 (rounded)
text-align: center;
}
.desc-message {
color: $color-black;
font-size: $fs26;
font-weight: $fw300;
text-align: center;
}
.sign-info {
margin-top: 20px;
color: $color-black;
font-size: $fs16;
font-weight: $fw200;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
b {
font-weight: $fw400;
}
.btn-primary {
margin-top: 15px;
}
}
}

View File

@ -1,390 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
input,
select,
textarea {
&.invalid {
border-color: $color-danger;
color: $color-danger;
}
}
.form-container,
.generic-form {
display: flex;
justify-content: center;
flex-direction: column;
.forms-container {
display: flex;
margin-top: 40px;
width: 536px;
justify-content: center;
}
form {
display: flex;
flex-direction: column;
// flex-basis: 368px;
}
.fields-row {
margin-bottom: 20px;
flex-direction: column;
.options {
display: flex;
justify-content: flex-end;
font-size: $fs14;
margin-top: 13px;
}
}
.field {
margin-bottom: 20px;
}
h1 {
font-size: $fs36;
color: #2c233e;
margin-bottom: 20px;
}
.subtitle {
font-size: $fs24;
color: #2c233e;
margin-bottom: 20px;
}
.notification-icon {
justify-content: center;
display: flex;
margin-bottom: 3rem;
svg {
fill: $color-gray-60;
height: 40%;
width: 40%;
}
}
.notification-text {
font-size: $fs18;
color: $color-gray-60;
margin-bottom: 20px;
}
.notification-text-email {
background: $color-gray-10;
border-radius: $br3;
color: $color-gray-60;
font-size: $fs18;
font-weight: $fw500;
margin: 1.5rem 0 2.5rem 0;
padding: 1rem;
text-align: center;
}
h2 {
font-size: $fs24;
color: $color-gray-60;
// height: 40px;
display: flex;
align-items: center;
}
a {
&:hover {
text-decoration: underline;
}
}
p {
color: $color-gray-60;
}
hr {
border-color: $color-gray-20;
}
}
.custom-input {
display: flex;
flex-direction: column;
position: relative;
input,
textarea {
background-color: $color-white;
border-radius: $br2;
border: 1px solid $color-gray-20;
color: $color-gray-60;
font-size: $fs14;
height: 40px;
margin: 0;
padding: 15px 15px 0 15px;
width: 100%;
}
textarea {
height: auto;
font-size: $fs14;
font-family: "worksans", sans-serif;
padding-top: 20px;
}
// Makes the background for autocomplete white
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
-webkit-box-shadow: 0 0 0 30px $color-white inset !important;
}
label {
font-size: $fs12;
color: $color-gray-50;
position: absolute;
left: 15px;
top: 6px;
}
&.invalid {
input {
border-color: $color-danger;
}
label {
color: $color-danger;
}
}
&.valid {
input {
border-color: $color-success;
}
}
&.focus {
input {
border-color: $color-gray-60;
}
}
&.disabled {
input {
background-color: lighten($color-gray-10, 5%);
user-select: none;
}
}
&.empty {
input {
padding-top: 0;
}
label {
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
width: 1px;
}
}
&.with-icon {
input {
padding-right: 50px;
}
}
.help-icon {
position: absolute;
right: 15px;
top: 12px;
display: flex;
justify-content: center;
align-items: center;
svg {
fill: $color-gray-30;
width: 15px;
height: 15px;
}
}
.hint {
color: $color-gray-40;
padding: 4px;
font-size: $fs12;
}
.error {
color: $color-danger;
padding: 4px;
font-size: $fs12;
}
}
.custom-multi-input {
border-radius: $br2;
border: 1px solid $color-gray-20;
max-height: 300px;
overflow-y: auto;
&.invalid {
label {
color: unset;
}
}
input {
border: 0px;
&.no-padding {
padding-top: 0px;
}
}
.selected-items {
padding-top: 25px;
padding-left: 15px;
display: flex;
flex-wrap: wrap;
}
.selected-item {
width: 100%;
&:not(:last-child) {
margin-right: 3px;
}
.around {
border: 1px solid $color-gray-20;
padding-left: 5px;
border-radius: $br4;
&.invalid {
border: 1px solid $color-danger;
}
&.caution {
border: 1px solid $color-warning;
}
.text {
display: inline-block;
max-width: 85%;
overflow: hidden;
text-overflow: ellipsis;
line-height: $lh-115; // Original value was 16px; 16px/14px = 114.285714286% => $lh-115 (rounded)
font-size: $fs14;
color: $color-black;
}
.icon {
cursor: pointer;
margin-left: 10px;
margin-right: 5px;
}
}
}
}
.custom-select {
display: flex;
flex-direction: column;
position: relative;
justify-content: center;
label {
font-size: $fs12;
color: $color-gray-30;
}
select {
cursor: pointer;
font-size: $fs14;
border: 0px;
opacity: 0;
z-index: 10;
padding: 0px;
margin: 0px;
background-color: transparent;
position: absolute;
width: calc(100% - 1px);
height: 100%;
padding: 15px;
}
.main-content {
flex-grow: 1;
display: flex;
flex-direction: column;
font-family: "worksans", sans-serif;
justify-content: center;
padding-top: 6px;
padding-bottom: 6px;
padding-left: 15px;
}
.input-container {
display: flex;
flex-direction: row;
background-color: $color-white;
border-radius: $br2;
border: 1px solid $color-gray-20;
height: 40px;
&.focus {
border-color: $color-gray-60;
}
&.invalid {
border-color: $color-danger;
label {
color: $color-danger;
}
}
&.valid {
border-color: $color-success;
}
&.focus {
border-color: $color-gray-60;
}
&.disabled {
background-color: $color-gray-10;
user-select: none;
}
}
.value {
color: $color-gray-60;
font-size: $fs14;
width: 100%;
border: 0px;
padding: 0px;
margin: 0px;
}
.icon {
display: flex;
justify-content: center;
align-items: center;
padding-left: 10px;
padding-right: 10px;
pointer-events: none;
svg {
fill: $color-gray-30;
transform: rotate(90deg);
width: 15px;
height: 15px;
}
}
}

View File

@ -1,94 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
.project-bar {
background-color: $color-gray-50;
border-right: 1px solid $color-gray-10;
bottom: 0;
height: 100%;
left: 50px;
position: fixed;
width: 200px;
z-index: 9;
&.toggle {
left: -201px;
}
.project-bar-inside {
align-items: center;
display: flex;
flex-direction: column;
overflow-y: auto;
padding-top: 60px;
.project-name {
border-bottom: 1px solid $color-gray-10;
font-size: $fs14;
font-weight: $fw700;
padding: 0 $size-2;
width: 100%;
}
.btn-primary,
.btn-warning,
.btn-danger {
font-size: $fs12;
margin-bottom: 0.5rem;
padding: 8px $size-2;
width: 90%;
}
}
}
.tree-view {
width: 100%;
li {
align-items: center;
cursor: pointer;
display: flex;
padding: $size-1 $size-2;
position: relative;
svg {
fill: $color-gray-20;
height: 12px;
margin-right: $size-1;
width: 12px;
}
span {
font-size: $fs12;
}
&:hover,
&.current {
span {
color: $color-primary;
}
}
.options {
align-items: center;
position: absolute;
display: flex;
right: 0;
top: 40%;
svg {
fill: $color-gray-20;
height: 12px;
margin-right: $size-2;
width: 12px;
&:hover {
fill: $color-gray-40;
}
}
}
}
}

View File

@ -1,575 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
.settings-bar {
background-color: $color-gray-50;
border-left: 1px solid $color-gray-60;
position: relative;
&.settings-bar-left {
border-left: none;
border-right: 1px solid $color-gray-60;
& .tab-container-tabs {
padding-left: 1.5rem;
}
}
.settings-bar-inside {
display: grid;
grid-template-columns: 100%;
grid-template-rows: 100%;
height: calc(100% - 2px);
.tool-window {
position: relative;
border-bottom: 1px solid $color-gray-60;
display: flex;
flex-direction: column;
flex: 1;
width: 100%;
height: 100%;
.tool-window-bar {
align-items: center;
display: flex;
flex-shrink: 0;
padding: $size-2;
overflow: hidden;
margin: 0;
svg {
fill: $color-gray-20;
height: 12px;
width: 12px;
}
button,
div {
border: none;
background-color: transparent;
color: $color-gray-10;
font-size: $fs14;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
span.pages-title {
color: #e3e3e3;
font-size: 0.875rem;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
span.tool-badge {
border: 1px solid $color-primary;
border-radius: $br2;
font-size: $fs10;
color: $color-primary;
padding: 2px 4px;
margin-left: auto;
}
span.tool-link,
span.shared-library {
margin-left: auto;
padding-left: 17px;
display: flex;
svg {
fill: $color-gray-30;
height: 20px;
width: 20px;
}
}
span.tool-link:hover svg {
fill: $color-primary;
}
span.library-title {
color: $color-gray-10;
font-size: $fs14;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
&:first-letter {
text-transform: uppercase;
}
}
.tool-window-bar-icon {
height: 21px;
display: flex;
align-items: center;
justify-content: center;
svg {
width: 15px;
height: 15px;
}
}
&.big {
height: 3rem;
padding-bottom: 1rem;
}
.tool-window-bar-title {
font-size: $fs14;
margin-left: 0.5rem;
}
.tool-window-icon {
margin-right: $size-2;
display: none;
}
.tool-window-close {
cursor: pointer;
margin-left: auto;
transform: rotate(45deg);
&:hover {
svg {
fill: $color-danger;
}
}
}
& .view-only-mode {
color: $color-primary;
border: 1px solid $color-primary;
border-radius: $br3;
font-size: $fs10;
text-transform: uppercase;
display: flex;
align-items: center;
justify-content: center;
margin-left: auto;
padding: 0.25rem;
}
}
}
}
.empty {
color: $color-gray-20;
font-size: $fs12;
line-height: $lh-150;
text-align: center;
padding: 0 15px;
display: flex;
flex-direction: column;
gap: 20px;
margin-top: 12px;
.tool-window-bar-icon {
height: 32px;
display: flex;
align-items: center;
justify-content: center;
margin-top: 16px;
}
svg {
width: 32px;
height: 32px;
fill: $color-gray-30;
}
.btn-primary {
margin-top: 10px;
background-color: $color-gray-60;
color: $color-gray-10;
&:hover {
background-color: $color-primary;
color: $color-black;
}
}
}
& > .resize-area {
position: absolute;
width: 8px;
height: 100%;
z-index: 10;
cursor: ew-resize;
}
&.settings-bar-left > .resize-area {
right: -8px;
}
&.settings-bar-right > .resize-area {
left: -4px;
}
}
.tool-window-content {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
overflow-y: auto;
overflow-x: hidden;
&.inspect {
.tab-container-tabs {
padding-bottom: 0.5rem;
background-color: $color-gray-50;
border-bottom: 1px solid $color-gray-60;
height: 3rem;
}
.tab-container-tab-title {
border-radius: $br4;
&.current {
background-color: $color-primary;
color: black;
}
}
}
}
.element-list {
margin: 0;
width: 100%;
ul {
border-left: 9px solid $color-gray-50;
margin: 0 0 0 0.4rem;
li {
border-left: 1px solid $color-gray-40;
}
}
li {
cursor: pointer;
display: flex;
flex-direction: column;
width: 100%;
padding-top: 1px;
padding-bottom: 1px;
&.open {
ul {
li {
.element-list-body {
border-style: dashed;
}
}
}
}
}
}
.element-list.pages-list {
max-height: 10rem;
.context-menu {
position: fixed;
}
.context-menu-items {
border: none;
margin: none;
}
.context-menu-action {
width: 100%;
}
}
button.collapse-sidebar {
background: none;
border: none;
cursor: pointer;
height: 2.5rem;
padding-top: 0.75rem;
position: absolute;
width: 1rem;
& svg {
width: 12px;
height: 12px;
fill: $color-gray-20;
transform: rotate(180deg);
}
&.collapsed {
background: $color-gray-60;
left: 48px;
top: 48px;
width: 28px;
height: 48px;
padding: 0;
border-radius: 0 $br4 $br4 0;
border-left: 1px solid $color-gray-50;
& svg {
transform: rotate(0deg);
}
}
}
.layers-tab {
display: grid;
grid-template-rows: auto 1fr;
grid-template-columns: 100%;
height: 100%;
overflow: hidden;
position: relative;
.resize-area-horiz {
position: absolute;
top: var(--height, 200px);
left: 0;
height: 8px;
width: 100%;
z-index: 10;
cursor: ns-resize;
}
}
.shortcuts,
.debug-panel {
.shortcuts-header,
.debug-panel-header {
display: flex;
height: 40px;
background-color: $color-gray-60;
.shortcuts-title,
.debug-panel-title {
color: $color-white;
font-size: $fs12;
display: flex;
justify-content: center;
align-items: center;
flex-grow: 1;
svg {
height: 18px;
width: 18px;
transform: rotate(45deg);
fill: $color-gray-20;
}
}
.shortcuts-close-button,
.debug-panel-close-button {
display: flex;
justify-content: center;
background-color: transparent;
border: none;
cursor: pointer;
padding: 2px 0 2px 15px;
position: absolute;
top: 8px;
svg {
height: 18px;
width: 18px;
transform: rotate(45deg);
fill: $color-gray-20;
}
}
}
.search-field {
height: 60px;
display: flex;
justify-content: center;
align-items: center;
padding: 12px 10px;
.search-box {
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid $color-gray-30;
border-radius: $br2;
width: 100%;
&:focus-within {
border: 1px solid $color-primary;
}
.input-text {
margin: 0;
background: $color-gray-50;
width: 100%;
color: $color-white;
&:focus {
border-bottom: none;
}
}
.icon-wrapper {
display: flex;
justify-content: center;
align-items: center;
border: none;
background-color: transparent;
padding: 0;
.icon {
display: flex;
justify-content: center;
align-items: center;
&.close {
transform: rotate(45deg);
}
}
}
svg {
width: 16px;
height: 16px;
margin: 0 7px;
cursor: pointer;
fill: $color-gray-20;
}
}
}
.shortcut-list {
border-top: 1px solid $color-gray-60;
padding: 10px;
overflow-y: auto;
height: 90%;
margin-bottom: 15px;
.section-title {
background-color: $color-gray-60;
padding: 4px 0;
}
.section-title,
.subsection-title {
display: flex;
cursor: pointer;
margin-top: 4px;
font-size: $fs12;
.section-name {
color: $color-white;
}
.collapesed-shortcuts {
padding: 0 10px;
svg {
height: 8px;
width: 8px;
fill: $color-gray-20;
}
&.open {
svg {
transform: rotate(90deg);
}
}
}
.shortcut-count {
padding-left: 5px;
color: $color-white;
}
}
.subsection-title {
padding: 4px 0px;
.subsection-name {
color: $color-white;
}
}
.section-title,
.subsection-title {
&:hover {
background-color: $color-primary;
.subsection-name,
.section-name {
color: $color-gray-60;
}
svg {
fill: $color-gray-60;
}
}
}
.shortcut-name {
border: 1px solid $color-gray-60;
border-radius: $br4;
padding: 7px;
display: flex;
justify-content: space-between;
margin-top: 4px;
color: $color-white;
font-size: $fs12;
.command-name {
display: flex;
align-items: center;
}
.keys {
flex-grow: 1;
display: flex;
align-items: center;
justify-content: flex-end;
}
.char-box {
min-width: 15px;
background-color: $color-white;
color: $color-black;
border-radius: $br3;
padding: 2px 5px;
font-size: $fs11;
font-weight: $fw600;
margin: 0 2px;
text-transform: capitalize;
display: inline-block;
text-align: center;
}
.space {
margin: 0 3px;
}
}
}
.not-found {
background-color: $color-gray-60;
padding: 4px 0;
color: $color-white;
display: flex;
justify-content: center;
margin-top: 4px;
font-size: $fs12;
}
}
.debug-panel {
.debug-panel-inner {
padding: 8px;
}
.debug-option {
display: flex;
gap: 8px;
margin: 4px 0;
cursor: pointer;
label {
font-size: 80%;
cursor: pointer;
}
svg {
width: 15px;
height: 15px;
background: white;
}
&:hover {
svg {
stroke: $color-primary;
}
label {
color: $color-primary;
}
}
}
}

View File

@ -1,190 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
.signup-questions {
background-color: $color-white;
color: $color-gray-60;
max-width: 646px;
overflow-y: auto;
padding: 1.5rem 1rem;
position: relative;
width: 100%;
form {
display: flex;
flex-direction: column;
height: 100%;
}
h1,
h3 {
font-family: "worksans", sans-serif;
font-weight: $fw500;
}
h1 {
font-size: $fs36;
padding-top: 2.5rem;
}
h3 {
font-size: $fs23;
}
.step-header {
height: 2.5rem;
width: 100%;
}
.custom-select {
margin-bottom: 10px;
}
.step-number {
background-color: $color-gray-10;
border: none;
border-radius: 1rem; // Need to be investigated, before we can use variable
color: $color-gray-40;
float: right;
font-family: "worksans", sans-serif;
font-size: $fs12;
height: 1.5rem;
line-height: $lh-200; // Original value was 1.5rem = 24px; 24px/12px = 200% => lh-200
text-align: center;
width: 2.5rem;
}
.header-image {
width: 240px;
}
.intro {
font-size: $fs16;
padding: 0.5rem 0 1rem 0;
color: $color-gray-40;
}
.section {
display: block;
font-weight: $fw500;
font-size: $fs18;
margin: 0 0 0.3em 0;
padding: 0.8rem 0 0 0;
font-family: "worksans", sans-serif !important;
}
.other {
.custom-input {
margin: 0.75rem 0 2rem 0;
}
}
.buttons {
flex-grow: 1;
display: grid;
grid-template-columns: 50% 50%;
grid-template-areas: "previous next";
.step-prev {
display: flex;
align-items: flex-end;
justify-content: flex-start;
grid-area: previous;
button {
background-color: transparent;
border: none;
cursor: pointer;
height: 40px;
font-size: $fs15;
}
}
.step-next {
display: flex;
align-items: flex-end;
justify-content: flex-end;
grid-area: next;
input {
font-size: $fs15;
color: $color-black;
background-color: $color-primary;
width: 11rem;
margin-left: auto;
margin: 0;
}
}
}
.custom-radio {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
.input-radio {
margin: 0;
max-width: 12rem;
width: 100%;
&.with-image {
display: block;
padding: 0;
}
label {
font-family: "worksans", sans-serif !important;
color: $color-gray-60;
font-size: $fs15;
padding-left: 0;
position: relative;
text-align: center;
height: 4rem;
margin: 0;
&.with-image {
min-height: 120px;
display: flex;
padding-top: 4rem;
justify-content: center;
background-size: 50px;
background-repeat: no-repeat;
background-position: center 0.75rem;
}
}
input[type="radio"] {
/*We need it to be accesible so we can't use display none*/
display: inline;
opacity: 0;
}
input[type="radio"] + label:before {
background-color: $color-white;
border: 1px solid $color-gray-10;
}
input[type="radio"] + label.with-image:before {
background-color: transparent;
border-radius: 4px;
min-width: 100%;
min-height: 100%;
position: absolute;
top: 0;
left: 0;
margin: 0;
}
input[type="radio"]:focus + label:before {
border: 1px solid $color-gray-60;
}
input[type="radio"]:checked + label:before {
box-shadow: inset 0 0 0 4px $color-white;
background-color: $color-primary;
border: 1px solid $color-gray-30;
}
input[type="radio"]:checked + label.with-image:before {
border: 1px solid $color-primary;
background-color: transparent;
}
}
}
}

View File

@ -1,45 +0,0 @@
.tab-container {
display: grid;
grid-template-rows: auto 1fr;
grid-template-columns: 100%;
height: 100%;
}
.tab-container-tabs {
background: $color-gray-60;
cursor: pointer;
display: flex;
flex-direction: row;
font-size: $fs12;
height: 2.5rem;
padding: 0 0.25rem;
}
.tab-container-tab-title {
align-items: center;
background: $color-gray-60;
border-radius: $br2 $br2 0 0;
color: $color-white;
display: flex;
justify-content: center;
margin: 0.5rem 0.25rem 0 0.25rem;
width: 100%;
&.current {
background: $color-gray-50;
}
}
.tab-container-content {
overflow-y: auto;
overflow-x: hidden;
}
.inspect .tab-container-content {
overflow: hidden;
}
.tab-element,
.tab-element-content {
height: 100%;
}

View File

@ -1,188 +0,0 @@
.settings-content {
header {
display: flex;
flex-direction: column;
height: 160px;
background-color: $color-white;
.secondary-menu {
display: flex;
justify-content: space-between;
height: 40px;
font-size: $fs14;
color: $color-gray-60;
.icon {
display: flex;
align-items: center;
}
.left {
margin-left: 30px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
.label {
margin-left: 15px;
}
svg {
fill: $color-gray-60;
width: 14px;
height: 14px;
transform: rotate(180deg);
}
}
.right {
align-items: center;
cursor: pointer;
display: flex;
justify-content: center;
margin-right: 30px;
.label {
color: $color-primary-dark;
margin-right: 15px;
}
svg {
fill: $color-primary-dark;
width: 14px;
height: 14px;
}
&:hover {
.label {
color: $color-danger;
}
svg {
fill: $color-danger;
}
}
}
}
h1 {
align-items: top;
color: $color-gray-60;
display: flex;
flex-grow: 1;
font-size: $fs24;
font-weight: $fw400;
justify-content: center;
}
nav {
display: flex;
justify-content: center;
height: 40px;
.nav-item {
align-items: center;
color: $color-gray-40;
display: flex;
flex-basis: 140px;
justify-content: center;
&.current {
border-bottom: 3px solid $color-primary;
}
}
}
}
.settings-profile {
.forms-container {
margin-top: 80px;
}
}
.avatar-form {
flex-basis: 168px;
height: 100vh;
display: flex;
position: relative;
.image-change-field {
position: relative;
width: 120px;
height: 120px;
.update-overlay {
opacity: 0;
cursor: pointer;
position: absolute;
width: 121px;
height: 121px;
border-radius: 50%;
font-size: $fs24;
color: $color-white;
line-height: $lh-500; // Original value was 120px; 120px/24px =500% => $lh-500
text-align: center;
background: $color-primary-dark;
z-index: 14;
}
input[type="file"] {
width: 120px;
height: 120px;
position: absolute;
opacity: 0;
cursor: pointer;
top: 0;
z-index: 15;
}
&:hover {
img {
display: none;
}
.update-overlay {
opacity: 1;
}
}
}
}
.profile-form {
flex-grow: 1;
flex-basis: 390px;
display: flex;
flex-direction: column;
.change-email {
display: flex;
flex-direction: row;
font-size: $fs14;
color: $color-primary-dark;
justify-content: flex-end;
margin-bottom: 20px;
}
}
.avatar-form {
img {
border-radius: 50%;
flex-shrink: 0;
height: 120px;
margin-right: $size-4;
width: 120px;
}
}
.options-form,
.password-form {
display: flex;
flex-direction: column;
flex-basis: 368px;
h2 {
font-size: $fs14;
font-weight: $fw400;
margin-bottom: $size-4;
}
}
}

View File

@ -25,6 +25,7 @@
[app.common.types.component :as ctk] [app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl] [app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn] [app.common.types.container :as ctn]
[app.common.types.file :as ctf]
[app.common.types.shape :as cts] [app.common.types.shape :as cts]
[app.common.types.shape-tree :as ctst] [app.common.types.shape-tree :as ctst]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
@ -1598,6 +1599,34 @@
(let [frame (get objects parent-frame-id)] (let [frame (get objects parent-frame-id)]
(gsh/translate-to-frame shape frame)))) (gsh/translate-to-frame shape frame))))
;; When copying an instance that is nested inside another one, we need to
;; advance the shape refs to one or more levels of remote mains.
(advance-copies [state selected data]
(let [file (wsh/get-local-file-full state)
libraries (wsh/get-libraries state)
page (wsh/lookup-page state)
heads (mapcat #(ctn/get-child-heads (:objects data) %) selected)]
(update data :objects
#(reduce (partial advance-copy file libraries page)
%
heads))))
(advance-copy [file libraries page objects shape]
(if (and (ctk/instance-head? shape) (not (ctk/main-instance? shape)))
(let [level-delta (ctn/get-nesting-level-delta (:objects page) shape uuid/zero)]
(if (pos? level-delta)
(reduce (partial advance-shape file libraries page level-delta)
objects
(cfh/get-children-with-self objects (:id shape)))
objects))
objects))
(advance-shape [file libraries page level-delta objects shape]
(let [new-shape-ref (ctf/advance-shape-ref file page libraries shape level-delta {:include-deleted? true})]
(cond-> objects
(and (some? new-shape-ref) (not= new-shape-ref (:shape-ref shape)))
(assoc-in [(:id shape) :shape-ref] new-shape-ref))))
(on-copy-error [error] (on-copy-error [error]
(js/console.error "clipboard blocked:" error) (js/console.error "clipboard blocked:" error)
(rx/empty))] (rx/empty))]
@ -1636,6 +1665,7 @@
(rx/merge-map (partial prepare-object objects frame-id)) (rx/merge-map (partial prepare-object objects frame-id))
(rx/reduce collect-data initial) (rx/reduce collect-data initial)
(rx/map (partial sort-selected state)) (rx/map (partial sort-selected state))
(rx/map (partial advance-copies state selected))
(rx/map #(t/encode-str % {:type :json-verbose})) (rx/map #(t/encode-str % {:type :json-verbose}))
(rx/map wapi/write-to-clipboard) (rx/map wapi/write-to-clipboard)
(rx/catch on-copy-error) (rx/catch on-copy-error)

View File

@ -168,13 +168,6 @@
;; Toolbar ;; Toolbar
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn toggle-toolbar-visibility
[]
(ptk/reify ::toggle-toolbar-visibility
ptk/UpdateEvent
(update [_ state]
(update-in state [:workspace-local :hide-toolbar] not))))
(defn hide-toolbar (defn hide-toolbar
[] []
(ptk/reify ::hide-toolbar (ptk/reify ::hide-toolbar
@ -188,3 +181,10 @@
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(assoc-in state [:workspace-local :hide-toolbar] false)))) (assoc-in state [:workspace-local :hide-toolbar] false))))
(defn toggle-toolbar-visibility
[]
(ptk/reify ::toggle-toolbar-visibility
ptk/UpdateEvent
(update [_ state]
(update-in state [:workspace-local :hide-toolbar] not))))

View File

@ -89,7 +89,6 @@
(update [_ state] (update [_ state]
(update state :workspace-layout (update state :workspace-layout
(fn [flags] (fn [flags]
(prn flags)
(if force? (if force?
(conj flags flag) (conj flags flag)
(if (contains? flags flag) (if (contains? flags flag)

View File

@ -706,6 +706,38 @@
(rx/take-until stopper-s)))))) (rx/take-until stopper-s))))))
(defn sync-head
[id]
(ptk/reify ::sync-head
ptk/WatchEvent
(watch [it state _]
(log/info :msg "SYNC-head of shape" :id (str id))
(let [file (wsh/get-local-file state)
file-full (wsh/get-local-file-full state)
libraries (wsh/get-libraries state)
page-id (:current-page-id state)
container (cfh/get-container file :page page-id)
objects (:objects container)
shape-inst (ctn/get-shape container id)
parent (get objects (:parent-id shape-inst))
head (ctn/get-component-shape container parent)
components-v2
(features/active-feature? state "components/v2")
changes
(-> (pcb/empty-changes it)
(pcb/with-container container)
(pcb/with-objects (:objects container))
(dwlh/generate-sync-shape-direct file-full libraries container (:id head) false components-v2))]
(log/debug :msg "SYNC-head finished" :js/rchanges (log-changes
(:redo-changes changes)
file))
(rx/of (dch/commit-changes changes))))))
(defn reset-component (defn reset-component
"Cancels all modifications in the shape with the given id, and all its children, in "Cancels all modifications in the shape with the given id, and all its children, in
the current page. Set all attributes equal to the ones in the linked component, the current page. Set all attributes equal to the ones in the linked component,
@ -726,23 +758,26 @@
components-v2 components-v2
(features/active-feature? state "components/v2") (features/active-feature? state "components/v2")
shape-inst (ctn/get-shape container id)
swap-slot (-> (ctn/get-shape container id) swap-slot (-> (ctn/get-shape container id)
(ctk/get-swap-slot)) (ctk/get-swap-slot))
undo-id (js/Symbol)
changes changes
(-> (pcb/empty-changes it) (-> (pcb/empty-changes it)
(pcb/with-container container) (pcb/with-container container)
(pcb/with-objects (:objects container)) (pcb/with-objects (:objects container))
(dwlh/generate-sync-shape-direct file-full libraries container id true components-v2) (dwlh/generate-sync-shape-direct file-full libraries container id true components-v2))]
(cond->
(some? swap-slot)
;; We need to propagate parent changes
(dwlh/generate-sync-shape-direct file-full libraries container (:parent-id shape-inst) true components-v2)))]
(log/debug :msg "RESET-COMPONENT finished" :js/rchanges (log-changes (log/debug :msg "RESET-COMPONENT finished" :js/rchanges (log-changes
(:redo-changes changes) (:redo-changes changes)
file)) file))
(rx/of (dch/commit-changes changes)))))) (rx/of
(dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(when (some? swap-slot)
(sync-head id))
(dwu/commit-undo-transaction undo-id))))))
(defn reset-components (defn reset-components
"Cancels all modifications in the shapes with the given ids" "Cancels all modifications in the shapes with the given ids"
@ -1186,6 +1221,26 @@
:callback do-update}] :callback do-update}]
:tag :sync-dialog))))))) :tag :sync-dialog)))))))
(defn touch-component
"Update the modified-at attribute of the component to now"
[id]
(dm/verify! (uuid? id))
(ptk/reify ::touch-component
cljs.core/IDeref
(-deref [_] [id])
ptk/WatchEvent
(watch [it state _]
(let [data (get state :workspace-data)
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/update-component id #(assoc % :modified-at (dt/now))))]
(rx/of (dch/commit-changes {:origin it
:redo-changes (:redo-changes changes)
:undo-changes []
:save-undo? false}))))))
(defn component-changed (defn component-changed
"Notify that the component with the given id has changed, so it needs to be updated "Notify that the component with the given id has changed, so it needs to be updated
in the current file and in the copies. And also update its thumbnails." in the current file and in the copies. And also update its thumbnails."
@ -1197,6 +1252,7 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ _ _]
(rx/of (rx/of
(touch-component component-id)
(launch-component-sync component-id file-id undo-group))))) (launch-component-sync component-id file-id undo-group)))))
(defn watch-component-changes (defn watch-component-changes
@ -1244,13 +1300,18 @@
(map (partial ch/components-changed old-data)) (map (partial ch/components-changed old-data))
(reduce into #{})))] (reduce into #{})))]
(if (and (d/not-empty? changed-components) save-undo?) (if (d/not-empty? changed-components)
(do (log/info :msg "DETECTED COMPONENTS CHANGED" (if save-undo?
:ids (map str changed-components) (do (log/info :msg "DETECTED COMPONENTS CHANGED"
:undo-group undo-group) :ids (map str changed-components)
:undo-group undo-group)
(->> (rx/from changed-components) (->> (rx/from changed-components)
(rx/map #(component-changed % (:id old-data) undo-group)))) (rx/map #(component-changed % (:id old-data) undo-group))))
;; even if save-undo? is false, we need to update the :modified-date of the component
;; (for example, for undos)
(->> (rx/from changed-components)
(rx/map #(touch-component %))))
(rx/empty))))) (rx/empty)))))
changes-s changes-s

View File

@ -759,7 +759,8 @@
root-inst root-inst
root-main root-main
omit-touched? omit-touched?
set-remote-synced?) set-remote-synced?
components-v2)
changes)) changes))
both (fn [changes child-inst child-main] both (fn [changes child-inst child-main]
@ -813,7 +814,8 @@
swapped swapped
moved moved
false false
reset?)))) reset?
components-v2))))
(defn- generate-rename-component (defn- generate-rename-component
@ -948,7 +950,8 @@
component-container component-container
container container
root-inst root-inst
root-main)) root-main
components-v2))
only-main (fn [changes child-main] only-main (fn [changes child-main]
(remove-shape changes (remove-shape changes
@ -1001,7 +1004,8 @@
swapped swapped
moved moved
true true
true) true
components-v2)
;; The inverse sync may be made on a component that is inside a ;; The inverse sync may be made on a component that is inside a
;; remote library. We need to separate changes that are from ;; remote library. We need to separate changes that are from
@ -1019,7 +1023,7 @@
;; ---- Operation generation helpers ---- ;; ---- Operation generation helpers ----
(defn- compare-children (defn- compare-children
[changes children-inst children-main container-inst container-main file libraries only-inst-cb only-main-cb both-cb swapped-cb moved-cb inverse? reset?] [changes children-inst children-main container-inst container-main file libraries only-inst-cb only-main-cb both-cb swapped-cb moved-cb inverse? reset? components-v2]
(log/trace :msg "Compare children") (log/trace :msg "Compare children")
(loop [children-inst (seq (or children-inst [])) (loop [children-inst (seq (or children-inst []))
children-main (seq (or children-main [])) children-main (seq (or children-main []))
@ -1039,18 +1043,18 @@
(reduce only-inst-cb changes children-inst) (reduce only-inst-cb changes children-inst)
:else :else
(if (or (ctk/is-main-of? child-main child-inst) (if (or (ctk/is-main-of? child-main child-inst components-v2)
(and (ctf/match-swap-slot? child-main child-inst container-inst container-main file libraries) (not reset?))) (and (ctf/match-swap-slot? child-main child-inst container-inst container-main file libraries) (not reset?)))
(recur (next children-inst) (recur (next children-inst)
(next children-main) (next children-main)
(if (ctk/is-main-of? child-main child-inst) (if (ctk/is-main-of? child-main child-inst components-v2)
(both-cb changes child-inst child-main) (both-cb changes child-inst child-main)
(swapped-cb changes child-inst child-main))) (swapped-cb changes child-inst child-main)))
(let [child-inst' (d/seek #(or (ctk/is-main-of? child-main %) (let [child-inst' (d/seek #(or (ctk/is-main-of? child-main % components-v2)
(and (ctf/match-swap-slot? child-main % container-inst container-main file libraries) (not reset?))) (and (ctf/match-swap-slot? child-main % container-inst container-main file libraries) (not reset?)))
children-inst) children-inst)
child-main' (d/seek #(or (ctk/is-main-of? % child-inst) child-main' (d/seek #(or (ctk/is-main-of? % child-inst components-v2)
(and (ctf/match-swap-slot? % child-inst container-inst container-main file libraries) (not reset?))) (and (ctf/match-swap-slot? % child-inst container-inst container-main file libraries) (not reset?)))
children-main)] children-main)]
(cond (cond
@ -1066,7 +1070,7 @@
:else :else
(if inverse? (if inverse?
(let [is-main? (ctk/is-main-of? child-inst child-main')] (let [is-main? (ctk/is-main-of? child-inst child-main' components-v2)]
(recur (next children-inst) (recur (next children-inst)
(remove #(= (:id %) (:id child-main')) children-main) (remove #(= (:id %) (:id child-main')) children-main)
(cond-> changes (cond-> changes
@ -1076,7 +1080,7 @@
(swapped-cb child-inst child-main') (swapped-cb child-inst child-main')
:always :always
(moved-cb child-inst child-main')))) (moved-cb child-inst child-main'))))
(let [is-main? (ctk/is-main-of? child-inst' child-main)] (let [is-main? (ctk/is-main-of? child-inst' child-main components-v2)]
(recur (remove #(= (:id %) (:id child-inst')) children-inst) (recur (remove #(= (:id %) (:id child-inst')) children-inst)
(next children-main) (next children-main)
(cond-> changes (cond-> changes
@ -1088,13 +1092,13 @@
(moved-cb child-inst' child-main)))))))))))) (moved-cb child-inst' child-main))))))))))))
(defn- add-shape-to-instance (defn- add-shape-to-instance
[changes component-shape index component-page container root-instance root-main omit-touched? set-remote-synced?] [changes component-shape index component-page container root-instance root-main omit-touched? set-remote-synced? components-v2]
(log/info :msg (str "ADD [P " (pretty-uuid (:id container)) "] " (log/info :msg (str "ADD [P " (pretty-uuid (:id container)) "] "
(:name component-shape) (:name component-shape)
" " " "
(pretty-uuid (:id component-shape)))) (pretty-uuid (:id component-shape))))
(let [component-parent-shape (ctn/get-shape component-page (:parent-id component-shape)) (let [component-parent-shape (ctn/get-shape component-page (:parent-id component-shape))
parent-shape (d/seek #(ctk/is-main-of? component-parent-shape %) parent-shape (d/seek #(ctk/is-main-of? component-parent-shape % components-v2)
(cfh/get-children-with-self (:objects container) (cfh/get-children-with-self (:objects container)
(:id root-instance))) (:id root-instance)))
all-parents (into [(:id parent-shape)] all-parents (into [(:id parent-shape)]
@ -1163,13 +1167,13 @@
changes'))) changes')))
(defn- add-shape-to-main (defn- add-shape-to-main
[changes shape index component component-container page root-instance root-main] [changes shape index component component-container page root-instance root-main components-v2]
(log/info :msg (str "ADD [C " (pretty-uuid (:id component-container)) "] " (log/info :msg (str "ADD [C " (pretty-uuid (:id component-container)) "] "
(:name shape) (:name shape)
" " " "
(pretty-uuid (:id shape)))) (pretty-uuid (:id shape))))
(let [parent-shape (ctn/get-shape page (:parent-id shape)) (let [parent-shape (ctn/get-shape page (:parent-id shape))
component-parent-shape (d/seek #(ctk/is-main-of? % parent-shape) component-parent-shape (d/seek #(ctk/is-main-of? % parent-shape components-v2)
(cfh/get-children-with-self (:objects component-container) (cfh/get-children-with-self (:objects component-container)
(:id root-main))) (:id root-main)))
all-parents (into [(:id component-parent-shape)] all-parents (into [(:id component-parent-shape)]

View File

@ -546,10 +546,7 @@
:layout-padding-type :layout-padding-type
:layout-gap :layout-gap
:layout-item-margin :layout-item-margin
:layout-item-margin-type :layout-item-margin-type]})
:layout-grid-cells
:layout-grid-columns
:layout-grid-rows]})
;; We've applied the text-modifier so we can dissoc the temporary data ;; We've applied the text-modifier so we can dissoc the temporary data
(fn [state] (fn [state]
(update state :workspace-text-modifier #(apply dissoc % ids))) (update state :workspace-text-modifier #(apply dissoc % ids)))

View File

@ -22,13 +22,9 @@
(defn esc-pressed [] (defn esc-pressed []
(ptk/reify ::esc-pressed (ptk/reify ::esc-pressed
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ _ _]
;; Not interrupt when we're editing a path ;; Not interrupt when we're editing a path
(let [edition-id (or (get-in state [:workspace-drawing :object :id]) (rx/of :interrupt))))
(get-in state [:workspace-local :edition]))
path-edit-mode (get-in state [:workspace-local :edit-path edition-id :edit-mode])]
(when-not (= :draw path-edit-mode)
(rx/of :interrupt))))))
(def shortcuts (def shortcuts
{:move-nodes {:tooltip "M" {:move-nodes {:tooltip "M"

View File

@ -404,6 +404,7 @@
ids-map ids-map
%2 %2
delta delta
nil
libraries libraries
library-data library-data
it it
@ -459,10 +460,10 @@
;; TODO: move to common.files.shape-helpers ;; TODO: move to common.files.shape-helpers
(defn- prepare-duplicate-shape-change (defn- prepare-duplicate-shape-change
([changes objects page unames update-unames! ids-map obj delta libraries library-data it file-id] ([changes objects page unames update-unames! ids-map obj delta level-delta libraries library-data it file-id]
(prepare-duplicate-shape-change changes objects page unames update-unames! ids-map obj delta libraries library-data it file-id (:frame-id obj) (:parent-id obj) false false)) (prepare-duplicate-shape-change changes objects page unames update-unames! ids-map obj delta level-delta libraries library-data it file-id (:frame-id obj) (:parent-id obj) false false))
([changes objects page unames update-unames! ids-map obj delta libraries library-data it file-id frame-id parent-id duplicating-component? child?] ([changes objects page unames update-unames! ids-map obj delta level-delta libraries library-data it file-id frame-id parent-id duplicating-component? child?]
(cond (cond
(nil? obj) (nil? obj)
changes changes
@ -486,11 +487,14 @@
duplicating-component? (or duplicating-component? (ctk/instance-head? obj)) duplicating-component? (or duplicating-component? (ctk/instance-head? obj))
is-component-main? (ctk/main-instance? obj) is-component-main? (ctk/main-instance? obj)
original-ref-shape (-> (ctf/find-original-ref-shape nil page libraries obj {:include-deleted? true})
:id)
into-component? (and duplicating-component? into-component? (and duplicating-component?
(ctn/in-any-component? objects parent)) (ctn/in-any-component? objects parent))
level-delta (if (some? level-delta)
level-delta
(ctn/get-nesting-level-delta objects obj parent))
new-shape-ref (ctf/advance-shape-ref nil page libraries obj level-delta {:include-deleted? true})
regenerate-component regenerate-component
(fn [changes shape] (fn [changes shape]
(let [components-v2 (dm/get-in library-data [:options :components-v2]) (let [components-v2 (dm/get-in library-data [:options :components-v2])
@ -518,9 +522,9 @@
(cond-> (or frame? group? bool?) (cond-> (or frame? group? bool?)
(assoc :shapes [])) (assoc :shapes []))
(cond-> (and (some? original-ref-shape) (cond-> (and (some? new-shape-ref)
(not= original-ref-shape (:shape-ref obj))) (not= new-shape-ref (:shape-ref obj)))
(assoc :shape-ref original-ref-shape)) (assoc :shape-ref new-shape-ref))
(gsh/move delta) (gsh/move delta)
(d/update-when :interactions #(ctsi/remap-interactions % ids-map objects)) (d/update-when :interactions #(ctsi/remap-interactions % ids-map objects))
@ -561,6 +565,7 @@
ids-map ids-map
child child
delta delta
level-delta
libraries libraries
library-data library-data
it it

View File

@ -353,13 +353,14 @@
(ptk/reify ::create-artboard-from-selection (ptk/reify ::create-artboard-from-selection
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
(let [page-id (:current-page-id state) (let [page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id) objects (wsh/lookup-page-objects state page-id)
selected (wsh/lookup-selected state) selected (->> (wsh/lookup-selected state)
selected (cfh/clean-loops objects selected) (cfh/clean-loops objects)
(remove #(ctn/has-any-copy-parent? objects (get objects %))))
changes (-> (pcb/empty-changes it page-id) changes (-> (pcb/empty-changes it page-id)
(pcb/with-objects objects)) (pcb/with-objects objects))
[frame-shape changes] [frame-shape changes]
(cfsh/prepare-create-artboard-from-selection changes (cfsh/prepare-create-artboard-from-selection changes

View File

@ -606,11 +606,11 @@
(->> move-stream (->> move-stream
(rx/last) (rx/last)
(rx/mapcat (rx/mapcat
(fn [[_ target-frame drop-index]] (fn [[_ target-frame drop-index cell-data]]
(let [undo-id (js/Symbol)] (let [undo-id (js/Symbol)]
(rx/of (dwu/start-undo-transaction undo-id) (rx/of (dwu/start-undo-transaction undo-id)
(move-shapes-to-frame ids target-frame drop-index)
(dwm/apply-modifiers {:undo-transation? false}) (dwm/apply-modifiers {:undo-transation? false})
(move-shapes-to-frame ids target-frame drop-index cell-data)
(finish-transform) (finish-transform)
(dwu/commit-undo-transaction undo-id)))))))))))))) (dwu/commit-undo-transaction undo-id))))))))))))))
@ -832,7 +832,7 @@
:ignore-snap-pixel true})))))) :ignore-snap-pixel true}))))))
(defn- move-shapes-to-frame (defn- move-shapes-to-frame
[ids frame-id drop-index] [ids frame-id drop-index [row column :as cell]]
(ptk/reify ::move-shapes-to-frame (ptk/reify ::move-shapes-to-frame
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
@ -905,6 +905,11 @@
moving-shapes-ids moving-shapes-ids
(map :id moving-shapes) (map :id moving-shapes)
moving-shapes-children-ids
(->> moving-shapes
(mapcat #(cfh/get-children-with-self objects (:id %)))
(map :id))
changes changes
(-> (pcb/empty-changes it page-id) (-> (pcb/empty-changes it page-id)
(pcb/with-objects objects) (pcb/with-objects objects)
@ -913,7 +918,7 @@
(pcb/update-shapes moving-shapes-ids ctl/remove-layout-item-data)) (pcb/update-shapes moving-shapes-ids ctl/remove-layout-item-data))
;; Remove component-root property when moving a shape inside a component ;; Remove component-root property when moving a shape inside a component
(cond-> (ctn/get-instance-root objects frame) (cond-> (ctn/get-instance-root objects frame)
(pcb/update-shapes moving-shapes-ids #(dissoc % :component-root))) (pcb/update-shapes moving-shapes-children-ids #(dissoc % :component-root)))
;; Add component-root property when moving a component outside a component ;; Add component-root property when moving a component outside a component
(cond-> (not (ctn/get-instance-root objects frame)) (cond-> (not (ctn/get-instance-root objects frame))
(pcb/update-shapes moving-shapes-ids (fn [shape] (pcb/update-shapes moving-shapes-ids (fn [shape]
@ -924,7 +929,16 @@
(pcb/update-shapes shape-ids-to-detach ctk/detach-shape) (pcb/update-shapes shape-ids-to-detach ctk/detach-shape)
(pcb/change-parent frame-id moving-shapes drop-index) (pcb/change-parent frame-id moving-shapes drop-index)
(cond-> (ctl/grid-layout? objects frame-id) (cond-> (ctl/grid-layout? objects frame-id)
(-> (pcb/update-shapes [frame-id] ctl/assign-cell-positions {:with-objects? true}) (-> (pcb/update-shapes
[frame-id]
(fn [frame objects]
(-> frame
;; Assign the cell when pushing into a specific grid cell
(cond-> (some? cell)
(-> (ctl/push-into-cell moving-shapes-ids row column)
(ctl/assign-cells objects)))
(ctl/assign-cell-positions objects)))
{:with-objects? true})
(pcb/reorder-grid-children [frame-id]))) (pcb/reorder-grid-children [frame-id])))
(pcb/remove-objects empty-parents))] (pcb/remove-objects empty-parents))]

View File

@ -625,7 +625,7 @@
(if (some? shape) (if (some? shape)
(let [fonts (ff/shape->fonts shape objects) (let [fonts (ff/shape->fonts shape objects)
bounds (gsb/get-object-bounds objects shape) bounds (gsb/get-object-bounds objects shape {:ignore-margin? false})
background (when (str/ends-with? object-id "component") background (when (str/ends-with? object-id "component")
(or (:background options) (dom/get-css-variable "--assets-component-background-color") "#fff")) (or (:background options) (dom/get-css-variable "--assets-component-background-color") "#fff"))

View File

@ -7,6 +7,7 @@
(ns app.main.ui.auth (ns app.main.ui.auth
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.data.macros :as dm]
[app.config :as cf] [app.config :as cf]
[app.main.ui.auth.login :refer [login-page]] [app.main.ui.auth.login :refer [login-page]]
[app.main.ui.auth.recovery :refer [recovery-page]] [app.main.ui.auth.recovery :refer [recovery-page]]
@ -35,41 +36,39 @@
[:a {:href cf/privacy-policy-uri :target "_blank"} (tr "auth.privacy-policy")])]))) [:a {:href cf/privacy-policy-uri :target "_blank"} (tr "auth.privacy-policy")])])))
(mf/defc auth (mf/defc auth
[{:keys [route] :as props}] {::mf/props :obj}
(let [section (get-in route [:data :name]) [{:keys [route]}]
params (:query-params route) (let [section (dm/get-in route [:data :name])
show-illustration? (contains? cf/flags :login-illustration)] params (:query-params route)]
(mf/use-effect (mf/with-effect []
#(dom/set-html-title (tr "title.default"))) (dom/set-html-title (tr "title.default")))
[:main {:class (stl/css-case :auth-section true [:main {:class (stl/css :auth-section)}
:no-illustration (not show-illustration?))} [:div {:class (stl/css :login-illustration)}
(when show-illustration? i/login-illustration]
[:div {:class (stl/css :login-illustration)}
i/login-illustration])
[:section {:class (stl/css :auth-content)} [:section {:class (stl/css :auth-content)}
[:* [:a {:href "#/" :class (stl/css :logo-btn)} i/logo]
[:a {:href "#/" :class (stl/css :logo-btn)} i/logo] (case section
(case section :auth-register
:auth-register [:& register-page {:params params}]
[:& register-page {:params params}]
:auth-register-validate :auth-register-validate
[:& register-validate-page {:params params}] [:& register-validate-page {:params params}]
:auth-register-success :auth-register-success
[:& register-success-page {:params params}] [:& register-success-page {:params params}]
:auth-login :auth-login
[:& login-page {:params params}] [:& login-page {:params params}]
:auth-recovery-request :auth-recovery-request
[:& recovery-request-page] [:& recovery-request-page]
:auth-recovery :auth-recovery
[:& recovery-page {:params params}])] [:& recovery-page {:params params}])
(when (contains? #{:auth-login :auth-register} section) (when (or (= section :auth-login)
(= section :auth-register))
[:& terms-login])]])) [:& terms-login])]]))

View File

@ -17,11 +17,6 @@
width: 100%; width: 100%;
overflow: auto; overflow: auto;
&.no-illustration {
display: flex;
justify-content: center;
}
@media (max-width: 992px) { @media (max-width: 992px) {
display: flex; display: flex;
justify-content: center; justify-content: center;

View File

@ -35,6 +35,14 @@
:login-with-gitlab :login-with-gitlab
:login-with-oidc])) :login-with-oidc]))
(mf/defc demo-warning
{::mf/props :obj}
[]
[:div {:class (stl/css :banner)}
[:& context-notification
{:type :warning
:content (tr "auth.demo-warning")}]])
(defn- login-with-oidc (defn- login-with-oidc
[event provider params] [event provider params]
(dom/prevent-default event) (dom/prevent-default event)
@ -284,6 +292,9 @@
[:h1 {:class (stl/css :auth-title) [:h1 {:class (stl/css :auth-title)
:data-test "login-title"} (tr "auth.login-title")] :data-test "login-title"} (tr "auth.login-title")]
(when (contains? cf/flags :demo-warning)
[:& demo-warning])
[:hr {:class (stl/css :separator)}] [:hr {:class (stl/css :separator)}]
[:& login-methods {:params params}] [:& login-methods {:params params}]

View File

@ -18,20 +18,12 @@
[app.main.ui.components.forms :as fm] [app.main.ui.components.forms :as fm]
[app.main.ui.components.link :as lk] [app.main.ui.components.link :as lk]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.main.ui.notifications.context-notification :refer [context-notification]]
[app.util.i18n :refer [tr tr-html]] [app.util.i18n :refer [tr tr-html]]
[app.util.router :as rt] [app.util.router :as rt]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[cljs.spec.alpha :as s] [cljs.spec.alpha :as s]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(mf/defc demo-warning
[_]
[:div {:class (stl/css :banner)}
[:& context-notification
{:type :warning
:content (tr "auth.demo-warning")}]])
;; --- PAGE: Register ;; --- PAGE: Register
(defn- validate (defn- validate
@ -86,7 +78,7 @@
(st/emit! (rt/nav :auth-register-validate {} params))) (st/emit! (rt/nav :auth-register-validate {} params)))
(mf/defc register-form (mf/defc register-form
[{:keys [params on-success-callback] :as props}] [{:keys [params on-success-callback]}]
(let [initial (mf/use-memo (mf/deps params) (constantly params)) (let [initial (mf/use-memo (mf/deps params) (constantly params))
form (fm/use-form :spec ::register-form form (fm/use-form :spec ::register-form
:validators [validate :validators [validate
@ -136,7 +128,8 @@
(mf/defc register-methods (mf/defc register-methods
[{:keys [params on-success-callback] :as props}] {::mf/props :obj}
[{:keys [params on-success-callback]}]
[:* [:*
(when login/show-alt-login-buttons? (when login/show-alt-login-buttons?
[:* [:*
@ -146,14 +139,15 @@
[:& register-form {:params params :on-success-callback on-success-callback}]]) [:& register-form {:params params :on-success-callback on-success-callback}]])
(mf/defc register-page (mf/defc register-page
[{:keys [params] :as props}] {::mf/props :obj}
[{:keys [params]}]
[:div {:class (stl/css :auth-form)} [:div {:class (stl/css :auth-form)}
[:h1 {:class (stl/css :auth-title) [:h1 {:class (stl/css :auth-title)
:data-test "registration-title"} (tr "auth.register-title")] :data-test "registration-title"} (tr "auth.register-title")]
[:div {:class (stl/css :auth-subtitle)} (tr "auth.register-subtitle")] [:div {:class (stl/css :auth-subtitle)} (tr "auth.register-subtitle")]
(when (contains? cf/flags :demo-warning) (when (contains? cf/flags :demo-warning)
[:& demo-warning]) [:& login/demo-warning])
[:& register-methods {:params params}] [:& register-methods {:params params}]
@ -212,7 +206,7 @@
::accept-newsletter-subscription]))) ::accept-newsletter-subscription])))
(mf/defc register-validate-form (mf/defc register-validate-form
[{:keys [params on-success-callback] :as props}] [{:keys [params on-success-callback]}]
(let [form (fm/use-form :spec ::register-validate-form (let [form (fm/use-form :spec ::register-validate-form
:validators [(fm/validate-not-empty :fullname (tr "auth.name.not-all-space")) :validators [(fm/validate-not-empty :fullname (tr "auth.name.not-all-space"))
(fm/validate-length :fullname fm/max-length-allowed (tr "auth.name.too-long"))] (fm/validate-length :fullname fm/max-length-allowed (tr "auth.name.too-long"))]
@ -263,7 +257,7 @@
(mf/defc register-validate-page (mf/defc register-validate-page
[{:keys [params] :as props}] [{:keys [params]}]
[:div {:class (stl/css :auth-form)} [:div {:class (stl/css :auth-form)}
[:h1 {:class (stl/css :auth-title) [:h1 {:class (stl/css :auth-title)
:data-test "register-title"} (tr "auth.register-title")] :data-test "register-title"} (tr "auth.register-title")]
@ -279,7 +273,7 @@
(tr "labels.go-back")]]]]) (tr "labels.go-back")]]]])
(mf/defc register-success-page (mf/defc register-success-page
[{:keys [params] :as props}] [{:keys [params]}]
[:div {:class (stl/css :auth-form :register-success)} [:div {:class (stl/css :auth-form :register-success)}
[:div {:class (stl/css :notification-icon)} i/icon-verify] [:div {:class (stl/css :notification-icon)} i/icon-verify]
[:div {:class (stl/css :notification-text)} (tr "auth.verification-email-sent")] [:div {:class (stl/css :notification-text)} (tr "auth.verification-email-sent")]

View File

@ -17,7 +17,7 @@
[app.main.fonts :as fonts] [app.main.fonts :as fonts]
[app.main.rasterizer :as thr] [app.main.rasterizer :as thr]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.render :refer [component-svg]] [app.main.render :as render]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.color-bullet :as bc] [app.main.ui.components.color-bullet :as bc]
@ -49,8 +49,7 @@
(->> (rp/cmd! :create-file-thumbnail params) (->> (rp/cmd! :create-file-thumbnail params)
(rx/map :uri)))) (rx/map :uri))))
(defn- ask-for-thumbnail (defn render-thumbnail
"Creates some hooks to handle the files thumbnails cache"
[file-id revn] [file-id revn]
(->> (wrk/ask! {:cmd :thumbnails/generate-for-file (->> (wrk/ask! {:cmd :thumbnails/generate-for-file
:revn revn :revn revn
@ -61,7 +60,12 @@
(rx/map (fn [styles] (rx/map (fn [styles]
(assoc result (assoc result
:styles styles :styles styles
:width 252)))))) :width 252))))))))
(defn- ask-for-thumbnail
"Creates some hooks to handle the files thumbnails cache"
[file-id revn]
(->> (render-thumbnail file-id revn)
(rx/mapcat thr/render) (rx/mapcat thr/render)
(rx/mapcat (partial persist-thumbnail file-id revn)))) (rx/mapcat (partial persist-thumbnail file-id revn))))
@ -141,8 +145,8 @@
(let [root-id (or (:main-instance-id component) (:id component))] ;; Check for components-v2 in library (let [root-id (or (:main-instance-id component) (:id component))] ;; Check for components-v2 in library
[:div {:class (stl/css :asset-list-item) [:div {:class (stl/css :asset-list-item)
:key (str "assets-component-" (:id component))} :key (str "assets-component-" (:id component))}
[:& component-svg {:root-shape (get-in component [:objects root-id]) [:& render/component-svg {:root-shape (get-in component [:objects root-id])
:objects (:objects component)}] ;; Components in the summary come loaded with objects, even in v2 :objects (:objects component)}] ;; Components in the summary come loaded with objects, even in v2
[:div {:class (stl/css :name-block)} [:div {:class (stl/css :name-block)}
[:span {:class (stl/css :item-name) [:span {:class (stl/css :item-name)
:title (:name component)} :title (:name component)}

View File

@ -12,6 +12,8 @@
.modal-container { .modal-container {
@extend .modal-container-base; @extend .modal-container-base;
display: flex;
flex-direction: column;
} }
.modal-header { .modal-header {
@ -29,6 +31,9 @@
.modal-content { .modal-content {
@include bodySmallTypography; @include bodySmallTypography;
flex: 1;
overflow-y: auto;
overflow-x: hidden;
display: grid; display: grid;
grid-template-columns: 1fr; grid-template-columns: 1fr;
gap: $s-16; gap: $s-16;

View File

@ -378,6 +378,7 @@
tutorial-viewed? (:viewed-tutorial? props true) tutorial-viewed? (:viewed-tutorial? props true)
walkthrough-viewed? (:viewed-walkthrough? props true) walkthrough-viewed? (:viewed-walkthrough? props true)
is-my-penpot (= (:default-team-id profile) (:id team))
team-id (:id team) team-id (:id team)
@ -387,6 +388,7 @@
(st/emit! (du/update-profile-props {:team-hero? false}) (st/emit! (du/update-profile-props {:team-hero? false})
(ptk/data-event ::ev/event {::ev/name "dont-show-team-up-hero" (ptk/data-event ::ev/event {::ev/name "dont-show-team-up-hero"
::ev/origin "dashboard"})))) ::ev/origin "dashboard"}))))
close-tutorial close-tutorial
(mf/use-fn (mf/use-fn
(fn [] (fn []
@ -395,6 +397,7 @@
::ev/origin "get-started-hero" ::ev/origin "get-started-hero"
:type "tutorial" :type "tutorial"
:section "dashboard"})))) :section "dashboard"}))))
close-walkthrough close-walkthrough
(mf/use-fn (mf/use-fn
(fn [] (fn []
@ -402,7 +405,13 @@
(ptk/data-event ::ev/event {::ev/name "dont-show-walkthrough" (ptk/data-event ::ev/event {::ev/name "dont-show-walkthrough"
::ev/origin "get-started-hero" ::ev/origin "get-started-hero"
:type "walkthrough" :type "walkthrough"
:section "dashboard"}))))] :section "dashboard"}))))
show-hero? (and is-my-penpot
(or (not tutorial-viewed?)
(not walkthrough-viewed?)))
show-team-hero? (and (not is-my-penpot) team-hero?)]
(mf/with-effect [team] (mf/with-effect [team]
(let [tname (if (:is-default team) (let [tname (if (:is-default team)
@ -423,8 +432,7 @@
[:& team-hero {:team team :close-fn close-banner}]) [:& team-hero {:team team :close-fn close-banner}])
(when (and (contains? cf/flags :dashboard-templates-section) (when (and (contains? cf/flags :dashboard-templates-section)
(or (not tutorial-viewed?) show-hero?)
(not walkthrough-viewed?)))
[:div {:class (stl/css :hero-projects)} [:div {:class (stl/css :hero-projects)}
(when (and (not tutorial-viewed?) (:is-default team)) (when (and (not tutorial-viewed?) (:is-default team))
[:& tutorial-project [:& tutorial-project
@ -435,7 +443,11 @@
[:& interface-walkthrough [:& interface-walkthrough
{:close-walkthrough close-walkthrough}])]) {:close-walkthrough close-walkthrough}])])
[:div {:class (stl/css :dashboard-container :no-bg :dashboard-projects)} [:div {:class (stl/css-case :dashboard-container true
:no-bg true
:dashboard-projects true
:with-hero show-hero?
:with-team-hero show-team-hero?)}
(for [{:keys [id] :as project} projects] (for [{:keys [id] :as project} projects]
(let [files (when recent-map (let [files (when recent-map
(->> (vals recent-map) (->> (vals recent-map)

View File

@ -17,6 +17,12 @@
.dashboard-projects { .dashboard-projects {
user-select: none; user-select: none;
height: calc(100vh - $s-64);
}
.with-hero,
.with-team-hero {
height: calc(100vh - $s-280);
} }
.dashboard-shared { .dashboard-shared {

View File

@ -339,7 +339,7 @@
.profile-fullname { .profile-fullname {
@include smallTitleTipography; @include smallTitleTipography;
@include text-ellipsis; @include textEllipsis;
align-self: center; align-self: center;
max-width: $s-160; max-width: $s-160;
color: var(--profile-foreground-color); color: var(--profile-foreground-color);

View File

@ -118,6 +118,7 @@
:id id :id id
:data-index index :data-index index
:on-click on-click :on-click on-click
:on-mouse-down dom/prevent-default
:on-key-down on-key-down} :on-key-down on-key-down}
[:div {:class (stl/css :template-card)} [:div {:class (stl/css :template-card)}
[:div {:class (stl/css :img-container)} [:div {:class (stl/css :img-container)}

View File

@ -91,4 +91,4 @@
(defmethod rc/render-release-notes "0.0" (defmethod rc/render-release-notes "0.0"
[params] [params]
(rc/render-release-notes (assoc params :version "1.21"))) (rc/render-release-notes (assoc params :version "2.0")))

View File

@ -11,10 +11,6 @@
[app.main.ui.releases.common :as c] [app.main.ui.releases.common :as c]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(defmethod c/render-release-notes "1.21"
[data]
(c/render-release-notes (assoc data :version "2.0")))
;; TODO: Review all copies and alt text ;; TODO: Review all copies and alt text
(defmethod c/render-release-notes "2.0" (defmethod c/render-release-notes "2.0"
[{:keys [slide klass next finish navigate version]}] [{:keys [slide klass next finish navigate version]}]

View File

@ -39,8 +39,8 @@
(s/keys :req-un [::email-1 ::email-2])) (s/keys :req-un [::email-1 ::email-2]))
(defn- on-error (defn- on-error
[form {:keys [code] :as error}] [form error]
(case code (case (:code (ex-data error))
:email-already-exists :email-already-exists
(swap! form (fn [data] (swap! form (fn [data]
(let [error {:message (tr "errors.email-already-exists")}] (let [error {:message (tr "errors.email-already-exists")}]
@ -93,7 +93,6 @@
(let [different-emails-error? (= (dma/get-in @form [:errors :email-2 :code]) :different-emails) (let [different-emails-error? (= (dma/get-in @form [:errors :email-2 :code]) :different-emails)
email-1 (dma/get-in @form [:clean-data :email-1]) email-1 (dma/get-in @form [:clean-data :email-1])
email-2 (dma/get-in @form [:clean-data :email-2])] email-2 (dma/get-in @form [:clean-data :email-2])]
(println "different-emails-error?" (and different-emails-error? (= email-1 email-2)))
(when (and different-emails-error? (= email-1 email-2)) (when (and different-emails-error? (= email-1 email-2))
(swap! form d/dissoc-in [:errors :email-2])))))] (swap! form d/dissoc-in [:errors :email-2])))))]

View File

@ -62,32 +62,34 @@
(obj/merge! props (get-border-props shape))) (obj/merge! props (get-border-props shape)))
(defn add-fill! (defn add-fill!
[attrs fill-data render-id index type] ([attrs fill-data render-id index type]
(let [index (if (some? index) (dm/str "-" index) "")] (add-fill! attrs fill-data render-id index type "none"))
(cond ([attrs fill-data render-id index type fill-default]
(contains? fill-data :fill-image) (let [index (if (some? index) (dm/str "-" index) "")]
(let [id (dm/str "fill-image-" render-id)] (cond
(obj/set! attrs "fill" (dm/str "url(#" id ")"))) (contains? fill-data :fill-image)
(let [id (dm/str "fill-image-" render-id)]
(obj/set! attrs "fill" (dm/str "url(#" id ")")))
(some? (:fill-color-gradient fill-data)) (some? (:fill-color-gradient fill-data))
(let [id (dm/str "fill-color-gradient-" render-id index)] (let [id (dm/str "fill-color-gradient-" render-id index)]
(obj/set! attrs "fill" (dm/str "url(#" id ")"))) (obj/set! attrs "fill" (dm/str "url(#" id ")")))
(contains? fill-data :fill-color) (contains? fill-data :fill-color)
(obj/set! attrs "fill" (:fill-color fill-data)) (obj/set! attrs "fill" (:fill-color fill-data))
:else :else
(obj/set! attrs "fill" "none")) (obj/set! attrs "fill" fill-default))
(when (contains? fill-data :fill-opacity) (when (contains? fill-data :fill-opacity)
(obj/set! attrs "fillOpacity" (:fill-opacity fill-data))) (obj/set! attrs "fillOpacity" (:fill-opacity fill-data)))
(when (and (= :text type) (when (and (= :text type)
(nil? (:fill-color-gradient fill-data)) (nil? (:fill-color-gradient fill-data))
(nil? (:fill-color fill-data))) (nil? (:fill-color fill-data)))
(obj/set! attrs "fill" "black")) (obj/set! attrs "fill" "black"))
attrs)) attrs)))
(defn add-stroke! (defn add-stroke!
[attrs data render-id index open-path?] [attrs data render-id index open-path?]
@ -165,8 +167,10 @@
(obj/map->obj))))) (obj/map->obj)))))
(defn get-fill-style (defn get-fill-style
[fill-data index render-id type] ([fill-data index render-id type]
(add-fill! #js {} fill-data render-id index type)) (add-fill! #js {} fill-data render-id index type))
([fill-data index render-id type fill-default]
(add-fill! #js {} fill-data render-id index type fill-default)))
(defn add-fill-props! (defn add-fill-props!
([props shape render-id] ([props shape render-id]
@ -242,8 +246,10 @@
(obj/set! style "fillOpacity" opacity))) (obj/set! style "fillOpacity" opacity)))
^boolean (d/not-empty? shape-fills) ^boolean (d/not-empty? shape-fills)
(let [fill (nth shape-fills 0)] (let [fill (nth shape-fills 0)
(obj/merge! style (get-fill-style fill render-id 0 shape-type))) svg-fill (obj/get svg-attrs "fill")
fill-default (d/nilv svg-fill "none")]
(obj/merge! style (get-fill-style fill render-id 0 shape-type fill-default)))
(and ^boolean (cfh/path-shape? shape) (and ^boolean (cfh/path-shape? shape)
^boolean (empty? shape-fills)) ^boolean (empty? shape-fills))

View File

@ -476,7 +476,8 @@
svg-attrs (attrs/get-svg-props shape render-id) svg-attrs (attrs/get-svg-props shape render-id)
style (-> (obj/get props "style") style (-> (obj/get props "style")
(obj/clone)) (obj/clone)
(obj/merge! (obj/get svg-attrs "style")))
props (mf/spread-props svg-attrs props (mf/spread-props svg-attrs
{:id stroke-id {:id stroke-id

View File

@ -8,8 +8,8 @@
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.geom.rect :as grc]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.geom.shapes.bounds :as gsb]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
[app.config :as cf] [app.config :as cf]
[app.main.ui.context :as muc] [app.main.ui.context :as muc]
@ -119,7 +119,7 @@
points (dm/get-prop shape :points) points (dm/get-prop shape :points)
bounds (mf/with-memo [bounds points] bounds (mf/with-memo [bounds points]
(or bounds (grc/points->rect points))) (or bounds (gsb/get-frame-bounds shape)))
thumb (:thumbnail shape) thumb (:thumbnail shape)

View File

@ -51,7 +51,7 @@
.layer-title { .layer-title {
@include bodySmallTypography; @include bodySmallTypography;
@include text-ellipsis; @include textEllipsis;
height: $s-32; height: $s-32;
padding: $s-8 0; padding: $s-8 0;
color: var(--assets-item-name-foreground-color-rest); color: var(--assets-item-name-foreground-color-rest);

View File

@ -36,7 +36,7 @@
width: $s-272; width: $s-272;
padding: $s-6; padding: $s-6;
max-height: calc(100vh - 3 * ($s-2 + $s-48)); max-height: calc(100vh - 3 * ($s-2 + $s-48));
overflow: scroll; overflow: auto;
} }
.dropdown-element { .dropdown-element {

View File

@ -228,48 +228,54 @@
(mf/defc context-menu-group (mf/defc context-menu-group
[{:keys [shapes]}] [{:keys [shapes]}]
(let [multiple? (> (count shapes) 1) (let [multiple? (> (count shapes) 1)
single? (= (count shapes) 1) single? (= (count shapes) 1)
do-create-artboard-from-selection #(st/emit! (dwsh/create-artboard-from-selection))
objects (deref refs/workspace-page-objects)
any-in-copy? (some true? (map #(ctn/has-any-copy-parent? objects %) shapes))
;; components can't be ungrouped ;; components can't be ungrouped
has-frame? (->> shapes (d/seek #(and (cfh/frame-shape? %) (not (ctk/instance-head? %))))) has-frame? (->> shapes (d/seek #(and (cfh/frame-shape? %) (not (ctk/instance-head? %)))))
has-group? (->> shapes (d/seek #(and (cfh/group-shape? %) (not (ctk/instance-head? %))))) has-group? (->> shapes (d/seek #(and (cfh/group-shape? %) (not (ctk/instance-head? %)))))
has-bool? (->> shapes (d/seek cfh/bool-shape?)) has-bool? (->> shapes (d/seek cfh/bool-shape?))
has-mask? (->> shapes (d/seek :masked-group)) has-mask? (->> shapes (d/seek :masked-group))
is-group? (and single? has-group?) is-group? (and single? has-group?)
is-bool? (and single? has-bool?) is-bool? (and single? has-bool?)
do-create-group #(st/emit! dw/group-selected) do-create-group #(st/emit! dw/group-selected)
do-mask-group #(st/emit! dw/mask-group) do-mask-group #(st/emit! dw/mask-group)
do-remove-group #(st/emit! dw/ungroup-selected) do-remove-group #(st/emit! dw/ungroup-selected)
do-unmask-group #(st/emit! dw/unmask-group)] do-unmask-group #(st/emit! dw/unmask-group)
do-create-artboard-from-selection
#(st/emit! (dwsh/create-artboard-from-selection))]
[:* [:*
(when (or has-bool? has-group? has-mask? has-frame?) (when (not any-in-copy?)
[:& menu-entry {:title (tr "workspace.shape.menu.ungroup") [:*
:shortcut (sc/get-tooltip :ungroup) (when (or has-bool? has-group? has-mask? has-frame?)
:on-click do-remove-group}]) [:& menu-entry {:title (tr "workspace.shape.menu.ungroup")
:shortcut (sc/get-tooltip :ungroup)
:on-click do-remove-group}])
[:& menu-entry {:title (tr "workspace.shape.menu.group") [:& menu-entry {:title (tr "workspace.shape.menu.group")
:shortcut (sc/get-tooltip :group) :shortcut (sc/get-tooltip :group)
:on-click do-create-group}] :on-click do-create-group}]
(when (or multiple? (and is-group? (not has-mask?)) is-bool?) (when (or multiple? (and is-group? (not has-mask?)) is-bool?)
[:& menu-entry {:title (tr "workspace.shape.menu.mask") [:& menu-entry {:title (tr "workspace.shape.menu.mask")
:shortcut (sc/get-tooltip :mask) :shortcut (sc/get-tooltip :mask)
:on-click do-mask-group}]) :on-click do-mask-group}])
(when has-mask? (when has-mask?
[:& menu-entry {:title (tr "workspace.shape.menu.unmask") [:& menu-entry {:title (tr "workspace.shape.menu.unmask")
:shortcut (sc/get-tooltip :unmask) :shortcut (sc/get-tooltip :unmask)
:on-click do-unmask-group}]) :on-click do-unmask-group}])
[:& menu-entry {:title (tr "workspace.shape.menu.create-artboard-from-selection") [:& menu-entry {:title (tr "workspace.shape.menu.create-artboard-from-selection")
:shortcut (sc/get-tooltip :artboard-selection) :shortcut (sc/get-tooltip :artboard-selection)
:on-click do-create-artboard-from-selection}] :on-click do-create-artboard-from-selection}]
[:& menu-separator]])) [:& menu-separator]])]))
(mf/defc context-focus-mode-menu (mf/defc context-focus-mode-menu
[{:keys []}] [{:keys []}]
@ -391,7 +397,9 @@
(mf/defc context-menu-layout (mf/defc context-menu-layout
{::mf/props :obj} {::mf/props :obj}
[{:keys [shapes]}] [{:keys [shapes]}]
(let [single? (= (count shapes) 1) (let [single? (= (count shapes) 1)
objects (deref refs/workspace-page-objects)
any-in-copy? (some true? (map #(ctn/has-any-copy-parent? objects %) shapes))
has-flex? has-flex?
(and single? (every? ctl/flex-layout? shapes)) (and single? (every? ctl/flex-layout? shapes))
@ -414,29 +422,30 @@
(fn [_event] (fn [_event]
(let [ids (map :id shapes)] (let [ids (map :id shapes)]
(st/emit! (dwsl/remove-layout ids)))))] (st/emit! (dwsl/remove-layout ids)))))]
[:*
(when (not any-in-copy?)
(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}])]
(if (or ^boolean has-flex? [:div
^boolean has-grid?) [:& menu-separator]
[:div [:& menu-entry {:title (tr "workspace.shape.menu.add-flex")
[:& menu-separator] :shortcut (sc/get-tooltip :toggle-layout-flex)
(if has-flex? :value "flex"
[:& menu-entry {:title (tr "workspace.shape.menu.remove-flex") :on-click on-add-layout}]
:shortcut (sc/get-tooltip :toggle-layout-flex) [:& menu-entry {:title (tr "workspace.shape.menu.add-grid")
:on-click on-remove-layout}] :shortcut (sc/get-tooltip :toggle-layout-grid)
[:& menu-entry {:title (tr "workspace.shape.menu.remove-grid") :value "grid"
:shortcut (sc/get-tooltip :toggle-layout-grid) :on-click on-add-layout}]]))]))
: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 (mf/defc context-menu-component
[{:keys [shapes]}] [{:keys [shapes]}]

View File

@ -62,14 +62,11 @@
} }
} }
.submenu-icon { .submenu-icon svg {
position: absolute; @extend .button-icon-small;
right: $s-16; stroke: var(--menu-foreground-color);
svg {
@extend .button-icon-small;
stroke: var(--menu-foreground-color);
}
} }
&:hover { &:hover {
background-color: var(--menu-background-color-hover); background-color: var(--menu-background-color-hover);
.title { .title {

View File

@ -81,11 +81,6 @@
height: fit-content; height: fit-content;
} }
.item-name {
@include bodyLargeTypography;
color: var(--library-name-foreground-color);
}
.item-publish, .item-publish,
.item-unpublish { .item-unpublish {
@extend .button-primary; @extend .button-primary;
@ -216,8 +211,11 @@
} }
.item-name { .item-name {
@include bodyLargeTypography;
@include textEllipsis; @include textEllipsis;
margin: 0; margin: 0;
max-width: $s-244;
color: var(--library-name-foreground-color);
} }
.item-update { .item-update {
@ -254,6 +252,7 @@
.modal-v2-info { .modal-v2-info {
width: $s-664; width: $s-664;
height: fit-content; height: fit-content;
max-height: fit-content;
} }
.modal-v2-title { .modal-v2-title {

View File

@ -179,6 +179,10 @@
(mf/use-fn (mf/use-fn
(mf/deps selected-drawtool) (mf/deps selected-drawtool)
(fn [_] (fn [_]
(when (contains? layout :document-history)
(st/emit! (-> (dw/remove-layout-flag :document-history)
(vary-meta assoc ::ev/origin "workspace-header"))))
(if (= :comments selected-drawtool) (if (= :comments selected-drawtool)
(st/emit! :interrupt) (st/emit! :interrupt)
(active-comments)))) (active-comments))))
@ -187,8 +191,11 @@
(mf/use-fn (mf/use-fn
(mf/deps selected-drawtool) (mf/deps selected-drawtool)
(fn [] (fn []
(when (= :comments selected-drawtool) (when (= :comments selected-drawtool)
(st/emit! :interrupt)) (st/emit! :interrupt
(-> (dw/toggle-layout-flag :comments)
(vary-meta assoc ::ev/origin "workspace-header"))))
(st/emit! (-> (dw/toggle-layout-flag :document-history) (st/emit! (-> (dw/toggle-layout-flag :document-history)
(vary-meta assoc ::ev/origin "workspace-header")))))] (vary-meta assoc ::ev/origin "workspace-header")))))]

View File

@ -130,7 +130,7 @@
container-ref (mf/use-ref nil) container-ref (mf/use-ref nil)
content-ref (mf/use-ref nil) content-ref (mf/use-ref nil)
bounds (gsb/get-object-bounds objects shape) bounds (gsb/get-object-bounds objects shape {:ignore-margin? false})
x (dm/get-prop bounds :x) x (dm/get-prop bounds :x)
y (dm/get-prop bounds :y) y (dm/get-prop bounds :y)

View File

@ -219,7 +219,9 @@
:auto-focus true :auto-focus true
:default-value (cfh/merge-path-item (:path color) (:name color))}] :default-value (cfh/merge-path-item (:path color) (:name color))}]
[:div {:title (:name color) [:div {:title (if (= (:name color) default-name)
default-name
(dm/str (:name color) " (" default-name ")"))
:class (stl/css :name-block) :class (stl/css :name-block)
:on-double-click rename-color-clicked} :on-double-click rename-color-clicked}

View File

@ -109,6 +109,8 @@
:style {"--depth" depth "--parent-size" parent-size} :style {"--depth" depth "--parent-size" parent-size}
:ref ref :ref ref
:on-double-click start-edit} :on-double-click start-edit}
(d/nilv shape-name "")] (if (dbg/enabled? :show-ids)
(str (d/nilv shape-name "") " | " (str/slice (str shape-id) 24))
(d/nilv shape-name ""))]
(when (and (dbg/enabled? :show-touched) ^boolean shape-touched?) (when (and (dbg/enabled? :show-touched) ^boolean shape-touched?)
[:span {:class (stl/css :element-name-touched)} "*"])]))) [:span {:class (stl/css :element-name-touched)} "*"])])))

View File

@ -13,10 +13,10 @@
height: $s-32; height: $s-32;
min-height: $s-32; min-height: $s-32;
margin: $s-8 0 $s-4 $s-8; margin: $s-8 0 $s-4 $s-8;
padding-right: $s-8; padding-right: $s-12;
&.search { &.search {
padding: 0 $s-8 0 $s-12; padding: 0 $s-12 0 $s-8;
gap: $s-4; gap: $s-4;
.filter-button { .filter-button {
@include flexCenter; @include flexCenter;

View File

@ -26,6 +26,7 @@
[app.main.ui.hooks :as h] [app.main.ui.hooks :as h]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.main.ui.workspace.sidebar.assets.common :as cmm] [app.main.ui.workspace.sidebar.assets.common :as cmm]
[app.util.debug :as dbg]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.timers :as tm] [app.util.timers :as tm]
@ -639,4 +640,6 @@
[:& component-swap {:shapes copies}]) [:& component-swap {:shapes copies}])
(when (and (not swap-opened?) (not multi) components-v2) (when (and (not swap-opened?) (not multi) components-v2)
[:& component-annotation {:id id :shape shape :component component}])])]))) [:& component-annotation {:id id :shape shape :component component}])
(when (dbg/enabled? :display-touched)
[:div ":touched " (str (:touched shape))])])])))

View File

@ -187,15 +187,15 @@
(def ^:private corner-bottom-icon (def ^:private corner-bottom-icon
(i/icon-xref :corner-bottom (stl/css :corner-icon))) (i/icon-xref :corner-bottom (stl/css :corner-icon)))
(def ^:private corner-bottomleft-icon (def ^:private corner-bottomleft-icon
(i/icon-xref :corner-bottomleft (stl/css :corner-icon))) (i/icon-xref :corner-bottom-left (stl/css :corner-icon)))
(def ^:private corner-bottomright-icon (def ^:private corner-bottomright-icon
(i/icon-xref :corner-bottomright (stl/css :corner-icon))) (i/icon-xref :corner-bottom-right (stl/css :corner-icon)))
(def ^:private corner-top-icon (def ^:private corner-top-icon
(i/icon-xref :corner-top (stl/css :corner-icon))) (i/icon-xref :corner-top (stl/css :corner-icon)))
(def ^:private corner-topleft-icon (def ^:private corner-topleft-icon
(i/icon-xref :corner-topleft (stl/css :corner-icon))) (i/icon-xref :corner-top-left (stl/css :corner-icon)))
(def ^:private corner-topright-icon (def ^:private corner-topright-icon
(i/icon-xref :corner-topright (stl/css :corner-icon))) (i/icon-xref :corner-top-right (stl/css :corner-icon)))
(mf/defc interaction-entry (mf/defc interaction-entry
[{:keys [index shape interaction update-interaction remove-interaction]}] [{:keys [index shape interaction update-interaction remove-interaction]}]

View File

@ -227,5 +227,5 @@
.title-spacing-sitemap { .title-spacing-sitemap {
padding-inline-start: $s-8; padding-inline-start: $s-8;
margin-block-start: $s-8; margin-block-start: $s-8;
padding-inline-end: $s-20; margin-block-end: $s-4;
} }

View File

@ -11,7 +11,10 @@
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.main.store :as st] [app.main.store :as st]
[app.util.dom :as dom] [app.util.dom :as dom]
[rumext.v2 :as mf])) [app.util.mouse :as mse]
[goog.events :as events]
[rumext.v2 :as mf])
(:import goog.events.EventType))
(defonce viewport-ref (atom nil)) (defonce viewport-ref (atom nil))
(defonce current-observer (atom nil)) (defonce current-observer (atom nil))
@ -45,6 +48,8 @@
#(fn [node] #(fn [node]
(mf/set-ref-val! ref node) (mf/set-ref-val! ref node)
(reset! viewport-ref node) (reset! viewport-ref node)
(when (some? node)
(events/listen node EventType.MOUSELEAVE (fn [] (st/emit! (mse/->BlurEvent)))))
(init-observer node on-change-bounds)))])) (init-observer node on-change-bounds)))]))
(defn point->viewport (defn point->viewport

View File

@ -73,6 +73,9 @@
;; Show an asterisk for touched copies ;; Show an asterisk for touched copies
:show-touched :show-touched
;; Show the id with the name
:show-ids
;; ;;
:grid-layout :grid-layout
@ -80,7 +83,10 @@
:grid-cells :grid-cells
;; Show info about shapes ;; Show info about shapes
:shape-panel}) :shape-panel
;; Show what is touched in copies
:display-touched})
(defn enable! (defn enable!
[option] [option]

View File

@ -588,7 +588,7 @@
(t/is (= (:fill-color shape1) clr/black)) (t/is (= (:fill-color shape1) clr/black))
(t/is (= (:fill-opacity shape1) 0)) (t/is (= (:fill-opacity shape1) 0))
(t/is (= (:name shape2) "Rect 1")) (t/is (= (:name shape2) "Rect 1"))
(t/is (= (:touched shape2) nil)) (t/is (= (:touched shape2) #{:fill-group}))
(t/is (= (:fill-color shape2) clr/test)) (t/is (= (:fill-color shape2) clr/test))
(t/is (= (:fill-opacity shape2) 0.5)) (t/is (= (:fill-opacity shape2) 0.5))
(t/is (= (:name c-instance2) "Board")) (t/is (= (:name c-instance2) "Board"))

View File

@ -5064,7 +5064,7 @@ msgstr "Click to close the path"
#, markdown #, markdown
msgid "workspace.top-bar.view-only" msgid "workspace.top-bar.view-only"
msgstr "**Inspecting mode** (View Only)" msgstr "**Inspecting code** (View Only)"
msgid "workspace.top-bar.read-only.done" msgid "workspace.top-bar.read-only.done"
msgstr "Done" msgstr "Done"

View File

@ -5149,7 +5149,7 @@ msgstr "Pulsar para cerrar la ruta"
#, markdown #, markdown
msgid "workspace.top-bar.view-only" msgid "workspace.top-bar.view-only"
msgstr "**Modo inspección** (View only)" msgstr "**Inspeccionando código** (View only)"
msgid "workspace.top-bar.read-only.done" msgid "workspace.top-bar.read-only.done"
msgstr "Hecho" msgstr "Hecho"

View File

@ -1 +1 @@
1.21.0 2.0.0