diff --git a/backend/scripts/repl b/backend/scripts/repl index 4e454b761..6336331e4 100755 --- a/backend/scripts/repl +++ b/backend/scripts/repl @@ -4,7 +4,7 @@ export PENPOT_HOST=devenv export PENPOT_TENANT=dev export PENPOT_FLAGS="\ $PENPOT_FLAGS \ - enable-registration + enable-login-with-ldap \ enable-login-with-password enable-login-with-oidc \ enable-login-with-google \ @@ -28,9 +28,7 @@ export PENPOT_FLAGS="\ enable-access-tokens \ disable-feature-components-v2 \ enable-file-validation \ - enable-file-schema-validation \ - disable-soft-file-schema-validation \ - disable-soft-file-validation"; + enable-file-schema-validation"; # Setup default upload media file size to 100MiB diff --git a/backend/scripts/start-dev b/backend/scripts/start-dev index 89df83d96..1a2fa2842 100755 --- a/backend/scripts/start-dev +++ b/backend/scripts/start-dev @@ -15,13 +15,12 @@ export PENPOT_FLAGS="\ enable-feature-fdata-pointer-map \ enable-feature-fdata-objects-map \ disable-secure-session-cookies \ + enable-rpc-climit \ enable-smtp \ enable-access-tokens \ disable-feature-components-v2 \ enable-file-validation \ - enable-file-schema-validation \ - disable-soft-file-schema-validation \ - disable-soft-file-validation"; + enable-file-schema-validation"; export OPTIONS=" -A:jmx-remote -A:dev \ diff --git a/backend/src/app/features/components_v2.clj b/backend/src/app/features/components_v2.clj index 46d04636f..565d00320 100644 --- a/backend/src/app/features/components_v2.clj +++ b/backend/src/app/features/components_v2.clj @@ -215,10 +215,15 @@ (update :pages-index update-vals fix-container) (d/update-when :components update-vals fix-container)))) - fix-page-invalid-options + fix-invalid-page (fn [file-data] (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] (if (and (contains? options :background) @@ -433,7 +438,8 @@ (letfn [(fix-component [components id component] (let [root-shape (ctst/get-shape component (:id component))] (if (or (empty? (:objects component)) - (nil? root-shape)) + (nil? root-shape) + (nil? (:type root-shape))) (dissoc components id) components)))] @@ -731,43 +737,61 @@ (fn [file-data] ;; 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. - (letfn [(fix-container [container] - (reduce fix-shape container (ctn/shapes-seq container))) + (let [count (volatile! 0) - (fix-shape [container shape] - (if (ctk/in-component-copy? shape) - ;; First look for the direct shape. - (let [root (ctn/get-component-shape (:objects container) shape) - libraries (assoc-in libraries [(:id file-data) :data] file-data) - library (get libraries (:component-file root)) - component (ctkl/get-component (:data library) (:component-id root) true) - direct-shape (ctf/get-component-shape (:data library) component (:shape-ref shape))] - (if (some? direct-shape) - ;; If it exists, there is nothing else to do. - container - ;; If not found, find the near shape. - (let [near-shape (d/seek #(= (:shape-ref %) (:shape-ref shape)) - (ctf/get-component-shapes (:data library) component))] - (if (some? near-shape) - ;; If found, update the ref to point to the near shape. - (ctn/update-shape container (:id shape) #(assoc % :shape-ref (:id near-shape))) - ;; If not found, it may be a fostered component. Try to locate a direct shape - ;; in the head component. - (let [head (ctn/get-head-shape (:objects container) shape) - library-2 (get libraries (:component-file head)) - component-2 (ctkl/get-component (:data library-2) (:component-id head) true) - direct-shape-2 (ctf/get-component-shape (:data library-2) component-2 (:shape-ref shape))] - (if (some? direct-shape-2) - ;; If it exists, there is nothing else to do. - container - ;; If not found, detach shape and all children. - ;; container - (detach-shape container shape))))))) - container))] + fix-shape + (fn [container shape] + (if (ctk/in-component-copy? shape) + ;; First look for the direct shape. + (let [root (ctn/get-component-shape (:objects container) shape) + libraries (assoc-in libraries [(:id file-data) :data] file-data) + library (get libraries (:component-file root)) + component (ctkl/get-component (:data library) (:component-id root) true) + direct-shape (ctf/get-component-shape (:data library) component (:shape-ref shape))] + (if (some? direct-shape) + ;; If it exists, there is nothing else to do. + container + ;; If not found, find the near shape. + (let [near-shape (d/seek #(= (:shape-ref %) (:shape-ref shape)) + (ctf/get-component-shapes (:data library) component))] + (if (some? near-shape) + ;; If found, update the ref to point to the near shape. + (do + (vswap! count inc) + (ctn/update-shape container (:id shape) #(assoc % :shape-ref (:id near-shape)))) + ;; If not found, it may be a fostered component. Try to locate a direct shape + ;; in the head component. + (let [head (ctn/get-head-shape (:objects container) shape) + library-2 (get libraries (:component-file head)) + component-2 (ctkl/get-component (:data library-2) (:component-id head) true) + direct-shape-2 (ctf/get-component-shape (:data library-2) component-2 (:shape-ref shape))] + (if (some? direct-shape-2) + ;; If it exists, there is nothing else to do. + container + ;; If not found, detach shape and all children. + ;; container + (do + (vswap! count inc) + (detach-shape container shape)))))))) + container)) - (-> file-data - (update :pages-index update-vals fix-container) - (d/update-when :components update-vals fix-container)))) + fix-container + (fn [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 (fn [file-data] @@ -974,7 +998,7 @@ (-> file-data (fix-file-data) - (fix-page-invalid-options) + (fix-invalid-page) (fix-misc-shape-issues) (fix-recent-colors) (fix-missing-image-metadata) @@ -993,8 +1017,8 @@ (remove-nested-roots) (add-not-nested-roots) (fix-components-without-id) - (remap-refs) (fix-converted-copies) + (remap-refs-recur) (wrap-non-group-component-roots) (detach-non-group-instance-roots) (transform-to-frames) diff --git a/backend/src/app/rpc/climit.clj b/backend/src/app/rpc/climit.clj index 988fe29ad..3c23a7402 100644 --- a/backend/src/app/rpc/climit.clj +++ b/backend/src/app/rpc/climit.clj @@ -21,7 +21,6 @@ [app.worker :as-alias wrk] [clojure.edn :as edn] [clojure.spec.alpha :as s] - [cuerdas.core :as str] [datoteka.fs :as fs] [integrant.core :as ig] [promesa.exec :as px] @@ -241,16 +240,15 @@ [{:keys [::label ::profile-id ::rpc/climit ::mtx/metrics] :as cfg} f] (let [config (get climit ::config) cache (get climit ::cache)] - (reduce (fn [handler [limit-id limit-key :as ckey]] - (let [config (get config limit-id)] - (when-not config - (throw (IllegalArgumentException. - (str/ffmt "config not found for: %" limit-id)))) - + (if-let [config (get config limit-id)] (fn [& params] (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 (get-limits cfg)))) diff --git a/backend/src/app/rpc/commands/projects.clj b/backend/src/app/rpc/commands/projects.clj index b8a555f44..caa3fe7a0 100644 --- a/backend/src/app/rpc/commands/projects.clj +++ b/backend/src/app/rpc/commands/projects.clj @@ -190,8 +190,8 @@ {:project-id (:id project) :profile-id profile-id :team-id team-id - :is-pinned true}) - (assoc project :is-pinned true)))) + :is-pinned false}) + (assoc project :is-pinned false)))) ;; --- MUTATION: Toggle Project Pin diff --git a/backend/src/app/srepl/fixes.clj b/backend/src/app/srepl/fixes.clj index 2d50bd8a8..ee40421df 100644 --- a/backend/src/app/srepl/fixes.clj +++ b/backend/src/app/srepl/fixes.clj @@ -179,7 +179,7 @@ component-child (first component-children)] (if (or (nil? child) (nil? component-child)) 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)) (ctk/instance-head? child)) (let [slot (guess-swap-slot component-child component-container)] diff --git a/common/src/app/common/features.cljc b/common/src/app/common/features.cljc index f4751da8f..90b27587f 100644 --- a/common/src/app/common/features.cljc +++ b/common/src/app/common/features.cljc @@ -56,7 +56,9 @@ ;; migrations process, so all features referenced in migrations should ;; be here. (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 ;; and disabled freely by the user any time. This features does not diff --git a/common/src/app/common/files/changes_builder.cljc b/common/src/app/common/files/changes_builder.cljc index d219fc365..b0ab2b5bd 100644 --- a/common/src/app/common/files/changes_builder.cljc +++ b/common/src/app/common/files/changes_builder.cljc @@ -732,7 +732,8 @@ :main-instance-id (:main-instance-id new-component) :main-instance-page (:main-instance-page 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 :id id :name (:name prev-component) diff --git a/common/src/app/common/files/migrations.cljc b/common/src/app/common/files/migrations.cljc index 8ba085780..b62521b8f 100644 --- a/common/src/app/common/files/migrations.cljc +++ b/common/src/app/common/files/migrations.cljc @@ -73,7 +73,7 @@ [{:keys [version] :as file}] (if (int? version) file - (let [version (or version (-> file :data :version))] + (let [version (or (-> file :data :version) 0)] (-> file (assoc :version version) (update :data dissoc :version))))) @@ -853,11 +853,11 @@ (defn migrate-up-44 [data] (letfn [(fix-shadow [shadow] - (if (string? (:color shadow)) - (let [color {:color (:color shadow) - :opacity 1}] - (assoc shadow :color color)) - shadow)) + (let [color (if (string? (:color shadow)) + {:color (:color shadow) + :opacity 1} + (d/without-nils (:color shadow)))] + (assoc shadow :color color))) (update-object [object] (d/update-when object :shadow diff --git a/common/src/app/common/flags.cljc b/common/src/app/common/flags.cljc index ee86f729a..93b88f87e 100644 --- a/common/src/app/common/flags.cljc +++ b/common/src/app/common/flags.cljc @@ -12,9 +12,7 @@ (def default "A common flags that affects both: backend and frontend." [:enable-registration - :enable-login-with-password - :enable-login-illustration - :enable-feature-styles-v2]) + :enable-login-with-password]) (defn parse [& flags] @@ -35,5 +33,3 @@ :else (recur (rest flags) result))))))) - - diff --git a/common/src/app/common/geom/shapes/bounds.cljc b/common/src/app/common/geom/shapes/bounds.cljc index 6935c0d97..5612837b4 100644 --- a/common/src/app/common/geom/shapes/bounds.cljc +++ b/common/src/app/common/geom/shapes/bounds.cljc @@ -190,3 +190,8 @@ (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?}))) diff --git a/common/src/app/common/svg.cljc b/common/src/app/common/svg.cljc index d400f01f1..1b50a9dde 100644 --- a/common/src/app/common/svg.cljc +++ b/common/src/app/common/svg.cljc @@ -995,6 +995,9 @@ (= key :style) attrs + (= key :unicode) + attrs + (str/starts-with? (d/name key) "data-") attrs diff --git a/common/src/app/common/svg/shapes_builder.cljc b/common/src/app/common/svg/shapes_builder.cljc index 1796d08a5..619a81b94 100644 --- a/common/src/app/common/svg/shapes_builder.cljc +++ b/common/src/app/common/svg/shapes_builder.cljc @@ -383,13 +383,16 @@ (update :svg-attrs dissoc :fill) (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-in [:svg-attrs :style] dissoc :fillOpacity) (assoc-in [:fills 0 :fill-opacity] (-> (dm/get-in shape [:svg-attrs :fillOpacity]) (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 :svg-attrs dissoc :fillOpacity) (assoc-in [:fills 0 :fill-opacity] (-> (dm/get-in shape [:svg-attrs :style :fillOpacity]) diff --git a/common/src/app/common/types/component.cljc b/common/src/app/common/types/component.cljc index 205454d71..8491462b3 100644 --- a/common/src/app/common/types/component.cljc +++ b/common/src/app/common/types/component.cljc @@ -138,10 +138,10 @@ (= (:component-file shape) file-id))) (defn is-main-of? - [shape-main shape-inst] - (and (:shape-ref shape-inst) - (or (= (:shape-ref shape-inst) (:id shape-main)) - (= (:shape-ref shape-inst) (:shape-ref shape-main))))) + [shape-main shape-inst components-v2] + (or (= (:shape-ref shape-inst) (:id shape-main)) + (and (= (:shape-ref shape-inst) (:shape-ref shape-main)) + (not components-v2)))) (defn main-instance? "Check if this shape is the root of the main instance of some diff --git a/common/src/app/common/types/components_list.cljc b/common/src/app/common/types/components_list.cljc index 1eefe3021..8165c2d23 100644 --- a/common/src/app/common/types/components_list.cljc +++ b/common/src/app/common/types/components_list.cljc @@ -48,7 +48,7 @@ (wrap-object-fn))))))) (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*] (d/update-in-when file-data [:components id] (fn [component] @@ -69,6 +69,9 @@ (some? objects) (assoc :objects objects) + (some? modified-at) + (assoc :modified-at modified-at) + (some? annotation) (assoc :annotation annotation) @@ -76,7 +79,7 @@ (dissoc :annotation)) diff (set/difference (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) new-comp diff --git a/common/src/app/common/types/container.cljc b/common/src/app/common/types/container.cljc index ee2be1ae6..92f2969e0 100644 --- a/common/src/app/common/types/container.cljc +++ b/common/src/app/common/types/container.cljc @@ -154,6 +154,17 @@ :else (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 "Get all component heads that are ancestors of the shape, in top-down order (include self if it's also a head)." @@ -170,6 +181,20 @@ (filter #(and (ctk/instance-head? %) (ctk/in-component-copy? %))) (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 "Get the parent shape at the top of the component instance (main or copy)." [objects shape] diff --git a/common/src/app/common/types/file.cljc b/common/src/app/common/types/file.cljc index 3afb89c24..12775f322 100644 --- a/common/src/app/common/types/file.cljc +++ b/common/src/app/common/types/file.cljc @@ -216,14 +216,14 @@ (some find-ref-shape-in-head (ctn/get-parent-heads (:objects container) shape)))) -(defn find-original-ref-shape - "Recursively call to find-ref-shape until find the original shape of the original component" - [file container libraries shape & options] +(defn advance-shape-ref + "Get the shape-ref of the near main of the shape, recursively repeated as many times + as the given levels." + [file container libraries shape levels & options] (let [ref-shape (find-ref-shape file container libraries shape options)] - (if (nil? (:shape-ref ref-shape)) - ref-shape - (find-original-ref-shape file container libraries ref-shape options)))) - + (if (or (nil? (:shape-ref ref-shape)) (not (pos? levels))) + (:id ref-shape) + (advance-shape-ref file container libraries ref-shape (dec levels) options)))) (defn find-ref-component "Locate the nearest component in the local file or libraries that is referenced by the diff --git a/frontend/resources/styles/common/refactor/design-tokens.scss b/frontend/resources/styles/common/refactor/design-tokens.scss index a1527bf05..0088956e4 100644 --- a/frontend/resources/styles/common/refactor/design-tokens.scss +++ b/frontend/resources/styles/common/refactor/design-tokens.scss @@ -237,7 +237,7 @@ --assets-item-border-color: var(--color-accent-primary); --assets-item-background-color-drag: transparent; --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-border-color: var(--color-background-tertiary); --assets-component-border-selected: var(--color-accent-tertiary); @@ -419,8 +419,6 @@ } .light { - --assets-component-background-color: var(--color-background-secondary); - --tabs-background-color: var(--color-background-tertiary); --tab-background-color-selected: var(--color-background-primary); --tab-border-color: var(--color-background-tertiary); diff --git a/frontend/resources/styles/common/refactor/mixins.scss b/frontend/resources/styles/common/refactor/mixins.scss index 992380e57..1a9c06ab8 100644 --- a/frontend/resources/styles/common/refactor/mixins.scss +++ b/frontend/resources/styles/common/refactor/mixins.scss @@ -4,6 +4,18 @@ // // 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 { display: flex; justify-content: center; diff --git a/frontend/resources/styles/main-default.scss b/frontend/resources/styles/main-default.scss index 48c342b70..0f4f8e621 100644 --- a/frontend/resources/styles/main-default.scss +++ b/frontend/resources/styles/main-default.scss @@ -33,34 +33,20 @@ //################################################# @import "common/base"; -@import "main/layouts/login"; @import "main/layouts/main-layout"; @import "main/layouts/not-found"; -@import "main/layouts/viewer"; -@import "main/layouts/inspect"; //################################################# // Commons //################################################# @import "common/framework"; -@import "main/partials/forms"; @import "main/partials/texts"; -@import "main/partials/context-menu"; -@import "main/partials/dropdown"; //################################################# // Partials //################################################# -@import "main/partials/activity-bar"; @import "main/partials/debug-icons-preview"; -@import "main/partials/editable-label"; @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/exception-page"; -@import "main/partials/signup-questions"; diff --git a/frontend/resources/styles/main/layouts/inspect.scss b/frontend/resources/styles/main/layouts/inspect.scss deleted file mode 100644 index bbf4d1556..000000000 --- a/frontend/resources/styles/main/layouts/inspect.scss +++ /dev/null @@ -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; - } -} diff --git a/frontend/resources/styles/main/layouts/login.scss b/frontend/resources/styles/main/layouts/login.scss deleted file mode 100644 index cee660c18..000000000 --- a/frontend/resources/styles/main/layouts/login.scss +++ /dev/null @@ -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; - } - } -} diff --git a/frontend/resources/styles/main/layouts/viewer.scss b/frontend/resources/styles/main/layouts/viewer.scss deleted file mode 100644 index 840c2b559..000000000 --- a/frontend/resources/styles/main/layouts/viewer.scss +++ /dev/null @@ -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); - } -} diff --git a/frontend/resources/styles/main/partials/activity-bar.scss b/frontend/resources/styles/main/partials/activity-bar.scss deleted file mode 100644 index 5e5ad957b..000000000 --- a/frontend/resources/styles/main/partials/activity-bar.scss +++ /dev/null @@ -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; - } - } - } -} diff --git a/frontend/resources/styles/main/partials/context-menu.scss b/frontend/resources/styles/main/partials/context-menu.scss deleted file mode 100644 index 3f4e3f0dd..000000000 --- a/frontend/resources/styles/main/partials/context-menu.scss +++ /dev/null @@ -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; - } -} diff --git a/frontend/resources/styles/main/partials/dropdown.scss b/frontend/resources/styles/main/partials/dropdown.scss deleted file mode 100644 index ed3a2817f..000000000 --- a/frontend/resources/styles/main/partials/dropdown.scss +++ /dev/null @@ -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; - } - } -} diff --git a/frontend/resources/styles/main/partials/editable-label.scss b/frontend/resources/styles/main/partials/editable-label.scss deleted file mode 100644 index db2d42a89..000000000 --- a/frontend/resources/styles/main/partials/editable-label.scss +++ /dev/null @@ -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; - } -} diff --git a/frontend/resources/styles/main/partials/exception-page.scss b/frontend/resources/styles/main/partials/exception-page.scss deleted file mode 100644 index 30686023a..000000000 --- a/frontend/resources/styles/main/partials/exception-page.scss +++ /dev/null @@ -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; - } - } -} diff --git a/frontend/resources/styles/main/partials/forms.scss b/frontend/resources/styles/main/partials/forms.scss deleted file mode 100644 index 4d42d4c31..000000000 --- a/frontend/resources/styles/main/partials/forms.scss +++ /dev/null @@ -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; - } - } -} diff --git a/frontend/resources/styles/main/partials/project-bar.scss b/frontend/resources/styles/main/partials/project-bar.scss deleted file mode 100644 index dc8e76130..000000000 --- a/frontend/resources/styles/main/partials/project-bar.scss +++ /dev/null @@ -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; - } - } - } - } -} diff --git a/frontend/resources/styles/main/partials/sidebar.scss b/frontend/resources/styles/main/partials/sidebar.scss deleted file mode 100644 index 241682f4c..000000000 --- a/frontend/resources/styles/main/partials/sidebar.scss +++ /dev/null @@ -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; - } - } - } -} diff --git a/frontend/resources/styles/main/partials/signup-questions.scss b/frontend/resources/styles/main/partials/signup-questions.scss deleted file mode 100644 index 373cae531..000000000 --- a/frontend/resources/styles/main/partials/signup-questions.scss +++ /dev/null @@ -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; - } - } - } -} diff --git a/frontend/resources/styles/main/partials/tab-container.scss b/frontend/resources/styles/main/partials/tab-container.scss deleted file mode 100644 index 33c759e0a..000000000 --- a/frontend/resources/styles/main/partials/tab-container.scss +++ /dev/null @@ -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%; -} diff --git a/frontend/resources/styles/main/partials/user-settings.scss b/frontend/resources/styles/main/partials/user-settings.scss deleted file mode 100644 index 8fd19383f..000000000 --- a/frontend/resources/styles/main/partials/user-settings.scss +++ /dev/null @@ -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; - } - } -} diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index da8ac8eab..e2bf369b6 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -25,6 +25,7 @@ [app.common.types.component :as ctk] [app.common.types.components-list :as ctkl] [app.common.types.container :as ctn] + [app.common.types.file :as ctf] [app.common.types.shape :as cts] [app.common.types.shape-tree :as ctst] [app.common.types.shape.layout :as ctl] @@ -1598,6 +1599,34 @@ (let [frame (get objects parent-frame-id)] (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] (js/console.error "clipboard blocked:" error) (rx/empty))] @@ -1636,6 +1665,7 @@ (rx/merge-map (partial prepare-object objects frame-id)) (rx/reduce collect-data initial) (rx/map (partial sort-selected state)) + (rx/map (partial advance-copies state selected)) (rx/map #(t/encode-str % {:type :json-verbose})) (rx/map wapi/write-to-clipboard) (rx/catch on-copy-error) diff --git a/frontend/src/app/main/data/workspace/common.cljs b/frontend/src/app/main/data/workspace/common.cljs index 063c1e50a..d140bdb6b 100644 --- a/frontend/src/app/main/data/workspace/common.cljs +++ b/frontend/src/app/main/data/workspace/common.cljs @@ -168,13 +168,6 @@ ;; 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 [] (ptk/reify ::hide-toolbar @@ -188,3 +181,10 @@ ptk/UpdateEvent (update [_ state] (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)))) diff --git a/frontend/src/app/main/data/workspace/layout.cljs b/frontend/src/app/main/data/workspace/layout.cljs index 1c7582a59..157de23f7 100644 --- a/frontend/src/app/main/data/workspace/layout.cljs +++ b/frontend/src/app/main/data/workspace/layout.cljs @@ -89,7 +89,6 @@ (update [_ state] (update state :workspace-layout (fn [flags] - (prn flags) (if force? (conj flags flag) (if (contains? flags flag) diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 2f0961b78..6f08743c9 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -706,6 +706,38 @@ (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 "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, @@ -726,23 +758,26 @@ components-v2 (features/active-feature? state "components/v2") - shape-inst (ctn/get-shape container id) swap-slot (-> (ctn/get-shape container id) (ctk/get-swap-slot)) + + undo-id (js/Symbol) + changes (-> (pcb/empty-changes it) (pcb/with-container container) (pcb/with-objects (:objects container)) - (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)))] + (dwlh/generate-sync-shape-direct file-full libraries container id true components-v2))] (log/debug :msg "RESET-COMPONENT finished" :js/rchanges (log-changes (:redo-changes changes) 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 "Cancels all modifications in the shapes with the given ids" @@ -1186,6 +1221,26 @@ :callback do-update}] :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 "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." @@ -1197,6 +1252,7 @@ ptk/WatchEvent (watch [_ _ _] (rx/of + (touch-component component-id) (launch-component-sync component-id file-id undo-group))))) (defn watch-component-changes @@ -1244,13 +1300,18 @@ (map (partial ch/components-changed old-data)) (reduce into #{})))] - (if (and (d/not-empty? changed-components) save-undo?) - (do (log/info :msg "DETECTED COMPONENTS CHANGED" - :ids (map str changed-components) - :undo-group undo-group) + (if (d/not-empty? changed-components) + (if save-undo? + (do (log/info :msg "DETECTED COMPONENTS CHANGED" + :ids (map str changed-components) + :undo-group undo-group) - (->> (rx/from changed-components) - (rx/map #(component-changed % (:id old-data) undo-group)))) + (->> (rx/from changed-components) + (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))))) changes-s diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index b3aa937db..f6de7fb46 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -759,7 +759,8 @@ root-inst root-main omit-touched? - set-remote-synced?) + set-remote-synced? + components-v2) changes)) both (fn [changes child-inst child-main] @@ -813,7 +814,8 @@ swapped moved false - reset?)))) + reset? + components-v2)))) (defn- generate-rename-component @@ -948,7 +950,8 @@ component-container container root-inst - root-main)) + root-main + components-v2)) only-main (fn [changes child-main] (remove-shape changes @@ -1001,7 +1004,8 @@ swapped moved true - true) + true + components-v2) ;; The inverse sync may be made on a component that is inside a ;; remote library. We need to separate changes that are from @@ -1019,7 +1023,7 @@ ;; ---- Operation generation helpers ---- (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") (loop [children-inst (seq (or children-inst [])) children-main (seq (or children-main [])) @@ -1039,18 +1043,18 @@ (reduce only-inst-cb changes children-inst) :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?))) (recur (next children-inst) (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) (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?))) 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?))) children-main)] (cond @@ -1066,7 +1070,7 @@ :else (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) (remove #(= (:id %) (:id child-main')) children-main) (cond-> changes @@ -1076,7 +1080,7 @@ (swapped-cb child-inst child-main') :always (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) (next children-main) (cond-> changes @@ -1088,13 +1092,13 @@ (moved-cb child-inst' child-main)))))))))))) (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)) "] " (:name component-shape) " " (pretty-uuid (: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) (:id root-instance))) all-parents (into [(:id parent-shape)] @@ -1163,13 +1167,13 @@ changes'))) (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)) "] " (:name shape) " " (pretty-uuid (: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) (:id root-main))) all-parents (into [(:id component-parent-shape)] diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs index b552bee67..302f3e6b2 100644 --- a/frontend/src/app/main/data/workspace/modifiers.cljs +++ b/frontend/src/app/main/data/workspace/modifiers.cljs @@ -546,10 +546,7 @@ :layout-padding-type :layout-gap :layout-item-margin - :layout-item-margin-type - :layout-grid-cells - :layout-grid-columns - :layout-grid-rows]}) + :layout-item-margin-type]}) ;; We've applied the text-modifier so we can dissoc the temporary data (fn [state] (update state :workspace-text-modifier #(apply dissoc % ids))) diff --git a/frontend/src/app/main/data/workspace/path/shortcuts.cljs b/frontend/src/app/main/data/workspace/path/shortcuts.cljs index 653b6659e..07fb3b1fa 100644 --- a/frontend/src/app/main/data/workspace/path/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/path/shortcuts.cljs @@ -22,13 +22,9 @@ (defn esc-pressed [] (ptk/reify ::esc-pressed ptk/WatchEvent - (watch [_ state _] + (watch [_ _ _] ;; Not interrupt when we're editing a path - (let [edition-id (or (get-in state [:workspace-drawing :object :id]) - (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)))))) + (rx/of :interrupt)))) (def shortcuts {:move-nodes {:tooltip "M" diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index 5fa17a631..22ec299c0 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -404,6 +404,7 @@ ids-map %2 delta + nil libraries library-data it @@ -459,10 +460,10 @@ ;; TODO: move to common.files.shape-helpers (defn- prepare-duplicate-shape-change - ([changes objects page unames update-unames! ids-map obj 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)) + ([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 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 (nil? obj) changes @@ -486,11 +487,14 @@ duplicating-component? (or duplicating-component? (ctk/instance-head? 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? (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 (fn [changes shape] (let [components-v2 (dm/get-in library-data [:options :components-v2]) @@ -518,9 +522,9 @@ (cond-> (or frame? group? bool?) (assoc :shapes [])) - (cond-> (and (some? original-ref-shape) - (not= original-ref-shape (:shape-ref obj))) - (assoc :shape-ref original-ref-shape)) + (cond-> (and (some? new-shape-ref) + (not= new-shape-ref (:shape-ref obj))) + (assoc :shape-ref new-shape-ref)) (gsh/move delta) (d/update-when :interactions #(ctsi/remap-interactions % ids-map objects)) @@ -561,6 +565,7 @@ ids-map child delta + level-delta libraries library-data it diff --git a/frontend/src/app/main/data/workspace/shapes.cljs b/frontend/src/app/main/data/workspace/shapes.cljs index 7febfef6f..4fabc4e80 100644 --- a/frontend/src/app/main/data/workspace/shapes.cljs +++ b/frontend/src/app/main/data/workspace/shapes.cljs @@ -353,13 +353,14 @@ (ptk/reify ::create-artboard-from-selection ptk/WatchEvent (watch [it state _] - (let [page-id (:current-page-id state) - objects (wsh/lookup-page-objects state page-id) - selected (wsh/lookup-selected state) - selected (cfh/clean-loops objects selected) + (let [page-id (:current-page-id state) + objects (wsh/lookup-page-objects state page-id) + selected (->> (wsh/lookup-selected state) + (cfh/clean-loops objects) + (remove #(ctn/has-any-copy-parent? objects (get objects %)))) - changes (-> (pcb/empty-changes it page-id) - (pcb/with-objects objects)) + changes (-> (pcb/empty-changes it page-id) + (pcb/with-objects objects)) [frame-shape changes] (cfsh/prepare-create-artboard-from-selection changes diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index c52cd2fbb..15622d4dd 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -606,11 +606,11 @@ (->> move-stream (rx/last) (rx/mapcat - (fn [[_ target-frame drop-index]] + (fn [[_ target-frame drop-index cell-data]] (let [undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) - (move-shapes-to-frame ids target-frame drop-index) (dwm/apply-modifiers {:undo-transation? false}) + (move-shapes-to-frame ids target-frame drop-index cell-data) (finish-transform) (dwu/commit-undo-transaction undo-id)))))))))))))) @@ -832,7 +832,7 @@ :ignore-snap-pixel true})))))) (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/WatchEvent (watch [it state _] @@ -905,6 +905,11 @@ moving-shapes-ids (map :id moving-shapes) + moving-shapes-children-ids + (->> moving-shapes + (mapcat #(cfh/get-children-with-self objects (:id %))) + (map :id)) + changes (-> (pcb/empty-changes it page-id) (pcb/with-objects objects) @@ -913,7 +918,7 @@ (pcb/update-shapes moving-shapes-ids ctl/remove-layout-item-data)) ;; Remove component-root property when moving a shape inside a component (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 (cond-> (not (ctn/get-instance-root objects frame)) (pcb/update-shapes moving-shapes-ids (fn [shape] @@ -924,7 +929,16 @@ (pcb/update-shapes shape-ids-to-detach ctk/detach-shape) (pcb/change-parent frame-id moving-shapes drop-index) (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/remove-objects empty-parents))] diff --git a/frontend/src/app/main/render.cljs b/frontend/src/app/main/render.cljs index 60fc6f6be..16c961b5d 100644 --- a/frontend/src/app/main/render.cljs +++ b/frontend/src/app/main/render.cljs @@ -625,7 +625,7 @@ (if (some? shape) (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") (or (:background options) (dom/get-css-variable "--assets-component-background-color") "#fff")) diff --git a/frontend/src/app/main/ui/auth.cljs b/frontend/src/app/main/ui/auth.cljs index fe8473252..61fe0271b 100644 --- a/frontend/src/app/main/ui/auth.cljs +++ b/frontend/src/app/main/ui/auth.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.auth (:require-macros [app.main.style :as stl]) (:require + [app.common.data.macros :as dm] [app.config :as cf] [app.main.ui.auth.login :refer [login-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")])]))) (mf/defc auth - [{:keys [route] :as props}] - (let [section (get-in route [:data :name]) - params (:query-params route) - show-illustration? (contains? cf/flags :login-illustration)] + {::mf/props :obj} + [{:keys [route]}] + (let [section (dm/get-in route [:data :name]) + params (:query-params route)] - (mf/use-effect - #(dom/set-html-title (tr "title.default"))) + (mf/with-effect [] + (dom/set-html-title (tr "title.default"))) - [:main {:class (stl/css-case :auth-section true - :no-illustration (not show-illustration?))} - (when show-illustration? - [:div {:class (stl/css :login-illustration)} - i/login-illustration]) + [:main {:class (stl/css :auth-section)} + [:div {:class (stl/css :login-illustration)} + i/login-illustration] [:section {:class (stl/css :auth-content)} - [:* - [:a {:href "#/" :class (stl/css :logo-btn)} i/logo] - (case section - :auth-register - [:& register-page {:params params}] + [:a {:href "#/" :class (stl/css :logo-btn)} i/logo] + (case section + :auth-register + [:& register-page {:params params}] - :auth-register-validate - [:& register-validate-page {:params params}] + :auth-register-validate + [:& register-validate-page {:params params}] - :auth-register-success - [:& register-success-page {:params params}] + :auth-register-success + [:& register-success-page {:params params}] - :auth-login - [:& login-page {:params params}] + :auth-login + [:& login-page {:params params}] - :auth-recovery-request - [:& recovery-request-page] + :auth-recovery-request + [:& recovery-request-page] - :auth-recovery - [:& recovery-page {:params params}])] + :auth-recovery + [:& recovery-page {:params params}]) - (when (contains? #{:auth-login :auth-register} section) + (when (or (= section :auth-login) + (= section :auth-register)) [:& terms-login])]])) diff --git a/frontend/src/app/main/ui/auth.scss b/frontend/src/app/main/ui/auth.scss index 2e5d45fc7..f993d5b7f 100644 --- a/frontend/src/app/main/ui/auth.scss +++ b/frontend/src/app/main/ui/auth.scss @@ -17,11 +17,6 @@ width: 100%; overflow: auto; - &.no-illustration { - display: flex; - justify-content: center; - } - @media (max-width: 992px) { display: flex; justify-content: center; diff --git a/frontend/src/app/main/ui/auth/login.cljs b/frontend/src/app/main/ui/auth/login.cljs index b4d02fb11..cfd9d1d11 100644 --- a/frontend/src/app/main/ui/auth/login.cljs +++ b/frontend/src/app/main/ui/auth/login.cljs @@ -35,6 +35,14 @@ :login-with-gitlab :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 [event provider params] (dom/prevent-default event) @@ -284,6 +292,9 @@ [:h1 {:class (stl/css :auth-title) :data-test "login-title"} (tr "auth.login-title")] + (when (contains? cf/flags :demo-warning) + [:& demo-warning]) + [:hr {:class (stl/css :separator)}] [:& login-methods {:params params}] diff --git a/frontend/src/app/main/ui/auth/register.cljs b/frontend/src/app/main/ui/auth/register.cljs index 6789b99dc..1143d5928 100644 --- a/frontend/src/app/main/ui/auth/register.cljs +++ b/frontend/src/app/main/ui/auth/register.cljs @@ -18,20 +18,12 @@ [app.main.ui.components.forms :as fm] [app.main.ui.components.link :as lk] [app.main.ui.icons :as i] - [app.main.ui.notifications.context-notification :refer [context-notification]] [app.util.i18n :refer [tr tr-html]] [app.util.router :as rt] [beicon.v2.core :as rx] [cljs.spec.alpha :as s] [rumext.v2 :as mf])) -(mf/defc demo-warning - [_] - [:div {:class (stl/css :banner)} - [:& context-notification - {:type :warning - :content (tr "auth.demo-warning")}]]) - ;; --- PAGE: Register (defn- validate @@ -86,7 +78,7 @@ (st/emit! (rt/nav :auth-register-validate {} params))) (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)) form (fm/use-form :spec ::register-form :validators [validate @@ -136,7 +128,8 @@ (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? [:* @@ -146,14 +139,15 @@ [:& register-form {:params params :on-success-callback on-success-callback}]]) (mf/defc register-page - [{:keys [params] :as props}] + {::mf/props :obj} + [{:keys [params]}] [:div {:class (stl/css :auth-form)} [:h1 {:class (stl/css :auth-title) :data-test "registration-title"} (tr "auth.register-title")] [:div {:class (stl/css :auth-subtitle)} (tr "auth.register-subtitle")] (when (contains? cf/flags :demo-warning) - [:& demo-warning]) + [:& login/demo-warning]) [:& register-methods {:params params}] @@ -212,7 +206,7 @@ ::accept-newsletter-subscription]))) (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 :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"))] @@ -263,7 +257,7 @@ (mf/defc register-validate-page - [{:keys [params] :as props}] + [{:keys [params]}] [:div {:class (stl/css :auth-form)} [:h1 {:class (stl/css :auth-title) :data-test "register-title"} (tr "auth.register-title")] @@ -279,7 +273,7 @@ (tr "labels.go-back")]]]]) (mf/defc register-success-page - [{:keys [params] :as props}] + [{:keys [params]}] [:div {:class (stl/css :auth-form :register-success)} [:div {:class (stl/css :notification-icon)} i/icon-verify] [:div {:class (stl/css :notification-text)} (tr "auth.verification-email-sent")] diff --git a/frontend/src/app/main/ui/dashboard/grid.cljs b/frontend/src/app/main/ui/dashboard/grid.cljs index 276fa7ce4..dfffc9c66 100644 --- a/frontend/src/app/main/ui/dashboard/grid.cljs +++ b/frontend/src/app/main/ui/dashboard/grid.cljs @@ -17,7 +17,7 @@ [app.main.fonts :as fonts] [app.main.rasterizer :as thr] [app.main.refs :as refs] - [app.main.render :refer [component-svg]] + [app.main.render :as render] [app.main.repo :as rp] [app.main.store :as st] [app.main.ui.components.color-bullet :as bc] @@ -49,8 +49,7 @@ (->> (rp/cmd! :create-file-thumbnail params) (rx/map :uri)))) -(defn- ask-for-thumbnail - "Creates some hooks to handle the files thumbnails cache" +(defn render-thumbnail [file-id revn] (->> (wrk/ask! {:cmd :thumbnails/generate-for-file :revn revn @@ -61,7 +60,12 @@ (rx/map (fn [styles] (assoc result :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 (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 [:div {:class (stl/css :asset-list-item) :key (str "assets-component-" (:id component))} - [:& component-svg {:root-shape (get-in component [:objects root-id]) - :objects (:objects component)}] ;; Components in the summary come loaded with objects, even in v2 + [:& 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 [:div {:class (stl/css :name-block)} [:span {:class (stl/css :item-name) :title (:name component)} diff --git a/frontend/src/app/main/ui/dashboard/import.scss b/frontend/src/app/main/ui/dashboard/import.scss index a89dda6da..7708be6ef 100644 --- a/frontend/src/app/main/ui/dashboard/import.scss +++ b/frontend/src/app/main/ui/dashboard/import.scss @@ -12,6 +12,8 @@ .modal-container { @extend .modal-container-base; + display: flex; + flex-direction: column; } .modal-header { @@ -29,6 +31,9 @@ .modal-content { @include bodySmallTypography; + flex: 1; + overflow-y: auto; + overflow-x: hidden; display: grid; grid-template-columns: 1fr; gap: $s-16; diff --git a/frontend/src/app/main/ui/dashboard/projects.cljs b/frontend/src/app/main/ui/dashboard/projects.cljs index 97667fef6..fc0a51f97 100644 --- a/frontend/src/app/main/ui/dashboard/projects.cljs +++ b/frontend/src/app/main/ui/dashboard/projects.cljs @@ -378,6 +378,7 @@ tutorial-viewed? (:viewed-tutorial? props true) walkthrough-viewed? (:viewed-walkthrough? props true) + is-my-penpot (= (:default-team-id profile) (:id team)) team-id (:id team) @@ -387,6 +388,7 @@ (st/emit! (du/update-profile-props {:team-hero? false}) (ptk/data-event ::ev/event {::ev/name "dont-show-team-up-hero" ::ev/origin "dashboard"})))) + close-tutorial (mf/use-fn (fn [] @@ -395,6 +397,7 @@ ::ev/origin "get-started-hero" :type "tutorial" :section "dashboard"})))) + close-walkthrough (mf/use-fn (fn [] @@ -402,7 +405,13 @@ (ptk/data-event ::ev/event {::ev/name "dont-show-walkthrough" ::ev/origin "get-started-hero" :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] (let [tname (if (:is-default team) @@ -423,8 +432,7 @@ [:& team-hero {:team team :close-fn close-banner}]) (when (and (contains? cf/flags :dashboard-templates-section) - (or (not tutorial-viewed?) - (not walkthrough-viewed?))) + show-hero?) [:div {:class (stl/css :hero-projects)} (when (and (not tutorial-viewed?) (:is-default team)) [:& tutorial-project @@ -435,7 +443,11 @@ [:& interface-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] (let [files (when recent-map (->> (vals recent-map) diff --git a/frontend/src/app/main/ui/dashboard/projects.scss b/frontend/src/app/main/ui/dashboard/projects.scss index 105dabd50..eb73b2e89 100644 --- a/frontend/src/app/main/ui/dashboard/projects.scss +++ b/frontend/src/app/main/ui/dashboard/projects.scss @@ -17,6 +17,12 @@ .dashboard-projects { user-select: none; + height: calc(100vh - $s-64); +} + +.with-hero, +.with-team-hero { + height: calc(100vh - $s-280); } .dashboard-shared { diff --git a/frontend/src/app/main/ui/dashboard/sidebar.scss b/frontend/src/app/main/ui/dashboard/sidebar.scss index 1a5c627e6..f939c8d44 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.scss +++ b/frontend/src/app/main/ui/dashboard/sidebar.scss @@ -339,7 +339,7 @@ .profile-fullname { @include smallTitleTipography; - @include text-ellipsis; + @include textEllipsis; align-self: center; max-width: $s-160; color: var(--profile-foreground-color); diff --git a/frontend/src/app/main/ui/dashboard/templates.cljs b/frontend/src/app/main/ui/dashboard/templates.cljs index 7fb943a88..7d2dd5a87 100644 --- a/frontend/src/app/main/ui/dashboard/templates.cljs +++ b/frontend/src/app/main/ui/dashboard/templates.cljs @@ -118,6 +118,7 @@ :id id :data-index index :on-click on-click + :on-mouse-down dom/prevent-default :on-key-down on-key-down} [:div {:class (stl/css :template-card)} [:div {:class (stl/css :img-container)} diff --git a/frontend/src/app/main/ui/releases.cljs b/frontend/src/app/main/ui/releases.cljs index 50f87648f..562c1eab2 100644 --- a/frontend/src/app/main/ui/releases.cljs +++ b/frontend/src/app/main/ui/releases.cljs @@ -91,4 +91,4 @@ (defmethod rc/render-release-notes "0.0" [params] - (rc/render-release-notes (assoc params :version "1.21"))) + (rc/render-release-notes (assoc params :version "2.0"))) diff --git a/frontend/src/app/main/ui/releases/v2_0.cljs b/frontend/src/app/main/ui/releases/v2_0.cljs index 116a4c172..0c3af3060 100644 --- a/frontend/src/app/main/ui/releases/v2_0.cljs +++ b/frontend/src/app/main/ui/releases/v2_0.cljs @@ -11,10 +11,6 @@ [app.main.ui.releases.common :as c] [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 (defmethod c/render-release-notes "2.0" [{:keys [slide klass next finish navigate version]}] diff --git a/frontend/src/app/main/ui/settings/change_email.cljs b/frontend/src/app/main/ui/settings/change_email.cljs index 5543f7d18..daeaf8bf8 100644 --- a/frontend/src/app/main/ui/settings/change_email.cljs +++ b/frontend/src/app/main/ui/settings/change_email.cljs @@ -39,8 +39,8 @@ (s/keys :req-un [::email-1 ::email-2])) (defn- on-error - [form {:keys [code] :as error}] - (case code + [form error] + (case (:code (ex-data error)) :email-already-exists (swap! form (fn [data] (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) email-1 (dma/get-in @form [:clean-data :email-1]) 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)) (swap! form d/dissoc-in [:errors :email-2])))))] diff --git a/frontend/src/app/main/ui/shapes/attrs.cljs b/frontend/src/app/main/ui/shapes/attrs.cljs index 5c148d26d..15f99ddf4 100644 --- a/frontend/src/app/main/ui/shapes/attrs.cljs +++ b/frontend/src/app/main/ui/shapes/attrs.cljs @@ -62,32 +62,34 @@ (obj/merge! props (get-border-props shape))) (defn add-fill! - [attrs fill-data render-id index type] - (let [index (if (some? index) (dm/str "-" index) "")] - (cond - (contains? fill-data :fill-image) - (let [id (dm/str "fill-image-" render-id)] - (obj/set! attrs "fill" (dm/str "url(#" id ")"))) + ([attrs fill-data render-id index type] + (add-fill! attrs fill-data render-id index type "none")) + ([attrs fill-data render-id index type fill-default] + (let [index (if (some? index) (dm/str "-" index) "")] + (cond + (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)) - (let [id (dm/str "fill-color-gradient-" render-id index)] - (obj/set! attrs "fill" (dm/str "url(#" id ")"))) + (some? (:fill-color-gradient fill-data)) + (let [id (dm/str "fill-color-gradient-" render-id index)] + (obj/set! attrs "fill" (dm/str "url(#" id ")"))) - (contains? fill-data :fill-color) - (obj/set! attrs "fill" (:fill-color fill-data)) + (contains? fill-data :fill-color) + (obj/set! attrs "fill" (:fill-color fill-data)) - :else - (obj/set! attrs "fill" "none")) + :else + (obj/set! attrs "fill" fill-default)) - (when (contains? fill-data :fill-opacity) - (obj/set! attrs "fillOpacity" (:fill-opacity fill-data))) + (when (contains? fill-data :fill-opacity) + (obj/set! attrs "fillOpacity" (:fill-opacity fill-data))) - (when (and (= :text type) - (nil? (:fill-color-gradient fill-data)) - (nil? (:fill-color fill-data))) - (obj/set! attrs "fill" "black")) + (when (and (= :text type) + (nil? (:fill-color-gradient fill-data)) + (nil? (:fill-color fill-data))) + (obj/set! attrs "fill" "black")) - attrs)) + attrs))) (defn add-stroke! [attrs data render-id index open-path?] @@ -165,8 +167,10 @@ (obj/map->obj))))) (defn get-fill-style - [fill-data index render-id type] - (add-fill! #js {} fill-data render-id index type)) + ([fill-data index render-id 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! ([props shape render-id] @@ -242,8 +246,10 @@ (obj/set! style "fillOpacity" opacity))) ^boolean (d/not-empty? shape-fills) - (let [fill (nth shape-fills 0)] - (obj/merge! style (get-fill-style fill render-id 0 shape-type))) + (let [fill (nth shape-fills 0) + 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) ^boolean (empty? shape-fills)) diff --git a/frontend/src/app/main/ui/shapes/custom_stroke.cljs b/frontend/src/app/main/ui/shapes/custom_stroke.cljs index 43c9d607a..11c430a2d 100644 --- a/frontend/src/app/main/ui/shapes/custom_stroke.cljs +++ b/frontend/src/app/main/ui/shapes/custom_stroke.cljs @@ -476,7 +476,8 @@ svg-attrs (attrs/get-svg-props shape render-id) style (-> (obj/get props "style") - (obj/clone)) + (obj/clone) + (obj/merge! (obj/get svg-attrs "style"))) props (mf/spread-props svg-attrs {:id stroke-id diff --git a/frontend/src/app/main/ui/shapes/frame.cljs b/frontend/src/app/main/ui/shapes/frame.cljs index 970e1ce7f..c907291ad 100644 --- a/frontend/src/app/main/ui/shapes/frame.cljs +++ b/frontend/src/app/main/ui/shapes/frame.cljs @@ -8,8 +8,8 @@ (:require [app.common.data.macros :as dm] [app.common.files.helpers :as cfh] - [app.common.geom.rect :as grc] [app.common.geom.shapes :as gsh] + [app.common.geom.shapes.bounds :as gsb] [app.common.types.shape.layout :as ctl] [app.config :as cf] [app.main.ui.context :as muc] @@ -119,7 +119,7 @@ points (dm/get-prop shape :points) bounds (mf/with-memo [bounds points] - (or bounds (grc/points->rect points))) + (or bounds (gsb/get-frame-bounds shape))) thumb (:thumbnail shape) diff --git a/frontend/src/app/main/ui/viewer/inspect/right_sidebar.scss b/frontend/src/app/main/ui/viewer/inspect/right_sidebar.scss index 694d43e0f..a542a2a1b 100644 --- a/frontend/src/app/main/ui/viewer/inspect/right_sidebar.scss +++ b/frontend/src/app/main/ui/viewer/inspect/right_sidebar.scss @@ -51,7 +51,7 @@ .layer-title { @include bodySmallTypography; - @include text-ellipsis; + @include textEllipsis; height: $s-32; padding: $s-8 0; color: var(--assets-item-name-foreground-color-rest); diff --git a/frontend/src/app/main/ui/viewer/interactions.scss b/frontend/src/app/main/ui/viewer/interactions.scss index 74e7969b0..3cd9751b4 100644 --- a/frontend/src/app/main/ui/viewer/interactions.scss +++ b/frontend/src/app/main/ui/viewer/interactions.scss @@ -36,7 +36,7 @@ width: $s-272; padding: $s-6; max-height: calc(100vh - 3 * ($s-2 + $s-48)); - overflow: scroll; + overflow: auto; } .dropdown-element { diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index 987052774..7f899e947 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -228,48 +228,54 @@ (mf/defc context-menu-group [{:keys [shapes]}] - (let [multiple? (> (count shapes) 1) - single? (= (count shapes) 1) - do-create-artboard-from-selection #(st/emit! (dwsh/create-artboard-from-selection)) + (let [multiple? (> (count shapes) 1) + single? (= (count shapes) 1) + + objects (deref refs/workspace-page-objects) + any-in-copy? (some true? (map #(ctn/has-any-copy-parent? objects %) shapes)) ;; components can't be ungrouped 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-bool? (->> shapes (d/seek cfh/bool-shape?)) - has-mask? (->> shapes (d/seek :masked-group)) + has-bool? (->> shapes (d/seek cfh/bool-shape?)) + has-mask? (->> shapes (d/seek :masked-group)) - is-group? (and single? has-group?) - is-bool? (and single? has-bool?) + is-group? (and single? has-group?) + is-bool? (and single? has-bool?) do-create-group #(st/emit! dw/group-selected) do-mask-group #(st/emit! dw/mask-group) 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?) - [:& menu-entry {:title (tr "workspace.shape.menu.ungroup") - :shortcut (sc/get-tooltip :ungroup) - :on-click do-remove-group}]) + (when (not any-in-copy?) + [:* + (when (or has-bool? has-group? has-mask? has-frame?) + [:& 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") - :shortcut (sc/get-tooltip :group) - :on-click do-create-group}] + [:& menu-entry {:title (tr "workspace.shape.menu.group") + :shortcut (sc/get-tooltip :group) + :on-click do-create-group}] - (when (or multiple? (and is-group? (not has-mask?)) is-bool?) - [:& menu-entry {:title (tr "workspace.shape.menu.mask") - :shortcut (sc/get-tooltip :mask) - :on-click do-mask-group}]) + (when (or multiple? (and is-group? (not has-mask?)) is-bool?) + [:& menu-entry {:title (tr "workspace.shape.menu.mask") + :shortcut (sc/get-tooltip :mask) + :on-click do-mask-group}]) - (when has-mask? - [:& menu-entry {:title (tr "workspace.shape.menu.unmask") - :shortcut (sc/get-tooltip :unmask) - :on-click do-unmask-group}]) + (when has-mask? + [:& menu-entry {:title (tr "workspace.shape.menu.unmask") + :shortcut (sc/get-tooltip :unmask) + :on-click do-unmask-group}]) - [:& menu-entry {:title (tr "workspace.shape.menu.create-artboard-from-selection") - :shortcut (sc/get-tooltip :artboard-selection) - :on-click do-create-artboard-from-selection}] - [:& menu-separator]])) + [:& menu-entry {:title (tr "workspace.shape.menu.create-artboard-from-selection") + :shortcut (sc/get-tooltip :artboard-selection) + :on-click do-create-artboard-from-selection}] + [:& menu-separator]])])) (mf/defc context-focus-mode-menu [{:keys []}] @@ -391,7 +397,9 @@ (mf/defc context-menu-layout {::mf/props :obj} [{: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? (and single? (every? ctl/flex-layout? shapes)) @@ -414,29 +422,30 @@ (fn [_event] (let [ids (map :id shapes)] (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? - ^boolean has-grid?) - [:div - [:& menu-separator] - (if has-flex? - [:& menu-entry {:title (tr "workspace.shape.menu.remove-flex") - :shortcut (sc/get-tooltip :toggle-layout-flex) - :on-click on-remove-layout}] - [:& menu-entry {:title (tr "workspace.shape.menu.remove-grid") - :shortcut (sc/get-tooltip :toggle-layout-grid) - :on-click on-remove-layout}])] - - [:div - [:& menu-separator] - [:& menu-entry {:title (tr "workspace.shape.menu.add-flex") - :shortcut (sc/get-tooltip :toggle-layout-flex) - :value "flex" - :on-click on-add-layout}] - [:& menu-entry {:title (tr "workspace.shape.menu.add-grid") - :shortcut (sc/get-tooltip :toggle-layout-grid) - :value "grid" - :on-click on-add-layout}]]))) + [:div + [:& menu-separator] + [:& menu-entry {:title (tr "workspace.shape.menu.add-flex") + :shortcut (sc/get-tooltip :toggle-layout-flex) + :value "flex" + :on-click on-add-layout}] + [:& menu-entry {:title (tr "workspace.shape.menu.add-grid") + :shortcut (sc/get-tooltip :toggle-layout-grid) + :value "grid" + :on-click on-add-layout}]]))])) (mf/defc context-menu-component [{:keys [shapes]}] diff --git a/frontend/src/app/main/ui/workspace/context_menu.scss b/frontend/src/app/main/ui/workspace/context_menu.scss index b7ea96aa4..66e58c890 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.scss +++ b/frontend/src/app/main/ui/workspace/context_menu.scss @@ -62,14 +62,11 @@ } } - .submenu-icon { - position: absolute; - right: $s-16; - svg { - @extend .button-icon-small; - stroke: var(--menu-foreground-color); - } + .submenu-icon svg { + @extend .button-icon-small; + stroke: var(--menu-foreground-color); } + &:hover { background-color: var(--menu-background-color-hover); .title { diff --git a/frontend/src/app/main/ui/workspace/libraries.scss b/frontend/src/app/main/ui/workspace/libraries.scss index 6c430b0d8..6d5143d73 100644 --- a/frontend/src/app/main/ui/workspace/libraries.scss +++ b/frontend/src/app/main/ui/workspace/libraries.scss @@ -81,11 +81,6 @@ height: fit-content; } -.item-name { - @include bodyLargeTypography; - color: var(--library-name-foreground-color); -} - .item-publish, .item-unpublish { @extend .button-primary; @@ -216,8 +211,11 @@ } .item-name { + @include bodyLargeTypography; @include textEllipsis; margin: 0; + max-width: $s-244; + color: var(--library-name-foreground-color); } .item-update { @@ -254,6 +252,7 @@ .modal-v2-info { width: $s-664; height: fit-content; + max-height: fit-content; } .modal-v2-title { diff --git a/frontend/src/app/main/ui/workspace/right_header.cljs b/frontend/src/app/main/ui/workspace/right_header.cljs index 1fae7d76c..f2e1b53f0 100644 --- a/frontend/src/app/main/ui/workspace/right_header.cljs +++ b/frontend/src/app/main/ui/workspace/right_header.cljs @@ -179,6 +179,10 @@ (mf/use-fn (mf/deps selected-drawtool) (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) (st/emit! :interrupt) (active-comments)))) @@ -187,8 +191,11 @@ (mf/use-fn (mf/deps selected-drawtool) (fn [] + (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) (vary-meta assoc ::ev/origin "workspace-header")))))] diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index cd60ec811..70e46664a 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -130,7 +130,7 @@ container-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) y (dm/get-prop bounds :y) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/colors.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/colors.cljs index 1f0004462..02c3fe55b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/colors.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/colors.cljs @@ -219,7 +219,9 @@ :auto-focus true :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) :on-double-click rename-color-clicked} diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_name.cljs b/frontend/src/app/main/ui/workspace/sidebar/layer_name.cljs index 862b59410..45d0a9015 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layer_name.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layer_name.cljs @@ -109,6 +109,8 @@ :style {"--depth" depth "--parent-size" parent-size} :ref ref :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?) [:span {:class (stl/css :element-name-touched)} "*"])]))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.scss b/frontend/src/app/main/ui/workspace/sidebar/layers.scss index 4c0823ddf..2dbde9595 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.scss @@ -13,10 +13,10 @@ height: $s-32; min-height: $s-32; margin: $s-8 0 $s-4 $s-8; - padding-right: $s-8; + padding-right: $s-12; &.search { - padding: 0 $s-8 0 $s-12; + padding: 0 $s-12 0 $s-8; gap: $s-4; .filter-button { @include flexCenter; diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs index 49c077daa..d5170a8b5 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs @@ -26,6 +26,7 @@ [app.main.ui.hooks :as h] [app.main.ui.icons :as i] [app.main.ui.workspace.sidebar.assets.common :as cmm] + [app.util.debug :as dbg] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.timers :as tm] @@ -639,4 +640,6 @@ [:& component-swap {:shapes copies}]) (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))])])]))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs index 515c109eb..3a1ba4bd9 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs @@ -187,15 +187,15 @@ (def ^:private corner-bottom-icon (i/icon-xref :corner-bottom (stl/css :corner-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 - (i/icon-xref :corner-bottomright (stl/css :corner-icon))) + (i/icon-xref :corner-bottom-right (stl/css :corner-icon))) (def ^:private corner-top-icon (i/icon-xref :corner-top (stl/css :corner-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 - (i/icon-xref :corner-topright (stl/css :corner-icon))) + (i/icon-xref :corner-top-right (stl/css :corner-icon))) (mf/defc interaction-entry [{:keys [index shape interaction update-interaction remove-interaction]}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/sitemap.scss b/frontend/src/app/main/ui/workspace/sidebar/sitemap.scss index 1363ea1c5..cd12ae572 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/sitemap.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/sitemap.scss @@ -227,5 +227,5 @@ .title-spacing-sitemap { padding-inline-start: $s-8; margin-block-start: $s-8; - padding-inline-end: $s-20; + margin-block-end: $s-4; } diff --git a/frontend/src/app/main/ui/workspace/viewport/viewport_ref.cljs b/frontend/src/app/main/ui/workspace/viewport/viewport_ref.cljs index 41c379d02..4ba3d44fe 100644 --- a/frontend/src/app/main/ui/workspace/viewport/viewport_ref.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/viewport_ref.cljs @@ -11,7 +11,10 @@ [app.common.geom.point :as gpt] [app.main.store :as st] [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 current-observer (atom nil)) @@ -45,6 +48,8 @@ #(fn [node] (mf/set-ref-val! 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)))])) (defn point->viewport diff --git a/frontend/src/app/util/debug.cljs b/frontend/src/app/util/debug.cljs index 3129e856f..067ac8a54 100644 --- a/frontend/src/app/util/debug.cljs +++ b/frontend/src/app/util/debug.cljs @@ -73,6 +73,9 @@ ;; Show an asterisk for touched copies :show-touched + ;; Show the id with the name + :show-ids + ;; :grid-layout @@ -80,7 +83,10 @@ :grid-cells ;; Show info about shapes - :shape-panel}) + :shape-panel + + ;; Show what is touched in copies + :display-touched}) (defn enable! [option] diff --git a/frontend/test/frontend_tests/state_components_sync_test.cljs b/frontend/test/frontend_tests/state_components_sync_test.cljs index 1751fe9a5..4928ad74c 100644 --- a/frontend/test/frontend_tests/state_components_sync_test.cljs +++ b/frontend/test/frontend_tests/state_components_sync_test.cljs @@ -588,7 +588,7 @@ (t/is (= (:fill-color shape1) clr/black)) (t/is (= (:fill-opacity shape1) 0)) (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-opacity shape2) 0.5)) (t/is (= (:name c-instance2) "Board")) diff --git a/frontend/translations/en.po b/frontend/translations/en.po index fa989a0c7..a0ad7a087 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -5064,7 +5064,7 @@ msgstr "Click to close the path" #, markdown msgid "workspace.top-bar.view-only" -msgstr "**Inspecting mode** (View Only)" +msgstr "**Inspecting code** (View Only)" msgid "workspace.top-bar.read-only.done" msgstr "Done" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 73140e72c..30b388b04 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -5149,7 +5149,7 @@ msgstr "Pulsar para cerrar la ruta" #, markdown 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" msgstr "Hecho" diff --git a/version.txt b/version.txt index 3500250a4..227cea215 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.21.0 +2.0.0