diff --git a/.gitignore b/.gitignore index 8883932e85..0e271d1257 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ /*.jpg /*.md /*.png +/*.svg /*.sql /*.txt /*.yml diff --git a/backend/deps.edn b/backend/deps.edn index b3cacd663a..d3c9893183 100644 --- a/backend/deps.edn +++ b/backend/deps.edn @@ -26,7 +26,7 @@ :git/url "https://github.com/funcool/yetti.git" :exclusions [org.slf4j/slf4j-api]} - com.github.seancorfield/next.jdbc {:mvn/version "1.3.894"} + com.github.seancorfield/next.jdbc {:mvn/version "1.3.909"} metosin/reitit-core {:mvn/version "0.6.0"} nrepl/nrepl {:mvn/version "1.1.0"} cider/cider-nrepl {:mvn/version "0.43.1"} diff --git a/backend/dev/user.clj b/backend/dev/user.clj index 902ba62129..5d0cb7e371 100644 --- a/backend/dev/user.clj +++ b/backend/dev/user.clj @@ -8,6 +8,7 @@ (:require [app.common.data :as d] [app.common.exceptions :as ex] + [app.common.files.helpers :as cfh] [app.common.fressian :as fres] [app.common.geom.matrix :as gmt] [app.common.logging :as l] @@ -136,3 +137,12 @@ (add-tap #(locking debug-tap (prn "tap debug:" %))) 1)) + + +(defn calculate-frames + [{:keys [data]}] + (->> (vals (:pages-index data)) + (mapcat (comp vals :objects)) + (filter cfh/is-direct-child-of-root?) + (filter cfh/frame-shape?) + (count))) diff --git a/backend/src/app/config.clj b/backend/src/app/config.clj index f5a1602aff..ef021d7c1b 100644 --- a/backend/src/app/config.clj +++ b/backend/src/app/config.clj @@ -207,6 +207,7 @@ (s/def ::telemetry-uri ::us/string) (s/def ::telemetry-with-taiga ::us/boolean) (s/def ::tenant ::us/string) +(s/def ::svgo-max-procs ::us/integer) (s/def ::config (s/keys :opt-un [::secret-key @@ -326,7 +327,9 @@ ::telemetry-uri ::telemetry-referer ::telemetry-with-taiga - ::tenant])) + ::tenant + + ::svgo-max-procs])) (def default-flags [:enable-backend-api-doc diff --git a/backend/src/app/db.clj b/backend/src/app/db.clj index 8d7fbef4c9..b1c8476a76 100644 --- a/backend/src/app/db.clj +++ b/backend/src/app/db.clj @@ -19,6 +19,7 @@ [app.util.json :as json] [app.util.time :as dt] [clojure.java.io :as io] + [clojure.set :as set] [clojure.spec.alpha :as s] [integrant.core :as ig] [next.jdbc :as jdbc] @@ -239,6 +240,10 @@ (ex/raise :type :internal :code :unable-resolve-pool)))) +(defn get-update-count + [result] + (:next.jdbc/update-count result)) + (defn get-connection [cfg-or-conn] (if (connection? cfg-or-conn) @@ -265,48 +270,120 @@ :code :unable-resolve-connectable :hint "expected conn, pool or system"))) +(def ^:private params-mapping + {::return-keys? :return-keys + ::return-keys :return-keys}) + +(defn rename-opts + [opts] + (set/rename-keys opts params-mapping)) + +(def ^:private default-insert-opts + {:builder-fn sql/as-kebab-maps + :return-keys true}) + (def ^:private default-opts {:builder-fn sql/as-kebab-maps}) (defn exec! - ([ds sv] - (-> (get-connectable ds) - (jdbc/execute! sv default-opts))) + ([ds sv] (exec! ds sv nil)) ([ds sv opts] - (-> (get-connectable ds) - (jdbc/execute! sv (into default-opts (sql/adapt-opts opts)))))) + (let [conn (get-connectable ds) + opts (if (empty? opts) + default-opts + (into default-opts (rename-opts opts)))] + (jdbc/execute! conn sv opts)))) (defn exec-one! - ([ds sv] - (-> (get-connectable ds) - (jdbc/execute-one! sv default-opts))) + ([ds sv] (exec-one! ds sv nil)) ([ds sv opts] - (-> (get-connectable ds) - (jdbc/execute-one! sv (into default-opts (sql/adapt-opts opts)))))) + (let [conn (get-connectable ds) + opts (if (empty? opts) + default-opts + (into default-opts (rename-opts opts)))] + (jdbc/execute-one! conn sv opts)))) (defn insert! - [ds table params & {:as opts :keys [::return-keys?] :or {return-keys? true}}] - (-> (get-connectable ds) - (exec-one! (sql/insert table params opts) - (assoc opts ::return-keys? return-keys?)))) + "A helper that builds an insert sql statement and executes it. By + default returns the inserted row with all the field; you can delimit + the returned columns with the `::columns` option." + [ds table params & {:as opts}] + (let [conn (get-connectable ds) + sql (sql/insert table params opts) + opts (if (empty? opts) + default-insert-opts + (into default-insert-opts (rename-opts opts)))] + (jdbc/execute-one! conn sql opts))) -(defn insert-multi! - [ds table cols rows & {:as opts :keys [::return-keys?] :or {return-keys? true}}] - (-> (get-connectable ds) - (exec! (sql/insert-multi table cols rows opts) - (assoc opts ::return-keys? return-keys?)))) +(defn insert-many! + "An optimized version of `insert!` that perform insertion of multiple + values at once. + + This expands to a single SQL statement with placeholders for every + value being inserted. For large data sets, this may exceed the limit + of sql string size and/or number of parameters." + [ds table cols rows & {:as opts}] + (let [conn (get-connectable ds) + sql (sql/insert-many table cols rows opts) + opts (if (empty? opts) + default-insert-opts + (into default-insert-opts (rename-opts opts))) + opts (update opts :return-keys boolean)] + (jdbc/execute! conn sql opts))) (defn update! - [ds table params where & {:as opts :keys [::return-keys?] :or {return-keys? true}}] - (-> (get-connectable ds) - (exec-one! (sql/update table params where opts) - (assoc opts ::return-keys? return-keys?)))) + "A helper that build an UPDATE SQL statement and executes it. + + Given a connectable object, a table name, a hash map of columns and + values to set, and either a hash map of columns and values to search + on or a vector of a SQL where clause and parameters, perform an + update on the table. + + By default returns an object with the number of affected rows; a + complete row can be returned if you pass `::return-keys` with `true` + or with a vector of columns. + + Also it can be combined with the `::many` option if you perform an + update to multiple rows and you want all the affected rows to be + returned." + [ds table params where & {:as opts}] + (let [conn (get-connectable ds) + sql (sql/update table params where opts) + opts (if (empty? opts) + default-opts + (into default-opts (rename-opts opts))) + opts (update opts :return-keys boolean)] + (if (::many opts) + (jdbc/execute! conn sql opts) + (jdbc/execute-one! conn sql opts)))) (defn delete! - [ds table params & {:as opts :keys [::return-keys?] :or {return-keys? true}}] - (-> (get-connectable ds) - (exec-one! (sql/delete table params opts) - (assoc opts ::return-keys? return-keys?)))) + "A helper that builds an DELETE SQL statement and executes it. + + Given a connectable object, a table name, and either a hash map of columns + and values to search on or a vector of a SQL where clause and parameters, + perform a delete on the table. + + By default returns an object with the number of affected rows; a + complete row can be returned if you pass `::return-keys` with `true` + or with a vector of columns. + + Also it can be combined with the `::many` option if you perform an + update to multiple rows and you want all the affected rows to be + returned." + [ds table params & {:as opts}] + (let [conn (get-connectable ds) + sql (sql/delete table params opts) + opts (if (empty? opts) + default-opts + (into default-opts (rename-opts opts)))] + (if (::many opts) + (jdbc/execute! conn sql opts) + (jdbc/execute-one! conn sql opts)))) + +(defn query + [ds table params & {:as opts}] + (exec! ds (sql/select table params opts) opts)) (defn is-row-deleted? [{:keys [deleted-at]}] @@ -320,7 +397,7 @@ [ds table params & {:as opts}] (let [rows (exec! ds (sql/select table params opts)) rows (cond->> rows - (::remove-deleted? opts true) + (::remove-deleted opts true) (remove is-row-deleted?))] (first rows))) @@ -329,7 +406,7 @@ filters. Raises :not-found exception if no object is found." [ds table params & {:as opts}] (let [row (get* ds table params opts)] - (when (and (not row) (::check-deleted? opts true)) + (when (and (not row) (::check-deleted opts true)) (ex/raise :type :not-found :code :object-not-found :table table @@ -341,14 +418,29 @@ (-> (get-connectable ds) (jdbc/plan sql sql/default-opts))) +(defn cursor + "Return a lazy seq of rows using server side cursors" + [conn query & {:keys [chunk-size] :or {chunk-size 25}}] + (let [cname (str (gensym "cursor_")) + fquery [(str "FETCH " chunk-size " FROM " cname)]] + + ;; declare cursor + (exec-one! conn + (if (vector? query) + (into [(str "DECLARE " cname " CURSOR FOR " (nth query 0))] + (rest query)) + [(str "DECLARE " cname " CURSOR FOR " query)])) + + ;; return a lazy seq + ((fn fetch-more [] + (lazy-seq + (when-let [chunk (seq (exec! conn fquery))] + (concat chunk (fetch-more)))))))) + (defn get-by-id [ds table id & {:as opts}] (get ds table {:id id} opts)) -(defn query - [ds table params & {:as opts}] - (exec! ds (sql/select table params opts))) - (defn pgobject? ([v] (instance? PGobject v)) @@ -548,11 +640,6 @@ (.setType "jsonb") (.setValue (json/encode-str data))))) -(defn get-update-count - [result] - (:next.jdbc/update-count result)) - - ;; --- Locks (def ^:private siphash-state diff --git a/backend/src/app/db/sql.clj b/backend/src/app/db/sql.clj index 4b002f41b1..37814733de 100644 --- a/backend/src/app/db/sql.clj +++ b/backend/src/app/db/sql.clj @@ -8,7 +8,6 @@ (:refer-clojure :exclude [update]) (:require [app.db :as-alias db] - [clojure.set :as set] [clojure.string :as str] [next.jdbc.optional :as jdbc-opt] [next.jdbc.sql.builder :as sql])) @@ -20,14 +19,6 @@ {:table-fn snake-case :column-fn snake-case}) -(def params-mapping - {::db/return-keys? :return-keys - ::db/columns :columns}) - -(defn adapt-opts - [opts] - (set/rename-keys opts params-mapping)) - (defn as-kebab-maps [rs opts] (jdbc-opt/as-unqualified-modified-maps rs (assoc opts :label-fn kebab-case))) @@ -42,7 +33,7 @@ (assoc :suffix "ON CONFLICT DO NOTHING"))] (sql/for-insert table key-map opts)))) -(defn insert-multi +(defn insert-many [table cols rows opts] (let [opts (merge default-opts opts)] (sql/for-insert-multi table cols rows opts))) @@ -53,11 +44,9 @@ ([table where-params opts] (let [opts (merge default-opts opts) opts (cond-> opts - (::db/columns opts) (assoc :columns (::db/columns opts)) - (::db/for-update? opts) (assoc :suffix "FOR UPDATE") - (::db/for-share? opts) (assoc :suffix "FOR KEY SHARE") - (:for-update opts) (assoc :suffix "FOR UPDATE") - (:for-key-share opts) (assoc :suffix "FOR KEY SHARE"))] + (::columns opts) (assoc :columns (::columns opts)) + (::for-update opts) (assoc :suffix "FOR UPDATE") + (::for-share opts) (assoc :suffix "FOR KEY SHARE"))] (sql/for-query table where-params opts)))) (defn update @@ -65,11 +54,9 @@ (update table key-map where-params nil)) ([table key-map where-params opts] (let [opts (into default-opts opts) - opts (if-let [columns (::db/columns opts)] - (let [columns (if (seq columns) - (sql/as-cols columns opts) - "*")] - (assoc opts :suffix (str "RETURNING " columns))) + keys (::db/return-keys opts) + opts (if (vector? keys) + (assoc opts :suffix (str "RETURNING " (sql/as-cols keys opts))) opts)] (sql/for-update table key-map where-params opts)))) @@ -77,5 +64,9 @@ ([table where-params] (delete table where-params nil)) ([table where-params opts] - (let [opts (merge default-opts opts)] + (let [opts (merge default-opts opts) + keys (::db/return-keys opts) + opts (if (vector? keys) + (assoc opts :suffix (str "RETURNING " (sql/as-cols keys opts))) + opts)] (sql/for-delete table where-params opts)))) diff --git a/backend/src/app/features/components_v2.clj b/backend/src/app/features/components_v2.clj index 99074dd598..3c5d3e5b0f 100644 --- a/backend/src/app/features/components_v2.clj +++ b/backend/src/app/features/components_v2.clj @@ -39,27 +39,54 @@ [app.rpc.commands.media :as cmd.media] [app.storage :as sto] [app.storage.tmp :as tmp] + [app.svgo :as svgo] [app.util.blob :as blob] [app.util.pointer-map :as pmap] [app.util.time :as dt] [buddy.core.codecs :as bc] [cuerdas.core :as str] [datoteka.io :as io] - [promesa.exec :as px] - [promesa.exec.semaphore :as ps] - [promesa.util :as pu])) + [promesa.core :as p])) -(def ^:dynamic *system* nil) -(def ^:dynamic *stats* nil) -(def ^:dynamic *file-stats* nil) -(def ^:dynamic *team-stats* nil) -(def ^:dynamic *semaphore* nil) -(def ^:dynamic *skip-on-error* true) +(def ^:dynamic *stats* + "A dynamic var for setting up state for collect stats globally." + nil) + +(def ^:dynamic *skip-on-error* + "A dynamic var for setting up the default error behavior." + true) + +(def ^:dynamic ^:private *system* + "An internal var for making the current `system` available to all + internal functions without the need to explicitly pass it top down." + nil) + +(def ^:dynamic ^:private *max-procs* + "A dynamic variable that can optionally indicates the maxumum number + of concurrent graphics migration processes." + nil) + +(def ^:dynamic ^:private *file-stats* + "An internal dynamic var for collect stats by file." + nil) + +(def ^:dynamic ^:private *team-stats* + "An internal dynamic var for collect stats by team." + nil) (def grid-gap 50) (def frame-gap 200) (def max-group-size 50) +(defn decode-row + [{:keys [features data] :as row}] + (cond-> row + (some? features) + (assoc :features (db/decode-pgarray features #{})) + + (some? data) + (assoc :data (blob/decode data)))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; FILE PREPARATION BEFORE MIGRATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -220,19 +247,17 @@ (fn [file-data] ;; Transform component and copy heads to frames, and set the ;; frame-id of its childrens - (letfn [(fix-container - [container] + (letfn [(fix-container [container] (update container :objects update-vals fix-shape)) - (fix-shape - [shape] + (fix-shape [shape] (if (or (nil? (:parent-id shape)) (ctk/instance-head? shape)) (assoc shape - :type :frame ; Old groups must be converted - :fills (or (:fills shape) []) ; to frames and conform to spec - :hide-in-viewer (or (:hide-in-viewer shape) true) - :rx (or (:rx shape) 0) - :ry (or (:ry shape) 0)) + :type :frame ; Old groups must be converted + :fills (or (:fills shape) []) ; to frames and conform to spec + :hide-in-viewer (or (:hide-in-viewer shape) true) + :rx (or (:rx shape) 0) + :ry (or (:ry shape) 0)) shape))] (-> file-data (update :pages-index update-vals fix-container) @@ -310,10 +335,10 @@ (defn- get-asset-groups [assets generic-name] - (let [; Group by first element of the path. + (let [;; Group by first element of the path. groups (d/group-by #(first (cfh/split-path (:path %))) assets) - ; Split large groups in chunks of max-group-size elements + ;; Split large groups in chunks of max-group-size elements groups (loop [groups (seq groups) result {}] (if (empty? groups) @@ -334,15 +359,14 @@ result splits))))))) - ; Sort assets in each group by path + ;; Sort assets in each group by path groups (update-vals groups (fn [assets] (sort-by (fn [{:keys [path name]}] (str/lower (cfh/merge-path-item path name))) - assets))) + assets)))] - ; Sort groups by name - groups (into (sorted-map) groups)] - groups)) + ;; Sort groups by name + (into (sorted-map) groups))) (defn- create-frame [name position width height] @@ -612,14 +636,11 @@ (defn- create-shapes-for-svg [{:keys [id] :as mobj} file-id objects frame-id position] - (let [svg-text (get-svg-content id) - - optimizer (::csvg/optimizer *system*) - svg-text (csvg/optimize optimizer svg-text) - - svg-data (-> (csvg/parse svg-text) - (assoc :name (:name mobj)) - (collect-and-persist-images file-id))] + (let [svg-text (get-svg-content id) + svg-text (svgo/optimize *system* svg-text) + svg-data (-> (csvg/parse svg-text) + (assoc :name (:name mobj)) + (collect-and-persist-images file-id))] (sbuilder/create-svg-shapes svg-data position objects frame-id frame-id #{} false))) @@ -678,9 +699,7 @@ (defn- create-media-grid [fdata page-id frame-id grid media-group] - (let [factory (px/thread-factory :virtual true) - executor (px/fixed-executor :parallelism 10 :factory factory) - process (fn [mobj position] + (let [process (fn [mobj position] (let [position (gpt/add position (gpt/point grid-gap grid-gap)) tp1 (dt/tpoint)] (try @@ -690,7 +709,6 @@ :file-id (str (:id fdata)) :id (str (:id mobj)) :cause cause) - (if-not *skip-on-error* (throw cause) nil)) @@ -699,21 +717,24 @@ :file-id (str (:id fdata)) :media-id (str (:id mobj)) :elapsed (dt/format-duration (tp1)))))))] - (try - (->> (d/zip media-group grid) - (map (fn [[mobj position]] - (sse/tap {:type :migration-progress - :section :graphics - :name (:name mobj)}) - (px/submit! executor (partial process mobj position)))) - (reduce (fn [fdata promise] - (if-let [changes (deref promise)] - (-> (assoc-in fdata [:options :components-v2] true) - (cp/process-changes changes false)) - fdata)) - fdata)) - (finally - (pu/close! executor))))) + + (->> (d/zip media-group grid) + (partition-all (or *max-procs* 1)) + (mapcat (fn [partition] + (->> partition + (map (fn [[mobj position]] + (sse/tap {:type :migration-progress + :section :graphics + :name (:name mobj)}) + (p/vthread (process mobj position)))) + (doall) + (map deref) + (doall)))) + (filter some?) + (reduce (fn [fdata changes] + (-> (assoc-in fdata [:options :components-v2] true) + (cp/process-changes changes false))) + fdata)))) (defn- migrate-graphics [fdata] @@ -759,6 +780,11 @@ (create-media-grid fdata page-id (:id frame) grid assets) (gpt/add position (gpt/point 0 (+ height (* 2 grid-gap) frame-gap)))))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; PRIVATE HELPERS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (defn- migrate-fdata [fdata libs] (let [migrated? (dm/get-in fdata [:options :components-v2])] @@ -771,11 +797,22 @@ (defn- get-file [system id] (binding [pmap/*load-fn* (partial fdata/load-pointer system id)] - (-> (files/get-file system id :migrate? false) + (-> (db/get system :file {:id id} + {::db/remove-deleted false + ::db/check-deleted false}) + (decode-row) (update :data assoc :id id) (update :data fdata/process-pointers deref) (fmg/migrate-file)))) + +(defn- get-team + [system team-id] + (-> (db/get system :team {:id team-id} + {::db/remove-deleted false + ::db/check-deleted false}) + (decode-row))) + (defn- validate-file! [file libs throw-on-validate?] (try @@ -791,7 +828,8 @@ (let [file (get-file system id) libs (->> (files/get-file-libraries conn id) - (into [file] (comp (map :id) (map (partial get-file system)))) + (into [file] (comp (map :id) + (map (partial get-file system)))) (d/index-by :id)) file (-> file @@ -816,18 +854,39 @@ {:data (blob/encode (:data file)) :features (db/create-array conn "text" (:features file)) :revn (:revn file)} - {:id (:id file)} - {::db/return-keys? false}) + {:id (:id file)}) (dissoc file :data))) + +(def ^:private sql:get-and-lock-team-files + "SELECT f.id + FROM file AS f + JOIN project AS p ON (p.id = f.project_id) + WHERE p.team_id = ? + FOR UPDATE") + +(defn- get-and-lock-files + [conn team-id] + (->> (db/cursor conn [sql:get-and-lock-team-files team-id]) + (map :id))) + +(defn- update-team-features! + [conn team-id features] + (let [features (db/create-array conn "text" features)] + (db/update! conn :team + {:features features} + {:id team-id}))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; PUBLIC API +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (defn migrate-file! - [system file-id & {:keys [validate? throw-on-validate?]}] - (let [tpoint (dt/tpoint) - file-id (if (string? file-id) - (parse-uuid file-id) - file-id)] - (binding [*file-stats* (atom {})] + [system file-id & {:keys [validate? throw-on-validate? max-procs]}] + (let [tpoint (dt/tpoint)] + (binding [*file-stats* (atom {}) + *max-procs* max-procs] (try (l/dbg :hint "migrate:file:start" :file-id (str file-id)) @@ -839,7 +898,6 @@ (process-file system file-id :validate? validate? :throw-on-validate? throw-on-validate?))))) - (finally (let [elapsed (tpoint) components (get @*file-stats* :processed/components 0) @@ -855,73 +913,51 @@ (some-> *team-stats* (swap! update :processed/files (fnil inc 0))))))))) (defn migrate-team! - [system team-id & {:keys [validate? throw-on-validate?]}] - (let [tpoint (dt/tpoint) - team-id (if (string? team-id) - (parse-uuid team-id) - team-id)] - (l/dbg :hint "migrate:team:start" :team-id (dm/str team-id)) + [system team-id & {:keys [validate? throw-on-validate? max-procs]}] + + (l/dbg :hint "migrate:team:start" + :team-id (dm/str team-id)) + + (let [tpoint (dt/tpoint) + + migrate-file + (fn [system file-id] + (migrate-file! system file-id + :max-procs max-procs + :validate? validate? + :throw-on-validate? throw-on-validate?)) + migrate-team + (fn [{:keys [::db/conn] :as system} {:keys [id features] :as team}] + (let [features (-> features + (disj "ephimeral/v2-migration") + (conj "components/v2") + (conj "layout/grid") + (conj "styles/v2"))] + + (run! (partial migrate-file system) + (get-and-lock-files conn id)) + + (update-team-features! conn id features)))] + (binding [*team-stats* (atom {})] (try - ;; We execute this out of transaction because we want this - ;; change to be visible to all other sessions before starting - ;; the migration - (let [sql (str "UPDATE team SET features = " - " array_append(features, 'ephimeral/v2-migration') " - " WHERE id = ?")] - (db/exec-one! system [sql team-id])) - - (db/tx-run! system - (fn [{:keys [::db/conn] :as system}] - ;; Lock the team - (db/exec-one! conn ["SET idle_in_transaction_session_timeout = 0"]) - - (let [{:keys [features] :as team} (-> (db/get conn :team {:id team-id}) - (update :features db/decode-pgarray #{}))] - - (if (contains? features "components/v2") - (l/dbg :hint "team already migrated") - (let [sql (str/concat - "SELECT f.id FROM file AS f " - " JOIN project AS p ON (p.id = f.project_id) " - "WHERE p.team_id = ? AND f.deleted_at IS NULL AND p.deleted_at IS NULL " - "FOR UPDATE")] - - (doseq [file-id (->> (db/exec! conn [sql team-id]) - (map :id))] - (migrate-file! system file-id - :validate? validate? - :throw-on-validate? throw-on-validate?)) - - (let [features (-> features - (disj "ephimeral/v2-migration") - (conj "components/v2") - (conj "layout/grid") - (conj "styles/v2"))] - (db/update! conn :team - {:features (db/create-array conn "text" features)} - {:id team-id}))))))) + (db/tx-run! system (fn [system] + (db/exec-one! system ["SET idle_in_transaction_session_timeout = 0"]) + (let [team (get-team system team-id)] + (if (contains? (:features team) "components/v2") + (l/inf :hint "team already migrated") + (migrate-team system team))))) (finally - (some-> *semaphore* ps/release!) - (let [elapsed (tpoint)] + (let [elapsed (tpoint) + components (get @*team-stats* :processed/components 0) + graphics (get @*team-stats* :processed/graphics 0) + files (get @*team-stats* :processed/files 0)] + (some-> *stats* (swap! update :processed/teams (fnil inc 0))) - ;; We execute this out of transaction because we want this - ;; change to be visible to all other sessions before starting - ;; the migration - (let [sql (str "UPDATE team SET features = " - " array_remove(features, 'ephimeral/v2-migration') " - " WHERE id = ?")] - (db/exec-one! system [sql team-id])) - - (let [components (get @*team-stats* :processed/components 0) - graphics (get @*team-stats* :processed/graphics 0) - files (get @*team-stats* :processed/files 0)] - (l/dbg :hint "migrate:team:end" - :team-id (dm/str team-id) - :files files - :components components - :graphics graphics - :elapsed (dt/format-duration elapsed))))))))) - - + (l/dbg :hint "migrate:team:end" + :team-id (dm/str team-id) + :files files + :components components + :graphics graphics + :elapsed (dt/format-duration elapsed)))))))) diff --git a/backend/src/app/features/fdata.clj b/backend/src/app/features/fdata.clj index 832d3360d0..d04369d5d8 100644 --- a/backend/src/app/features/fdata.clj +++ b/backend/src/app/features/fdata.clj @@ -11,6 +11,7 @@ [app.common.exceptions :as ex] [app.common.logging :as l] [app.db :as db] + [app.db.sql :as-alias sql] [app.util.blob :as blob] [app.util.objects-map :as omap] [app.util.pointer-map :as pmap])) @@ -38,8 +39,8 @@ [system file-id id] (let [{:keys [content]} (db/get system :file-data-fragment {:id id :file-id file-id} - {::db/columns [:content] - ::db/check-deleted? false})] + {::sql/columns [:content] + ::db/check-deleted false})] (when-not content (ex/raise :type :internal :code :fragment-not-found diff --git a/backend/src/app/loggers/audit.clj b/backend/src/app/loggers/audit.clj index 4171f52ab3..3fbd09bddf 100644 --- a/backend/src/app/loggers/audit.clj +++ b/backend/src/app/loggers/audit.clj @@ -133,7 +133,7 @@ [_ {:keys [::db/pool] :as cfg}] (cond (db/read-only? pool) - (l/warn :hint "audit: disabled (db is read-only)") + (l/warn :hint "audit disabled (db is read-only)") :else cfg)) @@ -187,8 +187,7 @@ false)})) (defn- handle-event! - [conn-or-pool event] - (us/verify! ::event event) + [cfg event] (let [params {:id (uuid/next) :name (::name event) :type (::type event) @@ -201,19 +200,22 @@ ;; NOTE: this operation may cause primary key conflicts on inserts ;; because of the timestamp precission (two concurrent requests), in ;; this case we just retry the operation. - (rtry/with-retry {::rtry/when rtry/conflict-exception? - ::rtry/max-retries 6 - ::rtry/label "persist-audit-log" - ::db/conn (dm/check db/connection? conn-or-pool)} - (let [now (dt/now)] - (db/insert! conn-or-pool :audit-log - (-> params - (update :props db/tjson) - (update :context db/tjson) - (update :ip-addr db/inet) - (assoc :created-at now) - (assoc :tracked-at now) - (assoc :source "backend")))))) + (let [cfg (-> cfg + (assoc ::rtry/when rtry/conflict-exception?) + (assoc ::rtry/max-retries 6) + (assoc ::rtry/label "persist-audit-log")) + params (-> params + (update :props db/tjson) + (update :context db/tjson) + (update :ip-addr db/inet) + (assoc :source "backend"))] + + (rtry/invoke cfg (fn [cfg] + (let [tnow (dt/now) + params (-> params + (assoc :created-at tnow) + (assoc :tracked-at tnow))] + (db/insert! cfg :audit-log params)))))) (when (and (contains? cf/flags :webhooks) (::webhooks/event? event)) @@ -226,7 +228,7 @@ :else label) dedupe? (boolean (and batch-key batch-timeout))] - (wrk/submit! ::wrk/conn conn-or-pool + (wrk/submit! ::wrk/conn (::db/conn cfg) ::wrk/task :process-webhook-event ::wrk/queue :webhooks ::wrk/max-retries 0 @@ -243,12 +245,12 @@ (defn submit! "Submit audit event to the collector." [cfg params] - (let [conn (or (::db/conn cfg) (::db/pool cfg))] - (us/assert! ::db/pool-or-conn conn) - (try - (handle-event! conn (d/without-nils params)) - (catch Throwable cause - (l/error :hint "audit: unexpected error processing event" :cause cause))))) + (try + (let [event (d/without-nils params)] + (us/verify! ::event event) + (db/tx-run! cfg handle-event! event)) + (catch Throwable cause + (l/error :hint "unexpected error processing event" :cause cause)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; TASK: ARCHIVE diff --git a/backend/src/app/loggers/webhooks.clj b/backend/src/app/loggers/webhooks.clj index eb0f14fc8c..00ebd3f383 100644 --- a/backend/src/app/loggers/webhooks.clj +++ b/backend/src/app/loggers/webhooks.clj @@ -111,9 +111,11 @@ " where id=?") err (:id whook)] - res (db/exec-one! pool sql {::db/return-keys? true})] + res (db/exec-one! pool sql {::db/return-keys true})] (when (>= (:error-count res) max-errors) - (db/update! pool :webhook {:is-active false} {:id (:id whook)}))) + (db/update! pool :webhook + {:is-active false} + {:id (:id whook)}))) (db/update! pool :webhook {:updated-at (dt/now) diff --git a/backend/src/app/main.clj b/backend/src/app/main.clj index 953f51d8be..c80210a06b 100644 --- a/backend/src/app/main.clj +++ b/backend/src/app/main.clj @@ -10,7 +10,6 @@ [app.auth.oidc :as-alias oidc] [app.auth.oidc.providers :as-alias oidc.providers] [app.common.logging :as l] - [app.common.svg :as csvg] [app.config :as cf] [app.db :as-alias db] [app.email :as-alias email] @@ -34,7 +33,10 @@ [app.srepl :as-alias srepl] [app.storage :as-alias sto] [app.storage.fs :as-alias sto.fs] + [app.storage.gc-deleted :as-alias sto.gc-deleted] + [app.storage.gc-touched :as-alias sto.gc-touched] [app.storage.s3 :as-alias sto.s3] + [app.svgo :as-alias svgo] [app.util.time :as dt] [app.worker :as-alias wrk] [cider.nrepl :refer [cider-nrepl-handler]] @@ -202,11 +204,11 @@ :app.storage.tmp/cleaner {::wrk/executor (ig/ref ::wrk/executor)} - ::sto/gc-deleted-task + ::sto.gc-deleted/handler {::db/pool (ig/ref ::db/pool) ::sto/storage (ig/ref ::sto/storage)} - ::sto/gc-touched-task + ::sto.gc-touched/handler {::db/pool (ig/ref ::db/pool)} ::http.client/client @@ -314,7 +316,7 @@ ::mtx/metrics (ig/ref ::mtx/metrics) ::mbus/msgbus (ig/ref ::mbus/msgbus) ::rds/redis (ig/ref ::rds/redis) - ::csvg/optimizer (ig/ref ::csvg/optimizer) + ::svgo/optimizer (ig/ref ::svgo/optimizer) ::rpc/climit (ig/ref ::rpc/climit) ::rpc/rlimit (ig/ref ::rpc/rlimit) @@ -337,12 +339,13 @@ ::wrk/tasks {:sendmail (ig/ref ::email/handler) :objects-gc (ig/ref :app.tasks.objects-gc/handler) + :orphan-teams-gc (ig/ref :app.tasks.orphan-teams-gc/handler) :file-gc (ig/ref :app.tasks.file-gc/handler) :file-xlog-gc (ig/ref :app.tasks.file-xlog-gc/handler) - :storage-gc-deleted (ig/ref ::sto/gc-deleted-task) - :storage-gc-touched (ig/ref ::sto/gc-touched-task) :tasks-gc (ig/ref :app.tasks.tasks-gc/handler) :telemetry (ig/ref :app.tasks.telemetry/handler) + :storage-gc-deleted (ig/ref ::sto.gc-deleted/handler) + :storage-gc-touched (ig/ref ::sto.gc-touched/handler) :session-gc (ig/ref ::session.tasks/gc) :audit-log-archive (ig/ref ::audit.tasks/archive) :audit-log-gc (ig/ref ::audit.tasks/gc) @@ -373,6 +376,9 @@ {::db/pool (ig/ref ::db/pool) ::sto/storage (ig/ref ::sto/storage)} + :app.tasks.orphan-teams-gc/handler + {::db/pool (ig/ref ::db/pool)} + :app.tasks.file-gc/handler {::db/pool (ig/ref ::db/pool) ::sto/storage (ig/ref ::sto/storage)} @@ -403,8 +409,9 @@ ;; module requires the migrations to run before initialize. ::migrations (ig/ref :app.migrations/migrations)} - ::csvg/optimizer - {} + ::svgo/optimizer + {::wrk/executor (ig/ref ::wrk/executor) + ::svgo/max-procs (cf/get :svgo-max-procs)} ::audit.tasks/archive {::props (ig/ref ::setup/props) @@ -458,6 +465,9 @@ {:cron #app/cron "0 0 0 * * ?" ;; daily :task :objects-gc} + {:cron #app/cron "0 0 0 * * ?" ;; daily + :task :orphan-teams-gc} + {:cron #app/cron "0 0 0 * * ?" ;; daily :task :storage-gc-deleted} diff --git a/backend/src/app/migrations.clj b/backend/src/app/migrations.clj index d37c7fc32b..900ef75f53 100644 --- a/backend/src/app/migrations.clj +++ b/backend/src/app/migrations.clj @@ -337,7 +337,40 @@ :fn (mg/resource "app/migrations/sql/0106-mod-team-table.sql")} {:name "0107-mod-file-tagged-object-thumbnail-table" - :fn (mg/resource "app/migrations/sql/0107-mod-file-tagged-object-thumbnail-table.sql")}]) + :fn (mg/resource "app/migrations/sql/0107-mod-file-tagged-object-thumbnail-table.sql")} + + {:name "0107-add-deletion-protection-trigger-function" + :fn (mg/resource "app/migrations/sql/0107-add-deletion-protection-trigger-function.sql")} + + {:name "0108-mod-file-thumbnail-table" + :fn (mg/resource "app/migrations/sql/0108-mod-file-thumbnail-table.sql")} + + {:name "0109-mod-file-tagged-object-thumbnail-table" + :fn (mg/resource "app/migrations/sql/0109-mod-file-tagged-object-thumbnail-table.sql")} + + {:name "0110-mod-file-media-object-table" + :fn (mg/resource "app/migrations/sql/0110-mod-file-media-object-table.sql")} + + {:name "0111-mod-file-data-fragment-table" + :fn (mg/resource "app/migrations/sql/0111-mod-file-data-fragment-table.sql")} + + {:name "0112-mod-profile-table" + :fn (mg/resource "app/migrations/sql/0112-mod-profile-table.sql")} + + {:name "0113-mod-team-font-variant-table" + :fn (mg/resource "app/migrations/sql/0113-mod-team-font-variant-table.sql")} + + {:name "0114-mod-team-table" + :fn (mg/resource "app/migrations/sql/0114-mod-team-table.sql")} + + {:name "0115-mod-project-table" + :fn (mg/resource "app/migrations/sql/0115-mod-project-table.sql")} + + {:name "0116-mod-file-table" + :fn (mg/resource "app/migrations/sql/0116-mod-file-table.sql")} + + {:name "0117-mod-file-object-thumbnail-table" + :fn (mg/resource "app/migrations/sql/0117-mod-file-object-thumbnail-table.sql")}]) (defn apply-migrations! [pool name migrations] diff --git a/backend/src/app/migrations/sql/0107-add-deletion-protection-trigger-function.sql b/backend/src/app/migrations/sql/0107-add-deletion-protection-trigger-function.sql new file mode 100644 index 0000000000..1ccf9b8b79 --- /dev/null +++ b/backend/src/app/migrations/sql/0107-add-deletion-protection-trigger-function.sql @@ -0,0 +1,8 @@ +CREATE OR REPLACE FUNCTION raise_deletion_protection() + RETURNS TRIGGER AS $$ + BEGIN + RAISE EXCEPTION 'unable to proceed to delete row on "%"', TG_TABLE_NAME + USING HINT = 'disable deletion protection with "SET rules.deletion_protection TO off"'; + RETURN NULL; + END; +$$ LANGUAGE plpgsql; diff --git a/backend/src/app/migrations/sql/0108-mod-file-thumbnail-table.sql b/backend/src/app/migrations/sql/0108-mod-file-thumbnail-table.sql new file mode 100644 index 0000000000..b7d05bdc7f --- /dev/null +++ b/backend/src/app/migrations/sql/0108-mod-file-thumbnail-table.sql @@ -0,0 +1,25 @@ +--- Add missing index for deleted_at column, we include all related +--- columns because we expect the index to be small and expect use +--- index-only scans. +CREATE INDEX IF NOT EXISTS file_thumbnail__deleted_at__idx + ON file_thumbnail (deleted_at, file_id, revn, media_id) + WHERE deleted_at IS NOT NULL; + +--- Add missing for media_id column, used mainly for refs checking +CREATE INDEX IF NOT EXISTS file_thumbnail__media_id__idx ON file_thumbnail (media_id); + +--- Remove CASCADE from media_id and file_id foreign constraint +ALTER TABLE file_thumbnail + DROP CONSTRAINT file_thumbnail_file_id_fkey, + ADD FOREIGN KEY (file_id) REFERENCES file(id) DEFERRABLE; + +ALTER TABLE file_thumbnail + DROP CONSTRAINT file_thumbnail_media_id_fkey, + ADD FOREIGN KEY (media_id) REFERENCES storage_object(id) DEFERRABLE; + +--- Add deletion protection +CREATE OR REPLACE TRIGGER deletion_protection__tgr +BEFORE DELETE ON file_thumbnail FOR EACH STATEMENT + WHEN ((current_setting('rules.deletion_protection', true) IN ('on', '')) OR + (current_setting('rules.deletion_protection', true) IS NULL)) + EXECUTE PROCEDURE raise_deletion_protection(); diff --git a/backend/src/app/migrations/sql/0109-mod-file-tagged-object-thumbnail-table.sql b/backend/src/app/migrations/sql/0109-mod-file-tagged-object-thumbnail-table.sql new file mode 100644 index 0000000000..3184a6576f --- /dev/null +++ b/backend/src/app/migrations/sql/0109-mod-file-tagged-object-thumbnail-table.sql @@ -0,0 +1,26 @@ +ALTER TABLE file_tagged_object_thumbnail + ADD COLUMN updated_at timestamptz NULL, + ADD COLUMN deleted_at timestamptz NULL; + +--- Add index for deleted_at column, we include all related columns +--- because we expect the index to be small and expect use index-only +--- scans. +CREATE INDEX IF NOT EXISTS file_tagged_object_thumbnail__deleted_at__idx + ON file_tagged_object_thumbnail (deleted_at, file_id, object_id, media_id) + WHERE deleted_at IS NOT NULL; + +--- Remove CASCADE from media_id and file_id foreign constraint +ALTER TABLE file_tagged_object_thumbnail + DROP CONSTRAINT file_tagged_object_thumbnail_media_id_fkey, + ADD FOREIGN KEY (media_id) REFERENCES storage_object(id) DEFERRABLE; + +ALTER TABLE file_tagged_object_thumbnail + DROP CONSTRAINT file_tagged_object_thumbnail_file_id_fkey, + ADD FOREIGN KEY (file_id) REFERENCES file(id) DEFERRABLE; + +--- Add deletion protection +CREATE OR REPLACE TRIGGER deletion_protection__tgr +BEFORE DELETE ON file_tagged_object_thumbnail FOR EACH STATEMENT + WHEN ((current_setting('rules.deletion_protection', true) IN ('on', '')) OR + (current_setting('rules.deletion_protection', true) IS NULL)) + EXECUTE PROCEDURE raise_deletion_protection(); diff --git a/backend/src/app/migrations/sql/0110-mod-file-media-object-table.sql b/backend/src/app/migrations/sql/0110-mod-file-media-object-table.sql new file mode 100644 index 0000000000..49cbebc96c --- /dev/null +++ b/backend/src/app/migrations/sql/0110-mod-file-media-object-table.sql @@ -0,0 +1,27 @@ +--- Fix legacy naming +ALTER INDEX media_object_pkey RENAME TO file_media_object_pkey; +ALTER INDEX media_object__file_id__idx RENAME TO file_media_object__file_id__idx; + +--- Create index for the deleted_at column +CREATE INDEX IF NOT EXISTS file_media_object__deleted_at__idx + ON file_media_object (deleted_at, id, media_id) + WHERE deleted_at IS NOT NULL; + +--- Drop now unnecesary trigger because this will be handled by the +--- application code +DROP TRIGGER file_media_object__on_delete__tgr ON file_media_object; +DROP FUNCTION on_delete_file_media_object ( ) CASCADE; +DROP TRIGGER file_media_object__on_insert__tgr ON file_media_object; +DROP FUNCTION on_media_object_insert () CASCADE; + +--- Remove CASCADE from file FOREIGN KEY +ALTER TABLE file_media_object + DROP CONSTRAINT file_media_object_file_id_fkey, + ADD FOREIGN KEY (file_id) REFERENCES file(id) DEFERRABLE; + +--- Add deletion protection +CREATE OR REPLACE TRIGGER deletion_protection__tgr +BEFORE DELETE ON file_media_object FOR EACH STATEMENT + WHEN ((current_setting('rules.deletion_protection', true) IN ('on', '')) OR + (current_setting('rules.deletion_protection', true) IS NULL)) + EXECUTE PROCEDURE raise_deletion_protection(); diff --git a/backend/src/app/migrations/sql/0111-mod-file-data-fragment-table.sql b/backend/src/app/migrations/sql/0111-mod-file-data-fragment-table.sql new file mode 100644 index 0000000000..8397124c3f --- /dev/null +++ b/backend/src/app/migrations/sql/0111-mod-file-data-fragment-table.sql @@ -0,0 +1,9 @@ +ALTER TABLE file_data_fragment + ADD COLUMN deleted_at timestamptz NULL; + +--- Add index for deleted_at column, we include all related columns +--- because we expect the index to be small and expect use index-only +--- scans. +CREATE INDEX IF NOT EXISTS file_data_fragment__deleted_at__idx + ON file_data_fragment (deleted_at, file_id, id) + WHERE deleted_at IS NOT NULL; diff --git a/backend/src/app/migrations/sql/0112-mod-profile-table.sql b/backend/src/app/migrations/sql/0112-mod-profile-table.sql new file mode 100644 index 0000000000..2db8d75b0a --- /dev/null +++ b/backend/src/app/migrations/sql/0112-mod-profile-table.sql @@ -0,0 +1,15 @@ +ALTER TABLE profile + DROP CONSTRAINT profile_photo_id_fkey, + ADD FOREIGN KEY (photo_id) REFERENCES storage_object(id) DEFERRABLE, + DROP CONSTRAINT profile_default_project_id_fkey, + ADD FOREIGN KEY (default_project_id) REFERENCES project(id) DEFERRABLE, + DROP CONSTRAINT profile_default_team_id_fkey, + ADD FOREIGN KEY (default_team_id) REFERENCES team(id) DEFERRABLE; + +--- Add deletion protection +CREATE OR REPLACE TRIGGER deletion_protection__tgr +BEFORE DELETE ON profile FOR EACH STATEMENT + WHEN ((current_setting('rules.deletion_protection', true) IN ('on', '')) OR + (current_setting('rules.deletion_protection', true) IS NULL)) + EXECUTE PROCEDURE raise_deletion_protection(); + diff --git a/backend/src/app/migrations/sql/0113-mod-team-font-variant-table.sql b/backend/src/app/migrations/sql/0113-mod-team-font-variant-table.sql new file mode 100644 index 0000000000..b9caa08f6e --- /dev/null +++ b/backend/src/app/migrations/sql/0113-mod-team-font-variant-table.sql @@ -0,0 +1,20 @@ +--- Remove ON DELETE SET NULL from foreign constraint on +--- storage_object table +ALTER TABLE team_font_variant + DROP CONSTRAINT team_font_variant_otf_file_id_fkey, + ADD FOREIGN KEY (otf_file_id) REFERENCES storage_object(id) DEFERRABLE, + DROP CONSTRAINT team_font_variant_ttf_file_id_fkey, + ADD FOREIGN KEY (ttf_file_id) REFERENCES storage_object(id) DEFERRABLE, + DROP CONSTRAINT team_font_variant_woff1_file_id_fkey, + ADD FOREIGN KEY (woff1_file_id) REFERENCES storage_object(id) DEFERRABLE, + DROP CONSTRAINT team_font_variant_woff2_file_id_fkey, + ADD FOREIGN KEY (woff2_file_id) REFERENCES storage_object(id) DEFERRABLE, + DROP CONSTRAINT team_font_variant_team_id_fkey, + ADD FOREIGN KEY (team_id) REFERENCES team(id) DEFERRABLE; + +--- Add deletion protection +CREATE OR REPLACE TRIGGER deletion_protection__tgr +BEFORE DELETE ON team_font_variant FOR EACH STATEMENT + WHEN ((current_setting('rules.deletion_protection', true) IN ('on', '')) OR + (current_setting('rules.deletion_protection', true) IS NULL)) + EXECUTE PROCEDURE raise_deletion_protection(); diff --git a/backend/src/app/migrations/sql/0114-mod-team-table.sql b/backend/src/app/migrations/sql/0114-mod-team-table.sql new file mode 100644 index 0000000000..8c76756437 --- /dev/null +++ b/backend/src/app/migrations/sql/0114-mod-team-table.sql @@ -0,0 +1,10 @@ +--- Add deletion protection +CREATE OR REPLACE TRIGGER deletion_protection__tgr +BEFORE DELETE ON team FOR EACH STATEMENT + WHEN ((current_setting('rules.deletion_protection', true) IN ('on', '')) OR + (current_setting('rules.deletion_protection', true) IS NULL)) + EXECUTE PROCEDURE raise_deletion_protection(); + +ALTER TABLE team + DROP CONSTRAINT team_photo_id_fkey, + ADD FOREIGN KEY (photo_id) REFERENCES storage_object(id) DEFERRABLE; diff --git a/backend/src/app/migrations/sql/0115-mod-project-table.sql b/backend/src/app/migrations/sql/0115-mod-project-table.sql new file mode 100644 index 0000000000..f37470dce8 --- /dev/null +++ b/backend/src/app/migrations/sql/0115-mod-project-table.sql @@ -0,0 +1,3 @@ +ALTER TABLE project + DROP CONSTRAINT project_team_id_fkey, + ADD FOREIGN KEY (team_id) REFERENCES team(id) DEFERRABLE; diff --git a/backend/src/app/migrations/sql/0116-mod-file-table.sql b/backend/src/app/migrations/sql/0116-mod-file-table.sql new file mode 100644 index 0000000000..1d3bce11a2 --- /dev/null +++ b/backend/src/app/migrations/sql/0116-mod-file-table.sql @@ -0,0 +1,3 @@ +ALTER TABLE file + DROP CONSTRAINT file_project_id_fkey, + ADD FOREIGN KEY (project_id) REFERENCES project(id) DEFERRABLE; diff --git a/backend/src/app/migrations/sql/0117-mod-file-object-thumbnail-table.sql b/backend/src/app/migrations/sql/0117-mod-file-object-thumbnail-table.sql new file mode 100644 index 0000000000..e3f6cb6d4b --- /dev/null +++ b/backend/src/app/migrations/sql/0117-mod-file-object-thumbnail-table.sql @@ -0,0 +1,12 @@ +ALTER TABLE file_object_thumbnail + DROP CONSTRAINT file_object_thumbnail_file_id_fkey, + ADD FOREIGN KEY (file_id) REFERENCES file(id) DEFERRABLE, + DROP CONSTRAINT file_object_thumbnail_media_id_fkey, + ADD FOREIGN KEY (media_id) REFERENCES storage_object(id) DEFERRABLE; + +--- Mark all related storage_object row as touched +-- UPDATE storage_object SET touched_at = now() +-- WHERE id IN (SELECT DISTINCT media_id +-- FROM file_object_thumbnail +-- WHERE media_id IS NOT NULL) +-- AND touched_at IS NULL; diff --git a/backend/src/app/rpc/commands/audit.clj b/backend/src/app/rpc/commands/audit.clj index fa56087219..76bd6e1880 100644 --- a/backend/src/app/rpc/commands/audit.clj +++ b/backend/src/app/rpc/commands/audit.clj @@ -48,7 +48,7 @@ (map event->row)) events (sequence xform events)] (when (seq events) - (db/insert-multi! pool :audit-log event-columns events)))) + (db/insert-many! pool :audit-log event-columns events)))) (def schema:event [:map {:title "Event"} diff --git a/backend/src/app/rpc/commands/auth.clj b/backend/src/app/rpc/commands/auth.clj index 13d05bf0f3..949d528acf 100644 --- a/backend/src/app/rpc/commands/auth.clj +++ b/backend/src/app/rpc/commands/auth.clj @@ -54,7 +54,9 @@ :hint "the current account does not have password") (let [result (profile/verify-password cfg password (:password profile))] (when (:update result) - (l/trace :hint "updating profile password" :id (:id profile) :email (:email profile)) + (l/trc :hint "updating profile password" + :id (str (:id profile)) + :email (:email profile)) (profile/update-profile-password! conn (assoc profile :password password))) (:valid result)))) @@ -131,7 +133,8 @@ (update-password [conn profile-id] (let [pwd (profile/derive-password cfg password)] - (db/update! conn :profile {:password pwd} {:id profile-id})))] + (db/update! conn :profile {:password pwd} {:id profile-id}) + nil))] (db/with-atomic [conn pool] (->> (validate-token token) @@ -301,7 +304,8 @@ (-> (db/update! conn :profile {:default-team-id (:id team) :default-project-id (:default-project-id team)} - {:id id}) + {:id id} + {::db/return-keys true}) (profile/decode-row)))) diff --git a/backend/src/app/rpc/commands/binfile.clj b/backend/src/app/rpc/commands/binfile.clj index 7e7327b32e..c173ec3bb6 100644 --- a/backend/src/app/rpc/commands/binfile.clj +++ b/backend/src/app/rpc/commands/binfile.clj @@ -317,7 +317,7 @@ [cfg file-id] (db/run! cfg (fn [{:keys [::db/conn] :as cfg}] (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)] - (some-> (db/get* conn :file {:id file-id} {::db/remove-deleted? false}) + (some-> (db/get* conn :file {:id file-id} {::db/remove-deleted false}) (files/decode-row) (update :data feat.fdata/process-pointers deref)))))) @@ -593,6 +593,7 @@ (declare lookup-index) (declare update-index) (declare relink-media) +(declare relink-colors) (declare relink-shapes) (defmulti read-import ::version) @@ -663,6 +664,7 @@ (case feature "components/v2" (feat.compv2/migrate-file! options file-id + :max-procs 2 :validate? validate? :throw-on-validate? true) @@ -723,6 +725,7 @@ (update :pages-index relink-shapes) (update :components relink-shapes) (update :media relink-media) + (update :colors relink-colors) (d/without-nils)))))) @@ -997,6 +1000,17 @@ media media)) +(defn- relink-colors + "A function responsible of process the :colors attr of file data and + remap the old ids with the new ones." + [colors] + (reduce-kv (fn [res k v] + (if (:image v) + (update-in res [k :image :id] lookup-index) + res)) + colors + colors)) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; HIGH LEVEL API ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/backend/src/app/rpc/commands/comments.clj b/backend/src/app/rpc/commands/comments.clj index 5e87884f6d..9e1a9d4365 100644 --- a/backend/src/app/rpc/commands/comments.clj +++ b/backend/src/app/rpc/commands/comments.clj @@ -12,6 +12,7 @@ [app.common.spec :as us] [app.common.uuid :as uuid] [app.db :as db] + [app.db.sql :as sql] [app.features.fdata :as feat.fdata] [app.loggers.audit :as-alias audit] [app.loggers.webhooks :as-alias webhooks] @@ -62,8 +63,8 @@ (decode-row))) (defn- get-comment - [conn comment-id & {:keys [for-update?]}] - (db/get-by-id conn :comment comment-id {:for-update for-update?})) + [conn comment-id & {:as opts}] + (db/get-by-id conn :comment comment-id opts)) (defn- get-next-seqn [conn file-id] @@ -309,23 +310,21 @@ ::quotes/project-id project-id ::quotes/file-id file-id})) - (rtry/with-retry {::rtry/when rtry/conflict-exception? - ::rtry/max-retries 3 - ::rtry/label "create-comment-thread" - ::db/conn conn} - (create-comment-thread conn - {:created-at request-at - :profile-id profile-id - :file-id file-id - :page-id page-id - :page-name page-name - :position position - :content content - :frame-id frame-id})))))) + (-> cfg + (assoc ::rtry/when rtry/conflict-exception?) + (assoc ::rtry/label "create-comment-thread") + (rtry/invoke create-comment-thread {:created-at request-at + :profile-id profile-id + :file-id file-id + :page-id page-id + :page-name page-name + :position position + :content content + :frame-id frame-id})))))) (defn- create-comment-thread - [conn {:keys [profile-id file-id page-id page-name created-at position content frame-id]}] + [{:keys [::db/conn]} {:keys [profile-id file-id page-id page-name created-at position content frame-id]}] (let [;; NOTE: we take the next seq number from a separate query because the whole ;; operation can be retried on conflict, and in this case the new seq shold be ;; retrieved from the database. @@ -377,7 +376,7 @@ {::doc/added "1.15"} [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id id share-id] :as params}] (db/with-atomic [conn pool] - (let [{:keys [file-id] :as thread} (get-comment-thread conn id ::db/for-update? true)] + (let [{:keys [file-id] :as thread} (get-comment-thread conn id ::sql/for-update true)] (files/check-comment-permissions! conn profile-id file-id share-id) (upsert-comment-thread-status! conn profile-id id)))) @@ -394,7 +393,7 @@ {::doc/added "1.15"} [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id id is-resolved share-id] :as params}] (db/with-atomic [conn pool] - (let [{:keys [file-id] :as thread} (get-comment-thread conn id ::db/for-update? true)] + (let [{:keys [file-id] :as thread} (get-comment-thread conn id ::sql/for-update true)] (files/check-comment-permissions! conn profile-id file-id share-id) (db/update! conn :comment-thread {:is-resolved is-resolved} @@ -417,7 +416,7 @@ [cfg {:keys [::rpc/profile-id ::rpc/request-at thread-id share-id content]}] (db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}] - (let [{:keys [file-id page-id] :as thread} (get-comment-thread conn thread-id ::db/for-update? true) + (let [{:keys [file-id page-id] :as thread} (get-comment-thread conn thread-id ::sql/for-update true) {:keys [team-id project-id page-name] :as file} (get-file cfg file-id page-id)] (files/check-comment-permissions! conn profile-id (:id file) share-id) @@ -473,8 +472,8 @@ (db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}] - (let [{:keys [thread-id owner-id] :as comment} (get-comment conn id ::db/for-update? true) - {:keys [file-id page-id] :as thread} (get-comment-thread conn thread-id ::db/for-update? true)] + (let [{:keys [thread-id owner-id] :as comment} (get-comment conn id ::sql/for-update true) + {:keys [file-id page-id] :as thread} (get-comment-thread conn thread-id ::sql/for-update true)] (files/check-comment-permissions! conn profile-id file-id share-id) @@ -506,7 +505,7 @@ {::doc/added "1.15"} [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id id share-id]}] (db/with-atomic [conn pool] - (let [{:keys [owner-id file-id] :as thread} (get-comment-thread conn id ::db/for-update? true)] + (let [{:keys [owner-id file-id] :as thread} (get-comment-thread conn id ::sql/for-update true)] (files/check-comment-permissions! conn profile-id file-id share-id) (when-not (= owner-id profile-id) (ex/raise :type :validation @@ -526,14 +525,14 @@ {::doc/added "1.15"} [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id id share-id] :as params}] (db/with-atomic [conn pool] - (let [{:keys [owner-id thread-id] :as comment} (get-comment conn id ::db/for-update? true) + (let [{:keys [owner-id thread-id] :as comment} (get-comment conn id ::sql/for-update true) {:keys [file-id] :as thread} (get-comment-thread conn thread-id)] (files/check-comment-permissions! conn profile-id file-id share-id) (when-not (= owner-id profile-id) (ex/raise :type :validation :code :not-allowed)) - (db/delete! conn :comment {:id id})))) - + (db/delete! conn :comment {:id id}) + nil))) ;; --- COMMAND: Update comment thread position @@ -546,7 +545,7 @@ {::doc/added "1.15"} [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id ::rpc/request-at id position frame-id share-id]}] (db/with-atomic [conn pool] - (let [{:keys [file-id] :as thread} (get-comment-thread conn id ::db/for-update? true)] + (let [{:keys [file-id] :as thread} (get-comment-thread conn id ::sql/for-update true)] (files/check-comment-permissions! conn profile-id file-id share-id) (db/update! conn :comment-thread {:modified-at request-at @@ -566,7 +565,7 @@ {::doc/added "1.15"} [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id ::rpc/request-at id frame-id share-id]}] (db/with-atomic [conn pool] - (let [{:keys [file-id] :as thread} (get-comment-thread conn id ::db/for-update? true)] + (let [{:keys [file-id] :as thread} (get-comment-thread conn id ::sql/for-update true)] (files/check-comment-permissions! conn profile-id file-id share-id) (db/update! conn :comment-thread {:modified-at request-at diff --git a/backend/src/app/rpc/commands/files.clj b/backend/src/app/rpc/commands/files.clj index 096e961957..58b10742d7 100644 --- a/backend/src/app/rpc/commands/files.clj +++ b/backend/src/app/rpc/commands/files.clj @@ -20,6 +20,7 @@ [app.common.types.file :as ctf] [app.config :as cf] [app.db :as db] + [app.db.sql :as-alias sql] [app.features.fdata :as feat.fdata] [app.loggers.audit :as-alias audit] [app.loggers.webhooks :as-alias webhooks] @@ -238,8 +239,7 @@ (db/update! conn :file {:data (blob/encode (:data file)) :features (db/create-array conn "text" (:features file))} - {:id id} - {::db/return-keys? false}) + {:id id}) (when (contains? (:features file) "fdata/pointer-map") (feat.fdata/persist-pointers! cfg id))) @@ -262,9 +262,9 @@ (when (some? project-id) {:project-id project-id})) file (-> (db/get conn :file params - {::db/check-deleted? (not include-deleted?) - ::db/remove-deleted? (not include-deleted?) - ::db/for-update? lock-for-update?}) + {::db/check-deleted (not include-deleted?) + ::db/remove-deleted (not include-deleted?) + ::sql/for-update lock-for-update?}) (decode-row))] (if migrate? (migrate-file cfg file) @@ -516,7 +516,7 @@ ft.media_id from file as f inner join project as p on (p.id = f.project_id) - left join file_thumbnail as ft on (ft.file_id = f.id and ft.revn = f.revn) + left join file_thumbnail as ft on (ft.file_id = f.id and ft.revn = f.revn and ft.deleted_at is null) where f.is_shared = true and f.deleted_at is null and p.deleted_at is null @@ -733,7 +733,8 @@ (db/update! conn :file {:name name :modified-at (dt/now)} - {:id id})) + {:id id} + {::db/return-keys true})) (sv/defmethod ::rename-file {::doc/added "1.17" @@ -860,9 +861,7 @@ (let [file (assoc file :is-shared true)] (db/update! conn :file {:is-shared true} - {:id id} - ::db/return-keys? false) - + {:id id}) file) :else @@ -899,7 +898,7 @@ (db/update! conn :file {:deleted-at (dt/now)} {:id file-id} - {::db/columns [:id :name :is-shared :project-id :created-at :modified-at]})) + {::db/return-keys [:id :name :is-shared :project-id :created-at :modified-at]})) (def ^:private schema:delete-file @@ -998,8 +997,8 @@ [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}] (db/with-atomic [conn pool] (check-edition-permissions! conn profile-id file-id) - (unlink-file-from-library conn params))) - + (unlink-file-from-library conn params) + nil)) ;; --- MUTATION COMMAND: update-sync @@ -1008,7 +1007,8 @@ (db/update! conn :file-library-rel {:synced-at (dt/now)} {:file-id file-id - :library-file-id library-id})) + :library-file-id library-id} + {::db/return-keys true})) (def ^:private schema:update-file-library-sync-status [:map {:title "update-file-library-sync-status"} @@ -1031,7 +1031,8 @@ [conn {:keys [file-id date] :as params}] (db/update! conn :file {:ignore-sync-until date} - {:id file-id})) + {:id file-id} + {::db/return-keys true})) (s/def ::ignore-file-library-sync-status (s/keys :req [::rpc/profile-id] diff --git a/backend/src/app/rpc/commands/files_thumbnails.clj b/backend/src/app/rpc/commands/files_thumbnails.clj index 19f36072f4..712c212047 100644 --- a/backend/src/app/rpc/commands/files_thumbnails.clj +++ b/backend/src/app/rpc/commands/files_thumbnails.clj @@ -17,6 +17,7 @@ [app.common.types.shape-tree :as ctt] [app.config :as cf] [app.db :as db] + [app.db.sql :as-alias sql] [app.features.fdata :as feat.fdata] [app.loggers.audit :as-alias audit] [app.loggers.webhooks :as-alias webhooks] @@ -27,6 +28,7 @@ [app.rpc.commands.teams :as teams] [app.rpc.cond :as-alias cond] [app.rpc.doc :as-alias doc] + [app.rpc.retry :as rtry] [app.storage :as sto] [app.util.pointer-map :as pmap] [app.util.services :as sv] @@ -46,7 +48,7 @@ (let [sql (str/concat "select object_id, media_id, tag " " from file_tagged_object_thumbnail" - " where file_id=? and tag=?") + " where file_id=? and tag=? and deleted_at is null") res (db/exec! conn [sql file-id tag])] (->> res (d/index-by :object-id (fn [row] @@ -58,7 +60,7 @@ (let [sql (str/concat "select object_id, media_id, tag " " from file_tagged_object_thumbnail" - " where file_id=?") + " where file_id=? and deleted_at is null") res (db/exec! conn [sql file-id])] (->> res (d/index-by :object-id (fn [row] @@ -69,7 +71,7 @@ (let [sql (str/concat "select object_id, media_id, tag " " from file_tagged_object_thumbnail" - " where file_id=? and object_id = ANY(?)") + " where file_id=? and object_id = ANY(?) and deleted_at is null") ids (db/create-array conn "text" (seq object-ids)) res (db/exec! conn [sql file-id ids])] @@ -226,34 +228,54 @@ ;; MUTATION COMMANDS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; --- MUTATION COMMAND: create-file-object-thumbnail - -(def ^:private sql:create-object-thumbnail - "insert into file_tagged_object_thumbnail(file_id, object_id, media_id, tag) - values (?, ?, ?, ?) - on conflict(file_id, tag, object_id) do - update set media_id = ? - returning *;") +;; MUTATION COMMAND: create-file-object-thumbnail (defn- create-file-object-thumbnail! [{:keys [::db/conn ::sto/storage]} file-id object-id media tag] - (let [path (:path media) + (let [thumb (db/get* conn :file-tagged-object-thumbnail + {:file-id file-id + :object-id object-id + :tag tag} + {::db/remove-deleted false + ::sql/for-update true}) + + path (:path media) mtype (:mtype media) hash (sto/calculate-hash path) data (-> (sto/content path) (sto/wrap-with-hash hash)) + tnow (dt/now) + media (sto/put-object! storage {::sto/content data ::sto/deduplicate? true - ::sto/touched-at (dt/now) + ::sto/touched-at tnow :content-type mtype :bucket "file-object-thumbnail"})] - (db/exec-one! conn [sql:create-object-thumbnail file-id object-id - (:id media) tag (:id media)]))) + (if (some? thumb) + (do + ;; We mark the old media id as touched if it does not matches + (when (not= (:id media) (:media-id thumb)) + (sto/touch-object! storage (:media-id thumb))) + (db/update! conn :file-tagged-object-thumbnail + {:media-id (:id media) + :deleted-at nil + :updated-at tnow} + {:file-id file-id + :object-id object-id + :tag tag})) + (db/insert! conn :file-tagged-object-thumbnail + {:file-id file-id + :object-id object-id + :created-at tnow + :updated-at tnow + :tag tag + :media-id (:id media)})))) -(def schema:create-file-object-thumbnail +(def ^:private + schema:create-file-object-thumbnail [:map {:title "create-file-object-thumbnail"} [:file-id ::sm/uuid] [:object-id :string] @@ -268,32 +290,36 @@ ::audit/skip true ::sm/params schema:create-file-object-thumbnail} - [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id file-id object-id media tag]}] - (db/with-atomic [conn pool] - (files/check-edition-permissions! conn profile-id file-id) - (media/validate-media-type! media) - (media/validate-media-size! media) + [cfg {:keys [::rpc/profile-id file-id object-id media tag]}] + (db/tx-run! cfg + (fn [{:keys [::db/conn] :as cfg}] + (files/check-edition-permissions! conn profile-id file-id) + (media/validate-media-type! media) + (media/validate-media-size! media) - (when-not (db/read-only? conn) - (-> cfg - (update ::sto/storage media/configure-assets-storage) - (assoc ::db/conn conn) - (create-file-object-thumbnail! file-id object-id media (or tag "frame")))))) + (when-not (db/read-only? conn) + (let [cfg (-> cfg + (update ::sto/storage media/configure-assets-storage) + (assoc ::rtry/when rtry/conflict-exception?) + (assoc ::rtry/max-retries 5) + (assoc ::rtry/label "create-file-object-thumbnail"))] + (rtry/invoke cfg create-file-object-thumbnail! + file-id object-id media (or tag "frame"))))))) ;; --- MUTATION COMMAND: delete-file-object-thumbnail (defn- delete-file-object-thumbnail! [{:keys [::db/conn ::sto/storage]} file-id object-id] - (when-let [{:keys [media-id]} (db/get* conn :file-tagged-object-thumbnail - {:file-id file-id - :object-id object-id} - {::db/for-update? true})] - + (when-let [{:keys [media-id tag]} (db/get* conn :file-tagged-object-thumbnail + {:file-id file-id + :object-id object-id} + {::sql/for-update true})] (sto/touch-object! storage media-id) - (db/delete! conn :file-tagged-object-thumbnail + (db/update! conn :file-tagged-object-thumbnail + {:deleted-at (dt/now)} {:file-id file-id - :object-id object-id}) - nil)) + :object-id object-id + :tag tag}))) (s/def ::delete-file-object-thumbnail (s/keys :req [::rpc/profile-id] @@ -302,29 +328,21 @@ (sv/defmethod ::delete-file-object-thumbnail {::doc/added "1.19" ::doc/module :files + ::doc/deprecated "1.20" ::climit/id :file-thumbnail-ops ::climit/key-fn ::rpc/profile-id ::audit/skip true} - [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id file-id object-id]}] - - (db/with-atomic [conn pool] - (files/check-edition-permissions! conn profile-id file-id) - - (when-not (db/read-only? conn) - (-> cfg - (update ::sto/storage media/configure-assets-storage) - (assoc ::db/conn conn) - (delete-file-object-thumbnail! file-id object-id)) - nil))) + [cfg {:keys [::rpc/profile-id file-id object-id]}] + (db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}] + (files/check-edition-permissions! conn profile-id file-id) + (when-not (db/read-only? conn) + (-> cfg + (update ::sto/storage media/configure-assets-storage conn) + (delete-file-object-thumbnail! file-id object-id)) + nil)))) ;; --- MUTATION COMMAND: create-file-thumbnail -(def ^:private sql:create-file-thumbnail - "insert into file_thumbnail (file_id, revn, media_id, props) - values (?, ?, ?, ?::jsonb) - on conflict(file_id, revn) do - update set media_id=?, props=?, updated_at=now();") - (defn- create-file-thumbnail! [{:keys [::db/conn ::sto/storage]} {:keys [file-id revn props media] :as params}] (media/validate-media-type! media) @@ -336,14 +354,42 @@ hash (sto/calculate-hash path) data (-> (sto/content path) (sto/wrap-with-hash hash)) + tnow (dt/now) media (sto/put-object! storage {::sto/content data - ::sto/deduplicate? false + ::sto/deduplicate? true + ::sto/touched-at tnow :content-type mtype - :bucket "file-thumbnail"})] - (db/exec-one! conn [sql:create-file-thumbnail file-id revn - (:id media) props - (:id media) props]) + :bucket "file-thumbnail"}) + + thumb (db/get* conn :file-thumbnail + {:file-id file-id + :revn revn} + {::db/remove-deleted false + ::sql/for-update true})] + + (if (some? thumb) + (do + ;; We mark the old media id as touched if it does not match + (when (not= (:id media) (:media-id thumb)) + (sto/touch-object! storage (:media-id thumb))) + + (db/update! conn :file-thumbnail + {:media-id (:id media) + :deleted-at nil + :updated-at tnow + :props props} + {:file-id file-id + :revn revn})) + + (db/insert! conn :file-thumbnail + {:file-id file-id + :revn revn + :created-at tnow + :updated-at tnow + :props props + :media-id (:id media)})) + media)) (sv/defmethod ::create-file-thumbnail @@ -359,13 +405,14 @@ [:revn :int] [:media ::media/upload]]} - [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}] - (db/with-atomic [conn pool] - (files/check-edition-permissions! conn profile-id file-id) - (when-not (db/read-only? conn) - (let [media (-> cfg - (update ::sto/storage media/configure-assets-storage) - (assoc ::db/conn conn) - (create-file-thumbnail! params))] - - {:uri (files/resolve-public-uri (:id media))})))) + [cfg {:keys [::rpc/profile-id file-id] :as params}] + (db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}] + (files/check-edition-permissions! conn profile-id file-id) + (when-not (db/read-only? conn) + (let [cfg (-> cfg + (update ::sto/storage media/configure-assets-storage) + (assoc ::rtry/when rtry/conflict-exception?) + (assoc ::rtry/max-retries 5) + (assoc ::rtry/label "create-thumbnail")) + media (rtry/invoke cfg create-file-thumbnail! params)] + {:uri (files/resolve-public-uri (:id media))}))))) diff --git a/backend/src/app/rpc/commands/files_update.clj b/backend/src/app/rpc/commands/files_update.clj index 7a27a834e8..03e1b04da4 100644 --- a/backend/src/app/rpc/commands/files_update.clj +++ b/backend/src/app/rpc/commands/files_update.clj @@ -250,7 +250,8 @@ :features (db/create-array conn "text" (:features file)) :data (when (take-snapshot? file) (:data file)) - :changes (blob/encode changes)}) + :changes (blob/encode changes)} + {::db/return-keys false}) (db/update! conn :file {:revn (:revn file) @@ -305,7 +306,7 @@ (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id) pmap/*tracked* nil] (-> (files/get-file cfg id :migrate? false) - (feat.fdata/process-pointers deref) ; ensure all pointers resolved + (update :data feat.fdata/process-pointers deref) ; ensure all pointers resolved (fmg/migrate-file)))))) (d/index-by :id))) diff --git a/backend/src/app/rpc/commands/fonts.clj b/backend/src/app/rpc/commands/fonts.clj index 4fc55a77f9..c19b8a2854 100644 --- a/backend/src/app/rpc/commands/fonts.clj +++ b/backend/src/app/rpc/commands/fonts.clj @@ -8,9 +8,10 @@ (:require [app.common.data.macros :as dm] [app.common.exceptions :as ex] - [app.common.spec :as us] + [app.common.schema :as sm] [app.common.uuid :as uuid] [app.db :as db] + [app.db.sql :as-alias sql] [app.loggers.audit :as-alias audit] [app.loggers.webhooks :as-alias webhooks] [app.media :as media] @@ -25,39 +26,27 @@ [app.storage :as sto] [app.util.services :as sv] [app.util.time :as dt] - [app.worker :as-alias wrk] - [clojure.spec.alpha :as s])) + [app.worker :as-alias wrk])) (def valid-weight #{100 200 300 400 500 600 700 800 900 950}) (def valid-style #{"normal" "italic"}) -(s/def ::data (s/map-of ::us/string any?)) -(s/def ::file-id ::us/uuid) -(s/def ::font-id ::us/uuid) -(s/def ::id ::us/uuid) -(s/def ::name ::us/not-empty-string) -(s/def ::project-id ::us/uuid) -(s/def ::share-id ::us/uuid) -(s/def ::style valid-style) -(s/def ::team-id ::us/uuid) -(s/def ::weight valid-weight) - ;; --- QUERY: Get font variants -(s/def ::get-font-variants - (s/and - (s/keys :req [::rpc/profile-id] - :opt-un [::team-id - ::file-id - ::project-id - ::share-id]) - (fn [o] - (or (contains? o :team-id) - (contains? o :file-id) - (contains? o :project-id))))) +(def ^:private + schema:get-font-variants + [:schema {:title "get-font-variants"} + [:and + [:map + [:team-id {:optional true} ::sm/uuid] + [:file-id {:optional true} ::sm/uuid] + [:project-id {:optional true} ::sm/uuid] + [:share-id {:optional true} ::sm/uuid]] + [::sm/contains-any #{:team-id :file-id :project-id}]]]) (sv/defmethod ::get-font-variants - {::doc/added "1.18"} + {::doc/added "1.18" + ::sm/params schema:get-font-variants} [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id team-id file-id project-id share-id] :as params}] (dm/with-open [conn (db/open pool)] (cond @@ -87,28 +76,31 @@ (declare create-font-variant) -(s/def ::create-font-variant - (s/keys :req [::rpc/profile-id] - :req-un [::team-id - ::data - ::font-id - ::font-family - ::font-weight - ::font-style])) +(def ^:private schema:create-font-variant + [:map {:title "create-font-variant"} + [:team-id ::sm/uuid] + [:data [:map-of :string :any]] + [:font-id ::sm/uuid] + [:font-family :string] + [:font-weight [::sm/one-of {:format "number"} valid-weight]] + [:font-style [::sm/one-of {:format "string"} valid-style]]]) (sv/defmethod ::create-font-variant {::doc/added "1.18" - ::webhooks/event? true} - [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id team-id] :as params}] - (let [cfg (update cfg ::sto/storage media/configure-assets-storage)] - (teams/check-edition-permissions! pool profile-id team-id) - (quotes/check-quote! pool {::quotes/id ::quotes/font-variants-per-team - ::quotes/profile-id profile-id - ::quotes/team-id team-id}) - (create-font-variant cfg (assoc params :profile-id profile-id)))) + ::webhooks/event? true + ::sm/params schema:create-font-variant} + [cfg {:keys [::rpc/profile-id team-id] :as params}] + (db/tx-run! cfg + (fn [{:keys [::db/conn] :as cfg}] + (let [cfg (update cfg ::sto/storage media/configure-assets-storage)] + (teams/check-edition-permissions! conn profile-id team-id) + (quotes/check-quote! conn {::quotes/id ::quotes/font-variants-per-team + ::quotes/profile-id profile-id + ::quotes/team-id team-id}) + (create-font-variant cfg (assoc params :profile-id profile-id)))))) (defn create-font-variant - [{:keys [::sto/storage ::db/pool] :as cfg} {:keys [data] :as params}] + [{:keys [::sto/storage ::db/conn] :as cfg} {:keys [data] :as params}] (letfn [(generate-missing! [data] (let [data (media/run {:cmd :generate-fonts :input data})] (when (and (not (contains? data "font/otf")) @@ -136,6 +128,7 @@ ttf-params (prepare-font data "font/ttf") wf1-params (prepare-font data "font/woff") wf2-params (prepare-font data "font/woff2")] + (cond-> {} (some? otf-params) (assoc :otf (sto/put-object! storage otf-params)) @@ -147,7 +140,7 @@ (assoc :woff2 (sto/put-object! storage wf2-params))))) (insert-font-variant! [{:keys [woff1 woff2 otf ttf]}] - (db/insert! pool :team-font-variant + (db/insert! conn :team-font-variant {:id (uuid/next) :team-id (:team-id params) :font-id (:font-id params) @@ -168,63 +161,105 @@ ;; --- UPDATE FONT FAMILY -(s/def ::update-font - (s/keys :req [::rpc/profile-id] - :req-un [::team-id ::id ::name])) +(def ^:private + schema:update-font + [:map {:title "update-font"} + [:team-id ::sm/uuid] + [:id ::sm/uuid] + [:name :string]]) (sv/defmethod ::update-font {::doc/added "1.18" - ::webhooks/event? true} - [{:keys [::db/pool]} {:keys [::rpc/profile-id team-id id name]}] - (db/with-atomic [conn pool] - (teams/check-edition-permissions! conn profile-id team-id) - (rph/with-meta - (db/update! conn :team-font-variant - {:font-family name} - {:font-id id - :team-id team-id}) - {::audit/replace-props {:id id - :name name - :team-id team-id - :profile-id profile-id}}))) + ::webhooks/event? true + ::sm/params schema:update-font} + [cfg {:keys [::rpc/profile-id team-id id name]}] + (db/tx-run! cfg + (fn [{:keys [::db/conn]}] + (teams/check-edition-permissions! conn profile-id team-id) + + (db/update! conn :team-font-variant + {:font-family name} + {:font-id id + :team-id team-id}) + + (rph/with-meta (rph/wrap nil) + {::audit/replace-props {:id id + :name name + :team-id team-id + :profile-id profile-id}})))) ;; --- DELETE FONT -(s/def ::delete-font - (s/keys :req [::rpc/profile-id] - :req-un [::team-id ::id])) +(def ^:private + schema:delete-font + [:map {:title "delete-font"} + [:team-id ::sm/uuid] + [:id ::sm/uuid]]) (sv/defmethod ::delete-font {::doc/added "1.18" - ::webhooks/event? true} - [{:keys [::db/pool]} {:keys [::rpc/profile-id id team-id]}] - (db/with-atomic [conn pool] - (teams/check-edition-permissions! conn profile-id team-id) - (let [font (db/update! conn :team-font-variant - {:deleted-at (dt/now)} - {:font-id id :team-id team-id})] - (rph/with-meta (rph/wrap) - {::audit/props {:id id - :team-id team-id - :name (:font-family font) - :profile-id profile-id}})))) + ::webhooks/event? true + ::sm/params schema:delete-font} + [cfg {:keys [::rpc/profile-id id team-id]}] + (db/tx-run! cfg + (fn [{:keys [::db/conn ::sto/storage] :as cfg}] + (teams/check-edition-permissions! conn profile-id team-id) + (let [fonts (db/query conn :team-font-variant + {:team-id team-id + :font-id id + :deleted-at nil} + {::sql/for-update true}) + storage (media/configure-assets-storage storage conn) + tnow (dt/now)] + + (when-not (seq fonts) + (ex/raise :type :not-found + :code :object-not-found)) + + (doseq [font fonts] + (db/update! conn :team-font-variant + {:deleted-at tnow} + {:id (:id font)}) + (some->> (:woff1-file-id font) (sto/touch-object! storage)) + (some->> (:woff2-file-id font) (sto/touch-object! storage)) + (some->> (:ttf-file-id font) (sto/touch-object! storage)) + (some->> (:otf-file-id font) (sto/touch-object! storage))) + + (rph/with-meta (rph/wrap) + {::audit/props {:id id + :team-id team-id + :name (:font-family (peek fonts)) + :profile-id profile-id}}))))) ;; --- DELETE FONT VARIANT -(s/def ::delete-font-variant - (s/keys :req [::rpc/profile-id] - :req-un [::team-id ::id])) +(def ^:private schema:delete-font-variant + [:map {:title "delete-font-variant"} + [:team-id ::sm/uuid] + [:id ::sm/uuid]]) (sv/defmethod ::delete-font-variant {::doc/added "1.18" - ::webhooks/event? true} - [{:keys [::db/pool]} {:keys [::rpc/profile-id id team-id]}] - (db/with-atomic [conn pool] - (teams/check-edition-permissions! conn profile-id team-id) - (let [variant (db/update! conn :team-font-variant - {:deleted-at (dt/now)} - {:id id :team-id team-id})] - (rph/with-meta (rph/wrap) - {::audit/props {:font-family (:font-family variant) - :font-id (:font-id variant)}})))) + ::webhooks/event? true + ::sm/params schema:delete-font-variant} + [cfg {:keys [::rpc/profile-id id team-id]}] + (db/tx-run! cfg + (fn [{:keys [::db/conn ::sto/storage] :as cfg}] + (teams/check-edition-permissions! conn profile-id team-id) + (let [variant (db/get conn :team-font-variant + {:id id :team-id team-id} + {::sql/for-update true}) + storage (media/configure-assets-storage storage conn)] + (db/update! conn :team-font-variant + {:deleted-at (dt/now)} + {:id (:id variant)}) + + (some->> (:woff1-file-id variant) (sto/touch-object! storage)) + (some->> (:woff2-file-id variant) (sto/touch-object! storage)) + (some->> (:ttf-file-id variant) (sto/touch-object! storage)) + (some->> (:otf-file-id variant) (sto/touch-object! storage)) + + (rph/with-meta (rph/wrap) + {::audit/props {:font-family (:font-family variant) + :font-id (:font-id variant)}}))))) diff --git a/backend/src/app/rpc/commands/management.clj b/backend/src/app/rpc/commands/management.clj index 98f08a8672..aaf51c3a0c 100644 --- a/backend/src/app/rpc/commands/management.clj +++ b/backend/src/app/rpc/commands/management.clj @@ -215,7 +215,7 @@ (-> file (update :features #(db/create-array conn "text" %)) (update :data blob/encode)) - {::db/return-keys? false}) + {::db/return-keys false}) ;; The file profile creation is optional, so when no profile is ;; present (when this function is called from profile less @@ -231,10 +231,10 @@ {::db/return-keys? false})) (doseq [params flibs] - (db/insert! conn :file-library-rel params ::db/return-keys? false)) + (db/insert! conn :file-library-rel params ::db/return-keys false)) (doseq [params fmeds] - (db/insert! conn :file-media-object params ::db/return-keys? false)) + (db/insert! conn :file-media-object params ::db/return-keys false)) file)) diff --git a/backend/src/app/rpc/commands/media.clj b/backend/src/app/rpc/commands/media.clj index 04ad8bc9bc..a3dc357db5 100644 --- a/backend/src/app/rpc/commands/media.clj +++ b/backend/src/app/rpc/commands/media.clj @@ -23,6 +23,7 @@ [app.storage :as sto] [app.storage.tmp :as tmp] [app.util.services :as sv] + [app.util.time :as dt] [app.worker :as-alias wrk] [clojure.spec.alpha :as s] [cuerdas.core :as str] @@ -153,6 +154,11 @@ thumb (when-let [params (::thumb result)] (sto/put-object! storage params))] + (db/update! conn :file + {:modified-at (dt/now) + :has-media-trimmed false} + {:id file-id}) + (db/exec-one! conn [sql:create-file-media-object (or id (uuid/next)) file-id is-local name diff --git a/backend/src/app/rpc/commands/profile.clj b/backend/src/app/rpc/commands/profile.clj index 1deacf14ab..5b814abe62 100644 --- a/backend/src/app/rpc/commands/profile.clj +++ b/backend/src/app/rpc/commands/profile.clj @@ -13,6 +13,7 @@ [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] + [app.db.sql :as-alias sql] [app.email :as eml] [app.http.session :as session] [app.loggers.audit :as audit] @@ -99,7 +100,7 @@ ;; NOTE: we need to retrieve the profile independently if we use ;; it or not for explicit locking and avoid concurrent updates of ;; the same row/object. - (let [profile (-> (db/get-by-id conn :profile profile-id ::db/for-update? true) + (let [profile (-> (db/get-by-id conn :profile profile-id ::sql/for-update true) (decode-row)) ;; Update the profile map with direct params @@ -164,7 +165,7 @@ (defn- validate-password! [{:keys [::db/conn] :as cfg} {:keys [profile-id old-password] :as params}] - (let [profile (db/get-by-id conn :profile profile-id ::db/for-update? true)] + (let [profile (db/get-by-id conn :profile profile-id ::sql/for-update true)] (when (and (not= (:password profile) "!") (not (:valid (verify-password cfg old-password (:password profile))))) (ex/raise :type :validation @@ -176,7 +177,8 @@ (when-not (db/read-only? conn) (db/update! conn :profile {:password (auth/derive-password password)} - {:id id}))) + {:id id}) + nil)) ;; --- MUTATION: Update Photo @@ -202,7 +204,7 @@ (defn update-profile-photo [{:keys [::db/pool ::sto/storage] :as cfg} {:keys [profile-id file] :as params}] (let [photo (upload-photo cfg params) - profile (db/get-by-id pool :profile profile-id ::db/for-update? true)] + profile (db/get-by-id pool :profile profile-id ::sql/for-update true)] ;; Schedule deletion of old photo (when-let [id (:photo-id profile)] @@ -329,7 +331,7 @@ ::sm/params schema:update-profile-props} [{:keys [::db/pool]} {:keys [::rpc/profile-id props]}] (db/with-atomic [conn pool] - (let [profile (get-profile conn profile-id ::db/for-update? true) + (let [profile (get-profile conn profile-id ::sql/for-update true) props (reduce-kv (fn [props k v] ;; We don't accept namespaced keys (if (simple-ident? k) diff --git a/backend/src/app/rpc/commands/projects.clj b/backend/src/app/rpc/commands/projects.clj index 6b4e72091a..b8a555f449 100644 --- a/backend/src/app/rpc/commands/projects.clj +++ b/backend/src/app/rpc/commands/projects.clj @@ -9,6 +9,7 @@ [app.common.data.macros :as dm] [app.common.spec :as us] [app.db :as db] + [app.db.sql :as-alias sql] [app.loggers.audit :as-alias audit] [app.loggers.webhooks :as webhooks] [app.rpc :as-alias rpc] @@ -233,7 +234,7 @@ [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id id name] :as params}] (db/with-atomic [conn pool] (check-edition-permissions! conn profile-id id) - (let [project (db/get-by-id conn :project id ::db/for-update? true)] + (let [project (db/get-by-id conn :project id ::sql/for-update true)] (db/update! conn :project {:name name} {:id id}) @@ -259,7 +260,8 @@ (check-edition-permissions! conn profile-id id) (let [project (db/update! conn :project {:deleted-at (dt/now)} - {:id id :is-default false})] + {:id id :is-default false} + {::db/return-keys true})] (rph/with-meta (rph/wrap) {::audit/props {:team-id (:team-id project) :name (:name project) diff --git a/backend/src/app/rpc/commands/teams.clj b/backend/src/app/rpc/commands/teams.clj index 791bce1d6e..264fca2a1e 100644 --- a/backend/src/app/rpc/commands/teams.clj +++ b/backend/src/app/rpc/commands/teams.clj @@ -963,5 +963,6 @@ (let [invitation (db/delete! conn :team-invitation {:team-id team-id - :email-to (str/lower email)})] + :email-to (str/lower email)} + {::db/return-keys true})] (rph/wrap nil {::audit/props {:invitation-id (:id invitation)}}))))) diff --git a/backend/src/app/rpc/commands/webhooks.clj b/backend/src/app/rpc/commands/webhooks.clj index 1c6b812c56..13a5d02101 100644 --- a/backend/src/app/rpc/commands/webhooks.clj +++ b/backend/src/app/rpc/commands/webhooks.clj @@ -95,7 +95,8 @@ :mtype mtype :error-code nil :error-count 0} - {:id id}) + {:id id} + {::db/return-keys true}) (decode-row))) (sv/defmethod ::create-webhook diff --git a/backend/src/app/rpc/retry.clj b/backend/src/app/rpc/retry.clj index 9cb048ea91..bd9c3ea075 100644 --- a/backend/src/app/rpc/retry.clj +++ b/backend/src/app/rpc/retry.clj @@ -18,46 +18,47 @@ (and (instance? PSQLException e) (= "23505" (.getSQLState ^PSQLException e)))) -(def ^:private always-false (constantly false)) +(def ^:private always-false + (constantly false)) (defn wrap-retry - [_ f {:keys [::matches ::sv/name] :or {matches always-false} :as mdata}] + [_ f {:keys [::sv/name] :as mdata}] - (when (::enabled mdata) - (l/debug :hint "wrapping retry" :name name)) - - (if-let [max-retries (::max-retries mdata)] - (fn [cfg params] - ((fn run [retry] - (try - (f cfg params) - (catch Throwable cause - (if (matches cause) - (let [current-retry (inc retry)] - (l/trace :hint "running retry algorithm" :retry current-retry) - (if (<= current-retry max-retries) - (run current-retry) - (throw cause))) - (throw cause))))) 1)) + (if (::enabled mdata) + (let [max-retries (get mdata ::max-retries 3) + matches? (get mdata ::when always-false)] + (l/dbg :hint "wrapping retry" :name name :max-retries max-retries) + (fn [cfg params] + ((fn recursive-invoke [retry] + (try + (f cfg params) + (catch Throwable cause + (if (matches? cause) + (let [current-retry (inc retry)] + (l/wrn :hint "retrying operation" :retry current-retry :service name) + (if (<= current-retry max-retries) + (recursive-invoke current-retry) + (throw cause))) + (throw cause))))) 1))) f)) -(defmacro with-retry - [{:keys [::when ::max-retries ::label ::db/conn] :or {max-retries 3}} & body] - `(let [conn# ~conn] - (assert (or (nil? conn#) (db/connection? conn#)) "invalid database connection") - (loop [tnum# 1] - (let [result# (let [sp# (some-> conn# db/savepoint)] - (try - (let [result# (do ~@body)] - (some->> sp# (db/release! conn#)) - result#) - (catch Throwable cause# - (some->> sp# (db/rollback! conn#)) - (if (and (~when cause#) (<= tnum# ~max-retries)) - ::retry - (throw cause#)))))] - (if (= ::retry result#) - (do - (l/warn :hint "retrying operation" :label ~label :retry tnum#) - (recur (inc tnum#))) - result#))))) +(defn invoke + [{:keys [::db/conn ::max-retries] :or {max-retries 3} :as cfg} f & args] + (assert (db/connection? conn) "invalid database connection") + (loop [rnum 1] + (let [match? (get cfg ::when always-false) + result (let [spoint (db/savepoint conn)] + (try + (let [result (apply f cfg args)] + (db/release! conn spoint) + result) + (catch Throwable cause + (db/rollback! conn spoint) + (if (and (match? cause) (<= rnum max-retries)) + ::retry + (throw cause)))))] + (if (= ::retry result) + (let [label (get cfg ::label "anonymous")] + (l/warn :hint "retrying operation" :label label :retry rnum) + (recur (inc rnum))) + result)))) diff --git a/backend/src/app/srepl/cli.clj b/backend/src/app/srepl/cli.clj index c3a4bd0c12..9b4943bdcf 100644 --- a/backend/src/app/srepl/cli.clj +++ b/backend/src/app/srepl/cli.clj @@ -65,9 +65,8 @@ (let [res (db/update! conn :profile params {:email email - :deleted-at nil} - {::db/return-keys? false})] - (pos? (:next.jdbc/update-count res)))))))) + :deleted-at nil})] + (pos? (db/get-update-count res)))))))) (defmethod exec-command :delete-profile [{:keys [email soft]}] @@ -82,12 +81,10 @@ (let [res (if soft (db/update! conn :profile {:deleted-at (dt/now)} - {:email email :deleted-at nil} - {::db/return-keys? false}) + {:email email :deleted-at nil}) (db/delete! conn :profile - {:email email} - {::db/return-keys? false}))] - (pos? (:next.jdbc/update-count res)))))) + {:email email}))] + (pos? (db/get-update-count res)))))) (defmethod exec-command :search-profile [{:keys [email]}] diff --git a/backend/src/app/srepl/components_v2.clj b/backend/src/app/srepl/components_v2.clj index 598c13e6d5..049fc3574f 100644 --- a/backend/src/app/srepl/components_v2.clj +++ b/backend/src/app/srepl/components_v2.clj @@ -6,8 +6,6 @@ (ns app.srepl.components-v2 (:require - [app.common.data :as d] - [app.common.data.macros :as dm] [app.common.logging :as l] [app.common.pprint :as pp] [app.db :as db] @@ -19,6 +17,13 @@ [promesa.exec.semaphore :as ps] [promesa.util :as pu])) +(def ^:dynamic *scope* nil) +(def ^:dynamic *semaphore* nil) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; PRIVATE HELPERS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (defn- print-stats! [stats] (->> stats @@ -87,210 +92,228 @@ res (db/exec-one! pool [sql])] (:count res))) -(defn migrate-file! - [system file-id & {:keys [rollback?] :or {rollback? true}}] - (l/dbg :hint "migrate:start") - (let [tpoint (dt/tpoint)] - (try - (binding [feat/*stats* (atom {})] +(defn- mark-team-migration! + [{:keys [::db/pool]} team-id] + ;; We execute this out of transaction because we want this + ;; change to be visible to all other sessions before starting + ;; the migration + (let [sql (str "UPDATE team SET features = " + " array_append(features, 'ephimeral/v2-migration') " + " WHERE id = ?")] + (db/exec-one! pool [sql team-id]))) + +(defn- unmark-team-migration! + [{:keys [::db/pool]} team-id] + ;; We execute this out of transaction because we want this + ;; change to be visible to all other sessions before starting + ;; the migration + (let [sql (str "UPDATE team SET features = " + " array_remove(features, 'ephimeral/v2-migration') " + " WHERE id = ?")] + (db/exec-one! pool [sql team-id]))) + +(def ^:private sql:get-teams + "SELECT id, features + FROM team + WHERE deleted_at IS NULL + ORDER BY created_at ASC") + +(defn- get-teams + [conn] + (->> (db/cursor conn sql:get-teams) + (map feat/decode-row))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; PUBLIC API +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn migrate-file! + [system file-id & {:keys [rollback? max-procs] + :or {rollback? true}}] + + (l/dbg :hint "migrate:start" :rollback rollback?) + (let [tpoint (dt/tpoint) + file-id (if (string? file-id) + (parse-uuid file-id) + file-id)] + (binding [feat/*stats* (atom {})] + (try (-> (assoc system ::db/rollback rollback?) - (feat/migrate-file! file-id)) + (feat/migrate-file! file-id :max-procs max-procs)) (-> (deref feat/*stats*) - (assoc :elapsed (dt/format-duration (tpoint))))) + (assoc :elapsed (dt/format-duration (tpoint)))) - (catch Throwable cause - (l/wrn :hint "migrate:error" :cause cause)) + (catch Throwable cause + (l/wrn :hint "migrate:error" :cause cause)) - (finally - (let [elapsed (dt/format-duration (tpoint))] - (l/dbg :hint "migrate:end" :elapsed elapsed)))))) - -(defn migrate-files! - [{:keys [::db/pool] :as system} - & {:keys [chunk-size max-jobs max-items start-at preset rollback? skip-on-error validate?] - :or {chunk-size 10 - skip-on-error true - max-jobs 10 - max-items Long/MAX_VALUE - preset :shutdown-on-failure - rollback? true - validate? false}}] - (letfn [(get-chunk [cursor] - (let [sql (str/concat - "SELECT id, created_at FROM file " - " WHERE created_at < ? AND deleted_at IS NULL " - " ORDER BY created_at desc LIMIT ?") - rows (db/exec! pool [sql cursor chunk-size])] - [(some->> rows peek :created-at) (seq rows)])) - - (get-candidates [] - (->> (d/iteration get-chunk - :vf second - :kf first - :initk (or start-at (dt/now))) - (take max-items) - (map :id)))] - - (l/dbg :hint "migrate:start") - (let [fsem (ps/create :permits max-jobs) - total (get-total-files pool) - stats (atom {:files/total total}) - tpoint (dt/tpoint)] - - (add-watch stats :progress-report (report-progress-files tpoint)) - - (binding [feat/*stats* stats - feat/*semaphore* fsem - feat/*skip-on-error* skip-on-error] - (try - (pu/with-open [scope (px/structured-task-scope :preset preset :factory :virtual)] - - (run! (fn [file-id] - (ps/acquire! feat/*semaphore*) - (px/submit! scope (fn [] - (-> (assoc system ::db/rollback rollback?) - (feat/migrate-file! file-id - :validate? validate? - :throw-on-validate? (not skip-on-error)))))) - (get-candidates)) - - (p/await! scope)) - - (-> (deref feat/*stats*) - (assoc :elapsed (dt/format-duration (tpoint)))) - - (catch Throwable cause - (l/dbg :hint "migrate:error" :cause cause)) - - (finally - (let [elapsed (dt/format-duration (tpoint))] - (l/dbg :hint "migrate:end" :elapsed elapsed)))))))) + (finally + (let [elapsed (dt/format-duration (tpoint))] + (l/dbg :hint "migrate:end" :rollback rollback? :elapsed elapsed))))))) (defn migrate-team! - [{:keys [::db/pool] :as system} team-id - & {:keys [rollback? skip-on-error validate?] - :or {rollback? true skip-on-error true validate? false}}] - (l/dbg :hint "migrate:start") + [{:keys [::db/pool] :as system} team-id & {:keys [rollback? skip-on-error validate? max-procs] + :or {rollback? true + skip-on-error true + validate? false + max-procs 1 } + :as opts}] - (let [total (get-total-files pool :team-id team-id) - stats (atom {:total/files total}) - tpoint (dt/tpoint)] + (l/dbg :hint "migrate:start" :rollback rollback?) + + (let [team-id (if (string? team-id) + (parse-uuid team-id) + team-id) + total (get-total-files pool :team-id team-id) + stats (atom {:total/files total}) + tpoint (dt/tpoint)] (add-watch stats :progress-report (report-progress-files tpoint)) - (try - (binding [feat/*stats* stats - feat/*skip-on-error* skip-on-error] + (binding [feat/*stats* stats + feat/*skip-on-error* skip-on-error] + + (try + (mark-team-migration! system team-id) + (-> (assoc system ::db/rollback rollback?) (feat/migrate-team! team-id + :max-procs max-procs :validate? validate? :throw-on-validate? (not skip-on-error))) (print-stats! (-> (deref feat/*stats*) (dissoc :total/files) - (assoc :elapsed (dt/format-duration (tpoint)))))) + (assoc :elapsed (dt/format-duration (tpoint))))) - (catch Throwable cause - (l/dbg :hint "migrate:error" :cause cause)) + (catch Throwable cause + (l/dbg :hint "migrate:error" :cause cause)) - (finally - (let [elapsed (dt/format-duration (tpoint))] - (l/dbg :hint "migrate:end" :elapsed elapsed)))))) + (finally + (unmark-team-migration! system team-id) -(defn default-on-end - [stats] - (print-stats! - (-> stats - (update :elapsed/total dt/format-duration) - (dissoc :total/teams)))) + (let [elapsed (dt/format-duration (tpoint))] + (l/dbg :hint "migrate:end" :rollback rollback? :elapsed elapsed))))))) (defn migrate-teams! - [{:keys [::db/pool] :as system} - & {:keys [chunk-size max-jobs max-items start-at - rollback? validate? preset skip-on-error - max-time on-start on-progress on-error on-end] - :or {chunk-size 10000 - validate? false - rollback? true - skip-on-error true - on-end default-on-end - preset :shutdown-on-failure - max-jobs Integer/MAX_VALUE - max-items Long/MAX_VALUE}}] + "A REPL helper for migrate all teams. - (letfn [(get-chunk [cursor] - (let [sql (str/concat - "SELECT id, created_at, features FROM team " - " WHERE created_at < ? AND deleted_at IS NULL " - " ORDER BY created_at desc LIMIT ?") - rows (db/exec! pool [sql cursor chunk-size])] - [(some->> rows peek :created-at) (seq rows)])) + This function starts multiple concurrent team migration processes + until thw maximum number of jobs is reached which by default has the + value of `1`. This is controled with the `:max-jobs` option. - (get-candidates [] - (->> (d/iteration get-chunk - :vf second - :kf first - :initk (or start-at (dt/now))) - (map #(update % :features db/decode-pgarray #{})) - (remove #(contains? (:features %) "ephimeral/v2-migration")) - (take max-items) - (map :id))) + Each tram migration process also can start multiple procs for + graphics migration, the total of that procs is controled with the + `:max-procs` option. - (migrate-team [team-id] - (try - (-> (assoc system ::db/rollback rollback?) - (feat/migrate-team! team-id - :validate? validate? - :throw-on-validate? (not skip-on-error))) - (catch Throwable cause - (l/err :hint "unexpected error on processing team" :team-id (dm/str team-id) :cause cause)))) + Internally, the graphics migration process uses SVGO module which by + default has a limited number of maximum concurent + operations (globally), ensure setting up correct number with + PENPOT_SVGO_MAX_PROCS environment variable." - (process-team [scope tpoint mtime team-id] - (ps/acquire! feat/*semaphore*) - (let [ts (tpoint)] - (if (and mtime (neg? (compare mtime ts))) - (l/inf :hint "max time constraint reached" :elapsed (dt/format-duration ts)) - (px/submit! scope (partial migrate-team team-id)))))] + [{:keys [::db/pool] :as system} & {:keys [max-jobs max-procs max-items + rollback? validate? preset + skip-on-error max-time + on-start on-progress on-error on-end] + :or {validate? false + rollback? true + skip-on-error true + preset :shutdown-on-failure + max-jobs 1 + max-procs 10 + max-items Long/MAX_VALUE} + :as opts}] - (l/dbg :hint "migrate:start") + (let [total (get-total-teams pool) + stats (atom {:total/teams (min total max-items)}) - (let [sem (ps/create :permits max-jobs) - total (get-total-teams pool) - stats (atom {:total/teams (min total max-items)}) - tpoint (dt/tpoint) - mtime (some-> max-time dt/duration)] + tpoint (dt/tpoint) + mtime (some-> max-time dt/duration) - (when (fn? on-start) - (on-start {:total total :rollback rollback?})) + scope (px/structured-task-scope :preset preset :factory :virtual) + sjobs (ps/create :permits max-jobs) - (add-watch stats :progress-report (report-progress-teams tpoint on-progress)) + migrate-team + (fn [{:keys [id features] :as team}] + (ps/acquire! sjobs) + (let [ts (tpoint)] + (cond + (and mtime (neg? (compare mtime ts))) + (do + (l/inf :hint "max time constraint reached" + :team-id (str id) + :elapsed (dt/format-duration ts)) + (ps/release! sjobs) + (reduced nil)) - (binding [feat/*stats* stats - feat/*semaphore* sem - feat/*skip-on-error* skip-on-error] + (or (contains? features "ephimeral/v2-migration") + (contains? features "components/v2")) + (do + (l/dbg :hint "skip team" :team-id (str id)) + (ps/release! sjobs)) + + :else + (px/submit! scope (fn [] + (try + (mark-team-migration! system id) + (-> (assoc system ::db/rollback rollback?) + (feat/migrate-team! id + :max-procs max-procs + :validate? validate? + :throw-on-validate? (not skip-on-error))) + (catch Throwable cause + (l/err :hint "unexpected error on processing team" + :team-id (str id) + :cause cause)) + (finally + (ps/release! sjobs) + (unmark-team-migration! system id))))))))] + + (l/dbg :hint "migrate:start" + :rollback rollback? + :total total + :max-jobs max-jobs + :max-procs max-procs + :max-items max-items) + + (add-watch stats :progress-report (report-progress-teams tpoint on-progress)) + + (binding [feat/*stats* stats + feat/*skip-on-error* skip-on-error] + (try + (when (fn? on-start) + (on-start {:total total :rollback rollback?})) + + (db/tx-run! system + (fn [{:keys [::db/conn]}] + (run! (partial migrate-team) + (->> (get-teams conn) + (take max-items))))) (try - (pu/with-open [scope (px/structured-task-scope :preset preset - :factory :virtual)] - (loop [candidates (get-candidates)] - (when-let [team-id (first candidates)] - (when (process-team scope tpoint mtime team-id) - (recur (rest candidates))))) - - (p/await! scope)) - - (when (fn? on-end) - (-> (deref stats) - (assoc :elapsed/total (tpoint)) - (on-end))) - - (catch Throwable cause - (l/dbg :hint "migrate:error" :cause cause) - (when (fn? on-error) - (on-error cause))) - + (p/await! scope) (finally - (let [elapsed (dt/format-duration (tpoint))] - (l/dbg :hint "migrate:end" :elapsed elapsed)))))))) + (pu/close! scope))) + + + (if (fn? on-end) + (-> (deref stats) + (assoc :elapsed/total (tpoint)) + (on-end)) + (-> (deref stats) + (assoc :elapsed/total (tpoint)) + (update :elapsed/total dt/format-duration) + (dissoc :total/teams) + (print-stats!))) + + (catch Throwable cause + (l/dbg :hint "migrate:error" :cause cause) + (when (fn? on-error) + (on-error cause))) + + (finally + (let [elapsed (dt/format-duration (tpoint))] + (l/dbg :hint "migrate:end" + :rollback rollback? + :elapsed elapsed))))))) diff --git a/backend/src/app/srepl/helpers.clj b/backend/src/app/srepl/helpers.clj index bc8caf6c17..c135d3e763 100644 --- a/backend/src/app/srepl/helpers.clj +++ b/backend/src/app/srepl/helpers.clj @@ -83,7 +83,7 @@ (into [file] (map (fn [{:keys [id]}] (binding [pmap/*load-fn* (partial feat.fdata/load-pointer system id)] (-> (files/get-file system id :migrate? false) - (feat.fdata/process-pointers deref) + (update :data feat.fdata/process-pointers deref) (pmg/migrate-file)))))) (d/index-by :id))] (validate/validate-file file libs)))))) @@ -101,7 +101,7 @@ (into [file] (map (fn [{:keys [id]}] (binding [pmap/*load-fn* (partial feat.fdata/load-pointer system id)] (-> (files/get-file system id :migrate? false) - (feat.fdata/process-pointers deref) + (update :data feat.fdata/process-pointers deref) (pmg/migrate-file)))))) (d/index-by :id)) errors (validate/validate-file file libs) diff --git a/backend/src/app/storage.clj b/backend/src/app/storage.clj index 299fefcbb0..c27672e2a1 100644 --- a/backend/src/app/storage.clj +++ b/backend/src/app/storage.clj @@ -9,8 +9,6 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] - [app.common.exceptions :as ex] - [app.common.logging :as l] [app.common.spec :as us] [app.common.uuid :as uuid] [app.db :as db] @@ -172,8 +170,7 @@ (let [id (if (impl/object? object-or-id) (:id object-or-id) object-or-id) rs (db/update! pool-or-conn :storage-object {:touched-at (dt/now)} - {:id id} - {::db/return-keys? false})] + {:id id})] (pos? (db/get-update-count rs)))) (defn get-object-data @@ -222,231 +219,8 @@ (let [id (if (impl/object? object-or-id) (:id object-or-id) object-or-id) res (db/update! pool-or-conn :storage-object {:deleted-at (dt/now)} - {:id id} - {::db/return-keys? false})] + {:id id})] (pos? (db/get-update-count res)))) (dm/export impl/resolve-backend) (dm/export impl/calculate-hash) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Garbage Collection: Permanently delete objects -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; A task responsible to permanently delete already marked as deleted -;; storage files. The storage objects are practically never marked to -;; be deleted directly by the api call. The touched-gc is responsible -;; of collecting the usage of the object and mark it as deleted. Only -;; the TMP files are are created with expiration date in future. - -(declare sql:retrieve-deleted-objects-chunk) - -(defmethod ig/pre-init-spec ::gc-deleted-task [_] - (s/keys :req [::storage ::db/pool])) - -(defmethod ig/prep-key ::gc-deleted-task - [_ cfg] - (assoc cfg ::min-age (dt/duration {:hours 2}))) - -(defmethod ig/init-key ::gc-deleted-task - [_ {:keys [::db/pool ::storage ::min-age]}] - (letfn [(get-to-delete-chunk [cursor] - (let [sql (str "select s.* " - " from storage_object as s " - " where s.deleted_at is not null " - " and s.deleted_at < ? " - " order by s.deleted_at desc " - " limit 25") - rows (db/exec! pool [sql cursor])] - [(some-> rows peek :deleted-at) - (some->> (seq rows) (d/group-by #(-> % :backend keyword) :id #{}) seq)])) - - (get-to-delete-chunks [min-age] - (d/iteration get-to-delete-chunk - :initk (dt/minus (dt/now) min-age) - :vf second - :kf first)) - - (delete-in-bulk! [backend-id ids] - (try - (db/with-atomic [conn pool] - (let [sql "delete from storage_object where id = ANY(?)" - ids' (db/create-array conn "uuid" ids) - - total (-> (db/exec-one! conn [sql ids']) - (db/get-update-count))] - - (-> (impl/resolve-backend storage backend-id) - (impl/del-objects-in-bulk ids)) - - (doseq [id ids] - (l/dbg :hint "gc-deleted: permanently delete storage object" :backend backend-id :id id)) - - total)) - - (catch Throwable cause - (l/err :hint "gc-deleted: unexpected error on bulk deletion" - :ids (vec ids) - :cause cause) - 0)))] - - (fn [params] - (let [min-age (or (some-> params :min-age dt/duration) min-age)] - (loop [total 0 - chunks (get-to-delete-chunks min-age)] - (if-let [[backend-id ids] (first chunks)] - (let [deleted (delete-in-bulk! backend-id ids)] - (recur (+ total deleted) - (rest chunks))) - (do - (l/inf :hint "gc-deleted: task finished" - :min-age (dt/format-duration min-age) - :total total) - {:deleted total}))))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Garbage Collection: Analyze touched objects -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; This task is part of the garbage collection process of storage -;; objects and is responsible on analyzing the touched objects and -;; mark them for deletion if corresponds. -;; -;; For example: when file_media_object is deleted, the depending -;; storage_object are marked as touched. This means that some files -;; that depend on a concrete storage_object are no longer exists and -;; maybe this storage_object is no longer necessary and can be -;; eligible for elimination. This task periodically analyzes touched -;; objects and mark them as freeze (means that has other references -;; and the object is still valid) or deleted (no more references to -;; this object so is ready to be deleted). - -(declare sql:retrieve-file-media-object-nrefs) -(declare sql:retrieve-file-object-thumbnail-nrefs) -(declare sql:retrieve-profile-nrefs) -(declare sql:retrieve-team-font-variant-nrefs) -(declare sql:retrieve-touched-objects-chunk) - -(defmethod ig/pre-init-spec ::gc-touched-task [_] - (s/keys :req [::db/pool])) - -(defmethod ig/init-key ::gc-touched-task - [_ {:keys [::db/pool]}] - (letfn [(get-team-font-variant-nrefs [conn id] - (-> (db/exec-one! conn [sql:retrieve-team-font-variant-nrefs id id id id]) :nrefs)) - - (get-file-media-object-nrefs [conn id] - (-> (db/exec-one! conn [sql:retrieve-file-media-object-nrefs id id]) :nrefs)) - - (get-profile-nrefs [conn id] - (-> (db/exec-one! conn [sql:retrieve-profile-nrefs id id]) :nrefs)) - - (get-file-object-thumbnails [conn id] - (-> (db/exec-one! conn [sql:retrieve-file-object-thumbnail-nrefs id]) :nrefs)) - - (mark-freeze-in-bulk [conn ids] - (db/exec-one! conn ["update storage_object set touched_at=null where id = ANY(?)" - (db/create-array conn "uuid" ids)])) - - (mark-delete-in-bulk [conn ids] - (db/exec-one! conn ["update storage_object set deleted_at=now(), touched_at=null where id = ANY(?)" - (db/create-array conn "uuid" ids)])) - - ;; NOTE: A getter that retrieves the key witch will be used - ;; for group ids; previously we have no value, then we - ;; introduced the `:reference` prop, and then it is renamed - ;; to `:bucket` and now is string instead. This is - ;; implemented in this way for backward comaptibilty. - - ;; NOTE: we use the "file-media-object" as default value for - ;; backward compatibility because when we deploy it we can - ;; have old backend instances running in the same time as - ;; the new one and we can still have storage-objects created - ;; without bucket value. And we know that if it does not - ;; have value, it means :file-media-object. - - (get-bucket [{:keys [metadata]}] - (or (some-> metadata :bucket) - (some-> metadata :reference d/name) - "file-media-object")) - - (retrieve-touched-chunk [conn cursor] - (let [rows (->> (db/exec! conn [sql:retrieve-touched-objects-chunk cursor]) - (mapv #(d/update-when % :metadata db/decode-transit-pgobject)))] - (when (seq rows) - [(-> rows peek :created-at) - (d/group-by get-bucket :id #{} rows)]))) - - (retrieve-touched [conn] - (d/iteration (partial retrieve-touched-chunk conn) - :initk (dt/now) - :vf second - :kf first)) - - (process-objects! [conn get-fn ids bucket] - (loop [to-freeze #{} - to-delete #{} - ids (seq ids)] - (if-let [id (first ids)] - (let [nrefs (get-fn conn id)] - (if (pos? nrefs) - (do - (l/debug :hint "gc-touched: processing storage object" - :id id :status "freeze" - :bucket bucket :refs nrefs) - (recur (conj to-freeze id) to-delete (rest ids))) - (do - (l/debug :hint "gc-touched: processing storage object" - :id id :status "delete" - :bucket bucket :refs nrefs) - (recur to-freeze (conj to-delete id) (rest ids))))) - (do - (some->> (seq to-freeze) (mark-freeze-in-bulk conn)) - (some->> (seq to-delete) (mark-delete-in-bulk conn)) - [(count to-freeze) (count to-delete)]))))] - - (fn [_] - (db/with-atomic [conn pool] - (loop [to-freeze 0 - to-delete 0 - groups (retrieve-touched conn)] - (if-let [[bucket ids] (first groups)] - (let [[f d] (case bucket - "file-media-object" (process-objects! conn get-file-media-object-nrefs ids bucket) - "team-font-variant" (process-objects! conn get-team-font-variant-nrefs ids bucket) - "file-object-thumbnail" (process-objects! conn get-file-object-thumbnails ids bucket) - "profile" (process-objects! conn get-profile-nrefs ids bucket) - (ex/raise :type :internal - :code :unexpected-unknown-reference - :hint (dm/fmt "unknown reference %" bucket)))] - (recur (+ to-freeze (long f)) - (+ to-delete (long d)) - (rest groups))) - (do - (l/info :hint "gc-touched: task finished" :to-freeze to-freeze :to-delete to-delete) - {:freeze to-freeze :delete to-delete}))))))) - -(def sql:retrieve-touched-objects-chunk - "SELECT so.* - FROM storage_object AS so - WHERE so.touched_at IS NOT NULL - AND so.created_at < ? - ORDER by so.created_at DESC - LIMIT 500;") - -(def sql:retrieve-file-media-object-nrefs - "select ((select count(*) from file_media_object where media_id = ?) + - (select count(*) from file_media_object where thumbnail_id = ?)) as nrefs") - -(def sql:retrieve-file-object-thumbnail-nrefs - "select (select count(*) from file_tagged_object_thumbnail where media_id = ?) as nrefs") - -(def sql:retrieve-team-font-variant-nrefs - "select ((select count(*) from team_font_variant where woff1_file_id = ?) + - (select count(*) from team_font_variant where woff2_file_id = ?) + - (select count(*) from team_font_variant where otf_file_id = ?) + - (select count(*) from team_font_variant where ttf_file_id = ?)) as nrefs") - -(def sql:retrieve-profile-nrefs - "select ((select count(*) from profile where photo_id = ?) + - (select count(*) from team where photo_id = ?)) as nrefs") diff --git a/backend/src/app/storage/gc_deleted.clj b/backend/src/app/storage/gc_deleted.clj new file mode 100644 index 0000000000..2852c8fe6b --- /dev/null +++ b/backend/src/app/storage/gc_deleted.clj @@ -0,0 +1,125 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.storage.gc-deleted + "A task responsible to permanently delete already marked as deleted + storage files. The storage objects are practically never marked to + be deleted directly by the api call. + + The touched-gc is responsible of collecting the usage of the object + and mark it as deleted. Only the TMP files are are created with + expiration date in future." + (:require + [app.common.data :as d] + [app.common.logging :as l] + [app.db :as db] + [app.storage :as-alias sto] + [app.storage.impl :as impl] + [app.util.time :as dt] + [clojure.spec.alpha :as s] + [integrant.core :as ig])) + +(def ^:private sql:lock-sobjects + "SELECT id FROM storage_object + WHERE id = ANY(?::uuid[]) + FOR UPDATE + SKIP LOCKED") + +(defn- lock-ids + "Perform a select before delete for proper object locking and + prevent concurrent operations and we proceed only with successfully + locked objects." + [conn ids] + (let [ids (db/create-array conn "uuid" ids)] + (->> (db/exec! conn [sql:lock-sobjects ids]) + (into #{} (map :id)) + (not-empty)))) + + +(def ^:private sql:delete-sobjects + "DELETE FROM storage_object + WHERE id = ANY(?::uuid[])") + +(defn- delete-sobjects! + [conn ids] + (let [ids (db/create-array conn "uuid" ids)] + (-> (db/exec-one! conn [sql:delete-sobjects ids]) + (db/get-update-count)))) + +(defn- delete-in-bulk! + [cfg backend-id ids] + ;; We run the deletion on a separate transaction. This is + ;; because if some exception is raised inside procesing + ;; one chunk, it does not affects the rest of the chunks. + (try + (db/tx-run! cfg + (fn [{:keys [::db/conn ::sto/storage]}] + (when-let [ids (lock-ids conn ids)] + (let [total (delete-sobjects! conn ids)] + (-> (impl/resolve-backend storage backend-id) + (impl/del-objects-in-bulk ids)) + + (doseq [id ids] + (l/dbg :hint "permanently delete storage object" + :id (str id) + :backend (name backend-id))) + total)))) + (catch Throwable cause + (l/err :hint "unexpected error on bulk deletion" + :ids ids + :cause cause)))) + + +(defn- group-by-backend + [items] + (d/group-by (comp keyword :backend) :id #{} items)) + +(def ^:private sql:get-deleted-sobjects + "SELECT s.* FROM storage_object AS s + WHERE s.deleted_at IS NOT NULL + AND s.deleted_at < now() - ?::interval + ORDER BY s.deleted_at ASC") + +(defn- get-buckets + [conn min-age] + (let [age (db/interval min-age)] + (sequence + (comp (partition-all 25) + (mapcat group-by-backend)) + (db/cursor conn [sql:get-deleted-sobjects age])))) + + +(defn- clean-deleted! + [{:keys [::db/conn ::min-age] :as cfg}] + (reduce (fn [total [backend-id ids]] + (let [deleted (delete-in-bulk! cfg backend-id ids)] + (+ total (or deleted 0)))) + 0 + (get-buckets conn min-age))) + + +(defmethod ig/pre-init-spec ::handler [_] + (s/keys :req [::sto/storage ::db/pool])) + +(defmethod ig/prep-key ::handler + [_ cfg] + (assoc cfg ::min-age (dt/duration {:hours 2}))) + +(defmethod ig/init-key ::handler + [_ {:keys [::min-age] :as cfg}] + (fn [params] + (let [min-age (dt/duration (or (:min-age params) min-age))] + (db/tx-run! cfg (fn [cfg] + (let [cfg (assoc cfg ::min-age min-age) + total (clean-deleted! cfg)] + + (l/inf :hint "task finished" + :min-age (dt/format-duration min-age) + :total total) + + {:deleted total})))))) + + diff --git a/backend/src/app/storage/gc_touched.clj b/backend/src/app/storage/gc_touched.clj new file mode 100644 index 0000000000..bd499bb655 --- /dev/null +++ b/backend/src/app/storage/gc_touched.clj @@ -0,0 +1,208 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.storage.gc-touched + "This task is part of the garbage collection process of storage + objects and is responsible on analyzing the touched objects and mark + them for deletion if corresponds. + + For example: when file_media_object is deleted, the depending + storage_object are marked as touched. This means that some files + that depend on a concrete storage_object are no longer exists and + maybe this storage_object is no longer necessary and can be eligible + for elimination. This task periodically analyzes touched objects and + mark them as freeze (means that has other references and the object + is still valid) or deleted (no more references to this object so is + ready to be deleted)." + (:require + [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.exceptions :as ex] + [app.common.logging :as l] + [app.db :as db] + [app.storage :as-alias sto] + [app.storage.impl :as impl] + [clojure.spec.alpha :as s] + [integrant.core :as ig])) + +(def ^:private sql:get-team-font-variant-nrefs + "SELECT ((SELECT count(*) FROM team_font_variant WHERE woff1_file_id = ?) + + (SELECT count(*) FROM team_font_variant WHERE woff2_file_id = ?) + + (SELECT count(*) FROM team_font_variant WHERE otf_file_id = ?) + + (SELECT count(*) FROM team_font_variant WHERE ttf_file_id = ?)) AS nrefs") + +(defn- get-team-font-variant-nrefs + [conn id] + (-> (db/exec-one! conn [sql:get-team-font-variant-nrefs id id id id]) + (get :nrefs))) + + +(def ^:private + sql:get-file-media-object-nrefs + "SELECT ((SELECT count(*) FROM file_media_object WHERE media_id = ?) + + (SELECT count(*) FROM file_media_object WHERE thumbnail_id = ?)) AS nrefs") + +(defn- get-file-media-object-nrefs + [conn id] + (-> (db/exec-one! conn [sql:get-file-media-object-nrefs id id]) + (get :nrefs))) + + +(def ^:private sql:get-profile-nrefs + "SELECT ((SELECT count(*) FROM profile WHERE photo_id = ?) + + (SELECT count(*) FROM team WHERE photo_id = ?)) AS nrefs") + +(defn- get-profile-nrefs + [conn id] + (-> (db/exec-one! conn [sql:get-profile-nrefs id id]) + (get :nrefs))) + + +(def ^:private + sql:get-file-object-thumbnail-nrefs + "SELECT (SELECT count(*) FROM file_tagged_object_thumbnail WHERE media_id = ?) AS nrefs") + +(defn- get-file-object-thumbnails + [conn id] + (-> (db/exec-one! conn [sql:get-file-object-thumbnail-nrefs id]) + (get :nrefs))) + + +(def ^:private + sql:get-file-thumbnail-nrefs + "SELECT (SELECT count(*) FROM file_thumbnail WHERE media_id = ?) AS nrefs") + +(defn- get-file-thumbnails + [conn id] + (-> (db/exec-one! conn [sql:get-file-thumbnail-nrefs id]) + (get :nrefs))) + + +(def ^:private sql:mark-freeze-in-bulk + "UPDATE storage_object + SET touched_at = NULL + WHERE id = ANY(?::uuid[])") + +(defn- mark-freeze-in-bulk! + [conn ids] + (let [ids (db/create-array conn "uuid" ids)] + (db/exec-one! conn [sql:mark-freeze-in-bulk ids]))) + + +(def ^:private sql:mark-delete-in-bulk + "UPDATE storage_object + SET deleted_at = now(), + touched_at = NULL + WHERE id = ANY(?::uuid[])") + +(defn- mark-delete-in-bulk! + [conn ids] + (let [ids (db/create-array conn "uuid" ids)] + (db/exec-one! conn [sql:mark-delete-in-bulk ids]))) + +;; NOTE: A getter that retrieves the key which will be used for group +;; ids; previously we have no value, then we introduced the +;; `:reference` prop, and then it is renamed to `:bucket` and now is +;; string instead. This is implemented in this way for backward +;; comaptibilty. + +;; NOTE: we use the "file-media-object" as default value for +;; backward compatibility because when we deploy it we can +;; have old backend instances running in the same time as +;; the new one and we can still have storage-objects created +;; without bucket value. And we know that if it does not +;; have value, it means :file-media-object. + +(defn- lookup-bucket + [{:keys [metadata]}] + (or (some-> metadata :bucket) + (some-> metadata :reference d/name) + "file-media-object")) + +(defn- process-objects! + [conn get-fn ids bucket] + (loop [to-freeze #{} + to-delete #{} + ids (seq ids)] + (if-let [id (first ids)] + (let [nrefs (get-fn conn id)] + (if (pos? nrefs) + (do + (l/debug :hint "processing object" + :id (str id) + :status "freeze" + :bucket bucket :refs nrefs) + (recur (conj to-freeze id) to-delete (rest ids))) + (do + (l/debug :hint "processing object" + :id (str id) + :status "delete" + :bucket bucket :refs nrefs) + (recur to-freeze (conj to-delete id) (rest ids))))) + (do + (some->> (seq to-freeze) (mark-freeze-in-bulk! conn)) + (some->> (seq to-delete) (mark-delete-in-bulk! conn)) + [(count to-freeze) (count to-delete)])))) + +(defn- process-bucket! + [conn bucket ids] + (case bucket + "file-media-object" (process-objects! conn get-file-media-object-nrefs ids bucket) + "team-font-variant" (process-objects! conn get-team-font-variant-nrefs ids bucket) + "file-object-thumbnail" (process-objects! conn get-file-object-thumbnails ids bucket) + "file-thumbnail" (process-objects! conn get-file-thumbnails ids bucket) + "profile" (process-objects! conn get-profile-nrefs ids bucket) + (ex/raise :type :internal + :code :unexpected-unknown-reference + :hint (dm/fmt "unknown reference %" bucket)))) + + +(def ^:private + sql:get-touched-storage-objects + "SELECT so.* + FROM storage_object AS so + WHERE so.touched_at IS NOT NULL + ORDER BY touched_at ASC + FOR UPDATE + SKIP LOCKED") + +(defn- group-by-bucket + [row] + (d/group-by lookup-bucket :id #{} row)) + +(defn- get-buckets + [conn] + (sequence + (comp (map impl/decode-row) + (partition-all 25) + (mapcat group-by-bucket)) + (db/cursor conn sql:get-touched-storage-objects))) + +(defn- process-touched! + [{:keys [::db/conn]}] + (loop [buckets (get-buckets conn) + freezed 0 + deleted 0] + (if-let [[bucket ids] (first buckets)] + (let [[nfo ndo] (process-bucket! conn bucket ids)] + (recur (rest buckets) + (+ freezed nfo) + (+ deleted ndo))) + (do + (l/inf :hint "task finished" + :to-freeze freezed + :to-delete deleted) + + {:freeze freezed :delete deleted})))) + +(defmethod ig/pre-init-spec ::handler [_] + (s/keys :req [::db/pool])) + +(defmethod ig/init-key ::handler + [_ cfg] + (fn [_] + (db/tx-run! cfg process-touched!))) + diff --git a/backend/src/app/storage/impl.clj b/backend/src/app/storage/impl.clj index 9dc7facc14..156d86b872 100644 --- a/backend/src/app/storage/impl.clj +++ b/backend/src/app/storage/impl.clj @@ -9,7 +9,7 @@ (:require [app.common.data.macros :as dm] [app.common.exceptions :as ex] - [app.db :as-alias db] + [app.db :as db] [app.storage :as-alias sto] [buddy.core.codecs :as bc] [buddy.core.hash :as bh] @@ -22,6 +22,13 @@ java.nio.file.Path java.util.UUID)) +(defn decode-row + "Decode the storage-object row fields" + [{:keys [metadata] :as row}] + (cond-> row + (some? metadata) + (assoc :metadata (db/decode-transit-pgobject metadata)))) + ;; --- API Definition (defmulti put-object (fn [cfg _ _] (::sto/type cfg))) diff --git a/backend/src/app/storage/s3.clj b/backend/src/app/storage/s3.clj index 3800be401d..e019ad2676 100644 --- a/backend/src/app/storage/s3.clj +++ b/backend/src/app/storage/s3.clj @@ -39,6 +39,7 @@ software.amazon.awssdk.core.async.AsyncRequestBody software.amazon.awssdk.core.async.AsyncResponseTransformer software.amazon.awssdk.core.client.config.ClientAsyncConfiguration + software.amazon.awssdk.core.client.config.SdkAdvancedAsyncClientOption software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient software.amazon.awssdk.http.nio.netty.SdkEventLoopGroup software.amazon.awssdk.regions.Region @@ -169,32 +170,34 @@ (defn- build-s3-client [{:keys [::region ::endpoint ::io-threads]}] - (let [aconfig (-> (ClientAsyncConfiguration/builder) - (.build)) + (let [executor (px/resolve-executor :virtual) + aconfig (-> (ClientAsyncConfiguration/builder) + (.advancedOption SdkAdvancedAsyncClientOption/FUTURE_COMPLETION_EXECUTOR executor) + (.build)) - sconfig (-> (S3Configuration/builder) - (cond-> (some? endpoint) (.pathStyleAccessEnabled true)) - (.build)) + sconfig (-> (S3Configuration/builder) + (cond-> (some? endpoint) (.pathStyleAccessEnabled true)) + (.build)) - thr-num (or io-threads (min 16 (px/get-available-processors))) - hclient (-> (NettyNioAsyncHttpClient/builder) - (.eventLoopGroupBuilder (-> (SdkEventLoopGroup/builder) - (.numberOfThreads (int thr-num)))) - (.connectionAcquisitionTimeout default-timeout) - (.connectionTimeout default-timeout) - (.readTimeout default-timeout) - (.writeTimeout default-timeout) - (.build)) + thr-num (or io-threads (min 16 (px/get-available-processors))) + hclient (-> (NettyNioAsyncHttpClient/builder) + (.eventLoopGroupBuilder (-> (SdkEventLoopGroup/builder) + (.numberOfThreads (int thr-num)))) + (.connectionAcquisitionTimeout default-timeout) + (.connectionTimeout default-timeout) + (.readTimeout default-timeout) + (.writeTimeout default-timeout) + (.build)) - client (let [builder (S3AsyncClient/builder) - builder (.serviceConfiguration ^S3AsyncClientBuilder builder ^S3Configuration sconfig) - builder (.asyncConfiguration ^S3AsyncClientBuilder builder ^ClientAsyncConfiguration aconfig) - builder (.httpClient ^S3AsyncClientBuilder builder ^NettyNioAsyncHttpClient hclient) - builder (.region ^S3AsyncClientBuilder builder (lookup-region region)) - builder (cond-> ^S3AsyncClientBuilder builder - (some? endpoint) - (.endpointOverride (URI. endpoint)))] - (.build ^S3AsyncClientBuilder builder))] + client (let [builder (S3AsyncClient/builder) + builder (.serviceConfiguration ^S3AsyncClientBuilder builder ^S3Configuration sconfig) + builder (.asyncConfiguration ^S3AsyncClientBuilder builder ^ClientAsyncConfiguration aconfig) + builder (.httpClient ^S3AsyncClientBuilder builder ^NettyNioAsyncHttpClient hclient) + builder (.region ^S3AsyncClientBuilder builder (lookup-region region)) + builder (cond-> ^S3AsyncClientBuilder builder + (some? endpoint) + (.endpointOverride (URI. endpoint)))] + (.build ^S3AsyncClientBuilder builder))] (reify clojure.lang.IDeref diff --git a/backend/src/app/svgo.clj b/backend/src/app/svgo.clj new file mode 100644 index 0000000000..70d7c6b2b3 --- /dev/null +++ b/backend/src/app/svgo.clj @@ -0,0 +1,65 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.svgo + "A SVG Optimizer service" + (:require + [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.jsrt :as jsrt] + [app.common.logging :as l] + [app.common.spec :as us] + [app.worker :as-alias wrk] + [clojure.spec.alpha :as s] + [integrant.core :as ig] + [promesa.exec :as px] + [promesa.exec.bulkhead :as bh] + [promesa.exec.semaphore :as ps] + [promesa.util :as pu])) + +(def ^:dynamic *semaphore* + "A dynamic variable that can optionally contain a traffic light to + appropriately delimit the use of resources, managed externally." + nil) + +(defn optimize + [system data] + (dm/assert! "expect data to be a string" (string? data)) + + (letfn [(optimize-fn [pool] + (jsrt/run! pool + (fn [context] + (jsrt/set! context "svgData" data) + (jsrt/eval! context "penpotSvgo.optimize(svgData, {plugins: ['safeAndFastPreset']})"))))] + (try + (some-> *semaphore* ps/acquire!) + (let [{:keys [::jsrt/pool ::wrk/executor]} (::optimizer system)] + (dm/assert! "expect optimizer instance" (jsrt/pool? pool)) + (px/invoke! executor (partial optimize-fn pool))) + (finally + (some-> *semaphore* ps/release!))))) + +(s/def ::max-procs (s/nilable ::us/integer)) + +(defmethod ig/pre-init-spec ::optimizer [_] + (s/keys :req [::wrk/executor ::max-procs])) + +(defmethod ig/prep-key ::optimizer + [_ cfg] + (merge {::max-procs 20} (d/without-nils cfg))) + +(defmethod ig/init-key ::optimizer + [_ {:keys [::wrk/executor ::max-procs]}] + (l/inf :hint "initializing svg optimizer pool" :max-procs max-procs) + (let [init (jsrt/resource->source "app/common/svg/optimizer.js") + executor (bh/create :type :executor :executor executor :permits max-procs)] + {::jsrt/pool (jsrt/pool :init init) + ::wrk/executor executor})) + +(defmethod ig/halt-key! ::optimizer + [_ {:keys [::jsrt/pool]}] + (l/info :hint "stopping svg optimizer pool") + (pu/close! pool)) diff --git a/backend/src/app/tasks/file_gc.clj b/backend/src/app/tasks/file_gc.clj index 5e97bdabc0..dcb92a4570 100644 --- a/backend/src/app/tasks/file_gc.clj +++ b/backend/src/app/tasks/file_gc.clj @@ -10,7 +10,6 @@ file is eligible to be garbage collected after some period of inactivity (the default threshold is 72h)." (:require - [app.common.data :as d] [app.common.files.migrations :as pmg] [app.common.logging :as l] [app.common.thumbnails :as thc] @@ -30,7 +29,7 @@ [integrant.core :as ig])) (declare ^:private get-candidates) -(declare ^:private process-file) +(declare ^:private clean-file!) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; HANDLER @@ -44,67 +43,61 @@ (assoc cfg ::min-age cf/deletion-delay)) (defmethod ig/init-key ::handler - [_ {:keys [::db/pool] :as cfg}] + [_ cfg] (fn [{:keys [file-id] :as params}] + (db/tx-run! cfg + (fn [{:keys [::db/conn] :as cfg}] + (let [min-age (dt/duration (or (:min-age params) (::min-age cfg))) + cfg (-> cfg + (update ::sto/storage media/configure-assets-storage conn) + (assoc ::file-id file-id) + (assoc ::min-age min-age)) - (db/with-atomic [conn pool] - (let [min-age (dt/duration (or (:min-age params) (::min-age cfg))) - cfg (-> cfg - (update ::sto/storage media/configure-assets-storage conn) - (assoc ::db/conn conn) - (assoc ::file-id file-id) - (assoc ::min-age min-age)) + total (reduce (fn [total file] + (clean-file! cfg file) + (inc total)) + 0 + (get-candidates cfg))] - total (reduce (fn [total file] - (process-file cfg file) - (inc total)) - 0 - (get-candidates cfg))] + (l/inf :hint "task finished" + :min-age (dt/format-duration min-age) + :processed total) - (l/info :hint "task finished" :min-age (dt/format-duration min-age) :processed total) + ;; Allow optional rollback passed by params + (when (:rollback? params) + (db/rollback! conn)) - ;; Allow optional rollback passed by params - (when (:rollback? params) - (db/rollback! conn)) - - {:processed total})))) + {:processed total}))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; IMPL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def ^:private - sql:get-candidates-chunk - "select f.id, + sql:get-candidates + "SELECT f.id, f.data, f.revn, f.features, f.modified_at - from file as f - where f.has_media_trimmed is false - and f.modified_at < now() - ?::interval - and f.modified_at < ? - order by f.modified_at desc - limit 1 - for update skip locked") + FROM file AS f + WHERE f.has_media_trimmed IS false + AND f.modified_at < now() - ?::interval + ORDER BY f.modified_at DESC + FOR UPDATE + SKIP LOCKED") (defn- get-candidates [{:keys [::db/conn ::min-age ::file-id]}] (if (uuid? file-id) (do - (l/warn :hint "explicit file id passed on params" :file-id file-id) + (l/warn :hint "explicit file id passed on params" :file-id (str file-id)) (->> (db/query conn :file {:id file-id}) (map #(update % :features db/decode-pgarray #{})))) - (let [interval (db/interval min-age) - get-chunk (fn [cursor] - (let [rows (db/exec! conn [sql:get-candidates-chunk interval cursor])] - [(some->> rows peek :modified-at) - (map #(update % :features db/decode-pgarray #{}) rows)]))] - (d/iteration get-chunk - :vf second - :kf first - :initk (dt/now))))) + (let [min-age (db/interval min-age)] + (->> (db/cursor conn [sql:get-candidates min-age] {:chunk-size 1}) + (map #(update % :features db/decode-pgarray #{})))))) (defn collect-used-media "Given a fdata (file data), returns all media references." @@ -134,101 +127,93 @@ (into xform pages) (into (keys (:media data)))))) + +(def ^:private sql:mark-file-media-object-deleted + "UPDATE file_media_object + SET deleted_at = now() + WHERE file_id = ? AND id != ALL(?::uuid[]) + RETURNING id") + (defn- clean-file-media! "Performs the garbage collection of file media objects." [conn file-id data] (let [used (collect-used-media data) - unused (->> (db/query conn :file-media-object {:file-id file-id}) - (remove #(contains? used (:id %))))] + ids (db/create-array conn "uuid" used) + unused (->> (db/exec! conn [sql:mark-file-media-object-deleted file-id ids]) + (into #{} (map :id)))] - (doseq [mobj unused] - (l/dbg :hint "delete file media object" - :id (:id mobj) - :media-id (:media-id mobj) - :thumbnail-id (:thumbnail-id mobj)) + (doseq [id unused] + (l/trc :hint "mark deleted" + :rel "file-media-object" + :id (str id) + :file-id (str file-id))) - ;; NOTE: deleting the file-media-object in the database - ;; automatically marks as touched the referenced storage - ;; objects. The touch mechanism is needed because many files can - ;; point to the same storage objects and we can't just delete - ;; them. - (db/delete! conn :file-media-object {:id (:id mobj)})))) + (count unused))) + + +(def ^:private sql:mark-file-object-thumbnails-deleted + "UPDATE file_tagged_object_thumbnail + SET deleted_at = now() + WHERE file_id = ? AND object_id != ALL(?::text[]) + RETURNING object_id") (defn- clean-file-object-thumbnails! - [{:keys [::db/conn ::sto/storage]} file-id data] - (let [stored (->> (db/query conn :file-tagged-object-thumbnail - {:file-id file-id} - {:columns [:object-id]}) - (into #{} (map :object-id))) + [{:keys [::db/conn]} file-id data] + (let [using (->> (vals (:pages-index data)) + (into #{} (comp + (mapcat (fn [{:keys [id objects]}] + (->> (ctt/get-frames objects) + (map #(assoc % :page-id id))))) + (mapcat (fn [{:keys [id page-id]}] + (list + (thc/fmt-object-id file-id page-id id "frame") + (thc/fmt-object-id file-id page-id id "component"))))))) - using (into #{} - (comp - (mapcat (fn [{:keys [id objects]}] - (->> (ctt/get-frames objects) - (map #(assoc % :page-id id))))) - (mapcat (fn [{:keys [id page-id]}] - (list - (thc/fmt-object-id file-id page-id id "frame") - (thc/fmt-object-id file-id page-id id "component"))))) + ids (db/create-array conn "text" using) + unused (->> (db/exec! conn [sql:mark-file-object-thumbnails-deleted file-id ids]) + (into #{} (map :object-id)))] - (vals (:pages-index data))) + (doseq [object-id unused] + (l/trc :hint "mark deleted" + :rel "file-tagged-object-thumbnail" + :object-id object-id + :file-id (str file-id))) - unused (set/difference stored using)] + (count unused))) - (when (seq unused) - (let [sql (str "delete from file_tagged_object_thumbnail " - " where file_id=? and object_id=ANY(?)" - " returning media_id") - res (db/exec! conn [sql file-id (db/create-array conn "text" unused)])] - (l/dbg :hint "delete file object thumbnails" - :file-id (str file-id) - :total (count res)) - - (doseq [media-id (into #{} (keep :media-id) res)] - ;; Mark as deleted the storage object related with the - ;; photo-id field. - (l/trc :hint "touch file object thumbnail storage object" :id (str media-id)) - (sto/touch-object! storage media-id)))))) +(def ^:private sql:mark-file-thumbnails-deleted + "UPDATE file_thumbnail + SET deleted_at = now() + WHERE file_id = ? AND revn < ? + RETURNING revn") (defn- clean-file-thumbnails! - [{:keys [::db/conn ::sto/storage]} file-id revn] - (let [sql (str "delete from file_thumbnail " - " where file_id=? and revn < ? " - " returning media_id") - res (db/exec! conn [sql file-id revn])] + [{:keys [::db/conn]} file-id revn] + (let [unused (->> (db/exec! conn [sql:mark-file-thumbnails-deleted file-id revn]) + (into #{} (map :revn)))] - (when (seq res) - (l/dbg :hint "delete file thumbnails" - :file-id (str file-id) - :total (count res)) + (doseq [revn unused] + (l/trc :hint "mark deleted" + :rel "file-thumbnail" + :revn revn + :file-id (str file-id))) - (doseq [media-id (into #{} (keep :media-id) res)] - ;; Mark as deleted the storage object related with the - ;; media-id field. - (l/trc :hint "delete file thumbnail storage object" :id (str media-id)) - (sto/del-object! storage media-id))))) + (count unused))) -(def ^:private - sql:get-files-for-library - "select f.data, f.modified_at - from file as f - left join file_library_rel as fl on (fl.file_id = f.id) - where fl.library_file_id = ? - and f.modified_at < ? - and f.deleted_at is null - order by f.modified_at desc - limit 1") + +(def ^:private sql:get-files-for-library + "SELECT f.id, f.data, f.modified_at + FROM file AS f + LEFT JOIN file_library_rel AS fl ON (fl.file_id = f.id) + WHERE fl.library_file_id = ? + AND f.deleted_at IS null + ORDER BY f.modified_at ASC") (defn- clean-deleted-components! "Performs the garbage collection of unreferenced deleted components." - [conn file-id data] - (letfn [(get-files-chunk [cursor] - (let [rows (db/exec! conn [sql:get-files-for-library file-id cursor])] - [(some-> rows peek :modified-at) - (map (comp blob/decode :data) rows)])) - - (get-used-components [fdata components] + [{:keys [::db/conn] :as cfg} file-id data] + (letfn [(get-used-components [fdata components] ;; Find which of the components are used in the file. (into #{} (filter #(ctf/used-in? fdata file-id % :component)) @@ -246,65 +231,85 @@ files-data))] (let [deleted (into #{} (ctkl/deleted-components-seq data)) - unused (->> (d/iteration get-files-chunk :vf second :kf first :initk (dt/now)) + unused (->> (db/cursor conn [sql:get-files-for-library file-id] {:chunk-size 1}) + (map (fn [{:keys [id data] :as file}] + (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)] + (-> (blob/decode data) + (feat.fdata/process-pointers deref))))) (cons data) (get-unused-components deleted) (mapv :id))] - (when (seq unused) - (l/dbg :hint "clean deleted components" :total (count unused)) + (doseq [id unused] + (l/trc :hint "delete component" :component-id (str id) :file-id (str file-id))) - (let [data (reduce ctkl/delete-component data unused)] - (db/update! conn :file - {:data (blob/encode data)} - {:id file-id})))))) + + (when-let [data (some->> (seq unused) + (reduce ctkl/delete-component data) + (blob/encode))] + (db/update! conn :file + {:data data} + {:id file-id})) + + (count unused)))) + + +(def ^:private sql:get-changes + "SELECT id, data FROM file_change + WHERE file_id = ? AND data IS NOT NULL + ORDER BY created_at ASC") + +(def ^:private sql:mark-deleted-data-fragments + "UPDATE file_data_fragment + SET deleted_at = now() + WHERE file_id = ? + AND id != ALL(?::uuid[]) + RETURNING id") (defn- clean-data-fragments! [conn file-id data] - (letfn [(get-pointers-chunk [cursor] - (let [sql (str "select id, data, created_at " - " from file_change " - " where file_id = ? " - " and data is not null " - " and created_at < ? " - " order by created_at desc " - " limit 1;") - rows (db/exec! conn [sql file-id cursor])] - [(some-> rows peek :created-at) - (mapcat (comp feat.fdata/get-used-pointer-ids blob/decode :data) rows)]))] + (let [used (->> (db/cursor conn [sql:get-changes file-id]) + (into (feat.fdata/get-used-pointer-ids data) + (comp (map :data) + (map blob/decode) + (mapcat feat.fdata/get-used-pointer-ids)))) - (let [used (into (feat.fdata/get-used-pointer-ids data) - (d/iteration get-pointers-chunk - :vf second - :kf first - :initk (dt/now))) + unused (let [ids (db/create-array conn "uuid" used)] + (->> (db/exec! conn [sql:mark-deleted-data-fragments file-id ids]) + (into #{} (map :id))))] - sql (str "select id from file_data_fragment " - " where file_id = ? AND id != ALL(?::uuid[])") - used (db/create-array conn "uuid" used) - rows (db/exec! conn [sql file-id used])] + (doseq [id unused] + (l/trc :hint "mark deleted" + :rel "file-data-fragment" + :id (str id) + :file-id (str file-id))) - (doseq [fragment-id (map :id rows)] - (l/trc :hint "remove unused file data fragment" :id (str fragment-id)) - (db/delete! conn :file-data-fragment {:id fragment-id :file-id file-id}))))) + (count unused))) -(defn- process-file - [{:keys [::db/conn] :as cfg} {:keys [id data revn modified-at features] :as file}] - (l/dbg :hint "processing file" :file-id (str id) :modified-at modified-at) + +(defn- clean-file! + [{:keys [::db/conn] :as cfg} {:keys [id data revn modified-at] :as file}] (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id) pmap/*tracked* (pmap/create-tracked)] (let [data (-> (blob/decode data) (assoc :id id) - (pmg/migrate-data))] + (pmg/migrate-data)) - (clean-file-media! conn id data) - (clean-file-object-thumbnails! cfg id data) - (clean-file-thumbnails! cfg id revn) - (clean-deleted-components! conn id data) + nfm (clean-file-media! conn id data) + nfot (clean-file-object-thumbnails! cfg id data) + nft (clean-file-thumbnails! cfg id revn) + nc (clean-deleted-components! cfg id data) + ndf (clean-data-fragments! conn id data)] - (when (contains? features "fdata/pointer-map") - (clean-data-fragments! conn id data)) + (l/dbg :hint "file clened" + :file-id (str id) + :modified-at (dt/format-instant modified-at) + :media-objects nfm + :thumbnails nft + :object-thumbnails nfot + :components nc + :data-fragments ndf) ;; Mark file as trimmed (db/update! conn :file diff --git a/backend/src/app/tasks/objects_gc.clj b/backend/src/app/tasks/objects_gc.clj index 83b86dcdea..c5e74ce3ac 100644 --- a/backend/src/app/tasks/objects_gc.clj +++ b/backend/src/app/tasks/objects_gc.clj @@ -8,7 +8,6 @@ "A maintenance task that performs a general purpose garbage collection of deleted or unreachable objects." (:require - [app.common.data :as d] [app.common.logging :as l] [app.config :as cf] [app.db :as db] @@ -18,12 +17,15 @@ [clojure.spec.alpha :as s] [integrant.core :as ig])) -(declare ^:private delete-profiles!) -(declare ^:private delete-teams!) -(declare ^:private delete-fonts!) -(declare ^:private delete-projects!) +(declare ^:private delete-file-data-fragments!) +(declare ^:private delete-file-media-objects!) +(declare ^:private delete-file-object-thumbnails!) +(declare ^:private delete-file-thumbnails!) (declare ^:private delete-files!) -(declare ^:private delete-orphan-teams!) +(declare ^:private delete-fonts!) +(declare ^:private delete-profiles!) +(declare ^:private delete-projects!) +(declare ^:private delete-teams!) (defmethod ig/pre-init-spec ::handler [_] (s/keys :req [::db/pool ::sto/storage])) @@ -33,211 +35,306 @@ (assoc cfg ::min-age cf/deletion-delay)) (defmethod ig/init-key ::handler - [_ {:keys [::db/pool ::sto/storage] :as cfg}] + [_ cfg] (fn [params] - (db/with-atomic [conn pool] - (let [min-age (or (:min-age params) (::min-age cfg)) - _ (l/info :hint "gc started" - :min-age (dt/format-duration min-age) - :rollback? (boolean (:rollback? params))) + (db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}] + ;; Disable deletion protection for the current transaction + (db/exec-one! conn ["SET LOCAL rules.deletion_protection TO off"]) + (db/exec-one! conn ["SET CONSTRAINTS ALL DEFERRED"]) - storage (media/configure-assets-storage storage conn) - cfg (-> cfg - (assoc ::min-age (db/interval min-age)) - (assoc ::conn conn) - (assoc ::storage storage)) + (let [min-age (dt/duration (or (:min-age params) (::min-age cfg))) + cfg (-> cfg + (assoc ::min-age (db/interval min-age)) + (update ::sto/storage media/configure-assets-storage conn)) - htotal (+ (delete-profiles! cfg) - (delete-teams! cfg) - (delete-projects! cfg) - (delete-files! cfg) - (delete-fonts! cfg)) - stotal (delete-orphan-teams! cfg)] + total (reduce + 0 + [(delete-profiles! cfg) + (delete-teams! cfg) + (delete-fonts! cfg) + (delete-projects! cfg) + (delete-files! cfg) + (delete-file-thumbnails! cfg) + (delete-file-object-thumbnails! cfg) + (delete-file-data-fragments! cfg) + (delete-file-media-objects! cfg)])] - (l/info :hint "gc finished" - :deleted htotal - :orphans stotal - :rollback? (boolean (:rollback? params))) + (l/info :hint "task finished" + :deleted total + :rollback? (boolean (:rollback? params))) - (when (:rollback? params) - (db/rollback! conn)) + (when (:rollback? params) + (db/rollback! conn)) - {:processed (+ stotal htotal) - :orphans stotal})))) + {:processed total}))))) -(def ^:private sql:get-profiles-chunk - "select id, photo_id, created_at from profile - where deleted_at is not null - and deleted_at < now() - ?::interval - and created_at < ? - order by created_at desc - limit 10 - for update skip locked") +(def ^:private sql:get-profiles + "SELECT id, photo_id FROM profile + WHERE deleted_at IS NOT NULL + AND deleted_at < now() - ?::interval + ORDER BY deleted_at ASC + FOR UPDATE + SKIP LOCKED") (defn- delete-profiles! - [{:keys [::conn ::min-age ::storage] :as cfg}] - (letfn [(get-chunk [cursor] - (let [rows (db/exec! conn [sql:get-profiles-chunk min-age cursor])] - [(some->> rows peek :created-at) rows])) + [{:keys [::db/conn ::min-age ::sto/storage] :as cfg}] + (->> (db/cursor conn [sql:get-profiles min-age]) + (reduce (fn [total {:keys [id photo-id]}] + (l/trc :hint "permanently delete" :rel "profile" :id (str id)) - (process-profile [total {:keys [id photo-id]}] - (l/debug :hint "permanently delete profile" :id (str id)) + ;; Mark as deleted the storage object + (some->> photo-id (sto/touch-object! storage)) - ;; Mark as deleted the storage object related with the - ;; photo-id field. - (some->> photo-id (sto/touch-object! storage)) + ;; And finally, permanently delete the profile. The + ;; relevant objects will be deleted using DELETE + ;; CASCADE database triggers. This may leave orphan + ;; teams, but there is a special task for deleting + ;; orphaned teams. + (db/delete! conn :profile {:id id}) - ;; And finally, permanently delete the profile. - (db/delete! conn :profile {:id id}) + (inc total)) + 0))) - (inc total))] - - (->> (d/iteration get-chunk :vf second :kf first :initk (dt/now)) - (reduce process-profile 0)))) - -(def ^:private sql:get-teams-chunk - "select id, photo_id, created_at from team - where deleted_at is not null - and deleted_at < now() - ?::interval - and created_at < ? - order by created_at desc - limit 10 - for update skip locked") +(def ^:private sql:get-teams + "SELECT deleted_at, id, photo_id FROM team + WHERE deleted_at IS NOT NULL + AND deleted_at < now() - ?::interval + ORDER BY deleted_at ASC + FOR UPDATE + SKIP LOCKED") (defn- delete-teams! - [{:keys [::conn ::min-age ::storage] :as cfg}] - (letfn [(get-chunk [cursor] - (let [rows (db/exec! conn [sql:get-teams-chunk min-age cursor])] - [(some->> rows peek :created-at) rows])) + [{:keys [::db/conn ::min-age ::sto/storage] :as cfg}] - (process-team [total {:keys [id photo-id]}] - (l/debug :hint "permanently delete team" :id (str id)) + (->> (db/cursor conn [sql:get-teams min-age]) + (reduce (fn [total {:keys [id photo-id deleted-at]}] + (l/trc :hint "permanently delete" + :rel "team" + :id (str id) + :deleted-at (dt/format-instant deleted-at)) - ;; Mark as deleted the storage object related with the - ;; photo-id field. - (some->> photo-id (sto/touch-object! storage)) + ;; Mark as deleted the storage object + (some->> photo-id (sto/touch-object! storage)) - ;; And finally, permanently delete the team. - (db/delete! conn :team {:id id}) + ;; And finally, permanently delete the team. + (db/delete! conn :team {:id id}) - (inc total))] + ;; Mark for deletion in cascade + (db/update! conn :team-font-variant + {:deleted-at deleted-at} + {:team-id id}) - (->> (d/iteration get-chunk :vf second :kf first :initk (dt/now)) - (reduce process-team 0)))) + (db/update! conn :project + {:deleted-at deleted-at} + {:team-id id}) -(def ^:private sql:get-orphan-teams-chunk - "select t.id, t.created_at - from team as t - left join team_profile_rel as tpr - on (t.id = tpr.team_id) - where tpr.profile_id is null - and t.created_at < ? - order by t.created_at desc - limit 10 - for update of t skip locked;") + (inc total)) + 0))) -(defn- delete-orphan-teams! - "Find all orphan teams (with no members and mark them for - deletion (soft delete)." - [{:keys [::conn] :as cfg}] - (letfn [(get-chunk [cursor] - (let [rows (db/exec! conn [sql:get-orphan-teams-chunk cursor])] - [(some->> rows peek :created-at) rows])) - - (process-team [total {:keys [id]}] - (let [result (db/update! conn :team - {:deleted-at (dt/now)} - {:id id :deleted-at nil} - {::db/return-keys? false}) - count (db/get-update-count result)] - (when (pos? count) - (l/debug :hint "mark team for deletion" :id (str id))) - - (+ total count)))] - - (->> (d/iteration get-chunk :vf second :kf first :initk (dt/now)) - (reduce process-team 0)))) - -(def ^:private sql:get-fonts-chunk - "select id, created_at, woff1_file_id, woff2_file_id, otf_file_id, ttf_file_id - from team_font_variant - where deleted_at is not null - and deleted_at < now() - ?::interval - and created_at < ? - order by created_at desc - limit 10 - for update skip locked") +(def ^:private sql:get-fonts + "SELECT id, team_id, deleted_at, woff1_file_id, woff2_file_id, otf_file_id, ttf_file_id + FROM team_font_variant + WHERE deleted_at IS NOT NULL + AND deleted_at < now() - ?::interval + ORDER BY deleted_at ASC + FOR UPDATE + SKIP LOCKED") (defn- delete-fonts! - [{:keys [::conn ::min-age ::storage] :as cfg}] - (letfn [(get-chunk [cursor] - (let [rows (db/exec! conn [sql:get-fonts-chunk min-age cursor])] - [(some->> rows peek :created-at) rows])) + [{:keys [::db/conn ::min-age ::sto/storage] :as cfg}] + (->> (db/cursor conn [sql:get-fonts min-age]) + (reduce (fn [total {:keys [id team-id deleted-at] :as font}] + (l/trc :hint "permanently delete" + :rel "team-font-variant" + :id (str id) + :team-id (str team-id) + :deleted-at (dt/format-instant deleted-at)) - (process-font [total {:keys [id] :as font}] - (l/debug :hint "permanently delete font variant" :id (str id)) + ;; Mark as deleted the all related storage objects + (some->> (:woff1-file-id font) (sto/touch-object! storage)) + (some->> (:woff2-file-id font) (sto/touch-object! storage)) + (some->> (:otf-file-id font) (sto/touch-object! storage)) + (some->> (:ttf-file-id font) (sto/touch-object! storage)) - ;; Mark as deleted the all related storage objects - (some->> (:woff1-file-id font) (sto/touch-object! storage)) - (some->> (:woff2-file-id font) (sto/touch-object! storage)) - (some->> (:otf-file-id font) (sto/touch-object! storage)) - (some->> (:ttf-file-id font) (sto/touch-object! storage)) + ;; And finally, permanently delete the team font variant + (db/delete! conn :team-font-variant {:id id}) - ;; And finally, permanently delete the team font variant - (db/delete! conn :team-font-variant {:id id}) + (inc total)) + 0))) - (inc total))] - - (->> (d/iteration get-chunk :vf second :kf first :initk (dt/now)) - (reduce process-font 0)))) - -(def ^:private sql:get-projects-chunk - "select id, created_at - from project - where deleted_at is not null - and deleted_at < now() - ?::interval - and created_at < ? - order by created_at desc - limit 10 - for update skip locked") +(def ^:private sql:get-projects + "SELECT id, deleted_at, team_id + FROM project + WHERE deleted_at IS NOT NULL + AND deleted_at < now() - ?::interval + ORDER BY deleted_at ASC + FOR UPDATE + SKIP LOCKED") (defn- delete-projects! - [{:keys [::conn ::min-age] :as cfg}] - (letfn [(get-chunk [cursor] - (let [rows (db/exec! conn [sql:get-projects-chunk min-age cursor])] - [(some->> rows peek :created-at) rows])) + [{:keys [::db/conn ::min-age] :as cfg}] + (->> (db/cursor conn [sql:get-projects min-age]) + (reduce (fn [total {:keys [id team-id deleted-at]}] + (l/trc :hint "permanently delete" + :rel "project" + :id (str id) + :team-id (str team-id) + :deleted-at (dt/format-instant deleted-at)) - (process-project [total {:keys [id]}] - (l/debug :hint "permanently delete project" :id (str id)) - ;; And finally, permanently delete the project. - (db/delete! conn :project {:id id}) + ;; And finally, permanently delete the project. + (db/delete! conn :project {:id id}) - (inc total))] + ;; Mark files to be deleted + (db/update! conn :file + {:deleted-at deleted-at} + {:project-id id}) - (->> (d/iteration get-chunk :vf second :kf first :initk (dt/now)) - (reduce process-project 0)))) + (inc total)) + 0))) -(def ^:private sql:get-files-chunk - "select id, created_at - from file - where deleted_at is not null - and deleted_at < now() - ?::interval - and created_at < ? - order by created_at desc - limit 10 - for update skip locked") +(def ^:private sql:get-files + "SELECT id, deleted_at, project_id + FROM file + WHERE deleted_at IS NOT NULL + AND deleted_at < now() - ?::interval + ORDER BY deleted_at ASC + FOR UPDATE + SKIP LOCKED") (defn- delete-files! - [{:keys [::conn ::min-age] :as cfg}] - (letfn [(get-chunk [cursor] - (let [rows (db/exec! conn [sql:get-files-chunk min-age cursor])] - [(some->> rows peek :created-at) rows])) + [{:keys [::db/conn ::min-age] :as cfg}] + (->> (db/cursor conn [sql:get-files min-age]) + (reduce (fn [total {:keys [id deleted-at project-id]}] + (l/trc :hint "permanently delete" + :rel "file" + :id (str id) + :project-id (str project-id) + :deleted-at (dt/format-instant deleted-at)) - (process-file [total {:keys [id]}] - (l/debug :hint "permanently delete file" :id (str id)) - ;; And finally, permanently delete the file. - (db/delete! conn :file {:id id}) - (inc total))] + ;; And finally, permanently delete the file. + (db/delete! conn :file {:id id}) - (->> (d/iteration get-chunk :vf second :kf first :initk (dt/now)) - (reduce process-file 0)))) + ;; Mark file media objects to be deleted + (db/update! conn :file-media-object + {:deleted-at deleted-at} + {:file-id id}) + + ;; Mark thumbnails to be deleted + (db/update! conn :file-thumbnail + {:deleted-at deleted-at} + {:file-id id}) + + (db/update! conn :file-tagged-object-thumbnail + {:deleted-at deleted-at} + {:file-id id}) + + (inc total)) + 0))) + + +(def ^:private sql:get-file-thumbnails + "SELECT file_id, revn, media_id, deleted_at + FROM file_thumbnail + WHERE deleted_at IS NOT NULL + AND deleted_at < now() - ?::interval + ORDER BY deleted_at ASC + FOR UPDATE + SKIP LOCKED") + +(defn delete-file-thumbnails! + [{:keys [::db/conn ::min-age ::sto/storage] :as cfg}] + (->> (db/cursor conn [sql:get-file-thumbnails min-age]) + (reduce (fn [total {:keys [file-id revn media-id deleted-at]}] + (l/trc :hint "permanently delete" + :rel "file-thumbnail" + :file-id (str file-id) + :revn revn + :deleted-at (dt/format-instant deleted-at)) + + ;; Mark as deleted the storage object + (some->> media-id (sto/touch-object! storage)) + + ;; And finally, permanently delete the object + (db/delete! conn :file-thumbnail {:file-id file-id :revn revn}) + + (inc total)) + 0))) + +(def ^:private sql:get-file-object-thumbnails + "SELECT file_id, object_id, media_id, deleted_at + FROM file_tagged_object_thumbnail + WHERE deleted_at IS NOT NULL + AND deleted_at < now() - ?::interval + ORDER BY deleted_at ASC + FOR UPDATE + SKIP LOCKED") + +(defn delete-file-object-thumbnails! + [{:keys [::db/conn ::min-age ::sto/storage] :as cfg}] + (->> (db/cursor conn [sql:get-file-object-thumbnails min-age]) + (reduce (fn [total {:keys [file-id object-id media-id deleted-at]}] + (l/trc :hint "permanently delete" + :rel "file-tagged-object-thumbnail" + :file-id (str file-id) + :object-id object-id + :deleted-at (dt/format-instant deleted-at)) + + ;; Mark as deleted the storage object + (some->> media-id (sto/touch-object! storage)) + + ;; And finally, permanently delete the object + (db/delete! conn :file-tagged-object-thumbnail {:file-id file-id :object-id object-id}) + + (inc total)) + 0))) + +(def ^:private sql:get-file-data-fragments + "SELECT file_id, id, deleted_at + FROM file_data_fragment + WHERE deleted_at IS NOT NULL + AND deleted_at < now() - ?::interval + ORDER BY deleted_at ASC + FOR UPDATE + SKIP LOCKED") + +(defn- delete-file-data-fragments! + [{:keys [::db/conn ::min-age] :as cfg}] + (->> (db/cursor conn [sql:get-file-data-fragments min-age]) + (reduce (fn [total {:keys [file-id id deleted-at]}] + (l/trc :hint "permanently delete" + :rel "file-data-fragment" + :id (str id) + :file-id (str file-id) + :deleted-at (dt/format-instant deleted-at)) + + (db/delete! conn :file-data-fragment {:file-id file-id :id id}) + + (inc total)) + 0))) + +(def ^:private sql:get-file-media-objects + "SELECT id, file_id, media_id, thumbnail_id, deleted_at + FROM file_media_object + WHERE deleted_at IS NOT NULL + AND deleted_at < now() - ?::interval + ORDER BY deleted_at ASC + FOR UPDATE + SKIP LOCKED") + +(defn- delete-file-media-objects! + [{:keys [::db/conn ::min-age ::sto/storage] :as cfg}] + (->> (db/cursor conn [sql:get-file-media-objects min-age]) + (reduce (fn [total {:keys [id file-id deleted-at] :as fmo}] + (l/trc :hint "permanently delete" + :rel "file-media-object" + :id (str id) + :file-id (str file-id) + :deleted-at (dt/format-instant deleted-at)) + + ;; Mark as deleted the all related storage objects + (some->> (:media-id fmo) (sto/touch-object! storage)) + (some->> (:thumbnail-id fmo) (sto/touch-object! storage)) + + (db/delete! conn :file-media-object {:id id}) + + (inc total)) + 0))) diff --git a/backend/src/app/tasks/orphan_teams_gc.clj b/backend/src/app/tasks/orphan_teams_gc.clj new file mode 100644 index 0000000000..c04123a831 --- /dev/null +++ b/backend/src/app/tasks/orphan_teams_gc.clj @@ -0,0 +1,59 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.tasks.orphan-teams-gc + "A maintenance task that performs orphan teams GC." + (:require + [app.common.logging :as l] + [app.db :as db] + [app.util.time :as dt] + [clojure.spec.alpha :as s] + [integrant.core :as ig])) + +(declare ^:private delete-orphan-teams!) + +(defmethod ig/pre-init-spec ::handler [_] + (s/keys :req [::db/pool])) + +(defmethod ig/init-key ::handler + [_ cfg] + (fn [params] + (db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}] + (l/inf :hint "gc started" :rollback? (boolean (:rollback? params))) + (let [total (delete-orphan-teams! cfg)] + (l/inf :hint "task finished" + :teams total + :rollback? (boolean (:rollback? params))) + + (when (:rollback? params) + (db/rollback! conn)) + + {:processed total}))))) + +(def ^:private sql:get-orphan-teams + "SELECT t.id + FROM team AS t + LEFT JOIN team_profile_rel AS tpr + ON (t.id = tpr.team_id) + WHERE tpr.profile_id IS NULL + AND t.deleted_at IS NULL + ORDER BY t.created_at ASC + FOR UPDATE OF t + SKIP LOCKED") + +(defn- delete-orphan-teams! + "Find all orphan teams (with no members) and mark them for + deletion (soft delete)." + [{:keys [::db/conn] :as cfg}] + (->> (db/cursor conn sql:get-orphan-teams) + (map :id) + (reduce (fn [total team-id] + (l/trc :hint "mark orphan team for deletion" :id (str team-id)) + (db/update! conn :team + {:deleted-at (dt/now)} + {:id team-id}) + (inc total)) + 0))) diff --git a/backend/src/app/worker.clj b/backend/src/app/worker.clj index 0ab867971b..0e830ac9a3 100644 --- a/backend/src/app/worker.clj +++ b/backend/src/app/worker.clj @@ -42,8 +42,8 @@ (defmethod ig/init-key ::executor [_ _] - (let [factory (px/thread-factory :prefix "penpot/default/") - executor (px/cached-executor :factory factory :keepalive 30000)] + (let [factory (px/thread-factory :prefix "penpot/default/") + executor (px/cached-executor :factory factory :keepalive 60000)] (l/inf :hint "starting executor") (reify java.lang.AutoCloseable diff --git a/backend/test/backend_tests/helpers.clj b/backend/test/backend_tests/helpers.clj index 3fad161142..646905c515 100644 --- a/backend/test/backend_tests/helpers.clj +++ b/backend/test/backend_tests/helpers.clj @@ -175,12 +175,11 @@ " WHERE table_schema = 'public' " " AND table_name != 'migrations';")] (db/with-atomic [conn *pool*] + (db/exec-one! conn ["SET CONSTRAINTS ALL DEFERRED"]) + (db/exec-one! conn ["SET LOCAL rules.deletion_protection TO off"]) (let [result (->> (db/exec! conn [sql]) (map :table-name) - (remove #(= "task" %))) - sql (str "TRUNCATE " - (apply str (interpose ", " result)) - " CASCADE;")] + (remove #(= "task" %)))] (doseq [table result] (db/exec! conn [(str "delete from " table ";")])))) @@ -433,11 +432,11 @@ (us/pretty-explain data)) (= :params-validation (:code data)) - (app.common.pprint/pprint + (println (sm/humanize-explain (::sm/explain data))) (= :data-validation (:code data)) - (app.common.pprint/pprint + (println (sm/humanize-explain (::sm/explain data))) (= :service-error (:type data)) @@ -512,6 +511,10 @@ [sql] (db/exec! *pool* sql)) +(defn db-exec-one! + [sql] + (db/exec-one! *pool* sql)) + (defn db-delete! [& params] (apply db/delete! *pool* params)) diff --git a/backend/test/backend_tests/rpc_file_test.clj b/backend/test/backend_tests/rpc_file_test.clj index cbf96c2167..cbce720c56 100644 --- a/backend/test/backend_tests/rpc_file_test.clj +++ b/backend/test/backend_tests/rpc_file_test.clj @@ -149,7 +149,7 @@ shape-id (uuid/random)] ;; Preventive file-gc - (let [res (th/run-task! "file-gc" {:min-age 0})] + (let [res (th/run-task! :file-gc {:min-age 0})] (t/is (= 1 (:processed res)))) ;; Check the number of fragments before adding the page @@ -175,7 +175,7 @@ (t/is (= 2 (count rows)))) ;; The file-gc should remove unused fragments - (let [res (th/run-task! "file-gc" {:min-age 0})] + (let [res (th/run-task! :file-gc {:min-age 0})] (t/is (= 1 (:processed res)))) @@ -203,7 +203,7 @@ (t/is (= 3 (count rows)))) ;; The file-gc should remove unused fragments - (let [res (th/run-task! "file-gc" {:min-age 0})] + (let [res (th/run-task! :file-gc {:min-age 0})] (t/is (= 1 (:processed res)))) ;; Check the number of fragments; should be 3 because changes @@ -220,12 +220,23 @@ ;; The file-gc should remove fragments related to changes ;; snapshots previously deleted. - (let [res (th/run-task! "file-gc" {:min-age 0})] + (let [res (th/run-task! :file-gc {:min-age 0})] (t/is (= 1 (:processed res)))) ;; Check the number of fragments; (let [rows (th/db-query :file-data-fragment {:file-id (:id file)})] - (t/is (= 2 (count rows))))))) + ;; (pp/pprint rows) + (t/is (= 3 (count rows))) + (t/is (= 2 (count (remove (comp some? :deleted-at) rows))))) + + (let [res (th/run-task! :objects-gc {:min-age 0})] + (t/is (= 1 (:processed res)))) + + (let [rows (th/db-query :file-data-fragment {:file-id (:id file)})] + (t/is (= 2 (count rows)))) + ))) + + (t/deftest file-gc-task-with-thumbnails (letfn [(add-file-media-object [& {:keys [profile-id file-id]}] @@ -301,17 +312,16 @@ ;; freeze because of the deduplication (we have uploaded 2 times ;; the same files). - (let [task (:app.storage/gc-touched-task th/*system*) - res (task {:min-age (dt/duration 0)})] + (let [res (th/run-task! :storage-gc-touched {:min-age 0})] (t/is (= 2 (:freeze res))) (t/is (= 0 (:delete res)))) ;; run the file-gc task immediately without forced min-age - (let [res (th/run-task! "file-gc")] + (let [res (th/run-task! :file-gc)] (t/is (= 0 (:processed res)))) ;; run the task again - (let [res (th/run-task! "file-gc" {:min-age 0})] + (let [res (th/run-task! :file-gc {:min-age 0})] (t/is (= 1 (:processed res)))) ;; retrieve file and check trimmed attribute @@ -319,8 +329,17 @@ (t/is (true? (:has-media-trimmed row)))) ;; check file media objects - (let [rows (th/db-exec! ["select * from file_media_object where file_id = ?" (:id file)])] - (t/is (= 1 (count rows)))) + (let [rows (th/db-query :file-media-object {:file-id (:id file)})] + (t/is (= 2 (count rows))) + (t/is (= 1 (count (remove (comp some? :deleted-at) rows))))) + + (let [res (th/run-task! :objects-gc {:min-age 0})] + (t/is (= 2 (:processed res)))) + + ;; check file media objects + (let [rows (th/db-query :file-media-object {:file-id (:id file)})] + (t/is (= 1 (count rows))) + (t/is (= 1 (count (remove (comp some? :deleted-at) rows))))) ;; The underlying storage objects are still available. (t/is (some? (sto/get-object storage (:media-id fmo2)))) @@ -340,15 +359,16 @@ ;; Now, we have deleted the usage of pointers to the ;; file-media-objects, if we paste file-gc, they should be marked ;; as deleted. - (let [task (:app.tasks.file-gc/handler th/*system*) - res (task {:min-age (dt/duration 0)})] + (let [res (th/run-task! :file-gc {:min-age 0})] + (t/is (= 1 (:processed res)))) + + (let [res (th/run-task! :objects-gc {:min-age 0})] (t/is (= 1 (:processed res)))) ;; Now that file-gc have deleted the file-media-object usage, ;; lets execute the touched-gc task, we should see that two of ;; them are marked to be deleted. - (let [task (:app.storage/gc-touched-task th/*system*) - res (task {:min-age (dt/duration 0)})] + (let [res (th/run-task! :storage-gc-touched {:min-age 0})] (t/is (= 0 (:freeze res))) (t/is (= 2 (:delete res)))) @@ -457,11 +477,14 @@ :strokes [{:opacity 1 :stroke-image {:id (:id fmo5) :width 100 :height 100 :mtype "image/jpeg"}}]})}]) ;; run the file-gc task immediately without forced min-age - (let [res (th/run-task! "file-gc")] + (let [res (th/run-task! :file-gc)] (t/is (= 0 (:processed res)))) ;; run the task again - (let [res (th/run-task! "file-gc" {:min-age 0})] + (let [res (th/run-task! :file-gc {:min-age 0})] + (t/is (= 1 (:processed res)))) + + (let [res (th/run-task! :objects-gc {:min-age 0})] (t/is (= 1 (:processed res)))) ;; retrieve file and check trimmed attribute @@ -494,15 +517,16 @@ ;; Now, we have deleted the usage of pointers to the ;; file-media-objects, if we paste file-gc, they should be marked ;; as deleted. - (let [task (:app.tasks.file-gc/handler th/*system*) - res (task {:min-age (dt/duration 0)})] + (let [res (th/run-task! :file-gc {:min-age 0})] (t/is (= 1 (:processed res)))) + (let [res (th/run-task! :objects-gc {:min-age 0})] + (t/is (= 5 (:processed res)))) + ;; Now that file-gc have deleted the file-media-object usage, ;; lets execute the touched-gc task, we should see that two of ;; them are marked to be deleted. - (let [task (:app.storage/gc-touched-task th/*system*) - res (task {:min-age (dt/duration 0)})] + (let [res (th/run-task! :storage-gc-touched {:min-age 0})] (t/is (= 0 (:freeze res))) (t/is (= 2 (:delete res)))) @@ -515,7 +539,6 @@ (t/is (nil? (sto/get-object storage (:media-id fmo2)))) (t/is (nil? (sto/get-object storage (:media-id fmo1))))))) - (t/deftest file-gc-task-with-object-thumbnails (letfn [(insert-file-object-thumbnail! [& {:keys [profile-id file-id page-id frame-id]}] (let [object-id (thc/fmt-object-id file-id page-id frame-id "frame") @@ -609,16 +632,16 @@ ;; because of the deduplication (we have uploaded 2 times the ;; same files). - (let [res (th/run-task! "storage-gc-touched" {:min-age (dt/duration 0)})] + (let [res (th/run-task! :storage-gc-touched {:min-age 0})] (t/is (= 1 (:freeze res))) (t/is (= 0 (:delete res)))) ;; run the file-gc task immediately without forced min-age - (let [res (th/run-task! "file-gc")] + (let [res (th/run-task! :file-gc)] (t/is (= 0 (:processed res)))) ;; run the task again - (let [res (th/run-task! "file-gc" {:min-age 0})] + (let [res (th/run-task! :file-gc {:min-age 0})] (t/is (= 1 (:processed res)))) ;; retrieve file and check trimmed attribute @@ -648,22 +671,29 @@ :page-id page-id :id frame-id-2}]) - (let [res (th/run-task! "file-gc" {:min-age (dt/duration 0)})] + (let [res (th/run-task! :file-gc {:min-age 0})] (t/is (= 1 (:processed res)))) - (let [rows (th/db-exec! ["select * from file_tagged_object_thumbnail where file_id = ?" file-id])] - ;; (pp/pprint rows) - (t/is (= 1 (count rows))) + (let [rows (th/db-query :file-tagged-object-thumbnail {:file-id file-id})] + (t/is (= 2 (count rows))) + (t/is (= 1 (count (remove (comp some? :deleted-at) rows)))) + (t/is (= (thc/fmt-object-id file-id page-id frame-id-1 "frame") (-> rows first :object-id)))) - ;; Now that file-gc have deleted the object thumbnail lets + ;; Now that file-gc have marked for deletion the object + ;; thumbnail lets execute the objects-gc task which remove + ;; the rows and mark as touched the storage object rows + (let [res (th/run-task! :objects-gc {:min-age 0})] + (t/is (= 2 (:processed res)))) + + ;; Now that objects-gc have deleted the object thumbnail lets ;; execute the touched-gc task - (let [res (th/run-task! "storage-gc-touched" {:min-age (dt/duration 0)})] + (let [res (th/run-task! "storage-gc-touched" {:min-age 0})] (t/is (= 1 (:freeze res)))) ;; check file media objects - (let [rows (th/db-exec! ["select * from storage_object where deleted_at is null"])] + (let [rows (th/db-query :storage-object {:deleted-at nil})] ;; (pp/pprint rows) (t/is (= 1 (count rows)))) @@ -676,31 +706,32 @@ :page-id page-id :id frame-id-1}]) - (let [res (th/run-task! "file-gc" {:min-age (dt/duration 0)})] + (let [res (th/run-task! :file-gc {:min-age 0})] (t/is (= 1 (:processed res)))) - (let [rows (th/db-exec! ["select * from file_tagged_object_thumbnail where file_id = ?" file-id])] - (t/is (= 0 (count rows)))) + (let [rows (th/db-query :file-tagged-object-thumbnail {:file-id file-id})] + (t/is (= 1 (count rows))) + (t/is (= 0 (count (remove (comp some? :deleted-at) rows))))) + + (let [res (th/run-task! :objects-gc {:min-age 0})] + ;; (pp/pprint res) + (t/is (= 1 (:processed res)))) ;; We still have th storage objects in the table - (let [rows (th/db-exec! ["select * from storage_object where deleted_at is null"])] + (let [rows (th/db-query :storage-object {:deleted-at nil})] ;; (pp/pprint rows) (t/is (= 1 (count rows)))) ;; Now that file-gc have deleted the object thumbnail lets ;; execute the touched-gc task - (let [res (th/run-task! "storage-gc-touched" {:min-age (dt/duration 0)})] + (let [res (th/run-task! :storage-gc-touched {:min-age 0})] (t/is (= 1 (:delete res)))) ;; check file media objects - (let [rows (th/db-exec! ["select * from storage_object where deleted_at is null"])] + (let [rows (th/db-query :storage-object {:deleted-at nil})] ;; (pp/pprint rows) (t/is (= 0 (count rows))))))) - - - - (t/deftest permissions-checks-creating-file (let [profile1 (th/create-profile* 1) profile2 (th/create-profile* 2) @@ -811,13 +842,12 @@ (t/is (th/ex-of-type? error :not-found)))) (t/deftest deletion - (let [task (:app.tasks.objects-gc/handler th/*system*) - profile1 (th/create-profile* 1) + (let [profile1 (th/create-profile* 1) file (th/create-file* 1 {:project-id (:default-project-id profile1) :profile-id (:id profile1)})] ;; file is not deleted because it does not meet all ;; conditions to be deleted. - (let [result (task {:min-age (dt/duration 0)})] + (let [result (th/run-task! :objects-gc {:min-age 0})] (t/is (= 0 (:processed result)))) ;; query the list of files @@ -848,7 +878,7 @@ (t/is (= 0 (count result))))) ;; run permanent deletion (should be noop) - (let [result (task {:min-age (dt/duration {:minutes 1})})] + (let [result (th/run-task! :objects-gc {:min-age (dt/duration {:minutes 1})})] (t/is (= 0 (:processed result)))) ;; query the list of file libraries of a after hard deletion @@ -862,7 +892,7 @@ (t/is (= 0 (count result))))) ;; run permanent deletion - (let [result (task {:min-age (dt/duration 0)})] + (let [result (th/run-task! :objects-gc {:min-age 0})] (t/is (= 1 (:processed result)))) ;; query the list of file libraries of a after hard deletion @@ -874,7 +904,8 @@ (let [error (:error out) error-data (ex-data error)] (t/is (th/ex-info? error)) - (t/is (= (:type error-data) :not-found)))))) + (t/is (= (:type error-data) :not-found)))) + )) (t/deftest object-thumbnails-ops @@ -1075,7 +1106,7 @@ (th/sleep 300) ;; run the task - (let [res (th/run-task! "file-gc" {:min-age 0})] + (let [res (th/run-task! :file-gc {:min-age 0})] (t/is (= 1 (:processed res)))) ;; check that object thumbnails are still here @@ -1104,13 +1135,19 @@ (t/is (= 2 (count res)))) ;; run the task again - (let [res (th/run-task! "file-gc" {:min-age 0})] + (let [res (th/run-task! :file-gc {:min-age 0})] (t/is (= 1 (:processed res)))) ;; check that the unknown frame thumbnail is deleted - (let [res (th/db-exec! ["select * from file_tagged_object_thumbnail"])] - (t/is (= 1 (count res))))))) + (let [rows (th/db-query :file-tagged-object-thumbnail {:file-id (:id file)})] + (t/is (= 2 (count rows))) + (t/is (= 1 (count (remove (comp some? :deleted-at) rows))))) + (let [res (th/run-task! :objects-gc {:min-age 0})] + (t/is (= 2 (:processed res)))) + + (let [rows (th/db-query :file-tagged-object-thumbnail {:file-id (:id file)})] + (t/is (= 1 (count rows))))))) (t/deftest file-thumbnail-ops (let [prof (th/create-profile* 1 {:is-active true}) @@ -1155,12 +1192,19 @@ (t/testing "gc task" ;; make the file eligible for GC waiting 300ms (configured ;; timeout for testing) - (th/sleep 300) + (let [res (th/run-task! :file-gc {:min-age 0})] + (t/is (= 1 (:processed res)))) - (let [res (th/run-task! "file-gc" {:min-age 0})] + (let [rows (th/db-query :file-thumbnail {:file-id (:id file)})] + (t/is (= 2 (count rows))) + (t/is (= 1 (count (remove (comp some? :deleted-at) rows))))) + + (let [res (th/run-task! :objects-gc {:min-age 0})] (t/is (= 1 (:processed res)))) (let [rows (th/db-query :file-thumbnail {:file-id (:id file)})] (t/is (= 1 (count rows))))))) + + diff --git a/backend/test/backend_tests/rpc_file_thumbnails_test.clj b/backend/test/backend_tests/rpc_file_thumbnails_test.clj index b31e955569..12e5d71586 100644 --- a/backend/test/backend_tests/rpc_file_thumbnails_test.clj +++ b/backend/test/backend_tests/rpc_file_thumbnails_test.clj @@ -6,6 +6,7 @@ (ns backend-tests.rpc-file-thumbnails-test (:require + [app.common.pprint :as pp] [app.common.thumbnails :as thc] [app.common.types.shape :as cts] [app.common.uuid :as uuid] @@ -114,9 +115,12 @@ ;; Run the File GC task that should remove unused file object ;; thumbnails - (let [result (th/run-task! :file-gc {:min-age (dt/duration 0)})] + (let [result (th/run-task! :file-gc {:min-age 0})] (t/is (= 1 (:processed result)))) + (let [result (th/run-task! :objects-gc {:min-age 0})] + (t/is (= 2 (:processed result)))) + ;; check if row2 related thumbnail row still exists (let [[row :as rows] (th/db-query :file-tagged-object-thumbnail {:file-id (:id file)} @@ -136,19 +140,19 @@ (t/is (= 0 (:freeze res)))) ;; check that storage object is still exists but is marked as deleted - (let [row (th/db-get :storage-object {:id (:media-id row1)} {::db/remove-deleted? false})] + (let [row (th/db-get :storage-object {:id (:media-id row1)} {::db/remove-deleted false})] (t/is (some? (:deleted-at row)))) ;; Run the storage gc deleted task, it should permanently delete ;; all storage objects related to the deleted thumbnails - (let [result (th/run-task! :storage-gc-deleted {:min-age (dt/duration 0)})] + (let [result (th/run-task! :storage-gc-deleted {:min-age 0})] (t/is (= 1 (:deleted result)))) (t/is (nil? (sto/get-object storage (:media-id row1)))) (t/is (some? (sto/get-object storage (:media-id row2)))) ;; check that storage object is still exists but is marked as deleted - (let [row (th/db-get :storage-object {:id (:media-id row1)} {::db/remove-deleted? false})] + (let [row (th/db-get :storage-object {:id (:media-id row1)} {::db/remove-deleted false})] (t/is (nil? row)))))) (t/deftest create-file-thumbnail @@ -188,13 +192,12 @@ (let [[row1 row2 :as rows] (th/db-query :file-thumbnail {:file-id (:id file)} - {:order-by [[:created-at :asc]]})] + {:order-by [[:revn :asc]]})] (t/is (= 2 (count rows))) (t/is (= (:file-id data1) (:file-id row1))) (t/is (= (:revn data1) (:revn row1))) (t/is (uuid? (:media-id row1))) - (t/is (= (:file-id data2) (:file-id row2))) (t/is (= (:revn data2) (:revn row2))) (t/is (uuid? (:media-id row2))) @@ -215,7 +218,10 @@ ;; Run the File GC task that should remove unused file object ;; thumbnails - (let [result (th/run-task! :file-gc {:min-age (dt/duration 0)})] + (let [result (th/run-task! :file-gc {:min-age 0})] + (t/is (= 1 (:processed result)))) + + (let [result (th/run-task! :objects-gc {:min-age 0})] (t/is (= 1 (:processed result)))) ;; check if row1 related thumbnail row still exists @@ -227,19 +233,54 @@ (t/is (= (:object-id data1) (:object-id row))) (t/is (uuid? (:media-id row1)))) + (let [result (th/run-task! :storage-gc-touched {:min-age 0})] + (t/is (= 1 (:delete result)))) + ;; Check if storage objects still exists after file-gc (t/is (nil? (sto/get-object storage (:media-id row1)))) (t/is (some? (sto/get-object storage (:media-id row2)))) - (let [row (th/db-get :storage-object {:id (:media-id row1)} {::db/remove-deleted? false})] + (let [row (th/db-get :storage-object {:id (:media-id row1)} {::db/remove-deleted false})] (t/is (some? (:deleted-at row)))) ;; Run the storage gc deleted task, it should permanently delete ;; all storage objects related to the deleted thumbnails - (let [result (th/run-task! :storage-gc-deleted {:min-age (dt/duration 0)})] + (let [result (th/run-task! :storage-gc-deleted {:min-age 0})] (t/is (= 1 (:deleted result)))) - (t/is (some? (sto/get-object storage (:media-id row2))))))) + (t/is (some? (sto/get-object storage (:media-id row2)))) + + ))) + +(t/deftest error-on-direct-storage-obj-deletion + (let [storage (::sto/storage th/*system*) + profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false + :revn 3}) + + data1 {::th/type :create-file-thumbnail + ::rpc/profile-id (:id profile) + :file-id (:id file) + :revn 2 + :media {:filename "sample.jpg" + :size 7923 + :path (th/tempfile "backend_tests/test_files/sample2.jpg") + :mtype "image/jpeg"}}] + + (let [out (th/command! data1)] + ;; (th/print-result! out) + (t/is (nil? (:error out))) + (t/is (contains? (:result out) :uri))) + + (let [[row1 :as rows] (th/db-query :file-thumbnail {:file-id (:id file)})] + (t/is (= 1 (count rows))) + + (t/is (thrown? org.postgresql.util.PSQLException + (th/db-delete! :storage-object {:id (:media-id row1)})))))) + + (t/deftest get-file-object-thumbnail (let [storage (::sto/storage th/*system*) @@ -279,6 +320,3 @@ (let [result (:result out)] (t/is (contains? result "test-key-2")))))) - - - diff --git a/backend/test/backend_tests/rpc_font_test.clj b/backend/test/backend_tests/rpc_font_test.clj index d1c3bdd60a..5d31f14b1f 100644 --- a/backend/test/backend_tests/rpc_font_test.clj +++ b/backend/test/backend_tests/rpc_font_test.clj @@ -92,3 +92,192 @@ :font-family :font-weight :font-style)))) + +(t/deftest font-deletion-1 + (let [prof (th/create-profile* 1 {:is-active true}) + team-id (:default-team-id prof) + proj-id (:default-project-id prof) + font-id (uuid/custom 10 1) + + data1 (-> (io/resource "backend_tests/test_files/font-1.woff") + io/input-stream + io/read-as-bytes) + + data2 (-> (io/resource "backend_tests/test_files/font-2.woff") + io/input-stream + io/read-as-bytes)] + + ;; Create front variant + (let [params {::th/type :create-font-variant + ::rpc/profile-id (:id prof) + :team-id team-id + :font-id font-id + :font-family "somefont" + :font-weight 400 + :font-style "normal" + :data {"font/woff" data1}} + out (th/command! params)] + ;; (th/print-result! out) + (t/is (nil? (:error out)))) + + (let [params {::th/type :create-font-variant + ::rpc/profile-id (:id prof) + :team-id team-id + :font-id font-id + :font-family "somefont" + :font-weight 500 + :font-style "normal" + :data {"font/woff" data2}} + out (th/command! params)] + ;; (th/print-result! out) + (t/is (nil? (:error out)))) + + (let [res (th/run-task! :storage-gc-touched {:min-age 0})] + (t/is (= 6 (:freeze res)))) + + (let [params {::th/type :delete-font + ::rpc/profile-id (:id prof) + :team-id team-id + :id font-id} + out (th/command! params)] + ;; (th/print-result! out) + (t/is (nil? (:error out))) + (t/is (nil? (:result out)))) + + (let [res (th/run-task! :storage-gc-touched {:min-age 0})] + (t/is (= 6 (:freeze res))) + (t/is (= 0 (:delete res)))) + + (let [res (th/run-task! :objects-gc {:min-age 0})] + (t/is (= 2 (:processed res)))) + + (let [res (th/run-task! :storage-gc-touched {:min-age 0})] + (t/is (= 0 (:freeze res))) + (t/is (= 6 (:delete res)))) + )) + +(t/deftest font-deletion-2 + (let [prof (th/create-profile* 1 {:is-active true}) + team-id (:default-team-id prof) + proj-id (:default-project-id prof) + font-id (uuid/custom 10 1) + + data1 (-> (io/resource "backend_tests/test_files/font-1.woff") + io/input-stream + io/read-as-bytes) + + data2 (-> (io/resource "backend_tests/test_files/font-2.woff") + io/input-stream + io/read-as-bytes)] + + ;; Create front variant + (let [params {::th/type :create-font-variant + ::rpc/profile-id (:id prof) + :team-id team-id + :font-id font-id + :font-family "somefont" + :font-weight 400 + :font-style "normal" + :data {"font/woff" data1}} + out (th/command! params)] + ;; (th/print-result! out) + (t/is (nil? (:error out)))) + + (let [params {::th/type :create-font-variant + ::rpc/profile-id (:id prof) + :team-id team-id + :font-id (uuid/custom 10 2) + :font-family "somefont" + :font-weight 400 + :font-style "normal" + :data {"font/woff" data2}} + out (th/command! params)] + ;; (th/print-result! out) + (t/is (nil? (:error out)))) + + (let [res (th/run-task! :storage-gc-touched {:min-age 0})] + (t/is (= 6 (:freeze res)))) + + (let [params {::th/type :delete-font + ::rpc/profile-id (:id prof) + :team-id team-id + :id font-id} + out (th/command! params)] + ;; (th/print-result! out) + (t/is (nil? (:error out))) + (t/is (nil? (:result out)))) + + (let [res (th/run-task! :storage-gc-touched {:min-age 0})] + (t/is (= 3 (:freeze res))) + (t/is (= 0 (:delete res)))) + + (let [res (th/run-task! :objects-gc {:min-age 0})] + (t/is (= 1 (:processed res)))) + + (let [res (th/run-task! :storage-gc-touched {:min-age 0})] + (t/is (= 0 (:freeze res))) + (t/is (= 3 (:delete res)))) + )) + +(t/deftest font-deletion-3 + (let [prof (th/create-profile* 1 {:is-active true}) + team-id (:default-team-id prof) + proj-id (:default-project-id prof) + font-id (uuid/custom 10 1) + + data1 (-> (io/resource "backend_tests/test_files/font-1.woff") + io/input-stream + io/read-as-bytes) + + data2 (-> (io/resource "backend_tests/test_files/font-2.woff") + io/input-stream + io/read-as-bytes) + params1 {::th/type :create-font-variant + ::rpc/profile-id (:id prof) + :team-id team-id + :font-id font-id + :font-family "somefont" + :font-weight 400 + :font-style "normal" + :data {"font/woff" data1}} + + params2 {::th/type :create-font-variant + ::rpc/profile-id (:id prof) + :team-id team-id + :font-id font-id + :font-family "somefont" + :font-weight 500 + :font-style "normal" + :data {"font/woff" data2}} + + out1 (th/command! params1) + out2 (th/command! params2)] + + ;; (th/print-result! out1) + (t/is (nil? (:error out1))) + (t/is (nil? (:error out2))) + + (let [res (th/run-task! :storage-gc-touched {:min-age 0})] + (t/is (= 6 (:freeze res)))) + + (let [params {::th/type :delete-font-variant + ::rpc/profile-id (:id prof) + :team-id team-id + :id (-> out1 :result :id)} + out (th/command! params)] + ;; (th/print-result! out) + (t/is (nil? (:error out))) + (t/is (nil? (:result out)))) + + (let [res (th/run-task! :storage-gc-touched {:min-age 0})] + (t/is (= 3 (:freeze res))) + (t/is (= 0 (:delete res)))) + + (let [res (th/run-task! :objects-gc {:min-age 0})] + (t/is (= 1 (:processed res)))) + + (let [res (th/run-task! :storage-gc-touched {:min-age 0})] + (t/is (= 0 (:freeze res))) + (t/is (= 3 (:delete res)))) + + )) diff --git a/backend/test/backend_tests/rpc_profile_test.clj b/backend/test/backend_tests/rpc_profile_test.clj index 64ccde95a3..f21dc17ecd 100644 --- a/backend/test/backend_tests/rpc_profile_test.clj +++ b/backend/test/backend_tests/rpc_profile_test.clj @@ -125,7 +125,7 @@ ;; profile is not deleted because it does not meet all ;; conditions to be deleted. - (let [result (th/run-task! :objects-gc {:min-age (dt/duration 0)})] + (let [result (th/run-task! :objects-gc {:min-age 0})] (t/is (= 0 (:processed result)))) ;; Request profile to be deleted @@ -144,12 +144,20 @@ (t/is (= 1 (count (:result out))))) ;; execute permanent deletion task - (let [result (th/run-task! :objects-gc {:min-age (dt/duration "-1m")})] - (t/is (= 2 (:processed result)))) + (let [result (th/run-task! :objects-gc {:min-age 0})] + (t/is (= 1 (:processed result)))) (let [row (th/db-get :team {:id (:default-team-id prof)} - {::db/remove-deleted? false})] + {::db/remove-deleted false})] + (t/is (nil? (:deleted-at row)))) + + (let [result (th/run-task! :orphan-teams-gc {:min-age 0})] + (t/is (= 1 (:processed result)))) + + (let [row (th/db-get :team + {:id (:default-team-id prof)} + {::db/remove-deleted false})] (t/is (dt/instant? (:deleted-at row)))) ;; query profile after delete @@ -158,67 +166,9 @@ out (th/command! params)] ;; (th/print-result! out) (let [result (:result out)] - (t/is (= uuid/zero (:id result))))))) + (t/is (= uuid/zero (:id result))))) -(t/deftest profile-immediate-deletion - (let [prof1 (th/create-profile* 1) - prof2 (th/create-profile* 2) - file (th/create-file* 1 {:profile-id (:id prof1) - :project-id (:default-project-id prof1) - :is-shared false}) - - team (th/create-team* 1 {:profile-id (:id prof1)}) - _ (th/create-team-role* {:team-id (:id team) - :profile-id (:id prof2) - :role :admin})] - - ;; profile is not deleted because it does not meet all - ;; conditions to be deleted. - (let [result (th/run-task! :objects-gc {:min-age (dt/duration 0)})] - (t/is (= 0 (:orphans result))) - (t/is (= 0 (:processed result)))) - - ;; just delete the profile - (th/db-delete! :profile {:id (:id prof1)}) - - ;; query files after profile deletion, expecting not found - (let [params {::th/type :get-project-files - ::rpc/profile-id (:id prof1) - :project-id (:default-project-id prof1)} - out (th/command! params)] - ;; (th/print-result! out) - (t/is (not (th/success? out))) - (let [edata (-> out :error ex-data)] - (t/is (= :not-found (:type edata))))) - - ;; the files and projects still exists on the database - (let [files (th/db-query :file {:project-id (:default-project-id prof1)}) - projects (th/db-query :project {:team-id (:default-team-id prof1)})] - (t/is (= 1 (count files))) - (t/is (= 1 (count projects)))) - - ;; execute the gc task - (let [result (th/run-task! :objects-gc {:min-age (dt/duration "-1m")})] - (t/is (= 1 (:processed result))) - (t/is (= 1 (:orphans result)))) - - ;; Check the deletion flag on the default profile team - (let [row (th/db-get :team - {:id (:default-team-id prof1)} - {::db/remove-deleted? false})] - (t/is (dt/instant? (:deleted-at row)))) - - ;; Check the deletion flag on the shared team - (let [row (th/db-get :team - {:id (:id team)} - {::db/remove-deleted? false})] - (t/is (nil? (:deleted-at row)))) - - ;; Check the roles on the shared team - (let [rows (th/db-query :team-profile-rel {:team-id (:id team)})] - (t/is (= 1 (count rows))) - (t/is (= (:id prof2) (get-in rows [0 :profile-id]))) - (t/is (= false (get-in rows [0 :is-owner])))))) + )) (t/deftest registration-domain-whitelist (let [whitelist #{"gmail.com" "hey.com" "ya.ru"}] diff --git a/backend/test/backend_tests/rpc_project_test.clj b/backend/test/backend_tests/rpc_project_test.clj index acfb6fdf21..f35105a97f 100644 --- a/backend/test/backend_tests/rpc_project_test.clj +++ b/backend/test/backend_tests/rpc_project_test.clj @@ -172,14 +172,13 @@ (t/deftest test-deletion - (let [task (:app.tasks.objects-gc/handler th/*system*) - profile1 (th/create-profile* 1) + (let [profile1 (th/create-profile* 1) project (th/create-project* 1 {:team-id (:default-team-id profile1) :profile-id (:id profile1)})] ;; project is not deleted because it does not meet all ;; conditions to be deleted. - (let [result (task {:min-age (dt/duration 0)})] + (let [result (th/run-task! :objects-gc {:min-age 0})] (t/is (= 0 (:processed result)))) ;; query the list of projects @@ -187,6 +186,7 @@ ::rpc/profile-id (:id profile1) :team-id (:default-team-id profile1)} out (th/command! data)] + ;; (th/print-result! out) (t/is (nil? (:error out))) (let [result (:result out)] @@ -210,7 +210,7 @@ (t/is (= 1 (count result))))) ;; run permanent deletion (should be noop) - (let [result (task {:min-age (dt/duration {:minutes 1})})] + (let [result (th/run-task! :objects-gc {:min-age (dt/duration {:minutes 1})})] (t/is (= 0 (:processed result)))) ;; query the list of files of a after soft deletion @@ -224,7 +224,7 @@ (t/is (= 0 (count result))))) ;; run permanent deletion - (let [result (task {:min-age (dt/duration 0)})] + (let [result (th/run-task! :objects-gc {:min-age 0})] (t/is (= 1 (:processed result)))) ;; query the list of files of a after hard deletion diff --git a/backend/test/backend_tests/rpc_team_test.clj b/backend/test/backend_tests/rpc_team_test.clj index 45e4c48071..8252e9aa30 100644 --- a/backend/test/backend_tests/rpc_team_test.clj +++ b/backend/test/backend_tests/rpc_team_test.clj @@ -269,76 +269,6 @@ (t/is (= 1 (count members))) (t/is (true? (-> members first :can-edit)))))))) -(t/deftest team-deletion - (let [profile1 (th/create-profile* 1 {:is-active true}) - team (th/create-team* 1 {:profile-id (:id profile1)}) - pool (:app.db/pool th/*system*) - data {::th/type :delete-team - ::rpc/profile-id (:id profile1) - :team-id (:id team)}] - - ;; team is not deleted because it does not meet all - ;; conditions to be deleted. - (let [result (th/run-task! :objects-gc {:min-age (dt/duration 0)})] - (t/is (= 0 (:processed result)))) - - ;; query the list of teams - (let [data {::th/type :get-teams - ::rpc/profile-id (:id profile1)} - out (th/command! data)] - ;; (th/print-result! out) - (t/is (th/success? out)) - (let [result (:result out)] - (t/is (= 2 (count result))) - (t/is (= (:id team) (get-in result [1 :id]))) - (t/is (= (:default-team-id profile1) (get-in result [0 :id]))))) - - ;; Request team to be deleted - (let [params {::th/type :delete-team - ::rpc/profile-id (:id profile1) - :id (:id team)} - out (th/command! params)] - (t/is (th/success? out))) - - ;; query the list of teams after soft deletion - (let [data {::th/type :get-teams - ::rpc/profile-id (:id profile1)} - out (th/command! data)] - ;; (th/print-result! out) - (t/is (th/success? out)) - (let [result (:result out)] - (t/is (= 1 (count result))) - (t/is (= (:default-team-id profile1) (get-in result [0 :id]))))) - - ;; run permanent deletion (should be noop) - (let [result (th/run-task! :objects-gc {:min-age (dt/duration {:minutes 1})})] - (t/is (= 0 (:processed result)))) - - ;; query the list of projects after hard deletion - (let [data {::th/type :get-projects - ::rpc/profile-id (:id profile1) - :team-id (:id team)} - out (th/command! data)] - ;; (th/print-result! out) - (t/is (not (th/success? out))) - (let [edata (-> out :error ex-data)] - (t/is (= :not-found (:type edata))))) - - ;; run permanent deletion - (let [result (th/run-task! :objects-gc {:min-age (dt/duration 0)})] - (t/is (= 1 (:processed result)))) - - ;; query the list of projects of a after hard deletion - (let [data {::th/type :get-projects - ::rpc/profile-id (:id profile1) - :team-id (:id team)} - out (th/command! data)] - ;; (th/print-result! out) - - (t/is (not (th/success? out))) - (let [edata (-> out :error ex-data)] - (t/is (= :not-found (:type edata))))))) - (t/deftest query-team-invitations (let [prof (th/create-profile* 1 {:is-active true}) team (th/create-team* 1 {:profile-id (:id prof)}) @@ -418,3 +348,119 @@ (t/is (th/success? out)) (t/is (nil? (:result out))) (t/is (nil? res))))) + + +(t/deftest team-deletion-1 + (let [profile1 (th/create-profile* 1 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id profile1)}) + pool (:app.db/pool th/*system*) + data {::th/type :delete-team + ::rpc/profile-id (:id profile1) + :team-id (:id team)}] + + ;; team is not deleted because it does not meet all + ;; conditions to be deleted. + (let [result (th/run-task! :objects-gc {:min-age (dt/duration 0)})] + (t/is (= 0 (:processed result)))) + + ;; query the list of teams + (let [data {::th/type :get-teams + ::rpc/profile-id (:id profile1)} + out (th/command! data)] + ;; (th/print-result! out) + (t/is (th/success? out)) + (let [result (:result out)] + (t/is (= 2 (count result))) + (t/is (= (:id team) (get-in result [1 :id]))) + (t/is (= (:default-team-id profile1) (get-in result [0 :id]))))) + + ;; Request team to be deleted + (let [params {::th/type :delete-team + ::rpc/profile-id (:id profile1) + :id (:id team)} + out (th/command! params)] + (t/is (th/success? out))) + + ;; query the list of teams after soft deletion + (let [data {::th/type :get-teams + ::rpc/profile-id (:id profile1)} + out (th/command! data)] + ;; (th/print-result! out) + (t/is (th/success? out)) + (let [result (:result out)] + (t/is (= 1 (count result))) + (t/is (= (:default-team-id profile1) (get-in result [0 :id]))))) + + ;; run permanent deletion (should be noop) + (let [result (th/run-task! :objects-gc {:min-age (dt/duration {:minutes 1})})] + (t/is (= 0 (:processed result)))) + + ;; query the list of projects after hard deletion + (let [data {::th/type :get-projects + ::rpc/profile-id (:id profile1) + :team-id (:id team)} + out (th/command! data)] + ;; (th/print-result! out) + (t/is (not (th/success? out))) + (let [edata (-> out :error ex-data)] + (t/is (= :not-found (:type edata))))) + + ;; run permanent deletion + (let [result (th/run-task! :objects-gc {:min-age (dt/duration 0)})] + (t/is (= 2 (:processed result)))) + + ;; query the list of projects of a after hard deletion + (let [data {::th/type :get-projects + ::rpc/profile-id (:id profile1) + :team-id (:id team)} + out (th/command! data)] + ;; (th/print-result! out) + + (t/is (not (th/success? out))) + (let [edata (-> out :error ex-data)] + (t/is (= :not-found (:type edata))))))) + + +(t/deftest team-deletion-2 + (let [storage (-> (:app.storage/storage th/*system*) + (assoc ::sto/backend :assets-fs)) + prof (th/create-profile* 1) + + team (th/create-team* 1 {:profile-id (:id prof)}) + + proj (th/create-project* 1 {:profile-id (:id prof) + :team-id (:id team)}) + file (th/create-file* 1 {:profile-id (:id prof) + :project-id (:default-project-id team) + :is-shared false}) + + mfile {:filename "sample.jpg" + :path (th/tempfile "backend_tests/test_files/sample.jpg") + :mtype "image/jpeg" + :size 312043}] + + + (let [params {::th/type :upload-file-media-object + ::rpc/profile-id (:id prof) + :file-id (:id file) + :is-local true + :name "testfile" + :content mfile} + + out (th/command! params)] + (t/is (nil? (:error out)))) + + (let [params {::th/type :delete-team + ::rpc/profile-id (:id prof) + :id (:id team)} + out (th/command! params)] + #_(th/print-result! out) + (t/is (nil? (:error out)))) + + (let [rows (th/db-exec! ["select * from team where id = ?" (:id team)])] + (t/is (= 1 (count rows))) + (t/is (dt/instant? (:deleted-at (first rows))))) + + (let [result (th/run-task! :objects-gc {:min-age 0})] + (t/is (= 5 (:processed result)))) + )) diff --git a/backend/test/backend_tests/storage_test.clj b/backend/test/backend_tests/storage_test.clj index 5277544204..ccc0d08630 100644 --- a/backend/test/backend_tests/storage_test.clj +++ b/backend/test/backend_tests/storage_test.clj @@ -113,7 +113,7 @@ (let [res (th/run-task! :storage-gc-deleted {})] (t/is (= 1 (:deleted res)))) - (let [res (db/exec-one! th/*pool* ["select count(*) from storage_object;"])] + (let [res (th/db-exec-one! ["select count(*) from storage_object;"])] (t/is (= 2 (:count res)))))) (t/deftest test-touched-gc-task-1 @@ -156,29 +156,33 @@ (t/is (= (:media-id result-1) (:media-id result-2))) - ;; now we proceed to manually delete one file-media-object - (db/exec-one! th/*pool* ["delete from file_media_object where id = ?" (:id result-1)]) + (th/db-update! :file-media-object + {:deleted-at (dt/now)} + {:id (:id result-1)}) + + ;; run the objects gc task for permanent deletion + (let [res (th/run-task! :objects-gc {:min-age 0})] + (t/is (= 1 (:processed res)))) ;; check that we still have all the storage objects - (let [res (db/exec-one! th/*pool* ["select count(*) from storage_object"])] + (let [res (th/db-exec-one! ["select count(*) from storage_object"])] (t/is (= 2 (:count res)))) ;; now check if the storage objects are touched - (let [res (db/exec-one! th/*pool* ["select count(*) from storage_object where touched_at is not null"])] + (let [res (th/db-exec-one! ["select count(*) from storage_object where touched_at is not null"])] (t/is (= 2 (:count res)))) ;; run the touched gc task - (let [task (:app.storage/gc-touched-task th/*system*) - res (task {})] + (let [res (th/run-task! :storage-gc-touched {})] (t/is (= 2 (:freeze res))) (t/is (= 0 (:delete res)))) ;; now check that there are no touched objects - (let [res (db/exec-one! th/*pool* ["select count(*) from storage_object where touched_at is not null"])] + (let [res (th/db-exec-one! ["select count(*) from storage_object where touched_at is not null"])] (t/is (= 0 (:count res)))) ;; now check that all objects are marked to be deleted - (let [res (db/exec-one! th/*pool* ["select count(*) from storage_object where deleted_at is not null"])] + (let [res (th/db-exec-one! ["select count(*) from storage_object where deleted_at is not null"])] (t/is (= 0 (:count res))))))) @@ -231,31 +235,35 @@ (t/is (nil? (:error out2))) ;; run the touched gc task - (let [task (:app.storage/gc-touched-task th/*system*) - res (task {})] + (let [res (th/run-task! :storage-gc-touched {})] (t/is (= 5 (:freeze res))) (t/is (= 0 (:delete res))) (let [result-1 (:result out1) result-2 (:result out2)] - ;; now we proceed to manually delete one team-font-variant - (db/exec-one! th/*pool* ["delete from team_font_variant where id = ?" (:id result-2)]) + (th/db-update! :team-font-variant + {:deleted-at (dt/now)} + {:id (:id result-2)}) + + ;; run the objects gc task for permanent deletion + (let [res (th/run-task! :objects-gc {:min-age 0})] + (t/is (= 1 (:processed res)))) ;; revert touched state to all storage objects - (db/exec-one! th/*pool* ["update storage_object set touched_at=now()"]) + (th/db-exec-one! ["update storage_object set touched_at=now()"]) ;; Run the task again - (let [res (task {})] + (let [res (th/run-task! :storage-gc-touched {})] (t/is (= 2 (:freeze res))) (t/is (= 3 (:delete res)))) ;; now check that there are no touched objects - (let [res (db/exec-one! th/*pool* ["select count(*) from storage_object where touched_at is not null"])] + (let [res (th/db-exec-one! ["select count(*) from storage_object where touched_at is not null"])] (t/is (= 0 (:count res)))) ;; now check that all objects are marked to be deleted - (let [res (db/exec-one! th/*pool* ["select count(*) from storage_object where deleted_at is not null"])] + (let [res (th/db-exec-one! ["select count(*) from storage_object where deleted_at is not null"])] (t/is (= 3 (:count res)))))))) (t/deftest test-touched-gc-task-3 @@ -289,28 +297,28 @@ result-2 (:result out2)] ;; now we proceed to manually mark all storage objects touched - (db/exec-one! th/*pool* ["update storage_object set touched_at=now()"]) + (th/db-exec! ["update storage_object set touched_at=now()"]) ;; run the touched gc task - (let [task (:app.storage/gc-touched-task th/*system*) - res (task {})] + (let [res (th/run-task! "storage-gc-touched" {:min-age 0})] (t/is (= 2 (:freeze res))) (t/is (= 0 (:delete res)))) ;; check that we have all object in the db - (let [res (db/exec-one! th/*pool* ["select count(*) from storage_object where deleted_at is null"])] - (t/is (= 2 (:count res))))) + (let [rows (th/db-exec! ["select * from storage_object"])] + (t/is (= 2 (count rows))))) ;; now we proceed to manually delete all file_media_object - (db/exec-one! th/*pool* ["delete from file_media_object"]) + (th/db-exec! ["update file_media_object set deleted_at = now()"]) + + (let [res (th/run-task! "objects-gc" {:min-age 0})] + (t/is (= 2 (:processed res)))) ;; run the touched gc task - (let [task (:app.storage/gc-touched-task th/*system*) - res (task {})] + (let [res (th/run-task! "storage-gc-touched" {:min-age 0})] (t/is (= 0 (:freeze res))) (t/is (= 2 (:delete res)))) ;; check that we have all no objects - (let [res (db/exec-one! th/*pool* ["select count(*) from storage_object where deleted_at is null"])] - (t/is (= 0 (:count res)))))) - + (let [rows (th/db-exec! ["select * from storage_object where deleted_at is null"])] + (t/is (= 0 (count rows)))))) diff --git a/common/src/app/common/data.cljc b/common/src/app/common/data.cljc index c997c05c0f..4068839865 100644 --- a/common/src/app/common/data.cljc +++ b/common/src/app/common/data.cljc @@ -517,6 +517,13 @@ (->> (apply c/iteration args) (concat-all))) +(defn add-at-index + "Insert an element in a vector at an arbitrary index" + [coll index element] + (assert (vector? coll)) + (let [[before after] (split-at index coll)] + (concat-vec [] before [element] after))) + (defn insert-at-index "Insert a list of elements at the given index of a previous list. Replace all existing elems." diff --git a/common/src/app/common/files/changes_builder.cljc b/common/src/app/common/files/changes_builder.cljc index 7efc11c311..6668f91af8 100644 --- a/common/src/app/common/files/changes_builder.cljc +++ b/common/src/app/common/files/changes_builder.cljc @@ -358,13 +358,15 @@ (defn changed-attrs "Returns the list of attributes that will change when `update-fn` is applied" - [object objects update-fn {:keys [attrs]}] + [object objects update-fn {:keys [attrs with-objects?]}] (let [changed? (fn [old new attr] (let [old-val (get old attr) new-val (get new attr)] (not= old-val new-val))) - new-obj (update-fn object objects)] + new-obj (if with-objects? + (update-fn object objects) + (update-fn object))] (when-not (= object new-obj) (let [attrs (or attrs (d/concat-set (keys object) (keys new-obj)))] (filter (partial changed? object new-obj) attrs))))) @@ -375,8 +377,8 @@ ([changes ids update-fn] (update-shapes changes ids update-fn nil)) - ([changes ids update-fn {:keys [attrs ignore-geometry? ignore-touched] - :or {ignore-geometry? false ignore-touched false}}] + ([changes ids update-fn {:keys [attrs ignore-geometry? ignore-touched with-objects?] + :or {ignore-geometry? false ignore-touched false with-objects? false}}] (assert-container-id! changes) (assert-objects! changes) (let [page-id (::page-id (meta changes)) @@ -412,7 +414,7 @@ update-shape (fn [changes id] (let [old-obj (get objects id) - new-obj (update-fn old-obj objects)] + new-obj (if with-objects? (update-fn old-obj objects) (update-fn old-obj))] (if (= old-obj new-obj) changes (let [[rops uops] (-> (or attrs (d/concat-set (keys old-obj) (keys new-obj))) diff --git a/common/src/app/common/files/shapes_helpers.cljc b/common/src/app/common/files/shapes_helpers.cljc index a36fadf916..8a656886f1 100644 --- a/common/src/app/common/files/shapes_helpers.cljc +++ b/common/src/app/common/files/shapes_helpers.cljc @@ -16,7 +16,7 @@ [app.common.uuid :as uuid])) (defn prepare-add-shape - [changes shape objects _selected] + [changes shape objects] (let [index (:index (meta shape)) id (:id shape) @@ -34,7 +34,7 @@ (cond-> (some? cell) (pcb/update-shapes [(:parent-id shape)] #(ctl/push-into-cell % [id] row column))) (cond-> (ctl/grid-layout? objects (:parent-id shape)) - (pcb/update-shapes [(:parent-id shape)] ctl/assign-cells)))] + (pcb/update-shapes [(:parent-id shape)] ctl/assign-cells {:with-objects? true})))] [shape changes])) (defn prepare-move-shapes-into-frame @@ -50,58 +50,120 @@ (pcb/update-shapes ordered-indexes #(cond-> % (cfh/frame-shape? %) (assoc :hide-in-viewer true))) (pcb/change-parent frame-id to-move-shapes 0) (cond-> (ctl/grid-layout? objects frame-id) - (pcb/update-shapes [frame-id] ctl/assign-cells)) - (pcb/reorder-grid-children [frame-id])) + (-> (pcb/update-shapes [frame-id] ctl/assign-cells {:with-objects? true}) + (pcb/reorder-grid-children [frame-id])))) changes))) (defn prepare-create-artboard-from-selection - [changes id parent-id objects selected index frame-name without-fill?] - (let [selected-objs (map #(get objects %) selected) - new-index (or index - (cfh/get-index-replacement selected objects))] - (when (d/not-empty? selected) - (let [srect (gsh/shapes->rect selected-objs) - selected-id (first selected) + ([changes id parent-id objects selected index frame-name without-fill?] + (prepare-create-artboard-from-selection + changes id parent-id objects selected index frame-name without-fill? nil)) - frame-id (dm/get-in objects [selected-id :frame-id]) - parent-id (or parent-id (dm/get-in objects [selected-id :parent-id])) + ([changes id parent-id objects selected index frame-name without-fill? target-cell-id] + (let [selected-objs (map #(get objects %) selected) + new-index (or index + (cfh/get-index-replacement selected objects))] + (when (d/not-empty? selected) + (let [srect (gsh/shapes->rect selected-objs) + selected-id (first selected) - attrs {:type :frame - :x (:x srect) - :y (:y srect) - :width (:width srect) - :height (:height srect)} + frame-id (dm/get-in objects [selected-id :frame-id]) + parent-id (or parent-id (dm/get-in objects [selected-id :parent-id])) + base-parent (get objects parent-id) - shape (cts/setup-shape - (cond-> attrs - (some? id) - (assoc :id id) + attrs {:type :frame + :x (:x srect) + :y (:y srect) + :width (:width srect) + :height (:height srect)} - (some? frame-name) - (assoc :name frame-name) + shape (cts/setup-shape + (cond-> attrs + (some? id) + (assoc :id id) - :always - (assoc :frame-id frame-id - :parent-id parent-id - :shapes (into [] selected)) + (some? frame-name) + (assoc :name frame-name) - :always - (with-meta {:index new-index}) + :always + (assoc :frame-id frame-id + :parent-id parent-id + :shapes (into [] selected)) - (or (not= frame-id uuid/zero) without-fill?) - (assoc :fills [] :hide-in-viewer true))) + :always + (with-meta {:index new-index}) - [shape changes] - (prepare-add-shape changes shape objects selected) + (or (not= frame-id uuid/zero) without-fill?) + (assoc :fills [] :hide-in-viewer true))) - changes - (prepare-move-shapes-into-frame changes (:id shape) selected objects) + [shape changes] + (prepare-add-shape changes shape objects) - changes - (cond-> changes - (ctl/grid-layout? objects (:parent-id shape)) - (-> (pcb/update-shapes [(:parent-id shape)] ctl/assign-cells) - (pcb/reorder-grid-children [(:parent-id shape)])))] + changes + (prepare-move-shapes-into-frame changes (:id shape) selected objects) - [shape changes])))) + changes + (cond-> changes + (ctl/grid-layout? objects (:parent-id shape)) + (-> (cond-> (some? target-cell-id) + (pcb/update-shapes + [(:parent-id shape)] + (fn [parent] + (-> parent + (assoc :layout-grid-cells (:layout-grid-cells base-parent)) + (assoc-in [:layout-grid-cells target-cell-id :shapes] [id]) + (assoc :position :auto))))) + (pcb/update-shapes [(:parent-id shape)] ctl/assign-cells {:with-objects? true}) + (pcb/reorder-grid-children [(:parent-id shape)])))] + [shape changes]))))) + + +(defn prepare-create-empty-artboard + [changes frame-id parent-id objects index frame-name without-fill? target-cell-id] + + (let [base-parent (get objects parent-id) + + attrs {:type :frame + :x 0 + :y 0 + :width 0.01 + :height 0.01} + + shape (cts/setup-shape + (cond-> attrs + (some? frame-id) + (assoc :id frame-id) + + (some? frame-name) + (assoc :name frame-name) + + :always + (assoc :frame-id frame-id + :parent-id parent-id + :shapes []) + + :always + (with-meta {:index index}) + + (or (not= frame-id uuid/zero) without-fill?) + (assoc :fills [] :hide-in-viewer true))) + + [shape changes] + (prepare-add-shape changes shape objects) + + changes + (cond-> changes + (ctl/grid-layout? objects (:parent-id shape)) + (-> (cond-> (some? target-cell-id) + (pcb/update-shapes + [(:parent-id shape)] + (fn [parent] + (-> parent + (assoc :layout-grid-cells (:layout-grid-cells base-parent)) + (assoc-in [:layout-grid-cells target-cell-id :shapes] [frame-id]) + (assoc :position :auto))))) + (pcb/update-shapes [(:parent-id shape)] ctl/assign-cells {:with-objects? true}) + (pcb/reorder-grid-children [(:parent-id shape)])))] + + [shape changes])) diff --git a/common/src/app/common/geom/line.cljc b/common/src/app/common/geom/line.cljc new file mode 100644 index 0000000000..6ab28d5fc1 --- /dev/null +++ b/common/src/app/common/geom/line.cljc @@ -0,0 +1,18 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.common.geom.line) + +(defn line-value + [[{px :x py :y} {vx :x vy :y}] {:keys [x y]}] + (let [a vy + b (- vx) + c (+ (* (- vy) px) (* vx py))] + (+ (* a x) (* b y) c))) + +(defn is-inside-lines? + [line-1 line-2 pos] + (< (* (line-value line-1 pos) (line-value line-2 pos)) 0)) diff --git a/common/src/app/common/geom/shapes/grid_layout/params.cljc b/common/src/app/common/geom/shapes/grid_layout/params.cljc index b50fbf4483..7c0dafc4ed 100644 --- a/common/src/app/common/geom/shapes/grid_layout/params.cljc +++ b/common/src/app/common/geom/shapes/grid_layout/params.cljc @@ -113,8 +113,8 @@ ([_objects shapes parent] (if (empty? shapes) - (-> {:layout-grid-columns [{:type :auto} {:type :auto}] - :layout-grid-rows [{:type :auto} {:type :auto}]} + (-> {:layout-grid-columns [ctl/default-track-value ctl/default-track-value] + :layout-grid-rows [ctl/default-track-value ctl/default-track-value]} (ctl/create-cells [1 1 2 2])) (let [all-shapes-rect (gco/shapes->rect shapes) @@ -149,8 +149,8 @@ 0 (/ (- (:height all-shapes-rect) total-rows-height) (dec num-rows))) - layout-grid-rows (mapv (constantly (array-map :type :auto)) rows) - layout-grid-columns (mapv (constantly (array-map :type :auto)) cols) + layout-grid-rows (mapv (constantly ctl/default-track-value) rows) + layout-grid-columns (mapv (constantly ctl/default-track-value) cols) parent-childs-vector (gpt/to-vec (gpo/origin (:points parent)) (gpt/point all-shapes-rect)) p-left (:x parent-childs-vector) diff --git a/common/src/app/common/geom/shapes/grid_layout/positions.cljc b/common/src/app/common/geom/shapes/grid_layout/positions.cljc index e6504519f5..6ab0c0bb75 100644 --- a/common/src/app/common/geom/shapes/grid_layout/positions.cljc +++ b/common/src/app/common/geom/shapes/grid_layout/positions.cljc @@ -9,6 +9,7 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.files.helpers :as cfh] + [app.common.geom.line :as gl] [app.common.geom.point :as gpt] [app.common.geom.shapes.common :as gco] [app.common.geom.shapes.grid-layout.layout-data :as ld] @@ -182,18 +183,6 @@ (-> (ctm/add-modifiers fill-modifiers) (ctm/move position-delta))))) - -(defn line-value - [[{px :x py :y} {vx :x vy :y}] {:keys [x y]}] - (let [a vy - b (- vx) - c (+ (* (- vy) px) (* vx py))] - (+ (* a x) (* b y) c))) - -(defn is-inside-lines? - [line-1 line-2 pos] - (< (* (line-value line-1 pos) (line-value line-2 pos)) 0)) - (defn get-position-grid-coord [{:keys [layout-bounds row-tracks column-tracks]} position] @@ -206,7 +195,7 @@ (fn is-inside-track? [{:keys [start-p size] :as track}] (let [unit-v (vfn 1) end-p (gpt/add start-p (ofn size))] - (is-inside-lines? [start-p unit-v] [end-p unit-v] position))))) + (gl/is-inside-lines? [start-p unit-v] [end-p unit-v] position))))) make-min-distance-track (fn [type] @@ -214,8 +203,8 @@ (fn [[selected selected-dist] [cur-idx {:keys [start-p size] :as track}]] (let [unit-v (vfn 1) end-p (gpt/add start-p (ofn size)) - dist-1 (mth/abs (line-value [start-p unit-v] position)) - dist-2 (mth/abs (line-value [end-p unit-v] position))] + dist-1 (mth/abs (gl/line-value [start-p unit-v] position)) + dist-2 (mth/abs (gl/line-value [end-p unit-v] position))] (if (or (< dist-1 selected-dist) (< dist-2 selected-dist)) [[cur-idx track] (min dist-1 dist-2)] diff --git a/common/src/app/common/svg.cljc b/common/src/app/common/svg.cljc index 39379028f3..bc4f79f19e 100644 --- a/common/src/app/common/svg.cljc +++ b/common/src/app/common/svg.cljc @@ -9,9 +9,6 @@ #?(:cljs ["./svg/optimizer.js" :as svgo]) #?(:clj [clojure.xml :as xml] :cljs [tubax.core :as tubax]) - #?(:clj [integrant.core :as ig]) - #?(:clj [app.common.jsrt :as jsrt]) - #?(:clj [app.common.logging :as l]) [app.common.data :as d] [app.common.data.macros :as dm] [app.common.geom.matrix :as gmt] @@ -1053,21 +1050,6 @@ :height (d/parse-integer (:height attrs) 0)})))] (reduce-nodes redfn [] svg-data ))) -#?(:cljs - (defn optimize - ([input] (optimize input nil)) - ([input options] - (svgo/optimize input (clj->js options)))) - :clj - (defn optimize - [pool data] - (dm/assert! "expected a valid pool" (jsrt/pool? pool)) - (dm/assert! "expect data to be a string" (string? data)) - (jsrt/run! pool - (fn [context] - (jsrt/set! context "svgData" data) - (jsrt/eval! context "penpotSvgo.optimize(svgData, {})"))))) - #?(:clj (defn- secure-parser-factory [^InputStream input ^XMLHandler handler] @@ -1091,15 +1073,9 @@ (dm/with-open [istream (IOUtils/toInputStream text "UTF-8")] (xml/parse istream secure-parser-factory))))) -#?(:clj - (defmethod ig/init-key ::optimizer - [_ _] - (l/info :hint "initializing svg optimizer pool") - (let [init (jsrt/resource->source "app/common/svg/optimizer.js")] - (jsrt/pool :init init)))) - -#?(:clj - (defmethod ig/halt-key! ::optimizer - [_ pool] - (l/info :hint "stopping svg optimizer pool") - (.close ^java.lang.AutoCloseable pool))) +;; FIXME pass correct plugin set +#?(:cljs + (defn optimize + ([input] (optimize input nil)) + ([input options] + (svgo/optimize input (clj->js options))))) diff --git a/common/src/app/common/svg/optimizer.js b/common/src/app/common/svg/optimizer.js index 70826b8bdb..ccd19b6970 100644 --- a/common/src/app/common/svg/optimizer.js +++ b/common/src/app/common/svg/optimizer.js @@ -29431,12 +29431,13 @@ const optimize = (input, config) => { exports.optimize = optimize; -},{"./svgo/parser.js":322,"./svgo/plugins.js":324,"./svgo/stringifier.js":381,"./svgo/tools.js":383,"lodash/isPlainObject":308}],320:[function(require,module,exports){ +},{"./svgo/parser.js":322,"./svgo/plugins.js":324,"./svgo/stringifier.js":382,"./svgo/tools.js":384,"lodash/isPlainObject":308}],320:[function(require,module,exports){ 'use strict'; exports.builtin = [ require('./plugins/default.js'), require('./plugins/safe.js'), + require('./plugins/safeAndFast.js'), require('./plugins/addAttributesToSVGElement.js'), require('./plugins/addClassesToSVGElement.js'), require('./plugins/cleanupAttrs.js'), @@ -29489,7 +29490,7 @@ exports.builtin = [ require('./plugins/sortDefsChildren.js'), ]; -},{"./plugins/addAttributesToSVGElement.js":328,"./plugins/addClassesToSVGElement.js":329,"./plugins/cleanupAttrs.js":331,"./plugins/cleanupEnableBackground.js":332,"./plugins/cleanupIds.js":333,"./plugins/cleanupListOfValues.js":334,"./plugins/cleanupNumericValues.js":335,"./plugins/collapseGroups.js":336,"./plugins/convertColors.js":337,"./plugins/convertEllipseToCircle.js":338,"./plugins/convertPathData.js":339,"./plugins/convertShapeToPath.js":340,"./plugins/convertStyleToAttrs.js":341,"./plugins/convertTransform.js":342,"./plugins/default.js":343,"./plugins/inlineStyles.js":344,"./plugins/mergePaths.js":345,"./plugins/mergeStyles.js":346,"./plugins/minifyStyles.js":347,"./plugins/moveElemsAttrsToGroup.js":348,"./plugins/moveGroupAttrsToElems.js":349,"./plugins/prefixIds.js":350,"./plugins/removeAttributesBySelector.js":351,"./plugins/removeAttrs.js":352,"./plugins/removeComments.js":353,"./plugins/removeDesc.js":354,"./plugins/removeDimensions.js":355,"./plugins/removeDoctype.js":356,"./plugins/removeEditorsNSData.js":357,"./plugins/removeElementsByAttr.js":358,"./plugins/removeEmptyAttrs.js":359,"./plugins/removeEmptyContainers.js":360,"./plugins/removeEmptyText.js":361,"./plugins/removeHiddenElems.js":362,"./plugins/removeMetadata.js":363,"./plugins/removeNonInheritableGroupAttrs.js":364,"./plugins/removeOffCanvasPaths.js":365,"./plugins/removeRasterImages.js":366,"./plugins/removeScriptElement.js":367,"./plugins/removeStyleElement.js":368,"./plugins/removeTitle.js":369,"./plugins/removeUnknownsAndDefaults.js":370,"./plugins/removeUnusedNS.js":371,"./plugins/removeUselessDefs.js":372,"./plugins/removeUselessStrokeAndFill.js":373,"./plugins/removeViewBox.js":374,"./plugins/removeXMLNS.js":375,"./plugins/removeXMLProcInst.js":376,"./plugins/reusePaths.js":377,"./plugins/safe.js":378,"./plugins/sortAttrs.js":379,"./plugins/sortDefsChildren.js":380}],321:[function(require,module,exports){ +},{"./plugins/addAttributesToSVGElement.js":328,"./plugins/addClassesToSVGElement.js":329,"./plugins/cleanupAttrs.js":331,"./plugins/cleanupEnableBackground.js":332,"./plugins/cleanupIds.js":333,"./plugins/cleanupListOfValues.js":334,"./plugins/cleanupNumericValues.js":335,"./plugins/collapseGroups.js":336,"./plugins/convertColors.js":337,"./plugins/convertEllipseToCircle.js":338,"./plugins/convertPathData.js":339,"./plugins/convertShapeToPath.js":340,"./plugins/convertStyleToAttrs.js":341,"./plugins/convertTransform.js":342,"./plugins/default.js":343,"./plugins/inlineStyles.js":344,"./plugins/mergePaths.js":345,"./plugins/mergeStyles.js":346,"./plugins/minifyStyles.js":347,"./plugins/moveElemsAttrsToGroup.js":348,"./plugins/moveGroupAttrsToElems.js":349,"./plugins/prefixIds.js":350,"./plugins/removeAttributesBySelector.js":351,"./plugins/removeAttrs.js":352,"./plugins/removeComments.js":353,"./plugins/removeDesc.js":354,"./plugins/removeDimensions.js":355,"./plugins/removeDoctype.js":356,"./plugins/removeEditorsNSData.js":357,"./plugins/removeElementsByAttr.js":358,"./plugins/removeEmptyAttrs.js":359,"./plugins/removeEmptyContainers.js":360,"./plugins/removeEmptyText.js":361,"./plugins/removeHiddenElems.js":362,"./plugins/removeMetadata.js":363,"./plugins/removeNonInheritableGroupAttrs.js":364,"./plugins/removeOffCanvasPaths.js":365,"./plugins/removeRasterImages.js":366,"./plugins/removeScriptElement.js":367,"./plugins/removeStyleElement.js":368,"./plugins/removeTitle.js":369,"./plugins/removeUnknownsAndDefaults.js":370,"./plugins/removeUnusedNS.js":371,"./plugins/removeUselessDefs.js":372,"./plugins/removeUselessStrokeAndFill.js":373,"./plugins/removeViewBox.js":374,"./plugins/removeXMLNS.js":375,"./plugins/removeXMLProcInst.js":376,"./plugins/reusePaths.js":377,"./plugins/safe.js":378,"./plugins/safeAndFast.js":379,"./plugins/sortAttrs.js":380,"./plugins/sortDefsChildren.js":381}],321:[function(require,module,exports){ 'use strict'; const isTag = (node) => { @@ -30102,7 +30103,7 @@ const stringifyPathData = ({ pathData, precision, disableSpaceAfterFlags }) => { }; exports.stringifyPathData = stringifyPathData; -},{"./tools.js":383}],324:[function(require,module,exports){ +},{"./tools.js":384}],324:[function(require,module,exports){ 'use strict'; const { builtin } = require('./builtin.js'); @@ -33826,7 +33827,7 @@ const applyMatrixToPathData = (pathData, matrix) => { } }; -},{"../style.js":382,"../tools.js":383,"./_collections.js":325,"./_path.js":326,"./_transforms.js":327}],331:[function(require,module,exports){ +},{"../style.js":383,"../tools.js":384,"./_collections.js":325,"./_path.js":326,"./_transforms.js":327}],331:[function(require,module,exports){ 'use strict'; exports.name = 'cleanupAttrs'; @@ -33948,7 +33949,7 @@ exports.fn = (root) => { }; }; -},{"../xast.js":384}],333:[function(require,module,exports){ +},{"../xast.js":385}],333:[function(require,module,exports){ 'use strict'; const { visitSkip } = require('../xast.js'); @@ -34207,7 +34208,7 @@ exports.fn = (_root, params) => { }; }; -},{"../xast.js":384,"./_collections.js":325}],334:[function(require,module,exports){ +},{"../xast.js":385,"./_collections.js":325}],334:[function(require,module,exports){ 'use strict'; const { removeLeadingZero } = require('../tools.js'); @@ -34345,7 +34346,7 @@ exports.fn = (_root, params) => { }; }; -},{"../tools.js":383}],335:[function(require,module,exports){ +},{"../tools.js":384}],335:[function(require,module,exports){ 'use strict'; const { removeLeadingZero } = require('../tools.js'); @@ -34451,7 +34452,7 @@ exports.fn = (_root, params) => { }; }; -},{"../tools.js":383}],336:[function(require,module,exports){ +},{"../tools.js":384}],336:[function(require,module,exports){ 'use strict'; const { inheritableAttrs, elemsGroups } = require('./_collections.js'); @@ -35838,7 +35839,7 @@ function data2Path(params, pathData) { }, ''); } -},{"../style.js":382,"../tools.js":383,"../xast.js":384,"./_collections.js":325,"./_path.js":326,"./applyTransforms.js":330}],340:[function(require,module,exports){ +},{"../style.js":383,"../tools.js":384,"../xast.js":385,"./_collections.js":325,"./_path.js":326,"./applyTransforms.js":330}],340:[function(require,module,exports){ 'use strict'; const { stringifyPathData } = require('../path.js'); @@ -36004,7 +36005,7 @@ exports.fn = (root, params) => { }; }; -},{"../path.js":323,"../xast.js":384}],341:[function(require,module,exports){ +},{"../path.js":323,"../xast.js":385}],341:[function(require,module,exports){ 'use strict'; const { attrsGroups } = require('./_collections'); @@ -36506,7 +36507,7 @@ const smartRound = (precision, data) => { return data; }; -},{"../tools.js":383,"./_transforms.js":327}],343:[function(require,module,exports){ +},{"../tools.js":384,"./_transforms.js":327}],343:[function(require,module,exports){ 'use strict'; const { createPreset } = require('../tools.js'); @@ -36590,7 +36591,7 @@ const presetDefault = createPreset({ module.exports = presetDefault; -},{"../tools.js":383,"./cleanupAttrs.js":331,"./cleanupEnableBackground.js":332,"./cleanupIds.js":333,"./cleanupNumericValues.js":335,"./collapseGroups.js":336,"./convertColors.js":337,"./convertEllipseToCircle.js":338,"./convertPathData.js":339,"./convertShapeToPath.js":340,"./convertTransform.js":342,"./inlineStyles.js":344,"./mergePaths.js":345,"./mergeStyles.js":346,"./minifyStyles.js":347,"./moveElemsAttrsToGroup.js":348,"./moveGroupAttrsToElems.js":349,"./removeComments.js":353,"./removeDesc.js":354,"./removeDoctype.js":356,"./removeEditorsNSData.js":357,"./removeEmptyAttrs.js":359,"./removeEmptyContainers.js":360,"./removeEmptyText.js":361,"./removeHiddenElems.js":362,"./removeMetadata.js":363,"./removeNonInheritableGroupAttrs.js":364,"./removeTitle.js":369,"./removeUnknownsAndDefaults.js":370,"./removeUnusedNS.js":371,"./removeUselessDefs.js":372,"./removeUselessStrokeAndFill.js":373,"./removeViewBox.js":374,"./removeXMLProcInst.js":376,"./sortAttrs.js":379,"./sortDefsChildren.js":380}],344:[function(require,module,exports){ +},{"../tools.js":384,"./cleanupAttrs.js":331,"./cleanupEnableBackground.js":332,"./cleanupIds.js":333,"./cleanupNumericValues.js":335,"./collapseGroups.js":336,"./convertColors.js":337,"./convertEllipseToCircle.js":338,"./convertPathData.js":339,"./convertShapeToPath.js":340,"./convertTransform.js":342,"./inlineStyles.js":344,"./mergePaths.js":345,"./mergeStyles.js":346,"./minifyStyles.js":347,"./moveElemsAttrsToGroup.js":348,"./moveGroupAttrsToElems.js":349,"./removeComments.js":353,"./removeDesc.js":354,"./removeDoctype.js":356,"./removeEditorsNSData.js":357,"./removeEmptyAttrs.js":359,"./removeEmptyContainers.js":360,"./removeEmptyText.js":361,"./removeHiddenElems.js":362,"./removeMetadata.js":363,"./removeNonInheritableGroupAttrs.js":364,"./removeTitle.js":369,"./removeUnknownsAndDefaults.js":370,"./removeUnusedNS.js":371,"./removeUselessDefs.js":372,"./removeUselessStrokeAndFill.js":373,"./removeViewBox.js":374,"./removeXMLProcInst.js":376,"./sortAttrs.js":380,"./sortDefsChildren.js":381}],344:[function(require,module,exports){ 'use strict'; const csstree = require('css-tree'); @@ -36937,7 +36938,7 @@ exports.fn = (root, params) => { }; }; -},{"../xast.js":384,"css-tree":25,"csso":138}],345:[function(require,module,exports){ +},{"../xast.js":385,"css-tree":25,"csso":138}],345:[function(require,module,exports){ 'use strict'; const { detachNodeFromParent } = require('../xast.js'); @@ -37035,7 +37036,7 @@ exports.fn = (root, params) => { }; }; -},{"../style.js":382,"../xast.js":384,"./_path.js":326}],346:[function(require,module,exports){ +},{"../style.js":383,"../xast.js":385,"./_path.js":326}],346:[function(require,module,exports){ 'use strict'; const { visitSkip, detachNodeFromParent } = require('../xast.js'); @@ -37119,7 +37120,7 @@ exports.fn = () => { }; }; -},{"../xast.js":384}],347:[function(require,module,exports){ +},{"../xast.js":385}],347:[function(require,module,exports){ 'use strict'; const csso = require('csso'); @@ -37364,7 +37365,7 @@ exports.fn = (root) => { }; }; -},{"../xast.js":384,"./_collections.js":325}],349:[function(require,module,exports){ +},{"../xast.js":385,"./_collections.js":325}],349:[function(require,module,exports){ 'use strict'; const { pathElems, referencesProps } = require('./_collections.js'); @@ -37742,7 +37743,7 @@ exports.fn = (root, params) => { return {}; }; -},{"../xast.js":384}],352:[function(require,module,exports){ +},{"../xast.js":385}],352:[function(require,module,exports){ 'use strict'; exports.name = 'removeAttrs'; @@ -37924,7 +37925,7 @@ exports.fn = () => { }; }; -},{"../xast.js":384}],354:[function(require,module,exports){ +},{"../xast.js":385}],354:[function(require,module,exports){ 'use strict'; const { detachNodeFromParent } = require('../xast.js'); @@ -37963,7 +37964,7 @@ exports.fn = (root, params) => { }; }; -},{"../xast.js":384}],355:[function(require,module,exports){ +},{"../xast.js":385}],355:[function(require,module,exports){ 'use strict'; exports.name = 'removeDimensions'; @@ -38046,7 +38047,7 @@ exports.fn = () => { }; }; -},{"../xast.js":384}],357:[function(require,module,exports){ +},{"../xast.js":385}],357:[function(require,module,exports){ 'use strict'; const { detachNodeFromParent } = require('../xast.js'); @@ -38110,7 +38111,7 @@ exports.fn = (_root, params) => { }; }; -},{"../xast.js":384,"./_collections.js":325}],358:[function(require,module,exports){ +},{"../xast.js":385,"./_collections.js":325}],358:[function(require,module,exports){ 'use strict'; const { detachNodeFromParent } = require('../xast.js'); @@ -38183,7 +38184,7 @@ exports.fn = (root, params) => { }; }; -},{"../xast.js":384}],359:[function(require,module,exports){ +},{"../xast.js":385}],359:[function(require,module,exports){ 'use strict'; const { attrsGroups } = require('./_collections.js'); @@ -38270,7 +38271,7 @@ exports.fn = () => { }; }; -},{"../xast.js":384,"./_collections.js":325}],361:[function(require,module,exports){ +},{"../xast.js":385,"./_collections.js":325}],361:[function(require,module,exports){ 'use strict'; const { detachNodeFromParent } = require('../xast.js'); @@ -38321,7 +38322,7 @@ exports.fn = (root, params) => { }; }; -},{"../xast.js":384}],362:[function(require,module,exports){ +},{"../xast.js":385}],362:[function(require,module,exports){ 'use strict'; const { @@ -38631,7 +38632,7 @@ exports.fn = (root, params) => { }; }; -},{"../path.js":323,"../style.js":382,"../xast.js":384}],363:[function(require,module,exports){ +},{"../path.js":323,"../style.js":383,"../xast.js":385}],363:[function(require,module,exports){ 'use strict'; const { detachNodeFromParent } = require('../xast.js'); @@ -38658,7 +38659,7 @@ exports.fn = () => { }; }; -},{"../xast.js":384}],364:[function(require,module,exports){ +},{"../xast.js":385}],364:[function(require,module,exports){ 'use strict'; const { @@ -38815,7 +38816,7 @@ exports.fn = () => { }; }; -},{"../path.js":323,"../xast.js":384,"./_path.js":326}],366:[function(require,module,exports){ +},{"../path.js":323,"../xast.js":385,"./_path.js":326}],366:[function(require,module,exports){ 'use strict'; const { detachNodeFromParent } = require('../xast.js'); @@ -38846,7 +38847,7 @@ exports.fn = () => { }; }; -},{"../xast.js":384}],367:[function(require,module,exports){ +},{"../xast.js":385}],367:[function(require,module,exports){ 'use strict'; const { detachNodeFromParent } = require('../xast.js'); @@ -38873,7 +38874,7 @@ exports.fn = () => { }; }; -},{"../xast.js":384}],368:[function(require,module,exports){ +},{"../xast.js":385}],368:[function(require,module,exports){ 'use strict'; const { detachNodeFromParent } = require('../xast.js'); @@ -38900,7 +38901,7 @@ exports.fn = () => { }; }; -},{"../xast.js":384}],369:[function(require,module,exports){ +},{"../xast.js":385}],369:[function(require,module,exports){ 'use strict'; const { detachNodeFromParent } = require('../xast.js'); @@ -38927,7 +38928,7 @@ exports.fn = () => { }; }; -},{"../xast.js":384}],370:[function(require,module,exports){ +},{"../xast.js":385}],370:[function(require,module,exports){ 'use strict'; const { visitSkip, detachNodeFromParent } = require('../xast.js'); @@ -39135,7 +39136,7 @@ exports.fn = (root, params) => { }; }; -},{"../style.js":382,"../xast.js":384,"./_collections":325}],371:[function(require,module,exports){ +},{"../style.js":383,"../xast.js":385,"./_collections":325}],371:[function(require,module,exports){ 'use strict'; exports.name = 'removeUnusedNS'; @@ -39252,7 +39253,7 @@ const collectUsefulNodes = (node, usefulNodes) => { } }; -},{"../xast.js":384,"./_collections.js":325}],373:[function(require,module,exports){ +},{"../xast.js":385,"./_collections.js":325}],373:[function(require,module,exports){ 'use strict'; const { visit, visitSkip, detachNodeFromParent } = require('../xast.js'); @@ -39390,7 +39391,7 @@ exports.fn = (root, params) => { }; }; -},{"../style.js":382,"../xast.js":384,"./_collections.js":325}],374:[function(require,module,exports){ +},{"../style.js":383,"../xast.js":385,"./_collections.js":325}],374:[function(require,module,exports){ 'use strict'; exports.name = 'removeViewBox'; @@ -39497,7 +39498,7 @@ exports.fn = () => { }; }; -},{"../xast.js":384}],377:[function(require,module,exports){ +},{"../xast.js":385}],377:[function(require,module,exports){ 'use strict'; exports.name = 'reusePaths'; @@ -39678,7 +39679,72 @@ const presetSafe = createPreset({ module.exports = presetSafe; -},{"../tools.js":383,"./cleanupAttrs.js":331,"./cleanupEnableBackground.js":332,"./cleanupIds.js":333,"./cleanupNumericValues.js":335,"./collapseGroups.js":336,"./convertColors.js":337,"./convertEllipseToCircle.js":338,"./convertPathData.js":339,"./convertTransform.js":342,"./inlineStyles.js":344,"./mergePaths.js":345,"./mergeStyles.js":346,"./minifyStyles.js":347,"./removeComments.js":353,"./removeDesc.js":354,"./removeDoctype.js":356,"./removeEditorsNSData.js":357,"./removeEmptyAttrs.js":359,"./removeEmptyContainers.js":360,"./removeEmptyText.js":361,"./removeHiddenElems.js":362,"./removeMetadata.js":363,"./removeNonInheritableGroupAttrs.js":364,"./removeTitle.js":369,"./removeUnknownsAndDefaults.js":370,"./removeUnusedNS.js":371,"./removeUselessDefs.js":372,"./removeUselessStrokeAndFill.js":373,"./removeViewBox.js":374,"./removeXMLProcInst.js":376,"./sortDefsChildren.js":380}],379:[function(require,module,exports){ +},{"../tools.js":384,"./cleanupAttrs.js":331,"./cleanupEnableBackground.js":332,"./cleanupIds.js":333,"./cleanupNumericValues.js":335,"./collapseGroups.js":336,"./convertColors.js":337,"./convertEllipseToCircle.js":338,"./convertPathData.js":339,"./convertTransform.js":342,"./inlineStyles.js":344,"./mergePaths.js":345,"./mergeStyles.js":346,"./minifyStyles.js":347,"./removeComments.js":353,"./removeDesc.js":354,"./removeDoctype.js":356,"./removeEditorsNSData.js":357,"./removeEmptyAttrs.js":359,"./removeEmptyContainers.js":360,"./removeEmptyText.js":361,"./removeHiddenElems.js":362,"./removeMetadata.js":363,"./removeNonInheritableGroupAttrs.js":364,"./removeTitle.js":369,"./removeUnknownsAndDefaults.js":370,"./removeUnusedNS.js":371,"./removeUselessDefs.js":372,"./removeUselessStrokeAndFill.js":373,"./removeViewBox.js":374,"./removeXMLProcInst.js":376,"./sortDefsChildren.js":381}],379:[function(require,module,exports){ +'use strict'; + +const { createPreset } = require('../tools.js'); + +const removeDoctype = require('./removeDoctype.js'); +const removeXMLProcInst = require('./removeXMLProcInst.js'); +const removeComments = require('./removeComments.js'); +const removeMetadata = require('./removeMetadata.js'); +const removeEditorsNSData = require('./removeEditorsNSData.js'); +const cleanupAttrs = require('./cleanupAttrs.js'); +const mergeStyles = require('./mergeStyles.js'); +const minifyStyles = require('./minifyStyles.js'); +const cleanupIds = require('./cleanupIds.js'); +const removeUselessDefs = require('./removeUselessDefs.js'); +const cleanupNumericValues = require('./cleanupNumericValues.js'); +const convertColors = require('./convertColors.js'); +const removeUnknownsAndDefaults = require('./removeUnknownsAndDefaults.js'); +const removeNonInheritableGroupAttrs = require('./removeNonInheritableGroupAttrs.js'); +const removeUselessStrokeAndFill = require('./removeUselessStrokeAndFill.js'); +const removeViewBox = require('./removeViewBox.js'); +const cleanupEnableBackground = require('./cleanupEnableBackground.js'); +const removeHiddenElems = require('./removeHiddenElems.js'); +const removeEmptyText = require('./removeEmptyText.js'); +const collapseGroups = require('./collapseGroups.js'); +const removeEmptyAttrs = require('./removeEmptyAttrs.js'); +const removeEmptyContainers = require('./removeEmptyContainers.js'); +const mergePaths = require('./mergePaths.js'); +const removeUnusedNS = require('./removeUnusedNS.js'); +const sortDefsChildren = require('./sortDefsChildren.js'); +const removeTitle = require('./removeTitle.js'); +const removeDesc = require('./removeDesc.js'); + +const presetSafe = createPreset({ + name: 'safeAndFastPreset', + plugins: [ + removeDoctype, + removeXMLProcInst, + removeComments, + removeMetadata, + removeEditorsNSData, + cleanupAttrs, + mergeStyles, + cleanupIds, + removeUselessDefs, + cleanupNumericValues, + convertColors, + removeUnknownsAndDefaults, + removeNonInheritableGroupAttrs, + removeUselessStrokeAndFill, + removeViewBox, + cleanupEnableBackground, + removeHiddenElems, + removeEmptyText, + collapseGroups, + removeEmptyAttrs, + removeEmptyContainers, + removeUnusedNS, + removeTitle, + removeDesc, + ], +}); + +module.exports = presetSafe; + +},{"../tools.js":384,"./cleanupAttrs.js":331,"./cleanupEnableBackground.js":332,"./cleanupIds.js":333,"./cleanupNumericValues.js":335,"./collapseGroups.js":336,"./convertColors.js":337,"./mergePaths.js":345,"./mergeStyles.js":346,"./minifyStyles.js":347,"./removeComments.js":353,"./removeDesc.js":354,"./removeDoctype.js":356,"./removeEditorsNSData.js":357,"./removeEmptyAttrs.js":359,"./removeEmptyContainers.js":360,"./removeEmptyText.js":361,"./removeHiddenElems.js":362,"./removeMetadata.js":363,"./removeNonInheritableGroupAttrs.js":364,"./removeTitle.js":369,"./removeUnknownsAndDefaults.js":370,"./removeUnusedNS.js":371,"./removeUselessDefs.js":372,"./removeUselessStrokeAndFill.js":373,"./removeViewBox.js":374,"./removeXMLProcInst.js":376,"./sortDefsChildren.js":381}],380:[function(require,module,exports){ 'use strict'; exports.name = 'sortAttrs'; @@ -39778,7 +39844,7 @@ exports.fn = (_root, params) => { }; }; -},{}],380:[function(require,module,exports){ +},{}],381:[function(require,module,exports){ 'use strict'; exports.name = 'sortDefsChildren'; @@ -39836,7 +39902,7 @@ exports.fn = () => { }; }; -},{}],381:[function(require,module,exports){ +},{}],382:[function(require,module,exports){ 'use strict'; const { textElems } = require('./plugins/_collections.js'); @@ -40070,7 +40136,7 @@ const stringifyText = (node, config, state) => { ); }; -},{"./plugins/_collections.js":325}],382:[function(require,module,exports){ +},{"./plugins/_collections.js":325}],383:[function(require,module,exports){ 'use strict'; const csstree = require('css-tree'); @@ -40300,7 +40366,7 @@ const computeStyle = (stylesheet, node) => { }; exports.computeStyle = computeStyle; -},{"./plugins/_collections.js":325,"./xast.js":384,"css-tree":25,"csso":138}],383:[function(require,module,exports){ +},{"./plugins/_collections.js":325,"./xast.js":385,"css-tree":25,"csso":138}],384:[function(require,module,exports){ (function (Buffer){(function (){ 'use strict'; @@ -40455,7 +40521,7 @@ exports.invokePlugins = invokePlugins; exports.createPreset = createPreset; }).call(this)}).call(this,require("buffer").Buffer) -},{"./xast.js":384,"buffer":4}],384:[function(require,module,exports){ +},{"./xast.js":385,"buffer":4}],385:[function(require,module,exports){ 'use strict'; const { selectAll, selectOne, is } = require('css-select'); diff --git a/common/src/app/common/types/container.cljc b/common/src/app/common/types/container.cljc index a7f060391e..3d5cb31c43 100644 --- a/common/src/app/common/types/container.cljc +++ b/common/src/app/common/types/container.cljc @@ -255,7 +255,11 @@ (dissoc :component-root))) [new-root-shape new-shapes updated-shapes] - (ctst/clone-object shape nil objects update-new-shape update-original-shape) + (ctst/clone-shape shape + nil + objects + :update-new-shape update-new-shape + :update-original-shape update-original-shape) ;; If frame-id points to a shape inside the component, remap it to the ;; corresponding new frame shape. If not, set it to nil. @@ -339,15 +343,14 @@ (dissoc :component-root)))) [new-shape new-shapes _] - (ctst/clone-object component-shape - frame-id - (if components-v2 (:objects component-page) (:objects component)) - update-new-shape - (fn [object _] object) - force-id - keep-ids? - frame-id) - + (ctst/clone-shape component-shape + frame-id + (if components-v2 (:objects component-page) (:objects component)) + :update-new-shape update-new-shape + :force-id force-id + :keep-ids? keep-ids? + :frame-id frame-id + :dest-objects (:objects container)) ;; Fix empty parent-id and remap all grid cells to the new ids. remap-ids diff --git a/common/src/app/common/types/shape/layout.cljc b/common/src/app/common/types/shape/layout.cljc index 30a866b76a..745cd941e4 100644 --- a/common/src/app/common/types/shape/layout.cljc +++ b/common/src/app/common/types/shape/layout.cljc @@ -327,6 +327,20 @@ [pad-top pad-right pad-top pad-right] [pad-top pad-right pad-bottom pad-left]))) +(defn h-padding + [{:keys [layout-padding-type layout-padding]}] + (let [{pad-right :p2 pad-left :p4} layout-padding] + (if (= :simple layout-padding-type) + (+ pad-right pad-right) + (+ pad-right pad-left)))) + +(defn v-padding + [{:keys [layout-padding-type layout-padding]}] + (let [{pad-top :p1 pad-bottom :p3} layout-padding] + (if (= :simple layout-padding-type) + (+ pad-top pad-top) + (+ pad-top pad-bottom)))) + (defn child-min-width [child] (if (and (fill-width? child) @@ -590,7 +604,8 @@ (declare assign-cells) (def default-track-value - {:type :auto}) + {:type :flex + :value 1}) (def grid-cell-defaults {:row-span 1 @@ -600,53 +615,225 @@ :justify-self :auto :shapes []}) +(declare resize-cell-area) +(declare cells-by-column) +(declare cells-by-row) + +(defn remove-cell-areas + "Remove the areas in the given `index` before and after the index" + [parent prop index] + (let [prop-span (if (= prop :column) :column-span :row-span) + cells (if (= prop :column) (cells-by-column parent index) (cells-by-row parent index))] + (->> cells + (filter #(> (get % prop-span) 1)) + (reduce + (fn [parent cell] + (let [area? (= :area (:position cell)) + changed-cells + (cond + ;; New track at the beginning + (= (get cell prop) (inc index)) + [(assoc cell prop-span 1) + (-> cell + (assoc :id (uuid/next) :shapes [] prop (inc (get cell prop)) prop-span (dec (get cell prop-span))) + (dissoc :area-name) + (cond-> area? (assoc :position :manual)))] + + ;; New track at the middle + (< (get cell prop) (inc index) (+ (get cell prop) (dec (get cell prop-span)))) + [(assoc cell prop-span (- (inc index) (get cell prop))) + (-> cell + (assoc :id (uuid/next) :shapes [] prop (inc index) prop-span 1) + (dissoc :area-name) + (cond-> area? (assoc :position :manual))) + (-> cell + (assoc :id (uuid/next) :shapes [] prop (+ index 2) prop-span (- (+ (get cell prop) (dec (get cell prop-span))) (inc index))) + (dissoc :area-name) + (cond-> area? (assoc :position :manual)))] + + ;; New track at the end + (= (+ (get cell prop) (dec (get cell prop-span))) (inc index)) + [(assoc cell prop-span (- (inc index) (get cell prop))) + (-> cell + (assoc :id (uuid/next) :shapes [] prop (inc index) prop-span 1) + (dissoc :area-name) + (cond-> area? (assoc :position :manual)))])] + + (->> changed-cells + (reduce #(update %1 :layout-grid-cells assoc (:id %2) %2) parent)))) + parent)))) + +(defn remove-cell-areas-after + "Remove the areas in the given `index` but only after the index." + [parent prop index] + (let [prop-span (if (= prop :column) :column-span :row-span) + cells (if (= prop :column) (cells-by-column parent index) (cells-by-row parent index))] + (->> cells + (filter #(> (get % prop-span) 1)) + (reduce + (fn [parent cell] + (let [area? (= :area (:position cell)) + changed-cells + (cond + ;; New track at the beginning + (= (get cell prop) (inc index)) + [(assoc cell prop-span 1) + (-> cell + (assoc :id (uuid/next) :shapes [] prop (inc (get cell prop)) prop-span (dec (get cell prop-span))) + (dissoc :area-name) + (cond-> area? (assoc :position :manual)))] + + ;; New track at the middle + (< (get cell prop) (inc index) (+ (get cell prop) (dec (get cell prop-span)))) + [(assoc cell prop-span (- (+ index 2) (get cell prop))) + (-> cell + (assoc :id (uuid/next) :shapes [] prop (+ index 2) prop-span (- (+ (get cell prop) (dec (get cell prop-span))) (inc index))) + (dissoc :area-name) + (cond-> area? (assoc :position :manual)))])] + (->> changed-cells + (reduce #(update %1 :layout-grid-cells assoc (:id %2) %2) parent)))) + parent)))) + ;; Adding a track creates the cells. We should check the shapes that are not tracked (with default values) and assign to the correct tracked values + +(defn add-grid-track + ([type parent value] + (add-grid-track type parent value nil)) + ([type parent value index] + (dm/assert! + "expected a valid grid definition for `value`" + (check-grid-track! value)) + + (let [[tracks-prop tracks-prop-other prop prop-other prop-span prop-span-other] + (if (= type :column) + [:layout-grid-columns :layout-grid-rows :column :row :column-span :row-span] + [:layout-grid-rows :layout-grid-columns :row :column :row-span :column-span]) + + new-index (d/nilv index (count (get parent tracks-prop))) + new-track-num (inc new-index) + + ;; Increase the values for the existing cells + layout-grid-cells + (-> (:layout-grid-cells parent) + (update-vals + (fn [cell] + (cond-> cell + (>= (get cell prop) new-track-num) + (update prop inc) + + (and (< (get cell prop) new-track-num) + (> (get cell prop-span) 1) + (>= (+ (get cell prop) (dec (get cell prop-span))) new-track-num)) + (update prop-span inc))))) + + ;; Search for the cells already created + exist-cells? + (into #{} + (comp (filter + (fn [cell] + (and (>= new-track-num (get cell prop)) + (< new-track-num (+ (get cell prop) (get cell prop-span)))))) + (mapcat #(range (get % prop-other) (+ (get % prop-other) (get % prop-span-other))))) + (vals layout-grid-cells)) + + ;; Create the new cells as necesary + layout-grid-cells + (->> (d/enumerate (get parent tracks-prop-other)) + (remove (fn [[idx _]] (exist-cells? (inc idx)))) + (reduce + (fn [result [idx _]] + (let [id (uuid/next)] + (assoc result id + (merge {:id id + prop-other (inc idx) + prop new-track-num} + grid-cell-defaults)))) + layout-grid-cells))] + + (-> parent + (update tracks-prop d/add-at-index new-index value) + (assoc :layout-grid-cells layout-grid-cells))))) + (defn add-grid-column - [parent value] - (dm/assert! - "expected a valid grid definition for `value`" - (check-grid-track! value)) - - (let [rows (:layout-grid-rows parent) - new-col-num (inc (count (:layout-grid-columns parent))) - - layout-grid-cells - (->> (d/enumerate rows) - (reduce (fn [result [row-idx _]] - (let [id (uuid/next)] - (assoc result id - (merge {:id id - :row (inc row-idx) - :column new-col-num} - grid-cell-defaults)))) - (:layout-grid-cells parent)))] - (-> parent - (update :layout-grid-columns (fnil conj []) value) - (assoc :layout-grid-cells layout-grid-cells)))) + ([parent value] + (add-grid-column parent value nil)) + ([parent value index] + (add-grid-track :column parent value index))) (defn add-grid-row - [parent value] - (dm/assert! - "expected a valid grid definition for `value`" - (check-grid-track! value)) + ([parent value] + (add-grid-row parent value nil)) + ([parent value index] + (add-grid-track :row parent value index))) - (let [cols (:layout-grid-columns parent) - new-row-num (inc (count (:layout-grid-rows parent))) +(defn- duplicate-cells + [shape prop from-index to-index ids-map] - layout-grid-cells - (->> (d/enumerate cols) - (reduce (fn [result [col-idx _]] - (let [id (uuid/next)] - (assoc result id - (merge {:id id - :column (inc col-idx) - :row new-row-num} - grid-cell-defaults)))) - (:layout-grid-cells parent)))] - (-> parent - (update :layout-grid-rows (fnil conj []) value) - (assoc :layout-grid-cells layout-grid-cells)))) + (let [[prop-span prop-other prop-other-span] + (if (= prop :column) + [:column-span :row :row-span] + [:row-span :column :column-span]) + from-cells + (if (= prop :column) + (cells-by-column shape from-index) + (cells-by-row shape from-index)) + + to-cells + (if (= prop :column) + (cells-by-column shape to-index) + (cells-by-row shape to-index)) + + to-cells-idx (d/index-by prop-other to-cells) + + ;; This loop will go throught the original cells and copy their data to the target cell + ;; After this some cells could have no correspondence and should be removed + [shape matched] + (loop [from-cells (seq from-cells) + matched #{} + result shape] + (if-let [cell (first from-cells)] + (let [match-cell + (-> (get to-cells-idx (get cell prop-other)) + (d/patch-object (select-keys cell [prop-other-span :position :align-self :justify-self])) + (cond-> (= :area (:position cell)) + (assoc :position :manual)) + (cond-> (= (get cell prop-span) 1) + (assoc :shapes (mapv ids-map (:shapes cell)))))] + (recur (rest from-cells) + (conj matched (:id match-cell)) + (assoc-in result [:layout-grid-cells (:id match-cell)] match-cell))) + + [result matched])) + + ;; Remove cells that haven't been matched + shape + (->> to-cells + (remove (fn [{:keys [id]}] (contains? matched id))) + (reduce (fn [shape cell] + (update shape :layout-grid-cells dissoc (:id cell))) + shape))] + + shape)) + + +(defn duplicate-row + [shape objects index ids-map] + (let [value (dm/get-in shape [:layout-grid-rows index])] + (-> shape + (remove-cell-areas-after :row index) + (add-grid-row value (inc index)) + (duplicate-cells :row index (inc index) ids-map) + (assign-cells objects)))) + +(defn duplicate-column + [shape objects index ids-map] + (let [value (dm/get-in shape [:layout-grid-columns index])] + (-> shape + (remove-cell-areas-after :column index) + (add-grid-column value (inc index)) + (duplicate-cells :column index (inc index) ids-map) + (assign-cells objects)))) (defn make-remove-cell [attr span-attr track-num] @@ -747,12 +934,9 @@ update-vals (fn [cell] (update cell prop #(get remap-tracks % %))))))) -(declare resize-cell-area) -(declare cells-by-column) -(declare cells-by-row) (defn- reorder-grid-track - [parent from-index to-index move-content? cells-by tracks-props prop prop-span] + [parent from-index to-index move-content? tracks-props prop] (let [from-track (inc from-index) to-track (if (< to-index from-index) (+ to-index 2) @@ -761,23 +945,10 @@ (and move-content? (not= from-track to-track)) parent - (if move-content? - (->> (concat (cells-by parent (dec from-track)) - (cells-by parent (dec to-track))) - (reduce (fn [parent cell] - (cond-> parent - (and (> (get cell prop-span) 1) - (or (> to-track from-track) (not (= to-track (get cell prop)))) - (or (< to-track from-track) (not (= to-track (+ (get cell prop) (dec (get cell prop-span))))))) - (resize-cell-area - (:row cell) - (:column cell) - (:row cell) - (:column cell) - (if (= prop :row) 1 (:row-span cell)) - (if (= prop :column) 1 (:column-span cell))))) - parent)) - parent) + (cond-> parent + move-content? + (-> (remove-cell-areas prop from-index) + (remove-cell-areas-after prop to-index))) parent (reorder-grid-tracks parent tracks-props from-index to-index)] @@ -788,11 +959,11 @@ (defn reorder-grid-column [parent from-index to-index move-content?] - (reorder-grid-track parent from-index to-index move-content? cells-by-column :layout-grid-columns :column :column-span)) + (reorder-grid-track parent from-index to-index move-content? :layout-grid-columns :column)) (defn reorder-grid-row [parent from-index to-index move-content?] - (reorder-grid-track parent from-index to-index move-content? cells-by-row :layout-grid-rows :row :row-span)) + (reorder-grid-track parent from-index to-index move-content? :layout-grid-rows :row)) (defn cells-seq [{:keys [layout-grid-cells layout-grid-dir]} & {:keys [sort?] :or {sort? false}}] @@ -877,6 +1048,41 @@ parent overlaps)) +(defn reassign-positions + "Propagate the manual positioning to the following cells" + [parent] + (->> (cells-seq parent :sort? true) + (reduce + (fn [[parent auto?] cell] + + (let [[cell auto?] + (cond + (and (empty? (:shapes cell)) + (= :manual (:position cell)) + (= (:row-span cell) 1) + (= (:column-span cell) 1)) + [(assoc cell :position :auto) false] + + (and (or (not= (:row-span cell) 1) + (not= (:column-span cell) 1)) + (= :auto (:position cell))) + [(assoc cell :position :manual) false] + + (empty? (:shapes cell)) + [cell false] + + (and (not auto?) (= :auto (:position cell))) + [(assoc cell :position :manual) false] + + (= :manual (:position cell)) + [cell false] + + :else + [cell auto?])] + [(assoc-in parent [:layout-grid-cells (:id cell)] cell) auto?])) + [parent true]) + (first))) + (defn position-auto-shapes [parent] ;; Iterate through the cells. While auto and contains shape no changes. @@ -903,6 +1109,14 @@ (rest shapes)))))] parent)) +(defn assign-cell-positions + [parent objects] + (prn ">>>>assign-cell-positions" (:name parent)) + (-> parent + (check-deassigned-cells objects) + (reassign-positions) + (position-auto-shapes))) + ;; Assign cells takes the children and move them into the allotted cells. If there are not enough cells it creates ;; not-tracked rows/columns and put the shapes there ;; Non-tracked tracks need to be deleted when they are empty and there are no more shapes unallocated @@ -914,13 +1128,10 @@ ;; - (maybe) create group/frames. This case will assigna a cell that had one of its children (defn assign-cells [parent objects] - (let [;; TODO: Remove this, shouldn't be happening - ;;overlaps (overlapping-cells parent) - ;;_ (when (not (empty? overlaps)) - ;; (.warn js/console "OVERLAPS" overlaps)) - parent (cond-> (check-deassigned-cells parent objects) - #_(d/not-empty? overlaps) - #_(fix-overlaps overlaps)) + (prn ">assign-cells") + (let [ + + parent (assign-cell-positions parent objects) shape-has-cell? (into #{} (mapcat (comp :shapes second)) (:layout-grid-cells parent)) @@ -928,9 +1139,7 @@ no-cell-shapes (->> (:shapes parent) (remove shape-has-cell?) - (remove (partial position-absolute? objects))) - - parent (position-auto-shapes parent)] + (remove (partial position-absolute? objects)))] (if (empty? no-cell-shapes) ;; All shapes are within a cell. No need to assign @@ -1080,6 +1289,7 @@ target-cell (-> prev-cell (assoc + :position :manual :row new-row :column new-column :row-span new-row-span @@ -1224,30 +1434,64 @@ (assoc parent :shapes (into [] (reverse new-shapes))))) (defn cells-by-row - [parent index] - (->> (:layout-grid-cells parent) - (filter (fn [[_ {:keys [row row-span]}]] - (and (>= (inc index) row) - (< (inc index) (+ row row-span))))) - (map second))) + ([parent index] + (cells-by-row parent index true)) + ([parent index check-span?] + (->> (:layout-grid-cells parent) + (vals) + (filter + (fn [{:keys [row row-span]}] + (if check-span? + (and (>= (inc index) row) + (< (inc index) (+ row row-span))) + (= (inc index) row))))))) (defn cells-by-column - [parent index] + ([parent index] + (cells-by-column parent index true)) + ([parent index check-span?] + (->> (:layout-grid-cells parent) + (vals) + (filter + (fn [{:keys [column column-span] :as cell}] + (if check-span? + (and (>= (inc index) column) + (< (inc index) (+ column column-span))) + (= (inc index) column))))))) + +(defn cells-in-area + [parent first-row last-row first-column last-column] (->> (:layout-grid-cells parent) - (filter (fn [[_ {:keys [column column-span]}]] - (and (>= (inc index) column) - (< (inc index) (+ column column-span))))) - (map second))) + (vals) + (filter + (fn [{:keys [row column row-span column-span] :as cell}] + (and + (or (<= row first-row (+ row row-span -1)) + (<= row last-row (+ row row-span -1)) + (<= first-row row last-row) + (<= first-row (+ row row-span -1) last-row)) + + (or (<= column first-column (+ column column-span -1)) + (<= column last-column (+ column column-span -1)) + (<= first-column column last-column) + (<= first-column (+ column column-span -1) last-column))))))) (defn shapes-by-row - [parent index] - (->> (cells-by-row parent index) - (mapcat :shapes))) + "Find all the shapes for a given row" + ([parent index] + (shapes-by-row parent index true)) + ;; check-span? if false will only see if there is a coincidence in file&row + ([parent index check-span?] + (->> (cells-by-row parent index check-span?) + (mapcat :shapes)))) (defn shapes-by-column - [parent index] - (->> (cells-by-column parent index) - (mapcat :shapes))) + "Find all the shapes for a given column" + ([parent index] + (shapes-by-column parent index true)) + ([parent index check-span?] + (->> (cells-by-column parent index check-span?) + (mapcat :shapes)))) (defn cells-coordinates "Given a group of cells returns the coordinates that define" @@ -1300,7 +1544,6 @@ (defn valid-area-cells? [cells] - (let [{:keys [first-row last-row first-column last-column cell-coords]} (cells-coordinates cells)] (every? #(contains? cell-coords %) @@ -1309,7 +1552,7 @@ [r c])))) (defn remap-grid-cells - "Remaps the shapes inside the cells" + "Remaps the shapes ids inside the cells" [shape ids-map] (let [do-remap-cells (fn [cell] diff --git a/common/src/app/common/types/shape_tree.cljc b/common/src/app/common/types/shape_tree.cljc index 928a5ed664..ab6f08e41b 100644 --- a/common/src/app/common/types/shape_tree.cljc +++ b/common/src/app/common/types/shape_tree.cljc @@ -342,91 +342,89 @@ [frame] (not (mth/almost-zero? (:rotation frame 0)))) -(defn clone-object - "Gets a copy of the object and all its children, with new ids and with +(defn clone-shape + "Gets a copy of the shape and all its children, with new ids and with the parent-children links correctly set. Admits functions to make - more transformations to the cloned objects and the original ones. + more transformations to the cloned shapes and the original ones. - Returns the cloned object, the list of all new objects (including - the cloned one), and possibly a list of original objects modified. + Returns the cloned shape, the list of all new shapes (including + the cloned one), and possibly a list of original shapes modified. - The list of objects are returned in tree traversal order, respecting + The list of shapes are returned in tree traversal order, respecting the order of the children of each parent." - - ([object parent-id objects] - (clone-object object parent-id objects (fn [object _] object) (fn [object _] object) nil false nil)) - - ([object parent-id objects update-new-object] - (clone-object object parent-id objects update-new-object (fn [object _] object) nil false nil)) - - ([object parent-id objects update-new-object update-original-object] - (clone-object object parent-id objects update-new-object update-original-object nil false nil)) - - ([object parent-id objects update-new-object update-original-object force-id] - (clone-object object parent-id objects update-new-object update-original-object force-id false nil)) - - ([object parent-id objects update-new-object update-original-object force-id keep-ids?] - (clone-object object parent-id objects update-new-object update-original-object force-id keep-ids? nil)) - - ([object parent-id objects update-new-object update-original-object force-id keep-ids? frame-id] - (let [new-id (cond - (some? force-id) force-id - keep-ids? (:id object) - :else (uuid/next)) + [shape parent-id objects & {:keys [update-new-shape update-original-shape force-id keep-ids? frame-id dest-objects] + :or {update-new-shape (fn [shape _] shape) + update-original-shape (fn [shape _] shape) + force-id nil + keep-ids? false + frame-id nil + dest-objects objects}}] + (let [new-id (cond + (some? force-id) force-id + keep-ids? (:id shape) + :else (uuid/next)) ;; Assign the correct frame-id for the given parent. It's the parent-id (if parent is frame) ;; or the parent's frame-id otherwise. Only for the first cloned shapes. In recursive calls ;; this is not needed. - frame-id (cond - (and (nil? frame-id) (cfh/frame-shape? objects parent-id)) - parent-id + frame-id (cond + (and (nil? frame-id) (cfh/frame-shape? dest-objects parent-id)) + parent-id - (nil? frame-id) - (dm/get-in objects [parent-id :frame-id]) + (nil? frame-id) + (dm/get-in dest-objects [parent-id :frame-id] uuid/zero) - :else - frame-id)] + :else + frame-id)] - (loop [child-ids (seq (:shapes object)) - new-direct-children [] - new-children [] - updated-children []] + (loop [child-ids (seq (:shapes shape)) + new-direct-children [] + new-children [] + updated-children []] - (if (empty? child-ids) - (let [new-object (cond-> object - :always - (assoc :id new-id - :parent-id parent-id - :frame-id frame-id) + (if (empty? child-ids) + (let [new-shape (cond-> shape + :always + (assoc :id new-id + :parent-id parent-id + :frame-id frame-id) - (some? (:shapes object)) - (assoc :shapes (mapv :id new-direct-children))) + (some? (:shapes shape)) + (assoc :shapes (mapv :id new-direct-children))) - new-object (update-new-object new-object object) - new-objects (into [new-object] new-children) + new-shape (update-new-shape new-shape shape) + new-shapes (into [new-shape] new-children) - updated-object (update-original-object object new-object) - updated-objects (if (identical? object updated-object) - updated-children - (into [updated-object] updated-children))] + updated-shape (update-original-shape shape new-shape) + updated-shapes (if (identical? shape updated-shape) + updated-children + (into [updated-shape] updated-children))] - [new-object new-objects updated-objects]) + [new-shape new-shapes updated-shapes]) - (let [child-id (first child-ids) - child (get objects child-id) - _ (dm/assert! (some? child)) - frame-id-child (if (cfh/frame-shape? object) - new-id - frame-id) + (let [child-id (first child-ids) + child (get objects child-id) + _ (dm/assert! (some? child)) + frame-id-child (if (cfh/frame-shape? shape) + new-id + frame-id) - [new-child new-child-objects updated-child-objects] - (clone-object child new-id objects update-new-object update-original-object nil keep-ids? frame-id-child)] + [new-child new-child-shapes updated-child-shapes] + (clone-shape child + new-id + objects + :update-new-shape update-new-shape + :update-original-shape update-original-shape + :force-id nil + :keep-ids? keep-ids? + :frame-id frame-id-child + :dest-objects dest-objects)] - (recur - (next child-ids) - (into new-direct-children [new-child]) - (into new-children new-child-objects) - (into updated-children updated-child-objects)))))))) + (recur + (next child-ids) + (into new-direct-children [new-child]) + (into new-children new-child-shapes) + (into updated-children updated-child-shapes))))))) (defn generate-shape-grid "Generate a sequence of positions that lays out the list of diff --git a/frontend/gulpfile.js b/frontend/gulpfile.js index eee619276b..ce92a04e28 100644 --- a/frontend/gulpfile.js +++ b/frontend/gulpfile.js @@ -1,6 +1,6 @@ import fs from "fs"; import l from "lodash"; -import path from "path" +import path from "path"; import gulp from "gulp"; import gulpConcat from "gulp-concat"; @@ -9,8 +9,8 @@ import gulpMustache from "gulp-mustache"; import gulpPostcss from "gulp-postcss"; import gulpRename from "gulp-rename"; -import * as sass from 'sass'; -import gsass from 'gulp-sass'; +import * as sass from "sass"; +import gsass from "gulp-sass"; const gulpSass = gsass(sass); import svgSprite from "gulp-svg-sprite"; @@ -204,6 +204,7 @@ function templatePipeline(options) { manifest: manifest, translations: JSON.stringify(locales), themes: JSON.stringify(themes), + isDebug: process.env.NODE_ENV !== "production", }); return gulp.src(input).pipe(tmpl).pipe(gulpRename(name)).pipe(gulp.dest(output)).pipe(touch()); @@ -231,16 +232,16 @@ gulp.task("scss:modules", function () { modules({ getJSON: function (cssFileName, json, outputFileName) { // We do nothing because we don't want the generated JSON files - }, + }, // Calculates the whole css-module selector name. // Should be the same as the one in the file `/src/app/main/style.clj` generateScopedName: function (selector, filename, css) { const dir = path.dirname(filename); const name = path.basename(filename, ".css"); const parts = dir.split("/"); - const rootIdx = parts.findIndex(s => s === ROOT_NAME); + const rootIdx = parts.findIndex((s) => s === ROOT_NAME); return parts.slice(rootIdx + 1).join("_") + "_" + name + "__" + selector; - }, + }, }), autoprefixer(), ]), @@ -249,13 +250,15 @@ gulp.task("scss:modules", function () { }); gulp.task("scss:main", function () { + const sources = [`${paths.resources}styles/main-default.scss`, `${paths.resources}styles/debug.scss`]; + return gulp - .src(paths.resources + "styles/main-default.scss") - .pipe(gulpSass.sync({ - includePaths: [ - "./node_modules/animate.css" - ] - })) + .src(sources) + .pipe( + gulpSass.sync({ + includePaths: ["./node_modules/animate.css"], + }), + ) .pipe(gulpPostcss([autoprefixer])) .pipe(gulp.dest(paths.output + "css/")); }); diff --git a/frontend/resources/styles/common/base.scss b/frontend/resources/styles/common/base.scss index 6654083ac6..fd1e4ecb0f 100644 --- a/frontend/resources/styles/common/base.scss +++ b/frontend/resources/styles/common/base.scss @@ -17,9 +17,6 @@ body { width: 100vw; height: 100vh; overflow: hidden; - - background-color: red; //debugger colors - color: yellow; //debugger colors } #app { diff --git a/frontend/resources/styles/common/refactor/basic-rules.scss b/frontend/resources/styles/common/refactor/basic-rules.scss index 5ca389803e..25d70b6f66 100644 --- a/frontend/resources/styles/common/refactor/basic-rules.scss +++ b/frontend/resources/styles/common/refactor/basic-rules.scss @@ -216,6 +216,7 @@ .button-disabled { @include buttonStyle; @include flexCenter; + height: $s-32; background-color: var(--button-background-color-disabled); border: $s-1 solid var(--button-border-color-disabled); color: var(--button-foreground-color-disabled); @@ -738,7 +739,6 @@ @include titleTipography; color: var(--color-foreground-primary); text-align: left; - display: grid; grid-template-columns: 1fr 22px; grid-template-areas: "name button"; diff --git a/frontend/resources/styles/common/refactor/common-refactor.scss b/frontend/resources/styles/common/refactor/common-refactor.scss index a200893e95..c1033735c8 100644 --- a/frontend/resources/styles/common/refactor/common-refactor.scss +++ b/frontend/resources/styles/common/refactor/common-refactor.scss @@ -34,7 +34,3 @@ $da-primary: var(--color-accent-primary); $da-primary-muted: var(--color-accent-primary-muted); $da-secondary: var(--color-accent-secondary); $da-tertiary: var(--color-accent-tertiary); - -#app { - background-color: var(--app-background); -} diff --git a/frontend/resources/styles/common/refactor/design-tokens.scss b/frontend/resources/styles/common/refactor/design-tokens.scss index a69026ba08..c9717d2e24 100644 --- a/frontend/resources/styles/common/refactor/design-tokens.scss +++ b/frontend/resources/styles/common/refactor/design-tokens.scss @@ -305,6 +305,11 @@ --resize-area-background-color: var(--color-background-primary); --resize-area-border-color: var(--color-background-quaternary); + --flow-tag-background-color: var(--color-background-tertiary); + --flow-tag-foreground-color: var(--color-foreground-secondary); + --flow-tag-background-color-hover: var(--color-accent-primary); + --flow-tag-foreground-color-hover: var(--color-background-primary); + // VIEWER --viewer-background-color: var(--color-background-secondary); --viewer-paginator-background-color: var(--color-background-tertiary); @@ -314,3 +319,7 @@ --viewer-thumbnail-border-color: var(--color-accent-primary); --viewer-thumbnail-background-color-selected: var(--color-accent-primary-muted); } + +#app { + background-color: var(--app-background); +} diff --git a/frontend/resources/styles/debug.scss b/frontend/resources/styles/debug.scss new file mode 100644 index 0000000000..dda48e1fc0 --- /dev/null +++ b/frontend/resources/styles/debug.scss @@ -0,0 +1,15 @@ +// 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 + +// NOTE: This CSS only gets included when the NODE_ENV env var +// is *not* set to `production`. +// It is useful to have some styles that are useful in local dev, like +// debugging. + +body { + background-color: red; + color: yellow; +} diff --git a/frontend/resources/styles/main-default.scss b/frontend/resources/styles/main-default.scss index a961862db5..8690f68d52 100644 --- a/frontend/resources/styles/main-default.scss +++ b/frontend/resources/styles/main-default.scss @@ -44,7 +44,6 @@ //################################################# @import "common/framework"; -@import "main/partials/modal"; @import "main/partials/forms"; @import "main/partials/texts"; @import "main/partials/context-menu"; @@ -58,31 +57,16 @@ @import "main/partials/viewer-header"; @import "main/partials/viewer-thumbnails"; @import "main/partials/activity-bar"; -@import "main/partials/colorpicker"; -@import "main/partials/dashboard"; -@import "main/partials/dashboard-header"; -@import "main/partials/dashboard-grid"; -@import "main/partials/dashboard-sidebar"; -@import "main/partials/dashboard-team"; -@import "main/partials/dashboard-settings"; -@import "main/partials/dashboard-fonts"; @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/sidebar-align-options"; -@import "main/partials/sidebar-assets"; -@import "main/partials/sidebar-document-history"; -@import "main/partials/sidebar-element-options"; -@import "main/partials/sidebar-interactions"; @import "main/partials/tab-container"; @import "main/partials/tool-bar"; @import "main/partials/user-settings"; @import "main/partials/workspace"; -@import "main/partials/comments"; @import "main/partials/color-bullet"; @import "main/partials/inspect"; @import "main/partials/exception-page"; -@import "main/partials/share-link"; @import "main/partials/signup-questions"; diff --git a/frontend/resources/styles/main/partials/colorpicker.scss b/frontend/resources/styles/main/partials/colorpicker.scss deleted file mode 100644 index e70546d659..0000000000 --- a/frontend/resources/styles/main/partials/colorpicker.scss +++ /dev/null @@ -1,598 +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 - -.colorpicker { - box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); - background-color: $color-white; -} - -.colorpicker-content { - display: flex; - flex-direction: column; - padding: $size-2; - - & > * { - width: 200px; - } - - .top-actions { - display: flex; - margin-bottom: $size-1; - flex-direction: row-reverse; - justify-content: space-between; - - .picker-btn { - background: none; - border: none; - cursor: pointer; - - &.active svg, - &:hover svg { - fill: $color-primary; - } - - svg { - width: 14px; - height: 14px; - } - } - - .element-set-content { - width: auto; - padding: 0.25rem 0; - .custom-select { - border: none; - &:hover { - border: none; - } - .custom-select-dropdown { - left: auto; - right: 0; - } - } - } - } - - .select-image { - .content { - display: flex; - justify-content: center; - background-image: url("/images/colorpicker-no-image.png"); - background-position: center; - background-size: auto 6.75rem; - height: 6.75rem; - img { - height: fit-content; - width: fit-content; - max-height: 100%; - max-width: 100%; - margin: auto; - } - } - button { - width: 100%; - margin-top: 10px; - } - } - .gradients-buttons { - .gradient { - cursor: pointer; - width: 15px; - height: 15px; - padding: 0; - margin: 0; - border: 1px solid $color-gray-20; - border-radius: $br2; - margin-left: $size-1; - } - - .active { - border-color: $color-primary; - } - - .linear-gradient { - background: linear-gradient(180deg, $color-gray-20, transparent); - } - - .radial-gradient { - background: radial-gradient(transparent, $color-gray-20); - } - } - - .gradient-stops { - height: 10px; - display: flex; - margin-top: $size-2; - margin-bottom: $size-4; - - .gradient-background-wrapper { - height: 100%; - width: 100%; - border: 1px solid $color-gray-10; - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=") - left center; - } - - .gradient-background { - height: 100%; - width: 100%; - } - - .gradient-stop-wrapper { - position: absolute; - width: calc(100% - 2rem); - margin-left: 0.5rem; - } - - .gradient-stop { - display: grid; - grid-template-columns: 50% 50%; - position: absolute; - width: 15px; - height: 15px; - border-radius: $br2; - border: 1px solid $color-gray-20; - margin-top: -2px; - margin-left: -7px; - box-shadow: 0 2px 2px rgb(0 0 0 / 15%); - - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=") - left center; - background-color: $color-white; - - &.active { - border-color: $color-primary; - } - } - } - - .picker-detail-wrapper { - position: relative; - - .center-circle { - width: 14px; - height: 14px; - border: 2px solid $color-white; - border-radius: $br8; - position: absolute; - left: 50%; - top: 50%; - transform: translate(-7px, -7px); - filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25)); - } - } - - #picker-detail { - border: 1px solid $color-gray-10; - } - - .slider-selector { - --gradient-direction: 90deg; - --background-repeat: left; - - &.vertical { - --gradient-direction: 0deg; - --background-repeat: top; - } - - border: 1px solid $color-gray-10; - - background: linear-gradient( - var(--gradient-direction), - rgba(var(--color), 0) 0%, - rgba(var(--color), 1) 100% - ); - align-self: center; - position: relative; - cursor: pointer; - - width: 100%; - height: calc(0.5rem + 1px); - - &.vertical { - width: calc(0.5rem + 1px); - height: 100%; - } - - &.hue { - background: linear-gradient( - var(--gradient-direction), - #f00 0%, - #ff0 17%, - #0f0 33%, - #0ff 50%, - #00f 67%, - #f0f 83%, - #f00 100% - ); - } - - &.saturation { - background: linear-gradient( - var(--gradient-direction), - var(--saturation-grad-from) 0%, - var(--saturation-grad-to) 100% - ); - } - - &.opacity { - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=") - var(--background-repeat) center; - - &::after { - content: ""; - position: absolute; - width: 100%; - height: 100%; - background: linear-gradient( - var(--gradient-direction), - rgba(var(--color), 0) 0%, - rgba(var(--color), 1) 100% - ); - } - } - - &.value { - background: linear-gradient(var(--gradient-direction), #000 0%, #fff 100%); - } - - .handler { - background-color: $color-white; - box-shadow: rgba(0, 0, 0, 0.37) 0px 1px 4px 0px; - transform: translate(-6px, -2px); - left: 50%; - position: absolute; - width: 12px; - height: 12px; - border-radius: $br6; - z-index: 1; - } - - &.vertical .handler { - transform: translate(-6px, 6px); - } - } - - .value-saturation-selector { - background-color: rgba(var(--hue-rgb)); - position: relative; - height: 6.75rem; - cursor: pointer; - - .handler { - position: absolute; - width: 12px; - height: 12px; - border-radius: $br6; - z-index: 1; - border: 1px solid $color-white; - box-shadow: - rgb(255, 255, 255) 0px 0px 0px 1px inset, - rgb(0 0 0 / 0.25) 0px 4px 4px inset, - rgb(0 0 0 / 0.25) 0px 4px 4px; - transform: translate(-6px, -6px); - left: 50%; - top: 50%; - } - - &::before { - content: ""; - position: absolute; - width: 100%; - height: 100%; - background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0)); - } - - &::after { - content: ""; - position: absolute; - width: 100%; - height: 100%; - background: linear-gradient(to top, #000, rgba(0, 0, 0, 0)); - } - } - - .shade-selector { - display: grid; - justify-items: center; - align-items: center; - grid-template-areas: - "color hue" - "color opacity"; - grid-template-columns: 2.5rem 1fr; - height: 3.5rem; - grid-row-gap: 0.5rem; - cursor: pointer; - margin-bottom: 0.25rem; - - .slider-selector.hue { - grid-area: "hue"; - align-self: flex-end; - } - - .slider-selector.opacity { - grid-area: "opacity"; - align-self: flex-start; - } - } - - .color-values { - display: grid; - grid-template-columns: 3.5rem repeat(4, 1fr); - grid-row-gap: 0.25rem; - justify-items: center; - grid-column-gap: 0.25rem; - - &.disable-opacity { - grid-template-columns: 3.5rem repeat(3, 1fr); - } - - input { - width: 100%; - margin: 0; - border: 1px solid $color-gray-10; - border-radius: $br2; - font-size: $fs12; - height: 1.5rem; - padding: 0 $size-1; - color: $color-gray-40; - } - - label { - font-size: $fs12; - } - } - - .libraries { - border-top: 1px solid $color-gray-10; - padding-top: 0.5rem; - margin-top: 0.25rem; - width: 200px; - - select { - background-image: url(/images/icons/arrow-down.svg); - background-repeat: no-repeat; - background-position: 95% 48%; - background-size: 10px; - margin: 0; - margin-bottom: $size-2; - width: 100%; - padding: $size-1 0.25rem; - font-size: $fs12; - color: $color-gray-40; - cursor: pointer; - border: 1px solid $color-gray-10; - border-radius: $br2; - - option { - padding: 0; - } - } - - .selected-colors { - display: grid; - grid-template-columns: repeat(8, 1fr); - justify-content: space-between; - margin-right: -8px; - max-height: 5.5rem; - overflow: auto; - div { - grid-area: unset; - } - } - - .selected-colors::after { - content: ""; - flex: auto; - } - } - - .actions { - margin-top: 0.5rem; - display: flex; - flex-direction: row; - justify-content: center; - - .btn-primary { - height: 1.5rem; - font-size: $fs12; - width: 100%; - } - } - - .harmony-selector { - display: flex; - flex-direction: row; - margin-bottom: 0.5rem; - - .hue-wheel-wrapper { - position: relative; - - .hue-wheel { - width: 152px; - height: 152px; - } - - .handler { - position: absolute; - width: 12px; - height: 12px; - border-radius: $br6; - z-index: 1; - border: 1px solid $color-white; - box-shadow: - rgb(255, 255, 255) 0px 0px 0px 1px inset, - rgb(0 0 0 / 0.25) 0px 4px 4px inset, - rgb(0 0 0 / 0.25) 0px 4px 4px; - transform: translate(-6px, -6px); - left: 50%; - top: 50%; - } - - .handler.complement { - background-color: $color-white; - box-shadow: rgb(0 0 0 / 0.25) 0px 4px 4px; - } - } - - .handlers-wrapper { - height: 152px; - display: flex; - flex-direction: row; - flex-grow: 1; - justify-content: space-around; - padding-top: 0.5rem; - - & > * { - height: 100%; - } - } - } - - .hsva-selector { - display: grid; - padding: 0.25rem; - grid-template-columns: 20px 1fr; - grid-template-rows: repeat(4, 2rem); - grid-row-gap: 0.5rem; - margin-bottom: 0.5rem; - - .hue, - .saturation, - .value, - .opacity { - border-radius: $br10; - } - - .hsva-selector-label { - grid-column: 1; - align-self: center; - } - } -} - -.colorpicker-tooltip { - border-radius: $br3; - display: flex; - flex-direction: column; - left: 1400px; - top: 100px; - position: absolute; - z-index: 11; - width: auto; - - span { - color: $color-gray-20; - font-size: $fs12; - } - - .inputs-area { - .input-text { - color: $color-gray-60; - font-size: $fs12; - margin: 5px; - padding: 5px; - width: 100%; - } - } - - .colorpicker-tabs { - display: flex; - margin-bottom: $size-2; - border-radius: $br5; - border: 1px solid $color-gray-10; - height: 2rem; - - .colorpicker-tab { - cursor: pointer; - display: flex; - flex-grow: 1; - justify-content: center; - align-items: center; - - svg { - width: 16px; - height: 16px; - fill: $color-gray-20; - } - } - - .active { - background-color: $color-gray-10; - svg { - fill: $color-gray-60; - } - } - - :hover svg { - fill: $color-primary; - } - } -} - -.color-data { - align-items: center; - display: flex; - margin-bottom: $size-2; - position: relative; - - &[draggable="true"] { - cursor: pointer; - } - - .color-name { - font-size: $fs12; - margin: 5px 6px 0px 6px; - } - - .color-info { - flex: 1 1 0; - - input { - background-color: $color-gray-50; - border: 1px solid $color-gray-30; - border-radius: $br3; - color: $color-white; - height: 20px; - margin: 5px 0 0 0; - padding: 0 $size-1; - width: 84px; - font-size: $fs12; - - &:focus { - border-color: $color-primary !important; - color: $color-white; - outline: none; - } - - &:hover { - border-color: $color-gray-20; - } - - &:invalid { - border-color: $color-danger; - } - } - } - - ::placeholder { - color: $color-gray-10; - } - - .type { - color: $color-gray-10; - margin-right: $size-1; - } - - .number { - color: $color-gray-60; - } - - .element-set-actions-button svg { - width: 10px; - height: 10px; - } -} diff --git a/frontend/resources/styles/main/partials/comments.scss b/frontend/resources/styles/main/partials/comments.scss deleted file mode 100644 index 7c2d4bf002..0000000000 --- a/frontend/resources/styles/main/partials/comments.scss +++ /dev/null @@ -1,467 +0,0 @@ -.comments-section { - .thread-bubble { - position: absolute; - display: flex; - transform: translate(-15px, -15px); - - cursor: pointer; - pointer-events: auto; - background-color: $color-gray-10; - color: $color-gray-60; - border: 1px solid #b1b2b5; - box-sizing: border-box; - box-shadow: 0px 4px 4px rgba($color-black, 0.25); - - font-size: $fs12; - width: 30px; - height: 30px; - border-radius: 50%; - - display: flex; - align-items: center; - justify-content: center; - - &.resolved { - color: $color-gray-10; - background-color: $color-gray-50; - } - - &.unread { - background-color: $color-primary; - } - span { - user-select: none; - } - } - - .thread-content { - position: absolute; - pointer-events: auto; - margin-left: 10px; - background: $color-white; - border: 1px solid $color-gray-20; - box-sizing: border-box; - box-shadow: 0px 2px 8px rgba($color-black, 0.25); - border-radius: $br2; - min-width: 280px; - max-width: 280px; - user-select: text; - - .comments { - max-height: 420px; - min-height: 105px; - overflow-y: auto; - } - - hr { - border: 0; - height: 1px; - background-color: $color-gray-20; - margin: 0px 10px; - } - } - - .reply-form { - display: flex; - padding: 10px; - flex-direction: column; - - &.edit-form { - padding-bottom: 0px; - } - - textarea { - font-family: "worksans", sans-serif; - font-size: $fs12; - min-height: 32px; - outline: none; - overflow: hidden; - padding: $size-2; - resize: none; - width: 100%; - border-radius: $br2; - border: 1px solid $color-gray-20; - max-height: 4rem; - } - - .buttons { - margin-top: 10px; - display: flex; - justify-content: flex-end; - - input { - margin: 0px; - font-size: $fs14; - - &:not(:last-child) { - margin-right: 6px; - } - } - } - } - - .comment-container { - position: relative; - } - - .comment { - display: flex; - flex-direction: column; - padding: $size-4 $size-2; - - .author { - display: flex; - align-items: center; - height: 26px; - max-height: 26px; - position: relative; - - .name { - display: flex; - flex-direction: column; - - .fullname { - font-weight: $fw700; - color: $color-gray-60; - font-size: $fs12; - - @include text-ellipsis; - width: 174px; - } - .timeago { - margin-top: -2px; - font-size: $fs12; - color: $color-gray-30; - } - } - - .avatar { - display: flex; - align-items: center; - padding-right: 6px; - - img { - border-radius: 50%; - flex-shrink: 0; - height: 24px; - width: 24px; - } - } - - .options-resolve { - position: absolute; - right: 20px; - top: 0px; - width: 16px; - height: 16px; - - cursor: pointer; - - svg { - width: 16px; - height: 16px; - fill: $color-gray-30; - } - } - - .options { - position: absolute; - right: -2px; - top: 2px; - height: 16px; - display: flex; - align-items: center; - cursor: pointer; - - .options-icon { - svg { - width: 14px; - height: 14px; - fill: $color-black; - } - } - } - } - - .content { - margin: $size-4 0; - font-size: $fs14; - color: $color-black; - .text { - margin: 0 $size-2 0 26px; - white-space: pre-wrap; - display: inline-block; - word-break: break-word; - } - } - } - - .comment-options-dropdown { - top: 7px; - right: 7px; - width: 150px; - - border: 1px solid #b1b2b5; - } -} - -.workspace-comment-threads-sidebar-header { - display: flex; - background-color: $color-black; - height: 34px; - align-items: center; - padding: 0px 9px; - color: $color-gray-10; - font-size: $fs12; - justify-content: space-between; - - .options { - display: flex; - margin-right: 3px; - cursor: pointer; - - .label { - padding-right: 8px; - } - - .icon { - display: flex; - align-items: center; - } - - svg { - fill: $color-gray-10; - width: 10px; - height: 10px; - } - } - - .dropdown { - top: 80px; - right: 7px; - } -} - -.comment-threads-section { - pointer-events: auto; - - .thread-groups { - height: calc(100% - 34px); - overflow-y: scroll; - hr { - border: 0; - height: 1px; - background-color: $color-gray-30; - margin: 0px 0px; - } - } - - .thread-group { - display: flex; - flex-direction: column; - font-size: $fs12; - - .section-title { - margin: 0px 10px; - margin-top: 15px; - - .icon { - margin-right: 4px; - } - - .label { - &.filename { - font-weight: $fw700; - } - } - - svg { - fill: $color-gray-10; - height: 10px; - width: 10px; - } - } - } - - .thread-bubble { - position: unset; - transform: unset; - width: 24px; - height: 24px; - margin-right: 6px; - box-shadow: unset; - } - - .comment { - cursor: pointer; - .author { - margin-bottom: $size-4; - .name { - display: flex; - - .fullname { - width: unset; - max-width: 170px; - color: $color-gray-20; - padding-right: 3px; - } - .timeago { - margin-top: unset; - color: $color-gray-20; - } - } - } - - .content { - margin-top: 0px; - color: $color-white; - - &.replies { - margin: 0 $size-2 0 26px; - display: flex; - .total-replies { - margin-right: 9px; - color: $color-info; - } - - .new-replies { - color: $color-primary; - } - } - } - } -} - -.viewer-comments-container { - width: 100%; - height: 100%; - z-index: 1; - position: absolute; - top: 0px; - left: 0px; -} - -.workspace-comments-container { - width: 100%; - height: 100%; - grid-column: 1 / span 2; - grid-row: 1 / span 2; - z-index: 1000; - pointer-events: none; - overflow: hidden; - user-select: text; - - .threads { - position: absolute; - top: 0px; - left: 0px; - } -} - -.dashboard-comments-section { - width: 25px; - height: 25px; - display: flex; - align-items: center; - justify-content: center; - background-color: $color-dashboard; - border-radius: $br3; - position: relative; - - .button { - width: 25px; - height: 25px; - display: flex; - align-items: center; - justify-content: center; - background-color: $color-dashboard; - border-radius: $br3; - - svg { - width: 15px; - height: 15px; - } - - &.unread { - background-color: $color-warning; - } - - &.open { - background-color: $color-black; - svg { - fill: $color-primary; - } - } - } - - .dropdown { - width: 280px; - bottom: 35px; - left: 0px; - border-radius: $br3; - } - - .header { - display: flex; - height: 40px; - align-items: center; - padding: 0px 11px; - - h3 { - font-weight: $fw400; - color: $color-black; - font-size: $fs14; - line-height: $lh-128; // Original value was $fs18 => 1.125rem = 18px; 18px/14px = 128.571428571% => $lh-128 (rounded) - flex-grow: 1; - } - - .close { - display: flex; - align-items: center; - } - - svg { - width: 15px; - height: 15px; - transform: rotate(45deg); - } - } - - .thread-groups { - max-height: calc(30rem - 40px); - overflow: auto; - - hr { - background-color: $color-gray-10; - } - } - - .thread-group .section-title { - color: $color-black; - } - - .comment { - .author .name .fullname { - color: $color-gray-40; - } - .content { - color: $color-black; - } - } -} - -.thread-groups-placeholder { - align-items: center; - display: flex; - flex-direction: column; - font-size: $fs12; - padding: $size-5; - text-align: center; - - svg { - fill: $color-gray-20; - height: 24px; - margin-bottom: $size-5; - width: 24px; - } -} diff --git a/frontend/resources/styles/main/partials/dashboard-fonts.scss b/frontend/resources/styles/main/partials/dashboard-fonts.scss deleted file mode 100644 index 544e50341e..0000000000 --- a/frontend/resources/styles/main/partials/dashboard-fonts.scss +++ /dev/null @@ -1,254 +0,0 @@ -.dashboard-fonts { - display: flex; - flex-direction: column; - align-items: center; - - .dashboard-installed-fonts { - max-width: 1000px; - width: 100%; - display: flex; - margin-top: $size-5; - flex-direction: column; - - h3 { - font-size: $fs14; - color: $color-gray-30; - margin: $size-1; - } - - .font-item { - color: $color-black; - } - } - - .installed-fonts-header { - color: $color-gray-40; - display: flex; - height: 40px; - font-size: $fs12; - background-color: $color-white; - align-items: center; - padding: 0px $size-5; - - > .family { - min-width: 200px; - width: 200px; - } - - > .variants { - padding-left: 12px; - } - - .search-input { - display: flex; - flex-grow: 1; - justify-content: flex-end; - - input { - font-size: $fs12; - border: 1px solid $color-gray-30; - border-radius: $br3; - width: 130px; - padding: $size-1; - margin: 0px; - } - } - } - - .font-item { - color: $color-gray-40; - font-size: $fs14; - background-color: $color-white; - display: flex; - max-width: 1000px; - width: 100%; - min-height: 97px; - align-items: center; - padding: $size-5; - justify-content: space-between; - - &:not(:first-child) { - border-top: 1px solid $color-gray-10; - } - - input { - border: 1px solid $color-gray-30; - border-radius: $br3; - margin: 0px; - padding: $size-2; - font-size: $fs12; - } - - > .family { - min-width: 200px; - width: 200px; - } - - > .filenames { - min-width: 200px; - } - - > .variants { - font-size: $fs14; - display: flex; - flex-wrap: wrap; - flex-grow: 1; - - .variant { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 12px; - cursor: pointer; - - .icon { - display: flex; - height: 16px; - width: 16px; - margin-left: 6px; - align-items: center; - svg { - fill: transparent; - width: 12px; - height: 12px; - transform: rotate(45deg); - } - } - - &:hover { - .icon svg { - fill: $color-gray-30; - } - } - } - } - - .filenames { - display: flex; - flex-direction: column; - font-size: $fs12; - } - - .options { - display: flex; - justify-content: flex-end; - min-width: 180px; - - .icon { - width: $size-5; - cursor: pointer; - display: flex; - margin-left: 10px; - justify-content: center; - align-items: center; - &.failure { - margin-right: 10px; - svg { - fill: $color-warning; - } - } - svg { - width: 16px; - height: 16px; - } - - &.close { - svg { - transform: rotate(45deg); - } - } - } - } - } - - .dashboard-fonts-upload { - max-width: 1000px; - width: 100%; - display: flex; - flex-direction: column; - - .upload-button { - width: 100px; - } - - .btn-secondary { - margin-left: 10px; - } - } - - .dashboard-fonts-hero { - font-size: $fs14; - padding: $size-6; - background-color: $color-white; - margin-top: $size-6; - display: flex; - justify-content: space-between; - - .banner { - background-color: $color-info-lighter; - display: grid; - grid-template-columns: 40px 1fr; - &:not(:last-child) { - margin-bottom: 10px; - } - .icon { - display: flex; - align-items: flex-start; - justify-content: center; - padding-top: 10px; - background-color: $color-info; - svg { - fill: $color-white; - } - } - .content { - margin: 10px; - } - &.warning { - background-color: $color-warning-lighter; - .icon { - background-color: $color-warning; - } - } - } - - .desc { - h2 { - margin-bottom: $size-4; - color: $color-black; - } - width: 80%; - color: $color-gray-40; - } - - .btn-primary { - flex-shrink: 0; - } - } - - .fonts-placeholder { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - max-width: 1000px; - width: 100%; - height: 161px; - - border: 1px dashed $color-gray-20; - margin-top: 16px; - - .icon { - svg { - fill: $color-gray-40; - width: 32px; - height: 32px; - } - } - - .label { - color: $color-gray-40; - font-size: $fs14; - } - } -} diff --git a/frontend/resources/styles/main/partials/dashboard-grid.scss b/frontend/resources/styles/main/partials/dashboard-grid.scss deleted file mode 100644 index e5f14c116a..0000000000 --- a/frontend/resources/styles/main/partials/dashboard-grid.scss +++ /dev/null @@ -1,527 +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 - -.dashboard-grid { - font-size: $fs14; - height: 100%; - overflow: hidden; - overflow-y: auto; - margin-bottom: 0; - - .grid-row { - display: grid; - width: 99%; - margin-left: 13px; - } - - .grid-item { - align-items: center; - cursor: pointer; - display: flex; - flex-direction: column; - flex: 1 0 260px; - height: 230px; - margin: $size-3 $size-4 $size-4 $size-2; - position: relative; - text-align: center; - a, - button { - width: 100%; - font-weight: $fw400; - } - button { - background-color: transparent; - border: none; - } - @media #{$bp-max-1366} { - height: 200px; - flex: 1 0 230px; - } - - &:hover { - .grid-item-th { - border: 2px solid $color-primary; - } - } - - .grid-item-th { - border-radius: $br3; - border: 2px solid lighten($color-gray-20, 15%); - text-align: initial; - - img { - object-fit: contain; - } - } - - &.dragged { - border-radius: $br3; - border: 2px solid lighten($color-gray-20, 15%); - text-align: initial; - max-height: 160px; - } - - &.placeholder { - min-width: 115px; - max-width: 115px; - - display: flex; - flex-direction: column; - justify-content: center; - - .placeholder-icon { - svg { - transform: rotate(-90deg); - width: 20px; - height: 20px; - fill: $color-gray-30; - } - } - - .placeholder-label { - font-size: $fs14; - } - } - - &.overlay { - border-radius: $br4; - border: 2px solid $color-primary; - height: 100%; - opacity: 0; - pointer-events: none; - position: absolute; - width: 100%; - z-index: 1; - } - - &:hover .overlay { - display: block; - opacity: 1; - } - - &.small-item { - max-width: 12%; - min-width: 190px; - padding: $size-4; - justify-content: center; - } - - .grid-item-icon { - width: 90px; - height: 90px; - } - .info-wrapper { - display: grid; - grid-template-columns: 1fr auto; - } - - .item-info { - display: grid; - padding: $size-2; - text-align: left; - width: 100%; - font-size: $fs12; - - h3 { - border: 1px solid transparent; - color: $color-gray-60; - font-size: $fs14; - font-weight: $fw500; - overflow: hidden; - padding: 0; - height: 27px; - padding-right: $size-2; - text-overflow: ellipsis; - width: 100%; - white-space: nowrap; - line-height: $lh-192; // Original value was 27px; 27px/14px = 192.857142857% => $lh-192 (rounded) - max-width: 260px; - @media #{$bp-max-1366} { - max-width: 230px; - } - } - - span.date { - color: $color-gray-30; - overflow: hidden; - text-overflow: ellipsis; - width: 100%; - white-space: nowrap; - max-width: 260px; - &::first-letter { - text-transform: capitalize; - } - @media #{$bp-max-1366} { - max-width: 230px; - } - } - - .edit-wrapper { - .element-title { - padding: 0px; - height: 25px; - color: $color-gray-60; - font-size: $fs14; - font-weight: $fw400; - } - } - } - - .item-badge { - background-color: $color-white; - border: 1px solid $color-gray-20; - border-radius: $br2; - position: absolute; - top: $size-2; - right: $size-2; - height: 32px; - width: 32px; - display: flex; - align-items: center; - justify-content: center; - - svg { - fill: $color-gray-30; - height: 16px; - width: 16px; - } - } - - &.add-file { - border: 1px dashed $color-gray-20; - justify-content: center; - box-shadow: none; - - span { - color: $color-gray-60; - font-size: $fs14; - } - - &:hover { - background-color: $color-white; - border: 2px solid $color-primary; - } - } - - // PROJECTS, ELEMENTS & ICONS GRID - &.project-th { - background-color: $color-white; - - &:hover, - &:focus, - &:focus-within { - .project-th-actions { - opacity: 1; - } - a { - text-decoration: none; - } - } - - .selected { - .grid-item-th { - border: 2px solid $color-primary; - } - } - - .project-th-actions { - align-items: center; - opacity: 0; - display: flex; - right: 5px; - justify-content: center; - width: 30px; - height: 100%; - - span { - color: $color-black; - } - - .project-th-icon { - align-items: center; - display: flex; - margin-right: $size-2; - - &.menu { - margin-right: 0; - display: flex; - justify-content: center; - align-items: flex-end; - flex-direction: column; - width: 100%; - height: 30px; - margin-top: 20px; - - > svg { - fill: $color-gray-60; - margin-right: 0; - height: 18px; - width: 18px; - } - - &:hover, - &:focus { - > svg { - fill: $color-primary-dark; - } - } - } - } - } - - .project-th-actions.force-display { - opacity: 1; - } - } - - // IMAGES SECTION - &.images-th { - border: 1px dashed $color-gray-20; - border-bottom: 2px solid lighten($color-gray-20, 12%); - - &:hover { - border-color: $color-primary; - } - } - - .grid-item-image { - svg { - max-height: 100px; - max-width: 100px; - min-height: 40px; - min-width: 40px; - width: 8vw; - } - } - - .color-swatch { - border-top-left-radius: $br5; - border-top-right-radius: $br5; - height: 25%; - left: 0; - position: absolute; - top: 0; - width: 100%; - } - - .color-data { - color: $color-gray-30; - margin-top: 15px; - } - - .drag-counter { - position: absolute; - top: 5px; - left: 4px; - width: 32px; - height: 32px; - background-color: $color-primary; - border-radius: 50%; - color: $color-black; - font-size: $fs18; - display: flex; - justify-content: center; - align-items: center; - } - } - - .grid-item-th { - background-position: center; - background-size: auto 80%; - background-repeat: no-repeat; - border-top-left-radius: $br3; - border-top-right-radius: $br3; - height: 230px; - max-height: 160px; - overflow: hidden; - position: relative; - width: 100%; - - background-color: $color-canvas; - display: flex; - justify-content: center; - flex-direction: row; - - .img-th { - height: auto; - width: 100%; - } - - svg { - height: 100%; - width: 100%; - } - - svg#loader-pencil { - fill: $color-gray-20; - } - } - - // LIBRARY VIEW - .grid-item { - .library { - height: 580px; - } - - &.project-th.library { - height: 610px; - width: 300px; - } - - .grid-item-th.library { - background-color: $color-gray-50; - flex-direction: column; - height: 90%; - justify-content: flex-start; - max-height: 550px; - padding: $size-6; - - .asset-section { - font-size: $fs12; - color: $color-gray-20; - - &:not(:first-child) { - margin-top: $size-4; - } - } - - .asset-title { - display: flex; - font-size: $fs12; - text-transform: uppercase; - - & .num-assets { - color: $color-gray-30; - } - } - - .asset-list-item { - display: flex; - align-items: center; - border: 1px solid transparent; - border-radius: $br3; - margin-top: $size-1; - padding: 2px; - font-size: $fs12; - color: $color-white; - position: relative; - - & .name-block { - color: $color-gray-20; - width: calc(100% - 24px - #{$size-2}); - } - - & .item-name { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - display: block; - } - - & svg { - background-color: $color-canvas; - border-radius: $br4; - border: 2px solid transparent; - height: 24px; - width: 24px; - margin-right: $size-2; - } - - & .color-name { - color: $color-white; - } - - & .color-value { - margin-left: $size-1; - color: $color-gray-30; - text-transform: uppercase; - } - - & .typography-sample { - height: 20px; - margin-right: $size-1; - width: 20px; - } - } - } - } -} - -.grid-empty-placeholder { - border-radius: $br12; - display: grid; - background-color: rgba(227, 227, 227, 0.3); - padding: 13px; - margin-right: 13px; - height: 230px; - &.loader { - justify-items: center; - } - .icon { - display: flex; - align-items: center; - justify-content: center; - } - &.libs { - background-image: url(/images/ph-left.svg), url(/images/ph-right.svg); - background-position: - 15% bottom, - 85% top; - background-repeat: no-repeat; - align-items: center; - border: 1px dashed #b1b2b5; - border-radius: $br3; - display: flex; - flex-direction: column; - height: 200px; - margin: 1rem; - padding: 3rem; - justify-content: center; - .text { - p { - max-width: 360px; - text-align: center; - font-size: $fs16; - } - } - } - .create-new { - background-color: white; - border: 2px solid $color-gray-10; - border-radius: $br3; - color: $color-black; - cursor: pointer; - height: 158px; - font-family: "worksans", sans-serif; - margin: 0.5rem; - &:hover { - border: 2px solid $color-primary; - } - } - - &.search { - align-items: center; - display: flex; - justify-content: center; - flex-direction: column; - height: 200px; - background: $color-white; - border: 1px dashed #e3e3e3; - border-radius: $br0; - } - - svg { - width: 36px; - height: 36px; - fill: $color-gray-20; - } - - .text { - margin-top: 10px; - color: $color-gray-30; - font-size: $fs16; - } -} diff --git a/frontend/resources/styles/main/partials/dashboard-header.scss b/frontend/resources/styles/main/partials/dashboard-header.scss deleted file mode 100644 index 157a440058..0000000000 --- a/frontend/resources/styles/main/partials/dashboard-header.scss +++ /dev/null @@ -1,139 +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 - -.dashboard-header { - display: flex; - align-items: center; - justify-content: space-between; - background-color: $color-white; - height: 63px; - padding: $size-1 $size-4 $size-1 $size-2; - position: relative; - z-index: 10; - user-select: none; - &.team { - display: grid; - grid-template-columns: 20% 1fr 20%; - } - - .element-name { - margin-right: $size-2; - } - - .btn-secondary { - flex-shrink: 0; - z-index: 10; - height: 32px; - } - - svg { - fill: $color-black; - height: 14px; - margin-right: $size-1; - width: 14px; - } - - nav { - display: flex; - align-items: flex-end; - justify-content: center; - z-index: 1; - - ul { - display: flex; - font-size: $fs14; - justify-content: center; - margin: 0; - } - - li { - a { - display: flex; - align-items: center; - flex-basis: 140px; - border-bottom: 3px solid transparent; - color: $color-gray-30; - height: 40px; - padding: $size-1 $size-5; - font-weight: $fw400; - &:hover { - color: $color-black; - text-decoration: none; - } - } - - &.active { - a { - color: $color-black; - border-color: $color-primary; - } - } - } - } - - .dashboard-title { - display: flex; - align-items: center; - margin-left: 13px; - - h1 { - color: $color-black; - display: flex; - flex-shrink: 0; - font-size: $fs22; - font-weight: $fw600; - z-index: 10; - user-select: all; - } - - .context-menu.is-open { - margin-top: 10px; - } - } - - .icon { - display: flex; - align-items: center; - cursor: pointer; - margin-left: $size-2; - z-index: 10; - - svg { - fill: $color-gray-40; - width: 15px; - height: 15px; - - &:hover { - fill: $color-primary-dark; - } - } - } - - .dashboard-buttons { - display: flex; - justify-content: flex-end; - align-items: center; - } - - .dashboard-header-actions { - display: flex; - } - - .pin-icon { - margin: 0 $size-2 0 $size-5; - background-color: transparent; - border: none; - svg { - fill: $color-gray-20; - } - - &.active { - svg { - fill: $color-gray-50; - } - } - } -} diff --git a/frontend/resources/styles/main/partials/dashboard-settings.scss b/frontend/resources/styles/main/partials/dashboard-settings.scss deleted file mode 100644 index 0cd1146870..0000000000 --- a/frontend/resources/styles/main/partials/dashboard-settings.scss +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright (c) 2020 KALEIDOS INC -// 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 - -.dashboard-sidebar { - &.settings { - .back-to-dashboard { - padding: 12px 18px; - font-size: $fs14; - cursor: pointer; - display: flex; - - .icon { - display: flex; - align-items: center; - margin-right: 14px; - } - - .text { - color: $color-gray-60; - } - - svg { - fill: $color-gray-60; - transform: rotate(90deg); - width: 12px; - height: 12px; - } - } - } -} - -.dashboard-settings { - display: flex; - width: 100%; - justify-content: center; - align-items: center; - - .form-container { - margin-top: 50px; - display: flex; - max-width: 368px; - margin-bottom: 2rem; - width: 100%; - - &.two-columns { - max-width: 536px; - justify-content: space-between; - flex-direction: row; - } - - h2 { - margin-bottom: 1rem; - } - } - - .avatar-form { - display: flex; - flex-direction: column; - width: 120px; - min-width: 120px; - - img { - border-radius: 50%; - flex-shrink: 0; - height: 120px; - margin-right: $size-4; - width: 120px; - } - - .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 { - .update-overlay { - opacity: 0.8; - } - } - } - } - - .profile-form { - display: flex; - flex-direction: column; - max-width: 368px; - width: 100%; - - .newsletter-subs { - border-bottom: 1px solid $color-gray-20; - border-top: 1px solid $color-gray-20; - padding: 30px 0; - margin-bottom: 31px; - - .newsletter-title { - font-family: "worksans", sans-serif; - color: $color-gray-30; - font-size: $fs14; - } - - label { - font-family: "worksans", sans-serif; - color: $color-gray-60; - font-size: $fs12; - margin-right: -17px; - margin-bottom: 13px; - } - - .info { - font-family: "worksans", sans-serif; - color: $color-gray-30; - font-size: $fs12; - margin-bottom: 8px; - } - - .input-checkbox label { - align-items: flex-start; - } - } - } - - .options-form, - .password-form { - h2 { - font-size: $fs14; - margin-bottom: 20px; - } - } -} - -.dashboard-access-tokens { - display: flex; - flex-direction: column; - align-items: center; - - .access-tokens-hero-container { - max-width: 1000px; - width: 100%; - display: flex; - flex-direction: column; - } - - .access-tokens-hero { - font-size: $fs14; - padding: $size-6; - background-color: $color-white; - margin-top: $size-6; - display: flex; - justify-content: space-between; - - .desc { - width: 80%; - color: $color-gray-40; - h2 { - margin-bottom: $size-4; - color: $color-black; - } - p { - font-size: $fs16; - } - } - - .btn-primary { - flex-shrink: 0; - } - } - - .access-tokens-empty { - text-align: center; - max-width: 1000px; - width: 100%; - padding: $size-6; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - border: 1px dashed $color-gray-20; - color: $color-gray-40; - margin-top: 12px; - min-height: 136px; - } - - .table-row { - background-color: $color-white; - display: grid; - grid-template-columns: 1fr 43% 12px; - height: 63px; - &:not(:first-child) { - margin-top: 8px; - } - } - - .table-field { - &.name { - color: $color-gray-60; - } - - &.expiration-date { - color: $color-gray-40; - font-size: $fs14; - .content { - padding: 2px 5px; - &.expired { - background-color: $color-warning-lighter; - border-radius: $br4; - color: $color-gray-40; - } - } - } - - &.access-token-created { - word-break: break-all; - } - - &.actions { - position: relative; - } - } -} - -.access-tokens-modal { - .action-buttons { - gap: 10px; - - .cancel-button { - border: 1px solid $color-gray-30; - background: $color-canvas; - border-radius: $br3; - padding: 0.5rem 1rem; - cursor: pointer; - margin-right: 8px; - - &:hover { - background: $color-gray-20; - } - } - } - .access-token-created { - position: relative; - word-break: break-all; - - .custom-input input { - background-color: $color-success-lighter; - border: 0; - padding: 0 0 0 15px; - } - } - - .help-icon { - border: none; - height: 40px; - width: 40px; - position: absolute; - top: 0; - right: 0; - cursor: pointer; - background-color: $color-success-lighter; - - svg { - fill: $color-gray-30; - } - - &:hover { - svg { - fill: $color-gray-60; - } - } - } - - .token-created-info { - font-size: $fs12; - color: $color-gray-40; - } -} diff --git a/frontend/resources/styles/main/partials/dashboard-sidebar.scss b/frontend/resources/styles/main/partials/dashboard-sidebar.scss deleted file mode 100644 index 646d53a303..0000000000 --- a/frontend/resources/styles/main/partials/dashboard-sidebar.scss +++ /dev/null @@ -1,476 +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 - -.dashboard-sidebar { - background-color: $color-white; - z-index: 1; - display: flex; - flex-direction: column; - height: 100%; - padding-top: $size-2; - - .sidebar-content { - display: flex; - flex-direction: column; - height: 100%; - overflow-y: auto; - padding: 0; - - hr { - border-color: $color-gray-10; - margin: 1rem 15px; - } - } - - .sidebar-team-switch { - position: relative; - display: flex; - margin: 5px 15px; - - .teams-dropdown { - left: 0; - top: 50px; - z-index: 12; - max-height: 30rem; - min-width: 234px; - overflow-y: auto; - } - - .options-dropdown { - right: 2px; - top: 50px; - z-index: 12; - max-height: 30rem; - min-width: 162px; - } - - .switch-content { - height: 40px; - display: flex; - width: 100%; - border: 1px solid $color-gray-10; - border-radius: $br5; - align-items: center; - } - - .switch-options { - display: flex; - max-width: 22px; - min-width: 28px; - border-left: 1px solid $color-gray-10; - justify-content: center; - align-items: center; - cursor: pointer; - background-color: transparent; - border: none; - svg { - width: 15px; - height: 13px; - fill: $color-gray-60; - } - } - - .current-team { - cursor: pointer; - display: flex; - align-items: center; - flex-grow: 1; - font-size: $fs14; - padding: 0px 10px; - background-color: transparent; - border: none; - } - - .team-name { - flex-grow: 1; - display: flex; - height: 40px; - align-items: center; - - &.action { - .team-icon { - border-radius: 50%; - background-color: $color-gray-10; - height: 24px; - margin-right: 10px; - padding: 6px; - width: 24px; - - svg { - height: 12px; - width: 12px; - } - } - - &:hover { - .team-icon { - background-color: $color-primary; - } - } - - .team-text { - width: 150px; - } - } - - .team-icon { - display: flex; - align-items: center; - padding-right: 10px; - - svg { - width: 23px; - height: 23px; - fill: $color-gray-60; - } - - img { - border-radius: 50%; - flex-shrink: 0; - height: 23px; - width: 23px; - } - } - - .team-text { - color: $color-gray-60; - @include text-ellipsis; - width: 130px; - text-align: left; - } - - .icon { - margin-left: auto; - svg { - fill: $color-gray-60; - } - } - } - - .switch-icon { - display: flex; - align-items: center; - justify-content: center; - svg { - width: 10px; - height: 10px; - fill: $color-gray-60; - } - } - } - - .sidebar-empty-placeholder { - padding: 10px 12px; - color: $color-gray-30; - display: flex; - align-items: flex-start; - - .icon { - padding: 0px 10px; - svg { - fill: $color-gray-30; - width: 12px; - height: 12px; - } - } - .text { - font-size: $fs12; - } - } - - .sidebar-nav { - display: flex; - flex-direction: column; - overflow-y: auto; - margin: 0; - user-select: none; - - // TODO: should be deprecated / unclear name - &.dashboard-common { - overflow: unset; - } - - &.no-overflow { - overflow: unset; - } - - & > li { - align-items: center; - cursor: pointer; - display: flex; - flex-shrink: 0; - padding: $size-2; - a { - font-weight: $fw400; - width: 100%; - &:hover { - text-decoration: none; - } - } - - svg { - fill: $color-black; - margin-right: 8px; - height: $size-3; - width: $size-3; - } - - span.element-title { - color: $color-gray-60; - font-size: $fs14; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - &::before { - background-color: transparent; - border-radius: $br3; - content: ""; - height: 26px; - margin-right: $size-2; - width: 4px; - } - - &.recent-projects { - svg { - fill: $color-white; - } - } - - & .edit-wrapper { - border: 1px solid $color-gray-10; - border-radius: $br3; - display: flex; - width: 100%; - } - - input.element-title { - border: 0; - height: 30px; - padding: 5px; - margin: 0; - width: 100%; - background-color: $color-white; - } - - .close { - background-color: $color-white; - cursor: pointer; - padding-left: 5px; - - svg { - fill: $color-gray-30; - height: 15px; - transform: rotate(45deg) translateY(7px); - width: 15px; - margin: 0; - } - } - - .element-subtitle { - color: $color-gray-20; - font-style: italic; - } - - &:hover { - &::before { - background-color: $color-gray-10; - } - } - - &.current { - a { - font-weight: $fw700; - } - - &::before { - background-color: $color-primary; - } - } - - &.dragging { - background-color: color.adjust($color-primary, $alpha: -0.69); - } - } - } - - .sidebar-search { - align-items: center; - background-color: $color-white; - border: 1px solid $color-gray-10; - border-radius: $br5; - display: flex; - margin: 5px 15px; - - .input-text { - background: transparent; - border: 0; - color: $color-gray-60; - font-size: $fs14; - padding: 6px; - margin: 0; - max-width: 195px; - width: 100%; - height: 40px; - } - - &:focus, - &:focus-within { - border-color: $color-black; - } - - .search, - .clear-search { - align-items: center; - cursor: pointer; - display: flex; - height: 22px; - margin-left: auto; - padding: 0 $size-2; - width: 32px; - - svg { - fill: $color-gray-30; - height: 15px; - width: 15px; - } - } - - .clear-search svg { - transform: rotate(45deg); - - &:hover { - fill: $color-danger; - } - } - } - - &.profile-bar { - background-color: $color-gray-10; - - .dashboard-sidebar-inside { - display: none; - } - } -} - -.projects-row { - align-items: center; - display: flex; - margin-top: 1rem; - padding: $size-2; - position: relative; - - span { - color: $color-gray-30; - font-size: $fs14; - } - - .btn-icon-light { - margin-left: auto; - } - - &::before { - background-color: $color-gray-10; - content: ""; - height: 1px; - left: 4%; - position: absolute; - right: 4%; - top: -5px; - width: 92%; - } -} - -.team-form-modal { - h2 { - font-weight: $fw400; - color: $color-gray-40; - font-size: 28px; - margin-bottom: 30px; - } - - .buttons-row { - margin-top: 20px; - display: flex; - justify-content: center; - } - - input[type="submit"] { - margin-bottom: 0px; - } -} - -.profile-section { - align-items: center; - cursor: pointer; - display: flex; - padding: 10px 15px; - position: relative; - - .profile { - align-items: center; - cursor: pointer; - display: flex; - flex-grow: 1; - - span { - @include text-ellipsis; - color: $color-black; - margin: 10px; - font-size: $fs14; - max-width: 160px; - } - - img { - border-radius: 50%; - flex-shrink: 0; - height: 25px; - width: 25px; - } - svg { - height: 10px; - margin-left: auto; - margin-right: $size-2; - width: 10px; - } - } - - .dropdown { - left: 15px; - bottom: 45px; - min-width: 189px; - - @include animation(0, 0.2s, fadeInUp); - - li { - font-size: $fs14; - padding: $size-2 $size-4; - - svg { - fill: $color-gray-20; - margin-right: $size-2; - - height: 12px; - width: 12px; - } - - &.separator { - border-top: 1px solid $color-gray-10; - } - } - } -} - -.primary-badge { - border: 1px solid $color-primary; - border-radius: $br2; - font-size: $fs9 !important; - font-weight: $fw500; - color: $color-primary !important; - padding: 2px 4px; -} diff --git a/frontend/resources/styles/main/partials/dashboard-team.scss b/frontend/resources/styles/main/partials/dashboard-team.scss deleted file mode 100644 index 5bdfcebda5..0000000000 --- a/frontend/resources/styles/main/partials/dashboard-team.scss +++ /dev/null @@ -1,605 +0,0 @@ -.dashboard-invite-modal { - top: 72px; - right: 13px; - padding: 32px; - box-shadow: 0px 4px 8px rgba($color-black, 0.25); - border-radius: $br8; - width: 400px; - position: fixed; - z-index: 16; - &.hero { - top: 218px; - right: 35px; - } - - form { - width: 100%; - } - - .form-row { - display: flex; - justify-content: space-between; - margin: 4px 0px; - .label { - margin-bottom: 0; - display: flex; - align-items: center; - } - } - - .custom-input { - width: 100%; - min-height: 116px; - max-height: 176px; - overflow-y: hidden; - input { - &.no-padding { - padding-top: 12px; - height: 50px; - } - min-height: 40px; - } - .selected-items { - gap: 8px; - padding: 8px; - max-height: 132px; - overflow-y: scroll; - .selected-item { - .around { - height: 24px; - display: flex; - align-items: center; - justify-content: flex-start; - width: fit-content; - .icon { - display: flex; - align-items: center; - justify-content: center; - } - } - } - } - } - - .custom-select { - width: 180px; - overflow: hidden; - justify-content: normal; - select { - height: auto; - } - } - - .action-buttons { - display: flex; - margin-top: 16px; - input[type="submit"] { - margin-bottom: 0px; - } - } - - .title { - color: $color-black; - font-weight: $fw700; - margin-bottom: 16px; - } - - .hint { - font-size: $fs12; - - &.hidden { - display: none; - } - } - - svg { - width: 12px; - height: 12px; - fill: $color-gray-20; - } - - .error, - .warning { - width: 100%; - display: flex; - .icon { - text-align: center; - padding: 5px; - svg { - fill: $color-white; - width: 20px; - height: 20px; - margin: 5px; - } - } - .text { - color: $color-black; - padding: 5px; - font-size: $fs12; - } - } - - .error { - background-color: #ffd9e0; - - .icon { - background-color: $color-danger; - } - } - - .warning { - background-color: #ffeaca; - - .icon { - background-color: $color-warning; - } - } -} - -.dashboard-team-members, -.dashboard-team-invitations, -.dashboard-team-webhooks { - .empty-invitations { - height: 156px; - max-width: 1040px; - width: 100%; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - border: 1px dashed $color-gray-20; - margin-top: 16px; - } - .table-header { - user-select: none; - } - - .table-row { - background-color: $color-white; - height: 63px; - &:not(:first-child) { - margin-top: 16px; - } - } - - .table-field { - &.name { - width: 43%; - min-width: 300px; - display: flex; - .member-info { - display: flex; - flex-direction: column; - margin-left: 16px; - .member-name { - font-size: $fs16; - .you { - color: $color-gray-30; - margin-left: 5px; - } - } - .member-email { - color: $color-gray-30; - font-size: $fs12; - } - } - - .member-image { - height: 32px; - width: 32px; - img { - border-radius: 50%; - } - } - } - - &.roles { - flex-grow: 1; - cursor: default; - position: relative; - .rol-label { - user-select: none; - } - .rol-selector { - &.has-priv { - border: 1px solid $color-gray-20; - cursor: pointer; - } - min-width: 160px; - height: 32px; - display: flex; - justify-content: space-between; - align-items: center; - border-radius: $br2; - padding: 3px 8px; - font-size: $fs14; - } - } - - &.actions { - position: relative; - .actions-dropdown { - max-height: 30rem; - min-width: 180px; - } - } - - &.status { - .status-badge { - color: $color-white; - border-radius: $br12; - min-width: 74px; - height: 24px; - display: flex; - justify-content: center; - align-items: center; - - &.pending { - background-color: $color-warning; - } - - &.expired { - background-color: $color-gray-20; - } - - .status-label { - font-size: $fs12; - } - } - } - - &.uri { - flex-grow: 1; - } - - &.active { - min-width: 100px; - } - - &.last-delivery { - display: flex; - justify-content: center; - width: 50px; - position: relative; - .success svg { - fill: $color-primary; - width: 16px; - height: 16px; - } - .failure svg { - fill: $color-warning; - width: 16px; - height: 16px; - } - - .icon-container { - width: 16px; - height: 16px; - overflow-x: visible; - } - - .icon { - padding: 0; - } - } - - .tooltip { - display: none; - position: absolute; - top: -58px; - left: 50%; - transform: translate(-50%, 0); - text-align: center; - - .label { - border-radius: $br3; - color: $color-white; - background-color: $color-black; - white-space: nowrap; - padding: 12px 20px; - } - - .arrow-down { - margin: 0 auto; - width: 0; - height: 0; - border-left: 8px solid transparent; - border-right: 8px solid transparent; - border-top: 8px solid $color-black; - } - } - - .last-delivery-icon:hover { - .tooltip { - display: block; - } - } - } - - .dropdown { - position: absolute; - max-height: 30rem; - overflow-y: auto; - background-color: $color-white; - border-radius: $br4; - box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25); - z-index: 12; - top: 30px; - left: -151px; - width: 155px; - - hr { - margin: 0; - border-color: $color-gray-10; - } - - li { - display: flex; - align-items: center; - color: $color-gray-60; - cursor: pointer; - font-size: $fs14; - height: 31px; - padding: 5px 16px; - - &.title { - font-weight: $fw600; - cursor: default; - } - - &:hover { - background-color: $color-primary-lighter; - } - } - } -} - -.dashboard-team-settings { - .team-settings { - display: flex; - justify-content: center; - margin-top: 16px; - - svg { - width: 20px; - height: 20px; - } - - .horizontal-blocks { - display: flex; - max-width: 1010px; - justify-content: space-between; - width: 100%; - } - - .block { - display: flex; - max-width: 324px; - width: 324px; - background-color: $color-white; - flex-direction: column; - padding: 12px; - - .label { - font-size: $fs13; - color: $color-gray-30; - } - } - - .info-block { - position: relative; - - .name { - margin-top: 10px; - font-size: $fs24; - color: $color-black; - @include text-ellipsis; - margin-right: 90px; - } - - .icon { - position: absolute; - padding: 15px; - width: 100px; - height: 100px; - right: 0px; - top: 0px; - - img { - border-radius: 50%; - width: 70px; - height: 70px; - } - - .update-overlay { - opacity: 0; - cursor: pointer; - position: absolute; - display: flex; - justify-content: center; - align-items: center; - width: 71px; - height: 71px; - border-radius: 50%; - color: $color-white; - background: $color-primary-dark; - z-index: 14; - - svg { - fill: $color-white; - } - } - - &:hover { - .update-overlay { - opacity: 1; - width: 72px; - height: 72px; - top: 14px; - left: 14px; - } - } - } - } - - .owner-block { - img { - width: 30px; - height: 30px; - border-radius: 50%; - } - - svg { - width: 12px; - height: 12px; - fill: $color-black; - } - - .owner { - margin-top: 5px; - display: flex; - align-items: center; - color: $color-black; - .icon { - margin-right: 12px; - } - } - - .summary { - margin-top: 5px; - color: $color-black; - .icon { - padding: 0px 10px; - margin-right: 12px; - } - } - } - - .stats-block { - svg { - fill: $color-black; - } - - .projects, - .files { - margin-top: 7px; - display: flex; - align-items: center; - color: $color-black; - - .icon { - display: flex; - align-items: center; - padding: 0px 2px; - margin-right: 14px; - } - } - } - } -} - -.dashboard-team-webhooks { - display: flex; - flex-direction: column; - align-items: center; - - .webhooks-hero-container { - max-width: 1000px; - width: 100%; - display: flex; - flex-direction: column; - - .upload-button { - width: 100px; - } - - .btn-secondary { - margin-left: 10px; - } - } - - .webhooks-hero { - font-size: $fs14; - - padding: $size-6; - background-color: $color-white; - margin-top: $size-6; - display: flex; - justify-content: space-between; - - .banner { - background-color: unset; - - display: flex; - - .icon { - display: flex; - align-items: center; - padding-left: 0px; - padding-right: 10px; - svg { - fill: $color-info; - } - } - } - - .desc { - h2 { - margin-bottom: $size-4; - color: $color-black; - } - width: 80%; - color: $color-gray-40; - p { - font-size: $fs16; - } - } - - .btn-primary { - flex-shrink: 0; - } - } - - .webhooks-empty { - text-align: center; - max-width: 1000px; - width: 100%; - padding: $size-6; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - border: 1px dashed $color-gray-20; - color: $color-gray-40; - margin-top: 12px; - min-height: 136px; - } -} - -.webhooks-modal { - .action-buttons { - gap: 10px; - - .cancel-button { - border: 1px solid $color-gray-30; - background: $color-canvas; - border-radius: $br3; - padding: 0.5rem 1rem; - cursor: pointer; - margin-right: 8px; - - &:hover { - background: $color-gray-20; - } - } - } - .input-checkbox label { - font-size: $fs14; - color: $color-black; - } - - .explain { - font-size: $fs12; - color: $color-gray-40; - } -} diff --git a/frontend/resources/styles/main/partials/dashboard.scss b/frontend/resources/styles/main/partials/dashboard.scss deleted file mode 100644 index f48e89e57d..0000000000 --- a/frontend/resources/styles/main/partials/dashboard.scss +++ /dev/null @@ -1,638 +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 - -.team-hero { - display: flex; - position: relative; - border: 2px solid $color-gray-10; - border-radius: $br8; - padding: 20px; - margin: 0 1rem 0 21px; - height: 154px; - - .text { - flex-grow: 1; - padding-left: 20px; - .title { - font-size: $fs24; - font-weight: $fw700; - color: $color-black; - } - .info { - span { - color: $color-gray-30; - display: block; - } - padding-top: 10px; - } - } - .close { - position: absolute; - top: 20px; - right: 20px; - background-color: transparent; - border: none; - cursor: pointer; - svg { - transform: rotate(45deg); - width: 16px; - height: 16px; - } - } - .invite { - align-self: flex-end; - height: 40px; - font-family: "worksans", sans-serif; - width: 180px; - } - img { - width: 274px; - margin-bottom: -19px; - @media (max-width: 1200px) { - display: none; - width: 0; - } - } -} - -.hero-projects { - display: grid; - grid-template-columns: 1fr 1fr; - grid-gap: 30px; - margin: 0 1rem 1rem 1.2rem; - .tutorial, - .walkthrough { - display: grid; - grid-template-columns: 1fr 1fr; - position: relative; - border: 2px solid $color-gray-10; - border-radius: $br8; - min-height: 211px; - - .thumbnail { - border-top-left-radius: $br6; - border-bottom-left-radius: $br6; - padding: 30px; - display: block; - background-color: #e0e4e9; - } - - .text { - padding: 30px; - .title { - color: $color-black; - font-size: $fs24; - font-weight: $fw700; - margin-bottom: 8px; - } - .info { - color: $color-gray-50; - margin-bottom: 20px; - font-size: $fs14; - } - } - .action { - font-family: "worksans", sans-serif; - width: 180px; - height: 40px; - } - .close { - position: absolute; - top: 0; - right: 0; - width: $size-5; - cursor: pointer; - display: flex; - margin: 20px; - justify-content: center; - align-items: center; - background-color: transparent; - border: none; - .icon { - svg { - fill: $color-gray-30; - height: 16px; - width: 16px; - transform: rotate(45deg); - &:hover { - fill: $color-primary; - } - } - } - } - - @media (max-width: 1846px) { - grid-template-columns: 190px 1fr; - } - @media (max-width: 1526px) { - grid-template-columns: 1fr; - .img { - display: none; - width: 0; - } - } - } - .walkthrough { - .thumbnail { - background-image: url("/images/walkthrough-cover.png"); - background-position: center; - background-repeat: no-repeat; - background-size: cover; - } - } - .tutorial { - .thumbnail { - background-image: url("/images/hands-on-tutorial.png"); - background-position: center; - background-repeat: no-repeat; - background-size: cover; - } - .loader { - display: flex; - svg#loader-pencil { - width: 31px; - } - } - } -} - -.dashboard-container { - background-color: $color-dashboard; - flex: 1 0 0; - margin-right: $size-4; - overflow-y: auto; - width: 100%; - &.dashboard-projects { - user-select: none; - } - &.no-bg { - background-color: transparent; - } - &.dashboard-shared { - width: calc(100vw - 320px); - margin-right: 50px; - } - - &.search { - margin-top: 10px; - } -} - -.dashboard-project-row { - margin-bottom: $size-5; - position: relative; - - .project { - align-items: center; - background: $color-white; - border-radius: $br3; - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - margin-top: $size-4; - padding: $size-2 $size-2 $size-2 $size-4; - width: 99%; - max-height: 40px; - gap: $size-2; - .project-name-wrapper { - display: flex; - align-items: center; - justify-content: flex-start; - min-height: 32px; - margin-left: $size-2; - } - .show-more { - align-items: center; - color: $color-gray-30; - display: flex; - font-size: $fs14; - justify-content: space-between; - cursor: pointer; - background-color: transparent; - border: none; - .placeholder-icon { - transform: rotate(-90deg); - margin-left: 10px; - svg { - height: 14px; - width: 14px; - fill: $color-gray-30; - } - } - &:hover { - color: $color-primary-dark; - svg { - fill: $color-primary-dark; - } - } - } - - .btn-secondary { - border: none; - padding: $size-2; - } - - h2 { - cursor: pointer; - font-size: $fs18; - line-height: $lh-088; // Original value was 1rem = 16px; 16px/18px = 88.88888% => $lh-088 - font-weight: $fw600; - color: $color-black; - margin-right: $size-1; - } - - .edit-wrapper { - margin-right: $size-4; - } - - .info { - font-size: $fs14; - line-height: $lh-115; // Original value was 1rem = 16px; 16px/14px = 114.285714286% => $lh-115 (rounded) - font-weight: $fw400; - color: $color-gray-60; - margin-left: 0.75rem; - @media (max-width: 760px) { - display: none; - } - } - - .project-actions { - display: flex; - opacity: 0; - margin-left: $size-6; - - .btn-small { - height: 32px; - margin: 0 $size-2; - width: 32px; - } - } - - .pin-icon { - cursor: pointer; - display: flex; - align-items: center; - margin-right: 14px; - background-color: transparent; - border: none; - svg { - width: 15px; - height: 15px; - fill: $color-gray-20; - } - - &.active { - svg { - fill: $color-gray-50; - } - } - } - } - - &:hover, - &:focus, - &:focus-within { - .project-actions { - opacity: 1; - } - } - - .show-more { - align-items: center; - color: $color-gray-30; - display: flex; - font-size: $fs14; - justify-content: space-between; - cursor: pointer; - background-color: transparent; - border: none; - position: absolute; - top: 9px; - right: 53px; - .placeholder-icon { - transform: rotate(-90deg); - margin-left: 10px; - svg { - height: 14px; - width: 14px; - fill: $color-gray-30; - } - } - &:hover { - color: $color-primary-dark; - svg { - fill: $color-primary-dark; - } - } - } -} - -.recent-files-row-title-info { - color: $color-gray-60; - line-height: $lh-115; // Original value was 1rem = 16px; 16px/14px = 114.285714286% => $lh-115 - font-size: $fs14; - font-weight: $fw400; - @media (max-width: 880px) { - display: none; - } -} - -.dashboard-table { - display: flex; - flex-direction: column; - align-items: center; - margin-top: 20px; - font-size: $fs16; - - &.team-members { - margin-bottom: 52px; - } - - &.invitations { - .table-row { - display: grid; - grid-template-columns: 43% 1fr 109px 12px; - } - } - - .table-header { - display: grid; - grid-template-columns: 43% 1fr 109px 12px; - max-width: 1040px; - background-color: $color-white; - color: $color-gray-30; - width: 100%; - height: 40px; - padding: 0px 16px; - } - - .table-rows { - display: flex; - flex-direction: column; - max-width: 1040px; - width: 100%; - margin-top: 16px; - color: $color-black; - } - - .table-row { - display: flex; - width: 100%; - height: 45px; - align-items: center; - padding: 0px 16px; - } - - .table-field { - display: flex; - align-items: center; - - .icon { - padding-left: 10px; - cursor: pointer; - } - } - - svg { - width: 10px; - height: 10px; - fill: $color-black; - } -} - -.edit-wrapper { - border: 1px solid $color-gray-10; - border-radius: $br3; - display: flex; - padding-right: $size-5; - position: relative; - - input.element-title { - border: 0; - height: 30px; - padding: 5px; - margin: 0; - width: 100%; - background-color: $color-white; - } - - .close { - cursor: pointer; - position: absolute; - - top: 1px; - right: 2px; - - svg { - fill: $color-gray-30; - height: 15px; - transform: rotate(45deg) translateY(7px); - width: 15px; - margin: 0; - } - } -} - -.import-file-btn { - align-items: center; - display: flex; - flex-direction: column; - height: 2rem; - justify-content: center; - overflow: hidden; - padding: 4px; - width: 2rem; - - background: none; - border: 1px solid $color-gray-20; - border-radius: $br2; - cursor: pointer; - transition: all 0.4s; - margin-left: 1rem; - - &:hover { - background: $color-primary; - } - - svg { - width: 16px; - height: 16px; - } -} - -.dashboard-templates-section { - position: absolute; - display: flex; - flex-direction: column; - justify-content: flex-end; - bottom: 0; - width: 100%; - height: 228px; - transition: bottom 300ms; - pointer-events: none; - &.collapsed { - bottom: -228px; - transition: bottom 300ms; - } - - .title { - pointer-events: all; - width: fit-content; - top: -56px; - right: -28px; - text-align: right; - height: 56px; - position: absolute; - button { - border: none; - cursor: pointer; - height: 58px; - display: inline-flex; - align-items: center; - border-top: 2px solid #e4e4e4; - border-left: 2px solid #e4e4e4; - border-right: 2px solid #e4e4e4; - border-top-left-radius: $br10; - border-top-right-radius: $br10; - margin-right: 30px; - background-color: $color-white; - position: relative; - z-index: 1; - span { - display: inline-block; - vertical-align: middle; - line-height: $lh-normal; - font-size: $fs18; - font-weight: $fw600; - color: $color-black; - margin-left: 18px; - margin-right: 10px; - &.icon { - margin-left: 10px; - margin-right: 16px; - } - } - svg { - width: 12px; - height: 12px; - } - } - } - - .button { - position: absolute; - top: 133px; - border: 2px solid #e0e4e9; - border-radius: 50%; - text-align: center; - width: 35px; - height: 35px; - cursor: pointer; - background-color: $color-white; - display: flex; - align-items: center; - justify-content: center; - pointer-events: all; - svg { - width: 12px; - height: 12px; - } - - &.left { - left: 0; - margin-left: 43px; - } - - &.right { - right: 0; - margin-right: 43px; - } - - &:hover { - border: 2px solid $color-primary; - } - } - - .content { - pointer-events: all; - background-color: $color-white; - width: 200%; - height: 229px; - border-top: 2px solid #e4e4e4; - border-left: 2px solid #e4e4e4; - margin-left: 5px; - position: absolute; - - .card-container { - width: 275px; - margin-top: 20px; - display: inline-block; - text-align: center; - vertical-align: top; - background-color: transparent; - border: none; - padding: 0; - } - - .template-card { - display: inline-block; - width: 255px; - font-size: $fs16; - color: #181a22; - cursor: pointer; - .img-container { - width: 100%; - height: 135px; - margin-bottom: 15px; - border-radius: $br5; - border: 2px solid #e0e4e9; - display: flex; - justify-content: center; - flex-direction: column; - } - - .card-name { - padding: 0 5px; - display: flex; - justify-content: space-between; - height: 23px; - svg { - width: 16px; - height: 16px; - } - span { - font-weight: $fw500; - font-size: $fs14; - } - } - - .template-link { - border: 2px solid transparent; - margin: 30px; - padding: 32px 0; - } - - .template-link-title { - font-size: $fs14; - font-weight: $fw600; - color: $color-gray-60; - } - - .template-link-text { - font-size: $fs12; - margin-top: $size-2; - color: $color-gray-50; - } - - &:hover { - .img-container { - border: 2px solid $color-primary; - } - } - } - } -} diff --git a/frontend/resources/styles/main/partials/modal.scss b/frontend/resources/styles/main/partials/modal.scss deleted file mode 100644 index f3ca1df159..0000000000 --- a/frontend/resources/styles/main/partials/modal.scss +++ /dev/null @@ -1,1930 +0,0 @@ -.modal-overlay { - align-items: center; - background-color: $color-dark-bg; - display: flex; - justify-content: center; - position: fixed; - height: 100%; - left: 0; - top: 0; - width: 100%; - z-index: 1000; - - &.transparent { - background-color: rgba($color-white, 0); - } -} - -.modal, -.generic-modal { - background-color: $color-white; - width: 565px; - display: flex; - position: relative; - - .close { - cursor: pointer; - position: absolute; - right: 16px; - top: 16px; - svg { - width: 16px; - height: 16px; - transform: rotate(45deg); - } - } - - .modal-content { - display: flex; - flex-grow: 1; - flex-direction: column; - padding: 60px 100px; - } - - .button-row { - display: flex; - justify-content: space-between; - - > button { - font-size: $fs12; - } - > button:not(:first-child) { - margin-left: 25px; - } - } -} - -// NEW GEN MODALS - -.modal-container { - border-radius: $br5; - display: flex; - flex-direction: column; - width: 448px; - background-color: $color-white; - max-height: 700px; - - .modal-header { - align-items: center; - background-color: $color-white; - border-radius: $br8 $br8 0 0; - color: $color-black; - display: flex; - height: 63px; - justify-content: space-between; - } - - .modal-header-title { - display: flex; - align-items: center; - font-size: $fs18; - padding-left: 16px; - - h2 { - font-size: $fs18; - font-weight: $fw400; - margin: 0; - } - } - - .modal-close-button { - align-items: center; - cursor: pointer; - display: flex; - height: 30px; - justify-content: center; - margin-right: 16px; - - svg { - transform: rotate(45deg); - width: 16px; - height: 16px; - } - } - - .component-list { - max-height: 408px; - overflow-y: scroll; - } - - .modal-item-element { - display: flex; - padding-bottom: 3px; - margin-left: 10px; - font-size: $fs14; - color: $color-info; - - .modal-component-icon { - margin-right: 16px; - display: flex; - justify-content: center; - align-items: center; - svg { - width: 16px; - height: 16px; - fill: $color-info; - } - } - } - - .modal-content { - display: flex; - flex-direction: column; - padding: 32px; - border-top: 1px solid $color-gray-10; - h3 { - color: $color-gray-40; - font-size: $fs16; - font-weight: $fw400; - } - &.delete-shared { - padding: 15px 32px; - .modal-item-element { - font-size: $fs16; - } - } - } - - .modal-footer { - display: flex; - height: 63px; - padding: 0px 18px; - border-top: 1px solid $color-gray-10; - - .action-buttons { - display: flex; - width: 100%; - height: 100%; - justify-content: flex-end; - // border: 1px solid red; - align-items: center; - - input { - margin-bottom: 0px; - } - } - } - - .btn-disabled { - opacity: 0.5; - } -} - -.change-email-modal { - h2 { - font-size: $fs18; - } - - h3 { - margin-bottom: 15px; - } - - .modal-footer .action-buttons { - justify-content: space-around; - gap: 15px; - } - - .fields-container { - margin-top: 1rem; - } -} - -.confirm-dialog, -.alert-dialog { - background-color: $color-white; - - p { - font-size: $fs14; - color: $color-gray-40; - } - - .action-buttons { - display: flex; - flex-direction: row; - width: 100%; - font-size: $fs14; - } - - .cancel-button { - border: 1px solid $color-gray-30; - background: $color-canvas; - border-radius: $br3; - padding: 0.5rem 1rem; - cursor: pointer; - margin-right: 8px; - - &:hover { - background: $color-gray-20; - } - } - - .accept-button { - border-radius: $br3; - cursor: pointer; - padding: 0.5rem 1rem; - - &.danger { - background: $color-danger; - border: 1px solid $color-danger; - color: $color-white; - &:hover { - background: $color-danger-dark; - } - } - - &.primary { - background: $color-primary; - border: 1px solid $color-primary; - color: $color-black; - - &:hover { - background: $color-primary-dark; - } - } - } -} - -.import-dialog, -.export-dialog, -.export-multiple-dialog { - background-color: $color-white; - border: 1px solid $color-gray-20; - width: 30rem; - min-height: 14rem; - - &.empty { - width: 39rem; - } - - p { - font-size: $fs14; - color: $color-black; - } - - .detail { - font-size: $fs12; - } - - .detail, - .explain { - padding: 0 1rem; - } - - .cancel-button { - border: 1px solid $color-gray-20; - background: $color-white; - border-radius: $br3; - padding: 0.5rem 2.25rem; - cursor: pointer; - margin-right: 18px; - - &:hover { - background: $color-gray-20; - } - } - - .accept-button { - background: $color-primary; - border-radius: $br3; - border: 1px solid $color-primary; - color: $color-black; - cursor: pointer; - padding: 0.5rem 2.25rem; - - &[disabled] { - border: 1px solid #e3e3e3; - } - - &:hover { - background: $color-primary-dark; - } - } - - .modal-content { - flex: 1; - overflow-y: auto; - max-height: calc(65vh); - } - - .modal-header-title { - padding-left: 2rem; - - h2 { - font-size: $fs18; - } - } - - .modal-content { - padding: 1rem; - } - - .file-entry { - margin: 0.75rem 1rem; - user-select: none; - - &.editable:hover { - .file-name-label { - background-color: $color-primary-lighter; - } - .edit-entry-buttons { - display: flex; - background-color: $color-primary-lighter; - } - } - } - - .file-icon { - width: 18px; - display: flex; - align-items: center; - justify-content: center; - margin-right: 1rem; - - svg { - width: 18px; - height: 18px; - } - - #loader-pencil { - fill: $color-black; - } - - .icon-tick { - fill: $color-success; - } - - .icon-msg-warning { - fill: $color-warning; - } - - .icon-close { - transform: rotate(45deg); - fill: $color-danger; - } - } - - .file-name { - display: flex; - align-items: center; - color: $color-black; - - .file-name-label { - align-items: center; - flex: 1; - height: 2rem; - margin-left: -0.25rem; - overflow: hidden; - padding-left: 0.25rem; - padding-top: 0.25rem; - text-overflow: ellipsis; - white-space: nowrap; - - .icon-library { - height: 14px; - width: 14px; - fill: $color-gray-20; - margin-left: 0.5rem; - padding-top: 1px; - } - } - - .file-name-edit { - width: 100%; - - input { - margin: 0; - border: none; - border-bottom: 1px solid $color-gray-20; - height: 2rem; - width: 100%; - } - } - } - - .feedback-banner { - color: $color-black; - background: $color-success-lighter; - height: 40px; - display: flex; - align-items: center; - margin: 0 1rem; - - .message { - padding: 0 1rem; - font-size: $fs12; - } - - .icon { - background: $color-success; - height: 40px; - width: 40px; - display: flex; - align-items: center; - justify-content: center; - - svg { - width: 20px; - height: 20px; - fill: $color-white; - } - } - - &.warning { - background: $color-warning-lighter; - .icon { - background: $color-warning; - } - } - } - - .error-message { - margin: 0 2rem; - color: $color-danger; - font-size: $fs12; - font-style: italic; - } - - .progress-message { - margin: 0 2rem; - color: $color-info; - font-size: $fs12; - font-style: italic; - } - - .linked-libraries { - display: flex; - flex-wrap: wrap; - margin-left: 2rem; - - .icon-chain, - .icon-unchain { - width: 10px; - height: 10px; - margin-right: 2px; - } - - .linked-library-tag { - font-size: $fs10; - color: $color-black; - background: #d8f7fe; - border-radius: $br3; - padding: 2px 4px; - display: flex; - align-items: center; - margin: 0.25rem; - - &.error { - background-color: $color-danger-lighter; - } - } - } - - .edit-entry-buttons { - display: flex; - flex-direction: row; - font-size: $fs14; - height: 2rem; - display: none; - - button { - border: none; - background: none; - display: block; - cursor: pointer; - - svg { - width: 14px; - height: 14px; - } - - &:hover svg { - fill: $color-primary; - } - } - } -} - -.export-dialog { - .export-option { - border-radius: $br4; - border: 1px solid $color-gray-10; - margin-bottom: 0.5rem; - - h3 { - font-weight: $fw700; - } - - h3, - p { - font-size: $fs12; - line-height: $lh-150; - margin: 0; - color: $color-black; - padding: 0; - } - - &.selected { - border: 1px solid $color-primary; - } - - &.table-row { - align-items: center; - display: flex; - flex-wrap: wrap; - height: 45px; - justify-content: space-between; - padding: 0px 0px; - width: 100%; - } - - .table-field { - flex-grow: 0; - padding: 0px 4px; - overflow: hidden; - text-overflow: ellipsis; - width: 50px; - - &.check { - width: 30px; - } - - &.scale { - flex-grow: 1; - width: 15%; - } - - &.name { - flex-grow: 1; - width: 40%; - } - } - } - - .option-container { - display: block; - position: relative; - padding-left: 40px; - padding-right: 1rem; - padding-top: 1rem; - padding-bottom: 1rem; - cursor: pointer; - user-select: none; - - input { - position: absolute; - opacity: 0; - cursor: pointer; - height: 0; - width: 0; - } - - .option-radio-check { - position: absolute; - top: 1rem; - left: 12px; - height: 18px; - width: 18px; - background-color: $color-white; - border: 1px solid $color-gray-10; - border-radius: 50%; - } - - &:hover input ~ .option-radio-check { - border-color: $color-primary; - } - - input:checked ~ .option-radio-check { - border-color: $color-primary; - background-color: $color-white; - } - - .option-radio-check:after { - content: ""; - position: absolute; - display: none; - } - - input:checked ~ .option-radio-check:after { - display: block; - background-color: $color-primary; - } - - .option-radio-check:after { - top: 3px; - left: 3px; - width: 10px; - height: 10px; - border-radius: 50%; - background: white; - } - } -} - -.libraries-dialog { - border-radius: $br5; - height: 664px; - width: 920px; - max-height: 100%; - - .modal-content { - display: flex; - flex-grow: 1; - flex-direction: column; - padding: 0; - } - - .libraries-header { - border-bottom: 1px solid $color-gray-20; - padding: 2rem 1rem 0.5rem 1rem; - display: flex; - justify-content: center; - - .header-item { - cursor: pointer; - color: $color-gray-40; - font-size: $fs14; - - &.active { - color: $color-gray-60; - border-bottom: 2px solid $color-primary; - } - - &:not(:first-child) { - margin-left: 3rem; - } - } - } - - .libraries-content { - display: flex; - justify-content: stretch; - align-items: stretch; - flex-grow: 1; - padding: 0 $size-4; - color: $color-gray-40; - - .section { - flex-basis: 0; - flex-grow: 1; - padding: $size-4 0; - display: flex; - flex-direction: column; - - &:not(:first-child) { - border-left: 1px solid $color-gray-20; - } - } - - .section-title { - color: $color-black; - font-size: $fs14; - padding: 0 $size-4; - font-weight: $fw500; - } - - .section-list { - flex-basis: 0; - flex-grow: 1; - padding: 0 $size-4; - overflow: auto; - - .section-list-item { - padding: $size-4 0; - border-bottom: 1px solid $color-gray-20; - position: relative; - - .item-name { - color: $color-gray-60; - font-size: $fs14; - margin-right: calc(4.5rem + 1rem); - } - - .item-contents { - color: $color-gray-40; - font-size: $fs12; - margin-right: calc(4.5rem + 1rem); - } - - .item-button { - cursor: pointer; - top: $size-4; - right: 0; - border: 1px solid $color-primary; - border-radius: $br2; - min-width: 4.5rem; - background: $color-primary; - color: $color-black; - padding: $size-2; - margin-bottom: 0; - position: absolute; - top: 1rem; - - &:hover { - color: $color-primary; - background: $color-black; - } - - &.item-update { - background: $color-warning; - border-color: $color-warning; - color: $color-black; - - &.disabled { - background: $color-gray-10; - border-color: $color-gray-10; - color: $color-black; - cursor: unset; - } - } - } - } - } - - .section-list-empty { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - padding: 0 $size-4; - flex-grow: 1; - - & svg { - fill: $color-gray-20; - height: 50px; - width: 50px; - margin-bottom: $size-4; - } - - & svg#loader-pencil { - fill: $color-gray-20; - height: 75px; - width: 75px; - margin-bottom: $size-4; - } - } - - .libraries-search { - border: 1px solid $color-gray-20; - margin: $size-4; - padding: $size-1 $size-2; - display: flex; - align-items: center; - - &:focus-within { - border-color: $color-primary; - } - - & .search-input { - border: none; - color: $color-gray-60; - font-size: $fs12; - margin: 0; - padding: 0; - flex-grow: 1; - - &:hover, - &:focus { - outline: none; - } - } - - & .search-icon { - display: flex; - align-items: center; - - svg { - fill: $color-gray-30; - height: 16px; - width: 16px; - } - - &.search-close { - transform: rotate(45deg); - cursor: pointer; - } - } - } - - .libraries-updates { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); - grid-gap: $size-5; - font-size: $fs12; - margin-top: $size-4; - - .libraries-updates-item { - display: flex; - align-items: center; - - &:not(:first-child) { - margin-top: $size-2; - } - - & svg { - background-color: $color-canvas; - border-radius: $br4; - border: 2px solid transparent; - height: 24px; - width: 24px; - min-height: 24px; - min-width: 24px; - margin-right: $size-2; - } - - & .color-bullet { - margin-right: $size-2; - } - - & .typography-sample { - margin-right: $size-2; - } - - & .name-block { - color: $color-gray-20; - width: calc(100% - 24px - #{$size-2}); - - &.ellipsis { - padding-left: calc(24px + #{$size-2}); - } - } - - & .item-name { - display: block; - margin: 0; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } - } - } - - .libraries-updates-see-all { - margin-top: $size-2; - direction: rtl; - & input { - border: none; - color: $color-info; - cursor: pointer; - margin-bottom: 0; - text-decoration: underline; - } - } - } -} - -//- ONBOARDING -.onboarding { - background-color: $color-white; - box-shadow: 0 10px 10px rgba(0, 0, 0, 0.2); - display: flex; - max-height: 710px; - min-height: 420px; - flex-direction: row; - font-family: "sourcesanspro", sans-serif; - min-width: 620px; - position: relative; - - .modal-left { - align-items: center; - background-color: $color-pink; - border-top-left-radius: $br5; - border-bottom-left-radius: $br5; - display: flex; - flex-shrink: 0; - justify-content: center; - overflow: hidden; - padding: $size-6; - width: 230px; - - &.welcome { - padding: 0; - } - } - - .modal-right { - border-top-right-radius: $br5; - border-bottom-right-radius: $br5; - display: flex; - flex-direction: column; - padding: $size-6; - - .modal-title h2 { - color: $color-black; - font-size: $fs24; - font-weight: $fw900; - } - - .release { - background-color: $color-primary; - color: $color-black; - font-size: $fs12; - font-weight: $fw700; - margin-top: $size-2; - padding: 2px $size-1; - width: max-content; - } - - .modal-content { - border: none; - padding: $size-5 0; - - p { - color: $color-black; - font-size: $fs16; - margin-top: $size-2; - } - } - - .modal-navigation { - align-items: center; - display: flex; - margin-top: auto; - - .skip { - cursor: pointer; - font-family: "worksans", sans-serif; - font-size: $fs12; - margin-left: $size-5; - - &:hover { - color: $color-black; - } - } - } - - .step-dots { - align-items: center; - display: flex; - margin-bottom: 0; - margin-left: auto; - - li { - background-color: $color-gray-10; - border-radius: 50%; - cursor: pointer; - height: $size-2; - margin-left: $size-2; - width: $size-2; - - &.current { - background-color: $color-primary; - } - } - } - } - - &.onboarding-v2 { - min-height: unset; - height: 100%; - min-width: 752px; - .modal-left { - background-color: $color-gray-50; - width: 297px; - } - - .welcome img { - width: 297px; - height: 464px; - position: absolute; - bottom: 0; - border-radius: $br5; - } - - .modal-right { - padding: 0; - } - - .release-container { - width: 100%; - text-align: right; - position: relative; - height: 2rem; - } - - .release { - background-color: $color-primary; - border-radius: $br4; - color: #1f1f1f; - padding: 4px $size-1; - display: inline-block; - margin-top: 1rem; - margin-right: 1rem; - } - - .right-content { - padding: $size-6; - - .modal-content { - padding: $size-1 0; - p { - margin-top: 0.4rem; - } - } - - .welcome-card { - display: flex; - color: $color-black; - margin-top: $size-5; - width: 90%; - - .title a { - font-weight: $fw700; - color: $color-black; - text-decoration: none; - &:hover { - color: $color-primary-dark; - } - } - - .description { - font-size: $fs12; - text-decoration: none; - text-transform: none; - } - - img { - width: 48px; - height: 48px; - margin-right: 6px; - } - } - } - - .modal-navigation { - width: 100%; - padding: 0 2rem 2rem 0; - - button { - margin-left: auto; - padding: 0 0.5rem; - } - } - } - - &.black { - .modal-left { - background-color: $color-black; - } - } - - button { - font-family: "worksans", sans-serif; - } - - &.feature { - .modal-left { - padding: 0; - - img { - border-top-left-radius: $br5; - border-bottom-left-radius: $br5; - height: 100%; - width: 115%; - } - } - } - - &.final { - // TODO: Juan revisa TODA esta parte - - padding: $size-5 0 0 0; - flex-direction: column; - - .modal-top { - padding: 40px 40px 0 40px; - color: $color-gray-60; - display: flex; - flex-direction: column; - align-items: center; - - h1 { - font-family: "worksans", sans-serif; - font-weight: $fw700; - font-size: $fs26; - margin-bottom: $size-3; - text-align: center; - } - p { - font-family: "worksans", sans-serif; - font-weight: $fw500; - font-size: $fs18; - text-align: center; - } - } - - .modal-columns { - display: flex; - margin: 17px; - - .modal-left { - background-image: url("/images/on-solo.svg"); - background-position: left top; - background-size: 11%; - } - - .modal-left:hover { - background-image: url("/images/on-solo-hover.svg"); - background-size: 15%; - } - - .modal-right { - background-image: url("/images/on-teamup.svg"); - background-position: right top; - background-size: 28%; - } - - .modal-right:hover { - background-image: url("/images/on-teamup-hover.svg"); - background-size: 32%; - } - - .modal-right, - .modal-left { - background-repeat: no-repeat; - border-radius: $br5; - transition: all ease 0.3s; - &:hover { - background-color: $color-primary; - } - } - } - - .modal-left { - margin-right: 35px; - } - - .modal-left, - .modal-right { - justify-content: center; - align-items: center; - background-color: $color-white; - color: $color-black; - flex: 1; - flex-direction: column; - // overflow: visible; - // padding: $size-6 40px; - text-align: center; - - border: 1px solid $color-gray-10; - border-radius: $br2; - min-height: 180px; - width: 233px; - cursor: pointer; - - h2 { - font-weight: $fw700; - margin-bottom: $size-5; - font-size: $fs24; - } - - p { - font-size: $fs14; - } - - img { - box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.25); - border-radius: $br5; - margin-bottom: $size-6; - margin-top: -90px; - width: 150px; - } - } - } - - &.newsletter { - padding: $size-5 0 0 0; - flex-direction: column; - min-width: 623px; - .modal-top { - padding: 87px 40px 0 40px; - color: $color-gray-60; - display: flex; - flex-direction: column; - - h1 { - font-family: sourcesanspro; - font-weight: $fw700; - font-size: $fs36; - margin-bottom: 0.75rem; - } - - p { - font-family: sourcesanspro; - font-weight: $fw500; - font-size: $fs16; - margin-bottom: 1.5rem; - } - } - - .modal-bottom { - margin: 0 32px; - padding: 32px 0; - color: $color-gray-60; - display: flex; - flex-direction: column; - border-top: 1px solid $color-gray-10; - - .input-checkbox { - margin-bottom: 23px; - - label { - font-family: "worksans", sans-serif; - text-align: left; - color: $color-black; - font-size: $fs12; - } - } - - p { - font-family: "worksans", sans-serif; - text-align: left; - color: $color-gray-30; - } - } - - .modal-footer { - padding: 17px; - display: flex; - justify-content: flex-end; - - .btn-secondary { - margin-right: 16px; - } - } - - .deco.top { - width: 183px; - top: -106px; - left: 213px; - } - } -} - -.deco { - left: -10px; - position: absolute; - top: -18px; - width: 60px; - - &.left { - left: -50px; - top: 100px; - color: $color-primary; - } - - &.right { - left: auto; - color: $color-primary; - top: 100px; - right: -40px; - } - - &.square { - top: -18px; - left: 631px; - width: 24px; - height: 24px; - color: $color-primary; - } - - &.circle { - top: -16px; - left: 21px; - width: 24px; - height: 24px; - color: $color-primary; - } - - &.line1 { - top: 110px; - left: -12px; - width: 16px; - height: 49px; - color: $color-primary; - } - - &.line2 { - top: 440px; - left: 733px; - width: 46px; - height: 43px; - color: $color-primary; - } - - &.top { - width: 183px; - top: -106px; - left: 161px; - } - - &.newsletter-right { - left: 586px; - top: 50px; - } - - &.newsletter-left { - width: 26px; - left: -15px; - top: -15px; - } -} - -.relnotes .onboarding { - height: 420px; -} - -.onboarding-templates { - position: fixed; - top: 0; - right: 0; - width: 348px; - height: 100vh; - - .modal-close-button { - width: 34px; - height: 34px; - margin-right: 13px; - margin-top: 13px; - svg { - width: 24px; - height: 24px; - } - } - - .modal-header { - height: unset; - border-radius: unset; - justify-content: flex-end; - } - - .modal-content { - border: 0px; - padding: 0px 25px; - background-color: $color-white; - flex-grow: 1; - - p, - h3 { - color: $color-gray-60; - text-align: center; - } - - h3 { - font-size: $fs18; - font-weight: $fw700; - } - - p { - font-size: $fs16; - } - - .templates { - display: flex; - flex-direction: column; - align-items: center; - margin-top: 8%; - } - - .template-item { - width: 275px; - border: 1px solid $color-gray-10; - - display: flex; - flex-direction: column; - text-align: left; - border-radius: $br3; - - &:not(:last-child) { - margin-bottom: 22px; - } - } - - .template-item-content { - // height: 144px; - flex-grow: 1; - - img { - border-radius: $br3 $br3 0 0; - } - } - - .template-item-title { - padding: 6px 12px; - height: 64px; - border-top: 1px solid $color-gray-10; - - .label { - color: $color-black; - padding: 0px 4px; - font-size: $fs16; - display: flex; - } - - .action { - color: $color-primary-dark; - cursor: pointer; - font-size: $fs14; - font-weight: $fw600; - display: flex; - justify-content: flex-end; - margin-top: $size-2; - } - } - } -} - -.onboarding-team, -.onboarding-team-members { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - min-width: 620px; - min-height: 420px; - width: 816px; - height: 488px; - position: relative; - .team-right { - width: 361px; - height: 100%; - background-color: $color-gray-50; - padding: 64px; - display: flex; - flex-direction: column; - justify-content: space-between; - border-radius: 0 $br5 $br5 0; - .subtitle { - font-weight: $fw700; - font-size: $fs20; - color: $color-gray-10; - text-transform: uppercase; - margin-bottom: 8px; - } - .info { - font-size: $fs14; - color: $color-gray-20; - margin-bottom: 32px; - } - .team-features { - flex-grow: 1; - display: flex; - justify-content: space-between; - flex-direction: column; - margin-bottom: 0; - .feature { - display: flex; - align-items: center; - .icon { - display: flex; - align-items: center; - justify-content: center; - background: #31efb8; - min-width: 28px; - width: 28px; - height: 28px; - border-radius: 50%; - - svg { - height: 16px; - width: 16px; - fill: $color-black; - } - } - .feature-txt { - margin: 0; - margin-left: 13px; - color: $color-white; - font-size: $fs14; - display: flex; - align-items: center; - } - } - } - } - .team-left { - width: 455px; - height: 100%; - padding: 64px; - display: flex; - flex-direction: column; - justify-content: space-between; - .title { - font-size: $fs26; - font-weight: $fw700; - color: $color-gray-60; - margin-bottom: 8px; - } - .info { - font-size: $fs14; - color: $color-black; - } - form { - flex-grow: 1; - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: flex-start; - row-gap: 1.5rem; - .custom-input { - width: 100%; - } - .btn-disabled { - color: $color-gray-40; - opacity: 100%; - } - .invite-row { - width: 100%; - .role-wrapper { - display: flex; - justify-content: space-between; - width: 100%; - margin-bottom: 8px; - .rol { - display: flex; - justify-content: center; - align-items: center; - font-size: $fs12; - color: $color-gray-60; - } - } - .custom-input { - height: 96px; - } - .custom-select { - width: 180px; - overflow: hidden; - justify-content: normal; - select { - height: auto; - } - } - - svg { - width: 12px; - height: 12px; - fill: $color-gray-20; - } - } - .buttons { - margin-top: 32px; - width: 100%; - display: grid; - grid-template-columns: 1fr auto; - gap: 8px; - .btn-primary { - max-width: 250px; - } - } - } - .skip-action { - background-color: transparent; - border: none; - text-align: left; - font-size: $fs12; - color: $color-gray-30; - } - } - - .paginator { - width: 32px; - height: 24px; - background-color: $color-gray-60; - position: absolute; - top: 16px; - right: 16px; - display: flex; - justify-content: center; - align-items: center; - font-size: $fs12; - } - .deco { - &.line2 { - top: 422px; - left: 798px; - } - &.square { - top: -14px; - left: 483px; - } - &.line1 { - top: 357px; - } - } -} - -.onboarding-team-members { - .team-left { - form { - align-items: stretch; - margin-top: 24px; - row-gap: normal; - .invite-row { - .custom-input { - width: 100%; - min-height: 80px; - height: fit-content; - max-height: 176px; - overflow-y: hidden; - input { - &.no-padding { - padding-top: 12px; - } - min-height: 40px; - } - .selected-items { - gap: 7px; - padding: 7px; - max-height: 132px; - overflow-y: scroll; - .selected-item { - .around { - height: 24px; - display: flex; - align-items: center; - justify-content: flex-start; - width: fit-content; - .icon { - display: flex; - align-items: center; - justify-content: center; - } - } - } - } - } - } - .buttons { - margin: 32px 0 16px 0; - button { - height: auto; - } - input { - white-space: break-spaces; - word-break: break-word; - height: fit-content; - margin: 0; - padding: 5px 10px; - min-height: 40px; - max-height: 90px; - } - } - } - } -} - -.questions-form { - .modal-overlay { - z-index: 2001; - } - - .modal-container { - display: flex; - flex-direction: row; - justify-content: center; - - .af-form { - --primary-color: #00c38b; - --input-background-color: #ffffff; - --label-font-size: $fs16; - --field-error-font-color: #e65244; - --message-success-font-color: #49d793; - --message-fail-font-color: #e65244; - --invalid-field-border-color: #e65244; - --dropdown-background-color: #ffffff; - --primary-font-color: #000; - --input-border-color: rgb(224, 230, 240); - --input-border-radius: $br3; - --button-border-radius: $br3; - --message-border-radius: $br3; - --checkbox-border-radius: $br3; - --dropdown-option-background-color: rgba(0, 195, 139, 1); - --dropdown-option-active-background-color: rgba(0, 138, 98, 1); - --invalid-field-background-color: rgba(238.51780000000002, 205.7178, 204.11780000000002, 1); - --message-fail-background-color: rgba(238.51780000000002, 205.7178, 204.11780000000002, 1); - --message-success-background-color: rgba(171, 232, 197, 1); - } - } -} - -// Nudge modal - -.nudge-modal-overlay { - display: flex; - align-items: center; - justify-content: center; - position: fixed; - left: calc(50vw - 107px); - top: calc(50vh - 57px); - height: 110px; - width: 215px; - padding: 8px 20px; - background-color: $color-white; - box-shadow: 0px 2px 8px 0px rgb(0 0 0 / 20%); - z-index: 1000; - - &.transparent { - background-color: rgba($color-white, 0); - } - - .nudge-modal-container { - display: flex; - flex-direction: column; - justify-content: space-around; - height: 100%; - width: 100%; - - .nudge-modal-header { - display: flex; - justify-content: space-between; - margin-bottom: 7px; - - .modal-close-button { - display: flex; - justify-content: center; - align-items: center; - background-color: transparent; - border: none; - cursor: pointer; - - svg { - height: 12px; - width: 12px; - transform: rotate(45deg); - } - } - - .nudge-modal-title { - padding: 0; - margin: 0; - color: $color-black; - font-size: $fs12; - } - } - - .nudge-modal-body { - display: flex; - justify-content: space-between; - - .nudge-subtitle { - margin: 0; - } - - input { - width: 72px; - background-color: transparent; - border: none; - border-bottom: 1px solid $color-black; - margin-bottom: 12px; - - &:active, - &:focus, - &:hover { - border-bottom: 1px solid $color-primary; - } - } - } - } -} - -.remove-graphics-dialog { - .modal-content { - padding-top: 16px; - } - - h2 { - font-size: $fs18; - } - - p { - font-size: $fs12; - color: $color-gray-30; - - &:last-child { - margin-bottom: 0; - } - - &.progress-message { - color: $color-info; - } - - &.error-message { - color: $color-black; - - & svg { - width: 16px; - height: 16px; - fill: $color-danger; - margin-right: 0.5rem; - position: relative; - bottom: -4px; - transform: rotate(45deg); - } - } - } - - .close { - border: 1px solid $color-gray-30; - background: $color-canvas; - border-radius: $br3; - padding: 0.5rem 1rem; - cursor: pointer; - margin-right: 8px; - - &:hover { - background: $color-gray-20; - } - } - - .button-primary { - background: $color-primary; - border: 1px solid $color-primary; - border-radius: $br3; - color: $color-black; - cursor: pointer; - padding: 0.5rem 1rem; - - &:hover { - background: $color-primary-dark; - } - } - - .button-secondary { - border: 1px solid $color-gray-30; - background: $color-white; - border-radius: $br3; - padding: 0.5rem 1rem; - cursor: pointer; - margin-right: 8px; - - &:hover { - background: $color-gray-20; - } - } -} - -//- LOGIN -.login-register { - background-color: $color-white; - box-shadow: 0 10px 10px rgba(0, 0, 0, 0.2); - display: flex; - font-family: "worksans", sans-serif; - width: 472px; - height: 100%; - max-height: 700px; - position: relative; - overflow: auto; - position: relative; - - .title { - margin-left: 32px; - h2 { - font-size: $fs24; - color: $color-black; - line-height: $lh-150; // Original value was 2.25rem = 36px; 36px/24px = 150% => $lh-150 - letter-spacing: 0px; - margin: 0 30px 20px 0; - } - - .modal-close-button { - margin-top: 7px; - margin-right: 12px; - justify-content: right; - svg { - fill: $color-black; - } - } - } - - .modal-bottom { - margin: 0 32px; - color: #1f1f1f; - display: flex; - flex-direction: column; - - &.auth-content { - align-items: initial; - height: auto; - .links { - margin: 7px 0 0 0; - text-align: left; - } - } - } - - .modal-footer { - justify-content: center; - align-items: center; - - .terms-login { - position: relative; - bottom: 0; - span { - margin: 0 $size-2; - } - } - } - - .hint { - color: #b1b2b5; - } -} diff --git a/frontend/resources/styles/main/partials/share-link.scss b/frontend/resources/styles/main/partials/share-link.scss deleted file mode 100644 index 8b1d0225f0..0000000000 --- a/frontend/resources/styles/main/partials/share-link.scss +++ /dev/null @@ -1,243 +0,0 @@ -.share-modal { - background: none; - display: block; - top: 50px; - left: calc(100vw - 500px); - - .share-link-dialog { - width: 480px; - background-color: $color-white; - box-shadow: 0px 2px 8px 0px rgb(0 0 0 / 20%); - - .modal-content { - padding: 16px 32px; - &:first-child { - border-top: 0px; - padding: 0; - height: 50px; - display: flex; - justify-content: center; - } - - .title { - display: flex; - justify-content: space-between; - align-items: center; - height: 100%; - margin-left: 32px; - h2 { - font-size: $fs18; - color: $color-black; - } - - .modal-close-button { - margin-right: 16px; - } - } - - .share-link-section { - .custom-input { - display: flex; - flex-direction: row; - margin-bottom: 15px; - border: 1px solid $color-gray-20; - input { - padding: 0 0 0 15px; - border: none; - } - } - .hint-wrapper { - display: flex; - justify-content: space-between; - align-items: center; - .hint { - font-size: $fs12; - color: $color-gray-40; - } - .confirm-dialog { - display: flex; - flex-direction: column; - background-color: unset; - .actions { - display: flex; - justify-content: flex-end; - gap: 16px; - } - .description { - font-size: $fs12; - margin-bottom: 16px; - color: $color-black; - } - .btn-primary, - .btn-secondary, - .btn-warning, - .btn-danger { - width: 126px; - margin-bottom: 0px; - - &:not(:last-child) { - margin-right: 10px; - } - } - } - } - - label { - font-size: $fs12; - color: $color-black; - } - - .help-icon { - height: 40px; - width: 40px; - display: flex; - justify-content: center; - align-items: center; - cursor: pointer; - position: relative; - right: 0; - top: 0; - background-color: $color-gray-10; - border-left: 1px solid $color-gray-20; - svg { - fill: $color-gray-30; - } - &:hover { - background-color: $color-primary; - svg { - fill: $color-gray-60; - } - } - } - input { - margin: 0; - } - } - - &.ops-section { - .manage-permissions { - display: flex; - color: $color-primary-dark; - font-size: $fs12; - cursor: pointer; - .icon { - svg { - height: 16px; - width: 16px; - fill: $color-primary-dark; - } - } - .title { - margin-left: 8px; - } - } - .view-mode { - min-height: 34px; - .subtitle { - height: 32px; - } - .row { - display: flex; - justify-content: space-between; - align-items: center; - .count-pages { - font-size: $fs12; - color: $color-gray-30; - } - } - .current-tag { - font-size: $fs12; - color: $color-gray-30; - } - label { - color: $color-black; - } - } - .access-mode, - .inspect-mode { - display: grid; - grid-template-columns: auto 1fr; - .items { - display: flex; - justify-content: flex-end; - align-items: center; - } - } - .view-mode, - .access-mode, - .inspect-mode { - margin: 8px 0; - .subtitle { - display: flex; - justify-content: flex-start; - align-items: center; - color: $color-black; - font-size: $fs16; - .icon { - display: flex; - justify-content: center; - align-items: center; - margin-right: 10px; - svg { - height: 16px; - width: 16px; - } - } - } - .items { - .input-select { - background-image: url("/images/icons/arrow-down.svg"); - margin: 0; - padding-right: 28px; - border: 1px solid $color-gray-10; - max-width: 227px; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } - > .input-radio { - display: flex; - user-select: none; - margin-top: 0; - margin-bottom: 0; - label { - display: flex; - align-items: center; - color: $color-black; - max-width: 115px; - - &::before { - height: 16px; - width: 16px; - } - .hint { - margin-left: 5px; - color: $color-gray-30; - } - } - - &.disabled { - label { - color: $color-gray-30; - } - } - } - } - } - - .pages-selection { - border-top: 1px solid $color-gray-10; - border-bottom: 1px solid $color-gray-10; - padding-left: 20px; - max-height: 200px; - overflow-y: scroll; - user-select: none; - - label { - color: $color-black; - } - } - } - } - } -} diff --git a/frontend/resources/styles/main/partials/sidebar-align-options.scss b/frontend/resources/styles/main/partials/sidebar-align-options.scss deleted file mode 100644 index b792e060c2..0000000000 --- a/frontend/resources/styles/main/partials/sidebar-align-options.scss +++ /dev/null @@ -1,62 +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 - -.align-options { - display: flex; - border-bottom: solid 1px $color-gray-60; - height: 40px; - - .align-group { - padding: 0 $size-1; - display: flex; - justify-content: flex-start; - width: 50%; - - &:not(:last-child) { - border-right: solid 1px $color-gray-60; - } - } - - .align-button { - align-items: center; - cursor: pointer; - display: flex; - height: 30px; - justify-content: center; - margin: 5px 0; - padding: $size-2 $size-1; - width: 25%; - - svg { - height: 16px; - width: 16px; - fill: $color-gray-20; - } - - &:hover { - background-color: $color-primary; - svg { - fill: $color-gray-50; - } - } - - &.disabled { - background-color: transparent; - cursor: default; - svg { - fill: $color-gray-40; - } - } - - &.selected svg { - fill: $color-primary; - } - - &.selected:hover svg { - fill: $color-white; - } - } -} diff --git a/frontend/resources/styles/main/partials/sidebar-assets.scss b/frontend/resources/styles/main/partials/sidebar-assets.scss deleted file mode 100644 index aa1750b8dd..0000000000 --- a/frontend/resources/styles/main/partials/sidebar-assets.scss +++ /dev/null @@ -1,538 +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 - -.assets-bar { - display: flex; - flex-direction: column; - overflow: hidden; - height: 100%; - - .assets-bar-title { - color: $color-gray-10; - font-size: $fs14; - margin: $size-2 $size-2 0 $size-2; - display: flex; - align-items: center; - cursor: pointer; - - & .libraries-button { - margin-left: auto; - display: flex; - align-items: center; - - svg { - fill: $color-gray-30; - height: 16px; - width: 16px; - padding-right: 6px; - } - } - - & .libraries-button:hover { - color: $color-primary; - - & svg { - fill: $color-primary; - } - } - } - - .search-block { - border: 1px solid $color-gray-30; - margin: $size-2 $size-2 0 $size-2; - padding: $size-1 $size-2; - display: flex; - align-items: center; - - &:hover { - border-color: $color-gray-20; - } - - &:focus-within { - border-color: $color-primary; - } - - & .search-input { - background-color: $color-gray-50; - border: none; - color: $color-gray-10; - font-size: $fs12; - margin: 0; - padding: 0; - flex-grow: 1; - - &:focus { - color: lighten($color-gray-10, 8%); - outline: none; - } - } - - & .search-icon { - display: flex; - align-items: center; - - svg { - fill: $color-gray-30; - height: 16px; - width: 16px; - } - - &.close { - transform: rotate(45deg); - cursor: pointer; - } - } - } - - .input-select { - background-color: $color-gray-50; - color: $color-gray-10; - border: 1px solid transparent; - border-bottom-color: $color-gray-40; - padding: $size-1; - margin: $size-2 $size-2 $size-4 $size-2; - - &:focus { - color: lighten($color-gray-10, 8%); - } - - &:active { - border-color: $color-primary; - } - - option { - background: $color-white; - color: $color-gray-60; - font-size: $fs12; - } - } - - .collapse-library { - margin-right: $size-2; - flex-shrink: inherit; // Inheriting shrink behaviour - - &.open svg { - transform: rotate(90deg); - } - } - - .library-bar { - cursor: pointer; - } - - .listing-options { - background-color: $color-gray-60; - display: flex; - align-items: center; - padding: $size-4 $size-2 0 $size-2; - - .selected-count { - color: $color-primary; - font-size: $fs12; - } - - .listing-option-btn { - cursor: pointer; - margin-left: $size-2; - - &.first { - margin-left: auto; - } - - svg { - fill: $color-gray-20; - height: 16px; - width: 16px; - } - } - } - - .asset-section { - background-color: $color-gray-60; - padding: $size-2; - font-size: $fs12; - color: $color-gray-20; - /* TODO: see if this is useful, or is better to leave only - one scroll bar in the whole sidebar - (also see .asset-list) */ - // max-height: 30rem; - // overflow-y: scroll; - - // First child is the listing options buttons - &:not(:nth-child(2)) { - border-top: 1px solid $color-gray-50; - } - - .asset-title { - display: flex; - cursor: pointer; - font-size: $fs12; - text-transform: uppercase; - - & .num-assets { - color: $color-gray-30; - } - - & svg { - height: 8px; - width: 8px; - fill: $color-gray-30; - margin-right: 4px; - transform: rotate(90deg); - } - - &.closed svg { - transform: rotate(0deg); - transition: transform 0.3s; - } - } - - .group-title { - display: flex; - cursor: pointer; - margin-top: $size-2; - margin-bottom: $size-1; - color: $color-white; - - & svg { - height: 8px; - width: 8px; - fill: $color-white; - margin-right: 4px; - transform: rotate(90deg); - } - - &.closed svg { - transform: rotate(0deg); - transition: transform 0.3s; - } - - & .dim { - color: $color-gray-40; - } - } - - .assets-button { - margin-left: auto; - cursor: pointer; - - & svg { - width: 0.7rem; - height: 0.7rem; - fill: #f0f0f0; - } - - &:hover svg { - fill: $color-primary; - } - } - - .asset-title + .asset-grid { - margin-top: $size-2; - } - - .asset-grid { - display: grid; - grid-template-columns: repeat(4, 1fr); - grid-auto-rows: 6vh; - column-gap: 0.5rem; - row-gap: 0.5rem; - - &.big { - grid-template-columns: repeat(2, 1fr); - grid-auto-rows: 10vh; - - .three-row & { - grid-template-columns: repeat(3, 1fr); - } - - .four-row & { - grid-template-columns: repeat(4, 1fr); - } - - .grid-cell { - padding: $size-1; - - & svg { - height: 10vh; - } - } - } - } - - .grid-cell { - background-color: $color-canvas; - border-radius: $br4; - border: 2px solid transparent; - overflow: hidden; - display: flex; - align-items: center; - justify-content: center; - padding: $size-2; - position: relative; - cursor: pointer; - - & img { - max-height: 100%; - max-width: 100%; - height: auto; - width: auto; - pointer-events: none; - } - } - - .cell-name { - background-color: $color-gray-60; - font-size: $fs9; - display: none; - position: absolute; - left: 0; - bottom: 0; - width: 100%; - padding: 3px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - - &.editing { - display: block; - } - - .editable-label-input { - border: 1px solid $color-gray-20; - border-radius: $br3; - font-size: $fs12; - padding: 2px; - margin: 0; - height: unset; - width: 100%; - } - - .editable-label-close { - display: none; - } - } - - .grid-cell:hover { - border: 2px solid $color-primary; - - & .cell-name { - display: block; - } - } - - .grid-cell.selected { - border: 2px solid $color-primary; - } - - .grid-placeholder { - border: 2px solid $color-gray-20; - border-radius: $br4; - } - - .drop-space { - height: 10px; - } - - .typography-container { - position: relative; - - &:last-child { - padding-bottom: 0.5em; - } - } - - .drag-counter { - position: absolute; - top: 5px; - left: 4px; - width: 16px; - height: 16px; - background-color: $color-primary; - border-radius: 50%; - color: $color-black; - font-size: $fs12; - display: flex; - justify-content: center; - align-items: center; - } - - .asset-title + .asset-enum { - margin-top: $size-2; - } - - .asset-enum { - .enum-item { - position: relative; - display: flex; - align-items: center; - margin-bottom: $size-2; - cursor: pointer; - - & > svg, - & > img { - background-color: $color-canvas; - border-radius: $br4; - border: 2px solid transparent; - height: 24px; - width: 24px; - margin-right: $size-2; - } - - .item-name { - width: calc(100% - 24px - #{$size-2}); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - display: block; - - &.editing { - display: flex; - align-items: center; - - .editable-label-input { - height: 24px; - } - - .editable-label-close { - display: none; - } - } - } - } - - .enum-item:hover, - .enum-item.selected { - color: $color-primary; - } - - .grid-placeholder { - margin-bottom: 5px; - } - } - - /* TODO: see if this is useful, or is better to leave only - one scroll bar in the whole sidebar - (also see .asset-section) */ - // .asset-list { - // max-height: 30rem; - // overflow-y: scroll; - // } - - .asset-list-item { - display: flex; - align-items: center; - border: 1px solid transparent; - border-radius: $br3; - margin-top: $size-1; - padding: 2px; - font-size: $fs12; - color: $color-white; - cursor: pointer; - position: relative; - - .name-block { - color: $color-gray-20; - width: calc(100% - 24px - #{$size-2}); - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - - & span { - margin-left: $size-1; - color: $color-gray-30; - text-transform: uppercase; - } - - &.selected { - border: 1px solid $color-primary; - } - } - - .context-menu { - position: fixed; - top: 10px; - left: 10px; - } - - .advanced-options { - border-color: $color-black; - background-color: $color-gray-60; - - .input-text, - .input-select, - .adv-typography-name { - background-color: $color-gray-60; - } - } - - .dragging { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: color.adjust($color-primary, $alpha: -0.5); - } - } -} - -.modal-create-color { - position: relative; - background-color: $color-white; - padding: 4rem; - display: flex; - flex-direction: column; - align-items: center; - - & .sketch-picker, - .chrome-picker { - box-shadow: none !important; - border: 1px solid $color-gray-10 !important; - border-radius: $br0 !important; - - & input { - background-color: $color-white; - } - } - - & .close { - position: absolute; - right: 1rem; - transform: rotate(45deg); - top: 1rem; - - svg { - fill: $color-black; - height: 20px; - width: 20px; - - &:hover { - fill: $color-danger; - } - } - } - - & .btn-primary { - width: 10rem; - padding: 0.5rem; - margin-top: 1rem; - } -} - -.modal-create-color-title { - color: $color-black; - font-size: $fs24; - font-weight: $fw400; -} - -.libraries-wrapper { - overflow: auto; - display: flex; - flex-direction: column; - flex: 1; -} diff --git a/frontend/resources/styles/main/partials/sidebar-document-history.scss b/frontend/resources/styles/main/partials/sidebar-document-history.scss deleted file mode 100644 index a8e00d41cc..0000000000 --- a/frontend/resources/styles/main/partials/sidebar-document-history.scss +++ /dev/null @@ -1,139 +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 - -.history-debug-overlay { - background: $color-gray-50; - bottom: 0; - max-height: 500px; - overflow-y: auto; - position: absolute; - width: 500px; - z-index: 1000; -} - -.history-toolbox { - display: flex; - flex-direction: column; -} - -.history-toolbox-title { - color: $color-gray-10; - font-size: $fs14; - padding: 0.5rem; -} - -.history-entry-empty { - display: flex; - flex-direction: column; - align-items: center; - padding: 1rem; - - .history-entry-empty-icon { - margin-bottom: 1rem; - svg { - width: 32px; - height: 32px; - fill: $color-gray-40; - } - } - - .history-entry-empty-msg { - color: $color-gray-30; - font-size: $fs12; - } -} - -.history-entries { - font-size: $fs12; - color: $color-gray-20; - fill: $color-gray-20; - height: 100%; - overflow-x: hidden; - overflow-y: auto; -} - -.history-entry { - border: 1px solid $color-gray-60; - border-radius: $br4; - margin: 0.5rem; - display: flex; - flex-direction: column; - padding: 0.5rem; - cursor: pointer; - - transition: border 0.2s; - - &.disabled { - opacity: 0.5; - } - - &.current { - background-color: $color-gray-60; - } - - &.hover { - border-color: $color-primary; - } -} - -.history-entry-summary { - display: flex; - flex-direction: row; - align-items: center; - - * { - display: flex; - } -} - -.history-entry-summary-icon { - svg { - width: 16px; - height: 16px; - } -} - -.history-entry-summary-text { - flex: 1; - margin: 0 0.5rem; - margin-top: 2px; -} - -.history-entry-summary-button { - opacity: 0; - transition: transform 0.2s; - - svg { - width: 12px; - height: 12px; - } - - .show-detail &, - .hover & { - opacity: 1; - } - - .show-detail & { - transform: rotate(90deg); - } -} - -.history-entry-detail { - display: none; - - .show-detail & { - display: block; - padding: 1rem 0 0.5rem 0; - } - - .history-entry-details-list { - margin: 0; - - li { - margin-bottom: 0.5rem; - } - } -} diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss deleted file mode 100644 index 5dab0ad90b..0000000000 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ /dev/null @@ -1,2593 +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 - -.element-options { - display: flex; - flex-direction: column; - width: 100%; - height: 100%; - - .element-icons { - background-color: $color-gray-60; - border: 1px solid $color-gray-60; - border-radius: $br3; - display: flex; - margin: $size-1; - - li { - align-items: center; - border-right: 1px solid $color-gray-60; - border-radius: $br3; - cursor: pointer; - display: flex; - flex: 1; - justify-content: center; - padding: $size-2; - - svg { - fill: $color-gray-20; - height: 15px; - width: 15px; - } - - &:hover { - svg { - fill: $color-primary; - } - } - - &.selected { - background-color: $color-primary; - - svg { - fill: $color-white; - } - } - - &:last-child { - border: none; - } - } - } - - &.inspect { - & > :first-child { - margin-top: 7px; - } - } - - .element-set { - border-bottom: 1px solid $color-gray-60; - color: $color-gray-20; - padding: $size-2 $size-1; - - .element-set-title { - height: 35px; - color: $color-gray-20; - display: flex; - font-size: $fs14; - padding: $size-1; - width: 100%; - align-items: center; - justify-content: space-between; - } - } - - .element-list { - margin-bottom: $size-2; - - li { - align-items: center; - border-bottom: 1px solid $color-gray-60; - display: flex; - flex-direction: row; - padding: $size-2; - width: 100%; - - .list-icon { - svg { - fill: $color-gray-30; - height: 15px; - margin-right: $size-1; - width: 15px; - } - } - - span { - color: $color-gray-20; - font-size: $fs12; - max-width: 75%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - .list-actions { - align-items: center; - cursor: pointer; - display: none; - margin-left: auto; - - a { - svg { - fill: $color-gray-60; - height: 15px; - margin-left: $size-1; - width: 15px; - - &:hover { - fill: $color-gray-20; - } - } - } - } - - &:hover { - .list-icon { - svg { - fill: $color-primary; - } - } - - span { - color: $color-primary; - } - } - - &.selected { - .list-icon { - svg { - fill: $color-primary; - } - } - - span { - color: $color-primary; - font-weight: $fw700; - } - } - } - - &:hover { - .list-actions { - display: flex; - @include animation(0s, 0.3s, fadeIn); - } - } - } -} - -.element-set-content { - display: flex; - flex-direction: column; - padding: $size-1; - width: 100%; - - .input-text { - background-color: $color-gray-50; - border: 1px solid transparent; - border-bottom-color: $color-gray-40; - color: $color-white; - font-size: $fs12; - margin: $size-1; - min-width: 0; - padding: $size-1; - width: 100%; - - &:focus { - color: lighten($color-gray-10, 8%); - border-color: $color-primary !important; - } - - &:hover { - border-color: $color-gray-40; - } - - &.error { - border-color: $color-danger; - } - - &[disabled] { - color: $color-gray-30; - } - } - - .input-select { - /* This padding is so the text won't overlap the arrow*/ - padding-right: 1rem; - overflow: hidden; - text-overflow: ellipsis; - color: $color-gray-10; - - &:focus { - color: lighten($color-gray-10, 8%); - border-color: $color-primary; - } - - option { - color: $color-gray-60; - background: $color-white; - font-size: $fs12; - - &:disabled { - color: $color-gray-20; - } - } - } - - .input-checkbox { - label { - color: $color-gray-20; - } - - label::before { - background-color: transparent; - width: 16px; - height: 16px; - } - - label::after { - width: 16px; - height: 16px; - } - - input:checked + label::before { - border-width: 1px; - } - - input:checked + label::after { - font-size: $fs13; - } - } - - .element-set-subtitle { - color: $color-gray-20; - font-size: $fs12; - width: 64px; - - &.wide { - min-width: 75px; - } - } - - .lock-size { - cursor: pointer; - margin: auto; - - svg { - fill: $color-gray-20; - height: 14px; - width: 14px; - - &:hover { - fill: $color-primary; - } - } - - &.selected { - svg { - fill: $color-primary; - } - } - - &.disabled { - cursor: unset; - svg { - fill: $color-gray-40; - } - } - } - - .save-btn { - width: 100%; - } - - .cancel-btn { - color: $color-danger; - text-align: center; - width: 100%; - } - - .custom-select { - border: 1px solid $color-gray-40; - border-radius: $br3; - cursor: pointer; - padding: $size-1 $size-5 $size-1 $size-1; - position: relative; - - .dropdown-button { - position: absolute; - right: $size-1; - top: 7px; - - svg { - fill: $color-gray-40; - height: 10px; - width: 10px; - } - } - - span { - font-size: $fs12; - } - - &:hover { - border: 1px solid $color-gray-20; - } - - &.no-check { - .custom-select-dropdown { - width: 100%; - min-width: unset; - .check-icon { - display: none; - } - li.checked-element { - padding-left: 0.5rem; - &.is-selected { - background-color: $color-primary; - } - } - } - } - } - .opened { - border: 1px solid $color-primary; - &:hover, - &:focus { - outline: none; - border: 1px solid $color-primary; - } - } - - .custom-select-dropdown { - background-color: $color-white; - border-radius: $br3; - box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25); - left: 0; - max-height: 30rem; - min-width: 7rem; - position: absolute; - overflow-y: auto; - top: 30px; - z-index: 12; - - .presets { - width: 200px; - } - - hr { - margin: 0; - border-color: $color-gray-20; - } - - li { - color: $color-gray-60; - cursor: pointer; - font-size: $fs14; - display: flex; - gap: 0 10px; - justify-content: flex-start; - padding: $size-2; - - span { - color: $color-gray-20; - display: flex; - justify-content: flex-start; - align-items: center; - } - - .check-icon { - min-width: 25px; - color: $color-gray-20; - justify-content: center; - } - - &.dropdown-separator:not(:last-child) { - border-bottom: 1px solid $color-gray-10; - } - - &.dropdown-label:not(:first-child) { - border-top: 1px solid $color-gray-10; - } - - &.dropdown-label span { - margin-left: 0; - } - - &:hover { - background-color: $color-primary-lighter; - } - } - } - - & li.checked-element { - padding-left: 0; - - & span { - color: $color-black; - } - - & svg { - visibility: hidden; - width: 8px; - height: 8px; - background: none; - margin: 0.25rem; - fill: $color-black; - } - - &.is-selected { - & svg { - visibility: visible; - } - } - } - - .editable-select { - border: 1px solid transparent; - position: relative; - height: 38px; - // margin-left: $size-2; - max-height: 30px; - position: relative; - width: 60%; - - svg { - fill: $color-gray-10; - height: 10px; - width: 10px; - } - - .input-text { - left: 0; - position: absolute; - top: -1px; - width: 60%; - } - - input.input-text { - border: none; - background: none; - } - - .input-select { - background-color: transparent; - border: none; - border-bottom: 1px solid $color-gray-40; - color: transparent; - left: 0; - position: absolute; - top: 0; - width: 100%; - - option { - color: $color-gray-60; - background: $color-white; - font-size: $fs12; - - &:disabled { - color: $color-gray-20; - } - } - } - - .dropdown-button { - position: absolute; - right: 0; - padding-right: 4px; - height: 100%; - display: flex; - align-items: center; - } - - &.input-option { - height: 26px; - border-bottom: 1px solid #64666a; - width: 100%; - margin-left: 0.25rem; - - .input-text { - border: none; - margin: 0; - width: calc(100% - 12px); - height: 100%; - top: auto; - color: $color-white; - } - } - - .custom-select-dropdown { - top: unset; - margin-bottom: 0; - } - - &:hover { - border: 1px solid $color-gray-40; - } - } -} - -.element-set-content .border-data { - &[draggable="true"] { - cursor: pointer; - } -} - -.element-set-content .grid-option-main { - .editable-select { - height: 2rem; - } - .editable-select svg { - fill: $color-gray-40; - } - .editable-select.input-option .input-text { - padding: 0; - padding-top: 0.18rem; - padding-left: 0.25rem; - } - - .input-element { - width: 55px; - margin: 0 0.2rem; - } - - .input-text { - padding-left: 0; - color: $color-white; - background-color: transparent; - height: 30px; - } -} - -.element-set-content .component-row { - display: flex; - align-items: center; - font-size: $fs12; - color: $color-gray-10; - - svg { - fill: $color-gray-20; - height: 16px; - width: 16px; - margin-right: $size-2; - } - - .component-name { - overflow: hidden; - text-overflow: ellipsis; - width: 100%; - } - - .row-actions { - margin-left: auto; - cursor: pointer; - - svg { - fill: $color-gray-20; - height: 12px; - margin-right: $size-1; - width: 12px; - } - - .context-menu-items { - right: 0.5rem; - left: unset; - top: 0; - - .context-menu-action { - overflow-wrap: break-word; - min-width: 223px; - white-space: break-spaces; - } - } - } - - &.copy { - flex-wrap: wrap; - border-radius: 8px; - border: 1px solid $color-gray-60; - padding: 0.5rem; - cursor: pointer; - - .component-name { - width: 80%; - color: $color-white; - } - .component-parent-name { - overflow: hidden; - text-overflow: ellipsis; - width: 100%; - padding-left: calc(0.5rem + 16px); - color: $color-gray-40; - } - } -} - -.grid-option .custom-select { - margin-bottom: 0; -} - -.presets { - &:focus, - &:focus-within { - outline: none; - border: 1px solid $color-primary; - } - .custom-select-dropdown { - width: 237px; - - li { - font-size: $fs12; - - span { - font-size: $fs12; - } - } - } -} - -.row-flex.align-icons { - margin-left: 0; -} - -.align-icons { - cursor: pointer; - display: flex; - flex: 1; - justify-content: flex-end; - margin: $size-2 0 $size-2 $size-2; - padding: 0 $size-1; - - &:first-child { - justify-content: flex-start; - margin-left: 0; - } - - span { - align-items: center; - display: flex; - height: 20px; - justify-content: center; - margin-right: $size-2; - position: relative; - width: 20px; - - svg { - fill: $color-gray-30; - height: 14px; - width: 14px; - } - - &:hover, - &.current { - svg { - fill: $color-primary; - } - } - - &:last-child { - margin-right: 0; - } - } -} - -.element-color-picker { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - margin: 0 4px; - - .color-picker-body { - height: 100%; - margin-right: 15px; - } - - .color-picker-bar { - height: 165px; - position: relative; - width: 15px; - - .color-bar-select { - background-color: $color-gray-50; - height: 3px; - left: -4px; - position: absolute; - width: 23px; - transition: none; - top: 30%; - } - } -} - -.radius-options, -.padding-options, -.margin-options { - align-items: center; - border: 1px solid $color-gray-60; - border-radius: $br4; - display: flex; - justify-content: space-between; - padding: 8px; - width: 64px; - - .radius-icon, - .padding-icon, - .margin-icon { - display: flex; - align-items: center; - - svg { - cursor: pointer; - height: 16px; - fill: $color-gray-30; - width: 16px; - } - - &:hover, - &.selected { - svg { - fill: $color-primary; - } - } - } -} - -.orientation-icon { - margin-left: $size-2; - display: flex; - align-items: center; - - svg { - cursor: pointer; - height: 20px; - fill: $color-gray-40; - width: 20px; - } - - &:hover { - svg { - fill: $color-gray-10; - } - } -} - -.navigate-icon { - background-color: $color-gray-60; - cursor: pointer; - margin-left: $size-2; - display: flex; - align-items: center; - justify-content: center; - width: 32px; - height: 32px; - - svg { - height: 16px; - fill: $color-gray-30; - width: 16px; - } - - &:hover { - svg { - stroke: $color-gray-10; - } - } -} - -.input-icon { - align-items: center; - display: flex; - width: 100%; - - &:first-child { - margin-right: $size-2; - } - - .icon-before { - align-items: center; - display: flex; - height: 18px; - position: relative; - width: 14px; - - svg { - fill: $color-gray-30; - height: 14px; - width: 14px; - } - } -} - -.custom-button { - cursor: pointer; - background: none; - border: none; - - & svg { - width: 12px; - height: 12px; - fill: $color-gray-20; - } - - &:hover svg, - &.is-active svg { - fill: $color-primary; - } -} - -.element-set-content .input-row { - & .element-set-subtitle { - width: 5.5rem; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } -} - -.grid-option, -.shadow-option { - margin-bottom: 0.5rem; - .advanced-options { - .row-flex { - justify-content: flex-end; - } - .custom-button { - left: 0; - position: absolute; - top: 12px; - } - .element-set-actions-button { - min-width: auto; - min-height: auto; - padding-right: 10px; - svg { - width: 12px; - height: 12px; - } - } - } -} - -.element-set-content .custom-select.input-option { - border-top: none; - border-left: none; - border-right: none; - margin-left: 0.25rem; -} - -.element-set-content .grid-option-main, -.element-set-content .shadow-option-main { - align-items: center; - display: flex; - padding: 0.3rem 0; - border: 1px solid $color-black; - border-radius: $br4; - height: 48px; - - &:hover { - background: $color-gray-60; - - .custom-select, - .editable-select, - input { - background-color: $color-gray-50; - } - } - - & .custom-select { - min-width: 4.75rem; - height: 2rem; - border-color: transparent; - border-bottom: 1px solid #65666a; - max-height: 30px; - - &:hover { - border: 1px solid $color-gray-40; - } - } - - & .input-element { - width: 50px; - overflow: hidden; - } - - & .custom-select-dropdown { - width: 96px; - } - - & .input-option { - margin-left: 0.5rem; - - & .custom-select-dropdown { - width: 5rem; - min-width: 5rem; - max-height: 10rem; - } - } -} - -.grid-option-main-actions, -.shadow-option-main-actions { - display: flex; - visibility: hidden; - - .grid-option:hover &, - .shadow-option:hover & { - visibility: visible; - } -} - -.focus-overlay { - background: $color-black; - height: 100%; - left: 0; - position: absolute; - top: 0; - width: calc(100%); - opacity: 0.4; - z-index: 10; - display: flex; -} - -.advanced-options-wrapper { - width: 100%; -} - -.advanced-options { - border: 1px solid $color-gray-60; - background-color: $color-gray-50; - border-radius: $br4; - padding: 8px; - position: relative; - top: 2px; - width: 100%; -} - -.btn-options { - cursor: pointer; - border: 1px solid $color-black; - background: $color-gray-60; - border-radius: $br2; - color: $color-gray-20; - font-size: $fs11; - line-height: $lh-145; // Original value was 16px; 16px/11px = 145.454545455% => $lh-145 - flex-grow: 1; - padding: 0.25rem 0; - - &:first-child { - margin-right: 0.5rem; - } - - &:not([disabled]):hover { - background: $color-primary; - color: $color-black; - } - - &[disabled] { - opacity: 0.4; - cursor: auto; - } -} - -.element-set-options-group { - display: flex; - justify-content: space-between; - padding: 3px; - border: 1px solid $color-black; - border-radius: $br4; - - &:hover { - background: #1f1f1f; - } - - &.selected { - border: 1px solid $color-primary; - } - - &:not(:first-child) { - margin-top: 7px; - } - - &.open { - &:hover { - background: unset; - } - } -} - -.interactions-options { - &.element-set { - border-bottom: 0; - } - - .element-set-options-group { - flex-wrap: wrap; - } - - &:not(:first-child) { - border-top: 1px solid $color-gray-60; - } -} - -.exports-options, -.shadow-options { - .element-set-options-group { - .delete-icon { - display: flex; - min-width: 40px; - min-height: 40px; - justify-content: center; - align-items: center; - cursor: pointer; - svg { - width: 12px; - height: 12px; - fill: $color-gray-20; - } - } - } - - .download-button { - margin-top: 10px; - } - - .input-element { - width: 100%; - flex-shrink: initial; - } - - .row-grid-2 { - grid-column-gap: 1em; - } - - .color-info { - input { - margin-right: 1em; - width: 74px; - &:focus { - border-color: $color-primary !important; - color: $color-white; - outline: none; - } - - &:hover { - border-color: $color-gray-20; - } - } - } -} - -.shadow-options .color-row-wrap { - margin-left: 6px; - margin-top: 0.5rem; -} - -.element-set-actions-button { - display: flex; - min-width: 30px; - min-height: 30px; - justify-content: center; - align-items: center; - cursor: pointer; - - svg { - width: 12px; - height: 12px; - fill: $color-gray-20; - stroke: $color-gray-20; - } - - &.remove { - min-width: 20px; - min-height: 20px; - } - - &:hover svg, - &.active svg { - fill: $color-primary; - stroke: $color-primary; - } - - &.actions-inside { - position: absolute; - right: 0; - } -} - -.element-set-label { - font-size: $fs12; - padding: 0.5rem; - color: $color-gray-10; -} - -.element-set-actions { - display: flex; - visibility: hidden; -} - -.row-flex-removable:hover .element-set-actions, -.element-set-options-group:hover .element-set-actions { - visibility: visible; -} - -.layer-actions { - visibility: visible; -} - -.typography-entry { - margin: 0.5rem 0.3rem; - display: flex; - flex-direction: row; - align-items: center; - - .typography-selection-wrapper { - display: flex; - flex-direction: row; - align-items: center; - flex: 1; - height: 100%; - - &.is-selectable { - cursor: pointer; - } - } - - .typography-sample { - font-size: $fs17; - color: $color-white; - margin: 0 0.5rem; - - font-family: sourcesanspro; - font-style: normal; - font-weight: $fw400; - } - - .typography-name { - flex-grow: 1; - font-size: $fs11; - margin-top: 4px; - max-width: calc(var(--width, 256px) - 100px); - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - - .element-set-actions-button svg { - width: 10px; - height: 10px; - } -} - -.spacing-options { - display: flex; - width: 100%; -} - -.asset-section { - .typography-entry { - margin: 0.25rem 0; - } - - .element-set-content .font-option, - .element-set-content .size-option { - margin: 0.5rem 0; - } - .element-set-content .variant-option { - margin-left: 0.5rem; - } -} - -.row-flex input.adv-typography-name { - font-size: $fs14; - color: $color-gray-10; - width: 100%; - max-width: none; - margin: 0; - background-color: #303236; - border-top: none; - border-left: none; - border-right: none; -} - -.size-option .custom-select-dropdown { - cursor: pointer; - max-height: 16rem; - min-width: 6rem; - left: initial; - top: 0; -} - -.typography-read-only-data { - font-size: $fs12; - color: $color-white; - - .typography-name { - font-size: $fs14; - } - - .row-flex { - padding: 0.5rem 0; - } - - .label { - color: $color-gray-30; - - &::after { - content: ":"; - margin-right: 0.25rem; - } - } - - .go-to-lib-button { - color: $color-white; - transition: - border 0.3s, - color 0.3s; - text-align: center; - background: $color-gray-50; - padding: 0.5rem; - border-radius: $br2; - cursor: pointer; - font-size: $fs14; - margin-top: 1rem; - - &:hover { - background: $color-primary; - color: $color-black; - } - } -} - -.multiple-typography { - margin: 0.5rem; - padding: 0.5rem; - border: 1px dashed $color-gray-30; - border-radius: $br4; - display: flex; - justify-content: space-between; - - .multiple-typography-text, - .multiple-typography-button { - font-size: $fs12; - display: flex; - align-items: center; - } - - .multiple-typography-button { - cursor: pointer; - svg { - transition: fill 0.3s; - width: 16px; - height: 16px; - fill: $color-gray-10; - } - - &:hover svg { - fill: $color-primary; - } - } -} - -.font-selector { - background: $color-black; - height: 100%; - left: 0; - position: absolute; - top: 0; - width: calc(100%); - z-index: 10; - display: flex; - justify-content: center; - align-items: center; - - .font-selector-dropdown { - background: #303236; - display: flex; - flex-direction: column; - flex-grow: 1; - height: 100%; - } - - header { - display: flex; - flex-direction: column; - position: relative; - - .backend-filters { - padding: $size-2 $size-4; - // width: 220px; - top: 40px; - right: 20px; - } - .backend-filter { - display: flex; - align-items: center; - padding: $size-2 0; - cursor: pointer; - - .checkbox-icon { - display: flex; - justify-content: center; - align-items: center; - width: $size-4; - height: $size-4; - border: 1px solid $color-gray-30; - border-radius: $br3; - - svg { - width: 8px; - display: none; - height: 8px; - fill: $color-black; - } - } - - .backend-name { - margin-left: $size-2; - color: $color-gray-50; - } - - &.selected { - .checkbox-icon { - svg { - display: inherit; - } - } - } - } - - input { - display: flex; - flex-grow: 1; - padding: 4px; - font-size: $fs12; - background: $color-gray-50; - border-radius: $br3; - color: $color-gray-20; - border: 1px solid $color-gray-30; - width: 88%; - margin: 15px 17px; - } - - .title { - font-size: $fs14; - margin: 9px 17px; - } - - .options { - display: flex; - justify-content: center; - align-items: center; - width: 24px; - height: 24px; - margin-left: $size-2; - - svg { - width: 16px; - height: 16px; - fill: $color-gray-20; - } - - &.active { - svg { - fill: $color-primary; - } - } - } - } - - .fonts-list { - display: flex; - flex-direction: column; - height: 100%; - - position: relative; - -webkit-box-flex: 1; - flex: 1 1 auto; - } - - hr { - margin-bottom: 0px; - margin-top: 0px; - } - - .font-item { - padding-left: $size-5; - height: $size-6; - max-height: $size-6; - width: 100%; - display: flex; - align-items: center; - cursor: pointer; - color: $color-gray-10; - - &.selected { - background-color: $color-black; - color: $color-primary; - - .icon svg { - fill: $color-primary; - } - } - - &:hover { - background-color: $color-primary; - color: $color-black; - } - - .icon { - display: flex; - // justify-content: center; - align-items: center; - // border: 1px solid red; - width: $size-5; - } - - .label { - font-size: $fs12; - } - - svg { - fill: $color-gray-10; - width: 10px; - height: 10px; - } - } -} - -.row-flex.align-top { - align-items: flex-start; -} - -.constraints-widget { - min-width: 72px; - min-height: 72px; - position: relative; - background-color: $color-gray-60; - flex-grow: 0; - - .constraints-box { - width: 28px; - height: 28px; - position: absolute; - top: 22px; - left: 22px; - border: 2px solid $color-gray-50; - } - - .constraint-button { - position: absolute; - cursor: pointer; - display: flex; - justify-content: center; - align-items: center; - - &::after { - content: " "; - background-color: $color-gray-30; - } - - &.active, - &:hover { - &::after { - background-color: $color-primary; - } - } - - &.top, - &.bottom { - width: 28px; - height: 22px; - left: calc(50% - 14px); - - &::after { - width: 3px; - height: 15px; - } - } - - &.top { - top: 0; - } - - &.bottom { - bottom: 0; - } - - &.left, - &.right { - width: 22px; - height: 28px; - top: calc(50% - 14px); - - &::after { - width: 15px; - height: 3px; - } - } - - &.left { - left: 0; - } - - &.right { - right: 0; - } - - &.centerv { - width: 28px; - height: 28px; - left: calc(50% - 14px); - top: calc(50% - 14px); - - &::after { - width: 3px; - height: 15px; - } - } - - &.centerh { - width: 28px; - height: 15px; - left: calc(50% - 14px); - top: calc(50% - 7px); - - &::after { - width: 15px; - height: 3px; - } - } - } -} - -.constraints-form { - display: flex; - flex-grow: 1; - flex-direction: column; - align-items: stretch; - justify-content: flex-start; - - .input-select { - font-size: $fs12; - margin: 0 $size-1 $size-2 $size-1; - padding: 0 $size-1; - } - - svg { - width: 15px; - height: 15px; - margin-left: $size-4; - fill: $color-gray-20; - } - - .left-right svg { - transform: rotate(45deg); - } - - .top-bottom svg { - transform: rotate(-45deg); - } - - .fix-when { - font-size: $fs12; - cursor: pointer; - display: flex; - - span { - margin-left: $size-2; - } - - &:hover, - &.active { - color: $color-primary; - - svg { - fill: $color-primary; - } - } - } -} - -.cap-select { - background-color: transparent; - border: 1px solid transparent; - border-bottom-color: $color-gray-40; - color: $color-gray-10; - cursor: pointer; - font-size: $fs12; - margin: $size-1; - overflow: hidden; - padding: $size-1; - padding-right: 20px; - position: relative; - text-overflow: ellipsis; - white-space: nowrap; - width: 100%; - - & .cap-select-button { - svg { - fill: $color-gray-10; - height: 11px; - position: absolute; - right: 5px; - top: 6px; - width: 11px; - } - } - - &:hover { - border-color: $color-gray-40; - } - - &:focus { - border-color: $color-primary; - } -} - -.cap-select-dropdown { - right: 5px; - top: 30px; - z-index: 12; - width: 200px; - height: 320px; - position: fixed; - - & li.separator { - border-top: 1px solid $color-gray-10; - } - - & li img { - width: 16px; - margin-right: $size-2; - } -} - -.expand-colors { - cursor: pointer; - display: flex; - - .text { - color: $color-gray-30; - font-size: $fs12; - padding-left: 10px; - } - - svg { - width: 16px; - height: 16px; - fill: $color-gray-20; - stroke: $color-gray-20; - } -} - -.selected-colors { - .color-data { - margin-bottom: 0; - padding-bottom: 5px; - - svg { - visibility: hidden; - } - - .percentil { - margin-bottom: 0; - } - } - - .color-data:hover { - background-color: $color-gray-60; - - svg { - visibility: visible; - } - } -} - -.layout-menu, -.layout-item-menu, -.layout-grid-item-menu { - font-family: "worksans", sans-serif; - svg { - height: 16px; - width: 16px; - fill: $color-gray-30; - } - .layout-row { - display: grid; - grid-template-columns: 60px 1fr; - margin-bottom: 5px; - .row-title { - font-size: $fs12; - display: flex; - justify-content: flex-start; - align-items: center; - margin-right: 5px; - font-family: "worksans", sans-serif; - - &.justify-content, - &.align-content, - &.sizing { - align-items: flex-start; - margin-top: 4px; - } - - &.align-items-grid, - &.jusfiy-content-grid { - align-items: flex-start; - margin-top: 11px; - } - } - .btn-wrapper { - display: flex; - width: 100%; - max-width: 185px; - - &.wrap { - flex-direction: column; - gap: 5px; - } - - &.justify-content, - &.align-content { - display: flex; - flex-direction: column; - gap: 5px; - } - - .direction, - .wrap-type, - .align-items-style, - .align-self-style, - .justify-content-style, - .align-content-style, - .layout-behavior, - .absolute { - display: flex; - border-radius: $br4; - border: 1px solid $color-gray-60; - height: 26px; - margin-right: 5px; - flex-grow: 1; - &.horizontal { - button svg { - transform: rotate(90deg); - } - } - button { - display: flex; - flex-grow: 1; - justify-content: center; - align-items: center; - background: none; - border: none; - cursor: pointer; - border-right: 1px solid $color-gray-60; - color: $color-gray-20; - &.active, - &:hover { - color: $color-primary; - &.dir { - color: $color-primary; - } - svg { - fill: $color-primary; - } - } - } - .dir { - display: flex; - justify-content: center; - align-items: center; - background: none; - border: none; - cursor: pointer; - border-right: 1px solid $color-gray-60; - padding: 2px; - &.row-reverse { - svg { - transform: rotate(180deg); - } - } - - &.column-reverse { - svg { - transform: rotate(-90deg); - } - } - &.column { - svg { - transform: rotate(90deg); - } - } - &.active, - &:hover { - svg { - fill: $color-primary; - } - } - } - :last-child { - border-right: none; - } - } - .z-index { - display: flex; - align-items: center; - margin-left: 2px; - margin-top: -4px; - svg { - width: 30px; - } - } - - .edit-mode { - align-items: center; - border-radius: 4px; - border: 1px solid $color-gray-60; - display: flex; - flex-direction: row; - gap: 10px; - justify-content: center; - margin-left: 5px; - padding: 0 8px; - text-align: left; - width: 120px; - - button { - color: $color-gray-30; - display: flex; - justify-content: center; - align-items: center; - background: transparent; - border: none; - cursor: pointer; - gap: 16px; - - &.active, - &:hover { - color: $color-primary; - svg { - fill: $color-primary; - } - } - } - } - - &.align-grid-items { - flex-direction: row; - gap: 0px; - margin: 7px 0; - - .align-items-style { - margin-right: 2px; - } - } - - &.align-grid-content { - flex-direction: column; - gap: 7px; - margin: 7px 0; - } - } - .position-wrapper { - display: flex; - width: 100%; - max-width: 185px; - height: 26px; - border-radius: 4px; - border: 1px solid $color-gray-60; - - .position-btn { - display: flex; - justify-content: center; - align-items: center; - background: transparent; - border: none; - cursor: pointer; - color: $color-gray-20; - border-right: 1px solid $color-gray-60; - flex: 1; - - &:last-child { - border-right: none; - } - &.active, - &:hover { - color: $color-primary; - } - - &[disabled] { - opacity: 0.5; - &:hover { - color: $color-gray-20; - } - } - } - } - - &.single-button { - display: flex; - justify-content: flex-end; - height: 1.5rem; - - .btn-wrapper { - width: initial; - } - } - } - .no-wrap { - display: flex; - align-items: center; - justify-content: center; - height: 21px; - width: 17px; - svg { - height: 0.7rem; - width: 0.7rem; - } - } - - .wrap { - padding: 1px; - } - - .gap-group { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 7px; - margin-top: 3px; - margin-bottom: 8px; - height: 26px; - max-width: 166px; - .gap-row { - display: flex; - justify-content: center; - align-items: center; - .icon { - display: flex; - justify-content: center; - align-items: center; - margin-right: 7px; - svg { - height: 14px; - width: 14px; - } - &.rotated { - transform: rotate(90deg); - } - &.activated { - svg { - fill: $color-primary; - } - } - } - input { - font-size: $fs12; - min-width: 0; - padding: 0.25rem; - height: 20px; - margin: 0; - } - } - button { - display: flex; - justify-content: center; - align-items: center; - background: none; - border: none; - cursor: pointer; - border-radius: $br3; - - &.lock { - border: 1px solid $color-gray-60; - border-radius: $br3; - width: 30px; - height: 30px; - } - &.active { - svg { - fill: $color-primary; - } - } - &:hover { - background-color: $color-primary; - svg { - fill: $color-gray-60; - } - } - } - } - - .padding-row, - .margin-row { - display: grid; - grid-template-columns: auto 30px; - max-width: 226px; - .padding-group, - .margin-item-group { - display: flex; - margin-top: 3px; - margin-bottom: 3px; - height: 26px; - .padding-item, - .margin-item { - display: flex; - align-items: center; - flex-grow: 1; - .icon { - display: flex; - justify-content: center; - align-items: center; - margin-right: 7px; - svg { - height: 14px; - width: 14px; - } - &.rotated { - transform: rotate(90deg); - } - &.activated { - svg { - fill: $color-primary; - } - } - } - input { - font-size: $fs12; - min-width: 0; - padding: 0.25rem; - width: 70px; - height: 20px; - margin: 0; - } - } - button { - display: flex; - justify-content: center; - align-items: center; - background: none; - border: none; - cursor: pointer; - border-radius: $br3; - - &.lock { - border: 1px solid $color-gray-60; - border-radius: $br3; - width: 30px; - height: 30px; - } - &.active { - svg { - fill: $color-primary; - } - } - &:hover { - background-color: $color-primary; - svg { - fill: $color-gray-60; - } - } - } - } - .padding-icons, - .margin-item-icons { - padding: 0; - border: 1px solid $color-gray-60; - border-radius: $br3; - margin-bottom: 8px; - margin-top: $br3; - margin-right: 1px; - height: 30px; - width: 30px; - - .padding-icon, - .margin-item-icon { - display: flex; - justify-content: center; - align-items: center; - padding: 6px; - flex-grow: 1; - border-right: 1px solid $color-gray-60; - cursor: pointer; - - &:hover, - &.selected { - svg { - fill: $color-primary; - } - } - svg { - height: 14px; - width: 14px; - fill: $color-gray-30; - } - } - - :last-child { - border: none; - } - } - - .wrapper { - display: flex; - height: 26px; - - .input-element { - margin: 0; - margin-top: 3px; - height: 26px; - } - } - } - - .layout-container { - border: 1px solid $color-gray-60; - border-radius: $br3; - margin: 5px 0; - .layout-entry { - display: flex; - align-items: center; - color: $color-gray-20; - height: 38px; - cursor: pointer; - &:hover { - svg { - fill: $color-primary; - } - } - } - - .layout-info { - font-size: $fs12; - width: 100%; - overflow-x: hidden; - text-overflow: ellipsis; - white-space: nowrap; - &::first-letter { - text-transform: capitalize; - } - } - - .layout-body { - display: flex; - align-items: center; - margin: 7px; - .selects-wrapper { - width: 100%; - margin-left: 12px; - select { - text-transform: capitalize; - } - option { - text-transform: capitalize; - } - } - - .orientation-grid { - background-color: $color-gray-60; - .button-wrapper { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - grid-template-rows: 1fr 1fr 1fr; - width: 47px; - height: 47px; - border: 1px solid $color-gray-30; - margin: 12px; - .orientation { - background: none; - border: none; - height: 14px; - width: 14px; - display: flex; - justify-content: center; - align-items: center; - padding: 2px; - cursor: pointer; - - .icon { - display: flex; - justify-content: center; - align-items: center; - svg { - fill: none; - height: 10px; - width: 10px; - } - &.rotated { - transform: rotate(90deg); - } - } - - &.active { - .icon { - svg { - fill: $color-primary; - } - } - } - &:hover { - .icon { - svg { - fill: $color-gray-20; - } - } - } - } - } - &.col { - .button-wrapper { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - grid-template-rows: 1fr; - .orientation { - height: 100%; - justify-content: space-between; - flex-direction: column; - } - } - } - &.row { - .button-wrapper { - display: grid; - grid-template-rows: 1fr 1fr 1fr; - grid-template-columns: 1fr; - .orientation { - width: 100%; - justify-content: space-between; - padding: 2px; - } - } - } - } - } - } - - .grid-columns { - border: 1px solid $color-gray-60; - padding: 5px; - min-height: 38px; - display: flex; - flex-direction: column; - align-items: center; - &:not(:first-child) { - margin-top: 5px; - } - .grid-columns-header { - display: flex; - justify-content: center; - align-items: center; - width: 100%; - height: 100%; - flex-grow: 1; - min-height: 36px; - .columns-info { - flex-grow: 1; - font-size: 12px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - .expand-icon, - .add-column { - cursor: pointer; - background-color: transparent; - border: none; - display: flex; - justify-content: center; - align-items: center; - - &.active, - &:hover { - svg { - fill: $color-primary; - } - } - } - - .add-column svg { - height: 12px; - width: 12px; - fill: $color-gray-20; - } - } - .columns-info-wrapper { - .column-info { - display: grid; - grid-template-columns: 35px 1fr 1fr auto; - background-color: $color-gray-60; - padding: 3px; - border-radius: 3px; - border: 1px solid transparent; - - &:hover { - border: 1px solid $color-primary; - } - - &:not(:first-child) { - margin-top: 3px; - } - .direction-grid-icon { - display: flex; - justify-content: center; - align-items: center; - padding: 5px; - } - input { - background-color: $color-gray-60; - } - .grid-column-value, - .grid-column-unit { - display: flex; - justify-content: center; - align-items: center; - height: 30px; - &.active, - &:focus, - &:focus-within { - border-bottom: 1px solid $color-primary; - } - } - .grid-column-unit-selector { - border: none; - border-bottom: 1px solid $color-gray-30; - margin: 0.25rem 0; - height: 23px; - width: 100%; - &:hover { - border-bottom: 1px solid $color-gray-20; - } - } - - .remove-grid-column { - cursor: pointer; - background-color: transparent; - border: none; - display: flex; - justify-content: center; - align-items: center; - margin-left: 40px; - svg { - height: 12px; - width: 12px; - fill: $color-gray-20; - } - &.active, - &:hover { - svg { - fill: $color-primary; - } - } - } - } - } - } - .manage-grid-columns { - margin-left: 60px; - margin-bottom: 10px; - .grid-auto, - .grid-manual { - display: grid; - grid-template-columns: 1fr 1fr; - .grid-columns-auto, - .grid-rows-auto { - display: grid; - grid-template-columns: 20px 1fr; - .icon { - display: flex; - justify-content: center; - align-items: center; - } - input { - width: 80%; - } - } - } - .grid-manual { - .input-wrapper { - display: grid; - grid-template-columns: 1fr 1fr; - } - } - } -} - -.advanced-ops { - margin: 10px 0; - display: flex; - align-items: center; - cursor: pointer; - font-size: $fs12; - color: $color-gray-30; - border: none; - background-color: transparent; - padding: 0; - .icon { - display: flex; - justify-content: flex-start; - align-items: center; - margin-right: 10px; - svg { - fill: $color-gray-20; - } - } - - &:hover { - svg { - fill: $color-primary; - } - } -} -.advanced-ops-body { - .input-wrapper { - display: grid; - grid-template-columns: 1fr 1fr; - .input-element { - width: 100%; - &::after { - content: attr(alt); - width: 100px; - } - } - } -} - -.component-annotation { - background-color: $color-gray-60; - border: 1px solid $color-gray-60; - border-radius: 2px; - - .title { - display: flex; - align-items: center; - justify-content: space-between; - border-bottom: 1px solid $color-gray-50; - font-size: $fs12; - color: $color-gray-20; - padding: 0 10px; - - &.expandeable { - cursor: pointer; - } - - div { - display: flex; - align-items: center; - line-height: 2.5; - } - - .expand { - svg { - fill: $color-gray-20; - width: $size-2; - height: $size-2; - margin-right: 10px; - } - } - - .icon { - display: none; - cursor: pointer; - svg { - fill: $color-gray-20; - width: $size-4; - height: $size-4; - margin-left: 15px; - &.icon-cross { - width: $size-3; - height: $size-3; - } - } - - .icon-tick:hover, - .icon-pencil:hover { - fill: $color-primary; - } - - .icon-cross:hover, - .icon-trash:hover { - fill: $color-danger; - } - } - - &:hover { - .icon { - display: flex; - } - } - } - - &.editing { - border: 1px solid $color-primary; - .title .icon { - display: flex; - } - - textarea { - min-height: 250px; - } - } - - &.creating { - textarea { - min-height: 250px; - } - } - - .hidden { - display: none; - svg { - display: none; - } - } - - .counter { - text-align: right; - font-size: $fs11; - color: $color-gray-30; - margin: 0 10px 10px 0; - } - - // Auto growing text - .grow-wrap { - // easy way to plop the elements on top of each other and have them both sized based on the tallest one's height - display: grid; - - &:after { - // The space is needed to preventy jumpy behavior - content: attr(data-replicated-value) " "; - white-space: pre-wrap; - visibility: hidden; - } - - textarea { - background-color: $color-gray-60; - color: $color-white; - padding: 10px; - - border: none; - overflow: hidden; - outline: none; - - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; - - resize: none; /*remove the resize handle on the bottom right*/ - } - - textarea, - &:after { - /* Identical styling required!! */ - font: inherit; - font-size: $fs14; - overflow-wrap: anywhere; - - padding: 10px; - - /* Place on top of each other */ - grid-area: 1 / 1 / 2 / 2; - } - } -} - -.component-block-title { - border: none; - background: none; - - svg { - height: 8px; - width: 8px; - fill: $color-gray-20; - margin-right: 1rem; - transform: rotate(180deg); - } - - &.back { - cursor: pointer; - } -} diff --git a/frontend/resources/styles/main/partials/sidebar-interactions.scss b/frontend/resources/styles/main/partials/sidebar-interactions.scss deleted file mode 100644 index 77c39c9a95..0000000000 --- a/frontend/resources/styles/main/partials/sidebar-interactions.scss +++ /dev/null @@ -1,215 +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 - -.interactions-help { - font-size: $fs12; - padding: 7px $size-4; - margin: 0 -7px; - text-align: center; - - &.separator { - padding-bottom: $size-4; - border-bottom: 1px solid $color-black; - } -} - -.interactions-help-icon { - height: 32px; - width: 32px; - margin: $size-4 auto; - - svg { - fill: $color-gray-40; - height: 32px; - width: 32px; - } -} - -.interactions-summary { - cursor: pointer; - flex-basis: 0; - flex-grow: 1; - - .trigger-name { - font-size: $fs12; - color: $color-white; - } - - .action-summary { - font-size: $fs12; - color: $color-gray-20; - } -} - -.interactions-element { - display: flex; - align-items: center; - margin: 0 -7px; - padding: 0 7px; - - .element-label { - color: $color-gray-20; - font-size: $fs12; - width: 64px; - } - - &.separator { - border-top: 1px solid $color-black; - margin-top: $size-1; - } -} - -.interactions-pos-buttons { - margin-top: $size-2; - padding-top: $size-2; - padding-bottom: $size-2; - justify-content: space-between; - - .element-set-actions-button { - min-width: 18px; - min-height: 18px; - } - - svg { - height: 18px; - width: 18px; - } -} - -.interactions-way-buttons { - display: grid; - grid-template-columns: 1fr 1fr; - - & .input-radio { - margin-bottom: 0; - - & label { - color: $color-gray-20; - - &:before { - background-color: unset; - } - } - - & input[type="radio"]:checked { - & + label { - &:before { - background-color: $color-primary; - box-shadow: inset 0 0 0 5px $color-gray-50; - } - } - } - } -} - -.interactions-direction-buttons { - margin-top: $size-2; - padding-top: $size-2; - padding-bottom: $size-2; - justify-content: space-around; - - .element-set-actions-button { - min-width: 40px; - min-height: 13px; - } - - svg { - height: 13px; - width: 13px; - } -} - -.interactions-easing-icon { - display: flex; - justify-content: center; - align-items: center; - min-width: 30px; - min-height: 30px; - - & svg { - width: 12px; - height: 12px; - stroke: $color-gray-20; - } -} - -.flow-element { - display: flex; - align-items: center; - padding: $size-1; - - .element-label { - font-size: $fs11; - } - - .flow-name { - cursor: pointer; - } - - & input.element-name { - background: transparent; - border-color: $color-primary; - color: $color-white; - font-size: $fs11; - } -} - -.flow-button { - cursor: pointer; - display: flex; - justify-content: center; - align-items: center; - margin-right: $size-2; - - & svg { - height: 12px; - width: 12px; - fill: $color-gray-20; - } - - &:hover svg { - fill: $color-primary; - } -} - -.flow-badge { - cursor: pointer; - display: flex; - - & .content { - align-items: center; - background-color: $color-gray-50; - border-radius: $br4; - display: flex; - height: 24px; - - & svg { - height: 12px; - margin: 0 $size-2; - width: 12px; - fill: $color-gray-20; - } - - & span { - color: $color-gray-20; - font-size: $fs12; - margin-right: $size-4; - } - } - - &.selected .content, - &:hover .content { - background-color: $color-primary; - - & svg { - fill: $color-gray-60; - } - - & span { - color: $color-gray-60; - } - } -} diff --git a/frontend/resources/templates/index.mustache b/frontend/resources/templates/index.mustache index 14f26d76a8..77475d7e30 100644 --- a/frontend/resources/templates/index.mustache +++ b/frontend/resources/templates/index.mustache @@ -4,6 +4,7 @@ Penpot - Design Freedom for Teams + @@ -17,6 +18,9 @@ + {{#isDebug}} + + {{/isDebug}} diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index b711b1ead4..6c72136001 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -855,7 +855,8 @@ (fn [parent objects] (cond-> parent (ctl/grid-layout? parent) - (ctl/assign-cells objects)))) + (ctl/assign-cells objects))) + {:with-objects? true}) (pcb/reorder-grid-children parents) @@ -1490,6 +1491,30 @@ (rx/of (show-context-menu (-> params (assoc :kind :page :selected (:id page)))))))) +(defn show-track-context-menu + [{:keys [grid-id type index] :as params}] + (ptk/reify ::show-track-context-menu + ptk/WatchEvent + (watch [_ _ _] + (rx/of (show-context-menu + (-> params (assoc :kind :grid-track + :grid-id grid-id + :type type + :index index))))))) + +(defn show-grid-cell-context-menu + [{:keys [grid-id] :as params}] + (ptk/reify ::show-grid-cell-context-menu + ptk/WatchEvent + (watch [_ state _] + (let [objects (wsh/lookup-page-objects state) + grid (get objects grid-id) + cells (->> (get-in state [:workspace-grid-edition grid-id :selected]) + (map #(get-in grid [:layout-grid-cells %])))] + (rx/of (show-context-menu + (-> params (assoc :kind :grid-cells + :grid grid + :cells cells)))))))) (def hide-context-menu (ptk/reify ::hide-context-menu ptk/UpdateEvent @@ -1497,6 +1522,8 @@ (assoc-in state [:workspace-local :context-menu] nil)))) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Clipboard ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/frontend/src/app/main/data/workspace/changes.cljs b/frontend/src/app/main/data/workspace/changes.cljs index 9b7f1c54dc..061b3c5503 100644 --- a/frontend/src/app/main/data/workspace/changes.cljs +++ b/frontend/src/app/main/data/workspace/changes.cljs @@ -15,6 +15,7 @@ [app.common.logging :as log] [app.common.schema :as sm] [app.common.types.shape-tree :as ctst] + [app.common.types.shape.layout :as ctl] [app.common.uuid :as uuid] [app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.undo :as dwu] @@ -51,8 +52,8 @@ (defn update-shapes ([ids update-fn] (update-shapes ids update-fn nil)) - ([ids update-fn {:keys [reg-objects? save-undo? stack-undo? attrs ignore-tree page-id ignore-remote? ignore-touched undo-group] - :or {reg-objects? false save-undo? true stack-undo? false ignore-remote? false ignore-touched false}}] + ([ids update-fn {:keys [reg-objects? save-undo? stack-undo? attrs ignore-tree page-id ignore-remote? ignore-touched undo-group with-objects?] + :or {reg-objects? false save-undo? true stack-undo? false ignore-remote? false ignore-touched false with-objects? false}}] (dm/assert! "expected a valid coll of uuid's" @@ -70,14 +71,15 @@ update-layout-ids (->> ids (map (d/getf objects)) - (filter #(some update-layout-attr? (pcb/changed-attrs % objects update-fn {:attrs attrs}))) + (filter #(some update-layout-attr? (pcb/changed-attrs % objects update-fn {:attrs attrs :with-objects? with-objects?}))) (map :id)) changes (reduce (fn [changes id] (let [opts {:attrs attrs :ignore-geometry? (get ignore-tree id) - :ignore-touched ignore-touched}] + :ignore-touched ignore-touched + :with-objects? with-objects?}] (pcb/update-shapes changes [id] update-fn (d/without-nils opts)))) (-> (pcb/empty-changes it page-id) (pcb/set-save-undo? save-undo?) @@ -86,6 +88,8 @@ (cond-> undo-group (pcb/set-undo-group undo-group))) ids) + grid-ids (->> ids (filter (partial ctl/grid-layout? objects))) + changes (pcb/update-shapes changes grid-ids ctl/assign-cell-positions {:with-objects? true}) changes (pcb/reorder-grid-children changes ids) changes (add-undo-group changes state)] (rx/concat diff --git a/frontend/src/app/main/data/workspace/comments.cljs b/frontend/src/app/main/data/workspace/comments.cljs index ed0734e707..0c4850a404 100644 --- a/frontend/src/app/main/data/workspace/comments.cljs +++ b/frontend/src/app/main/data/workspace/comments.cljs @@ -13,7 +13,7 @@ [app.common.schema :as sm] [app.common.types.shape-tree :as ctst] [app.main.data.comments :as dcm] - [app.main.data.workspace.changes :as dwc] + [app.main.data.workspace.changes :as dch] [app.main.data.workspace.common :as dwco] [app.main.data.workspace.drawing :as dwd] [app.main.data.workspace.state-helpers :as wsh] @@ -148,7 +148,7 @@ (pcb/update-page-option :comment-threads-position assoc thread-id (select-keys thread [:position :frame-id])))] (rx/merge - (rx/of (dwc/commit-changes changes)) + (rx/of (dch/commit-changes changes)) (->> (rp/cmd! :update-comment-thread-position thread) (rx/catch #(rx/throw {:type :update-comment-thread-position})) (rx/ignore)))))))) diff --git a/frontend/src/app/main/data/workspace/grid_layout/editor.cljs b/frontend/src/app/main/data/workspace/grid_layout/editor.cljs index dc6e99fa59..9af194119f 100644 --- a/frontend/src/app/main/data/workspace/grid_layout/editor.cljs +++ b/frontend/src/app/main/data/workspace/grid_layout/editor.cljs @@ -6,6 +6,7 @@ (ns app.main.data.workspace.grid-layout.editor (:require + [app.common.data.macros :as dm] [app.common.geom.rect :as grc] [app.common.types.shape.layout :as ctl] [app.main.data.workspace.state-helpers :as wsh] @@ -25,14 +26,34 @@ (conj hover-set cell-id) (disj hover-set cell-id)))))))) -(defn select-grid-cell - [grid-id cell-id add?] - (ptk/reify ::select-grid-cell +(defn add-to-selection + ([grid-id cell-id] + (add-to-selection grid-id cell-id false)) + ([grid-id cell-id shift?] + (ptk/reify ::add-to-selection + ptk/UpdateEvent + (update [_ state] + (if shift? + (let [objects (wsh/lookup-page-objects state) + grid (get objects grid-id) + selected (or (dm/get-in state [:workspace-grid-edition grid-id :selected]) #{}) + selected (into selected [cell-id]) + cells (->> selected (map #(dm/get-in grid [:layout-grid-cells %]))) + + {:keys [first-row last-row first-column last-column]} (ctl/cells-coordinates cells) + new-selected + (into #{} + (map :id) + (ctl/cells-in-area grid first-row last-row first-column last-column))] + (assoc-in state [:workspace-grid-edition grid-id :selected] new-selected)) + (update-in state [:workspace-grid-edition grid-id :selected] (fnil conj #{}) cell-id)))))) + +(defn set-selection + [grid-id cell-id] + (ptk/reify ::set-selection ptk/UpdateEvent (update [_ state] - (if add? - (update-in state [:workspace-grid-edition grid-id :selected] (fnil conj #{}) cell-id) - (assoc-in state [:workspace-grid-edition grid-id :selected] #{cell-id}))))) + (assoc-in state [:workspace-grid-edition grid-id :selected] #{cell-id})))) (defn remove-selection [grid-id cell-id] diff --git a/frontend/src/app/main/data/workspace/groups.cljs b/frontend/src/app/main/data/workspace/groups.cljs index 656173f5e4..ff477d3207 100644 --- a/frontend/src/app/main/data/workspace/groups.cljs +++ b/frontend/src/app/main/data/workspace/groups.cljs @@ -125,7 +125,7 @@ [parent-id] (fn [parent] (assoc-in parent [:layout-grid-cells (:id target-cell) :shapes] [(:id group)])))) - (pcb/update-shapes grid-parents ctl/assign-cells) + (pcb/update-shapes grid-parents ctl/assign-cells {:with-objects? true}) (pcb/remove-objects ids-to-delete))] [group changes])) @@ -216,7 +216,7 @@ (cond-> changes (ctl/grid-layout? objects (:parent-id shape)) - (pcb/update-shapes [(:parent-id shape)] ctl/assign-cells)))) + (pcb/update-shapes [(:parent-id shape)] ctl/assign-cells {:with-objects? true})))) selected (->> (wsh/lookup-selected state) (remove #(ctn/has-any-copy-parent? objects (get objects %))) diff --git a/frontend/src/app/main/data/workspace/guides.cljs b/frontend/src/app/main/data/workspace/guides.cljs index 5df3d41a09..2229210103 100644 --- a/frontend/src/app/main/data/workspace/guides.cljs +++ b/frontend/src/app/main/data/workspace/guides.cljs @@ -11,7 +11,7 @@ [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] [app.common.types.page :as ctp] - [app.main.data.workspace.changes :as dwc] + [app.main.data.workspace.changes :as dch] [app.main.data.workspace.state-helpers :as wsh] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -35,7 +35,7 @@ (-> (pcb/empty-changes it) (pcb/with-page page) (pcb/update-page-option :guides assoc (:id guide) guide))] - (rx/of (dwc/commit-changes changes)))))) + (rx/of (dch/commit-changes changes)))))) (defn remove-guide [guide] (dm/assert! @@ -56,7 +56,7 @@ (-> (pcb/empty-changes it) (pcb/with-page page) (pcb/update-page-option :guides dissoc (:id guide)))] - (rx/of (dwc/commit-changes changes)))))) + (rx/of (dch/commit-changes changes)))))) (defn remove-guides [ids] diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index 3037b9f893..f9854062fe 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -98,11 +98,11 @@ (gsh/move delta))) [new-instance-shape new-instance-shapes _] - (ctst/clone-object main-instance-shape - (:parent-id main-instance-shape) - (:objects main-instance-page) - update-new-shape - update-original-shape) + (ctst/clone-shape main-instance-shape + (:parent-id main-instance-shape) + (:objects main-instance-page) + :update-new-shape update-new-shape + :update-original-shape update-original-shape) remap-frame (fn [shape] @@ -134,10 +134,9 @@ (let [component-root (d/seek #(nil? (:parent-id %)) (vals (:objects component))) [new-component-shape new-component-shapes _] - (ctst/clone-object component-root - nil - (get component :objects) - identity)] + (ctst/clone-shape component-root + nil + (get component :objects))] [new-component-shape new-component-shapes nil nil])))) @@ -185,7 +184,8 @@ (fn [shape objects] (-> shape (ctl/push-into-cell [(:id first-shape)] row column) - (ctl/assign-cells objects)))) + (ctl/assign-cells objects))) + {:with-objects? true}) (pcb/reorder-grid-children [(:parent-id first-shape)]))) changes) @@ -975,11 +975,12 @@ original-shape) [_ new-shapes _] - (ctst/clone-object component-shape + (ctst/clone-shape component-shape (:id parent-shape) (get component-page :objects) - update-new-shape - update-original-shape) + :update-new-shape update-new-shape + :update-original-shape update-original-shape + :dest-objects (get container :objects)) add-obj-change (fn [changes shape'] (update changes :redo-changes conj @@ -1037,11 +1038,11 @@ original-shape)) [_new-shape new-shapes updated-shapes] - (ctst/clone-object shape - (:id component-parent-shape) - (get page :objects) - update-new-shape - update-original-shape) + (ctst/clone-shape shape + (:id component-parent-shape) + (get page :objects) + :update-new-shape update-new-shape + :update-original-shape update-original-shape) add-obj-change (fn [changes shape'] (update changes :redo-changes conj diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index 497202d7c4..b6828a1941 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -499,8 +499,7 @@ changes (-> (pcb/add-object changes new-obj {:ignore-touched (and duplicating-component? child?)}) (pcb/amend-last-change #(assoc % :old-id (:id obj))) (cond-> (ctl/grid-layout? objects (:parent-id obj)) - (-> (pcb/update-shapes [(:parent-id obj)] ctl/assign-cells) - (pcb/update-shapes [(:parent-id obj)] ctl/check-deassigned-cells) + (-> (pcb/update-shapes [(:parent-id obj)] ctl/assign-cells {:with-objects? true}) (pcb/reorder-grid-children [(:parent-id obj)])))) changes (cond-> changes diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index e89f415ea7..cc29ceca61 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -9,19 +9,22 @@ [app.common.colors :as clr] [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.files.changes-builder :as pcb] [app.common.files.helpers :as cfh] + [app.common.files.shapes-helpers :as cfsh] + [app.common.geom.point :as gpt] [app.common.geom.shapes.flex-layout :as flex] [app.common.geom.shapes.grid-layout :as grid] [app.common.types.component :as ctc] [app.common.types.modifiers :as ctm] [app.common.types.shape.layout :as ctl] [app.common.uuid :as uuid] - [app.main.data.workspace.changes :as dwc] + [app.main.data.workspace.changes :as dch] [app.main.data.workspace.colors :as cl] [app.main.data.workspace.grid-layout.editor :as dwge] [app.main.data.workspace.modifiers :as dwm] [app.main.data.workspace.selection :as dwse] - [app.main.data.workspace.shapes :as dws] + [app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.undo :as dwu] [beicon.v2.core :as rx] @@ -68,13 +71,13 @@ :layout-grid-columns []}) (defn get-layout-initializer - [type from-frame? objects] + [type from-frame?] (let [[initial-layout-data calculate-params] (case type :flex [initial-flex-layout flex/calculate-params] :grid [initial-grid-layout grid/calculate-params])] - (fn [shape] + (fn [shape objects] (let [shape (-> shape (merge initial-layout-data) @@ -127,11 +130,11 @@ (let [objects (wsh/lookup-page-objects state) parent (get objects id) undo-id (js/Symbol) - layout-initializer (get-layout-initializer type from-frame? objects)] + layout-initializer (get-layout-initializer type from-frame?)] (rx/of (dwu/start-undo-transaction undo-id) - (dwc/update-shapes [id] layout-initializer) - (dwc/update-shapes (dm/get-prop parent :shapes) #(dissoc % :constraints-h :constraints-v)) + (dch/update-shapes [id] layout-initializer {:with-objects? true}) + (dch/update-shapes (dm/get-prop parent :shapes) #(dissoc % :constraints-h :constraints-v)) (ptk/data-event :layout/update [id]) (dwu/commit-undo-transaction undo-id)))))) @@ -167,22 +170,22 @@ group-index (cfh/get-index-replacement selected objects)] (rx/of (dwse/select-shapes ordered-ids) - (dws/create-artboard-from-selection new-shape-id parent-id group-index (:name (first selected-shapes))) + (dwsh/create-artboard-from-selection new-shape-id parent-id group-index (:name (first selected-shapes))) (cl/remove-all-fills [new-shape-id] {:color clr/black :opacity 1}) (create-layout-from-id new-shape-id type false) - (dwc/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto)) - (dwc/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix)) - (dws/delete-shapes page-id selected) + (dch/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto)) + (dch/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix)) + (dwsh/delete-shapes page-id selected) (ptk/data-event :layout/update [new-shape-id]) (dwu/commit-undo-transaction undo-id))) ;; Create Layout from selection (rx/of - (dws/create-artboard-from-selection new-shape-id) + (dwsh/create-artboard-from-selection new-shape-id) (cl/remove-all-fills [new-shape-id] {:color clr/black :opacity 1}) (create-layout-from-id new-shape-id type false) - (dwc/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto)) - (dwc/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix)))) + (dch/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto)) + (dch/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix)))) (rx/of (ptk/data-event :layout/update [new-shape-id]) (dwu/commit-undo-transaction undo-id))))))) @@ -195,7 +198,7 @@ (let [undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) - (dwc/update-shapes ids #(apply dissoc % layout-keys)) + (dch/update-shapes ids #(apply dissoc % layout-keys)) (ptk/data-event :layout/update ids) (dwu/commit-undo-transaction undo-id)))))) @@ -242,42 +245,114 @@ (watch [_ _ _] (let [undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) - (dwc/update-shapes ids (d/patch-object changes)) + (dch/update-shapes ids (d/patch-object changes)) (ptk/data-event :layout/update ids) (dwu/commit-undo-transaction undo-id)))))) (defn add-layout-track - [ids type value] - (assert (#{:row :column} type)) - (ptk/reify ::add-layout-column - ptk/WatchEvent - (watch [_ _ _] - (let [undo-id (js/Symbol)] - (rx/of (dwu/start-undo-transaction undo-id) - (dwc/update-shapes - ids - (fn [shape] - (case type - :row (ctl/add-grid-row shape value) - :column (ctl/add-grid-column shape value)))) - (ptk/data-event :layout/update ids) - (dwu/commit-undo-transaction undo-id)))))) + ([ids type value] + (add-layout-track ids type value nil)) + ([ids type value index] + (assert (#{:row :column} type)) + (ptk/reify ::add-layout-track + ptk/WatchEvent + (watch [_ _ _] + (let [undo-id (js/Symbol)] + (rx/of (dwu/start-undo-transaction undo-id) + (dch/update-shapes + ids + (fn [shape] + (case type + :row (ctl/add-grid-row shape value index) + :column (ctl/add-grid-column shape value index)))) + (ptk/data-event :layout/update ids) + (dwu/commit-undo-transaction undo-id))))))) (defn remove-layout-track + [ids type index & {:keys [with-shapes?] :or {with-shapes? false}}] + (assert (#{:row :column} type)) + + (ptk/reify ::remove-layout-track + ptk/WatchEvent + (watch [_ state _] + (let [undo-id (js/Symbol)] + (let [objects (wsh/lookup-page-objects state) + + shapes-to-delete + (when with-shapes? + (->> ids + (mapcat + (fn [id] + (let [shape (get objects id)] + (if (= type :column) + (ctl/shapes-by-column shape index) + (ctl/shapes-by-row shape index))))) + (into #{})))] + (rx/of (dwu/start-undo-transaction undo-id) + (if shapes-to-delete + (dwsh/delete-shapes shapes-to-delete) + (rx/empty)) + (dch/update-shapes + ids + (fn [shape objects] + (case type + :row (ctl/remove-grid-row shape index objects) + :column (ctl/remove-grid-column shape index objects))) + {:with-objects? true}) + (ptk/data-event :layout/update ids) + (dwu/commit-undo-transaction undo-id))))))) + +(defn duplicate-layout-track [ids type index] (assert (#{:row :column} type)) - (ptk/reify ::remove-layout-column + (ptk/reify ::duplicate-layout-track ptk/WatchEvent - (watch [_ _ _] - (let [undo-id (js/Symbol)] + (watch [it state _] + (let [file-id (:current-file-id state) + page (wsh/lookup-page state) + objects (:objects page) + libraries (wsh/get-libraries state) + library-data (wsh/get-file state file-id) + shape-id (first ids) + base-shape (get objects shape-id) + + shapes-by-track + (if (= type :column) + (ctl/shapes-by-column base-shape index false) + (ctl/shapes-by-row base-shape index false)) + + ;; Change to set in order to use auxiliary functions + selected (set shapes-by-track) + + changes + (->> (dwse/prepare-duplicate-changes objects page selected (gpt/point 0 0) it libraries library-data file-id) + (dwse/duplicate-changes-update-indices objects selected)) + + ;; Creates a map with shape-id => duplicated-shape-id + ids-map + (->> changes + :redo-changes + (filter #(= (:type %) :add-obj)) + (filter #(selected (:old-id %))) + (map #(vector (:old-id %) (get-in % [:obj :id]))) + (into {})) + + changes + (-> changes + (pcb/update-shapes + ids + (fn [shape objects] + ;; The duplication could have altered the grid so we restore the values, we'll calculate the good ones now + (let [shape (merge shape (select-keys base-shape [:layout-grid-cells :layout-grid-columns :layout-grid-rows]))] + (case type + :row (ctl/duplicate-row shape objects index ids-map) + :column (ctl/duplicate-column shape objects index ids-map)))) + {:with-objects? true})) + + undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) - (dwc/update-shapes - ids - (fn [shape objects] - (case type - :row (ctl/remove-grid-row shape index objects) - :column (ctl/remove-grid-column shape index objects)))) + (dch/commit-changes changes) (ptk/data-event :layout/update ids) (dwu/commit-undo-transaction undo-id)))))) @@ -290,7 +365,7 @@ (watch [_ _ _] (let [undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) - (dwc/update-shapes + (dch/update-shapes ids (fn [shape] (case type @@ -336,7 +411,7 @@ :row :layout-grid-rows :column :layout-grid-columns)] (rx/of (dwu/start-undo-transaction undo-id) - (dwc/update-shapes + (dch/update-shapes ids (fn [shape] (-> shape @@ -428,15 +503,16 @@ parent-ids (->> ids (map #(cfh/get-parent-id objects %))) undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) - (dwc/update-shapes ids (d/patch-object changes)) - (dwc/update-shapes children-ids (partial fix-child-sizing objects changes)) - (dwc/update-shapes + (dch/update-shapes ids (d/patch-object changes)) + (dch/update-shapes children-ids (partial fix-child-sizing objects changes)) + (dch/update-shapes parent-ids (fn [parent objects] (-> parent (fix-parent-sizing objects (set ids) changes) (cond-> (ctl/grid-layout? parent) - (ctl/assign-cells objects))))) + (ctl/assign-cells objects)))) + {:with-objects? true}) (ptk/data-event :layout/update ids) (dwu/commit-undo-transaction undo-id)))))) @@ -449,7 +525,7 @@ (rx/of (dwu/start-undo-transaction undo-id) - (dwc/update-shapes + (dch/update-shapes [layout-id] (fn [shape] (->> ids @@ -472,7 +548,7 @@ (let [undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) - (dwc/update-shapes + (dch/update-shapes [layout-id] (fn [shape objects] (case mode @@ -521,7 +597,40 @@ (ctl/assign-cells objects))] (-> shape - (d/update-in-when [:layout-grid-cells (:id target-cell)] assoc :position :area)))))) + (d/update-in-when [:layout-grid-cells (:id target-cell)] assoc :position :area))))) + {:with-objects? true}) + (dwge/clean-selection layout-id) + (ptk/data-event :layout/update [layout-id]) + (dwu/commit-undo-transaction undo-id)))))) + +(defn merge-cells + [layout-id ids] + + (ptk/reify ::merge-cells + ptk/WatchEvent + (watch [_ _ _] + (let [undo-id (js/Symbol)] + (rx/of + (dwu/start-undo-transaction undo-id) + (dch/update-shapes + [layout-id] + (fn [shape objects] + (let [cells (->> ids (map #(get-in shape [:layout-grid-cells %]))) + + {:keys [first-row first-column last-row last-column]} + (ctl/cells-coordinates cells) + + target-cell + (ctl/get-cell-by-position shape first-row first-column)] + (-> shape + (ctl/resize-cell-area + (:row target-cell) (:column target-cell) + first-row + first-column + (inc (- last-row first-row)) + (inc (- last-column first-column))) + (ctl/assign-cells objects)))) + {:with-objects? true}) (dwge/clean-selection layout-id) (ptk/data-event :layout/update [layout-id]) (dwu/commit-undo-transaction undo-id)))))) @@ -531,14 +640,13 @@ (ptk/reify ::update-grid-cell-position ptk/WatchEvent - (watch [_ state _] - (let [objects (wsh/lookup-page-objects state) - undo-id (js/Symbol)] + (watch [_ _ _] + (let [undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) - (dwc/update-shapes + (dch/update-shapes [layout-id] - (fn [shape] + (fn [shape objects] (let [prev-data (-> (dm/get-in shape [:layout-grid-cells cell-id]) (select-keys [:row :column :row-span :column-span])) @@ -547,6 +655,62 @@ (ctl/resize-cell-area (:row prev-data) (:column prev-data) (:row new-data) (:column new-data) (:row-span new-data) (:column-span new-data)) - (ctl/assign-cells objects))))) + (ctl/assign-cells objects)))) + {:with-objects? true}) + (ptk/data-event :layout/update [layout-id]) + (dwu/commit-undo-transaction undo-id)))))) + + +(defn create-cell-board + [layout-id cell-ids] + (ptk/reify ::create-cell-board + ptk/WatchEvent + (watch [it state _] + (let [page-id (:current-page-id state) + objects (wsh/lookup-page-objects state) + frame-id (uuid/next) + + undo-id (js/Symbol) + + shape (get objects layout-id) + cells (->> cell-ids (map #(get-in shape [:layout-grid-cells %]))) + selected (into #{} (mapcat :shapes) cells) + + {:keys [first-row first-column last-row last-column]} (ctl/cells-coordinates cells) + + target-cell (ctl/get-cell-by-position shape first-row first-column) + + [_ changes] + (-> (pcb/empty-changes it page-id) + (pcb/with-objects objects) + (cond-> (d/not-empty? selected) + (cfsh/prepare-create-artboard-from-selection + frame-id layout-id objects selected 0 nil true (:id target-cell))) + + (cond-> (empty? (seq selected)) + (cfsh/prepare-create-empty-artboard + frame-id layout-id objects 0 nil true (:id target-cell)))) + + changes + (-> changes + (pcb/update-shapes + [frame-id] + (fn [shape] + (-> shape + (assoc :layout-item-h-sizing :fill) + (assoc :layout-item-v-sizing :fill)))) + (pcb/update-shapes + [layout-id] + (fn [shape] + (let [new-row-span (inc (- last-row first-row)) + new-col-span (inc (- last-column first-column))] + (-> shape + (ctl/resize-cell-area + (:row target-cell) (:column target-cell) + first-row first-column new-row-span new-col-span))))))] + + (rx/of + (dwu/start-undo-transaction undo-id) + (dch/commit-changes changes) (ptk/data-event :layout/update [layout-id]) (dwu/commit-undo-transaction undo-id)))))) diff --git a/frontend/src/app/main/data/workspace/shapes.cljs b/frontend/src/app/main/data/workspace/shapes.cljs index 42422fa521..9a95a69791 100644 --- a/frontend/src/app/main/data/workspace/shapes.cljs +++ b/frontend/src/app/main/data/workspace/shapes.cljs @@ -41,12 +41,11 @@ (watch [it state _] (let [page-id (:current-page-id state) objects (wsh/lookup-page-objects state page-id) - selected (wsh/lookup-selected state) [shape changes] (-> (pcb/empty-changes it page-id) (pcb/with-objects objects) - (cfsh/prepare-add-shape shape objects selected)) + (cfsh/prepare-add-shape shape objects)) changes (cond-> changes (cfh/text-shape? shape) diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 5cc82e5b07..16904a1eee 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -559,8 +559,8 @@ (fn [[_ target-frame drop-index]] (let [undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) - (dwm/apply-modifiers {:undo-transation? false}) (move-shapes-to-frame ids target-frame drop-index) + (dwm/apply-modifiers {:undo-transation? false}) (finish-transform) (dwu/commit-undo-transaction undo-id)))))))))))))) @@ -628,9 +628,13 @@ (ctl/swap-shapes id (:id next-cell))))) parent))] (-> changes - (pcb/update-shapes [(:id parent)] (fn [shape] (-> shape - (assoc :layout-grid-cells layout-grid-cells) - (ctl/assign-cells objects)))) + (pcb/update-shapes + [(:id parent)] + (fn [shape] + (-> shape + (assoc :layout-grid-cells layout-grid-cells) + ;; We want the previous objects value + (ctl/assign-cells objects)))) (pcb/reorder-grid-children [(:id parent)])))) changes @@ -868,7 +872,9 @@ (pcb/update-shapes moving-shapes-ids #(cond-> % (cfh/frame-shape? %) (assoc :hide-in-viewer true))) (pcb/update-shapes shape-ids-to-detach ctk/detach-shape) (pcb/change-parent frame-id moving-shapes drop-index) - (pcb/reorder-grid-children [frame-id]) + (cond-> (ctl/grid-layout? objects frame-id) + (-> (pcb/update-shapes [frame-id] ctl/assign-cell-positions {:with-objects? true}) + (pcb/reorder-grid-children [frame-id]))) (pcb/remove-objects empty-parents))] (when (and (some? frame-id) (d/not-empty? changes)) diff --git a/frontend/src/app/main/render.cljs b/frontend/src/app/main/render.cljs index 5b8620b462..c6bf57e9ab 100644 --- a/frontend/src/app/main/render.cljs +++ b/frontend/src/app/main/render.cljs @@ -67,17 +67,24 @@ (defn- calculate-dimensions [objects aspect-ratio] - (let [bounds - (->> (ctst/get-root-objects objects) - (map (partial gsb/get-object-bounds objects)) - (grc/join-rects))] - (-> bounds - (update :x mth/finite 0) - (update :y mth/finite 0) - (update :width mth/finite 100000) - (update :height mth/finite 100000) - (grc/update-rect :position) - (grc/fix-aspect-ratio aspect-ratio)))) + (let [root-objects (ctst/get-root-objects objects)] + (if (empty? root-objects) + ;; Empty page, we create an arbitrary rect for the thumbnail + (-> (grc/make-rect {:x 0 :y 0 :width 100 :height 100}) + (grc/update-rect :position) + (grc/fix-aspect-ratio aspect-ratio)) + + (let [bounds + (->> root-objects + (map (partial gsb/get-object-bounds objects)) + (grc/join-rects))] + (-> bounds + (update :x mth/finite 0) + (update :y mth/finite 0) + (update :width mth/finite 100000) + (update :height mth/finite 100000) + (grc/update-rect :position) + (grc/fix-aspect-ratio aspect-ratio)))))) (declare shape-wrapper-factory) @@ -201,6 +208,7 @@ (let [objects (:objects data) shapes (cfh/get-immediate-children objects) dim (calculate-dimensions objects aspect-ratio) + _ (prn ">>DIM" dim) vbox (format-viewbox dim) bgcolor (dm/get-in data [:options :background] default-color) diff --git a/frontend/src/app/main/ui/alert.cljs b/frontend/src/app/main/ui/alert.cljs index 7511c390b1..8c7ee4b020 100644 --- a/frontend/src/app/main/ui/alert.cljs +++ b/frontend/src/app/main/ui/alert.cljs @@ -9,7 +9,6 @@ (:require [app.main.data.modal :as modal] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -27,8 +26,8 @@ hint accept-label accept-style] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - on-accept (or on-accept identity) + + (let [on-accept (or on-accept identity) message (or message (tr "ds.alert-title")) accept-label (or accept-label (tr "ds.alert-ok")) accept-style (or accept-style :danger) @@ -50,54 +49,26 @@ (on-accept props)))] (->> (events/listen js/document "keydown" on-keydown) (partial events/unlistenByKey)))) - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-container)} - [:div {:class (stl/css :modal-header)} - [:h2 {:class (stl/css :modal-title)} title] - [:button {:class (stl/css :modal-close-btn) - :on-click accept-fn} i/close-refactor]] + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} title] + [:button {:class (stl/css :modal-close-btn) + :on-click accept-fn} i/close-refactor]] - [:div {:class (stl/css :modal-content)} - (when (and (string? message) (not= message "")) - [:h3 {:class (stl/css :modal-msg)} message]) - (when (and (string? scd-message) (not= scd-message "")) - [:h3 {:class (stl/css :modal-scd-msg)} scd-message]) - (when (string? hint) - [:p {:class (stl/css :modal-hint)} hint])] + [:div {:class (stl/css :modal-content)} + (when (and (string? message) (not= message "")) + [:h3 {:class (stl/css :modal-msg)} message]) + (when (and (string? scd-message) (not= scd-message "")) + [:h3 {:class (stl/css :modal-scd-msg)} scd-message]) + (when (string? hint) + [:p {:class (stl/css :modal-hint)} hint])] - [:div {:class (stl/css :modal-footer)} - [:div {:class (stl/css :action-buttons)} - [:input {:class (stl/css-case :accept-btn true - :danger (= accept-style :danger) - :primary (= accept-style :primary)) - :type "button" - :value accept-label - :on-click accept-fn}]]]]] - - - [:div.modal-overlay - [:div.modal-container.alert-dialog - [:div.modal-header - [:div.modal-header-title - [:h2 title]] - [:div.modal-close-button - {:on-click accept-fn} i/close]] - - [:div.modal-content - (when (and (string? message) (not= message "")) - [:h3 message]) - (when (and (string? scd-message) (not= scd-message "")) - [:h3 scd-message]) - (when (string? hint) - [:p hint])] - - [:div.modal-footer - [:div.action-buttons - [:input.accept-button - {:class (dom/classnames - :danger (= accept-style :danger) - :primary (= accept-style :primary)) - :type "button" - :value accept-label - :on-click accept-fn}]]]]]))) + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:input {:class (stl/css-case :accept-btn true + :danger (= accept-style :danger) + :primary (= accept-style :primary)) + :type "button" + :value accept-label + :on-click accept-fn}]]]]])) diff --git a/frontend/src/app/main/ui/alert.scss b/frontend/src/app/main/ui/alert.scss index 066381d194..2d4a8b1d78 100644 --- a/frontend/src/app/main/ui/alert.scss +++ b/frontend/src/app/main/ui/alert.scss @@ -11,39 +11,45 @@ &.transparent { background-color: transparent; } - .modal-container { - @extend .modal-container-base; - .modal-header { - margin-bottom: $s-24; - .modal-title { - @include tabTitleTipography; - color: var(--modal-title-foreground-color); - } - .modal-close-btn { - @extend .modal-close-btn-base; - } - } - .modal-content { - @include titleTipography; - margin-bottom: $s-24; - .modal-hint { - @include titleTipography; - } - } - .modal-footer { - .action-buttons { - @extend .modal-action-btns; - .cancel-button { - @extend .modal-cancel-btn; - } - .accept-btn { - @extend .modal-accept-btn; - &.danger { - @extend .modal-danger-btn; - } - } - } - } +} +.modal-container { + @extend .modal-container-base; +} + +.modal-header { + margin-bottom: $s-24; +} + +.modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); +} + +.modal-close-btn { + @extend .modal-close-btn-base; +} + +.modal-content { + @include titleTipography; + margin-bottom: $s-24; +} + +.modal-hint { + @include titleTipography; +} + +.action-buttons { + @extend .modal-action-btns; +} + +.cancel-button { + @extend .modal-cancel-btn; +} + +.accept-btn { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; } } diff --git a/frontend/src/app/main/ui/auth.cljs b/frontend/src/app/main/ui/auth.cljs index 37f5398722..fe84732526 100644 --- a/frontend/src/app/main/ui/auth.cljs +++ b/frontend/src/app/main/ui/auth.cljs @@ -12,7 +12,6 @@ [app.main.ui.auth.recovery :refer [recovery-page]] [app.main.ui.auth.recovery-request :refer [recovery-request-page]] [app.main.ui.auth.register :refer [register-page register-success-page register-validate-page]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -20,102 +19,57 @@ (mf/defc terms-login [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - show-all? (and cf/terms-of-service-uri cf/privacy-policy-uri) + (let [show-all? (and cf/terms-of-service-uri cf/privacy-policy-uri) show-terms? (some? cf/terms-of-service-uri) show-privacy? (some? cf/privacy-policy-uri)] - (if new-css-system - (when show-all? - [:div {:class (stl/css :terms-login)} - (when show-terms? - [:a {:href cf/terms-of-service-uri :target "_blank"} (tr "auth.terms-of-service")]) + (when show-all? + [:div {:class (stl/css :terms-login)} + (when show-terms? + [:a {:href cf/terms-of-service-uri :target "_blank"} (tr "auth.terms-of-service")]) - (when show-all? - [:span (tr "labels.and")]) + (when show-all? + [:span (tr "labels.and")]) - (when show-privacy? - [:a {:href cf/privacy-policy-uri :target "_blank"} (tr "auth.privacy-policy")])]) - - (when show-all? - [:div.terms-login - (when show-terms? - [:a {:href cf/terms-of-service-uri :target "_blank"} (tr "auth.terms-of-service")]) - - (when show-all? - [:span (tr "labels.and")]) - - (when show-privacy? - [:a {:href cf/privacy-policy-uri :target "_blank"} (tr "auth.privacy-policy")])])))) + (when show-privacy? + [:a {:href cf/privacy-policy-uri :target "_blank"} (tr "auth.privacy-policy")])]))) (mf/defc auth [{:keys [route] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - section (get-in route [:data :name]) + (let [section (get-in route [:data :name]) params (:query-params route) show-illustration? (contains? cf/flags :login-illustration)] (mf/use-effect #(dom/set-html-title (tr "title.default"))) - (if new-css-system - [: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-case :auth-section true + :no-illustration (not show-illustration?))} + (when show-illustration? + [: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}] + [:section {:class (stl/css :auth-content)} + [:* + [: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) - [:& terms-login])]] - - ;; OLD - [:main.auth - [:section.auth-sidebar - [:a.logo {:href "#/"} - [:span {:aria-hidden true} i/logo] - [:span.hidden-name "Home"]] - [:span.tagline (tr "auth.sidebar-tagline")]] - - [:section.auth-content - (case section - :auth-register - [:& register-page {:params params}] - - :auth-register-validate - [:& register-validate-page {:params params}] - - :auth-register-success - [:& register-success-page {:params params}] - - :auth-login - [:& login-page {:params params}] - - :auth-recovery-request - [:& recovery-request-page] - - :auth-recovery - [:& recovery-page {:params params}]) - - [:& terms-login {}]]]))) + (when (contains? #{:auth-login :auth-register} section) + [:& terms-login])]])) diff --git a/frontend/src/app/main/ui/auth/login.cljs b/frontend/src/app/main/ui/auth/login.cljs index 249ed8e99e..abf47e0404 100644 --- a/frontend/src/app/main/ui/auth/login.cljs +++ b/frontend/src/app/main/ui/auth/login.cljs @@ -18,7 +18,6 @@ [app.main.ui.components.button-link :as bl] [app.main.ui.components.forms :as fm] [app.main.ui.components.link :as lk] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.ui.messages :as msgs] [app.util.dom :as dom] @@ -94,8 +93,7 @@ (mf/defc login-form [{:keys [params on-success-callback origin] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - initial (mf/use-memo (mf/deps params) (constantly params)) + (let [initial (mf/use-memo (mf/deps params) (constantly params)) error (mf/use-state false) form (fm/use-form :spec ::login-form :validators [handle-error-messages] @@ -157,156 +155,86 @@ (mf/use-fn #(st/emit! (rt/nav :auth-recovery-request)))] - (if new-css-system - [:* - (when-let [message @error] - [:& msgs/inline-banner - {:type :warning - :content message - :on-close #(reset! error nil) - :data-test "login-banner" - :role "alert"}]) + [:* + (when-let [message @error] + [:& msgs/inline-banner + {:type :warning + :content message + :on-close #(reset! error nil) + :data-test "login-banner" + :role "alert"}]) - [:& fm/form {:on-submit on-submit :form form} - [:div {:class (stl/css :fields-row)} - [:& fm/input - {:name :email - :type "email" - :label (tr "auth.email") - :class (stl/css :form-field)}]] + [:& fm/form {:on-submit on-submit :form form} + [:div {:class (stl/css :fields-row)} + [:& fm/input + {:name :email + :type "email" + :label (tr "auth.email") + :class (stl/css :form-field)}]] - [:div {:class (stl/css :fields-row)} - [:& fm/input - {:type "password" - :name :password - :label (tr "auth.password") - :class (stl/css :form-field)}]] + [:div {:class (stl/css :fields-row)} + [:& fm/input + {:type "password" + :name :password + :label (tr "auth.password") + :class (stl/css :form-field)}]] - (when (and (not= origin :viewer) - (or (contains? cf/flags :login) - (contains? cf/flags :login-with-password))) - [:div {:class (stl/css :fields-row :forgot-password)} - [:& lk/link {:action on-recovery-request - :data-test "forgot-password"} - (tr "auth.forgot-password")]]) + (when (and (not= origin :viewer) + (or (contains? cf/flags :login) + (contains? cf/flags :login-with-password))) + [:div {:class (stl/css :fields-row :forgot-password)} + [:& lk/link {:action on-recovery-request + :data-test "forgot-password"} + (tr "auth.forgot-password")]]) - [:div {:class (stl/css :buttons-stack)} - (when (or (contains? cf/flags :login) - (contains? cf/flags :login-with-password)) - [:> fm/submit-button* - {:label (tr "auth.login-submit") - :data-test "login-submit" - :class (stl/css :login-button)}]) + [:div {:class (stl/css :buttons-stack)} + (when (or (contains? cf/flags :login) + (contains? cf/flags :login-with-password)) + [:> fm/submit-button* + {:label (tr "auth.login-submit") + :data-test "login-submit" + :class (stl/css :login-button)}]) - (when (contains? cf/flags :login-with-ldap) - [:> fm/submit-button* - {:label (tr "auth.login-with-ldap-submit") - :on-click on-submit-ldap}])]]] - - - ;; OLD - [:* - (when-let [message @error] - [:& msgs/inline-banner - {:type :warning - :content message - :on-close #(reset! error nil) - :data-test "login-banner" - :role "alert"}]) - - [:& fm/form {:on-submit on-submit :form form} - [:div.fields-row - [:& fm/input - {:name :email - :type "email" - :help-icon i/at - :label (tr "auth.email") - :class (stl/css :form-field)}]] - - [:div.fields-row - [:& fm/input - {:type "password" - :name :password - :help-icon i/eye - :label (tr "auth.password") - :class (stl/css :form-field)}]] - - [:div.buttons-stack - (when (or (contains? cf/flags :login) - (contains? cf/flags :login-with-password)) - [:> fm/submit-button* - {:label (tr "auth.login-submit") - :data-test "login-submit"}]) - - (when (contains? cf/flags :login-with-ldap) - [:> fm/submit-button* - {:label (tr "auth.login-with-ldap-submit") - :on-click on-submit-ldap}])]]]))) + (when (contains? cf/flags :login-with-ldap) + [:> fm/submit-button* + {:label (tr "auth.login-with-ldap-submit") + :on-click on-submit-ldap}])]]])) (mf/defc login-buttons [{:keys [params] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - - login-with-google (mf/use-fn (mf/deps params) #(login-with-oidc % :google params)) + (let [login-with-google (mf/use-fn (mf/deps params) #(login-with-oidc % :google params)) login-with-github (mf/use-fn (mf/deps params) #(login-with-oidc % :github params)) login-with-gitlab (mf/use-fn (mf/deps params) #(login-with-oidc % :gitlab params)) login-with-oidc (mf/use-fn (mf/deps params) #(login-with-oidc % :oidc params))] - (if new-css-system - [:div {:class (stl/css :auth-buttons)} - (when (contains? cf/flags :login-with-google) - [:& bl/button-link {:on-click login-with-google - :icon i/brand-google - :label (tr "auth.login-with-google-submit") - :class (stl/css :login-btn :btn-google-auth)}]) + [:div {:class (stl/css :auth-buttons)} + (when (contains? cf/flags :login-with-google) + [:& bl/button-link {:on-click login-with-google + :icon i/brand-google + :label (tr "auth.login-with-google-submit") + :class (stl/css :login-btn :btn-google-auth)}]) - (when (contains? cf/flags :login-with-github) - [:& bl/button-link {:on-click login-with-github - :icon i/brand-github - :label (tr "auth.login-with-github-submit") - :class (stl/css :login-btn :btn-github-auth)}]) + (when (contains? cf/flags :login-with-github) + [:& bl/button-link {:on-click login-with-github + :icon i/brand-github + :label (tr "auth.login-with-github-submit") + :class (stl/css :login-btn :btn-github-auth)}]) - (when (contains? cf/flags :login-with-gitlab) - [:& bl/button-link {:on-click login-with-gitlab - :icon i/brand-gitlab - :label (tr "auth.login-with-gitlab-submit") - :class (stl/css :login-btn :btn-gitlab-auth)}]) + (when (contains? cf/flags :login-with-gitlab) + [:& bl/button-link {:on-click login-with-gitlab + :icon i/brand-gitlab + :label (tr "auth.login-with-gitlab-submit") + :class (stl/css :login-btn :btn-gitlab-auth)}]) - (when (contains? cf/flags :login-with-oidc) - [:& bl/button-link {:on-click login-with-oidc - :icon i/brand-openid - :label (tr "auth.login-with-oidc-submit") - :class (stl/css :login-btn :btn-oidc-auth)}])] - - [:div.auth-buttons - (when (contains? cf/flags :login-with-google) - [:& bl/button-link {:on-click login-with-google - :icon i/brand-google - :label (tr "auth.login-with-google-submit") - :class "btn-google-auth"}]) - - (when (contains? cf/flags :login-with-github) - [:& bl/button-link {:on-click login-with-github - :icon i/brand-github - :label (tr "auth.login-with-github-submit") - :class "btn-github-auth"}]) - - (when (contains? cf/flags :login-with-gitlab) - [:& bl/button-link {:on-click login-with-gitlab - :icon i/brand-gitlab - :label (tr "auth.login-with-gitlab-submit") - :class "btn-gitlab-auth"}]) - - (when (contains? cf/flags :login-with-oidc) - [:& bl/button-link {:on-click login-with-oidc - :icon i/brand-openid - :label (tr "auth.login-with-oidc-submit") - :class "btn-github-auth"}])]))) + (when (contains? cf/flags :login-with-oidc) + [:& bl/button-link {:on-click login-with-oidc + :icon i/brand-openid + :label (tr "auth.login-with-oidc-submit") + :class (stl/css :login-btn :btn-oidc-auth)}])])) (mf/defc login-button-oidc [{:keys [params] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - login-oidc + (let [login-oidc (mf/use-fn (mf/deps params) (fn [event] @@ -317,69 +245,33 @@ (fn [event] (when (k/enter? event) (login-oidc event))))] - (if new-css-system - (when (contains? cf/flags :login-with-oidc) - [:div {:class (stl/css :link-entry :link-oidc)} - [:a {:tab-index "0" - :on-key-down handle-key-down - :on-click login-oidc} - (tr "auth.login-with-oidc-submit")]]) - - ;; OLD - (when (contains? cf/flags :login-with-oidc) - [:div.link-entry.link-oidc - [:a {:tab-index "0" - :on-key-down handle-key-down - :on-click login-oidc} - (tr "auth.login-with-oidc-submit")]])))) + (when (contains? cf/flags :login-with-oidc) + [:div {:class (stl/css :link-entry :link-oidc)} + [:a {:tab-index "0" + :on-key-down handle-key-down + :on-click login-oidc} + (tr "auth.login-with-oidc-submit")]]))) (mf/defc login-methods [{:keys [params on-success-callback origin] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:* - (when show-alt-login-buttons? - [:* - [:& login-buttons {:params params}] + [:* + (when show-alt-login-buttons? + [:* + [:& login-buttons {:params params}] - (when (or (contains? cf/flags :login) - (contains? cf/flags :login-with-password) - (contains? cf/flags :login-with-ldap)) - [:hr {:class (stl/css :separator)}])]) + (when (or (contains? cf/flags :login) + (contains? cf/flags :login-with-password) + (contains? cf/flags :login-with-ldap)) + [:hr {:class (stl/css :separator)}])]) - (when (or (contains? cf/flags :login) - (contains? cf/flags :login-with-password) - (contains? cf/flags :login-with-ldap)) - [:& login-form {:params params :on-success-callback on-success-callback :origin origin}])] - - ;; OLD - [:* - (when show-alt-login-buttons? - [:* - [:span.separator - [:span.line] - [:span.text (tr "labels.continue-with")] - [:span.line]] - - [:& login-buttons {:params params}] - - (when (or (contains? cf/flags :login) - (contains? cf/flags :login-with-password) - (contains? cf/flags :login-with-ldap)) - [:span.separator - [:span.line] - [:span.text (tr "labels.or")] - [:span.line]])]) - - (when (or (contains? cf/flags :login) - (contains? cf/flags :login-with-password) - (contains? cf/flags :login-with-ldap)) - [:& login-form {:params params :on-success-callback on-success-callback}])]))) + (when (or (contains? cf/flags :login) + (contains? cf/flags :login-with-password) + (contains? cf/flags :login-with-ldap)) + [:& login-form {:params params :on-success-callback on-success-callback :origin origin}])]) (mf/defc login-page [{:keys [params] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - go-register + (let [go-register (mf/use-fn #(st/emit! (rt/nav :auth-register {} params))) @@ -391,64 +283,33 @@ (mf/use-fn #(st/emit! (du/create-demo-profile)))] - (if new-css-system - [:div {:class (stl/css :auth-form)} - [:h1 {:class (stl/css :auth-title) - :data-test "login-title"} (tr "auth.login-title")] + [:div {:class (stl/css :auth-form)} + [:h1 {:class (stl/css :auth-title) + :data-test "login-title"} (tr "auth.login-title")] - [:hr {:class (stl/css :separator)}] + [:hr {:class (stl/css :separator)}] - [:& login-methods {:params params}] + [:& login-methods {:params params}] - [:div {:class (stl/css :links)} - (when (or (contains? cf/flags :login) - (contains? cf/flags :login-with-password)) - [:div {:class (stl/css :link-entry :register)} - [:& lk/link {:action on-pass-recovery - :data-test "forgot-password"} - (tr "auth.forgot-password")]]) + [:div {:class (stl/css :links)} + (when (or (contains? cf/flags :login) + (contains? cf/flags :login-with-password)) + [:div {:class (stl/css :link-entry :register)} + [:& lk/link {:action on-pass-recovery + :data-test "forgot-password"} + (tr "auth.forgot-password")]]) - (when (contains? cf/flags :registration) - [:div {:class (stl/css :link-entry :register)} - [:span (tr "auth.register") " "] - [:& lk/link {:action go-register - :data-test "register-submit"} - (tr "auth.register-submit")]])] + (when (contains? cf/flags :registration) + [:div {:class (stl/css :link-entry :register)} + [:span (tr "auth.register") " "] + [:& lk/link {:action go-register + :data-test "register-submit"} + (tr "auth.register-submit")]])] - (when (contains? cf/flags :demo-users) - [:div {:class (stl/css :link-entry :demo-account)} - [:span (tr "auth.create-demo-profile") " "] - [:& lk/link {:action on-create-demo-profile - :data-test "demo-account-link"} - (tr "auth.create-demo-account")]])] - - ;; OLD - [:div.generic-form.login-form - [:div.form-container - [:h1 {:data-test "login-title"} (tr "auth.login-title")] - - [:& login-methods {:params params}] - - [:div.links - (when (or (contains? cf/flags :login) - (contains? cf/flags :login-with-password)) - [:div.link-entry - [:& lk/link {:action on-pass-recovery - :data-test "forgot-password"} - (tr "auth.forgot-password")]]) - - (when (contains? cf/flags :registration) - [:div.link-entry - [:span (tr "auth.register") " "] - [:& lk/link {:action go-register - :data-test "register-submit"} - (tr "auth.register-submit")]])] - - (when (contains? cf/flags :demo-users) - [:div.links.demo - [:div.link-entry - [:span (tr "auth.create-demo-profile") " "] - [:& lk/link {:action on-create-demo-profile - :data-test "demo-account-link"} - (tr "auth.create-demo-account")]]])]]))) + (when (contains? cf/flags :demo-users) + [:div {:class (stl/css :link-entry :demo-account)} + [:span (tr "auth.create-demo-profile") " "] + [:& lk/link {:action on-create-demo-profile + :data-test "demo-account-link"} + (tr "auth.create-demo-account")]])])) diff --git a/frontend/src/app/main/ui/auth/recovery.cljs b/frontend/src/app/main/ui/auth/recovery.cljs index e8828c0031..f43b939bc4 100644 --- a/frontend/src/app/main/ui/auth/recovery.cljs +++ b/frontend/src/app/main/ui/auth/recovery.cljs @@ -12,7 +12,6 @@ [app.main.data.users :as du] [app.main.store :as st] [app.main.ui.components.forms :as fm] - [app.main.ui.context :as ctx] [app.util.i18n :as i18n :refer [tr]] [app.util.router :as rt] [cljs.spec.alpha :as s] @@ -57,73 +56,41 @@ (mf/defc recovery-form [{:keys [params] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - form (fm/use-form :spec ::recovery-form + (let [form (fm/use-form :spec ::recovery-form :validators [password-equality (fm/validate-not-empty :password-1 (tr "auth.password-not-empty")) (fm/validate-not-empty :password-2 (tr "auth.password-not-empty"))] :initial params)] - (if new-css-system - [:& fm/form {:on-submit on-submit :form form} - [:div {:class (stl/css :fields-row)} - [:& fm/input {:type "password" - :name :password-1 - :show-success? true - :label (tr "auth.new-password") - :class (stl/css :form-field)}]] + [:& fm/form {:on-submit on-submit :form form} + [:div {:class (stl/css :fields-row)} + [:& fm/input {:type "password" + :name :password-1 + :show-success? true + :label (tr "auth.new-password") + :class (stl/css :form-field)}]] - [:div {:class (stl/css :fields-row)} - [:& fm/input {:type "password" - :name :password-2 - :show-success? true - :label (tr "auth.confirm-password") - :class (stl/css :form-field)}]] + [:div {:class (stl/css :fields-row)} + [:& fm/input {:type "password" + :name :password-2 + :show-success? true + :label (tr "auth.confirm-password") + :class (stl/css :form-field)}]] - [:> fm/submit-button* - {:label (tr "auth.recovery-submit") - :class (stl/css :submit-btn)}]] - - ;; OLD - [:& fm/form {:on-submit on-submit - :form form} - [:div.fields-row - [:& fm/input {:type "password" - :name :password-1 - :label (tr "auth.new-password")}]] - - [:div.fields-row - [:& fm/input {:type "password" - :name :password-2 - :label (tr "auth.confirm-password")}]] - - [:> fm/submit-button* - {:label (tr "auth.recovery-submit")}]]))) + [:> fm/submit-button* + {:label (tr "auth.recovery-submit") + :class (stl/css :submit-btn)}]])) ;; --- Recovery Request Page (mf/defc recovery-page [{:keys [params] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:div {:class (stl/css :auth-form)} - [:h1 {:class (stl/css :auth-title)} "Forgot your password?"] - [:div {:class (stl/css :auth-subtitle)} "Please enter your new password"] - [:hr {:class (stl/css :separator)}] - [:& recovery-form {:params params}] + [:div {:class (stl/css :auth-form)} + [:h1 {:class (stl/css :auth-title)} "Forgot your password?"] + [:div {:class (stl/css :auth-subtitle)} "Please enter your new password"] + [:hr {:class (stl/css :separator)}] + [:& recovery-form {:params params}] - [:div {:class (stl/css :links)} - [:div {:class (stl/css :link-entry)} - [:a {:on-click #(st/emit! (rt/nav :auth-login))} - (tr "profile.recovery.go-to-login")]]]] - - ;; TODO - [:section.generic-form - [:div.form-container - [:h1 "Forgot your password?"] - [:div.subtitle "Please enter your new password"] - [:& recovery-form {:params params}] - - [:div.links - [:div.link-entry - [:a {:on-click #(st/emit! (rt/nav :auth-login))} - (tr "profile.recovery.go-to-login")]]]]]))) + [:div {:class (stl/css :links)} + [:div {:class (stl/css :link-entry)} + [:a {:on-click #(st/emit! (rt/nav :auth-login))} + (tr "profile.recovery.go-to-login")]]]]) diff --git a/frontend/src/app/main/ui/auth/recovery_request.cljs b/frontend/src/app/main/ui/auth/recovery_request.cljs index f9138025ff..d45720ea11 100644 --- a/frontend/src/app/main/ui/auth/recovery_request.cljs +++ b/frontend/src/app/main/ui/auth/recovery_request.cljs @@ -14,8 +14,6 @@ [app.main.store :as st] [app.main.ui.components.forms :as fm] [app.main.ui.components.link :as lk] - [app.main.ui.context :as ctx] - [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [tr]] [app.util.router :as rt] [beicon.v2.core :as rx] @@ -34,8 +32,7 @@ (mf/defc recovery-form [{:keys [on-success-callback] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - form (fm/use-form :spec ::recovery-request-form + (let [form (fm/use-form :spec ::recovery-request-form :validators [handle-error-messages] :initial {}) submitted (mf/use-state false) @@ -77,62 +74,34 @@ (reset! form nil) (st/emit! (du/request-profile-recovery params)))))] - (if new-css-system - [:& fm/form {:on-submit on-submit - :form form} - [:div {:class (stl/css :fields-row)} - [:& fm/input {:name :email - :label (tr "auth.email") - :type "text" - :class (stl/css :form-field)}]] + [:& fm/form {:on-submit on-submit + :form form} + [:div {:class (stl/css :fields-row)} + [:& fm/input {:name :email + :label (tr "auth.email") + :type "text" + :class (stl/css :form-field)}]] - [:> fm/submit-button* - {:label (tr "auth.recovery-request-submit") - :data-test "recovery-resquest-submit" - :class (stl/css :recover-btn)}]] - - ;; OLD - [:& fm/form {:on-submit on-submit - :form form} - [:div.fields-row - [:& fm/input {:name :email - :label (tr "auth.email") - :help-icon i/at - :type "text"}]] - - [:> fm/submit-button* - {:label (tr "auth.recovery-request-submit") - :data-test "recovery-resquest-submit"}]]))) + [:> fm/submit-button* + {:label (tr "auth.recovery-request-submit") + :data-test "recovery-resquest-submit" + :class (stl/css :recover-btn)}]])) ;; --- Recovery Request Page (mf/defc recovery-request-page [{:keys [params on-success-callback go-back-callback] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - default-go-back #(st/emit! (rt/nav :auth-login)) + (let [default-go-back #(st/emit! (rt/nav :auth-login)) go-back (or go-back-callback default-go-back)] - (if new-css-system - [:div {:class (stl/css :auth-form)} - [:h1 {:class (stl/css :auth-title)} (tr "auth.recovery-request-title")] - [:div {:class (stl/css :auth-subtitle)} (tr "auth.recovery-request-subtitle")] - [:hr {:class (stl/css :separator)}] + [:div {:class (stl/css :auth-form)} + [:h1 {:class (stl/css :auth-title)} (tr "auth.recovery-request-title")] + [:div {:class (stl/css :auth-subtitle)} (tr "auth.recovery-request-subtitle")] + [:hr {:class (stl/css :separator)}] - [:& recovery-form {:params params :on-success-callback on-success-callback}] + [:& recovery-form {:params params :on-success-callback on-success-callback}] - [:div {:class (stl/css :link-entry)} - [:& lk/link {:action go-back - :data-test "go-back-link"} - (tr "labels.go-back")]]] - - ;; old - [:section.generic-form - [:div.form-container - [:h1 (tr "auth.recovery-request-title")] - [:div.subtitle (tr "auth.recovery-request-subtitle")] - [:& recovery-form {:params params :on-success-callback on-success-callback}] - [:div.links - [:div.link-entry - [:& lk/link {:action go-back - :data-test "go-back-link"} - (tr "labels.go-back")]]]]]))) + [:div {:class (stl/css :link-entry)} + [:& lk/link {:action go-back + :data-test "go-back-link"} + (tr "labels.go-back")]]])) diff --git a/frontend/src/app/main/ui/auth/register.cljs b/frontend/src/app/main/ui/auth/register.cljs index 432c852ae4..573c989209 100644 --- a/frontend/src/app/main/ui/auth/register.cljs +++ b/frontend/src/app/main/ui/auth/register.cljs @@ -17,7 +17,6 @@ [app.main.ui.auth.login :as login] [app.main.ui.components.forms :as fm] [app.main.ui.components.link :as lk] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.ui.messages :as msgs] [app.util.i18n :refer [tr tr-html]] @@ -88,8 +87,7 @@ (mf/defc register-form [{:keys [params on-success-callback] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - initial (mf/use-memo (mf/deps params) (constantly params)) + (let [initial (mf/use-memo (mf/deps params) (constantly params)) form (fm/use-form :spec ::register-form :validators [validate (fm/validate-not-empty :password (tr "auth.password-not-empty"))] @@ -114,133 +112,64 @@ (partial handle-prepare-register-error form))))))] - (if new-css-system - [:& fm/form {:on-submit on-submit :form form} - [:div {:class (stl/css :fields-row)} - [:& fm/input {:type "email" - :name :email - :label (tr "auth.email") - :data-test "email-input" - :show-success? true - :class (stl/css :form-field)}]] - [:div {:class (stl/css :fields-row)} - [:& fm/input {:name :password - :hint (tr "auth.password-length-hint") - :label (tr "auth.password") - :show-success? true - :type "password" - :class (stl/css :form-field)}]] + [:& fm/form {:on-submit on-submit :form form} + [:div {:class (stl/css :fields-row)} + [:& fm/input {:type "email" + :name :email + :label (tr "auth.email") + :data-test "email-input" + :show-success? true + :class (stl/css :form-field)}]] + [:div {:class (stl/css :fields-row)} + [:& fm/input {:name :password + :hint (tr "auth.password-length-hint") + :label (tr "auth.password") + :show-success? true + :type "password" + :class (stl/css :form-field)}]] - [:> fm/submit-button* - {:label (tr "auth.register-submit") - :disabled @submitted? - :data-test "register-form-submit" - :class (stl/css :register-btn)}]] - - ;; OLD - [:& fm/form {:on-submit on-submit - :form form} - [:div.fields-row - [:& fm/input {:type "email" - :name :email - :help-icon i/at - :label (tr "auth.email") - :data-test "email-input"}]] - [:div.fields-row - [:& fm/input {:name :password - :hint (tr "auth.password-length-hint") - :label (tr "auth.password") - :type "password"}]] - - [:> fm/submit-button* - {:label (tr "auth.register-submit") - :disabled @submitted? - :data-test "register-form-submit"}]]))) + [:> fm/submit-button* + {:label (tr "auth.register-submit") + :disabled @submitted? + :data-test "register-form-submit" + :class (stl/css :register-btn)}]])) (mf/defc register-methods [{:keys [params on-success-callback] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:* - (when login/show-alt-login-buttons? - [:* - [:hr {:class (stl/css :separator)}] - [:& login/login-buttons {:params params}]]) - [:hr {:class (stl/css :separator)}] - [:& register-form {:params params :on-success-callback on-success-callback}]] - - ;; OLD - [:* - (when login/show-alt-login-buttons? - [:* - [:span.separator - [:span.line] - [:span.text (tr "labels.continue-with")] - [:span.line]] - - [:& login/login-buttons {:params params}] - - (when (or (contains? cf/flags :login) - (contains? cf/flags :login-with-ldap)) - [:span.separator - [:span.line] - [:span.text (tr "labels.or")] - [:span.line]])]) - - [:& register-form {:params params :on-success-callback on-success-callback}]]))) + [:* + (when login/show-alt-login-buttons? + [:* + [:hr {:class (stl/css :separator)}] + [:& login/login-buttons {:params params}]]) + [:hr {:class (stl/css :separator)}] + [:& register-form {:params params :on-success-callback on-success-callback}]]) (mf/defc register-page [{:keys [params] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [: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")] + [: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]) + (when (contains? cf/flags :demo-warning) + [:& demo-warning]) - [:& register-methods {:params params}] + [:& register-methods {:params params}] - [:div {:class (stl/css :links)} - [:div {:class (stl/css :link-entry :account)} - [:span (tr "auth.already-have-account") " "] + [:div {:class (stl/css :links)} + [:div {:class (stl/css :link-entry :account)} + [:span (tr "auth.already-have-account") " "] - [:& lk/link {:action #(st/emit! (rt/nav :auth-login {} params)) - :data-test "login-here-link"} - (tr "auth.login-here")]] + [:& lk/link {:action #(st/emit! (rt/nav :auth-login {} params)) + :data-test "login-here-link"} + (tr "auth.login-here")]] - (when (contains? cf/flags :demo-users) - [:div {:class (stl/css :link-entry :demo-users)} - [:span (tr "auth.create-demo-profile") " "] - [:& lk/link {:action #(st/emit! (du/create-demo-profile))} - (tr "auth.create-demo-account")]])]] - - ;; OLD - [:div.form-container - [:h1 {:data-test "registration-title"} (tr "auth.register-title")] - [:div.subtitle (tr "auth.register-subtitle")] - - (when (contains? cf/flags :demo-warning) - [:& demo-warning]) - - [:& register-methods {:params params}] - - [:div.links - [:div.link-entry - [:span (tr "auth.already-have-account") " "] - - [:& lk/link {:action #(st/emit! (rt/nav :auth-login {} params)) - :data-test "login-here-link"} - (tr "auth.login-here")]] - - (when (contains? cf/flags :demo-users) - [:div.link-entry - [:span (tr "auth.create-demo-profile") " "] - [:& lk/link {:action #(st/emit! (du/create-demo-profile))} - (tr "auth.create-demo-account")]])]]))) + (when (contains? cf/flags :demo-users) + [:div {:class (stl/css :link-entry :demo-users)} + [:span (tr "auth.create-demo-profile") " "] + [:& lk/link {:action #(st/emit! (du/create-demo-profile))} + (tr "auth.create-demo-account")]])]]) ;; --- PAGE: register validation @@ -284,8 +213,7 @@ (mf/defc register-validate-form [{:keys [params on-success-callback] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - form (fm/use-form :spec ::register-validate-form + (let [form (fm/use-form :spec ::register-validate-form :validators [(fm/validate-not-empty :fullname (tr "auth.name.not-all-space")) (fm/validate-length :fullname fm/max-length-allowed (tr "auth.name.too-long"))] :initial params) @@ -306,103 +234,55 @@ (rx/subs! on-success (partial handle-register-error form))))))] - (if new-css-system - [:& fm/form {:on-submit on-submit :form form} - [:div {:class (stl/css :fields-row)} - [:& fm/input {:name :fullname - :label (tr "auth.fullname") - :type "text" - :show-success? true - :class (stl/css :form-field)}]] + [:& fm/form {:on-submit on-submit :form form} + [:div {:class (stl/css :fields-row)} + [:& fm/input {:name :fullname + :label (tr "auth.fullname") + :type "text" + :show-success? true + :class (stl/css :form-field)}]] - (when (contains? cf/flags :terms-and-privacy-checkbox) - (let [terms-label - (mf/html - [:& tr-html - {:tag-name "div" - :label "auth.terms-privacy-agreement-md" - :params [cf/terms-of-service-uri cf/privacy-policy-uri]}])] - [:div {:class (stl/css :fields-row :input-visible :accept-terms-and-privacy-wrapper)} - [:& fm/input {:name :accept-terms-and-privacy - :class "check-primary" - :type "checkbox" - :label terms-label}]])) - - [:> fm/submit-button* - {:label (tr "auth.register-submit") - :disabled @submitted? - :class (stl/css :register-btn)}]] - - ;; OLD - [:& fm/form {:on-submit on-submit - :form form} - [:div.fields-row - [:& fm/input {:name :fullname - :label (tr "auth.fullname") - :type "text"}]] - - (when (contains? cf/flags :terms-and-privacy-checkbox) - [:div.fields-row.input-visible.accept-terms-and-privacy-wrapper + (when (contains? cf/flags :terms-and-privacy-checkbox) + (let [terms-label + (mf/html + [:& tr-html + {:tag-name "div" + :label "auth.terms-privacy-agreement-md" + :params [cf/terms-of-service-uri cf/privacy-policy-uri]}])] + [:div {:class (stl/css :fields-row :input-visible :accept-terms-and-privacy-wrapper)} [:& fm/input {:name :accept-terms-and-privacy :class "check-primary" - :type "checkbox"} - [:span - (tr "auth.terms-privacy-agreement")]] - [:div.auth-links - [:a {:href "https://penpot.app/terms" :target "_blank"} (tr "auth.terms-of-service")] - [:span ",\u00A0"] - [:a {:href "https://penpot.app/privacy" :target "_blank"} (tr "auth.privacy-policy")]]]) + :type "checkbox" + :label terms-label}]])) - [:> fm/submit-button* - {:label (tr "auth.register-submit") - :disabled @submitted?}]]))) + [:> fm/submit-button* + {:label (tr "auth.register-submit") + :disabled @submitted? + :class (stl/css :register-btn)}]])) (mf/defc register-validate-page [{:keys [params] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:div {:class (stl/css :auth-form)} - [:h1 {:class (stl/css :auth-title) - :data-test "register-title"} (tr "auth.register-title")] - [:div {:class (stl/css :auth-subtitle)} (tr "auth.register-subtitle")] + [:div {:class (stl/css :auth-form)} + [:h1 {:class (stl/css :auth-title) + :data-test "register-title"} (tr "auth.register-title")] + [:div {:class (stl/css :auth-subtitle)} (tr "auth.register-subtitle")] - [:hr {:class (stl/css :separator)}] + [:hr {:class (stl/css :separator)}] - [:& register-validate-form {:params params}] + [:& register-validate-form {:params params}] - [:div {:class (stl/css :links)} - [:div {:class (stl/css :link-entry :go-back)} - [:& lk/link {:action #(st/emit! (rt/nav :auth-register {} {}))} - (tr "labels.go-back")]]]] - - ;; OLD - [:div.form-container - [:h1 {:data-test "register-title"} (tr "auth.register-title")] - [:div.subtitle (tr "auth.register-subtitle")] - - [:& register-validate-form {:params params}] - - [:div.links - [:div.link-entry - [:& lk/link {:action #(st/emit! (rt/nav :auth-register {} {}))} - (tr "labels.go-back")]]]]))) + [:div {:class (stl/css :links)} + [:div {:class (stl/css :link-entry :go-back)} + [:& lk/link {:action #(st/emit! (rt/nav :auth-register {} {}))} + (tr "labels.go-back")]]]]) (mf/defc register-success-page [{:keys [params] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [: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")] - [:div {:class (stl/css :notification-text-email)} (:email params "")] - [:div {:class (stl/css :notification-text)} (tr "auth.check-your-email")]] - - ;; OLD - [:div.form-container - [:div.notification-icon i/icon-verify] - [:div.notification-text (tr "auth.verification-email-sent")] - [:div.notification-text-email (:email params "")] - [:div.notification-text (tr "auth.check-your-email")]]))) + [: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")] + [:div {:class (stl/css :notification-text-email)} (:email params "")] + [:div {:class (stl/css :notification-text)} (tr "auth.check-your-email")]]) diff --git a/frontend/src/app/main/ui/auth/verify_token.cljs b/frontend/src/app/main/ui/auth/verify_token.cljs index db8f7745ac..e0dcd7cfed 100644 --- a/frontend/src/app/main/ui/auth/verify_token.cljs +++ b/frontend/src/app/main/ui/auth/verify_token.cljs @@ -11,7 +11,6 @@ [app.main.data.users :as du] [app.main.repo :as rp] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.ui.static :as static] [app.util.dom :as dom] @@ -62,8 +61,7 @@ (mf/defc verify-token [{:keys [route] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - token (get-in route [:query-params :token]) + (let [token (get-in route [:query-params :token]) bad-token (mf/use-state false)] (mf/with-effect [] @@ -96,6 +94,5 @@ (if @bad-token [:> static/invalid-token {}] - [:div {:class (stl/css-case :verify-token new-css-system - :global/verify-token (not new-css-system))} + [:div {:class (stl/css :verify-token)} i/loader-pencil]))) diff --git a/frontend/src/app/main/ui/comments.cljs b/frontend/src/app/main/ui/comments.cljs index 7d053ab1eb..2e2c359b0b 100644 --- a/frontend/src/app/main/ui/comments.cljs +++ b/frontend/src/app/main/ui/comments.cljs @@ -18,7 +18,6 @@ [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.components.forms :as fm] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -81,20 +80,18 @@ (set! (.-height (.-style node)) "0") (set! (.-height (.-style node)) (str (+ 2 (.-scrollHeight node)) "px"))))) - [:textarea - {:ref local-ref - :auto-focus autofocus? - :on-key-down on-key-down - :on-focus on-focus* - :on-blur on-blur - :value value - :placeholder placeholder - :on-change on-change*}])) + [:textarea {:ref local-ref + :auto-focus autofocus? + :on-key-down on-key-down + :on-focus on-focus* + :on-blur on-blur + :value value + :placeholder placeholder + :on-change on-change*}])) (mf/defc reply-form [{:keys [thread] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - show-buttons? (mf/use-state false) + (let [show-buttons? (mf/use-state false) content (mf/use-state "") disabled? (or (fm/all-spaces? @content) @@ -123,54 +120,32 @@ (fn [] (st/emit! (dcm/add-comment thread @content)) (on-cancel)))] - (if new-css-system - [:div {:class (stl/css :reply-form)} - [:& resizing-textarea {:value @content - :placeholder "Reply" - :on-blur on-blur - :on-focus on-focus - :select-on-focus? false - :on-ctrl-enter on-submit - :on-change on-change}] - (when (or @show-buttons? (seq @content)) - [:div {:class (stl/css :buttons-wrapper)} - [:input.btn-secondary - {:type "button" - :class (stl/css :cancel-btn) - :value "Cancel" - :on-click on-cancel}] - [:input - {:type "button" - :class (stl/css-case :post-btn true - :global/disabled disabled?) - :value "Post" - :on-click on-submit - :disabled disabled?}]])] - - - [:div.reply-form - [:& resizing-textarea {:value @content - :placeholder "Reply" - :on-blur on-blur - :on-focus on-focus - :on-ctrl-enter on-submit - :on-change on-change}] - (when (or @show-buttons? (seq @content)) - [:div.buttons - [:input.btn-primary - {:type "button" - :value "Post" - :on-click on-submit - :disabled disabled?}] - [:input.btn-secondary - {:type "button" - :value "Cancel" - :on-click on-cancel}]])]))) + [:div {:class (stl/css :reply-form)} + [:& resizing-textarea {:value @content + :placeholder "Reply" + :on-blur on-blur + :on-focus on-focus + :select-on-focus? false + :on-ctrl-enter on-submit + :on-change on-change}] + (when (or @show-buttons? (seq @content)) + [:div {:class (stl/css :buttons-wrapper)} + [:input.btn-secondary + {:type "button" + :class (stl/css :cancel-btn) + :value "Cancel" + :on-click on-cancel}] + [:input + {:type "button" + :class (stl/css-case :post-btn true + :global/disabled disabled?) + :value "Post" + :on-click on-submit + :disabled disabled?}]])])) (mf/defc draft-thread [{:keys [draft zoom on-cancel on-submit position-modifier]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - position (cond-> (:position draft) + (let [position (cond-> (:position draft) (some? position-modifier) (gpt/transform position-modifier)) content (:content draft) @@ -201,73 +176,42 @@ (mf/deps draft) (partial on-submit draft))] + [:* + [:div + {:class (stl/css :floating-thread-bubble) + :style {:top (str pos-y "px") + :left (str pos-x "px")} + :on-click dom/stop-propagation} + "?"] + [:div {:class (stl/css :thread-content) + :style {:top (str (- pos-y 24) "px") + :left (str (+ pos-x 28) "px")} + :on-click dom/stop-propagation} + [:div {:class (stl/css :reply-form)} + [:& resizing-textarea {:placeholder (tr "labels.write-new-comment") + :value (or content "") + :autofocus true + :select-on-focus? false + :on-esc on-esc + :on-change on-change + :on-ctrl-enter on-submit}] + [:div {:class (stl/css :buttons-wrapper)} - (if new-css-system - [:* - [:div - {:class (stl/css :floating-thread-bubble) - :style {:top (str pos-y "px") - :left (str pos-x "px")} - :on-click dom/stop-propagation} - "?"] - [:div {:class (stl/css :thread-content) - :style {:top (str (- pos-y 24) "px") - :left (str (+ pos-x 28) "px")} - :on-click dom/stop-propagation} - [:div {:class (stl/css :reply-form)} - [:& resizing-textarea {:placeholder (tr "labels.write-new-comment") - :value (or content "") - :autofocus true - :select-on-focus? false - :on-esc on-esc - :on-change on-change - :on-ctrl-enter on-submit}] - [:div {:class (stl/css :buttons-wrapper)} + [:input {:on-click on-esc + :class (stl/css :cancel-btn) + :type "button" + :value "Cancel"}] - [:input {:on-click on-esc - :class (stl/css :cancel-btn) - :type "button" - :value "Cancel"}] - - [:input {:on-click on-submit - :type "button" - :value "Post" - :class (stl/css-case :post-btn true - :global/disabled disabled?) - :disabled disabled?}]]]]] - - [:* - [:div.thread-bubble - {:style {:top (str pos-y "px") - :left (str pos-x "px")} - :on-click dom/stop-propagation} - [:span "?"]] - [:div.thread-content - {:style {:top (str (- pos-y 14) "px") - :left (str (+ pos-x 14) "px")} - :on-click dom/stop-propagation} - [:div.reply-form - [:& resizing-textarea {:placeholder (tr "labels.write-new-comment") - :value (or content "") - :autofocus true - :on-esc on-esc - :on-ctrl-enter on-submit - :on-change on-change}] - [:div.buttons - [:input.btn-primary - {:on-click on-submit - :type "button" - :value "Post" - :disabled disabled?}] - [:input.btn-secondary - {:on-click on-esc - :type "button" - :value "Cancel"}]]]]]))) + [:input {:on-click on-submit + :type "button" + :value "Post" + :class (stl/css-case :post-btn true + :global/disabled disabled?) + :disabled disabled?}]]]]])) (mf/defc edit-form [{:keys [content on-submit on-cancel] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - content (mf/use-state content) + (let [content (mf/use-state content) on-change (mf/use-fn @@ -281,44 +225,28 @@ disabled? (or (fm/all-spaces? @content) (str/empty-or-nil? @content))] - (if new-css-system - [:div {:class (stl/css :edit-form)} - [:& resizing-textarea {:value @content - :autofocus true - :select-on-focus true - :select-on-focus? false - :on-ctrl-enter on-submit* - :on-change on-change}] - [:div {:class (stl/css :buttons-wrapper)} - [:input {:type "button" - :value "Cancel" - :class (stl/css :cancel-btn) - :on-click on-cancel}] - [:input {:type "button" - :class (stl/css-case :post-btn true - :global/disabled disabled?) - :value "Post" - :on-click on-submit* - :disabled disabled?}]]] - - - [:div.reply-form.edit-form - [:& resizing-textarea {:value @content - :autofocus true - :select-on-focus true - :on-ctrl-enter on-submit* - :on-change on-change}] - [:div.buttons - [:input.btn-primary {:type "button" - :value "Post" - :on-click on-submit* - :disabled disabled?}] - [:input.btn-secondary {:type "button" :value "Cancel" :on-click on-cancel}]]]))) + [:div {:class (stl/css :edit-form)} + [:& resizing-textarea {:value @content + :autofocus true + :select-on-focus true + :select-on-focus? false + :on-ctrl-enter on-submit* + :on-change on-change}] + [:div {:class (stl/css :buttons-wrapper)} + [:input {:type "button" + :value "Cancel" + :class (stl/css :cancel-btn) + :on-click on-cancel}] + [:input {:type "button" + :class (stl/css-case :post-btn true + :global/disabled disabled?) + :value "Post" + :on-click on-submit* + :disabled disabled?}]]])) (mf/defc comment-item [{:keys [comment thread users origin] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - owner (get users (:owner-id comment)) + (let [owner (get users (:owner-id comment)) profile (mf/deref refs/profile) options (mf/use-state false) edition? (mf/use-state false) @@ -382,82 +310,46 @@ (dom/stop-propagation event) (st/emit! (dcm/update-comment-thread (update thread :is-resolved not)))))] - (if new-css-system - [:div {:class (stl/css :comment-container)} - [:div {:class (stl/css :comment)} - [:div {:class (stl/css :author)} - [:div {:class (stl/css :avatar)} - [:img {:src (cfg/resolve-profile-photo-url owner)}]] - [:div {:class (stl/css :name)} - [:div {:class (stl/css :fullname)} (:fullname owner)] - [:div {:class (stl/css :timeago)} (dt/timeago (:modified-at comment))]] + [:div {:class (stl/css :comment-container)} + [:div {:class (stl/css :comment)} + [:div {:class (stl/css :author)} + [:div {:class (stl/css :avatar)} + [:img {:src (cfg/resolve-profile-photo-url owner)}]] + [:div {:class (stl/css :name)} + [:div {:class (stl/css :fullname)} (:fullname owner)] + [:div {:class (stl/css :timeago)} (dt/timeago (:modified-at comment))]] - (when (some? thread) - [:div {:class (stl/css :options-resolve-wrapper) - :on-click toggle-resolved} - [:span {:class (stl/css-case :options-resolve true - :global/checked (:is-resolved thread))} i/tick-refactor]]) + (when (some? thread) + [:div {:class (stl/css :options-resolve-wrapper) + :on-click toggle-resolved} + [:span {:class (stl/css-case :options-resolve true + :global/checked (:is-resolved thread))} i/tick-refactor]]) - (when (= (:id profile) (:id owner)) - [:div {:class (stl/css :options) - :on-click on-toggle-options} - i/menu-refactor])] + (when (= (:id profile) (:id owner)) + [:div {:class (stl/css :options) + :on-click on-toggle-options} + i/menu-refactor])] - [:div {:class (stl/css :content)} - (if @edition? - [:& edit-form {:content (:content comment) - :on-submit on-submit - :on-cancel on-cancel}] - [:span {:class (stl/css :text)} (:content comment)])]] + [:div {:class (stl/css :content)} + (if @edition? + [:& edit-form {:content (:content comment) + :on-submit on-submit + :on-cancel on-cancel}] + [:span {:class (stl/css :text)} (:content comment)])]] - [:& dropdown {:show @options - :on-close on-hide-options} - [:ul {:class (stl/css :comment-options-dropdown)} + [:& dropdown {:show @options + :on-close on-hide-options} + [:ul {:class (stl/css :comment-options-dropdown)} + [:li {:class (stl/css :context-menu-option) + :on-click on-edit-clicked} + (tr "labels.edit")] + (if thread [:li {:class (stl/css :context-menu-option) - :on-click on-edit-clicked} - (tr "labels.edit")] - (if thread - [:li {:class (stl/css :context-menu-option) - :on-click on-delete-thread} - (tr "labels.delete-comment-thread")] - [:li {:class (stl/css :context-menu-option) - :on-click on-delete-comment} - (tr "labels.delete-comment")])]]] - - - [:div.comment-container - [:div.comment - [:div.author - [:div.avatar - [:img {:src (cfg/resolve-profile-photo-url owner)}]] - [:div.name - [:div.fullname (:fullname owner)] - [:div.timeago (dt/timeago (:modified-at comment))]] - - (when (some? thread) - [:div.options-resolve {:on-click toggle-resolved} - (if (:is-resolved thread) - [:span i/checkbox-checked] - [:span i/checkbox-unchecked])]) - - (when (= (:id profile) (:id owner)) - [:div.options - [:div.options-icon {:on-click on-toggle-options} i/actions]])] - - [:div.content - (if @edition? - [:& edit-form {:content (:content comment) - :on-submit on-submit - :on-cancel on-cancel}] - [:span.text (:content comment)])]] - - [:& dropdown {:show @options - :on-close on-hide-options} - [:ul.dropdown.comment-options-dropdown - [:li {:on-click on-edit-clicked} (tr "labels.edit")] - (if thread - [:li {:on-click on-delete-thread} (tr "labels.delete-comment-thread")] - [:li {:on-click on-delete-comment} (tr "labels.delete-comment")])]]]))) + :on-click on-delete-thread} + (tr "labels.delete-comment-thread")] + [:li {:class (stl/css :context-menu-option) + :on-click on-delete-comment} + (tr "labels.delete-comment")])]]])) (defn make-comments-ref [thread-id] @@ -466,8 +358,7 @@ (mf/defc thread-comments {::mf/wrap [mf/memo]} [{:keys [thread zoom users origin position-modifier]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - ref (mf/use-ref) + (let [ref (mf/use-ref) thread-id (:id thread) @@ -477,12 +368,8 @@ (some? position-modifier) (gpt/transform position-modifier)) - pos-x (if new-css-system - (+ (* (:x pos) zoom) 24) - (+ (* (:x pos) zoom) 14)) - pos-y (if new-css-system - (- (* (:y pos) zoom) 28) - (- (* (:y pos) zoom) 14)) + pos-x (+ (* (:x pos) zoom) 24) + pos-y (- (* (:y pos) zoom) 28) comments-ref (mf/with-memo [thread-id] @@ -504,46 +391,24 @@ (mf/with-layout-effect [thread-pos comments-map] (when-let [node (mf/ref-val ref)] (dom/scroll-into-view-if-needed! node))) - (if new-css-system - (when (some? comment) - [:div {:class (stl/css :thread-content) - :style {:top (str pos-y "px") - :left (str pos-x "px")} - :on-click dom/stop-propagation} + (when (some? comment) + [:div {:class (stl/css :thread-content) + :style {:top (str pos-y "px") + :left (str pos-x "px")} + :on-click dom/stop-propagation} - [:div {:class (stl/css :comments)} - [:& comment-item {:comment comment - :users users - :thread thread - :origin origin}] - (for [item (rest comments)] - [:* {:key (dm/str (:id item))} - [:& comment-item {:comment item - :users users - :origin origin}]]) - [:div {:ref ref}]] - [:& reply-form {:thread thread}]]) - - - (when (some? comment) - [:div.thread-content - {:style {:top (str pos-y "px") - :left (str pos-x "px")} - :on-click dom/stop-propagation} - - [:div.comments - [:& comment-item {:comment comment - :users users - :thread thread - :origin origin}] - (for [item (rest comments)] - [:* {:key (dm/str (:id item))} - [:hr] - [:& comment-item {:comment item - :users users - :origin origin}]]) - [:div {:ref ref}]] - [:& reply-form {:thread thread}]])))) + [:div {:class (stl/css :comments)} + [:& comment-item {:comment comment + :users users + :thread thread + :origin origin}] + (for [item (rest comments)] + [:* {:key (dm/str (:id item))} + [:& comment-item {:comment item + :users users + :origin origin}]]) + [:div {:ref ref}]] + [:& reply-form {:thread thread}]]))) (defn use-buble [zoom {:keys [position frame-id]}] @@ -602,8 +467,7 @@ (mf/defc thread-bubble {::mf/wrap [mf/memo]} [{:keys [thread zoom open? on-click origin position-modifier]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - pos (cond-> (:position thread) + (let [pos (cond-> (:position thread) (some? position-modifier) (gpt/transform position-modifier)) @@ -658,37 +522,22 @@ (dom/stop-propagation event) (when (= origin :viewer) (on-click thread))))] - (if new-css-system - [:div {:style {:top (str pos-y "px") - :left (str pos-x "px")} - :on-pointer-down on-pointer-down* - :on-pointer-up on-pointer-up* - :on-pointer-move on-pointer-move* - :on-click on-click* - :on-lost-pointer-capture on-lost-pointer-capture - :class (stl/css-case - :floating-thread-bubble true - :resolved (:is-resolved thread) - :unread (pos? (:count-unread-comments thread)))} - [:span (:seqn thread)]] - - [:div.thread-bubble - {:style {:top (str pos-y "px") - :left (str pos-x "px")} - :on-pointer-down on-pointer-down* - :on-pointer-up on-pointer-up* - :on-pointer-move on-pointer-move* - :on-click on-click* - :on-lost-pointer-capture on-lost-pointer-capture - :class (dom/classnames - :resolved (:is-resolved thread) - :unread (pos? (:count-unread-comments thread)))} - [:span (:seqn thread)]]))) + [:div {:style {:top (str pos-y "px") + :left (str pos-x "px")} + :on-pointer-down on-pointer-down* + :on-pointer-up on-pointer-up* + :on-pointer-move on-pointer-move* + :on-click on-click* + :on-lost-pointer-capture on-lost-pointer-capture + :class (stl/css-case + :floating-thread-bubble true + :resolved (:is-resolved thread) + :unread (pos? (:count-unread-comments thread)))} + [:span (:seqn thread)]])) (mf/defc comment-thread [{:keys [item users on-click]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - owner (get users (:owner-id item)) + (let [owner (get users (:owner-id item)) on-click* (mf/use-fn (mf/deps item) @@ -698,99 +547,50 @@ (when (fn? on-click) (on-click item))))] - (if new-css-system - [:div {:class (stl/css :comment) - :on-click on-click*} - [:div {:class (stl/css :author)} - [:div {:class (stl/css-case :thread-bubble true - :resolved (:is-resolved item) - :unread (pos? (:count-unread-comments item)))} - (:seqn item)] - [:div {:class (stl/css :avatar)} - [:img {:src (cfg/resolve-profile-photo-url owner)}]] - [:div {:class (stl/css :name)} - [:div {:class (stl/css :fullname)} (:fullname owner)] - [:div {:class (stl/css :timeago)} (dt/timeago (:modified-at item))]]] - [:div {:class (stl/css :content)} - (:content item)] - [:div {:class (stl/css :replies)} - (let [unread (:count-unread-comments item ::none) - total (:count-comments item 1)] - [:* - (when (> total 1) - (if (= total 2) - [:span {:class (stl/css :total-replies)} "1 reply"] - [:span {:class (stl/css :total-replies)} (str (dec total) " replies")])) + [:div {:class (stl/css :comment) + :on-click on-click*} + [:div {:class (stl/css :author)} + [:div {:class (stl/css-case :thread-bubble true + :resolved (:is-resolved item) + :unread (pos? (:count-unread-comments item)))} + (:seqn item)] + [:div {:class (stl/css :avatar)} + [:img {:src (cfg/resolve-profile-photo-url owner)}]] + [:div {:class (stl/css :name)} + [:div {:class (stl/css :fullname)} (:fullname owner)] + [:div {:class (stl/css :timeago)} (dt/timeago (:modified-at item))]]] + [:div {:class (stl/css :content)} + (:content item)] + [:div {:class (stl/css :replies)} + (let [unread (:count-unread-comments item ::none) + total (:count-comments item 1)] + [:* + (when (> total 1) + (if (= total 2) + [:span {:class (stl/css :total-replies)} "1 reply"] + [:span {:class (stl/css :total-replies)} (str (dec total) " replies")])) - (when (and (> total 1) (> unread 0)) - (if (= unread 1) - [:span {:class (stl/css :new-replies)} "1 new reply"] - [:span {:class (stl/css :new-replies)} (str unread " new replies")]))])]] - - - [:div.comment {:on-click on-click*} - [:div.author - [:div.thread-bubble - {:class (dom/classnames - :resolved (:is-resolved item) - :unread (pos? (:count-unread-comments item)))} - (:seqn item)] - [:div.avatar - [:img {:src (cfg/resolve-profile-photo-url owner)}]] - [:div.name - [:div.fullname (:fullname owner) ", "] - [:div.timeago (dt/timeago (:modified-at item))]]] - [:div.content - [:span.text (:content item)]] - [:div.content.replies - (let [unread (:count-unread-comments item ::none) - total (:count-comments item 1)] - [:* - (when (> total 1) - (if (= total 2) - [:span.total-replies "1 reply"] - [:span.total-replies (str (dec total) " replies")])) - - (when (and (> total 1) (> unread 0)) - (if (= unread 1) - [:span.new-replies "1 new reply"] - [:span.new-replies (str unread " new replies")]))])]]))) + (when (and (> total 1) (> unread 0)) + (if (= unread 1) + [:span {:class (stl/css :new-replies)} "1 new reply"] + [:span {:class (stl/css :new-replies)} (str unread " new replies")]))])]])) (mf/defc comment-thread-group [{:keys [group users on-thread-click]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:div {:class (stl/css :thread-group)} - (if (:file-name group) - [:div {:class (stl/css :section-title)} - [:span {:class (stl/css :file-name)} (:file-name group) ", "] - [:span {:class (stl/css :page-name)} (:page-name group)]] + [:div {:class (stl/css :thread-group)} + (if (:file-name group) + [:div {:class (stl/css :section-title)} + [:span {:class (stl/css :file-name)} (:file-name group) ", "] + [:span {:class (stl/css :page-name)} (:page-name group)]] - [:div {:class (stl/css :section-title)} - [:span {:class (stl/css :icon)} i/document-refactor] - [:span {:class (stl/css :page-name)} (:page-name group)]]) + [:div {:class (stl/css :section-title)} + [:span {:class (stl/css :icon)} i/document-refactor] + [:span {:class (stl/css :page-name)} (:page-name group)]]) - [:div {:class (stl/css :threads)} - (for [item (:items group)] - [:& comment-thread - {:item item - :on-click on-thread-click - :users users - :key (:id item)}])]] - - - [:div.thread-group - (if (:file-name group) - [:div.section-title - [:span.label.filename (:file-name group) ", "] - [:span.label (:page-name group)]] - [:div.section-title - [:span.icon i/file-html] - [:span.label (:page-name group)]]) - [:div.threads - (for [item (:items group)] - [:& comment-thread - {:item item - :on-click on-thread-click - :users users - :key (:id item)}])]]))) + [:div {:class (stl/css :threads)} + (for [item (:items group)] + [:& comment-thread + {:item item + :on-click on-thread-click + :users users + :key (:id item)}])]]) diff --git a/frontend/src/app/main/ui/comments.scss b/frontend/src/app/main/ui/comments.scss index 89fc247d70..874a229ad3 100644 --- a/frontend/src/app/main/ui/comments.scss +++ b/frontend/src/app/main/ui/comments.scss @@ -13,96 +13,106 @@ border-radius: $br-8; padding: $s-8 $s-16; - .section-title { - @include titleTipography; - height: $s-32; - display: flex; - align-items: center; - margin-bottom: $s-8; - .file-name { - color: var(--comment-subtitle-color); - } - .page-name { - color: var(--comment-subtitle-color); - } - .icon { - display: flex; - align-items: center; - padding: 0 $s-6 0 $s-4; - width: $s-24; - height: $s-32; - margin-left: $s-6; - svg { - @extend .button-icon-small; - stroke: var(--icon-foreground); - } - } - } - .threads { - display: flex; - flex-direction: column; - gap: $s-24; - } - &:hover { background: $db-primary; } } +.section-title { + @include titleTipography; + height: $s-32; + display: flex; + align-items: center; + margin-bottom: $s-8; +} + +.file-name { + color: var(--comment-subtitle-color); +} + +.page-name { + color: var(--comment-subtitle-color); +} + +.icon { + display: flex; + align-items: center; + padding: 0 $s-6 0 $s-4; + width: $s-24; + height: $s-32; + margin-left: $s-6; + svg { + @extend .button-icon-small; + stroke: var(--icon-foreground); + } +} + +.threads { + display: flex; + flex-direction: column; + gap: $s-24; +} + // Comment-thread .comment { @include titleTipography; display: flex; flex-direction: column; gap: $s-12; - .author { - display: flex; - gap: $s-8; - .thread-bubble { - @extend .comment-bubbles; - &.resolved { - @extend .resolved-comment-bubble; - } - &.unread { - @extend .unread-comment-bubble; - } - } - .avatar { - height: $s-32; - width: $s-32; - border-radius: $br-circle; - img { - border-radius: $br-circle; - } - } - .name { - flex-grow: 1; - .fullname { - @include textEllipsis; - color: var(--comment-title-color); - } - .timeago { - @include textEllipsis; - color: var(--comment-subtitle-color); - } - } +} + +.author { + display: flex; + gap: $s-8; +} + +.thread-bubble { + @extend .comment-bubbles; + &.resolved { + @extend .resolved-comment-bubble; } - .content { - @include titleTipography; - color: var(--color-foreground-primary); - } - .replies { - display: flex; - gap: $s-8; - .total-replies { - color: var(--color-foreground-secondary); - } - .new-replies { - color: var(--color-accent-primary); - } + &.unread { + @extend .unread-comment-bubble; } } +.avatar { + height: $s-32; + width: $s-32; + border-radius: $br-circle; + img { + border-radius: $br-circle; + } +} + +.name { + flex-grow: 1; + .fullname { + @include textEllipsis; + color: var(--comment-title-color); + } + .timeago { + @include textEllipsis; + color: var(--comment-subtitle-color); + } +} + +.content { + @include titleTipography; + color: var(--color-foreground-primary); +} + +.replies { + display: flex; + gap: $s-8; +} + +.total-replies { + color: var(--color-foreground-secondary); +} +.new-replies { + color: var(--color-accent-primary); +} // Thread-bubble .floating-thread-bubble { diff --git a/frontend/src/app/main/ui/components/code_block.cljs b/frontend/src/app/main/ui/components/code_block.cljs index d6d81f8495..b766772344 100644 --- a/frontend/src/app/main/ui/components/code_block.cljs +++ b/frontend/src/app/main/ui/components/code_block.cljs @@ -8,7 +8,6 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] - [app.main.ui.context :as ctx] [cuerdas.core :as str] [promesa.core :as p] [rumext.v2 :as mf] @@ -20,8 +19,7 @@ (mf/defc code-block {::mf/wrap-props false} [{:keys [code type]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - block-ref (mf/use-ref) + (let [block-ref (mf/use-ref) code (str/trim code)] (mf/with-effect [code type] @@ -29,7 +27,5 @@ (p/let [highlight-fn (lazy/load highlight-fn)] (highlight-fn node)))) - (if new-css-system - [:pre {:class (dm/str type " " (stl/css :code-display)) :ref block-ref} code] - [:pre {:class (dm/str type " " "code-display") :ref block-ref} code]))) + [:pre {:class (dm/str type " " (stl/css :code-display)) :ref block-ref} code])) diff --git a/frontend/src/app/main/ui/components/color_bullet_new.cljs b/frontend/src/app/main/ui/components/color_bullet_new.cljs index 6af3f502b4..f15f3549c8 100644 --- a/frontend/src/app/main/ui/components/color_bullet_new.cljs +++ b/frontend/src/app/main/ui/components/color_bullet_new.cljs @@ -75,5 +75,5 @@ :on-click on-click :on-double-click on-double-click} (if (some? image) - (tr "media.image") + (or name (tr "media.image")) (or name color (uc/gradient-type->string (:type gradient))))]))) diff --git a/frontend/src/app/main/ui/components/context_menu_a11y.cljs b/frontend/src/app/main/ui/components/context_menu_a11y.cljs index 1b8bc1c610..e6a230bf58 100644 --- a/frontend/src/app/main/ui/components/context_menu_a11y.cljs +++ b/frontend/src/app/main/ui/components/context_menu_a11y.cljs @@ -11,7 +11,6 @@ [app.common.data.macros :as dm] [app.main.refs :as refs] [app.main.ui.components.dropdown :refer [dropdown']] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -68,7 +67,6 @@ min-width? (gobj/get props "min-width?" false) origin (gobj/get props "origin") route (mf/deref refs/route) - new-css-system (mf/use-ctx ctx/new-css-system) in-dashboard? (= :dashboard-projects (:name (:data route))) local (mf/use-state {:offset-y 0 :offset-x 0 @@ -191,149 +189,79 @@ (tm/schedule-on-idle #(dom/focus! (dom/get-element (first ids))))) - (if new-css-system - (when (and open? (some? (:levels @local))) - [:> dropdown' props - (let [level (-> @local :levels peek) - original-options (:options level) - parent-original (:parent-option level)] - [:div {:class (stl/css-case :is-selectable is-selectable - :context-menu true - :is-open open? - :fixed fixed?) - :style {:top (+ top (:offset-y @local)) - :left (+ left (:offset-x @local))} - :on-key-down (on-key-down original-options parent-original)} - (let [level (-> @local :levels peek)] - [:ul {:class (stl/css-case :min-width min-width? - :context-menu-items true) - :role "menu" - :ref check-menu-offscreen} - (when-let [parent-option (:parent-option level)] - [:* - [:& context-menu-a11y-item - {:id "go-back-sub-option" - :class (stl/css :context-menu-item) - :tab-index "0" - :on-key-down (fn [event] - (dom/prevent-default event))} - [:button {:class (stl/css :context-menu-action :submenu-back) + (when (and open? (some? (:levels @local))) + [:> dropdown' props + (let [level (-> @local :levels peek) + original-options (:options level) + parent-original (:parent-option level)] + [:div {:class (stl/css-case :is-selectable is-selectable + :context-menu true + :is-open open? + :fixed fixed?) + :style {:top (+ top (:offset-y @local)) + :left (+ left (:offset-x @local))} + :on-key-down (on-key-down original-options parent-original)} + (let [level (-> @local :levels peek)] + [:ul {:class (stl/css-case :min-width min-width? + :context-menu-items true) + :role "menu" + :ref check-menu-offscreen} + (when-let [parent-option (:parent-option level)] + [:* + [:& context-menu-a11y-item + {:id "go-back-sub-option" + :class (stl/css :context-menu-item) + :tab-index "0" + :on-key-down (fn [event] + (dom/prevent-default event))} + [:button {:class (stl/css :context-menu-action :submenu-back) + :data-no-close true + :on-click exit-submenu} + [:span {:class (stl/css :submenu-icon-back)} i/arrow-refactor] + parent-option]] + + [:li {:class (stl/css :separator)}]]) + + (for [[index option] (d/enumerate (:options level))] + (let [option-name (:option-name option) + id (:id option) + sub-options (:sub-options option) + option-handler (:option-handler option) + data-test (:data-test option)] + (when option-name + (if (= option-name :separator) + [:li {:key (dm/str "context-item-" index) + :class (stl/css :separator)}] + [:& context-menu-a11y-item + {:id id + :key id + :class (stl/css-case + :is-selected (and selected (= option-name selected)) + :selected (and selected (= data-test selected)) + :context-menu-item true) + :key-index (dm/str "context-item-" index) + :tab-index "0" + :on-key-down (fn [event] + (dom/prevent-default event))} + (if-not sub-options + [:a {:class (stl/css :context-menu-action) + :on-click #(do (dom/stop-propagation %) + (on-close) + (option-handler %)) + :data-test data-test} + (if (and in-dashboard? (= option-name "Default")) + (tr "dashboard.default-team-name") + option-name) + + (when (and selected (= data-test selected)) + [:span {:class (stl/css :selected-icon)} i/tick-refactor])] + + [:a {:class (stl/css :context-menu-action :submenu) :data-no-close true - :on-click exit-submenu} - [:span {:class (stl/css :submenu-icon-back)} i/arrow-refactor] - parent-option]] - - [:li {:class (stl/css :separator)}]]) - - (for [[index option] (d/enumerate (:options level))] - (let [option-name (:option-name option) - id (:id option) - sub-options (:sub-options option) - option-handler (:option-handler option) - data-test (:data-test option)] - (when option-name - (if (= option-name :separator) - [:li {:key (dm/str "context-item-" index) - :class (stl/css :separator)}] - [:& context-menu-a11y-item - {:id id - :key id - :class (stl/css-case - :is-selected (and selected (= option-name selected)) - :selected (and selected (= data-test selected)) - :context-menu-item true) - :key-index (dm/str "context-item-" index) - :tab-index "0" - :on-key-down (fn [event] - (dom/prevent-default event))} - (if-not sub-options - [:a {:class (stl/css :context-menu-action) - :on-click #(do (dom/stop-propagation %) - (on-close) - (option-handler %)) - :data-test data-test} - (if (and in-dashboard? (= option-name "Default")) - (tr "dashboard.default-team-name") - option-name) - - (when (and selected (= data-test selected)) - [:span {:class (stl/css :selected-icon)} i/tick-refactor])] - - [:a {:class (stl/css :context-menu-action :submenu) - :data-no-close true - :on-click (enter-submenu option-name sub-options) - :data-test data-test} - option-name - [:span {:class (stl/css :submenu-icon)} i/arrow-refactor]])]))))])])]) - - ;; OLD - (when (and open? (some? (:levels @local))) - [:> dropdown' props - - (let [level (-> @local :levels peek) - original-options (:options level) - parent-original (:parent-option level)] - [:div {:class (dom/classnames :is-selectable is-selectable - :context-menu true - :is-open open? - :fixed fixed?) - :style {:top (+ top (:offset-y @local)) - :left (+ left (:offset-x @local))} - :on-key-down (on-key-down original-options parent-original)} - (let [level (-> @local :levels peek)] - [:ul {:class (dom/classnames :min-width min-width? - :context-menu-items true) - :role "menu" - :ref check-menu-offscreen} - (when-let [parent-option (:parent-option level)] - [:* - [:& context-menu-a11y-item - {:id "go-back-sub-option" - :tab-index "0" - :on-key-down (fn [event] - (dom/prevent-default event))} - [:div {:class (dom/classnames :context-menu-action true - :submenu-back true) - :data-no-close true - :on-click exit-submenu} - [:span i/arrow-slide] - - parent-option]] - - [:li.separator]]) - - (for [[index option] (d/enumerate (:options level))] - (let [option-name (:option-name option) - id (:id option) - sub-options (:sub-options option) - option-handler (:option-handler option) - data-test (:data-test option)] - (when option-name - (if (= option-name :separator) - [:li.separator {:key (dm/str "context-item-" index)}] - [:& context-menu-a11y-item - {:id id - :key id - :class (dom/classnames :is-selected (and selected (= option-name selected))) - :key-index (dm/str "context-item-" index) - :tab-index "0" - :on-key-down (fn [event] - (dom/prevent-default event))} - (if-not sub-options - [:a {:class (dom/classnames :context-menu-action true) - :on-click #(do (dom/stop-propagation %) - (on-close) - (option-handler %)) - :data-test data-test} - (if (and in-dashboard? (= option-name "Default")) - (tr "dashboard.default-team-name") - option-name)] - [:a.context-menu-action.submenu - {:data-no-close true - :on-click (enter-submenu option-name sub-options) - :data-test data-test} - option-name - [:span i/arrow-slide]])]))))])])])))) + :on-click (enter-submenu option-name sub-options) + :data-test data-test} + option-name + [:span {:class (stl/css :submenu-icon)} i/arrow-refactor]])]))))])])]))) (mf/defc context-menu-a11y {::mf/wrap-props false} diff --git a/frontend/src/app/main/ui/components/file_uploader.cljs b/frontend/src/app/main/ui/components/file_uploader.cljs index 71af00d4ed..35429e09ee 100644 --- a/frontend/src/app/main/ui/components/file_uploader.cljs +++ b/frontend/src/app/main/ui/components/file_uploader.cljs @@ -30,15 +30,14 @@ (when label-text [:label {:for input-id :class-name label-class} label-text]) - [:input - {:style {:display "none" - :width 0} - :id input-id - :multiple multi - :accept accept - :type "file" - :ref input-ref - :on-change on-files-selected - :data-test data-test - :aria-label "uploader"}]])) + [:input {:style {:display "none" + :width 0} + :id input-id + :multiple multi + :accept accept + :type "file" + :ref input-ref + :on-change on-files-selected + :data-test data-test + :aria-label "uploader"}]])) diff --git a/frontend/src/app/main/ui/components/forms.cljs b/frontend/src/app/main/ui/components/forms.cljs index 11b633cc86..70469c23a1 100644 --- a/frontend/src/app/main/ui/components/forms.cljs +++ b/frontend/src/app/main/ui/components/forms.cljs @@ -386,21 +386,19 @@ (mf/defc submit-button* {::mf/wrap-props false} [props] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - form (or (unchecked-get props "form") + (let [form (or (unchecked-get props "form") (mf/use-ctx form-ctx)) label (unchecked-get props "label") on-click (unchecked-get props "onClick") children (unchecked-get props "children") - class (d/nilv (unchecked-get props "className") "btn-primary btn-large") + class (unchecked-get props "className") name (d/nilv (unchecked-get props "name") "submit") disabled? (or (and (some? form) (not (:valid @form))) (true? (unchecked-get props "disabled"))) - klass (dm/str class " " (if disabled? "btn-disabled" "")) new-klass (dm/str class " " (if disabled? (stl/css :btn-disabled) "")) on-key-down @@ -416,7 +414,7 @@ (obj/set! "onKeyDown" on-key-down) (obj/set! "name" name) (obj/set! "label" mf/undefined) - (obj/set! "className" (if new-css-system new-klass klass)) + (obj/set! "className" new-klass) (obj/set! "type" "submit"))] [:> "button" props diff --git a/frontend/src/app/main/ui/components/forms.scss b/frontend/src/app/main/ui/components/forms.scss index e0d10993b5..ec64c521c8 100644 --- a/frontend/src/app/main/ui/components/forms.scss +++ b/frontend/src/app/main/ui/components/forms.scss @@ -24,7 +24,7 @@ cursor: pointer; color: var(--modal-title-foreground-color); text-transform: uppercase; - + margin-bottom: $s-8; input { @extend .input-element; color: var(--input-foreground-color-active); @@ -143,6 +143,7 @@ .hint { @include titleTipography; + color: var(--modal-text-foreground-color); width: 99%; } diff --git a/frontend/src/app/main/ui/components/search_bar.cljs b/frontend/src/app/main/ui/components/search_bar.cljs index 507b68d1e7..705bb3b4d7 100644 --- a/frontend/src/app/main/ui/components/search_bar.cljs +++ b/frontend/src/app/main/ui/components/search_bar.cljs @@ -5,7 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.components.search-bar - (:require-macros [app.main.style :refer [css]]) + (:require-macros [app.main.style :as stl]) (:require [app.main.ui.icons :as i] [app.util.dom :as dom] @@ -45,16 +45,16 @@ node (dom/get-target event)] (when ^boolean enter? (dom/blur! node)) (when ^boolean esc? (dom/blur! node)))))] - [:span {:class (dom/classnames (css :search-box) true - (css :has-children) (some? children))} + [:span {:class (stl/css-case :search-box true + :has-children (some? children))} children - [:div {:class (dom/classnames (css :search-input-wrapper) true)} + [:div {:class (stl/css :search-input-wrapper)} icon [:input {:on-change handle-change :value value :placeholder placeholder :on-key-down handle-key-down}] (when (not= "" value) - [:button {:class (dom/classnames (css :clear) true) + [:button {:class (stl/css :clear) :on-click handle-clear} i/delete-text-refactor])]])) diff --git a/frontend/src/app/main/ui/components/search_bar.scss b/frontend/src/app/main/ui/components/search_bar.scss index 9f568ea286..850b8af221 100644 --- a/frontend/src/app/main/ui/components/search_bar.scss +++ b/frontend/src/app/main/ui/components/search_bar.scss @@ -13,55 +13,56 @@ width: 100%; border-radius: $br-8; background-color: var(--search-bar-background-color); +} - .search-input-wrapper { - @include flexCenter; - height: $s-32; +.search-input-wrapper { + @include flexCenter; + height: $s-32; + width: 100%; + border: $s-1 solid var(--search-bar-input-border-color); + border-radius: $br-8; + background-color: var(--search-bar-input-background-color); + input { width: 100%; - border: $s-1 solid var(--search-bar-input-border-color); - border-radius: $br-8; - background-color: var(--search-bar-input-background-color); - input { - width: 100%; - height: 100%; - margin: 0 $s-8 0 $s-4; - border: 0; - background-color: var(--input-background-color); - font-size: $fs-12; - color: var(--input-foreground-color); - &:focus { - outline: none; - } - } - &:hover { - border: $s-1 solid var(--input-background-color-hover); - background-color: var(--input-background-color-hover); - input { - background-color: var(--input-background-color-hover); - } - } - &:focus-within { - background-color: var(--input-background-color-active); - color: var(--input-foreground-color-active); - border: $s-1 solid var(--input-border-color-focus); - input { - background-color: var(--input-background-color-active); - } - } - - .clear { - @extend .button-tag; - border-radius: $br-8; - height: 100%; - svg { - @extend .button-icon-small; - color: transparent; - stroke: var(--icon-foreground); - } + height: 100%; + margin: 0 $s-8 0 $s-4; + border: 0; + background-color: var(--input-background-color); + font-size: $fs-12; + color: var(--input-foreground-color); + &:focus { + outline: none; } } - &.has-children .search-input-wrapper { - border-radius: $br-2 $br-8 $br-8 $br-2; - margin-left: 0; + &:hover { + border: $s-1 solid var(--input-background-color-hover); + background-color: var(--input-background-color-hover); + input { + background-color: var(--input-background-color-hover); + } + } + &:focus-within { + background-color: var(--input-background-color-active); + color: var(--input-foreground-color-active); + border: $s-1 solid var(--input-border-color-focus); + input { + background-color: var(--input-background-color-active); + } } } + +.clear { + @extend .button-tag; + border-radius: $br-8; + height: 100%; + svg { + @extend .button-icon-small; + color: transparent; + stroke: var(--icon-foreground); + } +} + +.search-box.has-children .search-input-wrapper { + border-radius: $br-2 $br-8 $br-8 $br-2; + margin-left: 0; +} diff --git a/frontend/src/app/main/ui/components/tab_container.cljs b/frontend/src/app/main/ui/components/tab_container.cljs index 9b52965a56..be104cfcf6 100644 --- a/frontend/src/app/main/ui/components/tab_container.cljs +++ b/frontend/src/app/main/ui/components/tab_container.cljs @@ -9,7 +9,6 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :refer [tr]] @@ -19,9 +18,8 @@ (mf/defc tab-element {::mf/wrap-props false} [props] - (let [children (unchecked-get props "children") - new-css-system (mf/use-ctx ctx/new-css-system)] - [:div {:class (stl/css new-css-system :tab-element)} + (let [children (unchecked-get props "children")] + [:div {:class (stl/css :tab-element)} children])) (mf/defc tab-container diff --git a/frontend/src/app/main/ui/confirm.cljs b/frontend/src/app/main/ui/confirm.cljs index d17cfaee91..cfce7c2c13 100644 --- a/frontend/src/app/main/ui/confirm.cljs +++ b/frontend/src/app/main/ui/confirm.cljs @@ -9,7 +9,6 @@ (:require [app.main.data.modal :as modal] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr t]] @@ -31,8 +30,7 @@ cancel-label accept-label accept-style] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - locale (mf/deref i18n/locale) + (let [locale (mf/deref i18n/locale) on-accept (or on-accept identity) on-cancel (or on-cancel identity) @@ -67,87 +65,45 @@ (partial events/unlistenByKey)))) - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-container)} - [:div {:class (stl/css :modal-header)} - [:h2 {:class (stl/css :modal-title)} title] - [:button {:class (stl/css :modal-close-btn) - :on-click cancel-fn} i/close-refactor]] + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} title] + [:button {:class (stl/css :modal-close-btn) + :on-click cancel-fn} i/close-refactor]] - [:div {:class (stl/css :modal-content)} - (when (and (string? message) (not= message "")) - [:h3 {:class (stl/css :modal-msg)} message]) - (when (and (string? scd-message) (not= scd-message "")) - [:h3 {:class (stl/css :modal-scd-msg)} scd-message]) - (when (string? hint) - [:p {:class (stl/css :modal-hint)} hint]) - (when (> (count items) 0) - [:* - [:p {:class (stl/css :modal-subtitle)} - (tr "ds.component-subtitle")] - [:ul {:class (stl/css :component-list)} - (for [item items] - [:li {:class (stl/css :modal-item-element)} - [:span {:class (stl/css :modal-component-icon)} - i/component-refactor] - [:span {:class (stl/css :modal-component-name)} - (:name item)]])]])] - - [:div {:class (stl/css :modal-footer)} - [:div {:class (stl/css :action-buttons)} - (when-not (= cancel-label :omit) - [:input - {:class (stl/css :cancel-button) - :type "button" - :value cancel-label - :on-click cancel-fn}]) + [:div {:class (stl/css :modal-content)} + (when (and (string? message) (not= message "")) + [:h3 {:class (stl/css :modal-msg)} message]) + (when (and (string? scd-message) (not= scd-message "")) + [:h3 {:class (stl/css :modal-scd-msg)} scd-message]) + (when (string? hint) + [:p {:class (stl/css :modal-hint)} hint]) + (when (> (count items) 0) + [:* + [:p {:class (stl/css :modal-subtitle)} + (tr "ds.component-subtitle")] + [:ul {:class (stl/css :component-list)} + (for [item items] + [:li {:class (stl/css :modal-item-element)} + [:span {:class (stl/css :modal-component-icon)} + i/component-refactor] + [:span {:class (stl/css :modal-component-name)} + (:name item)]])]])] + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + (when-not (= cancel-label :omit) [:input - {:class (stl/css-case :accept-btn true - :danger (= accept-style :danger) - :primary (= accept-style :primary)) + {:class (stl/css :cancel-button) :type "button" - :value accept-label - :on-click accept-fn}]]]]] + :value cancel-label + :on-click cancel-fn}]) - - [:div.modal-overlay - [:div.modal-container.confirm-dialog - [:div.modal-header - [:div.modal-header-title - [:h2 title]] - [:div.modal-close-button - {:on-click cancel-fn} i/close]] - - [:div.modal-content - (when (and (string? message) (not= message "")) - [:h3 message]) - (when (and (string? scd-message) (not= scd-message "")) - [:h3 scd-message]) - (when (string? hint) - [:p hint]) - (when (> (count items) 0) - [:* - [:p (tr "ds.component-subtitle")] - [:ul.component-list - (for [item items] - [:li.modal-item-element - [:span.modal-component-icon i/component] - [:span (:name item)]])]])] - - [:div.modal-footer - [:div.action-buttons - (when-not (= cancel-label :omit) - [:input.cancel-button - {:type "button" - :value cancel-label - :on-click cancel-fn}]) - - [:input.accept-button - {:class (dom/classnames - :danger (= accept-style :danger) - :primary (= accept-style :primary)) - :type "button" - :value accept-label - :on-click accept-fn}]]]]]))) + [:input + {:class (stl/css-case :accept-btn true + :danger (= accept-style :danger) + :primary (= accept-style :primary)) + :type "button" + :value accept-label + :on-click accept-fn}]]]]])) diff --git a/frontend/src/app/main/ui/confirm.scss b/frontend/src/app/main/ui/confirm.scss index 7d01aff684..ea944f3e5f 100644 --- a/frontend/src/app/main/ui/confirm.scss +++ b/frontend/src/app/main/ui/confirm.scss @@ -11,56 +11,63 @@ &.transparent { background-color: transparent; } - .modal-container { - @extend .modal-container-base; - .modal-header { - margin-bottom: $s-24; - .modal-title { - @include tabTitleTipography; - color: var(--modal-title-foreground-color); - } - .modal-close-btn { - @extend .modal-close-btn-base; - } - } - .modal-content { - @include titleTipography; - margin-bottom: $s-24; - .component-list { - .modal-item-element { - @include flexRow; - .modal-component-icon { - @include flexCenter; - height: $s-16; - width: $s-16; - svg { - @extend .button-icon-small; - stroke: var(--color); - } - } - .modal-component-name { - @include titleTipography; - } - } - } - .modal-hint { - @extend .modal-hint-base; - } - } - .modal-footer { - .action-buttons { - @extend .modal-action-btns; - .cancel-button { - @extend .modal-cancel-btn; - } - .accept-btn { - @extend .modal-accept-btn; - &.danger { - @extend .modal-danger-btn; - } - } - } - } +} + +.modal-container { + @extend .modal-container-base; +} + +.modal-header { + margin-bottom: $s-24; +} + +.modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); +} + +.modal-close-btn { + @extend .modal-close-btn-base; +} + +.modal-content { + @include titleTipography; + margin-bottom: $s-24; +} + +.modal-item-element { + @include flexRow; +} + +.modal-component-icon { + @include flexCenter; + height: $s-16; + width: $s-16; + svg { + @extend .button-icon-small; + stroke: var(--color); + } +} +.modal-component-name { + @include titleTipography; +} + +.modal-hint { + @extend .modal-hint-base; +} + +.action-buttons { + @extend .modal-action-btns; +} + +.cancel-button { + @extend .modal-cancel-btn; +} + +.accept-btn { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; } } diff --git a/frontend/src/app/main/ui/dashboard.cljs b/frontend/src/app/main/ui/dashboard.cljs index 6905c68bc0..ec956fa08e 100644 --- a/frontend/src/app/main/ui/dashboard.cljs +++ b/frontend/src/app/main/ui/dashboard.cljs @@ -53,8 +53,7 @@ (mf/defc dashboard-content [{:keys [team projects project section search-term profile] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - container (mf/use-ref) + (let [container (mf/use-ref) content-width (mf/use-state 0) project-id (:id project) team-id (:id team) @@ -82,123 +81,65 @@ (mf/use-effect on-resize) - (if new-css-system - [:div {:class (stl/css :dashboard-content) - :on-click clear-selected-fn :ref container} - (case section - :dashboard-projects - [:* - [:& projects-section - {:team team - :projects projects - :profile profile - :default-project-id default-project-id}] + [:div {:class (stl/css :dashboard-content) + :on-click clear-selected-fn :ref container} + (case section + :dashboard-projects + [:* + [:& projects-section + {:team team + :projects projects + :profile profile + :default-project-id default-project-id}] + (when (contains? cf/flags :dashboard-templates-section) + [:& templates-section {:profile profile + :project-id project-id + :team-id team-id + :default-project-id default-project-id + :content-width @content-width}])] + + :dashboard-fonts + [:& fonts-page {:team team}] + + :dashboard-font-providers + [:& font-providers-page {:team team}] + + :dashboard-files + (when project + [:* + [:& files-section {:team team :project project}] (when (contains? cf/flags :dashboard-templates-section) [:& templates-section {:profile profile - :project-id project-id :team-id team-id - :default-project-id default-project-id - :content-width @content-width}])] - - :dashboard-fonts - [:& fonts-page {:team team}] - - :dashboard-font-providers - [:& font-providers-page {:team team}] - - :dashboard-files - (when project - [:* - [:& files-section {:team team :project project}] - (when (contains? cf/flags :dashboard-templates-section) - [:& templates-section {:profile profile - :team-id team-id - :project-id project-id - :default-project-id default-project-id - :content-width @content-width}])]) - - :dashboard-search - [:& search-page {:team team - :search-term search-term}] - - :dashboard-libraries - [:& libraries-page {:team team}] - - :dashboard-team-members - [:& team-members-page {:team team :profile profile}] - - :dashboard-team-invitations - [:& team-invitations-page {:team team}] - - :dashboard-team-webhooks - [:& team-webhooks-page {:team team}] - - :dashboard-team-settings - [:& team-settings-page {:team team :profile profile}] - - nil)] - - ;; OLD - [:div.dashboard-content {:on-click clear-selected-fn :ref container} - (case section - :dashboard-projects - [:* - [:& projects-section - {:team team - :projects projects - :profile profile - :default-project-id default-project-id}] - - (when (contains? cf/flags :dashboard-templates-section) - [:& templates-section {:profile profile :project-id project-id - :team-id team-id :default-project-id default-project-id - :content-width @content-width}])] + :content-width @content-width}])]) - :dashboard-fonts - [:& fonts-page {:team team}] + :dashboard-search + [:& search-page {:team team + :search-term search-term}] - :dashboard-font-providers - [:& font-providers-page {:team team}] + :dashboard-libraries + [:& libraries-page {:team team}] - :dashboard-files - (when project - [:* - [:& files-section {:team team :project project}] - (when (contains? cf/flags :dashboard-templates-section) - [:& templates-section {:profile profile - :team-id team-id - :project-id project-id - :default-project-id default-project-id - :content-width @content-width}])]) + :dashboard-team-members + [:& team-members-page {:team team :profile profile}] - :dashboard-search - [:& search-page {:team team - :search-term search-term}] + :dashboard-team-invitations + [:& team-invitations-page {:team team}] - :dashboard-libraries - [:& libraries-page {:team team}] + :dashboard-team-webhooks + [:& team-webhooks-page {:team team}] - :dashboard-team-members - [:& team-members-page {:team team :profile profile}] + :dashboard-team-settings + [:& team-settings-page {:team team :profile profile}] - :dashboard-team-invitations - [:& team-invitations-page {:team team}] - - :dashboard-team-webhooks - [:& team-webhooks-page {:team team}] - - :dashboard-team-settings - [:& team-settings-page {:team team :profile profile}] - - nil)]))) + nil)])) (mf/defc dashboard [{:keys [route profile] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - section (get-in route [:data :name]) + (let [section (get-in route [:data :name]) params (parse-params route) project-id (:project-id params) @@ -227,59 +168,31 @@ (fn [] (events/unlistenByKey key)))) - (if new-css-system - [:& (mf/provider ctx/current-team-id) {:value team-id} - [:& (mf/provider ctx/current-project-id) {:value project-id} - ;; NOTE: dashboard events and other related functions assumes - ;; that the team is a implicit context variable that is - ;; available using react context or accessing - ;; the :current-team-id on the state. We set the key to the - ;; team-id because we want to completely refresh all the - ;; components on team change. Many components assumes that the - ;; team is already set so don't put the team into mf/deps. - (when team - [:main {:class (stl/css :dashboard) - :key (:id team)} - [:& sidebar - {:team team - :projects projects - :project project + [:& (mf/provider ctx/current-team-id) {:value team-id} + [:& (mf/provider ctx/current-project-id) {:value project-id} + ;; NOTE: dashboard events and other related functions assumes + ;; that the team is a implicit context variable that is + ;; available using react context or accessing + ;; the :current-team-id on the state. We set the key to the + ;; team-id because we want to completely refresh all the + ;; components on team change. Many components assumes that the + ;; team is already set so don't put the team into mf/deps. + (when team + [:main {:class (stl/css :dashboard) + :key (:id team)} + [:& sidebar + {:team team + :projects projects + :project project + :profile profile + :section section + :search-term search-term}] + (when (and team profile (seq projects)) + [:& dashboard-content + {:projects projects :profile profile - :section section - :search-term search-term}] - (when (and team profile (seq projects)) - [:& dashboard-content - {:projects projects - :profile profile - :project project - :section section - :search-term search-term - :team team}])])]] - - [:& (mf/provider ctx/current-team-id) {:value team-id} - [:& (mf/provider ctx/current-project-id) {:value project-id} - ;; NOTE: dashboard events and other related functions assumes - ;; that the team is a implicit context variable that is - ;; available using react context or accessing - ;; the :current-team-id on the state. We set the key to the - ;; team-id because we want to completely refresh all the - ;; components on team change. Many components assumes that the - ;; team is already set so don't put the team into mf/deps. - (when team - [:main {:class (dom/classnames :dashboard-layout true) :key (:id team)} - [:& sidebar - {:team team - :projects projects :project project - :profile profile :section section - :search-term search-term}] - (when (and team profile (seq projects)) - [:& dashboard-content - {:projects projects - :profile profile - :project project - :section section - :search-term search-term - :team team}])])]]))) + :search-term search-term + :team team}])])]])) diff --git a/frontend/src/app/main/ui/dashboard/change_owner.cljs b/frontend/src/app/main/ui/dashboard/change_owner.cljs index bb8eb1ea7d..cae033df37 100644 --- a/frontend/src/app/main/ui/dashboard/change_owner.cljs +++ b/frontend/src/app/main/ui/dashboard/change_owner.cljs @@ -12,7 +12,6 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.forms :as fm] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [tr]] [cljs.spec.alpha :as s] @@ -26,8 +25,7 @@ {::mf/register modal/components ::mf/register-as :leave-and-reassign} [{:keys [profile team accept]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - form (fm/use-form :spec ::leave-modal-form :initial {}) + (let [form (fm/use-form :spec ::leave-modal-form :initial {}) members-map (mf/deref refs/dashboard-team-members) members (vals members-map) @@ -42,71 +40,37 @@ (let [member-id (get-in @form [:clean-data :member-id])] (accept member-id)))] - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-container)} - [:div {:class (stl/css :modal-header)} - [:h2 {:class (stl/css :modal-title)} (tr "modals.leave-and-reassign.title")] - [:button {:class (stl/css :modal-close-btn) - :on-click on-cancel} i/close-refactor]] + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} (tr "modals.leave-and-reassign.title")] + [:button {:class (stl/css :modal-close-btn) + :on-click on-cancel} i/close-refactor]] - [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :modal-content)} + [:p {:class (stl/css :modal-msg)} + (tr "modals.leave-and-reassign.hint1" (:name team))] + + (if (empty? members) [:p {:class (stl/css :modal-msg)} - (tr "modals.leave-and-reassign.hint1" (:name team))] + (tr "modals.leave-and-reassign.forbidden")] + [:* + [:& fm/form {:form form} + [:& fm/select {:name :member-id + :options options}]]])] - (if (empty? members) - [:p {:class (stl/css :modal-msg)} - (tr "modals.leave-and-reassign.forbidden")] - [:* - [:& fm/form {:form form} - [:& fm/select {:name :member-id - :options options}]]])] + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:input {:class (stl/css :cancel-button) + :type "button" + :value (tr "labels.cancel") + :on-click on-cancel}] - [:div {:class (stl/css :modal-footer)} - [:div {:class (stl/css :action-buttons)} - [:input {:class (stl/css :cancel-button) - :type "button" - :value (tr "labels.cancel") - :on-click on-cancel}] - - [:input.accept-button - {:type "button" - :class (stl/css-case :accept-btn true - :danger (:valid @form) - :global/disabled (not (:valid @form))) - :disabled (not (:valid @form)) - :value (tr "modals.leave-and-reassign.promote-and-leave") - :on-click on-accept}]]]]] - - - [:div.modal-overlay - [:div.modal-container.confirm-dialog - [:div.modal-header - [:div.modal-header-title - [:h2 (tr "modals.leave-and-reassign.title")]] - [:div.modal-close-button - {:on-click on-cancel} i/close]] - - [:div.modal-content.generic-form - [:p (tr "modals.leave-and-reassign.hint1" (:name team))] - - (if (empty? members) - [:p (tr "modals.leave-and-reassign.forbidden")] - [:* - [:& fm/form {:form form} - [:& fm/select {:name :member-id - :options options}]]])] - - [:div.modal-footer - [:div.action-buttons - [:input.cancel-button - {:type "button" - :value (tr "labels.cancel") - :on-click on-cancel}] - - [:input.accept-button - {:type "button" - :class (if (:valid @form) "danger" "btn-disabled") - :disabled (not (:valid @form)) - :value (tr "modals.leave-and-reassign.promote-and-leave") - :on-click on-accept}]]]]]))) + [:input.accept-button + {:type "button" + :class (stl/css-case :accept-btn true + :danger (:valid @form) + :global/disabled (not (:valid @form))) + :disabled (not (:valid @form)) + :value (tr "modals.leave-and-reassign.promote-and-leave") + :on-click on-accept}]]]]])) diff --git a/frontend/src/app/main/ui/dashboard/change_owner.scss b/frontend/src/app/main/ui/dashboard/change_owner.scss index 799d566c7c..3c91c1a92b 100644 --- a/frontend/src/app/main/ui/dashboard/change_owner.scss +++ b/frontend/src/app/main/ui/dashboard/change_owner.scss @@ -8,39 +8,46 @@ .modal-overlay { @extend .modal-overlay-base; - .modal-container { - @extend .modal-container-base; - border: $s-1 solid var(--modal-border-color); - .modal-header { - margin-bottom: $s-24; - .modal-title { - @include tabTitleTipography; - color: var(--modal-title-foreground-color); - } - .modal-close-btn { - @extend .modal-close-btn-base; - } - } - .modal-content { - @include titleTipography; - margin-bottom: $s-24; - .input-wrapper { - @extend .input-with-label; - } - } - .modal-footer { - .action-buttons { - @extend .modal-action-btns; - .cancel-button { - @extend .modal-cancel-btn; - } - .accept-btn { - @extend .modal-accept-btn; - &.danger { - @extend .modal-danger-btn; - } - } - } - } +} + +.modal-container { + @extend .modal-container-base; + border: $s-1 solid var(--modal-border-color); +} + +.modal-header { + margin-bottom: $s-24; +} + +.modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); +} + +.modal-close-btn { + @extend .modal-close-btn-base; +} + +.modal-content { + @include titleTipography; + margin-bottom: $s-24; +} + +.input-wrapper { + @extend .input-with-label; +} + +.action-buttons { + @extend .modal-action-btns; +} + +.cancel-button { + @extend .modal-cancel-btn; +} + +.accept-btn { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; } } diff --git a/frontend/src/app/main/ui/dashboard/comments.cljs b/frontend/src/app/main/ui/dashboard/comments.cljs index 45c4d75d60..fcc5502517 100644 --- a/frontend/src/app/main/ui/dashboard/comments.cljs +++ b/frontend/src/app/main/ui/dashboard/comments.cljs @@ -14,9 +14,7 @@ [app.main.store :as st] [app.main.ui.comments :as cmt] [app.main.ui.components.dropdown :refer [dropdown]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] - [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] [potok.v2.core :as ptk] @@ -42,21 +40,18 @@ (on-show-comments event))))] [:div {:class (stl/css :dashboard-comments-section)} - [:button - {:tab-index "0" - :on-click on-show-comments - :on-key-down handle-keydown - :data-test "open-comments" - :class (stl/css-case :button true - :open show? - :unread (boolean (seq tgroups)))} - i/chat]])) + [:button {:tab-index "0" + :on-click on-show-comments + :on-key-down handle-keydown + :data-test "open-comments" + :class (stl/css-case :button true + :open show? + :unread (boolean (seq tgroups)))} + i/comments-refactor]])) (mf/defc comments-section - [{:keys [profile team show? on-show-comments on-hide-comments]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - - threads-map (mf/deref refs/comment-threads) + [{:keys [profile team show? on-hide-comments]}] + (let [threads-map (mf/deref refs/comment-threads) users (mf/deref refs/current-team-comments-users) team-id (:id team) @@ -91,79 +86,32 @@ (st/emit! (ptk/event ::ev/event {::ev/name "open-comment-notifications" ::ev/origin "dashboard"}))))) - (if new-css-system - [:div {:class (stl/css :dashboard-comments-section)} - [:& dropdown {:show show? :on-close on-hide-comments} - [:div {:class (stl/css :dropdown :comments-section :comment-threads-section)} - [:div {:class (stl/css :header)} - [:h3 (tr "labels.comments")] - [:button - {:class (stl/css :close) - :tab-index (if show? "0" "-1") - :on-click on-hide-comments - :on-key-down handle-keydown} i/close]] + [:div {:class (stl/css :dashboard-comments-section)} + [:& dropdown {:show show? :on-close on-hide-comments} + [:div {:class (stl/css :dropdown :comments-section :comment-threads-section)} + [:div {:class (stl/css :header)} + [:h3 (tr "labels.comments")] + [:button + {:class (stl/css :close) + :tab-index (if show? "0" "-1") + :on-click on-hide-comments + :on-key-down handle-keydown} i/close]] - (if (seq tgroups) - [:div {:class (stl/css :thread-groups)} + (if (seq tgroups) + [:div {:class (stl/css :thread-groups)} + [:& cmt/comment-thread-group + {:group (first tgroups) + :on-thread-click on-navigate + :show-file-name true + :users users}] + (for [tgroup (rest tgroups)] [:& cmt/comment-thread-group - {:group (first tgroups) + {:group tgroup :on-thread-click on-navigate :show-file-name true - :users users}] - (for [tgroup (rest tgroups)] - [:& cmt/comment-thread-group - {:group tgroup - :on-thread-click on-navigate - :show-file-name true - :users users - :key (:page-id tgroup)}])] + :users users + :key (:page-id tgroup)}])] - [:div {:class (stl/css :thread-groups-placeholder)} - i/chat - (tr "labels.no-comments-available")])]]] - - ;; OLD - [:div.dashboard-comments-section - [:div.button - {:tab-index "0" - :on-click on-show-comments - :on-key-down (fn [event] - (when (kbd/enter? event) - (on-show-comments event))) - :data-test "open-comments" - :class (dom/classnames :open show? - :unread (boolean (seq tgroups)))} - i/chat] - - [:& dropdown {:show show? :on-close on-hide-comments} - [:div.dropdown.comments-section.comment-threads-section. - [:div.header - [:h3 (tr "labels.comments")] - [:span.close {:tab-index (if show? "0" "-1") - :on-click on-hide-comments - :on-key-down handle-keydown} - i/close]] - - [:hr] - - (if (seq tgroups) - [:div.thread-groups - [:& cmt/comment-thread-group - {:group (first tgroups) - :on-thread-click on-navigate - :show-file-name true - :users users}] - (for [tgroup (rest tgroups)] - [:* - [:hr] - - [:& cmt/comment-thread-group - {:group tgroup - :on-thread-click on-navigate - :show-file-name true - :users users - :key (:page-id tgroup)}]])] - - [:div.thread-groups-placeholder - i/chat - (tr "labels.no-comments-available")])]]]))) + [:div {:class (stl/css :thread-groups-placeholder)} + i/comments-refactor + (tr "labels.no-comments-available")])]]])) diff --git a/frontend/src/app/main/ui/dashboard/comments.scss b/frontend/src/app/main/ui/dashboard/comments.scss index ed615fa73a..395d08005d 100644 --- a/frontend/src/app/main/ui/dashboard/comments.scss +++ b/frontend/src/app/main/ui/dashboard/comments.scss @@ -47,9 +47,11 @@ font-size: $fs-12; padding: $s-24; text-align: center; + color: $df-secondary; svg { - fill: $df-secondary; + stroke: $df-secondary; + fill: none; height: $s-24; margin-bottom: $s-24; width: $s-24; @@ -65,21 +67,22 @@ width: $s-32; svg { - width: $s-16; - height: $s-16; - fill: $df-secondary; + min-width: $s-16; + min-height: $s-16; + stroke: $df-secondary; + fill: none; } &.unread svg, &.open svg { - fill: $da-tertiary; + stroke: $da-tertiary; } &:hover { background-color: $db-cuaternary; svg { - fill: $da-primary; + stroke: $da-primary; } } } diff --git a/frontend/src/app/main/ui/dashboard/export.cljs b/frontend/src/app/main/ui/dashboard/export.cljs index d1ff72b124..3adee05946 100644 --- a/frontend/src/app/main/ui/dashboard/export.cljs +++ b/frontend/src/app/main/ui/dashboard/export.cljs @@ -11,7 +11,6 @@ [app.common.data.macros :as dm] [app.main.data.modal :as modal] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.worker :as uw] [app.util.dom :as dom] @@ -24,36 +23,19 @@ (mf/defc export-entry {::mf/wrap-props false} [{:keys [file]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] + [:div {:class (stl/css-case :file-entry true + :loading (:loading? file) + :success (:export-success? file) + :error (:export-error? file))} - (if new-css-system - [:div {:class (stl/css-case :file-entry true - :loading (:loading? file) - :success (:export-success? file) - :error (:export-error? file))} + [:div {:class (stl/css :file-name)} + [:span {:class (stl/css :file-icon)} + (cond (:export-success? file) i/tick-refactor + (:export-error? file) i/close-refactor + (:loading? file) i/loader-pencil)] - [:div {:class (stl/css :file-name)} - [:span {:class (stl/css :file-icon)} - (cond (:export-success? file) i/tick-refactor - (:export-error? file) i/close-refactor - (:loading? file) i/loader-pencil)] - - [:div {:class (stl/css :file-name-label)} - (:name file)]]] - - - [:div.file-entry - {:class (dom/classnames - :loading (:loading? file) - :success (:export-success? file) - :error (:export-error? file))} - [:div.file-name - [:div.file-icon - (cond (:export-success? file) i/tick - (:export-error? file) i/close - (:loading? file) i/loader-pencil)] - - [:div.file-name-label (:name file)]]]))) + [:div {:class (stl/css :file-name-label)} + (:name file)]]]) (defn- mark-file-error [files file-id] @@ -79,8 +61,7 @@ ::mf/register-as :export ::mf/wrap-props false} [{:keys [team-id files has-libraries? binary? features]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - state* (mf/use-state + (let [state* (mf/use-state #(let [files (mapv (fn [file] (assoc file :loading? true)) files)] {:status :prepare :selected :all @@ -140,136 +121,70 @@ (when-not has-libraries? (start-export))) - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-container)} - [:div {:class (stl/css :modal-header)} - [:h2 {:class (stl/css :modal-title)} - (tr "dashboard.export.title")] - [:button {:class (stl/css :modal-close-btn) - :on-click on-cancel} i/close-refactor]] + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} + (tr "dashboard.export.title")] + [:button {:class (stl/css :modal-close-btn) + :on-click on-cancel} i/close-refactor]] - (cond - (= status :prepare) - [:* - [:div {:class (stl/css :modal-content)} - [:p {:class (stl/css :modal-msg)} (tr "dashboard.export.explain")] - [:p {:class (stl/css :modal-scd-msg)} (tr "dashboard.export.detail")] + (cond + (= status :prepare) + [:* + [:div {:class (stl/css :modal-content)} + [:p {:class (stl/css :modal-msg)} (tr "dashboard.export.explain")] + [:p {:class (stl/css :modal-scd-msg)} (tr "dashboard.export.detail")] - (for [type export-types] - [:div {:class (stl/css :export-option true) - :key (name type)} - [:label {:for (str "export-" type) - :class (stl/css-case :global/checked (= selected type))} - ;; Execution time translation strings: - ;; dashboard.export.options.all.message - ;; dashboard.export.options.all.title - ;; dashboard.export.options.detach.message - ;; dashboard.export.options.detach.title - ;; dashboard.export.options.merge.message - ;; dashboard.export.options.merge.title - [:span {:class (stl/css-case :global/checked (= selected type))} - (when (= selected type) - i/status-tick-refactor)] - [:div {:class (stl/css :option-content)} - [:h3 {:class (stl/css :modal-subtitle)} (tr (dm/str "dashboard.export.options." (d/name type) ".title"))] - [:p {:class (stl/css :modal-msg)} (tr (dm/str "dashboard.export.options." (d/name type) ".message"))]] + (for [type export-types] + [:div {:class (stl/css :export-option true) + :key (name type)} + [:label {:for (str "export-" type) + :class (stl/css-case :global/checked (= selected type))} + ;; Execution time translation strings: + ;; dashboard.export.options.all.message + ;; dashboard.export.options.all.title + ;; dashboard.export.options.detach.message + ;; dashboard.export.options.detach.title + ;; dashboard.export.options.merge.message + ;; dashboard.export.options.merge.title + [:span {:class (stl/css-case :global/checked (= selected type))} + (when (= selected type) + i/status-tick-refactor)] + [:div {:class (stl/css :option-content)} + [:h3 {:class (stl/css :modal-subtitle)} (tr (dm/str "dashboard.export.options." (d/name type) ".title"))] + [:p {:class (stl/css :modal-msg)} (tr (dm/str "dashboard.export.options." (d/name type) ".message"))]] - [:input {:type "radio" - :class (stl/css :option-input) - :id (str "export-" type) - :checked (= selected type) - :name "export-option" - :data-type (name type) - :on-change on-change}]]])] + [:input {:type "radio" + :class (stl/css :option-input) + :id (str "export-" type) + :checked (= selected type) + :name "export-option" + :data-type (name type) + :on-change on-change}]]])] - [:div {:class (stl/css :modal-footer)} - [:div {:class (stl/css :action-buttons)} - [:input {:class (stl/css :cancel-button) - :type "button" - :value (tr "labels.cancel") - :on-click on-cancel}] + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:input {:class (stl/css :cancel-button) + :type "button" + :value (tr "labels.cancel") + :on-click on-cancel}] - [:input {:class (stl/css :accept-btn) - :type "button" - :value (tr "labels.continue") - :on-click on-accept}]]]] + [:input {:class (stl/css :accept-btn) + :type "button" + :value (tr "labels.continue") + :on-click on-accept}]]]] - (= status :exporting) - [:* - [:div {:class (stl/css :modal-content)} - (for [file (:files state)] - [:& export-entry {:file file :key (dm/str (:id file))}])] + (= status :exporting) + [:* + [:div {:class (stl/css :modal-content)} + (for [file (:files state)] + [:& export-entry {:file file :key (dm/str (:id file))}])] - [:div {:class (stl/css :modal-footer)} - [:div {:class (stl/css :action-buttons)} - [:input {:class (stl/css :accept-btn) - :type "button" - :value (tr "labels.close") - :disabled (->> state :files (some :loading?)) - :on-click on-cancel}]]]])]] - - - [:div.modal-overlay - [:div.modal-container.export-dialog - [:div.modal-header - [:div.modal-header-title - [:h2 (tr "dashboard.export.title")]] - - [:div.modal-close-button - {:on-click on-cancel} i/close]] - - (cond - (= status :prepare) - [:* - [:div.modal-content - [:p.explain (tr "dashboard.export.explain")] - [:p.detail (tr "dashboard.export.detail")] - - (for [type export-types] - [:div.export-option {:class (when (= selected type) "selected") - :key (name type)} - [:label.option-container - ;; Execution time translation strings: - ;; dashboard.export.options.all.message - ;; dashboard.export.options.all.title - ;; dashboard.export.options.detach.message - ;; dashboard.export.options.detach.title - ;; dashboard.export.options.merge.message - ;; dashboard.export.options.merge.title - [:h3 (tr (dm/str "dashboard.export.options." (d/name type) ".title"))] - [:p (tr (dm/str "dashboard.export.options." (d/name type) ".message"))] - [:input {:type "radio" - :checked (= selected type) - :data-type (name type) - :on-change on-change - :name "export-option"}] - [:span {:class "option-radio-check"}]]])] - - [:div.modal-footer - [:div.action-buttons - [:input.cancel-button - {:type "button" - :value (tr "labels.cancel") - :on-click on-cancel}] - - [:input.accept-button - {:class "primary" - :type "button" - :value (tr "labels.continue") - :on-click on-accept}]]]] - - (= status :exporting) - [:* - [:div.modal-content - (for [file (:files state)] - [:& export-entry {:file file :key (dm/str (:id file))}])] - - [:div.modal-footer - [:div.action-buttons - [:input.accept-button - {:class "primary" - :type "button" - :value (tr "labels.close") - :disabled (->> state :files (some :loading?)) - :on-click on-cancel}]]]])]]))) + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:input {:class (stl/css :accept-btn) + :type "button" + :value (tr "labels.close") + :disabled (->> state :files (some :loading?)) + :on-click on-cancel}]]]])]])) diff --git a/frontend/src/app/main/ui/dashboard/export.scss b/frontend/src/app/main/ui/dashboard/export.scss index 6d092f1679..98c9fd2516 100644 --- a/frontend/src/app/main/ui/dashboard/export.scss +++ b/frontend/src/app/main/ui/dashboard/export.scss @@ -8,59 +8,64 @@ .modal-overlay { @extend .modal-overlay-base; +} - .modal-container { - @extend .modal-container-base; - .modal-header { - margin-bottom: $s-24; - .modal-title { - @include tabTitleTipography; - color: var(--modal-title-foreground-color); - } - .modal-close-btn { - @extend .modal-close-btn-base; - } - } +.modal-container { + @extend .modal-container-base; +} - .modal-content { - @include titleTipography; - margin-bottom: $s-24; - .export-option { - @extend .input-checkbox; - width: 100%; - align-items: flex-start; - label { - align-items: flex-start; - .modal-subtitle { - @include tabTitleTipography; - color: var(--modal-title-foreground-color); - } - } - span { - margin-top: $s-8; - } - .option-content { - @include flexColumn; - @include titleTipography; - } - } - } +.modal-header { + margin-bottom: $s-24; +} - .modal-footer { - .action-buttons { - @extend .modal-action-btns; - .cancel-button { - @extend .modal-cancel-btn; - } - .accept-btn { - @extend .modal-accept-btn; - &.danger { - @extend .modal-danger-btn; - } - } - } +.modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); +} + +.modal-close-btn { + @extend .modal-close-btn-base; +} + +.modal-content { + @include titleTipography; + margin-bottom: $s-24; +} + +.export-option { + @extend .input-checkbox; + width: 100%; + align-items: flex-start; + label { + align-items: flex-start; + .modal-subtitle { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); } } + span { + margin-top: $s-8; + } +} + +.option-content { + @include flexColumn; + @include titleTipography; +} + +.action-buttons { + @extend .modal-action-btns; +} + +.cancel-button { + @extend .modal-cancel-btn; +} + +.accept-btn { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; + } } .modal-scd-msg, diff --git a/frontend/src/app/main/ui/dashboard/files.cljs b/frontend/src/app/main/ui/dashboard/files.cljs index 7faf96b97e..afbeb5afc4 100644 --- a/frontend/src/app/main/ui/dashboard/files.cljs +++ b/frontend/src/app/main/ui/dashboard/files.cljs @@ -11,7 +11,6 @@ [app.main.data.events :as ev] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.dashboard.grid :refer [grid]] [app.main.ui.dashboard.inline-edition :refer [inline-edition]] [app.main.ui.dashboard.project-menu :refer [project-menu]] @@ -26,8 +25,7 @@ (mf/defc header [{:keys [project create-fn] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - local (mf/use-state + (let [local (mf/use-state {:menu-open false :edition false}) @@ -64,137 +62,75 @@ (dd/clear-selected-files))))] - (if new-css-system - [:header {:class (stl/css :dashboard-header)} - (if (:is-default project) - [:div#dashboard-drafts-title {:class (stl/css :dashboard-title)} - [:h1 (tr "labels.drafts")]] + [:header {:class (stl/css :dashboard-header)} + (if (:is-default project) + [:div#dashboard-drafts-title {:class (stl/css :dashboard-title)} + [:h1 (tr "labels.drafts")]] - (if (:edition @local) - [:& inline-edition - {:content (:name project) - :on-end (fn [name] - (let [name (str/trim name)] - (when-not (str/empty? name) - (st/emit! (-> (dd/rename-project (assoc project :name name)) - (with-meta {::ev/origin "project"})))) - (swap! local assoc :edition false)))}] - [:div {:class (stl/css :dashboard-title)} - [:h1 {:on-double-click on-edit - :data-test "project-title" - :id (:id project)} - (:name project)]])) + (if (:edition @local) + [:& inline-edition + {:content (:name project) + :on-end (fn [name] + (let [name (str/trim name)] + (when-not (str/empty? name) + (st/emit! (-> (dd/rename-project (assoc project :name name)) + (with-meta {::ev/origin "project"})))) + (swap! local assoc :edition false)))}] + [:div {:class (stl/css :dashboard-title)} + [:h1 {:on-double-click on-edit + :data-test "project-title" + :id (:id project)} + (:name project)]])) - [:& project-menu {:project project - :show? (:menu-open @local) - :left (- (:x (:menu-pos @local)) 180) - :top (:y (:menu-pos @local)) - :on-edit on-edit - :on-menu-close on-menu-close - :on-import on-import}] + [:& project-menu {:project project + :show? (:menu-open @local) + :left (- (:x (:menu-pos @local)) 180) + :top (:y (:menu-pos @local)) + :on-edit on-edit + :on-menu-close on-menu-close + :on-import on-import}] - [:div {:class (stl/css :dashboard-header-actions)} - [:a - {:class (stl/css :btn-secondary :btn-small) + [:div {:class (stl/css :dashboard-header-actions)} + [:a + {:class (stl/css :btn-secondary :btn-small) + :tab-index "0" + :on-click on-create-click + :data-test "new-file" + :on-key-down (fn [event] + (when (kbd/enter? event) + (on-create-click event)))} + (tr "dashboard.new-file")] + + (when-not (:is-default project) + [:button + {:class (stl/css-case :icon true + :pin-icon true + :tooltip true + :tooltip-bottom true + :active (:is-pinned project)) :tab-index "0" - :on-click on-create-click - :data-test "new-file" + :on-click toggle-pin + :alt (tr "dashboard.pin-unpin") :on-key-down (fn [event] (when (kbd/enter? event) - (on-create-click event)))} - (tr "dashboard.new-file")] + (toggle-pin event)))} + (if (:is-pinned project) + i/pin-fill + i/pin)]) - (when-not (:is-default project) - [:button - {:class (stl/css-case :icon true - :pin-icon true - :tooltip true - :tooltip-bottom true - :active (:is-pinned project)) - :tab-index "0" - :on-click toggle-pin - :alt (tr "dashboard.pin-unpin") - :on-key-down (fn [event] - (when (kbd/enter? event) - (toggle-pin event)))} - (if (:is-pinned project) - i/pin-fill - i/pin)]) - - [:div - {:class (stl/css :icon :tooltip :tooltip-bottom-left) - :tab-index "0" - :on-click on-menu-click - :alt (tr "dashboard.options") - :on-key-down (fn [event] - (when (kbd/enter? event) - (on-menu-click event)))} - i/actions]]] - - ;; OLD - [:header.dashboard-header - (if (:is-default project) - [:div.dashboard-title#dashboard-drafts-title - [:h1 (tr "labels.drafts")]] - - (if (:edition @local) - [:& inline-edition {:content (:name project) - :on-end (fn [name] - (let [name (str/trim name)] - (when-not (str/empty? name) - (st/emit! (-> (dd/rename-project (assoc project :name name)) - (with-meta {::ev/origin "project"})))) - (swap! local assoc :edition false)))}] - [:div.dashboard-title - [:h1 {:on-double-click on-edit - :data-test "project-title" - :id (:id project)} - (:name project)]])) - - [:& project-menu {:project project - :show? (:menu-open @local) - :left (- (:x (:menu-pos @local)) 180) - :top (:y (:menu-pos @local)) - :on-edit on-edit - :on-menu-close on-menu-close - :on-import on-import}] - - [:div.dashboard-header-actions - [:a.btn-secondary.btn-small - {:tab-index "0" - :on-click on-create-click - :data-test "new-file" - :on-key-down (fn [event] - (when (kbd/enter? event) - (on-create-click event)))} - (tr "dashboard.new-file")] - - (when-not (:is-default project) - [:button.icon.pin-icon.tooltip.tooltip-bottom - {:tab-index "0" - :class (when (:is-pinned project) "active") - :on-click toggle-pin - :alt (tr "dashboard.pin-unpin") - :on-key-down (fn [event] - (when (kbd/enter? event) - (toggle-pin event)))} - (if (:is-pinned project) - i/pin-fill - i/pin)]) - - [:div.icon.tooltip.tooltip-bottom-left - {:tab-index "0" - :on-click on-menu-click - :alt (tr "dashboard.options") - :on-key-down (fn [event] - (when (kbd/enter? event) - (on-menu-click event)))} - i/actions]]]))) + [:div + {:class (stl/css :icon :tooltip :tooltip-bottom-left) + :tab-index "0" + :on-click on-menu-click + :alt (tr "dashboard.options") + :on-key-down (fn [event] + (when (kbd/enter? event) + (on-menu-click event)))} + i/actions]]])) (mf/defc files-section [{:keys [project team] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - files-map (mf/deref refs/dashboard-files) + (let [files-map (mf/deref refs/dashboard-files) project-id (:id project) [rowref limit] (hooks/use-dynamic-grid-item-width) @@ -233,28 +169,15 @@ (st/emit! (dd/fetch-files {:project-id project-id}) (dd/clear-selected-files))) - (if new-css-system - [:* - [:& header {:team team - :project project - :create-fn create-file}] - [:section {:class (stl/css :dashboard-container :no-bg) - :ref rowref} - [:& grid {:project project - :files files - :origin :files - :create-fn create-file - :limit limit}]]] - - ;; OLD - [:* - [:& header {:team team - :project project - :create-fn create-file}] - [:section.dashboard-container.no-bg {:ref rowref} - [:& grid {:project project - :files files - :origin :files - :create-fn create-file - :limit limit}]]]))) + [:* + [:& header {:team team + :project project + :create-fn create-file}] + [:section {:class (stl/css :dashboard-container :no-bg) + :ref rowref} + [:& grid {:project project + :files files + :origin :files + :create-fn create-file + :limit limit}]]])) diff --git a/frontend/src/app/main/ui/dashboard/fonts.cljs b/frontend/src/app/main/ui/dashboard/fonts.cljs index 30cfa5f392..8a5d5a8591 100644 --- a/frontend/src/app/main/ui/dashboard/fonts.cljs +++ b/frontend/src/app/main/ui/dashboard/fonts.cljs @@ -15,7 +15,6 @@ [app.main.store :as st] [app.main.ui.components.context-menu :refer [context-menu]] [app.main.ui.components.file-uploader :refer [file-uploader]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -40,17 +39,10 @@ (mf/defc header {::mf/wrap [mf/memo]} [{:keys [section team] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (use-set-page-title team section) - (if new-css-system - [:header {:class (stl/css :dashboard-header)} - [:div#dashboard-fonts-title {:class (stl/css :dashboard-title)} - [:h1 (tr "labels.fonts")]]] - - ;; OLD - [:header.dashboard-header - [:div.dashboard-title#dashboard-fonts-title - [:h1 (tr "labels.fonts")]]]))) + (use-set-page-title team section) + [:header {:class (stl/css :dashboard-header)} + [:div#dashboard-fonts-title {:class (stl/css :dashboard-title)} + [:h1 (tr "labels.fonts")]]]) (mf/defc font-variant-display-name [{:keys [variant]}] @@ -61,8 +53,7 @@ (mf/defc fonts-upload [{:keys [team installed-fonts] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - fonts (mf/use-state {}) + (let [fonts (mf/use-state {}) input-ref (mf/use-ref) uploading (mf/use-state #{}) @@ -121,151 +112,82 @@ handle-dismiss-all (mf/use-callback (mf/deps @fonts) #(on-dismiss-all (vals @fonts)))] - (if new-css-system - [:div {:class (stl/css :dashboard-fonts-upload)} - [:div {:class (stl/css :dashboard-fonts-hero)} - [:div {:class (stl/css :desc)} - [:h2 (tr "labels.upload-custom-fonts")] - [:& i18n/tr-html {:label "dashboard.fonts.hero-text1"}] + [:div {:class (stl/css :dashboard-fonts-upload)} + [:div {:class (stl/css :dashboard-fonts-hero)} + [:div {:class (stl/css :desc)} + [:h2 (tr "labels.upload-custom-fonts")] + [:& i18n/tr-html {:label "dashboard.fonts.hero-text1"}] - [:button - {:class (stl/css :btn-primary) - :on-click on-click - :tab-index "0"} - [:span (tr "labels.add-custom-font")] - [:& file-uploader {:input-id "font-upload" - :accept cm/str-font-types - :multi true - :ref input-ref - :on-selected on-selected}]] + [:button + {:class (stl/css :btn-primary) + :on-click on-click + :tab-index "0"} + [:span (tr "labels.add-custom-font")] + [:& file-uploader {:input-id "font-upload" + :accept cm/str-font-types + :multi true + :ref input-ref + :on-selected on-selected}]] - [:div {:class (stl/css :banner)} - [:div {:class (stl/css :icon)} i/msg-info] + [:div {:class (stl/css :banner)} + [:div {:class (stl/css :icon)} i/msg-info] + [:div {:class (stl/css :content)} + [:& i18n/tr-html {:tag-name "span" + :label "dashboard.fonts.hero-text2"}]]] + + (when problematic-fonts? + [:div {:class (stl/css :banner :warning)} + [:div {:class (stl/css :icon)} i/msg-warning] [:div {:class (stl/css :content)} [:& i18n/tr-html {:tag-name "span" - :label "dashboard.fonts.hero-text2"}]]] + :label "dashboard.fonts.warning-text"}]]])]] - (when problematic-fonts? - [:div {:class (stl/css :banner :warning)} - [:div {:class (stl/css :icon)} i/msg-warning] - [:div {:class (stl/css :content)} - [:& i18n/tr-html {:tag-name "span" - :label "dashboard.fonts.warning-text"}]]])]] + [:* + (when (some? (vals @fonts)) + [:div {:class (stl/css :font-item :table-row)} + [:span (tr "dashboard.fonts.fonts-added" (i18n/c (count (vals @fonts))))] + [:div {:class (stl/css :table-field :options)} + [:button {:class (stl/css :btn-primary) + :on-click handle-upload-all :data-test "upload-all"} + [:span (tr "dashboard.fonts.upload-all")]] + [:button {:class (stl/css :btn-secondary) + :on-click handle-dismiss-all :data-test "dismiss-all"} + [:span (tr "dashboard.fonts.dismiss-all")]]]]) + + (for [item (sort-by :font-family (vals @fonts))] + (let [uploading? (contains? @uploading (:id item))] + [:div {:class (stl/css :font-item :table-row) :key (:id item)} + [:div {:class (stl/css :table-field :family)} + [:input {:type "text" + :on-blur #(on-blur-name (:id item) %) + :default-value (:font-family item)}]] + [:div {:class (stl/css :table-field :variants)} + [:span {:class (stl/css :label)} + [:& font-variant-display-name {:variant item}]]] + + [:div {:class (stl/css :table-field :filenames)} + (for [item (:names item)] + [:span item])] - [:* - (when (some? (vals @fonts)) - [:div {:class (stl/css :font-item :table-row)} - [:span (tr "dashboard.fonts.fonts-added" (i18n/c (count (vals @fonts))))] [:div {:class (stl/css :table-field :options)} - [:button {:class (stl/css :btn-primary) - :on-click handle-upload-all :data-test "upload-all"} - [:span (tr "dashboard.fonts.upload-all")]] - [:button {:class (stl/css :btn-secondary) - :on-click handle-dismiss-all :data-test "dismiss-all"} - [:span (tr "dashboard.fonts.dismiss-all")]]]]) + (when (:height-warning? item) + [:span {:class (stl/css :icon :failure)} i/msg-warning]) - (for [item (sort-by :font-family (vals @fonts))] - (let [uploading? (contains? @uploading (:id item))] - [:div {:class (stl/css :font-item :table-row) :key (:id item)} - [:div {:class (stl/css :table-field :family)} - [:input {:type "text" - :on-blur #(on-blur-name (:id item) %) - :default-value (:font-family item)}]] - [:div {:class (stl/css :table-field :variants)} - [:span {:class (stl/css :label)} - [:& font-variant-display-name {:variant item}]]] - - [:div {:class (stl/css :table-field :filenames)} - (for [item (:names item)] - [:span item])] - - [:div {:class (stl/css :table-field :options)} - (when (:height-warning? item) - [:span {:class (stl/css :icon :failure)} i/msg-warning]) - - [:button - {:on-click #(on-upload item) - :class (stl/css-case :btn-primary true - :upload-button true - :disabled uploading?) - :disabled uploading?} - (if uploading? - (tr "labels.uploading") - (tr "labels.upload"))] - [:span {:class (stl/css :icon :close) - :on-click #(on-delete item)} i/close]]]))]] - ;; OLD - [:div.dashboard-fonts-upload - [:div.dashboard-fonts-hero - [:div.desc - [:h2 (tr "labels.upload-custom-fonts")] - [:& i18n/tr-html {:label "dashboard.fonts.hero-text1"}] - - [:div.banner - [:div.icon i/msg-info] - [:div.content - [:& i18n/tr-html {:tag-name "span" - :label "dashboard.fonts.hero-text2"}]]] - - (when problematic-fonts? - [:div.banner.warning - [:div.icon i/msg-warning] - [:div.content - [:& i18n/tr-html {:tag-name "span" - :label "dashboard.fonts.warning-text"}]]])] - - [:button.btn-primary - {:on-click on-click - :tab-index "0"} - [:span (tr "labels.add-custom-font")] - [:& file-uploader {:input-id "font-upload" - :accept cm/str-font-types - :multi true - :ref input-ref - :on-selected on-selected}]]] - - [:* - (when (some? (vals @fonts)) - [:div.font-item.table-row - [:span (tr "dashboard.fonts.fonts-added" (i18n/c (count (vals @fonts))))] - [:div.table-field.options - [:div.btn-primary - {:on-click #(on-upload-all (vals @fonts)) :data-test "upload-all"} - [:span (tr "dashboard.fonts.upload-all")]] - [:div.btn-secondary - {:on-click #(on-dismiss-all (vals @fonts)) :data-test "dismiss-all"} - [:span (tr "dashboard.fonts.dismiss-all")]]]]) - - (for [item (sort-by :font-family (vals @fonts))] - (let [uploading? (contains? @uploading (:id item))] - [:div.font-item.table-row {:key (:id item)} - [:div.table-field.family - [:input {:type "text" - :on-blur #(on-blur-name (:id item) %) - :default-value (:font-family item)}]] - [:div.table-field.variants - [:span.label - [:& font-variant-display-name {:variant item}]]] - [:div.table-field.filenames - (for [item (:names item)] - [:span item])] - - [:div.table-field.options - (when (:height-warning? item) - [:span.icon.failure i/msg-warning]) - [:button.btn-primary.upload-button - {:on-click #(on-upload item) - :class (dom/classnames :disabled uploading?) - :disabled uploading?} - (if uploading? - (tr "labels.uploading") - (tr "labels.upload"))] - [:span.icon.close {:on-click #(on-delete item)} i/close]]]))]]))) + [:button + {:on-click #(on-upload item) + :class (stl/css-case :btn-primary true + :upload-button true + :disabled uploading?) + :disabled uploading?} + (if uploading? + (tr "labels.uploading") + (tr "labels.upload"))] + [:span {:class (stl/css :icon :close) + :on-click #(on-delete item)} i/close]]]))]])) (mf/defc installed-font [{:keys [font-id variants] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - font (first variants) + (let [font (first variants) variants (sort-by (fn [item] [(:font-weight item) @@ -324,94 +246,52 @@ :on-accept (fn [_props] (delete-variant-fn id))})))] - (if new-css-system - [:div {:class (stl/css :font-item :table-row)} - [:div {:class (stl/css :table-field :family)} - (if @edit? - [:input {:type "text" - :default-value @state - :on-key-down on-key-down - :on-change on-change}] - [:span (:font-family font)])] + [:div {:class (stl/css :font-item :table-row)} + [:div {:class (stl/css :table-field :family)} + (if @edit? + [:input {:type "text" + :default-value @state + :on-key-down on-key-down + :on-change on-change}] + [:span (:font-family font)])] - [:div {:class (stl/css :table-field :variants)} - (for [item variants] - [:div {:class (stl/css :variant)} - [:span {:class (stl/css :label)} - [:& font-variant-display-name {:variant item}]] - [:span - {:class (stl/css :icon :close) - :on-click #(on-delete-variant (:id item))} - i/plus]])] + [:div {:class (stl/css :table-field :variants)} + (for [item variants] + [:div {:class (stl/css :variant)} + [:span {:class (stl/css :label)} + [:& font-variant-display-name {:variant item}]] + [:span + {:class (stl/css :icon :close) + :on-click #(on-delete-variant (:id item))} + i/plus]])] - (if @edit? - [:div {:class (stl/css :table-field :options)} - [:button - {:disabled (str/blank? @state) - :on-click on-save - :class (stl/css-case :btn-primary true - :btn-disabled (str/blank? @state))} - (tr "labels.save")] - [:button {:class (stl/css :icon :close) - :on-click on-cancel} i/close]] + (if @edit? + [:div {:class (stl/css :table-field :options)} + [:button + {:disabled (str/blank? @state) + :on-click on-save + :class (stl/css-case :btn-primary true + :btn-disabled (str/blank? @state))} + (tr "labels.save")] + [:button {:class (stl/css :icon :close) + :on-click on-cancel} i/close]] - [:div {:class (stl/css :table-field :options)} - [:span {:class (stl/css :icon) - :on-click #(reset! open-menu? true)} i/actions] - [:& context-menu - {:on-close #(reset! open-menu? false) - :show @open-menu? - :fixed? false - :top -15 - :left -115 - :options [[(tr "labels.edit") #(reset! edit? true) nil "font-edit"] - [(tr "labels.delete") on-delete nil "font-delete"]]}]])] - ;;OLD - [:div.font-item.table-row - [:div.table-field.family - (if @edit? - [:input {:type "text" - :default-value @state - :on-key-down on-key-down - :on-change on-change}] - [:span (:font-family font)])] - - [:div.table-field.variants - (for [item variants] - [:div.variant - [:span.label - [:& font-variant-display-name {:variant item}]] - [:span.icon.close - {:on-click #(on-delete-variant (:id item))} - i/plus]])] - - [:div] - - (if @edit? - [:div.table-field.options - [:button.btn-primary - {:disabled (str/blank? @state) - :on-click on-save - :class (dom/classnames :btn-disabled (str/blank? @state))} - (tr "labels.save")] - [:span.icon.close {:on-click on-cancel} i/close]] - - [:div.table-field.options - [:span.icon {:on-click #(reset! open-menu? true)} i/actions] - [:& context-menu - {:on-close #(reset! open-menu? false) - :show @open-menu? - :fixed? false - :top -15 - :left -115 - :options [[(tr "labels.edit") #(reset! edit? true) nil "font-edit"] - [(tr "labels.delete") on-delete nil "font-delete"]]}]])]))) + [:div {:class (stl/css :table-field :options)} + [:span {:class (stl/css :icon) + :on-click #(reset! open-menu? true)} i/actions] + [:& context-menu + {:on-close #(reset! open-menu? false) + :show @open-menu? + :fixed? false + :top -15 + :left -115 + :options [[(tr "labels.edit") #(reset! edit? true) nil "font-edit"] + [(tr "labels.delete") on-delete nil "font-delete"]]}]])])) (mf/defc installed-fonts [{:keys [fonts] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - sterm (mf/use-state "") + (let [sterm (mf/use-state "") matches? #(str/includes? (str/lower (:font-family %)) @sterm) @@ -422,98 +302,49 @@ (let [val (dom/get-target-val event)] (reset! sterm (str/lower val)))))] - (if new-css-system - [:div {:class (stl/css :dashboard-installed-fonts)} - [:h3 (tr "labels.installed-fonts")] - [:div {:class (stl/css :installed-fonts-header)} - [:div {:class (stl/css :table-field :family)} (tr "labels.font-family")] - [:div {:class (stl/css :table-field :variants)} (tr "labels.font-variants")] - [:div {:class (stl/css :table-field :search-input)} - [:input {:placeholder (tr "labels.search-font") - :default-value "" - :on-change on-change}]]] + [:div {:class (stl/css :dashboard-installed-fonts)} + [:h3 (tr "labels.installed-fonts")] + [:div {:class (stl/css :installed-fonts-header)} + [:div {:class (stl/css :table-field :family)} (tr "labels.font-family")] + [:div {:class (stl/css :table-field :variants)} (tr "labels.font-variants")] + [:div {:class (stl/css :table-field :search-input)} + [:input {:placeholder (tr "labels.search-font") + :default-value "" + :on-change on-change}]]] - (cond - (seq fonts) - (for [[font-id variants] (->> (vals fonts) - (filter matches?) - (group-by :font-id))] - [:& installed-font {:key (str font-id) - :font-id font-id - :variants variants}]) + (cond + (seq fonts) + (for [[font-id variants] (->> (vals fonts) + (filter matches?) + (group-by :font-id))] + [:& installed-font {:key (str font-id) + :font-id font-id + :variants variants}]) - (nil? fonts) - [:div {:class (stl/css :fonts-placeholder)} - [:div {:class (stl/css :icon)} i/loader] - [:div {:class (stl/css :label)} (tr "dashboard.loading-fonts")]] + (nil? fonts) + [:div {:class (stl/css :fonts-placeholder)} + [:div {:class (stl/css :icon)} i/loader] + [:div {:class (stl/css :label)} (tr "dashboard.loading-fonts")]] - :else - [:div {:class (stl/css :fonts-placeholder)} - [:div {:class (stl/css :icon)} i/text] - [:div {:class (stl/css :label)} (tr "dashboard.fonts.empty-placeholder")]])] - - ;; OLD - [:div.dashboard-installed-fonts - [:h3 (tr "labels.installed-fonts")] - [:div.installed-fonts-header - [:div.table-field.family (tr "labels.font-family")] - [:div.table-field.variants (tr "labels.font-variants")] - [:div] - [:div.table-field.search-input - [:input {:placeholder (tr "labels.search-font") - :default-value "" - :on-change on-change}]]] - - (cond - (seq fonts) - (for [[font-id variants] (->> (vals fonts) - (filter matches?) - (group-by :font-id))] - [:& installed-font {:key (str font-id) - :font-id font-id - :variants variants}]) - - (nil? fonts) - [:div.fonts-placeholder - [:div.icon i/loader] - [:div.label (tr "dashboard.loading-fonts")]] - - :else - [:div.fonts-placeholder - [:div.icon i/text] - [:div.label (tr "dashboard.fonts.empty-placeholder")]])]))) + :else + [:div {:class (stl/css :fonts-placeholder)} + [:div {:class (stl/css :icon)} i/text] + [:div {:class (stl/css :label)} (tr "dashboard.fonts.empty-placeholder")]])])) (mf/defc fonts-page [{:keys [team] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - fonts (mf/deref refs/dashboard-fonts)] - (if new-css-system - [:* - [:& header {:team team :section :fonts}] - [:section {:class (stl/css :dashboard-container :dashboard-fonts)} - [:& fonts-upload {:team team :installed-fonts fonts}] - [:& installed-fonts {:team team :fonts fonts}]]] - - ;; OLD - [:* - [:& header {:team team :section :fonts}] - [:section.dashboard-container.dashboard-fonts - [:& fonts-upload {:team team :installed-fonts fonts}] - [:& installed-fonts {:team team :fonts fonts}]]]))) + (let [fonts (mf/deref refs/dashboard-fonts)] + [:* + [:& header {:team team :section :fonts}] + [:section {:class (stl/css :dashboard-container :dashboard-fonts)} + [:& fonts-upload {:team team :installed-fonts fonts}] + [:& installed-fonts {:team team :fonts fonts}]]])) (mf/defc font-providers-page [{:keys [team] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:* - [:& header {:team team :section :providers}] - [:section {:class (stl/css :dashboard-container)} - [:span "font providers"]]] - - ;; OLD - [:* - [:& header {:team team :section :providers}] - [:section.dashboard-container - [:span "font providers"]]]))) + [:* + [:& header {:team team :section :providers}] + [:section {:class (stl/css :dashboard-container)} + [:span "font providers"]]]) diff --git a/frontend/src/app/main/ui/dashboard/grid.cljs b/frontend/src/app/main/ui/dashboard/grid.cljs index 57108c88b2..67dac1f45f 100644 --- a/frontend/src/app/main/ui/dashboard/grid.cljs +++ b/frontend/src/app/main/ui/dashboard/grid.cljs @@ -20,7 +20,6 @@ [app.main.repo :as rp] [app.main.store :as st] [app.main.ui.components.color-bullet :as bc] - [app.main.ui.context :as ctx] [app.main.ui.dashboard.file-menu :refer [file-menu]] [app.main.ui.dashboard.import :refer [use-import-file]] [app.main.ui.dashboard.inline-edition :refer [inline-edition]] @@ -68,8 +67,7 @@ (mf/defc grid-item-thumbnail {::mf/wrap-props false} [{:keys [file-id revn thumbnail-uri background-color]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - container (mf/use-ref) + (let [container (mf/use-ref) visible? (h/use-visible container :once? true)] (mf/with-effect [file-id revn visible? thumbnail-uri] @@ -83,28 +81,16 @@ :revn revn :message (ex-message cause))))))) - (if new-css-system - [:div {:class (stl/css :grid-item-th) - :style {:background-color background-color} - :ref container} - (when visible? - (if thumbnail-uri - [:img {:class (stl/css :grid-item-thumbnail-image) - :src thumbnail-uri - :loading "lazy" - :decoding "async"}] - i/loader-pencil))] - - ;; OLD - [:div.grid-item-th - {:style {:background-color background-color} - :ref container} - (when visible? - (if thumbnail-uri - [:img.grid-item-thumbnail-image {:src thumbnail-uri - :loading "lazy" - :decoding "async"}] - i/loader-pencil))]))) + [:div {:class (stl/css :grid-item-th) + :style {:background-color background-color} + :ref container} + (when visible? + (if thumbnail-uri + [:img {:class (stl/css :grid-item-thumbnail-image) + :src thumbnail-uri + :loading "lazy" + :decoding "async"}] + i/loader-pencil))])) ;; --- Grid Item Library @@ -112,228 +98,125 @@ {::mf/wrap [mf/memo]} [{:keys [file] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (mf/with-effect [file] - (when file - (let [font-ids (map :font-id (get-in file [:library-summary :typographies :sample] []))] - (run! fonts/ensure-loaded! font-ids)))) + (mf/with-effect [file] + (when file + (let [font-ids (map :font-id (get-in file [:library-summary :typographies :sample] []))] + (run! fonts/ensure-loaded! font-ids)))) - (if new-css-system - [:div {:class (stl/css :grid-item-th :library)} - (if (nil? file) - i/loader-pencil - (let [summary (:library-summary file) - components (:components summary) - colors (:colors summary) - typographies (:typographies summary)] - [:* - (when (and (zero? (:count components)) (zero? (:count colors)) (zero? (:count typographies))) - [:* - [:div {:class (stl/css :asset-section)} - [:div {:class (stl/css :asset-title)} - [:span (tr "workspace.assets.components")] - [:span {:class (stl/css :num-assets)} (str "\u00A0(") 0 ")"]]] ;; Unicode 00A0 is non-breaking space - [:div {:class (stl/css :asset-section)} - [:div {:class (stl/css :asset-title)} - [:span (tr "workspace.assets.colors")] - [:span {:class (stl/css :num-assets)} (str "\u00A0(") 0 ")"]]] ;; Unicode 00A0 is non-breaking space - [:div {:class (stl/css :asset-section)} - [:div {:class (stl/css :asset-title)} - [:span (tr "workspace.assets.typography")] - [:span {:class (stl/css :num-assets)} (str "\u00A0(") 0 ")"]]]]) ;; Unicode 00A0 is non-breaking space + [:div {:class (stl/css :grid-item-th :library)} + (if (nil? file) + i/loader-pencil + (let [summary (:library-summary file) + components (:components summary) + colors (:colors summary) + typographies (:typographies summary)] + [:* + (when (and (zero? (:count components)) (zero? (:count colors)) (zero? (:count typographies))) + [:* + [:div {:class (stl/css :asset-section)} + [:div {:class (stl/css :asset-title)} + [:span (tr "workspace.assets.components")] + [:span {:class (stl/css :num-assets)} (str "\u00A0(") 0 ")"]]] ;; Unicode 00A0 is non-breaking space + [:div {:class (stl/css :asset-section)} + [:div {:class (stl/css :asset-title)} + [:span (tr "workspace.assets.colors")] + [:span {:class (stl/css :num-assets)} (str "\u00A0(") 0 ")"]]] ;; Unicode 00A0 is non-breaking space + [:div {:class (stl/css :asset-section)} + [:div {:class (stl/css :asset-title)} + [:span (tr "workspace.assets.typography")] + [:span {:class (stl/css :num-assets)} (str "\u00A0(") 0 ")"]]]]) ;; Unicode 00A0 is non-breaking space - (when (pos? (:count components)) - [:div {:class (stl/css :asset-section)} - [:div {:class (stl/css :asset-title)} - [:span (tr "workspace.assets.components")] - [:span {:class (stl/css :num-assets)} (str "\u00A0(") (:count components) ")"]] ;; Unicode 00A0 is non-breaking space - [:div {:class (stl/css :asset-list)} - (for [component (:sample components)] - (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 - [:div {:class (stl/css :name-block)} - [:span {:class (stl/css :item-name) - :title (:name component)} - (:name component)]]])) - (when (> (:count components) (count (:sample components))) - [:div {:class (stl/css :asset-list-item)} - [:div {:class (stl/css :name-block)} - [:span {:class (stl/css :item-name)} "(...)"]]])]]) + (when (pos? (:count components)) + [:div {:class (stl/css :asset-section)} + [:div {:class (stl/css :asset-title)} + [:span (tr "workspace.assets.components")] + [:span {:class (stl/css :num-assets)} (str "\u00A0(") (:count components) ")"]] ;; Unicode 00A0 is non-breaking space + [:div {:class (stl/css :asset-list)} + (for [component (:sample components)] + (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 + [:div {:class (stl/css :name-block)} + [:span {:class (stl/css :item-name) + :title (:name component)} + (:name component)]]])) + (when (> (:count components) (count (:sample components))) + [:div {:class (stl/css :asset-list-item)} + [:div {:class (stl/css :name-block)} + [:span {:class (stl/css :item-name)} "(...)"]]])]]) - (when (pos? (:count colors)) - [:div {:class (stl/css :asset-section)} - [:div {:class (stl/css :asset-title)} - [:span (tr "workspace.assets.colors")] - [:span {:class (stl/css :num-assets)} (str "\u00A0(") (:count colors) ")"]] ;; Unicode 00A0 is non-breaking space - [:div {:class (stl/css :asset-list)} - (for [color (:sample colors)] - (let [default-name (cond - (:gradient color) (uc/gradient-type->string (get-in color [:gradient :type])) - (:color color) (:color color) - :else (:value color))] - [:div {:class (stl/css :asset-list-item) - :key (str "assets-color-" (:id color))} - [:& bc/color-bullet {:color {:color (:color color) - :opacity (:opacity color)}}] - [:div {:class (stl/css :name-block)} - [:span {:class (stl/css :color-name)} (:name color)] - (when-not (= (:name color) default-name) - [:span {:class (stl/css :color-value)} (:color color)])]])) + (when (pos? (:count colors)) + [:div {:class (stl/css :asset-section)} + [:div {:class (stl/css :asset-title)} + [:span (tr "workspace.assets.colors")] + [:span {:class (stl/css :num-assets)} (str "\u00A0(") (:count colors) ")"]] ;; Unicode 00A0 is non-breaking space + [:div {:class (stl/css :asset-list)} + (for [color (:sample colors)] + (let [default-name (cond + (:gradient color) (uc/gradient-type->string (get-in color [:gradient :type])) + (:color color) (:color color) + :else (:value color))] + [:div {:class (stl/css :asset-list-item) + :key (str "assets-color-" (:id color))} + [:& bc/color-bullet {:color {:color (:color color) + :opacity (:opacity color)}}] + [:div {:class (stl/css :name-block)} + [:span {:class (stl/css :color-name)} (:name color)] + (when-not (= (:name color) default-name) + [:span {:class (stl/css :color-value)} (:color color)])]])) - (when (> (:count colors) (count (:sample colors))) - [:div {:class (stl/css :asset-list-item)} - [:div {:class (stl/css :name-block)} - [:span {:class (stl/css :item-name)} "(...)"]]])]]) + (when (> (:count colors) (count (:sample colors))) + [:div {:class (stl/css :asset-list-item)} + [:div {:class (stl/css :name-block)} + [:span {:class (stl/css :item-name)} "(...)"]]])]]) - (when (pos? (:count typographies)) - [:div {:class (stl/css :asset-section)} - [:div {:class (stl/css :asset-title)} - [:span (tr "workspace.assets.typography")] - [:span {:class (stl/css :num-assets)} (str "\u00A0(") (:count typographies) ")"]] ;; Unicode 00A0 is non-breaking space - [:div {:class (stl/css :asset-list)} - (for [typography (:sample typographies)] - [:div {:class (stl/css :asset-list-item) - :key (str "assets-typography-" (:id typography))} - [:div {:class (stl/css :typography-sample) - :style {:font-family (:font-family typography) - :font-weight (:font-weight typography) - :font-style (:font-style typography)}} - (tr "workspace.assets.typography.sample")] - [:div {:class (stl/css :name-block)} - [:span {:class (stl/css :item-name) - :title (:name typography)} - (:name typography)]]]) + (when (pos? (:count typographies)) + [:div {:class (stl/css :asset-section)} + [:div {:class (stl/css :asset-title)} + [:span (tr "workspace.assets.typography")] + [:span {:class (stl/css :num-assets)} (str "\u00A0(") (:count typographies) ")"]] ;; Unicode 00A0 is non-breaking space + [:div {:class (stl/css :asset-list)} + (for [typography (:sample typographies)] + [:div {:class (stl/css :asset-list-item) + :key (str "assets-typography-" (:id typography))} + [:div {:class (stl/css :typography-sample) + :style {:font-family (:font-family typography) + :font-weight (:font-weight typography) + :font-style (:font-style typography)}} + (tr "workspace.assets.typography.sample")] + [:div {:class (stl/css :name-block)} + [:span {:class (stl/css :item-name) + :title (:name typography)} + (:name typography)]]]) - (when (> (:count typographies) (count (:sample typographies))) - [:div {:class (stl/css :asset-list-item)} - [:div {:class (stl/css :name-block)} - [:span {:class (stl/css :item-name)} "(...)"]]])]])]))] - - ;; OLD - [:div.grid-item-th.library - (if (nil? file) - i/loader-pencil - (let [summary (:library-summary file) - components (:components summary) - colors (:colors summary) - typographies (:typographies summary)] - [:* - - (when (and (zero? (:count components)) (zero? (:count colors)) (zero? (:count typographies))) - [:* - [:div.asset-section - [:div.asset-title - [:span (tr "workspace.assets.components")] - [:span.num-assets (str "\u00A0(") 0 ")"]]] ;; Unicode 00A0 is non-breaking space - [:div.asset-section - [:div.asset-title - [:span (tr "workspace.assets.colors")] - [:span.num-assets (str "\u00A0(") 0 ")"]]] ;; Unicode 00A0 is non-breaking space - [:div.asset-section - [:div.asset-title - [:span (tr "workspace.assets.typography")] - [:span.num-assets (str "\u00A0(") 0 ")"]]]]) ;; Unicode 00A0 is non-breaking space - - - (when (pos? (:count components)) - [:div.asset-section - [:div.asset-title - [:span (tr "workspace.assets.components")] - [:span.num-assets (str "\u00A0(") (:count components) ")"]] ;; Unicode 00A0 is non-breaking space - [:div.asset-list - (for [component (:sample components)] - (let [root-id (or (:main-instance-id component) (:id component))] ;; Check for components-v2 in library - [:div.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 - [:div.name-block - [:span.item-name {:title (:name component)} - (:name component)]]])) - (when (> (:count components) (count (:sample components))) - [:div.asset-list-item - [:div.name-block - [:span.item-name "(...)"]]])]]) - - (when (pos? (:count colors)) - [:div.asset-section - [:div.asset-title - [:span (tr "workspace.assets.colors")] - [:span.num-assets (str "\u00A0(") (:count colors) ")"]] ;; Unicode 00A0 is non-breaking space - [:div.asset-list - (for [color (:sample colors)] - (let [default-name (cond - (:gradient color) (uc/gradient-type->string (get-in color [:gradient :type])) - (:color color) (:color color) - :else (:value color))] - [:div.asset-list-item {:key (str "assets-color-" (:id color))} - [:& bc/color-bullet {:color {:color (:color color) - :opacity (:opacity color)}}] - [:div.name-block - [:span.color-name (:name color)] - (when-not (= (:name color) default-name) - [:span.color-value (:color color)])]])) - (when (> (:count colors) (count (:sample colors))) - [:div.asset-list-item - [:div.name-block - [:span.item-name "(...)"]]])]]) - - (when (pos? (:count typographies)) - [:div.asset-section - [:div.asset-title - [:span (tr "workspace.assets.typography")] - [:span.num-assets (str "\u00A0(") (:count typographies) ")"]] ;; Unicode 00A0 is non-breaking space - [:div.asset-list - (for [typography (:sample typographies)] - [:div.asset-list-item {:key (str "assets-typography-" (:id typography))} - [:div.typography-sample - {:style {:font-family (:font-family typography) - :font-weight (:font-weight typography) - :font-style (:font-style typography)}} - (tr "workspace.assets.typography.sample")] - [:div.name-block - [:span.item-name {:title (:name typography)} - (:name typography)]]]) - (when (> (:count typographies) (count (:sample typographies))) - [:div.asset-list-item - [:div.name-block - [:span.item-name "(...)"]]])]])]))]))) + (when (> (:count typographies) (count (:sample typographies))) + [:div {:class (stl/css :asset-list-item)} + [:div {:class (stl/css :name-block)} + [:span {:class (stl/css :item-name)} "(...)"]]])]])]))]) ;; --- Grid Item (mf/defc grid-item-metadata [{:keys [modified-at]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - locale (mf/deref i18n/locale) + (let [locale (mf/deref i18n/locale) time (dt/timeago modified-at {:locale locale})] - (if new-css-system - [:span {:class (stl/css :date)} time] - - ;; OLD - [:span.date time]))) + [:span {:class (stl/css :date)} time])) (defn create-counter-element - [_element file-count new-css-system] - (if new-css-system - (let [counter-el (dom/create-element "div")] - (dom/set-property! counter-el "class" (stl/css :drag-counter)) - (dom/set-text! counter-el (str file-count)) - counter-el) - - (let [counter-el (dom/create-element "div")] - (dom/set-property! counter-el "class" "drag-counter") - (dom/set-text! counter-el (str file-count)) - counter-el))) + [_element file-count] + (let [counter-el (dom/create-element "div")] + (dom/set-property! counter-el "class" (stl/css :drag-counter)) + (dom/set-text! counter-el (str file-count)) + counter-el)) (mf/defc grid-item {:wrap [mf/memo]} [{:keys [file navigate? origin library-view?] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - file-id (:id file) + (let [file-id (:id file) local (mf/use-state {:menu-open false :menu-pos nil :edition false}) @@ -380,8 +263,7 @@ item-el (if select-current? 1 - (count selected-files)) - new-css-system)] + (count selected-files)))] (when select-current? (st/emit! (dd/clear-selected-files)) (st/emit! (dd/toggle-file-select file))) @@ -453,132 +335,67 @@ (when (and (not selected?) (:menu-open @local)) (swap! local assoc :menu-open false))) - (if new-css-system - [:li - {:class (stl/css-case :grid-item true :project-th true :library library-view?)} - [:button - {:class (stl/css-case :selected selected? :library library-view?) - :ref node-ref - :title (:name file) - :draggable true - :on-click on-select - :on-key-down handle-key-down - :on-double-click on-navigate - :on-drag-start on-drag-start - :on-context-menu on-menu-click} + [:li + {:class (stl/css-case :grid-item true :project-th true :library library-view?)} + [:button + {:class (stl/css-case :selected selected? :library library-view?) + :ref node-ref + :title (:name file) + :draggable true + :on-click on-select + :on-key-down handle-key-down + :on-double-click on-navigate + :on-drag-start on-drag-start + :on-context-menu on-menu-click} - [:div {:class (stl/css :overlay)}] + [:div {:class (stl/css :overlay)}] - (if library-view? - [:& grid-item-library {:file file}] - [:& grid-item-thumbnail - {:file-id (:id file) - :revn (:revn file) - :thumbnail-uri (:thumbnail-uri file) - :background-color (dm/get-in file [:data :options :background])}]) + (if library-view? + [:& grid-item-library {:file file}] + [:& grid-item-thumbnail + {:file-id (:id file) + :revn (:revn file) + :thumbnail-uri (:thumbnail-uri file) + :background-color (dm/get-in file [:data :options :background])}]) - (when (and (:is-shared file) (not library-view?)) - [:div {:class (stl/css :item-badge)} i/library]) + (when (and (:is-shared file) (not library-view?)) + [:div {:class (stl/css :item-badge)} i/library]) - [:div {:class (stl/css :info-wrapper)} - [:div {:class (stl/css :item-info)} - (if (:edition @local) - [:& inline-edition {:content (:name file) - :on-end edit}] - [:h3 (:name file)]) - [:& grid-item-metadata {:modified-at (:modified-at file)}]] + [:div {:class (stl/css :info-wrapper)} + [:div {:class (stl/css :item-info)} + (if (:edition @local) + [:& inline-edition {:content (:name file) + :on-end edit}] + [:h3 (:name file)]) + [:& grid-item-metadata {:modified-at (:modified-at file)}]] - [:div {:class (stl/css-case :project-th-actions true :force-display (:menu-open @local))} - [:div - {:class (stl/css :project-th-icon :menu) - :tab-index "0" - :ref menu-ref - :id (str file-id "-action-menu") - :on-click on-menu-click - :on-key-down (fn [event] - (when (kbd/enter? event) - (dom/stop-propagation event) - (on-menu-click event)))} - i/actions - (when selected? - [:& file-menu {:files (vals selected-files) - :show? (:menu-open @local) - :left (+ 24 (:x (:menu-pos @local))) - :top (:y (:menu-pos @local)) - :navigate? navigate? - :on-edit on-edit - :on-menu-close on-menu-close - :origin origin - :dashboard-local dashboard-local - :parent-id (str file-id "-action-menu")}])]]]]] - - ;; OLD - [:li.grid-item.project-th {:class (dom/classnames :library library-view?)} - [:button - {:tab-index "0" - :class (dom/classnames :selected selected? - :library library-view?) - :ref node-ref - :draggable true - :on-click on-select - :on-key-down (fn [event] - (dom/stop-propagation event) - (when (kbd/enter? event) - (on-navigate event)) - (when (kbd/shift? event) - (when (or (kbd/down-arrow? event) (kbd/left-arrow? event) (kbd/up-arrow? event) (kbd/right-arrow? event)) - (on-select event)) ;; TODO Fix this - )) - :on-double-click on-navigate - :on-drag-start on-drag-start - :on-context-menu on-menu-click} - - [:div.overlay] - (if library-view? - [:& grid-item-library {:file file}] - [:& grid-item-thumbnail - {:file-id (:id file) - :revn (:revn file) - :thumbnail-uri (:thumbnail-uri file) - :background-color (dm/get-in file [:data :options :background])}]) - - (when (and (:is-shared file) (not library-view?)) - [:div.item-badge i/library]) - [:div.info-wrapper - [:div.item-info - (if (:edition @local) - [:& inline-edition {:content (:name file) - :on-end edit}] - [:h3 (:name file)]) - [:& grid-item-metadata {:modified-at (:modified-at file)}]] - [:div.project-th-actions {:class (dom/classnames - :force-display (:menu-open @local))} - [:div.project-th-icon.menu - {:tab-index "0" - :ref menu-ref - :id (str file-id "-action-menu") - :on-click on-menu-click - :on-key-down (fn [event] - (when (kbd/enter? event) - (dom/stop-propagation event) - (on-menu-click event)))} - i/actions - (when selected? - [:& file-menu {:files (vals selected-files) - :show? (:menu-open @local) - :left (+ 24 (:x (:menu-pos @local))) - :top (:y (:menu-pos @local)) - :navigate? navigate? - :on-edit on-edit - :on-menu-close on-menu-close - :origin origin - :dashboard-local dashboard-local - :parent-id (str file-id "-action-menu")}])]]]]]))) + [:div {:class (stl/css-case :project-th-actions true :force-display (:menu-open @local))} + [:div + {:class (stl/css :project-th-icon :menu) + :tab-index "0" + :ref menu-ref + :id (str file-id "-action-menu") + :on-click on-menu-click + :on-key-down (fn [event] + (when (kbd/enter? event) + (dom/stop-propagation event) + (on-menu-click event)))} + i/actions + (when selected? + [:& file-menu {:files (vals selected-files) + :show? (:menu-open @local) + :left (+ 24 (:x (:menu-pos @local))) + :top (:y (:menu-pos @local)) + :navigate? navigate? + :on-edit on-edit + :on-menu-close on-menu-close + :origin origin + :dashboard-local dashboard-local + :parent-id (str file-id "-action-menu")}])]]]]])) (mf/defc grid [{:keys [files project origin limit library-view? create-fn] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - dragging? (mf/use-state false) + (let [dragging? (mf/use-state false) project-id (:id project) node-ref (mf/use-var nil) @@ -621,109 +438,58 @@ (reset! dragging? false) (import-files (.-files (.-dataTransfer e))))))] - (if new-css-system - [:div - {:class (stl/css :dashboard-grid) - :on-drag-enter on-drag-enter - :on-drag-over on-drag-over - :on-drag-leave on-drag-leave - :on-drop on-drop - :ref node-ref} + [:div {:class (stl/css :dashboard-grid) + :on-drag-enter on-drag-enter + :on-drag-over on-drag-over + :on-drag-leave on-drag-leave + :on-drop on-drop + :ref node-ref} - (cond - (nil? files) - [:& loading-placeholder] - - (seq files) - (for [slice (partition-all limit files)] - [:ul {:class (stl/css :grid-row)} - (when @dragging? - [:li {:class (stl/css :grid-item)}]) - (for [item slice] - [:& grid-item - {:file item - :key (:id item) - :navigate? true - :origin origin - :library-view? library-view?}])]) - - :else - [:& empty-placeholder - {:limit limit - :create-fn create-fn - :origin origin}])] - - ;; OLD - [:div.dashboard-grid - {:on-drag-enter on-drag-enter - :on-drag-over on-drag-over - :on-drag-leave on-drag-leave - :on-drop on-drop - :ref node-ref} - (cond - (nil? files) - [:& loading-placeholder] - - (seq files) - [:ul.grid-row - {:style {:grid-template-columns (str "repeat(" limit ", 1fr)")}} + (cond + (nil? files) + [:& loading-placeholder] + (seq files) + (for [slice (partition-all limit files)] + [:ul {:class (stl/css :grid-row)} (when @dragging? - [:li.grid-item]) - - (for [item files] + [:li {:class (stl/css :grid-item)}]) + (for [item slice] [:& grid-item {:file item :key (:id item) :navigate? true :origin origin - :library-view? library-view?}])] + :library-view? library-view?}])]) - :else - [:& empty-placeholder - {:limit limit - :create-fn create-fn - :origin origin}])]))) + :else + [:& empty-placeholder + {:limit limit + :create-fn create-fn + :origin origin}])])) (mf/defc line-grid-row [{:keys [files selected-files dragging? limit] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - elements limit + (let [elements limit limit (if dragging? (dec limit) limit)] - (if new-css-system - [:ul - {:class (stl/css :grid-row :no-wrap) - :style {:grid-template-columns (dm/str "repeat(" elements ", 1fr)")}} + [:ul + {:class (stl/css :grid-row :no-wrap) + :style {:grid-template-columns (dm/str "repeat(" elements ", 1fr)")}} - (when dragging? - [:li {:class (stl/css :grid-item :dragged)}]) + (when dragging? + [:li {:class (stl/css :grid-item :dragged)}]) - (for [item (take limit files)] - [:& grid-item - {:id (:id item) - :file item - :selected-files selected-files - :key (:id item) - :navigate? false}])] - - ;; OLD - [:ul.grid-row.no-wrap - {:style {:grid-template-columns (dm/str "repeat(" elements ", 1fr)")}} - - (when dragging? - [:li.grid-item.dragged]) - (for [item (take limit files)] - [:& grid-item - {:id (:id item) - :file item - :selected-files selected-files - :key (:id item) - :navigate? false}])]))) + (for [item (take limit files)] + [:& grid-item + {:id (:id item) + :file item + :selected-files selected-files + :key (:id item) + :navigate? false}])])) (mf/defc line-grid [{:keys [project team files limit create-fn] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - dragging? (mf/use-state false) + (let [dragging? (mf/use-state false) project-id (:id project) team-id (:id team) @@ -799,47 +565,24 @@ (reset! dragging? false) (import-files (.-files (.-dataTransfer e)))))))] - (if new-css-system - [:div {:class (stl/css :dashboard-grid) - :on-drag-enter on-drag-enter - :on-drag-over on-drag-over - :on-drag-leave on-drag-leave - :on-drop on-drop} - (cond - (nil? files) - [:& loading-placeholder] + [:div {:class (stl/css :dashboard-grid) + :on-drag-enter on-drag-enter + :on-drag-over on-drag-over + :on-drag-leave on-drag-leave + :on-drop on-drop} + (cond + (nil? files) + [:& loading-placeholder] - (seq files) - [:& line-grid-row {:files files - :team-id team-id - :selected-files selected-files - :dragging? @dragging? - :limit limit}] + (seq files) + [:& line-grid-row {:files files + :team-id team-id + :selected-files selected-files + :dragging? @dragging? + :limit limit}] - :else - [:& empty-placeholder - {:dragging? @dragging? - :limit limit - :create-fn create-fn}])] - - ;; OLD - [:div.dashboard-grid {:on-drag-enter on-drag-enter - :on-drag-over on-drag-over - :on-drag-leave on-drag-leave - :on-drop on-drop} - (cond - (nil? files) - [:& loading-placeholder] - - (seq files) - [:& line-grid-row {:files files - :team-id team-id - :selected-files selected-files - :dragging? @dragging? - :limit limit}] - - :else - [:& empty-placeholder - {:dragging? @dragging? - :limit limit - :create-fn create-fn}])]))) + :else + [:& empty-placeholder + {:dragging? @dragging? + :limit limit + :create-fn create-fn}])])) diff --git a/frontend/src/app/main/ui/dashboard/import.cljs b/frontend/src/app/main/ui/dashboard/import.cljs index d0d4b4d2c5..2ac73e4214 100644 --- a/frontend/src/app/main/ui/dashboard/import.cljs +++ b/frontend/src/app/main/ui/dashboard/import.cljs @@ -18,7 +18,6 @@ [app.main.features :as features] [app.main.store :as st] [app.main.ui.components.file-uploader :refer [file-uploader]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.worker :as uw] [app.util.dom :as dom] @@ -152,8 +151,7 @@ (mf/defc import-entry [{:keys [state file editing? can-be-deleted?]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - loading? (or (= :analyzing (:status file)) + (let [loading? (or (= :analyzing (:status file)) (= :importing (:status file))) analyze-error? (= :analyze-error (:status file)) import-finish? (= :import-finish (:status file)) @@ -191,122 +189,66 @@ (fn [] (swap! state update :files remove-file (:file-id file))))] - (if new-css-system - [:div {:class (stl/css-case :file-entry true - :loading loading? - :success (and import-finish? (not import-warn?) (not import-error?)) - :warning (and import-finish? import-warn? (not import-error?)) - :error (or import-error? analyze-error?) - :editable (and ready? (not editing?)))} + [:div {:class (stl/css-case :file-entry true + :loading loading? + :success (and import-finish? (not import-warn?) (not import-error?)) + :warning (and import-finish? import-warn? (not import-error?)) + :error (or import-error? analyze-error?) + :editable (and ready? (not editing?)))} - [:div {:class (stl/css :file-name)} - [:div {:class (stl/css-case :file-icon true - :icon-fill ready?)} - (cond loading? i/loader-pencil - ready? i/logo-icon - import-warn? i/msg-warning - import-error? i/close-refactor - import-finish? i/tick-refactor - analyze-error? i/close-refactor)] + [:div {:class (stl/css :file-name)} + [:div {:class (stl/css-case :file-icon true + :icon-fill ready?)} + (cond loading? i/loader-pencil + ready? i/logo-icon + import-warn? i/msg-warning + import-error? i/close-refactor + import-finish? i/tick-refactor + analyze-error? i/close-refactor)] - (if editing? - [:div {:class (stl/css :file-name-edit)} - [:input {:type "text" - :auto-focus true - :default-value (:name file) - :on-key-press handle-edit-key-press - :on-blur handle-edit-blur}]] + (if editing? + [:div {:class (stl/css :file-name-edit)} + [:input {:type "text" + :auto-focus true + :default-value (:name file) + :on-key-press handle-edit-key-press + :on-blur handle-edit-blur}]] - [:div {:class (stl/css :file-name-label)} - (:name file) - (when is-shared? i/library-refactor)]) + [:div {:class (stl/css :file-name-label)} + (:name file) + (when is-shared? i/library-refactor)]) - [:div {:class (stl/css :edit-entry-buttons)} - (when (= "application/zip" (:type file)) - [:button {:on-click handle-edit-entry} i/curve-refactor]) - (when can-be-deleted? - [:button {:on-click handle-remove-entry} i/delete-refactor])]] - (cond - analyze-error? - [:div {:class (stl/css :error-message)} - (tr "dashboard.import.analyze-error")] + [:div {:class (stl/css :edit-entry-buttons)} + (when (= "application/zip" (:type file)) + [:button {:on-click handle-edit-entry} i/curve-refactor]) + (when can-be-deleted? + [:button {:on-click handle-remove-entry} i/delete-refactor])]] + (cond + analyze-error? + [:div {:class (stl/css :error-message)} + (tr "dashboard.import.analyze-error")] - import-error? - [:div {:class (stl/css :error-message)} - (tr "dashboard.import.import-error")] + import-error? + [:div {:class (stl/css :error-message)} + (tr "dashboard.import.import-error")] - (and (not import-finish?) (some? progress)) - [:div {:class (stl/css :progress-message)} (parse-progress-message progress)]) + (and (not import-finish?) (some? progress)) + [:div {:class (stl/css :progress-message)} (parse-progress-message progress)]) - [:div {:class (stl/css :linked-libraries)} - (for [library-id (:libraries file)] - (let [library-data (->> @state :files (d/seek #(= library-id (:file-id %)))) - error? (or (:deleted? library-data) (:import-error library-data))] - (when (some? library-data) - [:div {:class (stl/css-case :linked-library-tag true - :error error?)} - i/detach-refactor (:name library-data)])))]] - - - [:div.file-entry - {:class (dom/classnames - :loading loading? - :success (and import-finish? (not import-warn?) (not import-error?)) - :warning (and import-finish? import-warn? (not import-error?)) - :error (or import-error? analyze-error?) - :editable (and ready? (not editing?)))} - - [:div.file-name - [:div.file-icon - (cond loading? i/loader-pencil - ready? i/logo-icon - import-warn? i/msg-warning - import-error? i/close - import-finish? i/tick - analyze-error? i/close)] - - (if editing? - [:div.file-name-edit - [:input {:type "text" - :auto-focus true - :default-value (:name file) - :on-key-press handle-edit-key-press - :on-blur handle-edit-blur}]] - - [:div.file-name-label (:name file) (when is-shared? i/library)]) - - [:div.edit-entry-buttons - (when (= "application/zip" (:type file)) - [:button {:on-click handle-edit-entry} i/pencil]) - (when can-be-deleted? - [:button {:on-click handle-remove-entry} i/trash])]] - - (cond - analyze-error? - [:div.error-message - (tr "dashboard.import.analyze-error")] - - import-error? - [:div.error-message - (tr "dashboard.import.import-error")] - - (and (not import-finish?) (some? progress)) - [:div.progress-message (parse-progress-message progress)]) - - [:div.linked-libraries - (for [library-id (:libraries file)] - (let [library-data (->> @state :files (d/seek #(= library-id (:file-id %)))) - error? (or (:deleted? library-data) (:import-error library-data))] - (when (some? library-data) - [:div.linked-library-tag {:class (when error? "error")} - (if error? i/unchain i/chain) (:name library-data)])))]]))) + [:div {:class (stl/css :linked-libraries)} + (for [library-id (:libraries file)] + (let [library-data (->> @state :files (d/seek #(= library-id (:file-id %)))) + error? (or (:deleted? library-data) (:import-error library-data))] + (when (some? library-data) + [:div {:class (stl/css-case :linked-library-tag true + :error error?)} + i/detach-refactor (:name library-data)])))]])) (mf/defc import-dialog {::mf/register modal/components ::mf/register-as :import} [{:keys [project-id files template on-finish-import]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - state (mf/use-state + (let [state (mf/use-state {:status :analyzing :editing nil :importing-templates 0 @@ -425,122 +367,60 @@ #(doseq [file files] (wapi/revoke-uri (:uri file))))) - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-container)} - [:div {:class (stl/css :modal-header)} - [:h2 {:class (stl/css :modal-title)} (tr "dashboard.import")] + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} (tr "dashboard.import")] - [:button {:class (stl/css :modal-close-btn) - :on-click handle-cancel} i/close-refactor]] + [:button {:class (stl/css :modal-close-btn) + :on-click handle-cancel} i/close-refactor]] - [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :modal-content)} - (when (and (= :importing (:status @state)) (not pending-import?)) - (if (> warning-files 0) - [:div {:class (stl/css-case :feedback-banner true - :warning true)} - [:div {:class (stl/css :icon)} i/msg-warning-refactor] - [:div {:class (stl/css :message)} (tr "dashboard.import.import-warning" warning-files success-files)]] + (when (and (= :importing (:status @state)) (not pending-import?)) + (if (> warning-files 0) + [:div {:class (stl/css-case :feedback-banner true + :warning true)} + [:div {:class (stl/css :icon)} i/msg-warning-refactor] + [:div {:class (stl/css :message)} (tr "dashboard.import.import-warning" warning-files success-files)]] - [:div {:class (stl/css :feedback-banner)} - [:div {:class (stl/css :icon)} i/msg-success-refactor] - [:div {:class (stl/css :message)} (tr "dashboard.import.import-message" (i18n/c (if (some? template) 1 success-files)))]])) + [:div {:class (stl/css :feedback-banner)} + [:div {:class (stl/css :icon)} i/msg-success-refactor] + [:div {:class (stl/css :message)} (tr "dashboard.import.import-message" (i18n/c (if (some? template) 1 success-files)))]])) - (for [file files] - (let [editing? (and (some? (:file-id file)) - (= (:file-id file) (:editing @state)))] - [:& import-entry {:state state - :key (dm/str (:uri file)) - :file file - :editing? editing? - :can-be-deleted? (> (count files) 1)}])) - - (when (some? template) + (for [file files] + (let [editing? (and (some? (:file-id file)) + (= (:file-id file) (:editing @state)))] [:& import-entry {:state state - :file (assoc template :status (if (= 1 (:importing-templates @state)) :importing :ready)) - :editing? false - :can-be-deleted? false}])] + :key (dm/str (:uri file)) + :file file + :editing? editing? + :can-be-deleted? (> (count files) 1)}])) - [:div {:class (stl/css :modal-footer)} - [:div {:class (stl/css :action-buttons)} - (when (= :analyzing (:status @state)) - [:input {:class (stl/css :cancel-button) - :type "button" - :value (tr "labels.cancel") - :on-click handle-cancel}]) + (when (some? template) + [:& import-entry {:state state + :file (assoc template :status (if (= 1 (:importing-templates @state)) :importing :ready)) + :editing? false + :can-be-deleted? false}])] - (when (= :analyzing (:status @state)) - [:input {:class (stl/css :accept-btn) - :type "button" - :value (tr "labels.continue") - :disabled (or pending-analysis? (not valid-files?)) - :on-click handle-continue}]) + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + (when (= :analyzing (:status @state)) + [:input {:class (stl/css :cancel-button) + :type "button" + :value (tr "labels.cancel") + :on-click handle-cancel}]) - (when (= :importing (:status @state)) - [:input {:class (stl/css :accept-btn) - :type "button" - :value (tr "labels.accept") - :disabled (or pending-import? (not valid-files?)) - :on-click handle-accept}])]]]] + (when (= :analyzing (:status @state)) + [:input {:class (stl/css :accept-btn) + :type "button" + :value (tr "labels.continue") + :disabled (or pending-analysis? (not valid-files?)) + :on-click handle-continue}]) - - - [:div.modal-overlay - [:div.modal-container.import-dialog - [:div.modal-header - [:div.modal-header-title - [:h2 (tr "dashboard.import")]] - - [:div.modal-close-button - {:on-click handle-cancel} i/close]] - - [:div.modal-content - (when (and (= :importing (:status @state)) (not pending-import?)) - (if (> warning-files 0) - [:div.feedback-banner.warning - [:div.icon i/msg-warning] - [:div.message (tr "dashboard.import.import-warning" warning-files success-files)]] - - [:div.feedback-banner - [:div.icon i/checkbox-checked] - [:div.message (tr "dashboard.import.import-message" (i18n/c (if (some? template) 1 success-files)))]])) - - (for [file files] - (let [editing? (and (some? (:file-id file)) - (= (:file-id file) (:editing @state)))] - [:& import-entry {:state state - :key (dm/str (:uri file)) - :file file - :editing? editing? - :can-be-deleted? (> (count files) 1)}])) - - (when (some? template) - [:& import-entry {:state state - :file (assoc template :status (if (= 1 (:importing-templates @state)) :importing :ready)) - :editing? false - :can-be-deleted? false}])] - - [:div.modal-footer - [:div.action-buttons - (when (= :analyzing (:status @state)) - [:input.cancel-button - {:type "button" - :value (tr "labels.cancel") - :on-click handle-cancel}]) - - (when (= :analyzing (:status @state)) - [:input.accept-button - {:class "primary" - :type "button" - :value (tr "labels.continue") - :disabled (or pending-analysis? (not valid-files?)) - :on-click handle-continue}]) - - (when (= :importing (:status @state)) - [:input.accept-button - {:class "primary" - :type "button" - :value (tr "labels.accept") - :disabled (or pending-import? (not valid-files?)) - :on-click handle-accept}])]]]]))) + (when (= :importing (:status @state)) + [:input {:class (stl/css :accept-btn) + :type "button" + :value (tr "labels.accept") + :disabled (or pending-import? (not valid-files?)) + :on-click handle-accept}])]]]])) diff --git a/frontend/src/app/main/ui/dashboard/import.scss b/frontend/src/app/main/ui/dashboard/import.scss index 3ed738462e..4221530e0d 100644 --- a/frontend/src/app/main/ui/dashboard/import.scss +++ b/frontend/src/app/main/ui/dashboard/import.scss @@ -8,69 +8,72 @@ .modal-overlay { @extend .modal-overlay-base; +} - .modal-container { - @extend .modal-container-base; - border: $s-1 solid var(--modal-border-color); - .modal-header { - margin-bottom: $s-24; - .modal-title { - @include tabTitleTipography; - color: var(--modal-title-foreground-color); - } - .modal-close-btn { - @extend .modal-close-btn-base; - } +.modal-container { + @extend .modal-container-base; + border: $s-1 solid var(--modal-border-color); +} + +.modal-header { + margin-bottom: $s-24; +} + +.modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); +} + +.modal-close-btn { + @extend .modal-close-btn-base; +} + +.modal-content { + @include titleTipography; + margin-bottom: $s-24; +} + +.feedback-banner { + @include flexRow; + height: $s-32; + width: 100%; + margin-bottom: $s-24; + border-radius: $br-8; + background-color: var(--alert-background-color-ok); + color: var(--alert-foreground-color-ok); + + .icon { + @include flexCenter; + height: $s-24; + width: $s-24; + svg { + @extend .button-icon; + stroke: var(--alert-foreground-color-ok); } - - .modal-content { - @include titleTipography; - margin-bottom: $s-24; - .feedback-banner { - @include flexRow; - height: $s-32; - width: 100%; - margin-bottom: $s-24; - border-radius: $br-8; - background-color: var(--alert-background-color-ok); - color: var(--alert-foreground-color-ok); - - .icon { - @include flexCenter; - height: $s-24; - width: $s-24; - svg { - @extend .button-icon; - stroke: var(--alert-foreground-color-ok); - } - } - .message { - @include titleTipography; - } - &.warning { - background-color: var(--alert-background-color-warning); - color: var(--alert-foreground-color-warning); - .icon svg { - stroke: var(--alert-foreground-color-warning); - } - } - } + } + .message { + @include titleTipography; + } + &.warning { + background-color: var(--alert-background-color-warning); + color: var(--alert-foreground-color-warning); + .icon svg { + stroke: var(--alert-foreground-color-warning); } + } +} - .modal-footer { - .action-buttons { - @extend .modal-action-btns; - .cancel-button { - @extend .modal-cancel-btn; - } - .accept-btn { - @extend .modal-accept-btn; - &.danger { - @extend .modal-danger-btn; - } - } - } - } +.action-buttons { + @extend .modal-action-btns; +} + +.cancel-button { + @extend .modal-cancel-btn; +} +.accept-btn { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; } } @@ -122,6 +125,7 @@ .error-message, .progress-message { height: $s-32; + color: var(--modal-text-foreground-color); } .linked-libraries { diff --git a/frontend/src/app/main/ui/dashboard/inline_edition.cljs b/frontend/src/app/main/ui/dashboard/inline_edition.cljs index 00d269066d..499f522143 100644 --- a/frontend/src/app/main/ui/dashboard/inline_edition.cljs +++ b/frontend/src/app/main/ui/dashboard/inline_edition.cljs @@ -7,7 +7,6 @@ (ns app.main.ui.dashboard.inline-edition (:require-macros [app.main.style :as stl]) (:require - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.keyboard :as kbd] @@ -15,8 +14,7 @@ (mf/defc inline-edition [{:keys [content on-end] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - name (mf/use-state content) + (let [name (mf/use-state content) input-ref (mf/use-ref) on-input @@ -62,25 +60,14 @@ (dom/focus! node) (dom/select-text! node)))) - (if new-css-system - [:div {:class (stl/css :edit-wrapper)} - [:input {:class (stl/css :element-title) - :value @name - :ref input-ref - :on-click on-click - :on-change on-input - :on-key-down on-keyup - :on-blur on-blur}] - [:span {:class (stl/css :close) - :on-click on-cancel} i/close]] - - ;; OLD - [:div.edit-wrapper - [:input.element-title {:value @name - :ref input-ref - :on-click on-click - :on-change on-input - :on-key-down on-keyup - :on-blur on-blur}] - [:span.close {:on-click on-cancel} i/close]]))) + [:div {:class (stl/css :edit-wrapper)} + [:input {:class (stl/css :element-title) + :value @name + :ref input-ref + :on-click on-click + :on-change on-input + :on-key-down on-keyup + :on-blur on-blur}] + [:span {:class (stl/css :close) + :on-click on-cancel} i/close-refactor]])) diff --git a/frontend/src/app/main/ui/dashboard/inline_edition.scss b/frontend/src/app/main/ui/dashboard/inline_edition.scss index 8abb170cb0..0c84951f9c 100644 --- a/frontend/src/app/main/ui/dashboard/inline_edition.scss +++ b/frontend/src/app/main/ui/dashboard/inline_edition.scss @@ -12,42 +12,42 @@ padding-right: $s-24; position: relative; margin-right: $s-24; +} - input.element-title { - background-color: $db-primary; - border-radius: $br-8; - color: $df-primary; - font-size: $fs-16; - height: $s-32; - margin: 0; - border: none; - padding: $s-6; - width: 100%; +input.element-title { + background-color: $db-primary; + border-radius: $br-8; + color: $df-primary; + font-size: $fs-16; + height: $s-32; + margin: 0; + border: none; + padding: $s-6; + width: 100%; - &:focus-visible { - border: $s-1 solid $da-primary; - outline: none; - } + &:focus-visible { + border: $s-1 solid $da-primary; + outline: none; } +} - .close { - cursor: pointer; - position: absolute; +.close { + cursor: pointer; + position: absolute; - top: $s-1; - right: calc(-1 * $s-8); + top: $s-1; + right: calc(-1 * $s-8); + svg { + fill: $df-secondary; + height: $s-16; + transform: rotate(45deg) translateY(7px); + width: $s-16; + margin: 0; + } + &:hover { svg { - fill: $df-secondary; - height: $s-16; - transform: rotate(45deg) translateY(7px); - width: $s-16; - margin: 0; - } - &:hover { - svg { - fill: var(--warning-color); - } + fill: var(--warning-color); } } } diff --git a/frontend/src/app/main/ui/dashboard/libraries.cljs b/frontend/src/app/main/ui/dashboard/libraries.cljs index 2b80c8439b..dd543c154e 100644 --- a/frontend/src/app/main/ui/dashboard/libraries.cljs +++ b/frontend/src/app/main/ui/dashboard/libraries.cljs @@ -12,7 +12,6 @@ [app.main.features :as features] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.dashboard.grid :refer [grid]] [app.main.ui.hooks :as hooks] [app.util.dom :as dom] @@ -21,8 +20,7 @@ (mf/defc libraries-page [{:keys [team] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - files-map (mf/deref refs/dashboard-shared-files) + (let [files-map (mf/deref refs/dashboard-shared-files) projects (mf/deref refs/dashboard-projects) default-project (->> projects vals (d/seek :is-default)) @@ -49,27 +47,14 @@ (st/emit! (dd/fetch-shared-files (:id team)) (dd/clear-selected-files))) - (if new-css-system - [:* - [:header {:class (stl/css :dashboard-header)} - [:div#dashboard-libraries-title {:class (stl/css :dashboard-title)} - [:h1 (tr "dashboard.libraries-title")]]] - [:section {:class (stl/css :dashboard-container :no-bg :dashboard-shared) :ref rowref} - [:& grid {:files files - :project default-project - :origin :libraries - :limit limit - :library-view? components-v2}]]] - - ;; OLD - [:* - [:header.dashboard-header {:ref rowref} - [:div.dashboard-title#dashboard-libraries-title - [:h1 (tr "dashboard.libraries-title")]]] - [:section.dashboard-container.no-bg.dashboard-shared - [:& grid {:files files - :project default-project - :origin :libraries - :limit limit - :library-view? components-v2}]]]))) + [:* + [:header {:class (stl/css :dashboard-header)} + [:div#dashboard-libraries-title {:class (stl/css :dashboard-title)} + [:h1 (tr "dashboard.libraries-title")]]] + [:section {:class (stl/css :dashboard-container :no-bg :dashboard-shared) :ref rowref} + [:& grid {:files files + :project default-project + :origin :libraries + :limit limit + :library-view? components-v2}]]])) diff --git a/frontend/src/app/main/ui/dashboard/placeholder.cljs b/frontend/src/app/main/ui/dashboard/placeholder.cljs index 37fd949f35..ce8c5d81f3 100644 --- a/frontend/src/app/main/ui/dashboard/placeholder.cljs +++ b/frontend/src/app/main/ui/dashboard/placeholder.cljs @@ -7,65 +7,39 @@ (ns app.main.ui.dashboard.placeholder (:require-macros [app.main.style :as stl]) (:require - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) (mf/defc empty-placeholder [{:keys [dragging? limit origin create-fn] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - on-click + (let [on-click (mf/use-fn (mf/deps create-fn) (fn [_] (create-fn "dashboard:empty-folder-placeholder")))] - (if new-css-system - (cond - (true? dragging?) - [:ul - {:class (stl/css :grid-row :no-wrap) - :style {:grid-template-columns (str "repeat(" limit ", 1fr)")}} - [:li {:class (stl/css :grid-item :grid-empty-placeholder :dragged)}]] + (cond + (true? dragging?) + [:ul + {:class (stl/css :grid-row :no-wrap) + :style {:grid-template-columns (str "repeat(" limit ", 1fr)")}} + [:li {:class (stl/css :grid-item :grid-empty-placeholder :dragged)}]] - (= :libraries origin) - [:div {:class (stl/css :grid-empty-placeholder :libs) - :data-test "empty-placeholder"} - [:div {:class (stl/css :text)} - [:& i18n/tr-html {:label "dashboard.empty-placeholder-drafts"}]]] + (= :libraries origin) + [:div {:class (stl/css :grid-empty-placeholder :libs) + :data-test "empty-placeholder"} + [:div {:class (stl/css :text)} + [:& i18n/tr-html {:label "dashboard.empty-placeholder-drafts"}]]] - :else - [:div - {:class (stl/css :grid-empty-placeholder)} - [:button {:class (stl/css :create-new) - :on-click on-click} - i/add-refactor]]) - - ;; OLD - (cond - (true? dragging?) - [:ul.grid-row.no-wrap - {:style {:grid-template-columns (str "repeat(" limit ", 1fr)")}} - [:li.grid-item]] - - (= :libraries origin) - [:div.grid-empty-placeholder.libs {:data-test "empty-placeholder"} - [:div.text - [:& i18n/tr-html {:label "dashboard.empty-placeholder-drafts"}]]] - - :else - [:div.grid-empty-placeholder - {:style {:grid-template-columns (str "repeat(" limit ", 1fr)")}} - [:button.create-new {:on-click on-click} (tr "dashboard.new-file")]])))) + :else + [:div + {:class (stl/css :grid-empty-placeholder)} + [:button {:class (stl/css :create-new) + :on-click on-click} + i/add-refactor]]))) (mf/defc loading-placeholder [] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:div {:class (stl/css :grid-empty-placeholder :loader)} - [:div {:class (stl/css :icon)} i/loader] - [:div {:class (stl/css :text)} (tr "dashboard.loading-files")]] - - [:div.grid-empty-placeholder.loader - [:div.icon i/loader] - [:div.text (tr "dashboard.loading-files")]]))) + [:div {:class (stl/css :grid-empty-placeholder :loader)} + [:div {:class (stl/css :icon)} i/loader] + [:div {:class (stl/css :text)} (tr "dashboard.loading-files")]]) diff --git a/frontend/src/app/main/ui/dashboard/projects.cljs b/frontend/src/app/main/ui/dashboard/projects.cljs index 572c72f778..035385bb49 100644 --- a/frontend/src/app/main/ui/dashboard/projects.cljs +++ b/frontend/src/app/main/ui/dashboard/projects.cljs @@ -18,7 +18,6 @@ [app.main.errors :as errors] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.dashboard.grid :refer [line-grid]] [app.main.ui.dashboard.inline-edition :refer [inline-edition]] [app.main.ui.dashboard.project-menu :refer [project-menu]] @@ -37,32 +36,20 @@ (mf/defc header {::mf/wrap [mf/memo]} [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - on-click (mf/use-fn #(st/emit! (dd/create-project)))] - (if new-css-system - [:header {:class (stl/css :dashboard-header)} - [:div#dashboard-projects-title {:class (stl/css :dashboard-title)} - [:h1 (tr "dashboard.projects-title")]] - [:button - {:class (stl/css :btn-secondary :btn-small) - :on-click on-click - :data-test "new-project-button"} - (tr "dashboard.new-project")]] - - ;; OLD - [:header.dashboard-header - [:div.dashboard-title#dashboard-projects-title - [:h1 (tr "dashboard.projects-title")]] - [:button.btn-secondary.btn-small - {:on-click on-click - :data-test "new-project-button"} - (tr "dashboard.new-project")]]))) + (let [on-click (mf/use-fn #(st/emit! (dd/create-project)))] + [:header {:class (stl/css :dashboard-header)} + [:div#dashboard-projects-title {:class (stl/css :dashboard-title)} + [:h1 (tr "dashboard.projects-title")]] + [:button + {:class (stl/css :btn-secondary :btn-small) + :on-click on-click + :data-test "new-project-button"} + (tr "dashboard.new-project")]])) (mf/defc team-hero {::mf/wrap [mf/memo]} [{:keys [team close-fn] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - on-nav-members-click (mf/use-fn #(st/emit! (dd/go-to-team-members))) + (let [on-nav-members-click (mf/use-fn #(st/emit! (dd/go-to-team-members))) on-invite-click (mf/use-fn @@ -78,52 +65,33 @@ (dom/prevent-default event) (close-fn)))] - (if new-css-system - [:div {:class (stl/css :team-hero)} - [:div {:class (stl/css :img-wrapper)} - [:img {:src "images/deco-team-banner.png" - :border "0" - :role "presentation"}]] - [:div {:class (stl/css :text)} - [:div {:class (stl/css :title)} (tr "dasboard.team-hero.title")] - [:div {:class (stl/css :info)} - [:span (tr "dasboard.team-hero.text")] - [:a {:on-click on-nav-members-click} (tr "dasboard.team-hero.management")]] - [:button - {:class (stl/css :btn-primary :invite) - :on-click on-invite-click} - (tr "onboarding.choice.team-up.invite-members")]] + [:div {:class (stl/css :team-hero)} + [:div {:class (stl/css :img-wrapper)} + [:img {:src "images/deco-team-banner.png" + :border "0" + :role "presentation"}]] + [:div {:class (stl/css :text)} + [:div {:class (stl/css :title)} (tr "dasboard.team-hero.title")] + [:div {:class (stl/css :info)} + [:span (tr "dasboard.team-hero.text")] + [:a {:on-click on-nav-members-click} (tr "dasboard.team-hero.management")]] + [:button + {:class (stl/css :btn-primary :invite) + :on-click on-invite-click} + (tr "onboarding.choice.team-up.invite-members")]] - [:button - {:class (stl/css :close) - :on-click on-close-click - :aria-label (tr "labels.close")} - [:span i/close]]] - - ;; OLD - [:div.team-hero - [:img {:src "images/deco-team-banner.png" :border "0" - :role "presentation"}] - [:div.text - [:div.title (tr "dasboard.team-hero.title")] - [:div.info - [:span (tr "dasboard.team-hero.text")] - [:a {:on-click on-nav-members-click} (tr "dasboard.team-hero.management")]]] - [:button.btn-primary.invite - {:on-click on-invite-click} - (tr "onboarding.choice.team-up.invite-members")] - [:button.close - {:on-click on-close-click - :aria-label (tr "labels.close")} - [:span i/close]]]))) + [:button + {:class (stl/css :close) + :on-click on-close-click + :aria-label (tr "labels.close")} + [:span i/close]]])) (def builtin-templates (l/derived :builtin-templates st/state)) (mf/defc tutorial-project [{:keys [close-tutorial default-project-id] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - state (mf/use-state {:status :waiting + (let [state (mf/use-state {:status :waiting :file nil}) templates (mf/deref builtin-templates) @@ -155,88 +123,51 @@ (swap! state #(assoc % :status :importing)) (st/emit! (with-meta (dd/clone-template (with-meta params mdata)) {::ev/origin "get-started-hero-block"})))))] - (if new-css-system - [:article {:class (stl/css :tutorial)} - [:div {:class (stl/css :thumbnail)}] - [:div {:class (stl/css :text)} - [:h2 {:class (stl/css :title)} (tr "dasboard.tutorial-hero.title")] - [:p {:class (stl/css :info)} (tr "dasboard.tutorial-hero.info")] - [:button {:class (stl/css :btn-primary :action) - :on-click download-tutorial} - (case (:status @state) - :waiting (tr "dasboard.tutorial-hero.start") - :importing [:span.loader i/loader-pencil] - :success "")]] + [:article {:class (stl/css :tutorial)} + [:div {:class (stl/css :thumbnail)}] + [:div {:class (stl/css :text)} + [:h2 {:class (stl/css :title)} (tr "dasboard.tutorial-hero.title")] + [:p {:class (stl/css :info)} (tr "dasboard.tutorial-hero.info")] + [:button {:class (stl/css :btn-primary :action) + :on-click download-tutorial} + (case (:status @state) + :waiting (tr "dasboard.tutorial-hero.start") + :importing [:span.loader i/loader-pencil] + :success "")]] - [:button - {:class (stl/css :close) - :on-click close-tutorial - :aria-label (tr "labels.close")} - [:span {:class (stl/css :icon)} i/close]]] - - ;; OLD - [:article.tutorial - [:div.thumbnail] - [:div.text - [:h2.title (tr "dasboard.tutorial-hero.title")] - [:p.info (tr "dasboard.tutorial-hero.info")] - [:button.btn-primary.action {:on-click download-tutorial} - (case (:status @state) - :waiting (tr "dasboard.tutorial-hero.start") - :importing [:span.loader i/loader-pencil] - :success "")]] - - [:button.close - {:on-click close-tutorial - :aria-label (tr "labels.close")} - [:span.icon i/close]]]))) + [:button + {:class (stl/css :close) + :on-click close-tutorial + :aria-label (tr "labels.close")} + [:span {:class (stl/css :icon)} i/close]]])) (mf/defc interface-walkthrough {::mf/wrap [mf/memo]} [{:keys [close-walkthrough] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - handle-walkthrough-link + (let [handle-walkthrough-link (fn [] (st/emit! (ptk/event ::ev/event {::ev/name "show-walkthrough" ::ev/origin "get-started-hero-block" :section "dashboard"})))] - (if new-css-system - [:article {:class (stl/css :walkthrough)} - [:div {:class (stl/css :thumbnail)}] - [:div {:class (stl/css :text)} - [:h2 {:class (stl/css :title)} (tr "dasboard.walkthrough-hero.title")] - [:p {:class (stl/css :info)} (tr "dasboard.walkthrough-hero.info")] - [:a {:class (stl/css :btn-primary :action) - :href " https://design.penpot.app/walkthrough" - :target "_blank" - :on-click handle-walkthrough-link} - (tr "dasboard.walkthrough-hero.start")]] - [:button - {:class (stl/css :close) - :on-click close-walkthrough - :aria-label (tr "labels.close")} - [:span {:class (stl/css :icon)} i/close]]] - - ;; OLD - [:article.walkthrough - [:div.thumbnail] - [:div.text - [:h2.title (tr "dasboard.walkthrough-hero.title")] - [:p.info (tr "dasboard.walkthrough-hero.info")] - [:a.btn-primary.action - {:href " https://design.penpot.app/walkthrough" - :target "_blank" - :on-click handle-walkthrough-link} - (tr "dasboard.walkthrough-hero.start")]] - [:button.close - {:on-click close-walkthrough - :aria-label (tr "labels.close")} - [:span.icon i/close]]]))) + [:article {:class (stl/css :walkthrough)} + [:div {:class (stl/css :thumbnail)}] + [:div {:class (stl/css :text)} + [:h2 {:class (stl/css :title)} (tr "dasboard.walkthrough-hero.title")] + [:p {:class (stl/css :info)} (tr "dasboard.walkthrough-hero.info")] + [:a {:class (stl/css :btn-primary :action) + :href " https://design.penpot.app/walkthrough" + :target "_blank" + :on-click handle-walkthrough-link} + (tr "dasboard.walkthrough-hero.start")]] + [:button + {:class (stl/css :close) + :on-click close-walkthrough + :aria-label (tr "labels.close")} + [:span {:class (stl/css :icon)} i/close]]])) (mf/defc project-item [{:keys [project first? team files] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - locale (mf/deref i18n/locale) + (let [locale (mf/deref i18n/locale) file-count (or (:count project) 0) project-id (:id project) team-id (:id team) @@ -344,166 +275,85 @@ (dom/stop-propagation event) (on-menu-click event))))] - (if new-css-system - [:article {:class (stl/css-case :dashboard-project-row true :first first?)} - [:header {:class (stl/css :project)} - [:div {:class (stl/css :project-name-wrapper)} - (if (:edition? @local) - [:& inline-edition {:content (:name project) - :on-end on-edit}] - [:h2 {:on-click on-nav - :on-context-menu on-menu-click} - (if (:is-default project) - (tr "labels.drafts") - (:name project))]) + [:article {:class (stl/css-case :dashboard-project-row true :first first?)} + [:header {:class (stl/css :project)} + [:div {:class (stl/css :project-name-wrapper)} + (if (:edition? @local) + [:& inline-edition {:content (:name project) + :on-end on-edit}] + [:h2 {:on-click on-nav + :on-context-menu on-menu-click} + (if (:is-default project) + (tr "labels.drafts") + (:name project))]) - [:& project-menu - {:project project - :show? (:menu-open @local) - :left (+ 24 (:x (:menu-pos @local))) - :top (:y (:menu-pos @local)) - :on-edit on-edit-open - :on-menu-close on-menu-close - :on-import on-import}] - - [:span {:class (stl/css :info)} (str (tr "labels.num-of-files" (i18n/c file-count)))] - - (let [time (-> (:modified-at project) - (dt/timeago {:locale locale}))] - [:span {:class (stl/css :recent-files-row-title-info)} (str ", " time)]) - - [:div {:class (stl/css :project-actions)} - (when-not (:is-default project) - [:button - {:class (stl/css-case :pin-icon true - :tooltip true - :tooltip-bottom true - :active (:is-pinned project)) - :on-click toggle-pin - :alt (tr "dashboard.pin-unpin") - :aria-label (tr "dashboard.pin-unpin") - :tab-index "0"} - (if (:is-pinned project) - i/pin-fill - i/pin)]) - - [:button - {:class (stl/css :btn-secondary :btn-small :tooltip :tooltip-bottom) - :on-click on-create-click - :alt (tr "dashboard.new-file") - :aria-label (tr "dashboard.new-file") - :data-test "project-new-file" - :on-key-down handle-create-click} - i/close] - - [:button - {:class (stl/css :btn-secondary :btn-small :tooltip :tooltip-bottom) - :on-click on-menu-click - :alt (tr "dashboard.options") - :aria-label (tr "dashboard.options") - :data-test "project-options" - :on-key-down handle-menu-click} - i/actions]]]] - - [:div {:class (stl/css :grid-container) :ref rowref} - [:& line-grid - {:project project - :team team - :files files - :create-fn create-file - :limit limit}]] - - (when (and (> limit 0) - (> file-count limit)) - [:button - {:class (stl/css :show-more) - :on-click on-nav - :tab-index "0" - :on-key-down (fn [event] - (when (kbd/enter? event) - (on-nav)))} - [:div {:class (stl/css :placeholder-label)} (tr "dashboard.show-all-files")] - [:div {:class (stl/css :placeholder-icon)} i/arrow-down]])] - - ;; OLD - [:article.dashboard-project-row - {:class (when first? "first")} - [:header.project {:ref rowref} - [:div.project-name-wrapper - (if (:edition? @local) - [:& inline-edition {:content (:name project) - :on-end on-edit}] - [:h2 {:on-click on-nav - :on-context-menu on-menu-click} - (if (:is-default project) - (tr "labels.drafts") - (:name project))]) - - [:& project-menu - {:project project - :show? (:menu-open @local) - :left (+ 24 (:x (:menu-pos @local))) - :top (:y (:menu-pos @local)) - :on-edit on-edit-open - :on-menu-close on-menu-close - :on-import on-import}] - - [:span.info (str (tr "labels.num-of-files" (i18n/c file-count)))] - (let [time (-> (:modified-at project) - (dt/timeago {:locale locale}))] - [:span.recent-files-row-title-info (str ", " time)]) - [:div.project-actions - (when-not (:is-default project) - [:button.pin-icon.tooltip.tooltip-bottom - {:class (when (:is-pinned project) "active") - :on-click toggle-pin - :alt (tr "dashboard.pin-unpin") - :aria-label (tr "dashboard.pin-unpin") - :tab-index "0"} - (if (:is-pinned project) - i/pin-fill - i/pin)]) - - [:button.btn-secondary.btn-small.tooltip.tooltip-bottom - {:on-click on-create-click - :alt (tr "dashboard.new-file") - :aria-label (tr "dashboard.new-file") - :data-test "project-new-file" - :tab-index "0" - :on-key-down (fn [event] - (when (kbd/enter? event) - (on-create-click event)))} - i/close] - - [:button.btn-secondary.btn-small.tooltip.tooltip-bottom - {:on-click on-menu-click - :alt (tr "dashboard.options") - :aria-label (tr "dashboard.options") - :data-test "project-options" - :tab-index "0" - :on-key-down (fn [event] - (when (kbd/enter? event) - (dom/stop-propagation event) - (on-menu-click event)))} - i/actions]]]] - - [:& line-grid + [:& project-menu {:project project - :team team - :files files - :create-fn create-file - :limit limit}] + :show? (:menu-open @local) + :left (+ 24 (:x (:menu-pos @local))) + :top (:y (:menu-pos @local)) + :on-edit on-edit-open + :on-menu-close on-menu-close + :on-import on-import}] - (when (and (> limit 0) - (> file-count limit)) - [:button.show-more {:on-click on-nav - :tab-index "0" - :on-key-down (fn [event] - (when (kbd/enter? event) - (on-nav)))} - [:div.placeholder-label - (tr "dashboard.show-all-files")] - [:div.placeholder-icon i/arrow-down]])]))) + [:span {:class (stl/css :info)} (str (tr "labels.num-of-files" (i18n/c file-count)))] + + (let [time (-> (:modified-at project) + (dt/timeago {:locale locale}))] + [:span {:class (stl/css :recent-files-row-title-info)} (str ", " time)]) + + [:div {:class (stl/css :project-actions)} + (when-not (:is-default project) + [:button + {:class (stl/css-case :pin-icon true + :tooltip true + :tooltip-bottom true + :active (:is-pinned project)) + :on-click toggle-pin + :alt (tr "dashboard.pin-unpin") + :aria-label (tr "dashboard.pin-unpin") + :tab-index "0"} + (if (:is-pinned project) + i/pin-fill + i/pin)]) + + [:button + {:class (stl/css :btn-secondary :btn-small :tooltip :tooltip-bottom) + :on-click on-create-click + :alt (tr "dashboard.new-file") + :aria-label (tr "dashboard.new-file") + :data-test "project-new-file" + :on-key-down handle-create-click} + i/close] + + [:button + {:class (stl/css :btn-secondary :btn-small :tooltip :tooltip-bottom) + :on-click on-menu-click + :alt (tr "dashboard.options") + :aria-label (tr "dashboard.options") + :data-test "project-options" + :on-key-down handle-menu-click} + i/actions]]]] + + [:div {:class (stl/css :grid-container) :ref rowref} + [:& line-grid + {:project project + :team team + :files files + :create-fn create-file + :limit limit}]] + + (when (and (> limit 0) + (> file-count limit)) + [:button + {:class (stl/css :show-more) + :on-click on-nav + :tab-index "0" + :on-key-down (fn [event] + (when (kbd/enter? event) + (on-nav)))} + [:div {:class (stl/css :placeholder-label)} (tr "dashboard.show-all-files")] + [:div {:class (stl/css :placeholder-icon)} i/arrow-down]])])) (def recent-files-ref @@ -511,8 +361,7 @@ (mf/defc projects-section [{:keys [team projects profile default-project-id] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - projects (->> (vals projects) + (let [projects (->> (vals projects) (sort-by :modified-at) (reverse)) recent-map (mf/deref recent-files-ref) @@ -562,68 +411,34 @@ (st/emit! (dd/fetch-recent-files team-id) (dd/clear-selected-files))) - (if new-css-system - (when (seq projects) - [:* - [:& header] + (when (seq projects) + [:* + [:& header] - (when team-hero? - [:& team-hero {:team team :close-fn close-banner}]) + (when team-hero? + [:& team-hero {:team team :close-fn close-banner}]) - (when (and (contains? cf/flags :dashboard-templates-section) - (or (not tutorial-viewed?) - (not walkthrough-viewed?))) - [:div {:class (stl/css :hero-projects)} - (when (and (not tutorial-viewed?) (:is-default team)) - [:& tutorial-project - {:close-tutorial close-tutorial - :default-project-id default-project-id}]) + (when (and (contains? cf/flags :dashboard-templates-section) + (or (not tutorial-viewed?) + (not walkthrough-viewed?))) + [:div {:class (stl/css :hero-projects)} + (when (and (not tutorial-viewed?) (:is-default team)) + [:& tutorial-project + {:close-tutorial close-tutorial + :default-project-id default-project-id}]) - (when (and (not walkthrough-viewed?) (:is-default team)) - [:& interface-walkthrough - {:close-walkthrough close-walkthrough}])]) + (when (and (not walkthrough-viewed?) (:is-default team)) + [:& interface-walkthrough + {:close-walkthrough close-walkthrough}])]) - [:div {:class (stl/css :dashboard-container :no-bg :dashboard-projects)} - (for [{:keys [id] :as project} projects] - (let [files (when recent-map - (->> (vals recent-map) - (filterv #(= id (:project-id %))) - (sort-by :modified-at #(compare %2 %1))))] - [:& project-item {:project project - :team team - :files files - :first? (= project (first projects)) - :key id}]))]]) - - ;; OLD - (when (seq projects) - [:* - [:& header] - - (when team-hero? - [:& team-hero {:team team :close-fn close-banner}]) - - (when (and (contains? cf/flags :dashboard-templates-section) - (or (not tutorial-viewed?) - (not walkthrough-viewed?))) - [:div.hero-projects - (when (and (not tutorial-viewed?) (:is-default team)) - [:& tutorial-project - {:close-tutorial close-tutorial - :default-project-id default-project-id}]) - - (when (and (not walkthrough-viewed?) (:is-default team)) - [:& interface-walkthrough - {:close-walkthrough close-walkthrough}])]) - - [:div.dashboard-container.no-bg.dashboard-projects - (for [{:keys [id] :as project} projects] - (let [files (when recent-map - (->> (vals recent-map) - (filterv #(= id (:project-id %))) - (sort-by :modified-at #(compare %2 %1))))] - [:& project-item {:project project - :team team - :files files - :first? (= project (first projects)) - :key id}]))]])))) + [:div {:class (stl/css :dashboard-container :no-bg :dashboard-projects)} + (for [{:keys [id] :as project} projects] + (let [files (when recent-map + (->> (vals recent-map) + (filterv #(= id (:project-id %))) + (sort-by :modified-at #(compare %2 %1))))] + [:& project-item {:project project + :team team + :files files + :first? (= project (first projects)) + :key id}]))]]))) diff --git a/frontend/src/app/main/ui/dashboard/search.cljs b/frontend/src/app/main/ui/dashboard/search.cljs index 1f47edb4ed..3b4d090996 100644 --- a/frontend/src/app/main/ui/dashboard/search.cljs +++ b/frontend/src/app/main/ui/dashboard/search.cljs @@ -10,7 +10,6 @@ [app.main.data.dashboard :as dd] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.dashboard.grid :refer [grid]] [app.main.ui.hooks :as hooks] [app.main.ui.icons :as i] @@ -20,8 +19,7 @@ (mf/defc search-page [{:keys [team search-term] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - result (mf/deref refs/dashboard-search-result) + (let [result (mf/deref refs/dashboard-search-result) [rowref limit] (hooks/use-dynamic-grid-item-width)] (mf/use-effect @@ -38,61 +36,31 @@ (fn [] (st/emit! (dd/search {:search-term search-term}) (dd/clear-selected-files)))) - (if new-css-system - [:* - [:header {:class (stl/css :dashboard-header)} - [:div#dashboard-search-title {:class (stl/css :dashboard-title)} - [:h1 (tr "dashboard.title-search")]]] + [:* + [:header {:class (stl/css :dashboard-header)} + [:div#dashboard-search-title {:class (stl/css :dashboard-title)} + [:h1 (tr "dashboard.title-search")]]] - [:section {:class (stl/css :dashboard-container :search :no-bg) - :ref rowref} - (cond - (empty? search-term) - [:div {:class (stl/css :grid-empty-placeholder :search)} - [:div {:class (stl/css :icon)} i/search] - [:div {:class (stl/css :text)} (tr "dashboard.type-something")]] + [:section {:class (stl/css :dashboard-container :search :no-bg) + :ref rowref} + (cond + (empty? search-term) + [:div {:class (stl/css :grid-empty-placeholder :search)} + [:div {:class (stl/css :icon)} i/search] + [:div {:class (stl/css :text)} (tr "dashboard.type-something")]] - (nil? result) - [:div {:class (stl/css :grid-empty-placeholder :search)} - [:div {:class (stl/css :icon)} i/search] - [:div {:class (stl/css :text)} (tr "dashboard.searching-for" search-term)]] + (nil? result) + [:div {:class (stl/css :grid-empty-placeholder :search)} + [:div {:class (stl/css :icon)} i/search] + [:div {:class (stl/css :text)} (tr "dashboard.searching-for" search-term)]] - (empty? result) - [:div {:class (stl/css :grid-empty-placeholder :search)} - [:div {:class (stl/css :icon)} i/search] - [:div {:class (stl/css :text)} (tr "dashboard.no-matches-for" search-term)]] + (empty? result) + [:div {:class (stl/css :grid-empty-placeholder :search)} + [:div {:class (stl/css :icon)} i/search] + [:div {:class (stl/css :text)} (tr "dashboard.no-matches-for" search-term)]] - :else - [:& grid {:files result - :hide-new? true - :origin :search - :limit limit}])]] - - ;; OLD - [:* - [:header.dashboard-header - [:div.dashboard-title#dashboard-search-title - [:h1 (tr "dashboard.title-search")]]] - - [:section.dashboard-container.search.no-bg {:ref rowref} - (cond - (empty? search-term) - [:div.grid-empty-placeholder.search - [:div.icon i/search] - [:div.text (tr "dashboard.type-something")]] - - (nil? result) - [:div.grid-empty-placeholder.search - [:div.icon i/search] - [:div.text (tr "dashboard.searching-for" search-term)]] - - (empty? result) - [:div.grid-empty-placeholder.search - [:div.icon i/search] - [:div.text (tr "dashboard.no-matches-for" search-term)]] - - :else - [:& grid {:files result - :hide-new? true - :origin :search - :limit limit}])]]))) + :else + [:& grid {:files result + :hide-new? true + :origin :search + :limit limit}])]])) diff --git a/frontend/src/app/main/ui/dashboard/sidebar.cljs b/frontend/src/app/main/ui/dashboard/sidebar.cljs index 4b61fb2bab..219883e931 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.cljs +++ b/frontend/src/app/main/ui/dashboard/sidebar.cljs @@ -20,7 +20,6 @@ [app.main.store :as st] [app.main.ui.components.dropdown-menu :refer [dropdown-menu dropdown-menu-item*]] [app.main.ui.components.link :refer [link]] - [app.main.ui.context :as ctx] [app.main.ui.dashboard.comments :refer [comments-icon comments-section]] [app.main.ui.dashboard.inline-edition :refer [inline-edition]] [app.main.ui.dashboard.project-menu :refer [project-menu]] @@ -41,8 +40,7 @@ (mf/defc sidebar-project [{:keys [item selected?] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - dstate (mf/deref refs/dashboard-local) + (let [dstate (mf/deref refs/dashboard-local) selected-files (:selected-files dstate) selected-project (:selected-project dstate) edit-id (:project-for-edit dstate) @@ -136,59 +134,33 @@ mdata {:on-success on-drop-success}] (st/emit! (dd/move-files (with-meta data mdata)))))))] - (if new-css-system - [:* - [:li {:tab-index "0" - :class (stl/css-case :project-element true - :current selected? - :dragging (:dragging? local)) - :on-click on-click - :on-key-down on-key-down - :on-double-click on-edit-open - :on-context-menu on-menu-click - :on-drag-enter on-drag-enter - :on-drag-over on-drag-over - :on-drag-leave on-drag-leave - :on-drop on-drop} - (if (:edition? local) - [:& inline-edition {:content (:name item) - :on-end on-edit}] - [:span {:class (stl/css :element-title)} (:name item)])] - [:& project-menu {:project item - :show? (:menu-open local) - :left (:x (:menu-pos local)) - :top (:y (:menu-pos local)) - :on-edit on-edit-open - :on-menu-close on-menu-close}]] - - ;; OLD - [:* - [:li {:tab-index "0" - :class (if selected? "current" - (when (:dragging? local) "dragging")) - :on-click on-click - :on-key-down on-key-down - :on-double-click on-edit-open - :on-context-menu on-menu-click - :on-drag-enter on-drag-enter - :on-drag-over on-drag-over - :on-drag-leave on-drag-leave - :on-drop on-drop} - (if (:edition? local) - [:& inline-edition {:content (:name item) - :on-end on-edit}] - [:span.element-title (:name item)])] - [:& project-menu {:project item - :show? (:menu-open local) - :left (:x (:menu-pos local)) - :top (:y (:menu-pos local)) - :on-edit on-edit-open - :on-menu-close on-menu-close}]]))) + [:* + [:li {:tab-index "0" + :class (stl/css-case :project-element true + :current selected? + :dragging (:dragging? local)) + :on-click on-click + :on-key-down on-key-down + :on-double-click on-edit-open + :on-context-menu on-menu-click + :on-drag-enter on-drag-enter + :on-drag-over on-drag-over + :on-drag-leave on-drag-leave + :on-drop on-drop} + (if (:edition? local) + [:& inline-edition {:content (:name item) + :on-end on-edit}] + [:span {:class (stl/css :element-title)} (:name item)])] + [:& project-menu {:project item + :show? (:menu-open local) + :left (:x (:menu-pos local)) + :top (:y (:menu-pos local)) + :on-edit on-edit-open + :on-menu-close on-menu-close}]])) (mf/defc sidebar-search [{:keys [search-term team-id] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - search-term (or search-term "") + (let [search-term (or search-term "") focused? (mf/use-state false) emit! (mf/use-memo #(f/debounce st/emit! 500)) @@ -236,70 +208,39 @@ (when (kbd/enter? event) (on-clear-click event))))] - (if new-css-system - [:form {:class (stl/css :sidebar-search)} - [:input - {:class (stl/css :input-text) - :key "images-search-box" - :id "search-input" - :type "text" - :aria-label (tr "dashboard.search-placeholder") - :placeholder (tr "dashboard.search-placeholder") - :default-value search-term - :auto-complete "off" - ;; :on-focus on-search-focus - :on-blur on-search-blur - :on-change on-search-change - :on-key-press on-key-press - :ref #(when % (set! (.-value %) search-term))}] + [:form {:class (stl/css :sidebar-search)} + [:input + {:class (stl/css :input-text) + :key "images-search-box" + :id "search-input" + :type "text" + :aria-label (tr "dashboard.search-placeholder") + :placeholder (tr "dashboard.search-placeholder") + :default-value search-term + :auto-complete "off" + ;; :on-focus on-search-focus + :on-blur on-search-blur + :on-change on-search-change + :on-key-press on-key-press + :ref #(when % (set! (.-value %) search-term))}] - (if (or @focused? (seq search-term)) - [:div - {:class (stl/css :clear-search) - :tab-index "0" - :on-click on-clear-click - :on-key-down handle-clear-search} - i/close] + (if (or @focused? (seq search-term)) + [:div + {:class (stl/css :clear-search) + :tab-index "0" + :on-click on-clear-click + :on-key-down handle-clear-search} + i/close] - [:div - {:class (stl/css :search) - :on-click on-clear-click} - i/search])] - - ;; OLD - [:form.sidebar-search - [:input.input-text - {:key "images-search-box" - :id "search-input" - :type "text" - :aria-label (tr "dashboard.search-placeholder") - :placeholder (tr "dashboard.search-placeholder") - :default-value search-term - :auto-complete "off" - ;; :on-focus on-search-focus - :on-blur on-search-blur - :on-change on-search-change - :on-key-press on-key-press - :ref #(when % (set! (.-value %) search-term))}] - - (if (or @focused? (seq search-term)) - [:div.clear-search - {:tab-index "0" - :on-click on-clear-click - :on-key-down (fn [event] - (when (kbd/enter? event) - (on-clear-click event)))} - i/close] - - [:div.search - {:on-click on-clear-click} - i/search])]))) + [:div + {:class (stl/css :search) + :on-click on-clear-click} + i/search])])) (mf/defc teams-selector-dropdown-items {::mf/wrap-props false} [{:keys [team profile teams] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - on-create-clicked + (let [on-create-clicked (mf/use-callback #(st/emit! (modal/show :team-form {}))) @@ -318,76 +259,38 @@ (when (kbd/enter? event) (team-selected id event)))] - (if new-css-system - [:* - [:> dropdown-menu-item* {:on-click (partial team-selected (:default-team-id profile)) - :on-key-down handle-select-default - :id "teams-selector-default-team" - :class (stl/css :team-name)} - [:span {:class (stl/css :team-icon)} i/logo-icon] - [:span {:class (stl/css :team-text)} (tr "dashboard.your-penpot")] - (when (= (:default-team-id profile) (:id team)) - [:span {:class (stl/css :icon)} i/tick])] + [:* + [:> dropdown-menu-item* {:on-click (partial team-selected (:default-team-id profile)) + :on-key-down handle-select-default + :id "teams-selector-default-team" + :class (stl/css :team-name)} + [:span {:class (stl/css :team-icon)} i/logo-icon] + [:span {:class (stl/css :team-text)} (tr "dashboard.your-penpot")] + (when (= (:default-team-id profile) (:id team)) + [:span {:class (stl/css :icon)} i/tick])] - (for [team-item (remove :is-default (vals teams))] - [:> dropdown-menu-item* {:on-click (partial team-selected (:id team-item)) - :on-key-down (partial handle-select-team (:id team-item)) - :id (str "teams-selector-" (:id team-item)) - :class (stl/css :team-name) - :key (str "teams-selector-" (:id team-item))} - [:span {:class (stl/css :team-icon)} - [:img {:src (cf/resolve-team-photo-url team-item) - :alt (:name team-item)}]] - [:span {:class (stl/css :team-text) - :title (:name team-item)} (:name team-item)] - (when (= (:id team-item) (:id team)) - [:span {:class (stl/css :icon)} i/tick])]) - [:hr {:role "separator"}] - [:> dropdown-menu-item* {:on-click on-create-clicked - :on-key-down (fn [event] - (when (kbd/enter? event) - (on-create-clicked event))) - :id "teams-selector-create-team" - :class (stl/css :team-name :action)} - [:span {:class (stl/css :team-icon :new-team)} i/close] - [:span {:class (stl/css :team-text)} (tr "dashboard.create-new-team")]]] - - ;; OLD - [:* - [:> dropdown-menu-item* {:on-click (partial team-selected (:default-team-id profile)) - :on-key-down (fn [event] - (when (kbd/enter? event) - (team-selected (:default-team-id profile) event))) - :id "teams-selector-default-team" - :class "team-name"} - [:span.team-icon i/logo-icon] - [:span.team-text (tr "dashboard.your-penpot")] - (when (= (:default-team-id profile) (:id team)) - [:span.icon i/tick])] - - (for [team-item (remove :is-default (vals teams))] - [:> dropdown-menu-item* {:on-click (partial team-selected (:id team-item)) - :on-key-down (fn [event] - (when (kbd/enter? event) - (team-selected (:id team-item) event))) - :id (str "teams-selector-" (:id team-item)) - :class "team-name" - :key (str "teams-selector-" (:id team-item))} - [:span.team-icon - [:img {:src (cf/resolve-team-photo-url team-item) - :alt (:name team-item)}]] - [:span.team-text {:title (:name team-item)} (:name team-item)] - (when (= (:id team-item) (:id team)) - [:span.icon i/tick])]) - [:hr {:role "separator"}] - [:> dropdown-menu-item* {:on-click on-create-clicked - :on-key-down (fn [event] - (when (kbd/enter? event) - (on-create-clicked event))) - :id "teams-selector-create-team" - :class "team-name action"} - [:span.team-icon.new-team i/close] - [:span.team-text (tr "dashboard.create-new-team")]]]))) + (for [team-item (remove :is-default (vals teams))] + [:> dropdown-menu-item* {:on-click (partial team-selected (:id team-item)) + :on-key-down (partial handle-select-team (:id team-item)) + :id (str "teams-selector-" (:id team-item)) + :class (stl/css :team-name) + :key (str "teams-selector-" (:id team-item))} + [:span {:class (stl/css :team-icon)} + [:img {:src (cf/resolve-team-photo-url team-item) + :alt (:name team-item)}]] + [:span {:class (stl/css :team-text) + :title (:name team-item)} (:name team-item)] + (when (= (:id team-item) (:id team)) + [:span {:class (stl/css :icon)} i/tick])]) + [:hr {:role "separator"}] + [:> dropdown-menu-item* {:on-click on-create-clicked + :on-key-down (fn [event] + (when (kbd/enter? event) + (on-create-clicked event))) + :id "teams-selector-create-team" + :class (stl/css :team-name :action)} + [:span {:class (stl/css :team-icon :new-team)} i/close] + [:span {:class (stl/css :team-text)} (tr "dashboard.create-new-team")]]])) (s/def ::member-id ::us/uuid) (s/def ::leave-modal-form @@ -395,8 +298,7 @@ (mf/defc team-options-dropdown [{:keys [team profile] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - go-members #(st/emit! (dd/go-to-team-members)) + (let [go-members #(st/emit! (dd/go-to-team-members)) go-invitations #(st/emit! (dd/go-to-team-invitations)) go-webhooks #(st/emit! (dd/go-to-team-webhooks)) go-settings #(st/emit! (dd/go-to-team-settings)) @@ -549,15 +451,13 @@ (when (kbd/enter? event) (on-delete-clicked))) :id "teams-options-delete-team" - :class (if new-css-system (stl/css :warning) "warning") + :class (stl/css :warning) :data-test "delete-team"} (tr "dashboard.delete-team")])])) - (mf/defc sidebar-team-switch [{:keys [team profile] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - teams (mf/deref refs/teams) + (let [teams (mf/deref refs/teams) teams-without-default (into {} (filter (fn [[_ v]] (= false (:is-default v))) teams)) team-ids (map #(str "teams-selector-" %) (keys teams-without-default)) ids (concat ["teams-selector-default-team"] team-ids ["teams-selector-create-team"]) @@ -615,119 +515,55 @@ (fn [] (reset! show-teams-ddwn? false))] - (if new-css-system - [:div {:class (stl/css :sidebar-team-switch)} - [:div {:class (stl/css :switch-content)} + [:div {:class (stl/css :sidebar-team-switch)} + [:div {:class (stl/css :switch-content)} + [:button + {:class (stl/css :current-team) + :tab-index "0" + :on-click handle-show-team-click + :on-key-down handle-show-team-keydown} + + + (if (:is-default team) + [:div {:class (stl/css :team-name)} + [:span {:class (stl/css :team-icon)} i/logo-icon] + [:span {:class (stl/css :team-text)} (tr "dashboard.default-team-name")]] + [:div {:class (stl/css :team-name)} + [:span {:class (stl/css :team-icon)} + [:img {:src (cf/resolve-team-photo-url team) + :alt (:name team)}]] + [:span {:class (stl/css :team-text) :title (:name team)} (:name team)]]) + + [:span {:class (stl/css :switch-icon)} i/arrow-down]] + + (when-not (:is-default team) [:button - {:class (stl/css :current-team) + {:class (stl/css :switch-options) + :on-click handle-show-opts-click :tab-index "0" - :on-click handle-show-team-click - :on-key-down handle-show-team-keydown} + :on-key-down handle-show-opts-keydown} + i/actions])] + ;; Teams Dropdown + [:& dropdown-menu {:show @show-teams-ddwn? + :on-close handle-close-team + :ids ids + :list-class (stl/css :dropdown :teams-dropdown)} + [:& teams-selector-dropdown-items {:ids ids + :team team + :profile profile + :teams teams}]] - (if (:is-default team) - [:div {:class (stl/css :team-name)} - [:span {:class (stl/css :team-icon)} i/logo-icon] - [:span {:class (stl/css :team-text)} (tr "dashboard.default-team-name")]] - [:div {:class (stl/css :team-name)} - [:span {:class (stl/css :team-icon)} - [:img {:src (cf/resolve-team-photo-url team) - :alt (:name team)}]] - [:span {:class (stl/css :team-text) :title (:name team)} (:name team)]]) - - [:span {:class (stl/css :switch-icon)} i/arrow-down]] - - (when-not (:is-default team) - [:button - {:class (stl/css :switch-options) - :on-click handle-show-opts-click - :tab-index "0" - :on-key-down handle-show-opts-keydown} - i/actions])] - - ;; Teams Dropdown - [:& dropdown-menu {:show @show-teams-ddwn? - :on-close handle-close-team - :ids ids - :list-class (stl/css :dropdown :teams-dropdown)} - [:& teams-selector-dropdown-items {:ids ids - :team team - :profile profile - :teams teams}]] - - [:& dropdown-menu {:show @show-team-opts-ddwn? - :on-close #(reset! show-team-opts-ddwn? false) - :ids options-ids - :list-class (stl/css :dropdown :options-dropdown)} - [:& team-options-dropdown {:team team - :profile profile}]]] - - ;; Old css - [:div.sidebar-team-switch - [:div.switch-content - [:button.current-team {:tab-index "0" - :on-click (fn [event] - (dom/stop-propagation event) - (reset! show-teams-ddwn? true)) - :on-key-down (fn [event] - (when (or (kbd/space? event) (kbd/enter? event)) - (dom/prevent-default event) - (reset! show-teams-ddwn? true) - (ts/schedule-on-idle - (fn [] - (let [first-element (dom/get-element (first ids))] - (when first-element - (dom/focus! first-element)))))))} - (if (:is-default team) - [:div.team-name - [:span.team-icon i/logo-icon] - [:span.team-text (tr "dashboard.default-team-name")]] - [:div.team-name - [:span.team-icon - [:img {:src (cf/resolve-team-photo-url team) - :alt (:name team)}]] - [:span.team-text {:title (:name team)} (:name team)]]) - - [:span.switch-icon - i/arrow-down]] - - (when-not (:is-default team) - [:button.switch-options {:on-click (fn [event] - (dom/stop-propagation event) - (reset! show-team-opts-ddwn? true)) - :tab-index "0" - :on-key-down (fn [event] - (when (or (kbd/space? event) (kbd/enter? event)) - (dom/prevent-default event) - (reset! show-team-opts-ddwn? true) - (ts/schedule-on-idle - (fn [] - (let [first-element (dom/get-element (first options-ids))] - (when first-element - (dom/focus! first-element)))))))} - i/actions])] - - ;; Teams Dropdown - [:& dropdown-menu {:show @show-teams-ddwn? - :on-close #(reset! show-teams-ddwn? false) - :ids ids - :list-class "dropdown teams-dropdown"} - [:& teams-selector-dropdown-items {:ids ids - :team team - :profile profile - :teams teams}]] - - [:& dropdown-menu {:show @show-team-opts-ddwn? - :on-close #(reset! show-team-opts-ddwn? false) - :ids options-ids - :list-class "dropdown options-dropdown"} - [:& team-options-dropdown {:team team - :profile profile}]]]))) + [:& dropdown-menu {:show @show-team-opts-ddwn? + :on-close #(reset! show-team-opts-ddwn? false) + :ids options-ids + :list-class (stl/css :dropdown :options-dropdown)} + [:& team-options-dropdown {:team team + :profile profile}]]])) (mf/defc sidebar-content [{:keys [projects profile section team project search-term] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - default-project-id + (let [default-project-id (->> (vals projects) (d/seek :is-default) (:id)) @@ -813,115 +649,61 @@ (remove :is-default) (filter :is-pinned))] - (if new-css-system - [:div {:class (stl/css :sidebar-content)} - [:& sidebar-team-switch {:team team :profile profile}] - [:hr] - [:& sidebar-search {:search-term search-term - :team-id (:id team)}] + [:div {:class (stl/css :sidebar-content)} + [:& sidebar-team-switch {:team team :profile profile}] + [:hr] + [:& sidebar-search {:search-term search-term + :team-id (:id team)}] - [:div {:class (stl/css :sidebar-content-section)} - [:ul {:class (stl/css :sidebar-nav :no-overflow)} - [:li - {:class (stl/css :recent-projects) - :class-name (when projects? (stl/css :current))} - [:& link {:action go-projects - :keyboard-action go-projects-with-key} - [:span {:class (stl/css :element-title)} (tr "labels.projects")]]] + [:div {:class (stl/css :sidebar-content-section)} + [:ul {:class (stl/css :sidebar-nav :no-overflow)} + [:li + {:class (stl/css :recent-projects) + :class-name (when projects? (stl/css :current))} + [:& link {:action go-projects + :keyboard-action go-projects-with-key} + [:span {:class (stl/css :element-title)} (tr "labels.projects")]]] - [:li {:class-name (when drafts? (stl/css :current))} - [:& link {:action go-drafts - :keyboard-action go-drafts-with-key} - [:span {:class (stl/css :element-title)} (tr "labels.drafts")]]] + [:li {:class-name (when drafts? (stl/css :current))} + [:& link {:action go-drafts + :keyboard-action go-drafts-with-key} + [:span {:class (stl/css :element-title)} (tr "labels.drafts")]]] - [:li {:class-name (when libs? (stl/css :current))} - [:& link {:action go-libs - :keyboard-action go-libs-with-key} - [:span {:class (stl/css :element-title)} (tr "labels.shared-libraries")]]]]] + [:li {:class-name (when libs? (stl/css :current))} + [:& link {:action go-libs + :keyboard-action go-libs-with-key} + [:span {:class (stl/css :element-title)} (tr "labels.shared-libraries")]]]]] - [:hr] + [:hr] - [:div {:class (stl/css :sidebar-content-section)} - [:ul {:class (stl/css :sidebar-nav :no-overflow)} - [:li {:class-name (when fonts? (stl/css :current))} - [:& link {:action go-fonts - :keyboard-action go-fonts-with-key - :data-test "fonts"} - [:span {:class (stl/css :element-title)} (tr "labels.fonts")]]]]] - - [:hr] - [:div {:class (stl/css :sidebar-content-section) - :data-test "pinned-projects"} - (if (seq pinned-projects) - [:ul {:class (stl/css :sidebar-nav)} - (for [item pinned-projects] - [:& sidebar-project - {:item item - :key (dm/str (:id item)) - :id (:id item) - :team-id (:id team) - :selected? (= (:id item) (:id project))}])] - [:div {:class (stl/css :sidebar-empty-placeholder)} - [:span {:class (stl/css :icon)} i/pin] - [:span {:class (stl/css :text)} (tr "dashboard.no-projects-placeholder")]])]] - - ;; OLD STYLES - [:div.sidebar-content - [:& sidebar-team-switch {:team team :profile profile}] - [:hr] - [:& sidebar-search {:search-term search-term - :team-id (:id team)}] - [:div.sidebar-content-section - [:ul.sidebar-nav.no-overflow - [:li.recent-projects - {:class-name (when projects? "current")} - [:& link {:action go-projects - :keyboard-action go-projects-with-key} - [:span.element-title (tr "labels.projects")]]] - - [:li {:class-name (when drafts? "current")} - [:& link {:action go-drafts - :keyboard-action go-drafts-with-key} - [:span.element-title (tr "labels.drafts")]]] - - - [:li {:class-name (when libs? "current")} - [:& link {:action go-libs - :keyboard-action go-libs-with-key} - [:span.element-title (tr "labels.shared-libraries")]]]]] - - [:hr] - - [:div.sidebar-content-section - [:ul.sidebar-nav.no-overflow - [:li {:class-name (when fonts? "current")} - - [:& link {:action go-fonts - :keyboard-action go-fonts-with-key - :data-test "fonts"} - [:span.element-title (tr "labels.fonts")]]]]] - - [:hr] - [:div.sidebar-content-section {:data-test "pinned-projects"} - (if (seq pinned-projects) - [:ul.sidebar-nav - (for [item pinned-projects] - [:& sidebar-project - {:item item - :key (dm/str (:id item)) - :id (:id item) - :team-id (:id team) - :selected? (= (:id item) (:id project))}])] - [:div.sidebar-empty-placeholder - [:span.icon i/pin] - [:span.text (tr "dashboard.no-projects-placeholder")]])]]))) + [:div {:class (stl/css :sidebar-content-section)} + [:ul {:class (stl/css :sidebar-nav :no-overflow)} + [:li {:class-name (when fonts? (stl/css :current))} + [:& link {:action go-fonts + :keyboard-action go-fonts-with-key + :data-test "fonts"} + [:span {:class (stl/css :element-title)} (tr "labels.fonts")]]]]] + [:hr] + [:div {:class (stl/css :sidebar-content-section) + :data-test "pinned-projects"} + (if (seq pinned-projects) + [:ul {:class (stl/css :sidebar-nav)} + (for [item pinned-projects] + [:& sidebar-project + {:item item + :key (dm/str (:id item)) + :id (:id item) + :team-id (:id team) + :selected? (= (:id item) (:id project))}])] + [:div {:class (stl/css :sidebar-empty-placeholder)} + [:span {:class (stl/css :icon)} i/pin] + [:span {:class (stl/css :text)} (tr "dashboard.no-projects-placeholder")]])]])) (mf/defc profile-section [{:keys [profile team] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - show (mf/use-state false) + (let [show (mf/use-state false) photo (cf/resolve-profile-photo-url profile) on-click @@ -1024,236 +806,110 @@ (fn [event] (when (kbd/enter? event) (on-click (du/logout) event))))] - (if new-css-system - [:* - (when (and team profile) - [:& comments-section - {:profile profile - :team team - :show? show-comments? - :on-show-comments handle-show-comments - :on-hide-comments handle-hide-comments}]) - [:div {:class (stl/css :profile-section)} - [:div {:class (stl/css :profile) - :tab-index "0" - :on-click handle-click - :on-key-down handle-key-down - :data-test "profile-btn"} - [:img {:src photo - :alt (:fullname profile)}] - [:span (:fullname profile)]] + [:* + (when (and team profile) + [:& comments-section + {:profile profile + :team team + :show? show-comments? + :on-show-comments handle-show-comments + :on-hide-comments handle-hide-comments}]) - [:& dropdown-menu {:on-close handle-close :show @show} - [:ul {:class (stl/css :dropdown)} - [:li {:tab-index (if @show "0" "-1") - :on-click (partial on-click :settings-profile) - :on-key-down handle-key-down-profile - :data-test "profile-profile-opt"} - [:span {:class (stl/css :text)} (tr "labels.your-account")]] + [:div {:class (stl/css :profile-section)} + [:div {:class (stl/css :profile) + :tab-index "0" + :on-click handle-click + :on-key-down handle-key-down + :data-test "profile-btn"} + [:img {:src photo + :alt (:fullname profile)}] + [:span (:fullname profile)]] + [:& dropdown-menu {:on-close handle-close :show @show} + [:ul {:class (stl/css :dropdown)} + [:li {:tab-index (if @show "0" "-1") + :on-click (partial on-click :settings-profile) + :on-key-down handle-key-down-profile + :data-test "profile-profile-opt"} + [:span {:class (stl/css :text)} (tr "labels.your-account")]] + + [:li {:class (stl/css :separator) + :tab-index (if @show "0" "-1") + :data-url "https://help.penpot.app" + :on-click handle-click-url + :on-key-down handle-keydown-url + :data-test "help-center-profile-opt"} + [:span {:class (stl/css :text)} (tr "labels.help-center")]] + + [:li {:tab-index (if @show "0" "-1") + :data-url "https://community.penpot.app" + :on-click handle-click-url + :on-key-down handle-keydown-url} + [:span {:class (stl/css :text)} (tr "labels.community")]] + + [:li {:tab-index (if @show "0" "-1") + :data-url "https://www.youtube.com/c/Penpot" + :on-click handle-click-url + :on-key-down handle-keydown-url} + [:span {:class (stl/css :text)} (tr "labels.tutorials")]] + + [:li {:tab-index (if @show "0" "-1") + :on-click show-release-notes + :on-key-down handle-show-release-notes} + [:span {:class (stl/css :text)} (tr "labels.release-notes")]] + + [:li {:class (stl/css :separator) + :tab-index (if @show "0" "-1") + :data-url "https://penpot.app/libraries-templates" + :on-click handle-click-url + :on-key-down handle-keydown-url + :data-test "libraries-templates-profile-opt"} + [:span {:class (stl/css :text)} (tr "labels.libraries-and-templates")]] + + [:li {:tab-index (if @show "0" "-1") + :data-url "https://github.com/penpot/penpot" + :on-click handle-click-url + :on-key-down handle-keydown-url} + [:span {:class (stl/css :text)} (tr "labels.github-repo")]] + + [:li {:tab-index (if @show "0" "-1") + :data-url "https://penpot.app/terms" + :on-click handle-click-url + :on-key-down handle-keydown-url} + [:span {:class (stl/css :text)} (tr "auth.terms-of-service")]] + + (when (contains? cf/flags :user-feedback) [:li {:class (stl/css :separator) :tab-index (if @show "0" "-1") - :data-url "https://help.penpot.app" - :on-click handle-click-url - :on-key-down handle-keydown-url - :data-test "help-center-profile-opt"} - [:span {:class (stl/css :text)} (tr "labels.help-center")]] + :on-click handle-feedback-click + :on-key-down handle-feedback-keydown + :data-test "feedback-profile-opt"} + [:span {:class (stl/css :text)} (tr "labels.give-feedback")]]) - [:li {:tab-index (if @show "0" "-1") - :data-url "https://community.penpot.app" - :on-click handle-click-url - :on-key-down handle-keydown-url} - [:span {:class (stl/css :text)} (tr "labels.community")]] + [:li {:class (stl/css :separator) + :tab-index (if @show "0" "-1") + :on-click handle-logout-click + :on-key-down handle-logout-keydown + :data-test "logout-profile-opt"} + [:span {:class (stl/css :icon)} i/exit] + [:span {:class (stl/css :text)} (tr "labels.logout")]]]] - [:li {:tab-index (if @show "0" "-1") - :data-url "https://www.youtube.com/c/Penpot" - :on-click handle-click-url - :on-key-down handle-keydown-url} - [:span {:class (stl/css :text)} (tr "labels.tutorials")]] - - [:li {:tab-index (if @show "0" "-1") - :on-click show-release-notes - :on-key-down handle-show-release-notes} - [:span {:class (stl/css :text)} (tr "labels.release-notes")]] - - [:li {:class (stl/css :separator) - :tab-index (if @show "0" "-1") - :data-url "https://penpot.app/libraries-templates" - :on-click handle-click-url - :on-key-down handle-keydown-url - :data-test "libraries-templates-profile-opt"} - [:span {:class (stl/css :text)} (tr "labels.libraries-and-templates")]] - - [:li {:tab-index (if @show "0" "-1") - :data-url "https://github.com/penpot/penpot" - :on-click handle-click-url - :on-key-down handle-keydown-url} - [:span {:class (stl/css :text)} (tr "labels.github-repo")]] - - [:li {:tab-index (if @show "0" "-1") - :data-url "https://penpot.app/terms" - :on-click handle-click-url - :on-key-down handle-keydown-url} - [:span {:class (stl/css :text)} (tr "auth.terms-of-service")]] - - (when (contains? cf/flags :user-feedback) - [:li {:class (stl/css :separator) - :tab-index (if @show "0" "-1") - :on-click handle-feedback-click - :on-key-down handle-feedback-keydown - :data-test "feedback-profile-opt"} - [:span {:class (stl/css :text)} (tr "labels.give-feedback")]]) - - [:li {:class (stl/css :separator) - :tab-index (if @show "0" "-1") - :on-click handle-logout-click - :on-key-down handle-logout-keydown - :data-test "logout-profile-opt"} - [:span {:class (stl/css :icon)} i/exit] - [:span {:class (stl/css :text)} (tr "labels.logout")]]]] - - (when (and team profile) - [:& comments-icon - {:profile profile - :show? show-comments? - :on-show-comments handle-show-comments}])]] - - ;; OLD - [:div.profile-section - [:div.profile {:tab-index "0" - :on-click (fn [event] - (dom/stop-propagation event) - (reset! show true)) - :on-key-down (fn [event] - (when (kbd/enter? event) - (reset! show true))) - :data-test "profile-btn"} - [:img {:src photo - :alt (:fullname profile)}] - [:span (:fullname profile)]] - - [:& dropdown-menu {:on-close (fn [event] - (dom/stop-propagation event) - (reset! show false)) - :show @show} - [:ul.dropdown - [:li {:tab-index (if show - "0" - "-1") - :on-click (partial on-click :settings-profile) - :on-key-down (fn [event] - (when (kbd/enter? event) - (on-click :settings-profile event))) - :data-test "profile-profile-opt"} - [:span.text (tr "labels.your-account")]] - [:li.separator {:tab-index (if show - "0" - "-1") - :on-click #(dom/open-new-window "https://help.penpot.app") - :on-key-down (fn [event] - (when (kbd/enter? event) - (dom/open-new-window "https://help.penpot.app"))) - :data-test "help-center-profile-opt"} - [:span.text (tr "labels.help-center")]] - [:li {:tab-index (if show - "0" - "-1") - :on-click #(dom/open-new-window "https://community.penpot.app") - :on-key-down (fn [event] - (when (kbd/enter? event) - (dom/open-new-window "https://community.penpot.app")))} - [:span.text (tr "labels.community")]] - [:li {:tab-index (if show - "0" - "-1") - :on-click #(dom/open-new-window "https://www.youtube.com/c/Penpot") - :on-key-down (fn [event] - (when (kbd/enter? event) - (dom/open-new-window "https://www.youtube.com/c/Penpot")))} - [:span.text (tr "labels.tutorials")]] - [:li {:tab-index (if show - "0" - "-1") - :on-click show-release-notes - :on-key-down (fn [event] - (when (kbd/enter? event) - (show-release-notes)))} - [:span (tr "labels.release-notes")]] - - [:li.separator {:tab-index (if show - "0" - "-1") - :on-click #(dom/open-new-window "https://penpot.app/libraries-templates") - :on-key-down (fn [event] - (when (kbd/enter? event) - (dom/open-new-window "https://penpot.app/libraries-templates"))) - :data-test "libraries-templates-profile-opt"} - [:span.text (tr "labels.libraries-and-templates")]] - [:li {:tab-index (if show - "0" - "-1") - :on-click #(dom/open-new-window "https://github.com/penpot/penpot") - :on-key-down (fn [event] - (when (kbd/enter? event) - (dom/open-new-window "https://github.com/penpot/penpot")))} - [:span (tr "labels.github-repo")]] - [:li {:tab-index (if show - "0" - "-1") - :on-click #(dom/open-new-window "https://penpot.app/terms") - :on-key-down (fn [event] - (when (kbd/enter? event) - (dom/open-new-window "https://penpot.app/terms")))} - [:span (tr "auth.terms-of-service")]] - - (when (contains? cf/flags :user-feedback) - [:li.separator {:tab-index (if show - "0" - "-1") - :on-click (partial on-click :settings-feedback) - :on-key-down (fn [event] - (when (kbd/enter? event) - (on-click :settings-feedback event))) - :data-test "feedback-profile-opt"} - [:span.text (tr "labels.give-feedback")]]) - - [:li.separator {:tab-index (if show - "0" - "-1") - :on-click #(on-click (du/logout) %) - :on-key-down (fn [event] - (when (kbd/enter? event) - (on-click (du/logout) event))) - :data-test "logout-profile-opt"} - [:span.icon i/exit] - [:span.text (tr "labels.logout")]]]] - - (when (and team profile) - [:& comments-section - {:profile profile - :team team - :show? show-comments? - :on-show-comments handle-show-comments - :on-hide-comments handle-hide-comments}])]))) + (when (and team profile) + [:& comments-icon + {:profile profile + :show? show-comments? + :on-show-comments handle-show-comments}])]])) (mf/defc sidebar {::mf/wrap-props false ::mf/wrap [mf/memo]} [props] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - team (obj/get props "team") + (let [team (obj/get props "team") profile (obj/get props "profile")] - (if new-css-system - [:nav {:class (stl/css :dashboard-sidebar)} - [:> sidebar-content props] - [:& profile-section - {:profile profile - :team team}]] - - [:nav.dashboard-sidebar - [:> sidebar-content props] - [:& profile-section - {:profile profile - :team team}]]))) + [:nav {:class (stl/css :dashboard-sidebar)} + [:> sidebar-content props] + [:& profile-section + {:profile profile + :team team}]])) diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs index 2f4f57a65d..8a2f958a3b 100644 --- a/frontend/src/app/main/ui/dashboard/team.cljs +++ b/frontend/src/app/main/ui/dashboard/team.cljs @@ -21,7 +21,6 @@ [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.components.file-uploader :refer [file-uploader]] [app.main.ui.components.forms :as fm] - [app.main.ui.context :as ctx] [app.main.ui.dashboard.change-owner] [app.main.ui.dashboard.team-form] [app.main.ui.icons :as i] @@ -36,8 +35,7 @@ {::mf/wrap [mf/memo] ::mf/wrap-props false} [{:keys [section team]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - on-nav-members (mf/use-fn #(st/emit! (dd/go-to-team-members))) + (let [on-nav-members (mf/use-fn #(st/emit! (dd/go-to-team-members))) on-nav-settings (mf/use-fn #(st/emit! (dd/go-to-team-settings))) on-nav-invitations (mf/use-fn #(st/emit! (dd/go-to-team-invitations))) on-nav-webhooks (mf/use-fn #(st/emit! (dd/go-to-team-webhooks))) @@ -56,62 +54,33 @@ :team team :origin :team}))))] - (if new-css-system - [:header {:class (stl/css :dashboard-header :team)} - [:div {:class (stl/css :dashboard-title)} - [:h1 (cond - members-section? (tr "labels.members") - settings-section? (tr "labels.settings") - invitations-section? (tr "labels.invitations") - webhooks-section? (tr "labels.webhooks") - :else nil)]] - [:nav {:class (stl/css :dashboard-header-menu)} - [:ul {:class (stl/css :dashboard-header-options)} - [:li {:class (when members-section? (stl/css :active))} - [:a {:on-click on-nav-members} (tr "labels.members")]] - [:li {:class (when invitations-section? (stl/css :active))} - [:a {:on-click on-nav-invitations} (tr "labels.invitations")]] - (when (contains? cfg/flags :webhooks) - [:li {:class (when webhooks-section? (stl/css :active))} - [:a {:on-click on-nav-webhooks} (tr "labels.webhooks")]]) - [:li {:class (when settings-section? (stl/css :active))} - [:a {:on-click on-nav-settings} (tr "labels.settings")]]]] - [:div {:class (stl/css :dashboard-buttons)} - (if (and (or invitations-section? members-section?) (:is-admin permissions)) - [:a - {:class (stl/css :btn-secondary :btn-small) - :on-click on-invite-member - :data-test "invite-member"} - (tr "dashboard.invite-profile")] - [:div {:class (stl/css :blank-space)}])]] - - ;; OLD - [:header.dashboard-header.team - [:div.dashboard-title - [:h1 (cond - members-section? (tr "labels.members") - settings-section? (tr "labels.settings") - invitations-section? (tr "labels.invitations") - webhooks-section? (tr "labels.webhooks") - :else nil)]] - [:nav.dashboard-header-menu - [:ul.dashboard-header-options - [:li {:class (when members-section? "active")} - [:a {:on-click on-nav-members} (tr "labels.members")]] - [:li {:class (when invitations-section? "active")} - [:a {:on-click on-nav-invitations} (tr "labels.invitations")]] - (when (contains? cfg/flags :webhooks) - [:li {:class (when webhooks-section? "active")} - [:a {:on-click on-nav-webhooks} (tr "labels.webhooks")]]) - [:li {:class (when settings-section? "active")} - [:a {:on-click on-nav-settings} (tr "labels.settings")]]]] - [:div.dashboard-buttons - (if (and (or invitations-section? members-section?) (:is-admin permissions)) - [:a.btn-secondary.btn-small - {:on-click on-invite-member - :data-test "invite-member"} - (tr "dashboard.invite-profile")] - [:div.blank-space])]]))) + [:header {:class (stl/css :dashboard-header :team)} + [:div {:class (stl/css :dashboard-title)} + [:h1 (cond + members-section? (tr "labels.members") + settings-section? (tr "labels.settings") + invitations-section? (tr "labels.invitations") + webhooks-section? (tr "labels.webhooks") + :else nil)]] + [:nav {:class (stl/css :dashboard-header-menu)} + [:ul {:class (stl/css :dashboard-header-options)} + [:li {:class (when members-section? (stl/css :active))} + [:a {:on-click on-nav-members} (tr "labels.members")]] + [:li {:class (when invitations-section? (stl/css :active))} + [:a {:on-click on-nav-invitations} (tr "labels.invitations")]] + (when (contains? cfg/flags :webhooks) + [:li {:class (when webhooks-section? (stl/css :active))} + [:a {:on-click on-nav-webhooks} (tr "labels.webhooks")]]) + [:li {:class (when settings-section? (stl/css :active))} + [:a {:on-click on-nav-settings} (tr "labels.settings")]]]] + [:div {:class (stl/css :dashboard-buttons)} + (if (and (or invitations-section? members-section?) (:is-admin permissions)) + [:a + {:class (stl/css :btn-secondary :btn-small) + :on-click on-invite-member + :data-test "invite-member"} + (tr "dashboard.invite-profile")] + [:div {:class (stl/css :blank-space)}])]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; INVITATIONS MODAL @@ -140,8 +109,7 @@ ::mf/register-as :invite-members ::mf/wrap-props false} [{:keys [team origin]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - members-map (mf/deref refs/dashboard-team-members) + (let [members-map (mf/deref refs/dashboard-team-members) perms (:permissions team) roles (mf/use-memo (mf/deps perms) #(get-available-roles perms)) @@ -188,80 +156,41 @@ (dd/fetch-team-invitations))))] - (if new-css-system - [:div {:class (stl/css-case :modal-team-container true - :hero (= origin :hero))} - [:& fm/form {:on-submit on-submit :form form} - [:div {:class (stl/css :modal-title)} - (tr "modals.invite-team-member.title")] + [:div {:class (stl/css-case :modal-team-container true + :hero (= origin :hero))} + [:& fm/form {:on-submit on-submit :form form} + [:div {:class (stl/css :modal-title)} + (tr "modals.invite-team-member.title")] - (when-not (= "" @error-text) - [:div {:class (stl/css :error-msg)} - [:span {:class (stl/css :icon)} i/msg-error] - [:span {:class (stl/css :message)} @error-text]]) + (when-not (= "" @error-text) + [:div {:class (stl/css :error-msg)} + [:span {:class (stl/css :icon)} i/msg-error] + [:span {:class (stl/css :message)} @error-text]]) - (when (some current-data-emails current-members-emails) - [:div {:class (stl/css :warning-msg)} - [:span {:class (stl/css :icon)} i/msg-warning] - [:span {:class (stl/css :message)} (tr "modals.invite-member.repeated-invitation")]]) + (when (some current-data-emails current-members-emails) + [:div {:class (stl/css :warning-msg)} + [:span {:class (stl/css :icon)} i/msg-warning] + [:span {:class (stl/css :message)} (tr "modals.invite-member.repeated-invitation")]]) - [:div {:class (stl/css :role-select)} - [:p {:class (stl/css :role-title)} (tr "onboarding.choice.team-up.roles")] - [:& fm/select {:name :role :options roles}]] + [:div {:class (stl/css :role-select)} + [:p {:class (stl/css :role-title)} (tr "onboarding.choice.team-up.roles")] + [:& fm/select {:name :role :options roles}]] - [:div {:class (stl/css :invitation-row)} - [:& fm/multi-input {:type "email" - :name :emails - :auto-focus? true - :trim true - :valid-item-fn us/parse-email - :caution-item-fn current-members-emails - :label (tr "modals.invite-member.emails") - :on-submit on-submit}]] + [:div {:class (stl/css :invitation-row)} + [:& fm/multi-input {:type "email" + :name :emails + :auto-focus? true + :trim true + :valid-item-fn us/parse-email + :caution-item-fn current-members-emails + :label (tr "modals.invite-member.emails") + :on-submit on-submit}]] - [:div {:class (stl/css :action-buttons)} - [:> fm/submit-button* - {:label (tr "modals.invite-member-confirm.accept") - :disabled (and (boolean (some current-data-emails current-members-emails)) - (empty? (remove current-members-emails current-data-emails)))}]]]] - - - [:div.modal.dashboard-invite-modal.form-container - {:class (dom/classnames - :hero (= origin :hero))} - [:& fm/form {:on-submit on-submit :form form} - [:div.title - [:span.text (tr "modals.invite-team-member.title")]] - - (when-not (= "" @error-text) - [:div.error - [:span.icon i/msg-error] - [:span.text @error-text]]) - - (when (some current-data-emails current-members-emails) - [:div.warning - [:span.icon i/msg-warning] - [:span.text (tr "modals.invite-member.repeated-invitation")]]) - - [:div.form-row - [:p.label (tr "onboarding.choice.team-up.roles")] - [:& fm/select {:name :role :options roles}]] - - [:div.form-row - [:& fm/multi-input {:type "email" - :name :emails - :auto-focus? true - :trim true - :valid-item-fn us/parse-email - :caution-item-fn current-members-emails - :label (tr "modals.invite-member.emails") - :on-submit on-submit}]] - - [:div.action-buttons - [:> fm/submit-button* - {:label (tr "modals.invite-member-confirm.accept") - :disabled (and (boolean (some current-data-emails current-members-emails)) - (empty? (remove current-members-emails current-data-emails)))}]]]]))) + [:div {:class (stl/css :action-buttons)} + [:> fm/submit-button* + {:label (tr "modals.invite-member-confirm.accept") + :disabled (and (boolean (some current-data-emails current-members-emails)) + (empty? (remove current-members-emails current-data-emails)))}]]]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; MEMBERS SECTION @@ -270,33 +199,20 @@ (mf/defc member-info {::mf/wrap-props false} [{:keys [member profile]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - is-you? (= (:id profile) (:id member))] - (if new-css-system - [:* - [:div {:class (stl/css :member-image)} - [:img {:src (cfg/resolve-profile-photo-url member)}]] - [:div {:class (stl/css :member-info)} - [:div {:class (stl/css :member-name)} (:name member) - (when is-you? - [:span {:class (stl/css :you)} (tr "labels.you")])] - [:div {:class (stl/css :member-email)} (:email member)]]] - - ;; OLD - [:* - [:div.member-image - [:img {:src (cfg/resolve-profile-photo-url member)}]] - [:div.member-info - [:div.member-name (:name member) - (when is-you? - [:span.you (tr "labels.you")])] - [:div.member-email (:email member)]]]))) + (let [is-you? (= (:id profile) (:id member))] + [:* + [:div {:class (stl/css :member-image)} + [:img {:src (cfg/resolve-profile-photo-url member)}]] + [:div {:class (stl/css :member-info)} + [:div {:class (stl/css :member-name)} (:name member) + (when is-you? + [:span {:class (stl/css :you)} (tr "labels.you")])] + [:div {:class (stl/css :member-email)} (:email member)]]])) (mf/defc rol-info {::mf/wrap-props false} [{:keys [member team on-set-admin on-set-editor on-set-owner profile]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - member-is-owner? (:is-owner member) + (let [member-is-owner? (:is-owner member) member-is-admin? (and (:is-admin member) (not member-is-owner?)) member-is-editor? (and (:can-edit member) (and (not member-is-admin?) (not member-is-owner?))) show? (mf/use-state false) @@ -316,50 +232,29 @@ on-show (mf/use-fn #(reset! show? true)) on-hide (mf/use-fn #(reset! show? false))] - (if new-css-system - [:* - (if (and can-change-rol? not-superior? (not (and is-you? you-owner?))) - [:div {:class (stl/css :rol-selector :has-priv) - :on-click on-show} - [:span {:class (stl/css :rol-label)} (tr role)] - [:span {:class (stl/css :icon)} i/arrow-down]] - [:div {:class (stl/css :rol-selector)} - [:span {:class (stl/css :rol-label)} (tr role)]]) + [:* + (if (and can-change-rol? not-superior? (not (and is-you? you-owner?))) + [:div {:class (stl/css :rol-selector :has-priv) + :on-click on-show} + [:span {:class (stl/css :rol-label)} (tr role)] + [:span {:class (stl/css :icon)} i/arrow-down]] + [:div {:class (stl/css :rol-selector)} + [:span {:class (stl/css :rol-label)} (tr role)]]) - [:& dropdown {:show @show? :on-close on-hide} - [:ul {:class (stl/css :dropdown :options-dropdown)} - [:li {:on-click on-set-admin} (tr "labels.admin")] - [:li {:on-click on-set-editor} (tr "labels.editor")] - ;; Temporarily disabled viewer role - ;; https://tree.taiga.io/project/penpot/issue/1083 - ;; [:li {:on-click set-viewer} (tr "labels.viewer")] - (when you-owner? - [:li {:on-click (partial on-set-owner member)} (tr "labels.owner")])]]] - - ;; OLD - [:* - (if (and can-change-rol? not-superior? (not (and is-you? you-owner?))) - [:div.rol-selector.has-priv {:on-click on-show} - [:span.rol-label (tr role)] - [:span.icon i/arrow-down]] - [:div.rol-selector - [:span.rol-label (tr role)]]) - - [:& dropdown {:show @show? :on-close on-hide} - [:ul.dropdown.options-dropdown - [:li {:on-click on-set-admin} (tr "labels.admin")] - [:li {:on-click on-set-editor} (tr "labels.editor")] - ;; Temporarily disabled viewer role - ;; https://tree.taiga.io/project/penpot/issue/1083 - ;; [:li {:on-click set-viewer} (tr "labels.viewer")] - (when you-owner? - [:li {:on-click (partial on-set-owner member)} (tr "labels.owner")])]]]))) + [:& dropdown {:show @show? :on-close on-hide} + [:ul {:class (stl/css :dropdown :options-dropdown)} + [:li {:on-click on-set-admin} (tr "labels.admin")] + [:li {:on-click on-set-editor} (tr "labels.editor")] + ;; Temporarily disabled viewer role + ;; https://tree.taiga.io/project/penpot/issue/1083 + ;; [:li {:on-click set-viewer} (tr "labels.viewer")] + (when you-owner? + [:li {:on-click (partial on-set-owner member)} (tr "labels.owner")])]]])) (mf/defc member-actions {::mf/wrap-props false} [{:keys [member team on-delete on-leave profile]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - is-owner? (:is-owner member) + (let [is-owner? (:is-owner member) owner? (dm/get-in team [:permissions :is-owner]) admin? (dm/get-in team [:permissions :is-admin]) show? (mf/use-state false) @@ -369,30 +264,17 @@ on-show (mf/use-fn #(reset! show? true)) on-hide (mf/use-fn #(reset! show? false))] - (if new-css-system - [:* - (when (or is-you? (and can-delete? (not (and is-owner? (not owner?))))) - [:span {:class (stl/css :icon) - :on-click on-show} [i/actions]]) + [:* + (when (or is-you? (and can-delete? (not (and is-owner? (not owner?))))) + [:span {:class (stl/css :icon) + :on-click on-show} [i/actions]]) - [:& dropdown {:show @show? :on-close on-hide} - [:ul {:class (stl/css :dropdown :actions-dropdown)} - (when is-you? - [:li {:on-click on-leave} (tr "dashboard.leave-team")]) - (when (and can-delete? (not is-you?) (not (and is-owner? (not owner?)))) - [:li {:on-click on-delete} (tr "labels.remove-member")])]]] - - ;; OLD - [:* - (when (or is-you? (and can-delete? (not (and is-owner? (not owner?))))) - [:span.icon {:on-click on-show} [i/actions]]) - - [:& dropdown {:show @show? :on-close on-hide} - [:ul.dropdown.actions-dropdown - (when is-you? - [:li {:on-click on-leave} (tr "dashboard.leave-team")]) - (when (and can-delete? (not is-you?) (not (and is-owner? (not owner?)))) - [:li {:on-click on-delete} (tr "labels.remove-member")])]]]))) + [:& dropdown {:show @show? :on-close on-hide} + [:ul {:class (stl/css :dropdown :actions-dropdown)} + (when is-you? + [:li {:on-click on-leave} (tr "dashboard.leave-team")]) + (when (and can-delete? (not is-you?) (not (and is-owner? (not owner?)))) + [:li {:on-click on-delete} (tr "labels.remove-member")])]]])) (defn- set-role! [member-id role] (let [params {:member-id member-id :role role}] @@ -403,8 +285,7 @@ ::mf/wrap-props false} [{:keys [team member members profile]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - member-id (:id member) + (let [member-id (:id member) on-set-admin (mf/use-fn (mf/deps member-id) (partial set-role! member-id :admin)) on-set-editor (mf/use-fn (mf/deps member-id) (partial set-role! member-id :editor)) owner? (dm/get-in team [:permissions :is-owner]) @@ -513,50 +394,29 @@ (= true owner?) on-change-owner-and-leave :else on-leave)] - (if new-css-system - [:div {:class (stl/css :table-row)} - [:div {:class (stl/css :table-field :name)} - [:& member-info {:member member :profile profile}]] + [:div {:class (stl/css :table-row)} + [:div {:class (stl/css :table-field :name)} + [:& member-info {:member member :profile profile}]] - [:div {:class (stl/css :table-field :roles)} - [:& rol-info {:member member - :team team - :on-set-admin on-set-admin - :on-set-editor on-set-editor - :on-set-owner on-set-owner - :profile profile}]] + [:div {:class (stl/css :table-field :roles)} + [:& rol-info {:member member + :team team + :on-set-admin on-set-admin + :on-set-editor on-set-editor + :on-set-owner on-set-owner + :profile profile}]] - [:div {:class (stl/css :table-field :actions)} - [:& member-actions {:member member - :profile profile - :team team - :on-delete on-delete - :on-leave on-leave'}]]] - ;; OLD - [:div.table-row - [:div.table-field.name - [:& member-info {:member member :profile profile}]] - - [:div.table-field.roles - [:& rol-info {:member member - :team team - :on-set-admin on-set-admin - :on-set-editor on-set-editor - :on-set-owner on-set-owner - :profile profile}]] - - [:div.table-field.actions - [:& member-actions {:member member - :profile profile - :team team - :on-delete on-delete - :on-leave on-leave'}]]]))) + [:div {:class (stl/css :table-field :actions)} + [:& member-actions {:member member + :profile profile + :team team + :on-delete on-delete + :on-leave on-leave'}]]])) (mf/defc team-members {::mf/wrap-props false} [{:keys [members-map team profile]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - members (mf/with-memo [members-map] + (let [members (mf/with-memo [members-map] (->> (vals members-map) (sort-by :created-at) (remove :is-owner))) @@ -564,53 +424,30 @@ (->> (vals members-map) (d/seek :is-owner)))] - (if new-css-system - [:div {:class (stl/css :dashboard-table :team-members)} - [:div {:class (stl/css :table-header)} - [:div {:class (stl/css :table-field :name)} (tr "labels.member")] - [:div {:class (stl/css :table-field :role)} (tr "labels.role")]] + [:div {:class (stl/css :dashboard-table :team-members)} + [:div {:class (stl/css :table-header)} + [:div {:class (stl/css :table-field :name)} (tr "labels.member")] + [:div {:class (stl/css :table-field :role)} (tr "labels.role")]] - [:div {:class (stl/css :table-rows)} + [:div {:class (stl/css :table-rows)} + [:& team-member + {:member owner + :team team + :profile profile + :members members-map}] + + (for [item members] [:& team-member - {:member owner + {:member item :team team :profile profile - :members members-map}] - - (for [item members] - [:& team-member - {:member item - :team team - :profile profile - :key (:id item) - :members members-map}])]] - - ;; OLD - [:div.dashboard-table.team-members - [:div.table-header - [:div.table-field.name (tr "labels.member")] - [:div.table-field.role (tr "labels.role")]] - - [:div.table-rows - [:& team-member - {:member owner - :team team - :profile profile - :members members-map}] - - (for [item members] - [:& team-member - {:member item - :team team - :profile profile - :key (:id item) - :members members-map}])]]))) + :key (:id item) + :members members-map}])]])) (mf/defc team-members-page {::mf/wrap-props false} [{:keys [team profile]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - members-map (mf/deref refs/dashboard-team-members)] + (let [members-map (mf/deref refs/dashboard-team-members)] (mf/with-effect [team] (dom/set-html-title @@ -622,23 +459,13 @@ (mf/with-effect [team] (st/emit! (dd/fetch-team-members (:id team)))) - (if new-css-system - [:* - [:& header {:section :dashboard-team-members :team team}] - [:section {:class (stl/css :dashboard-container :dashboard-team-members)} - [:& team-members - {:profile profile - :team team - :members-map members-map}]]] - - ;; OLD - [:* - [:& header {:section :dashboard-team-members :team team}] - [:section.dashboard-container.dashboard-team-members - [:& team-members - {:profile profile - :team team - :members-map members-map}]]]))) + [:* + [:& header {:section :dashboard-team-members :team team}] + [:section {:class (stl/css :dashboard-container :dashboard-team-members)} + [:& team-members + {:profile profile + :team team + :members-map members-map}]]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; INVITATIONS SECTION @@ -647,8 +474,7 @@ (mf/defc invitation-role-selector {::mf/wrap-props false} [{:keys [can-invite? role status on-change]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - show? (mf/use-state false) + (let [show? (mf/use-state false) label (cond (= role :owner) (tr "labels.owner") (= role :admin) (tr "labels.admin") @@ -667,65 +493,37 @@ (keyword))] (on-change role event))))] - (if new-css-system - [:* - (if (and can-invite? (= status :pending)) - [:div {:class (stl/css :rol-selector :has-priv) - :on-click on-show} - [:span {:class (stl/css :rol-label)} label] - [:span {:class (stl/css :icon)} i/arrow-down]] - [:div {:class (stl/css :rol-selector)} - [:span {:class (stl/css :rol-label)} label]]) + [:* + (if (and can-invite? (= status :pending)) + [:div {:class (stl/css :rol-selector :has-priv) + :on-click on-show} + [:span {:class (stl/css :rol-label)} label] + [:span {:class (stl/css :icon)} i/arrow-down]] + [:div {:class (stl/css :rol-selector)} + [:span {:class (stl/css :rol-label)} label]]) - [:& dropdown {:show @show? :on-close on-hide} - [:ul {:class (stl/css :dropdown :options-dropdown)} - [:li {:data-role "admin" :on-click on-change'} (tr "labels.admin")] - [:li {:data-role "editor" :on-click on-change'} (tr "labels.editor")]]]] - - ;; OLD - [:* - (if (and can-invite? (= status :pending)) - [:div.rol-selector.has-priv {:on-click on-show} - [:span.rol-label label] - [:span.icon i/arrow-down]] - [:div.rol-selector - [:span.rol-label label]]) - - [:& dropdown {:show @show? :on-close on-hide} - [:ul.dropdown.options-dropdown - [:li {:data-role "admin" :on-click on-change'} (tr "labels.admin")] - [:li {:data-role "editor" :on-click on-change'} (tr "labels.editor")]]]]))) + [:& dropdown {:show @show? :on-close on-hide} + [:ul {:class (stl/css :dropdown :options-dropdown)} + [:li {:data-role "admin" :on-click on-change'} (tr "labels.admin")] + [:li {:data-role "editor" :on-click on-change'} (tr "labels.editor")]]]])) (mf/defc invitation-status-badge {::mf/wrap-props false} [{:keys [status]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:div - {:class (stl/css-case - :status-badge true - :expired (= status :expired) - :pending (= status :pending))} - [:span {:class (stl/css :status-label)} - (if (= status :expired) - (tr "labels.expired-invitation") - (tr "labels.pending-invitation"))]] - - ;; OLD - [:div.status-badge - {:class (dom/classnames - :expired (= status :expired) - :pending (= status :pending))} - [:span.status-label - (if (= status :expired) - (tr "labels.expired-invitation") - (tr "labels.pending-invitation"))]]))) + [:div + {:class (stl/css-case + :status-badge true + :expired (= status :expired) + :pending (= status :pending))} + [:span {:class (stl/css :status-label)} + (if (= status :expired) + (tr "labels.expired-invitation") + (tr "labels.pending-invitation"))]]) (mf/defc invitation-actions {::mf/wrap-props false} [{:keys [invitation team-id]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - show? (mf/use-state false) + (let [show? (mf/use-state false) email (:email invitation) role (:role invitation) @@ -800,32 +598,21 @@ on-hide (mf/use-fn #(reset! show? false)) on-show (mf/use-fn #(reset! show? true))] - (if new-css-system - [:* - [:span {:class (stl/css :icon) - :on-click on-show} [i/actions]] - [:& dropdown {:show @show? :on-close on-hide} - [:ul {:class (stl/css :dropdown :actions-dropdown)} - [:li {:on-click on-copy} (tr "labels.copy-invitation-link")] - [:li {:on-click on-resend} (tr "labels.resend-invitation")] - [:li {:on-click on-delete} (tr "labels.delete-invitation")]]]] - - ;; OLD - [:* - [:span.icon {:on-click on-show} [i/actions]] - [:& dropdown {:show @show? :on-close on-hide} - [:ul.dropdown.actions-dropdown - [:li {:on-click on-copy} (tr "labels.copy-invitation-link")] - [:li {:on-click on-resend} (tr "labels.resend-invitation")] - [:li {:on-click on-delete} (tr "labels.delete-invitation")]]]]))) + [:* + [:span {:class (stl/css :icon) + :on-click on-show} [i/actions]] + [:& dropdown {:show @show? :on-close on-hide} + [:ul {:class (stl/css :dropdown :actions-dropdown)} + [:li {:on-click on-copy} (tr "labels.copy-invitation-link")] + [:li {:on-click on-resend} (tr "labels.resend-invitation")] + [:li {:on-click on-delete} (tr "labels.delete-invitation")]]]])) (mf/defc invitation-row {::mf/wrap [mf/memo] ::mf/wrap-props false} [{:keys [invitation can-invite? team-id] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - expired? (:expired invitation) + (let [expired? (:expired invitation) email (:email invitation) role (:role invitation) status (if expired? :expired :pending) @@ -838,106 +625,58 @@ mdata {:on-success #(st/emit! (dd/fetch-team-invitations))}] (st/emit! (dd/update-team-invitation-role (with-meta params mdata))))))] - (if new-css-system - [:div {:class (stl/css :table-row)} - [:div {:class (stl/css :table-field :mail)} email] + [:div {:class (stl/css :table-row)} + [:div {:class (stl/css :table-field :mail)} email] - [:div {:class (stl/css :table-field :roles)} - [:& invitation-role-selector - {:can-invite? can-invite? - :role role - :status status - :on-change on-change-role}]] + [:div {:class (stl/css :table-field :roles)} + [:& invitation-role-selector + {:can-invite? can-invite? + :role role + :status status + :on-change on-change-role}]] - [:div {:class (stl/css :table-field :status)} - [:& invitation-status-badge {:status status}]] + [:div {:class (stl/css :table-field :status)} + [:& invitation-status-badge {:status status}]] - [:div {:class (stl/css :table-field :actions)} - (when can-invite? - [:& invitation-actions - {:invitation invitation - :team-id team-id}])]] - - ;; OLD - [:div.table-row - [:div.table-field.mail email] - - [:div.table-field.roles - [:& invitation-role-selector - {:can-invite? can-invite? - :role role - :status status - :on-change on-change-role}]] - - [:div.table-field.status - [:& invitation-status-badge {:status status}]] - - [:div.table-field.actions - (when can-invite? - [:& invitation-actions - {:invitation invitation - :team-id team-id}])]]))) + [:div {:class (stl/css :table-field :actions)} + (when can-invite? + [:& invitation-actions + {:invitation invitation + :team-id team-id}])]])) (mf/defc empty-invitation-table [{:keys [can-invite?] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:div {:class (stl/css :empty-invitations)} - [:span (tr "labels.no-invitations")] - (when can-invite? - [:& i18n/tr-html {:label "labels.no-invitations-hint" - :tag-name "span"}])] - ;; OLD - [:div.empty-invitations - [:span (tr "labels.no-invitations")] - (when can-invite? - [:& i18n/tr-html {:label "labels.no-invitations-hint" - :tag-name "span"}])]))) + [:div {:class (stl/css :empty-invitations)} + [:span (tr "labels.no-invitations")] + (when can-invite? + [:& i18n/tr-html {:label "labels.no-invitations-hint" + :tag-name "span"}])]) (mf/defc invitation-section [{:keys [team invitations] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - owner? (dm/get-in team [:permissions :is-owner]) + (let [owner? (dm/get-in team [:permissions :is-owner]) admin? (dm/get-in team [:permissions :is-admin]) can-invite? (or owner? admin?) team-id (:id team)] - (if new-css-system - [:div {:class (stl/css :dashboard-table :invitations)} - [:div {:class (stl/css :table-header)} - [:div {:class (stl/css :table-field :name)} (tr "labels.invitations")] - [:div {:class (stl/css :table-field :role)} (tr "labels.role")] - [:div {:class (stl/css :table-field :status)} (tr "labels.status")]] - (if (empty? invitations) - [:& empty-invitation-table {:can-invite? can-invite?}] - [:div {:class (stl/css :table-rows)} - (for [invitation invitations] - [:& invitation-row - {:key (:email invitation) - :invitation invitation - :can-invite? can-invite? - :team-id team-id}])])] - - ;; OLD - [:div.dashboard-table.invitations - [:div.table-header - [:div.table-field.name (tr "labels.invitations")] - [:div.table-field.role (tr "labels.role")] - [:div.table-field.status (tr "labels.status")]] - (if (empty? invitations) - [:& empty-invitation-table {:can-invite? can-invite?}] - [:div.table-rows - (for [invitation invitations] - [:& invitation-row - {:key (:email invitation) - :invitation invitation - :can-invite? can-invite? - :team-id team-id}])])]))) + [:div {:class (stl/css :dashboard-table :invitations)} + [:div {:class (stl/css :table-header)} + [:div {:class (stl/css :table-field :name)} (tr "labels.invitations")] + [:div {:class (stl/css :table-field :role)} (tr "labels.role")] + [:div {:class (stl/css :table-field :status)} (tr "labels.status")]] + (if (empty? invitations) + [:& empty-invitation-table {:can-invite? can-invite?}] + [:div {:class (stl/css :table-rows)} + (for [invitation invitations] + [:& invitation-row + {:key (:email invitation) + :invitation invitation + :can-invite? can-invite? + :team-id team-id}])])])) (mf/defc team-invitations-page [{:keys [team] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - invitations (mf/deref refs/dashboard-team-invitations)] + (let [invitations (mf/deref refs/dashboard-team-invitations)] (mf/with-effect [team] (dom/set-html-title @@ -949,21 +688,12 @@ (mf/with-effect [] (st/emit! (dd/fetch-team-invitations))) - (if new-css-system - [:* - [:& header {:section :dashboard-team-invitations - :team team}] - [:section {:class (stl/css :dashboard-container :dashboard-team-invitations)} - [:& invitation-section {:team team - :invitations invitations}]]] - - ;; OLD - [:* - [:& header {:section :dashboard-team-invitations - :team team}] - [:section.dashboard-container.dashboard-team-invitations - [:& invitation-section {:team team - :invitations invitations}]]]))) + [:* + [:& header {:section :dashboard-team-invitations + :team team}] + [:section {:class (stl/css :dashboard-container :dashboard-team-invitations)} + [:& invitation-section {:team team + :invitations invitations}]]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; WEBHOOKS SECTION @@ -987,8 +717,7 @@ ::mf/register-as :webhook} [{:keys [webhook] :as props}] ;; FIXME: this is a workaround because input fields do not support rendering hooks - (let [new-css-system (mf/use-ctx ctx/new-css-system) - initial (mf/use-memo (fn [] (or (some-> webhook (update :uri str)) + (let [initial (mf/use-memo (fn [] (or (some-> webhook (update :uri str)) {:is-active false :mtype "application/json"}))) form (fm/use-form :spec ::webhook-form :initial initial @@ -1052,181 +781,95 @@ (on-create-submit form))))) on-modal-close #(st/emit! (modal/hide))] - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-container)} - [:& fm/form {:form form :on-submit on-submit} - [:div {:class (stl/css :modal-header)} - (if webhook - [:h2 {:class (stl/css :modal-title)} (tr "modals.edit-webhook.title")] - [:h2 {:class (stl/css :modal-title)} (tr "modals.create-webhook.title")]) + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:& fm/form {:form form :on-submit on-submit} + [:div {:class (stl/css :modal-header)} + (if webhook + [:h2 {:class (stl/css :modal-title)} (tr "modals.edit-webhook.title")] + [:h2 {:class (stl/css :modal-title)} (tr "modals.create-webhook.title")]) - [:button {:class (stl/css :modal-close-btn) - :on-click on-modal-close} i/close-refactor]] + [:button {:class (stl/css :modal-close-btn) + :on-click on-modal-close} i/close-refactor]] - [:div {:class (stl/css :modal-content)} - [:div {:class (stl/css :fields-row)} - [:& fm/input {:type "text" - :auto-focus? true - :form form - :name :uri - :label (tr "modals.create-webhook.url.label") - :placeholder (tr "modals.create-webhook.url.placeholder")}]] - [:div {:class (stl/css :fields-row)} - [:div {:class (stl/css :select-title)} (tr "dashboard.webhooks.content-type")] - [:& fm/select {:options valid-webhook-mtypes - :default "application/json" - :name :mtype}]] - [:div {:class (stl/css :fields-row)} - [:& fm/input {:type "checkbox" - :class (stl/css :custom-input-checkbox) - :form form - :name :is-active - :label (tr "dashboard.webhooks.active")}] - [:div {:class (stl/css :hint)} (tr "dashboard.webhooks.active.explain")]]] + [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :fields-row)} + [:& fm/input {:type "text" + :auto-focus? true + :form form + :name :uri + :label (tr "modals.create-webhook.url.label") + :placeholder (tr "modals.create-webhook.url.placeholder")}]] + [:div {:class (stl/css :fields-row)} + [:div {:class (stl/css :select-title)} (tr "dashboard.webhooks.content-type")] + [:& fm/select {:options valid-webhook-mtypes + :default "application/json" + :name :mtype}]] + [:div {:class (stl/css :fields-row)} + [:& fm/input {:type "checkbox" + :class (stl/css :custom-input-checkbox) + :form form + :name :is-active + :label (tr "dashboard.webhooks.active")}] + [:div {:class (stl/css :hint)} (tr "dashboard.webhooks.active.explain")]]] - [:div {:class (stl/css :modal-footer)} - [:div {:class (stl/css :action-buttons)} - [:input {:class (stl/css :cancel-button) - :type "button" - :value (tr "labels.cancel") - :on-click #(modal/hide!)}] - [:> fm/submit-button* - {:label (if webhook - (tr "modals.edit-webhook.submit-label") - (tr "modals.create-webhook.submit-label"))}]]]]]] - - ;; OLD - [:div.modal-overlay - [:div.modal-container.webhooks-modal - [:& fm/form {:form form :on-submit on-submit} - - [:div.modal-header - [:div.modal-header-title - (if webhook - [:h2 (tr "modals.edit-webhook.title")] - [:h2 (tr "modals.create-webhook.title")])] - - [:div.modal-close-button - {:on-click #(st/emit! (modal/hide))} i/close]] - - [:div.modal-content.generic-form - [:div.fields-container - [:div.fields-row - [:& fm/input {:type "text" - :auto-focus? true - :form form - :name :uri - :label (tr "modals.create-webhook.url.label") - :placeholder (tr "modals.create-webhook.url.placeholder")}]] - - [:div.fields-row - [:& fm/select {:options valid-webhook-mtypes - :label (tr "dashboard.webhooks.content-type") - :default "application/json" - :name :mtype}]]] - [:div.fields-row - [:div.input-checkbox.check-primary - [:& fm/input {:type "checkbox" - :form form - :name :is-active - :label (tr "dashboard.webhooks.active")}]] - [:div.explain (tr "dashboard.webhooks.active.explain")]]] - - [:div.modal-footer - [:div.action-buttons - [:input.cancel-button - {:type "button" - :value (tr "labels.cancel") - :on-click #(modal/hide!)}] - [:> fm/submit-button* - {:label (if webhook - (tr "modals.edit-webhook.submit-label") - (tr "modals.create-webhook.submit-label"))}]]]]]]))) + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:input {:class (stl/css :cancel-button) + :type "button" + :value (tr "labels.cancel") + :on-click #(modal/hide!)}] + [:> fm/submit-button* + {:label (if webhook + (tr "modals.edit-webhook.submit-label") + (tr "modals.create-webhook.submit-label"))}]]]]]])) (mf/defc webhooks-hero {::mf/wrap-props false} [] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:div {:class (stl/css :webhooks-hero-container)} - [:div {:class (stl/css :webhooks-hero)} - [:div {:class (stl/css :desc)} - [:h2 (tr "labels.webhooks")] - [:& i18n/tr-html {:label "dashboard.webhooks.description"}]] + [:div {:class (stl/css :webhooks-hero-container)} + [:div {:class (stl/css :webhooks-hero)} + [:div {:class (stl/css :desc)} + [:h2 (tr "labels.webhooks")] + [:& i18n/tr-html {:label "dashboard.webhooks.description"}]] - [:div - {:class (stl/css :btn-primary) - :on-click #(st/emit! (modal/show :webhook {}))} - [:span (tr "dashboard.webhooks.create")]]]] - - ;; OLD - [:div.webhooks-hero-container - [:div.webhooks-hero - [:div.desc - [:h2 (tr "labels.webhooks")] - [:& i18n/tr-html {:label "dashboard.webhooks.description"}]] - - [:div.btn-primary - {:on-click #(st/emit! (modal/show :webhook {}))} - [:span (tr "dashboard.webhooks.create")]]]]))) + [:div + {:class (stl/css :btn-primary) + :on-click #(st/emit! (modal/show :webhook {}))} + [:span (tr "dashboard.webhooks.create")]]]]) (mf/defc webhook-actions {::mf/wrap-props false} [{:keys [on-edit on-delete]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - - show? (mf/use-state false) + (let [show? (mf/use-state false) on-show (mf/use-fn #(reset! show? true)) on-hide (mf/use-fn #(reset! show? false))] - - (if new-css-system - [:* - [:span {:class (stl/css :icon) - :on-click on-show} [i/actions]] - [:& dropdown {:show @show? :on-close on-hide} - [:ul {:class (stl/css :dropdown :actions-dropdown)} - [:li {:on-click on-edit} (tr "labels.edit")] - [:li {:on-click on-delete} (tr "labels.delete")]]]] - - ;; OLD - [:* - [:span.icon {:on-click on-show} [i/actions]] - [:& dropdown {:show @show? :on-close on-hide} - [:ul.dropdown.actions-dropdown - [:li {:on-click on-edit} (tr "labels.edit")] - [:li {:on-click on-delete} (tr "labels.delete")]]]]))) + + [:* + [:span {:class (stl/css :icon) + :on-click on-show} [i/actions]] + [:& dropdown {:show @show? :on-close on-hide} + [:ul {:class (stl/css :dropdown :actions-dropdown)} + [:li {:on-click on-edit} (tr "labels.edit")] + [:li {:on-click on-delete} (tr "labels.delete")]]]])) (mf/defc last-delivery-icon {::mf/wrap-props false} [{:keys [success? text]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:div {:class (stl/css :last-delivery-icon)} - [:div {:class (stl/css :tooltip)} - [:div {:class (stl/css :label)} text] - [:div {:class (stl/css :arrow-down)}]] - (if success? - [:span {:class (stl/css :icon :success)} i/msg-success] - [:span {:class (stl/css :icon :failure)} i/msg-warning])] - - ;; OLD - [:div.last-delivery-icon - [:div.tooltip - [:div.label text] - [:div.arrow-down]] - (if success? - [:span.icon.success i/msg-success] - [:span.icon.failure i/msg-warning])]))) + [:div {:class (stl/css :last-delivery-icon)} + [:div {:class (stl/css :tooltip)} + [:div {:class (stl/css :label)} text] + [:div {:class (stl/css :arrow-down)}]] + (if success? + [:span {:class (stl/css :icon :success)} i/msg-success] + [:span {:class (stl/css :icon :failure)} i/msg-warning])]) (mf/defc webhook-item {::mf/wrap [mf/memo]} [{:keys [webhook] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - - error-code (:error-code webhook) + (let [error-code (:error-code webhook) id (:id webhook) on-edit @@ -1265,64 +908,36 @@ (str/starts-with? error-code "unexpected-status") (dm/str " " (tr "errors.webhooks.unexpected-status" (extract-status error-code))))))] - - (if new-css-system - [:div {:class (stl/css :table-row)} - [:div {:class (stl/css :table-field :last-delivery)} - [:div {:class (stl/css :icon-container)} - [:& last-delivery-icon - {:success? (nil? error-code) - :text last-delivery-text}]]] - [:div {:class (stl/css :table-field :uri)} - [:div (dm/str (:uri webhook))]] - [:div {:class (stl/css :table-field :active)} - [:div (if (:is-active webhook) - (tr "labels.active") - (tr "labels.inactive"))]] - [:div {:class (stl/css :table-field :actions)} - [:& webhook-actions - {:on-edit on-edit - :on-delete on-delete}]]] - ;; OLD - [:div.table-row - [:div.table-field.last-delivery - [:div.icon-container - [:& last-delivery-icon - {:success? (nil? error-code) - :text last-delivery-text}]]] - [:div.table-field.uri - [:div (dm/str (:uri webhook))]] - [:div.table-field.active - [:div (if (:is-active webhook) - (tr "labels.active") - (tr "labels.inactive"))]] - [:div.table-field.actions - [:& webhook-actions - {:on-edit on-edit - :on-delete on-delete}]]]))) + [:div {:class (stl/css :table-row)} + [:div {:class (stl/css :table-field :last-delivery)} + [:div {:class (stl/css :icon-container)} + [:& last-delivery-icon + {:success? (nil? error-code) + :text last-delivery-text}]]] + [:div {:class (stl/css :table-field :uri)} + [:div (dm/str (:uri webhook))]] + [:div {:class (stl/css :table-field :active)} + [:div (if (:is-active webhook) + (tr "labels.active") + (tr "labels.inactive"))]] + [:div {:class (stl/css :table-field :actions)} + [:& webhook-actions + {:on-edit on-edit + :on-delete on-delete}]]])) (mf/defc webhooks-list {::mf/wrap-props false} [{:keys [webhooks]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:div {:class (stl/css :dashboard-table)} - [:div {:class (stl/css :table-rows)} - (for [webhook webhooks] - [:& webhook-item {:webhook webhook :key (:id webhook)}])]] - - ;; OLD - [:div.dashboard-table - [:div.table-rows - (for [webhook webhooks] - [:& webhook-item {:webhook webhook :key (:id webhook)}])]]))) + [:div {:class (stl/css :dashboard-table)} + [:div {:class (stl/css :table-rows)} + (for [webhook webhooks] + [:& webhook-item {:webhook webhook :key (:id webhook)}])]]) (mf/defc team-webhooks-page {::mf/wrap-props false} [{:keys [team]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - webhooks (mf/deref refs/dashboard-team-webhooks)] + (let [webhooks (mf/deref refs/dashboard-team-webhooks)] (mf/with-effect [team] (dom/set-html-title @@ -1334,29 +949,16 @@ (mf/with-effect [team] (st/emit! (dd/fetch-team-webhooks))) - (if new-css-system + [:* + [:& header {:team team :section :dashboard-team-webhooks}] + [:section {:class (stl/css :dashboard-container :dashboard-team-webhooks)} [:* - [:& header {:team team :section :dashboard-team-webhooks}] - [:section {:class (stl/css :dashboard-container :dashboard-team-webhooks)} - [:* - [:& webhooks-hero] - (if (empty? webhooks) - [:div {:class (stl/css :webhooks-empty)} - [:div (tr "dashboard.webhooks.empty.no-webhooks")] - [:div (tr "dashboard.webhooks.empty.add-one")]] - [:& webhooks-list {:webhooks webhooks}])]]] - - ;; OLD - [:* - [:& header {:team team :section :dashboard-team-webhooks}] - [:section.dashboard-container.dashboard-team-webhooks - [:* - [:& webhooks-hero] - (if (empty? webhooks) - [:div.webhooks-empty - [:div (tr "dashboard.webhooks.empty.no-webhooks")] - [:div (tr "dashboard.webhooks.empty.add-one")]] - [:& webhooks-list {:webhooks webhooks}])]]]))) + [:& webhooks-hero] + (if (empty? webhooks) + [:div {:class (stl/css :webhooks-empty)} + [:div (tr "dashboard.webhooks.empty.no-webhooks")] + [:div (tr "dashboard.webhooks.empty.add-one")]] + [:& webhooks-list {:webhooks webhooks}])]]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SETTINGS SECTION @@ -1365,8 +967,7 @@ (mf/defc team-settings-page {::mf/wrap-props false} [{:keys [team]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - finput (mf/use-ref) + (let [finput (mf/use-ref) members-map (mf/deref refs/dashboard-team-members) owner (->> (vals members-map) @@ -1398,77 +999,40 @@ (st/emit! (dd/fetch-team-members team-id) (dd/fetch-team-stats team-id)))) - (if new-css-system - [:* - [:& header {:section :dashboard-team-settings :team team}] - [:section {:class (stl/css :dashboard-container :dashboard-team-settings)} - [:div {:class (stl/css :team-settings)} - [:div {:class (stl/css :horizontal-blocks)} - [:div {:class (stl/css :block :info-block)} - [:div {:class (stl/css :label)} (tr "dashboard.team-info")] - [:div {:class (stl/css :name)} (:name team)] - [:div {:class (stl/css :icon)} - (when can-edit? - [:span {:class (stl/css :update-overlay) - :on-click on-image-click} i/image]) - [:img {:src (cfg/resolve-team-photo-url team)}] - (when can-edit? - [:& file-uploader {:accept "image/jpeg,image/png" - :multi false - :ref finput - :on-selected on-file-selected}])]] + [:* + [:& header {:section :dashboard-team-settings :team team}] + [:section {:class (stl/css :dashboard-container :dashboard-team-settings)} + [:div {:class (stl/css :team-settings)} + [:div {:class (stl/css :horizontal-blocks)} + [:div {:class (stl/css :block :info-block)} + [:div {:class (stl/css :label)} (tr "dashboard.team-info")] + [:div {:class (stl/css :name)} (:name team)] + [:div {:class (stl/css :icon)} + (when can-edit? + [:span {:class (stl/css :update-overlay) + :on-click on-image-click} i/image]) + [:img {:src (cfg/resolve-team-photo-url team)}] + (when can-edit? + [:& file-uploader {:accept "image/jpeg,image/png" + :multi false + :ref finput + :on-selected on-file-selected}])]] - [:div {:class (stl/css :block :owner-block)} - [:div {:class (stl/css :label)} (tr "dashboard.team-members")] - [:div {:class (stl/css :owner)} - [:span {:class (stl/css :icon)} [:img {:src (cfg/resolve-profile-photo-url owner)}]] - [:span {:class (stl/css :text)} (str (:name owner) " (" (tr "labels.owner") ")")]] - [:div {:class (stl/css :summary)} - [:span {:class (stl/css :icon)} i/user] - [:span {:class (stl/css :text)} (tr "dashboard.num-of-members" (count members-map))]]] + [:div {:class (stl/css :block :owner-block)} + [:div {:class (stl/css :label)} (tr "dashboard.team-members")] + [:div {:class (stl/css :owner)} + [:span {:class (stl/css :icon)} [:img {:src (cfg/resolve-profile-photo-url owner)}]] + [:span {:class (stl/css :text)} (str (:name owner) " (" (tr "labels.owner") ")")]] + [:div {:class (stl/css :summary)} + [:span {:class (stl/css :icon)} i/user] + [:span {:class (stl/css :text)} (tr "dashboard.num-of-members" (count members-map))]]] - [:div {:class (stl/css :block :stats-block)} - [:div {:class (stl/css :label)} (tr "dashboard.team-projects")] - [:div {:class (stl/css :projects)} - [:span {:class (stl/css :icon)} i/folder] - [:span {:class (stl/css :text)} (tr "labels.num-of-projects" (i18n/c (dec (:projects stats))))]] - [:div {:class (stl/css :files)} - [:span {:class (stl/css :icon)} i/file-html] - [:span {:class (stl/css :text)} (tr "labels.num-of-files" (i18n/c (:files stats)))]]]]]]] - - [:* - [:& header {:section :dashboard-team-settings :team team}] - [:section.dashboard-container.dashboard-team-settings - [:div.team-settings - [:div.horizontal-blocks - [:div.block.info-block - [:div.label (tr "dashboard.team-info")] - [:div.name (:name team)] - [:div.icon - (when can-edit? - [:span.update-overlay {:on-click on-image-click} i/image]) - [:img {:src (cfg/resolve-team-photo-url team)}] - (when can-edit? - [:& file-uploader {:accept "image/jpeg,image/png" - :multi false - :ref finput - :on-selected on-file-selected}])]] - - [:div.block.owner-block - [:div.label (tr "dashboard.team-members")] - [:div.owner - [:span.icon [:img {:src (cfg/resolve-profile-photo-url owner)}]] - [:span.text (str (:name owner) " (" (tr "labels.owner") ")")]] - [:div.summary - [:span.icon i/user] - [:span.text (tr "dashboard.num-of-members" (count members-map))]]] - - [:div.block.stats-block - [:div.label (tr "dashboard.team-projects")] - [:div.projects - [:span.icon i/folder] - [:span.text (tr "labels.num-of-projects" (i18n/c (dec (:projects stats))))]] - [:div.files - [:span.icon i/file-html] - [:span.text (tr "labels.num-of-files" (i18n/c (:files stats)))]]]]]]]))) + [:div {:class (stl/css :block :stats-block)} + [:div {:class (stl/css :label)} (tr "dashboard.team-projects")] + [:div {:class (stl/css :projects)} + [:span {:class (stl/css :icon)} i/folder] + [:span {:class (stl/css :text)} (tr "labels.num-of-projects" (i18n/c (dec (:projects stats))))]] + [:div {:class (stl/css :files)} + [:span {:class (stl/css :icon)} i/file-html] + [:span {:class (stl/css :text)} (tr "labels.num-of-files" (i18n/c (:files stats)))]]]]]]])) diff --git a/frontend/src/app/main/ui/dashboard/team.scss b/frontend/src/app/main/ui/dashboard/team.scss index 83c5eff7ca..d593616b2d 100644 --- a/frontend/src/app/main/ui/dashboard/team.scss +++ b/frontend/src/app/main/ui/dashboard/team.scss @@ -102,7 +102,7 @@ } } .btn-primary { - @extends .button-primary; + @extend .button-primary; height: $s-32; } } @@ -650,6 +650,7 @@ color: var(--modal-title-foreground-color); } } + .invitation-row { margin-top: $s-8; margin-bottom: $s-24; @@ -671,48 +672,56 @@ .modal-overlay { @extend .modal-overlay-base; - .modal-container { - @extend .modal-container-base; - border: $s-1 solid var(--modal-border-color); - .modal-header { - margin-bottom: $s-24; - .modal-title { - @include tabTitleTipography; - color: var(--modal-title-foreground-color); - } - .modal-close-btn { - @extend .modal-close-btn-base; - } - } +} - .modal-content { - @include flexColumn; - gap: $s-24; - @include titleTipography; - margin-bottom: $s-24; +.modal-container { + @extend .modal-container-base; + border: $s-1 solid var(--modal-border-color); +} - .fields-row { - @include flexColumn; - .select-title { - @include titleTipography; - color: var(--modal-title-foreground-color); - } - .custom-input-checkbox { - align-items: flex-start; - } - } - } +.modal-header { + margin-bottom: $s-24; +} - .modal-footer { - .action-buttons { - @extend .modal-action-btns; - button { - @extend .modal-accept-btn; - } - .cancel-button { - @extend .modal-cancel-btn; - } - } - } +.modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); +} + +.modal-close-btn { + @extend .modal-close-btn-base; +} + +.modal-content { + @include flexColumn; + gap: $s-24; + @include titleTipography; + margin-bottom: $s-24; +} + +.fields-row { + @include flexColumn; +} + +.select-title { + @include titleTipography; + color: var(--modal-title-foreground-color); +} + +.custom-input-checkbox { + align-items: flex-start; +} + +.hint { + color: var(--modal-text-foreground-color); +} + +.action-buttons { + @extend .modal-action-btns; + button { + @extend .modal-accept-btn; + } + .cancel-button { + @extend .modal-cancel-btn; } } diff --git a/frontend/src/app/main/ui/dashboard/team_form.cljs b/frontend/src/app/main/ui/dashboard/team_form.cljs index 08a8d1df89..258459beea 100644 --- a/frontend/src/app/main/ui/dashboard/team_form.cljs +++ b/frontend/src/app/main/ui/dashboard/team_form.cljs @@ -13,7 +13,6 @@ [app.main.data.modal :as modal] [app.main.store :as st] [app.main.ui.components.forms :as fm] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [tr]] [app.util.router :as rt] @@ -70,73 +69,42 @@ (mf/defc team-form-modal {::mf/register modal/components ::mf/register-as :team-form} [{:keys [team] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - initial (mf/use-memo (fn [] (or team {}))) + (let [initial (mf/use-memo (fn [] (or team {}))) form (fm/use-form :spec ::team-form :validators [(fm/validate-not-empty :name (tr "auth.name.not-all-space")) (fm/validate-length :name fm/max-length-allowed (tr "auth.name.too-long"))] :initial initial) on-close #(st/emit! (modal/hide))] - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-container)} - [:& fm/form {:form form :on-submit on-submit} + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:& fm/form {:form form :on-submit on-submit} - [:div {:class (stl/css :modal-header)} - (if team - [:h2 {:class (stl/css :modal-title)} - (tr "labels.rename-team")] - [:h2 {:class (stl/css :modal-title)} - (tr "labels.create-team")]) + [:div {:class (stl/css :modal-header)} + (if team + [:h2 {:class (stl/css :modal-title)} + (tr "labels.rename-team")] + [:h2 {:class (stl/css :modal-title)} + (tr "labels.create-team")]) - [:button {:class (stl/css :modal-close-btn) - :on-click on-close} i/close-refactor]] + [:button {:class (stl/css :modal-close-btn) + :on-click on-close} i/close-refactor]] - [:div {:class (stl/css :modal-content)} - [:& fm/input {:type "text" - :auto-focus? true - :class (stl/css :group-name-input) - :form form - :name :name - :placeholder "E.g. Design" - :label (tr "labels.create-team.placeholder")}]] + [:div {:class (stl/css :modal-content)} + [:& fm/input {:type "text" + :auto-focus? true + :class (stl/css :group-name-input) + :form form + :name :name + :placeholder "E.g. Design" + :label (tr "labels.create-team.placeholder")}]] - [:div {:class (stl/css :modal-footer)} - [:div {:class (stl/css :action-buttons)} - [:> fm/submit-button* - {:label (if team - (tr "labels.update-team") - (tr "labels.create-team")) - :className (stl/css :accept-btn)}]]]]]] - - - - [:div.modal-overlay - [:div.modal-container.team-form-modal - [:& fm/form {:form form :on-submit on-submit} - - [:div.modal-header - [:div.modal-header-title - (if team - [:h2 (tr "labels.rename-team")] - [:h2 (tr "labels.create-team")])] - - [:div.modal-close-button - {:on-click #(st/emit! (modal/hide))} i/close]] - - [:div.modal-content.generic-form - [:& fm/input {:type "text" - :auto-focus? true - :form form - :name :name - :label (tr "labels.create-team.placeholder")}]] - - [:div.modal-footer - [:div.action-buttons - [:> fm/submit-button* - {:label (if team - (tr "labels.update-team") - (tr "labels.create-team"))}]]]]]]))) + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:> fm/submit-button* + {:label (if team + (tr "labels.update-team") + (tr "labels.create-team")) + :className (stl/css :accept-btn)}]]]]]])) diff --git a/frontend/src/app/main/ui/dashboard/templates.cljs b/frontend/src/app/main/ui/dashboard/templates.cljs index c733d4f3e5..8b61fc95b8 100644 --- a/frontend/src/app/main/ui/dashboard/templates.cljs +++ b/frontend/src/app/main/ui/dashboard/templates.cljs @@ -16,7 +16,6 @@ [app.main.data.users :as du] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :refer [tr]] @@ -59,8 +58,7 @@ (mf/defc title {::mf/wrap-props false} [{:keys [collapsed]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - on-click + (let [on-click (mf/use-fn (mf/deps collapsed) (fn [_event] @@ -76,27 +74,17 @@ (dom/prevent-default event) (on-click event))))] - (if new-css-system - [:div {:class (stl/css :title)} - [:button {:tab-index "0" - :on-click on-click - :on-key-down on-key-down} - [:span (tr "dashboard.libraries-and-templates")] - [:span {:class (stl/css :icon)} (if ^boolean collapsed i/arrow-up i/arrow-down)]]] - - ;; OLD - [:div.title - [:button {:tab-index "0" - :on-click on-click - :on-key-down on-key-down} - [:span (tr "dashboard.libraries-and-templates")] - [:span.icon (if ^boolean collapsed i/arrow-up i/arrow-down)]]]))) + [:div {:class (stl/css :title)} + [:button {:tab-index "0" + :on-click on-click + :on-key-down on-key-down} + [:span (tr "dashboard.libraries-and-templates")] + [:span {:class (stl/css :icon)} (if ^boolean collapsed i/arrow-up i/arrow-down)]]])) (mf/defc card-item {::mf/wrap-props false} [{:keys [item index is-visible collapsed on-import]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - id (dm/str "card-container-" index) + (let [id (dm/str "card-container-" index) thb (assoc cf/public-uri :path (dm/str "/images/thumbnails/template-" (:id item) ".jpg")) on-click @@ -113,41 +101,24 @@ (dom/stop-propagation event) (on-import item event))))] - (if new-css-system - [:a - {:class (stl/css :card-container) - :tab-index (if (or (not is-visible) collapsed) "-1" "0") - :id id - :data-index index - :on-click on-click - :on-key-down on-key-down} - [:div {:class (stl/css :template-card)} - [:div {:class (stl/css :img-container)} - [:img {:src (dm/str thb) - :alt (:name item)}]] - [:div {:class (stl/css :card-name)} - [:span (:name item)] - [:span {:class (stl/css :icon)} i/download]]]] - - ;; OLD - [:a.card-container - {:tab-index (if (or (not is-visible) collapsed) "-1" "0") - :id id - :data-index index - :on-click on-click - :on-key-down on-key-down} - [:div.template-card - [:div.img-container - [:img {:src (dm/str thb) - :alt (:name item)}]] - [:div.card-name [:span (:name item)] - [:span.icon i/download]]]]))) + [:a {:class (stl/css :card-container) + :tab-index (if (or (not is-visible) collapsed) "-1" "0") + :id id + :data-index index + :on-click on-click + :on-key-down on-key-down} + [:div {:class (stl/css :template-card)} + [:div {:class (stl/css :img-container)} + [:img {:src (dm/str thb) + :alt (:name item)}]] + [:div {:class (stl/css :card-name)} + [:span (:name item)] + [:span {:class (stl/css :icon)} i/download]]]])) (mf/defc card-item-link {::mf/wrap-props false} [{:keys [total is-visible collapsed section]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - id (dm/str "card-container-" total) + (let [id (dm/str "card-container-" total) on-click (mf/use-fn @@ -165,39 +136,23 @@ (dom/stop-propagation event) (on-click event))))] - (if new-css-system - [:div {:class (stl/css :card-container)} - [:div {:class (stl/css :template-card)} - [:div {:class (stl/css :img-container)} - [:a {:id id - :tab-index (if (or (not is-visible) collapsed) "-1" "0") - :href "https://penpot.app/libraries-templates.html" - :target "_blank" - :on-click on-click - :on-key-down on-key-down} - [:div {:class (stl/css :template-link)} - [:div {:class (stl/css :template-link-title)} (tr "dashboard.libraries-and-templates")] - [:div {:class (stl/css :template-link-text)} (tr "dashboard.libraries-and-templates.explore")]]]]]] - - ;; OLD - [:div.card-container - [:div.template-card - [:div.img-container - [:a {:id id - :tab-index (if (or (not is-visible) collapsed) "-1" "0") - :href "https://penpot.app/libraries-templates.html" - :target "_blank" - :on-click on-click - :on-key-down on-key-down} - [:div.template-link - [:div.template-link-title (tr "dashboard.libraries-and-templates")] - [:div.template-link-text (tr "dashboard.libraries-and-templates.explore")]]]]]]))) + [:div {:class (stl/css :card-container)} + [:div {:class (stl/css :template-card)} + [:div {:class (stl/css :img-container)} + [:a {:id id + :tab-index (if (or (not is-visible) collapsed) "-1" "0") + :href "https://penpot.app/libraries-templates.html" + :target "_blank" + :on-click on-click + :on-key-down on-key-down} + [:div {:class (stl/css :template-link)} + [:div {:class (stl/css :template-link-title)} (tr "dashboard.libraries-and-templates")] + [:div {:class (stl/css :template-link-text)} (tr "dashboard.libraries-and-templates.explore")]]]]]])) (mf/defc templates-section {::mf/wrap-props false} [{:keys [default-project-id profile project-id team-id content-width]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - templates (mf/deref builtin-templates) + (let [templates (mf/deref builtin-templates) templates (mf/with-memo [templates] (filterv #(not= (:id %) "tutorial-for-beginners") templates)) @@ -273,94 +228,50 @@ (mf/use-fn (mf/deps default-project-id project-id section templates team-id) (fn [template _event] - (import-template! template team-id project-id default-project-id section))) - - ] + (import-template! template team-id project-id default-project-id section)))] (mf/with-effect [profile collapsed] (when (and profile (not collapsed)) (st/emit! (dd/fetch-builtin-templates)))) - (if new-css-system - [:div - {:class (stl/css-case :dashboard-templates-section true - :collapsed collapsed)} - [:& title {:collapsed collapsed}] + [:div {:class (stl/css-case :dashboard-templates-section true + :collapsed collapsed)} + [:& title {:collapsed collapsed}] - [:div {:class (stl/css :content) - :ref content-ref - :style {:left card-offset - :width (dm/str container-size "px")}} + [:div {:class (stl/css :content) + :ref content-ref + :style {:left card-offset + :width (dm/str container-size "px")}} - (for [index (range (count templates))] - [:& card-item - {:on-import on-import-template - :item (nth templates index) - :index index - :key index - :is-visible (and (>= index first-card) - (<= index last-card)) - :collapsed collapsed}]) + (for [index (range (count templates))] + [:& card-item + {:on-import on-import-template + :item (nth templates index) + :index index + :key index + :is-visible (and (>= index first-card) + (<= index last-card)) + :collapsed collapsed}]) - [:& card-item-link - {:is-visible (and (>= total first-card) (<= total last-card)) - :collapsed collapsed - :section section - :total total}]] + [:& card-item-link + {:is-visible (and (>= total first-card) (<= total last-card)) + :collapsed collapsed + :section section + :total total}]] - (when (< card-offset 0) - [:button - {:class (stl/css :button :left) - :tab-index (if ^boolean collapsed "-1" "0") - :on-click on-move-left - :on-key-down on-move-left-key-down} - i/go-prev]) + (when (< card-offset 0) + [:button + {:class (stl/css :button :left) + :tab-index (if ^boolean collapsed "-1" "0") + :on-click on-move-left + :on-key-down on-move-left-key-down} + i/go-prev]) - (when more-cards - [:button - {:class (stl/css :button :right) - :tab-index (if collapsed "-1" "0") - :on-click on-move-right - :aria-label (tr "labels.next") - :on-key-down on-move-right-key-down} - i/go-next])] - - ;; OLD - [:div.dashboard-templates-section - {:class (when ^boolean collapsed "collapsed")} - [:& title {:collapsed collapsed}] - - [:div.content {:ref content-ref - :style {:left card-offset - :width (dm/str container-size "px")}} - - (for [index (range (count templates))] - [:& card-item - {:on-import on-import-template - :item (nth templates index) - :index index - :key index - :is-visible (and (>= index first-card) - (<= index last-card)) - :collapsed collapsed}]) - - [:& card-item-link - {:is-visible (and (>= total first-card) (<= total last-card)) - :collapsed collapsed - :section section - :total total}]] - - (when (< card-offset 0) - [:button.button.left - {:tab-index (if ^boolean collapsed "-1" "0") - :on-click on-move-left - :on-key-down on-move-left-key-down} - i/go-prev]) - - (when more-cards - [:button.button.right - {:tab-index (if collapsed "-1" "0") - :on-click on-move-right - :aria-label (tr "labels.next") - :on-key-down on-move-right-key-down} - i/go-next])]))) + (when more-cards + [:button + {:class (stl/css :button :right) + :tab-index (if collapsed "-1" "0") + :on-click on-move-right + :aria-label (tr "labels.next") + :on-key-down on-move-right-key-down} + i/go-next])])) diff --git a/frontend/src/app/main/ui/delete_shared.cljs b/frontend/src/app/main/ui/delete_shared.cljs index a1e341f45a..f51f62df1e 100644 --- a/frontend/src/app/main/ui/delete_shared.cljs +++ b/frontend/src/app/main/ui/delete_shared.cljs @@ -11,7 +11,6 @@ [app.main.data.modal :as modal] [app.main.repo :as rp] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -27,8 +26,7 @@ ::mf/register-as :delete-shared-libraries ::mf/wrap-props false} [{:keys [ids on-accept on-cancel accept-style origin count-libraries]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - references* (mf/use-state {}) + (let [references* (mf/use-state {}) references (deref references*) on-accept (or on-accept noop) @@ -96,91 +94,43 @@ (let [key (events/listen js/document "keydown" on-keydown)] (partial events/unlistenByKey key)))) - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-container)} - [:div {:class (stl/css :modal-header)} - [:h2 {:class (stl/css :modal-title)} title] - [:button {:class (stl/css :modal-close-btn) - :on-click cancel-fn} i/close-refactor]] + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} title] + [:button {:class (stl/css :modal-close-btn) + :on-click cancel-fn} i/close-refactor]] - [:div {:class (stl/css :modal-content)} - (when (and (string? subtitle) (not= subtitle "")) - [:h3 {:class (stl/css :modal-subtitle)} subtitle]) - (when (not= 0 count-libraries) - (if (pos? (count references)) - [:* - [:div - (when (and (string? scd-msg) (not= scd-msg "")) - [:h3 {:class (stl/css :modal-scd-msg)} scd-msg]) - [:ul {:class (stl/css :element-list)} - (for [[file-id file-name] references] - [:li {:class (stl/css :list-item) - :key (dm/str file-id)} - [:span "- " file-name]])]] - (when (and (string? hint) (not= hint "")) - [:h3 {:class (stl/css :modal-hint)}hint])] - [:* - [:h3 {:class (stl/css :modal-msg)} no-files-msg]]))] + [:div {:class (stl/css :modal-content)} + (when (and (string? subtitle) (not= subtitle "")) + [:h3 {:class (stl/css :modal-subtitle)} subtitle]) + (when (not= 0 count-libraries) + (if (pos? (count references)) + [:* + [:div + (when (and (string? scd-msg) (not= scd-msg "")) + [:h3 {:class (stl/css :modal-scd-msg)} scd-msg]) + [:ul {:class (stl/css :element-list)} + (for [[file-id file-name] references] + [:li {:class (stl/css :list-item) + :key (dm/str file-id)} + [:span "- " file-name]])]] + (when (and (string? hint) (not= hint "")) + [:h3 {:class (stl/css :modal-hint)} hint])] + [:* + [:h3 {:class (stl/css :modal-msg)} no-files-msg]]))] - [:div {:class (stl/css :modal-footer)} - [:div {:class (stl/css :action-buttons)} - (when-not (= cancel-label :omit) - [:input {:class (stl/css :cancel-button) - :type "button" - :value cancel-label - :on-click cancel-fn}]) - - [:input {:class (stl/css-case :accept-btn true - :danger (= accept-style :danger) - :primary (= accept-style :primary)) + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + (when-not (= cancel-label :omit) + [:input {:class (stl/css :cancel-button) :type "button" - :value accept-label - :on-click accept-fn}]]]]] + :value cancel-label + :on-click cancel-fn}]) - - [:div.modal-overlay - [:div.modal-container.confirm-dialog - [:div.modal-header - [:div.modal-header-title - [:h2 title]] - [:div.modal-close-button - {:on-click cancel-fn} i/close]] - - [:div.modal-content.delete-shared - (when (and (string? subtitle) (not= subtitle "")) - [:h3 subtitle]) - (when (not= 0 count-libraries) - (if (pos? (count references)) - [:* - [:div - (when (and (string? scd-msg) (not= scd-msg "")) - [:h3 scd-msg]) - [:ul.file-list - (for [[file-id file-name] references] - [:li.modal-item-element - {:key (dm/str file-id)} - [:span "- " file-name]])]] - (when (and (string? hint) (not= hint "")) - [:h3 hint])] - [:* - [:h3 no-files-msg]]))] - - [:div.modal-footer - [:div.action-buttons - (when-not (= cancel-label :omit) - [:input.cancel-button - {:type "button" - :value cancel-label - :on-click cancel-fn}]) - - [:input.accept-button - {:class (dom/classnames - :danger (= accept-style :danger) - :primary (= accept-style :primary)) - :type "button" - :value accept-label - :on-click accept-fn}]]]]] - ) - - )) + [:input {:class (stl/css-case :accept-btn true + :danger (= accept-style :danger) + :primary (= accept-style :primary)) + :type "button" + :value accept-label + :on-click accept-fn}]]]]])) diff --git a/frontend/src/app/main/ui/delete_shared.scss b/frontend/src/app/main/ui/delete_shared.scss index caa20bdcbb..84ec2e9b54 100644 --- a/frontend/src/app/main/ui/delete_shared.scss +++ b/frontend/src/app/main/ui/delete_shared.scss @@ -11,45 +11,53 @@ &.transparent { background-color: transparent; } - .modal-container { - @extend .modal-container-base; - .modal-header { - margin-bottom: $s-24; - .modal-title { - @include tabTitleTipography; - color: var(--modal-title-foreground-color); - } - .modal-close-btn { - @extend .modal-close-btn-base; - } - } - .modal-content { - @include titleTipography; - margin-bottom: $s-24; - .modal-hint { - @extend .modal-hint-base; - } - .element-list { - @include titleTipography; - .list-item { - @include titleTipography; - } - } - } - .modal-footer { - .action-buttons { - @extend .modal-action-btns; - .cancel-button { - @extend .modal-cancel-btn; - } - .accept-btn { - @extend .modal-accept-btn; - &.danger { - @extend .modal-danger-btn; - } - } - } - } +} + +.modal-container { + @extend .modal-container-base; +} + +.modal-header { + margin-bottom: $s-24; +} + +.modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); +} + +.modal-close-btn { + @extend .modal-close-btn-base; +} + +.modal-content { + @include titleTipography; + margin-bottom: $s-24; +} + +.modal-hint { + @extend .modal-hint-base; +} + +.element-list { + @include titleTipography; + .list-item { + @include titleTipography; + } +} + +.action-buttons { + @extend .modal-action-btns; +} + +.cancel-button { + @extend .modal-cancel-btn; +} + +.accept-btn { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; } } diff --git a/frontend/src/app/main/ui/messages.cljs b/frontend/src/app/main/ui/messages.cljs index f04919fc67..489eae2a60 100644 --- a/frontend/src/app/main/ui/messages.cljs +++ b/frontend/src/app/main/ui/messages.cljs @@ -14,96 +14,52 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.link-button :as lb] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] - [app.util.dom :as dom] [rumext.v2 :as mf])) (mf/defc banner [{:keys [type position status controls content links actions on-close data-test role] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:div {:class (stl/css-case :banner true - :warning (= type :warning) - :error (= type :error) - :success (= type :success) - :info (= type :info) - :fixed (= position :fixed) - :floating (= position :floating) - :inline (= position :inline) - :hide (= status :hide))} - [:div {:class (stl/css :wrapper)} - [:div {:class (stl/css :icon)} - (case type - :warning i/msg-warning-refactor - :error i/msg-error-refactor - :success i/msg-success-refactor - :info i/msg-neutral-refactor - i/msg-error-refactor)] + [:div {:class (stl/css-case :banner true + :warning (= type :warning) + :error (= type :error) + :success (= type :success) + :info (= type :info) + :fixed (= position :fixed) + :floating (= position :floating) + :inline (= position :inline) + :hide (= status :hide))} + [:div {:class (stl/css :wrapper)} + [:div {:class (stl/css :icon)} + (case type + :warning i/msg-warning-refactor + :error i/msg-error-refactor + :success i/msg-success-refactor + :info i/msg-neutral-refactor + i/msg-error-refactor)] - [:div {:class (stl/css-case :content true - :inline-actions (= controls :inline-actions) - :bottom-actions (= controls :bottom-actions)) - :data-test data-test - :role role} - [:span {:class (stl/css :text)} - content - (for [[index link] (d/enumerate links)] - [:* {:key (dm/str "link-" index)} - " " [:& lb/link-button {:class "link" - :on-click (:callback link) - :value (:label link)}]])] + [:div {:class (stl/css-case :content true + :inline-actions (= controls :inline-actions) + :bottom-actions (= controls :bottom-actions)) + :data-test data-test + :role role} + [:span {:class (stl/css :text)} + content + (for [[index link] (d/enumerate links)] + [:* {:key (dm/str "link-" index)} + " " [:& lb/link-button {:class "link" + :on-click (:callback link) + :value (:label link)}]])] - (when (or (= controls :bottom-actions) (= controls :inline-actions)) - [:div {:class (stl/css :actions)} - (for [action actions] - [:button {:key (uuid/next) - :class (stl/css :action-bnt) - :on-click (:callback action)} - (:label action)])])] - (when (= controls :close) - [:button {:class (stl/css :btn-close) - :on-click on-close} i/close-refactor])]] - - - - [:div.banner {:class (dom/classnames - :warning (= type :warning) - :error (= type :error) - :success (= type :success) - :info (= type :info) - :fixed (= position :fixed) - :floating (= position :floating) - :inline (= position :inline) - :hide (= status :hide))} - [:div.wrapper - [:div.icon (case type - :warning i/msg-warning - :error i/msg-error - :success i/msg-success - :info i/msg-info - i/msg-error)] - [:div.content {:class (dom/classnames - :inline-actions (= controls :inline-actions) - :bottom-actions (= controls :bottom-actions)) - :data-test data-test - :role role} - [:span - content - (for [[index link] (d/enumerate links)] - [:* {:key (dm/str "link-" index)} - " " [:& lb/link-button {:class "link" - :on-click (:callback link) - :value (:label link)}]])] - - (when (or (= controls :bottom-actions) (= controls :inline-actions)) - [:div.actions - (for [action actions] - [:div.btn-secondary.btn-small {:key (uuid/next) - :on-click (:callback action)} - (:label action)])])] - (when (= controls :close) - [:div.btn-close {:on-click on-close} i/close])]]))) + (when (or (= controls :bottom-actions) (= controls :inline-actions)) + [:div {:class (stl/css :actions)} + (for [action actions] + [:button {:key (uuid/next) + :class (stl/css :action-bnt) + :on-click (:callback action)} + (:label action)])])] + (when (= controls :close) + [:button {:class (stl/css :btn-close) + :on-click on-close} i/close-refactor])]]) (mf/defc notifications [] diff --git a/frontend/src/app/main/ui/messages.scss b/frontend/src/app/main/ui/messages.scss index 1479f2f142..e2a571c8f7 100644 --- a/frontend/src/app/main/ui/messages.scss +++ b/frontend/src/app/main/ui/messages.scss @@ -100,15 +100,6 @@ @include titleTipography; } -.inline-actions { -} - -.bottom-actions { -} - -.actions { -} - .action-btn { @extend .button-tertiary; height: $s-32; diff --git a/frontend/src/app/main/ui/modal.cljs b/frontend/src/app/main/ui/modal.cljs index 3ed224d1a3..a7eb37a45a 100644 --- a/frontend/src/app/main/ui/modal.cljs +++ b/frontend/src/app/main/ui/modal.cljs @@ -51,7 +51,6 @@ (let [data (unchecked-get props "data") wrapper-ref (mf/use-ref nil) components (mf/deref dm/components) - new-css-system (mf/use-ctx ctx/new-css-system) allow-click-outside (:allow-click-outside data) @@ -78,9 +77,7 @@ (when-let [component (get components (:type data))] [:div {:ref wrapper-ref - :class (stl/css-case - :modal-wrapper new-css-system - :global/modal-wrapper (not new-css-system))} + :class (stl/css :modal-wrapper)} (mf/element component (:props data))]))) (def modal-ref diff --git a/frontend/src/app/main/ui/onboarding.cljs b/frontend/src/app/main/ui/onboarding.cljs index 8b9801d8a1..b16589df32 100644 --- a/frontend/src/app/main/ui/onboarding.cljs +++ b/frontend/src/app/main/ui/onboarding.cljs @@ -13,7 +13,6 @@ [app.main.data.modal :as modal] [app.main.data.users :as du] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.onboarding.newsletter] [app.main.ui.onboarding.questions] [app.main.ui.onboarding.team-choice] @@ -33,178 +32,113 @@ (mf/defc onboarding-welcome [{:keys [next] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - go-next + (let [go-next (fn [] (send-event "onboarding-step1-continue") (next))] - (if new-css-system - [:div {:class (stl/css :modal-container)} - [:div {:class (stl/css :modal-left)} - [:img {:src "images/onboarding-welcome.png" - :border "0" - :alt (tr "onboarding.welcome.alt")}]] - [:div {:class (stl/css :modal-right)} - [:div {:class (stl/css :release)} - "Version " (:main cf/version)] - [:div {:class (stl/css :modal-content)} - [:div {:class (stl/css :modal-header)} - [:h2 {:class (stl/css :modal-title) - :data-test "onboarding-welcome"} - (tr "onboarding-v2.welcome.title")]] + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-left)} + [:img {:src "images/onboarding-welcome.png" + :border "0" + :alt (tr "onboarding.welcome.alt")}]] + [:div {:class (stl/css :modal-right)} + [:div {:class (stl/css :release)} + "Version " (:main cf/version)] + [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title) + :data-test "onboarding-welcome"} + (tr "onboarding-v2.welcome.title")]] - [:div {:class (stl/css :modal-info)} - [:p {:class (stl/css :modal-text)} - (tr "onboarding-v2.welcome.desc1")] - [:div {:class (stl/css :property-block)} - [:img {:src "images/community.svg" - :border "0"}] - [:div {:class (stl/css :text-wrapper)} - [:div {:class (stl/css :property-title)} - [:a {:href "https://community.penpot.app/" - :target "_blank" - :on-click #(send-event "onboarding-community-link")} - (tr "onboarding-v2.welcome.desc2.title")]] - [:div {:class (stl/css :property-description)} - (tr "onboarding-v2.welcome.desc2")]]] + [:div {:class (stl/css :modal-info)} + [:p {:class (stl/css :modal-text)} + (tr "onboarding-v2.welcome.desc1")] + [:div {:class (stl/css :property-block)} + [:img {:src "images/community.svg" + :border "0"}] + [:div {:class (stl/css :text-wrapper)} + [:div {:class (stl/css :property-title)} + [:a {:href "https://community.penpot.app/" + :target "_blank" + :on-click #(send-event "onboarding-community-link")} + (tr "onboarding-v2.welcome.desc2.title")]] + [:div {:class (stl/css :property-description)} + (tr "onboarding-v2.welcome.desc2")]]] - [:div {:class (stl/css :property-block)} - [:img {:src "images/contributing.svg" - :border "0"}] - [:div {:class (stl/css :text-wrapper)} - [:div {:class (stl/css :property-title)} - [:a {:href "https://help.penpot.app/contributing-guide/" - :target "_blank" :on-click #(send-event "onboarding-contributing-link")} - (tr "onboarding-v2.welcome.desc3.title")]] - [:div {:class (stl/css :property-description)} - (tr "onboarding-v2.welcome.desc3")]]]]] + [:div {:class (stl/css :property-block)} + [:img {:src "images/contributing.svg" + :border "0"}] + [:div {:class (stl/css :text-wrapper)} + [:div {:class (stl/css :property-title)} + [:a {:href "https://help.penpot.app/contributing-guide/" + :target "_blank" :on-click #(send-event "onboarding-contributing-link")} + (tr "onboarding-v2.welcome.desc3.title")]] + [:div {:class (stl/css :property-description)} + (tr "onboarding-v2.welcome.desc3")]]]]] - [:div {:class (stl/css :modal-footer)} - [:button {:on-click go-next - :data-test "onboarding-next-btn"} - (tr "labels.continue")]]]] - - - [:div.modal-container.onboarding.onboarding-v2 - [:div.modal-left.welcome - [:img {:src "images/onboarding-welcome.png" :border "0" :alt (tr "onboarding.welcome.alt")}]] - [:div.modal-right - [:div.release-container [:span.release "Version " (:main cf/version)]] - [:div.right-content - [:div.modal-title - [:h2 {:data-test "onboarding-welcome"} (tr "onboarding-v2.welcome.title")]] - - [:div.modal-content - [:p (tr "onboarding-v2.welcome.desc1")] - [:div.welcome-card - [:img {:src "images/community.svg" :border "0"}] - [:div - [:div.title [:a {:href "https://community.penpot.app/" :target "_blank" :on-click #(send-event "onboarding-community-link")} (tr "onboarding-v2.welcome.desc2.title")]] - [:div.description (tr "onboarding-v2.welcome.desc2")]]] - - [:div.welcome-card - [:img {:src "images/contributing.svg" :border "0"}] - [:div - [:div.title [:a {:href "https://help.penpot.app/contributing-guide/" :target "_blank" :on-click #(send-event "onboarding-contributing-link")} (tr "onboarding-v2.welcome.desc3.title")]] - [:div.description (tr "onboarding-v2.welcome.desc3")]]]]] - [:div.modal-navigation - [:button.btn-secondary {:on-click go-next :data-test "onboarding-next-btn"} (tr "labels.continue")]] - [:img.deco.square {:src "images/deco-square.svg" :border "0"}] - [:img.deco.circle {:src "images/deco-circle.svg" :border "0"}] - [:img.deco.line1 {:src "images/deco-line1.svg" :border "0"}] - [:img.deco.line2 {:src "images/deco-line2.svg" :border "0"}]]]))) + [:div {:class (stl/css :modal-footer)} + [:button {:on-click go-next + :data-test "onboarding-next-btn"} + (tr "labels.continue")]]]])) (mf/defc onboarding-before-start [{:keys [next] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - go-next + (let [go-next (fn [] (send-event "onboarding-step2-continue") (next))] - (if new-css-system - [:div {:class (stl/css :modal-container)} - [:div {:class (stl/css :modal-left)} - [:img {:src "images/onboarding-people.png" - :border "0" - :alt (tr "onboarding.welcome.alt")}]] - [:div {:class (stl/css :modal-right)} - [:div {:class (stl/css :release)} - "Version " (:main cf/version)] - [:div {:class (stl/css :modal-content)} - [:div {:class (stl/css :modal-header)} - [:h2 {:class (stl/css :modal-title) - :data-test "onboarding-welcome"} - (tr "onboarding-v2.before-start.title")]] + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-left)} + [:img {:src "images/onboarding-people.png" + :border "0" + :alt (tr "onboarding.welcome.alt")}]] + [:div {:class (stl/css :modal-right)} + [:div {:class (stl/css :release)} + "Version " (:main cf/version)] + [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title) + :data-test "onboarding-welcome"} + (tr "onboarding-v2.before-start.title")]] - [:div {:class (stl/css :modal-info)} - [:p {:class (stl/css :modal-text)} - (tr "onboarding-v2.before-start.desc1")] - [:div {:class (stl/css :property-block)} - [:img {:src "images/user-guide.svg" :border "0"}] - [:div {:class (stl/css :text-wrapper)} - [:div {:class (stl/css :property-title)} - [:a {:class (stl/css :modal-link) - :href "https://help.penpot.app/user-guide/" - :target "_blank" - :on-click #(send-event "onboarding-user-guide-link")} - (tr "onboarding-v2.before-start.desc2.title")]] - [:div {:class (stl/css :property-description)} - (tr "onboarding-v2.before-start.desc2")]]] + [:div {:class (stl/css :modal-info)} + [:p {:class (stl/css :modal-text)} + (tr "onboarding-v2.before-start.desc1")] + [:div {:class (stl/css :property-block)} + [:img {:src "images/user-guide.svg" :border "0"}] + [:div {:class (stl/css :text-wrapper)} + [:div {:class (stl/css :property-title)} + [:a {:class (stl/css :modal-link) + :href "https://help.penpot.app/user-guide/" + :target "_blank" + :on-click #(send-event "onboarding-user-guide-link")} + (tr "onboarding-v2.before-start.desc2.title")]] + [:div {:class (stl/css :property-description)} + (tr "onboarding-v2.before-start.desc2")]]] - [:div {:class (stl/css :property-block)} - [:img {:src "images/video-tutorials.svg" :border "0"}] - [:div {:class (stl/css :text-wrapper)} - [:div {:class (stl/css :property-title)} - [:a {:class (stl/css :modal-link) - :href "https://www.youtube.com/c/Penpot" - :target "_blank" - :on-click #(send-event "onboarding-video-tutorials-link")} - (tr "onboarding-v2.before-start.desc3.title")]] - [:div {:class (stl/css :property-description)} - (tr "onboarding-v2.before-start.desc3")]]]]] + [:div {:class (stl/css :property-block)} + [:img {:src "images/video-tutorials.svg" :border "0"}] + [:div {:class (stl/css :text-wrapper)} + [:div {:class (stl/css :property-title)} + [:a {:class (stl/css :modal-link) + :href "https://www.youtube.com/c/Penpot" + :target "_blank" + :on-click #(send-event "onboarding-video-tutorials-link")} + (tr "onboarding-v2.before-start.desc3.title")]] + [:div {:class (stl/css :property-description)} + (tr "onboarding-v2.before-start.desc3")]]]]] - [:div {:class (stl/css :modal-footer)} - [:button {:on-click go-next - :data-test "onboarding-next-btn"} - (tr "labels.continue")]]]] - - - [:div.modal-container.onboarding.onboarding-v2 - [:div.modal-left.welcome - [:img {:src "images/onboarding-people.png" :border "0" :alt (tr "onboarding.welcome.alt")}]] - [:div.modal-right - [:div.release-container [:span.release "Version " (:main cf/version)]] - [:div.right-content - [:div.modal-title - [:h2 {:data-test "onboarding-welcome"} (tr "onboarding-v2.before-start.title")]] - - [:div.modal-content - [:p (tr "onboarding-v2.before-start.desc1")] - [:div.welcome-card - [:img {:src "images/user-guide.svg" :border "0"}] - [:div - [:div.title [:a {:href "https://help.penpot.app/user-guide/" :target "_blank" :on-click #(send-event "onboarding-user-guide-link")} (tr "onboarding-v2.before-start.desc2.title")]] - [:div.description (tr "onboarding-v2.before-start.desc2")]]] - - [:div.welcome-card - [:img {:src "images/video-tutorials.svg" :border "0"}] - [:div - [:div.title [:a {:href "https://www.youtube.com/c/Penpot" :target "_blank" :on-click #(send-event "onboarding-video-tutorials-link")} (tr "onboarding-v2.before-start.desc3.title")]] - [:div.description (tr "onboarding-v2.before-start.desc3")]]]]] - [:div.modal-navigation - [:button.btn-secondary {:on-click go-next :data-test "onboarding-next-btn"} (tr "labels.continue")]] - [:img.deco.square {:src "images/deco-square.svg" :border "0"}] - [:img.deco.circle {:src "images/deco-circle.svg" :border "0"}] - [:img.deco.line1 {:src "images/deco-line1.svg" :border "0"}] - [:img.deco.line2 {:src "images/deco-line2.svg" :border "0"}]]]))) + [:div {:class (stl/css :modal-footer)} + [:button {:on-click go-next + :data-test "onboarding-next-btn"} + (tr "labels.continue")]]]])) (mf/defc onboarding-modal {::mf/register modal/components ::mf/register-as :onboarding} [_] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - slide (mf/use-state :start) + (let [slide (mf/use-state :start) klass (mf/use-state "fadeInDown") navigate @@ -230,15 +164,8 @@ (reset! klass nil) (tm/dispose! sem)))) - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div.animated {:class(dm/str @klass " " (stl/css :animated))} - (case @slide - :start [:& onboarding-welcome {:next #(navigate :opensource)}] - :opensource [:& onboarding-before-start {:next skip}])]] - - [:div.modal-overlay - [:div.animated {:class @klass} - (case @slide - :start [:& onboarding-welcome {:next #(navigate :opensource)}] - :opensource [:& onboarding-before-start {:next skip}])]]))) + [:div {:class (stl/css :modal-overlay)} + [:div.animated {:class (dm/str @klass " " (stl/css :animated))} + (case @slide + :start [:& onboarding-welcome {:next #(navigate :opensource)}] + :opensource [:& onboarding-before-start {:next skip}])]])) diff --git a/frontend/src/app/main/ui/onboarding/newsletter.cljs b/frontend/src/app/main/ui/onboarding/newsletter.cljs index c147a7d5ec..e252452857 100644 --- a/frontend/src/app/main/ui/onboarding/newsletter.cljs +++ b/frontend/src/app/main/ui/onboarding/newsletter.cljs @@ -11,7 +11,6 @@ [app.main.data.modal :as modal] [app.main.data.users :as du] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) @@ -20,8 +19,7 @@ {::mf/register modal/components ::mf/register-as :onboarding-newsletter-modal} [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - message (tr "onboarding.newsletter.acceptance-message") + (let [message (tr "onboarding.newsletter.acceptance-message") newsletter-updates (mf/use-state false) newsletter-news (mf/use-state false) toggle @@ -39,78 +37,49 @@ (modal/show {:type :onboarding-team}) (du/update-profile-props {:newsletter-updates @newsletter-updates :newsletter-news @newsletter-news}))))] - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div.animated.fadeInDown {:class (stl/css :modal-container)} - [:div {:class (stl/css :modal-header)} - [:h2 {:class (stl/css :modal-title) - :data-test "onboarding-newsletter-title"} - (tr "onboarding.newsletter.title")] - [:p {:class (stl/css :modal-text)} - (tr "onboarding-v2.newsletter.desc")]] - [:div {:class (stl/css :modal-content)} - [:div {:class (stl/css :newsletter-options)} - [:div {:class (stl/css :input-wrapper)} - [:label {:for "newsletter-updates"} - [:span {:class (stl/css-case :global/checked @newsletter-updates)} - (when @newsletter-updates - i/status-tick-refactor)] - (tr "onboarding-v2.newsletter.updates") - [:input {:type "checkbox" - :id "newsletter-updates" - :on-change #(toggle newsletter-updates)}]]] + [:div {:class (stl/css :modal-overlay)} + [:div.animated.fadeInDown {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title) + :data-test "onboarding-newsletter-title"} + (tr "onboarding.newsletter.title")] + [:p {:class (stl/css :modal-text)} + (tr "onboarding-v2.newsletter.desc")]] + [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :newsletter-options)} + [:div {:class (stl/css :input-wrapper)} + [:label {:for "newsletter-updates"} + [:span {:class (stl/css-case :global/checked @newsletter-updates)} + (when @newsletter-updates + i/status-tick-refactor)] + (tr "onboarding-v2.newsletter.updates") + [:input {:type "checkbox" + :id "newsletter-updates" + :on-change #(toggle newsletter-updates)}]]] - [:div {:class (stl/css :input-wrapper)} - [:label {:for "newsletter-news"} - [:span {:class (stl/css-case :global/checked @newsletter-news)} - (when @newsletter-news - i/status-tick-refactor)] - (tr "onboarding-v2.newsletter.news") - [:input {:type "checkbox" - :id "newsletter-news" - :on-change #(toggle newsletter-news)}]]]] + [:div {:class (stl/css :input-wrapper)} + [:label {:for "newsletter-news"} + [:span {:class (stl/css-case :global/checked @newsletter-news)} + (when @newsletter-news + i/status-tick-refactor)] + (tr "onboarding-v2.newsletter.news") + [:input {:type "checkbox" + :id "newsletter-news" + :on-change #(toggle newsletter-news)}]]]] - [:div {:class (stl/css :modal-info)} - [:p {:class (stl/css :modal-text)} - (tr "onboarding-v2.newsletter.privacy1") - [:a {:class (stl/css :modal-link) - :target "_blank" - :href "https://penpot.app/privacy"} - (tr "onboarding.newsletter.policy")]] - [:p {:class (stl/css :modal-text)} - (tr "onboarding-v2.newsletter.privacy2")]]] + [:div {:class (stl/css :modal-info)} + [:p {:class (stl/css :modal-text)} + (tr "onboarding-v2.newsletter.privacy1") + [:a {:class (stl/css :modal-link) + :target "_blank" + :href "https://penpot.app/privacy"} + (tr "onboarding.newsletter.policy")]] + [:p {:class (stl/css :modal-text)} + (tr "onboarding-v2.newsletter.privacy2")]]] - [:div {:class (stl/css :modal-footer)} - [:button {:on-click accept} (tr "labels.continue")]] + [:div {:class (stl/css :modal-footer)} + [:button {:on-click accept} (tr "labels.continue")]] - [:img {:class (stl/css-case :deco true - :top true) - :src "images/deco-newsletter.png" :border "0"}]]] - - - - [:div.modal-overlay - [:div.modal-container.onboarding.newsletter.animated.fadeInDown - [:div.modal-top - [:h1.newsletter-title {:data-test "onboarding-newsletter-title"} (tr "onboarding.newsletter.title")] - [:p (tr "onboarding-v2.newsletter.desc")]] - [:div.modal-bottom - [:div.newsletter-options - [:div.input-checkbox.check-primary - [:input {:type "checkbox" - :id "newsletter-updates" - :on-change #(toggle newsletter-updates)}] - [:label {:for "newsletter-updates"} (tr "onboarding-v2.newsletter.updates")]] - [:div.input-checkbox.check-primary - [:input {:type "checkbox" - :id "newsletter-news" - :on-change #(toggle newsletter-news)}] - [:label {:for "newsletter-news"} (tr "onboarding-v2.newsletter.news")]]] - [:p (tr "onboarding-v2.newsletter.privacy1") [:a {:target "_blank" :href "https://penpot.app/privacy"} (tr "onboarding.newsletter.policy")]] - [:p (tr "onboarding-v2.newsletter.privacy2")]] - [:div.modal-footer - [:button.btn-primary {:on-click accept} (tr "labels.continue")]] - [:img.deco.top {:src "images/deco-newsletter.png" :border "0"}] - [:img.deco.newsletter-left {:src "images/deco-news-left.png" :border "0"}] - [:img.deco.newsletter-right {:src "images/deco-news-right.png" :border "0"}]]]) - )) + [:img {:class (stl/css-case :deco true + :top true) + :src "images/deco-newsletter.png" :border "0"}]]])) diff --git a/frontend/src/app/main/ui/onboarding/questions.cljs b/frontend/src/app/main/ui/onboarding/questions.cljs index 1f5d4b9f46..4f9a612158 100644 --- a/frontend/src/app/main/ui/onboarding/questions.cljs +++ b/frontend/src/app/main/ui/onboarding/questions.cljs @@ -13,7 +13,6 @@ [app.main.data.users :as du] [app.main.store :as st] [app.main.ui.components.forms :as fm] - [app.main.ui.context :as ctx] [app.util.i18n :as i18n :refer [tr]] [cljs.spec.alpha :as s] [cuerdas.core :as str] @@ -22,139 +21,75 @@ (mf/defc step-container [{:keys [form step on-next on-prev children] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] + [:& fm/form {:form form :on-submit on-next} + [:div {:class (stl/css :paginator)} (str/ffmt "%/4" step)] - (if new-css-system - [:& fm/form {:form form :on-submit on-next} - [:div {:class (stl/css :paginator)} (str/ffmt "%/4" step)] + children - children + [:div {:class (stl/css :action-buttons)} - [:div {:class (stl/css :action-buttons)} + (when on-prev + [:button {:class (stl/css :prev-button) + :on-click on-prev} (tr "questions.previous")]) - (when on-prev - [:button {:class (stl/css :prev-button) - :on-click on-prev} (tr "questions.previous")]) - - [:> fm/submit-button* - {:label (if (< step 4) (tr "questions.next") (tr "questions.start")) - :class (stl/css :next-button)}]]] - - - - [:& fm/form {:form form :on-submit on-next} - [:div.step-header - [:div.step-number (str/ffmt "%/4" step)]] - - children - - [:div.buttons - [:div.step-next - [:> fm/submit-button* - {:label (if (< step 4) (tr "questions.next") (tr "questions.start")) - :class "step-next"}]] - - (when on-prev - [:div.step-prev - [:button {:on-click on-prev} (tr "questions.previous")]])]]))) + [:> fm/submit-button* + {:label (if (< step 4) (tr "questions.next") (tr "questions.start")) + :class (stl/css :next-button)}]]]) (s/def ::questions-form-step-1 (s/keys :req-un [::planning])) (mf/defc step-1 [{:keys [on-next form] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:& step-container {:form form :step 1 :on-next on-next} - [:img {:class (stl/css :header-image) - :src "images/form/use-for-1.png" :alt (tr "questions.lets-get-started")}] - [:h1 {:class (stl/css :modal-title)} (tr "questions.lets-get-started")] - [:p {:class (stl/css :modal-text)} (tr "questions.your-feedback-will-help-us")] - [:h3 {:class (stl/css :modal-subtitle)} (tr "questions.questions-how-are-you-planning-to-use-penpot")] - [:& fm/select {:options [{:label (tr "questions.select-option") :value "" :key "questions-how-are-you-planning-to-use-penpot" :disabled true} - {:label (tr "questions.discover-more-about-penpot") :value "discover-more-about-penpot" :key "discover-more-about-penpot"} - {:label (tr "questions.test-penpot-to-see-if-its-a-fit-for-team") :value "test-penpot-to-see-if-its-a-fit-for-team" :key "test-penpot-to-see-if-its-a-fit-for-team"} - {:label (tr "questions.start-to-work-on-my-project") :value "start-to-work-on-my-project" :key "start-to-work-on-my-project"} - {:label (tr "questions.get-the-code-from-my-team-project") :value "get-the-code-from-my-team-project" :key "get-the-code-from-my-team-project"} - {:label (tr "questions.leave-feedback-for-my-team-project") :value "leave-feedback-for-my-team-project" :key "leave-feedback-for-my-team-project"} - {:label (tr "questions.work-in-concept-ideas") :value "work-in-concept-ideas" :key "work-in-concept-ideas"} - {:label (tr "questions.try-out-before-using-penpot-on-premise") :value "try-out-before-using-penpot-on-premise" :key "try-out-before-using-penpot-on-premise"}] - :default "" - :name :planning}]] - - - [:& step-container {:form form :step 1 :on-next on-next} - [:img.header-image {:src "images/form/use-for-1.png" :alt (tr "questions.lets-get-started")}] - [:h1 (tr "questions.lets-get-started")] - [:p.intro (tr "questions.your-feedback-will-help-us")] - [:h3 (tr "questions.questions-how-are-you-planning-to-use-penpot")] - [:& fm/select {:options [{:label (tr "questions.select-option") :value "" :key "questions-how-are-you-planning-to-use-penpot" :disabled true} - {:label (tr "questions.discover-more-about-penpot") :value "discover-more-about-penpot" :key "discover-more-about-penpot"} - {:label (tr "questions.test-penpot-to-see-if-its-a-fit-for-team") :value "test-penpot-to-see-if-its-a-fit-for-team" :key "test-penpot-to-see-if-its-a-fit-for-team"} - {:label (tr "questions.start-to-work-on-my-project") :value "start-to-work-on-my-project" :key "start-to-work-on-my-project"} - {:label (tr "questions.get-the-code-from-my-team-project") :value "get-the-code-from-my-team-project" :key "get-the-code-from-my-team-project"} - {:label (tr "questions.leave-feedback-for-my-team-project") :value "leave-feedback-for-my-team-project" :key "leave-feedback-for-my-team-project"} - {:label (tr "questions.work-in-concept-ideas") :value "work-in-concept-ideas" :key "work-in-concept-ideas"} - {:label (tr "questions.try-out-before-using-penpot-on-premise") :value "try-out-before-using-penpot-on-premise" :key "try-out-before-using-penpot-on-premise"}] - :default "" - :name :planning}]]))) + [:& step-container {:form form :step 1 :on-next on-next} + [:img {:class (stl/css :header-image) + :src "images/form/use-for-1.png" :alt (tr "questions.lets-get-started")}] + [:h1 {:class (stl/css :modal-title)} (tr "questions.lets-get-started")] + [:p {:class (stl/css :modal-text)} (tr "questions.your-feedback-will-help-us")] + [:h3 {:class (stl/css :modal-subtitle)} (tr "questions.questions-how-are-you-planning-to-use-penpot")] + [:& fm/select {:options [{:label (tr "questions.select-option") :value "" :key "questions-how-are-you-planning-to-use-penpot" :disabled true} + {:label (tr "questions.discover-more-about-penpot") :value "discover-more-about-penpot" :key "discover-more-about-penpot"} + {:label (tr "questions.test-penpot-to-see-if-its-a-fit-for-team") :value "test-penpot-to-see-if-its-a-fit-for-team" :key "test-penpot-to-see-if-its-a-fit-for-team"} + {:label (tr "questions.start-to-work-on-my-project") :value "start-to-work-on-my-project" :key "start-to-work-on-my-project"} + {:label (tr "questions.get-the-code-from-my-team-project") :value "get-the-code-from-my-team-project" :key "get-the-code-from-my-team-project"} + {:label (tr "questions.leave-feedback-for-my-team-project") :value "leave-feedback-for-my-team-project" :key "leave-feedback-for-my-team-project"} + {:label (tr "questions.work-in-concept-ideas") :value "work-in-concept-ideas" :key "work-in-concept-ideas"} + {:label (tr "questions.try-out-before-using-penpot-on-premise") :value "try-out-before-using-penpot-on-premise" :key "try-out-before-using-penpot-on-premise"}] + :default "" + :name :planning}]]) (s/def ::questions-form-step-2 (s/keys :req-un [::experience-branding-illustrations-marketing-pieces ::experience-interface-design-visual-assets-design-systems ::experience-interface-wireframes-user-journeys-flows-navigation-trees])) (mf/defc step-2 [{:keys [on-next on-prev form] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:& step-container {:form form :step 2 :on-next on-next :on-prev on-prev} - [:h3 {:class (stl/css :modal-subtitle)} - (tr "questions.describe-your-experience-working-on")] + [:& step-container {:form form :step 2 :on-next on-next :on-prev on-prev} + [:h3 {:class (stl/css :modal-subtitle)} + (tr "questions.describe-your-experience-working-on")] - [:div {:class (stl/css :modal-question)} - [:div {:class (stl/css :modal-text)} - (tr "branding-illustrations-marketing-pieces")] - [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} - {:label (tr "questions.some") :value "some"} - {:label (tr "questions.a-lot") :value "a-lot"}] - :name :experience-branding-illustrations-marketing-pieces}]] + [:div {:class (stl/css :modal-question)} + [:div {:class (stl/css :modal-text)} + (tr "branding-illustrations-marketing-pieces")] + [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} + {:label (tr "questions.some") :value "some"} + {:label (tr "questions.a-lot") :value "a-lot"}] + :name :experience-branding-illustrations-marketing-pieces}]] - [:div {:class (stl/css :modal-question)} - [:div {:class (stl/css :modal-text)} - (tr "questions.interface-design-visual-assets-design-systems")] - [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} - {:label (tr "questions.some") :value "some"} - {:label (tr "questions.a-lot") :value "a-lot"}] - :name :experience-interface-design-visual-assets-design-systems}]] + [:div {:class (stl/css :modal-question)} + [:div {:class (stl/css :modal-text)} + (tr "questions.interface-design-visual-assets-design-systems")] + [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} + {:label (tr "questions.some") :value "some"} + {:label (tr "questions.a-lot") :value "a-lot"}] + :name :experience-interface-design-visual-assets-design-systems}]] - [:div {:class (stl/css :modal-question)} - [:div {:class (stl/css :modal-text)} - (tr "questions.wireframes-user-journeys-flows-navigation-trees")] - [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} - {:label (tr "questions.some") :value "some"} - {:label (tr "questions.a-lot") :value "a-lot"}] - :name :experience-interface-wireframes-user-journeys-flows-navigation-trees}]]] - - - [:& step-container {:form form :step 2 :on-next on-next :on-prev on-prev} - [:h3 (tr "questions.describe-your-experience-working-on")] - - [:div.section (tr "branding-illustrations-marketing-pieces")] - [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} - {:label (tr "questions.some") :value "some"} - {:label (tr "questions.a-lot") :value "a-lot"}] - :name :experience-branding-illustrations-marketing-pieces}] - - [:div.section (tr "questions.interface-design-visual-assets-design-systems")] - [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} - {:label (tr "questions.some") :value "some"} - {:label (tr "questions.a-lot") :value "a-lot"}] - :name :experience-interface-design-visual-assets-design-systems}] - - [:div.section (tr "questions.wireframes-user-journeys-flows-navigation-trees")] - [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} - {:label (tr "questions.some") :value "some"} - {:label (tr "questions.a-lot") :value "a-lot"}] - :name :experience-interface-wireframes-user-journeys-flows-navigation-trees}]]))) + [:div {:class (stl/css :modal-question)} + [:div {:class (stl/css :modal-text)} + (tr "questions.wireframes-user-journeys-flows-navigation-trees")] + [:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"} + {:label (tr "questions.some") :value "some"} + {:label (tr "questions.a-lot") :value "a-lot"}] + :name :experience-interface-wireframes-user-journeys-flows-navigation-trees}]]]) (s/def ::questions-form-step-3 (s/keys :req-un [::experience-design-tool] @@ -170,8 +105,7 @@ (mf/defc step-3 [{:keys [on-next on-prev form] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - experience-design-tool (dm/get-in @form [:clean-data :experience-design-tool]) + (let [experience-design-tool (dm/get-in @form [:clean-data :experience-design-tool]) on-design-tool-change (fn [_ _] (let [experience-design-tool (dm/get-in @form [:clean-data :experience-design-tool])] @@ -180,40 +114,23 @@ (swap! form d/dissoc-in [:data :experience-design-tool-other]) (swap! form d/dissoc-in [:errors :experience-design-tool-other])))))] - (if new-css-system - [:& step-container {:form form :step 3 :on-next on-next :on-prev on-prev} - [:h3 {:class (stl/css :modal-subtitle)} - (tr "question.design-tool-more-experienced-with")] - [:& fm/radio-buttons {:options [{:label (tr "questions.figma") :value "figma" :image "images/form/figma.png"} - {:label (tr "questions.sketch") :value "sketch" :image "images/form/sketch.png"} - {:label (tr "questions.adobe-xd") :value "adobe-xd" :image "images/form/adobe-xd.png"} - {:label (tr "questions.canva") :value "canva" :image "images/form/canva.png"} - {:label (tr "questions.invision") :value "invision" :image "images/form/invision.png"} - {:label (tr "questions.never-used-a-tool") :value "never-used-a-tool" :image "images/form/never-used.png"} - {:label (tr "questions.other") :value "other"}] - :name :experience-design-tool - :on-change on-design-tool-change}] + [:& step-container {:form form :step 3 :on-next on-next :on-prev on-prev} + [:h3 {:class (stl/css :modal-subtitle)} + (tr "question.design-tool-more-experienced-with")] + [:& fm/radio-buttons {:options [{:label (tr "questions.figma") :value "figma" :image "images/form/figma.png"} + {:label (tr "questions.sketch") :value "sketch" :image "images/form/sketch.png"} + {:label (tr "questions.adobe-xd") :value "adobe-xd" :image "images/form/adobe-xd.png"} + {:label (tr "questions.canva") :value "canva" :image "images/form/canva.png"} + {:label (tr "questions.invision") :value "invision" :image "images/form/invision.png"} + {:label (tr "questions.never-used-a-tool") :value "never-used-a-tool" :image "images/form/never-used.png"} + {:label (tr "questions.other") :value "other"}] + :name :experience-design-tool + :on-change on-design-tool-change}] - [:& fm/input {:name :experience-design-tool-other - :placeholder (tr "questions.other") - :label "" - :disabled (not= experience-design-tool "other")}]] - - - [:& step-container {:form form :step 3 :on-next on-next :on-prev on-prev} - [:h3 (tr "question.design-tool-more-experienced-with")] - [:& fm/radio-buttons {:options [{:label (tr "questions.figma") :value "figma" :image "images/form/figma.png"} - {:label (tr "questions.sketch") :value "sketch" :image "images/form/sketch.png"} - {:label (tr "questions.adobe-xd") :value "adobe-xd" :image "images/form/adobe-xd.png"} - {:label (tr "questions.canva") :value "canva" :image "images/form/canva.png"} - {:label (tr "questions.invision") :value "invision" :image "images/form/invision.png"} - {:label (tr "questions.never-used-a-tool") :value "never-used-a-tool" :image "images/form/never-used.png"} - {:label (tr "questions.other") :value "other"}] - :name :experience-design-tool - :on-change on-design-tool-change}] - [:div.other - [:label (tr "questions.other")] - [:& fm/input {:name :experience-design-tool-other :label (tr "questions.other") :disabled (not= experience-design-tool "other")}]]]))) + [:& fm/input {:name :experience-design-tool-other + :placeholder (tr "questions.other") + :label "" + :disabled (not= experience-design-tool "other")}]])) (s/def ::questions-form-step-4 (s/keys :req-un [::team-size ::role] @@ -229,8 +146,7 @@ (mf/defc step-4 [{:keys [on-next on-prev form] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - role (dm/get-in @form [:data :role]) + (let [role (dm/get-in @form [:data :role]) on-role-change (fn [_ _] (let [experience-design-tool (dm/get-in @form [:clean-data :experience-design-tool])] @@ -239,63 +155,34 @@ (swap! form d/dissoc-in [:data :role-other]) (swap! form d/dissoc-in [:errors :role-other])))))] - (if new-css-system - [:& step-container {:form form :step 4 :on-next on-next :on-prev on-prev} - [:h3 {:class (stl/css :modal-subtitle)} (tr "questions.role")] - [:& fm/radio-buttons {:options [{:label (tr "questions.designer") :value "designer"} - {:label (tr "questions.developer") :value "developer"} - {:label (tr "questions.manager") :value "manager"} - {:label (tr "questions.founder") :value "founder"} - {:label (tr "questions.marketing") :value "marketing"} - {:label (tr "questions.student-teacher") :value "student-teacher"} - {:label (tr "questions.other") :value "other"}] - :name :role - :on-change on-role-change}] - [:& fm/input {:name :role-other :label "" :placeholder (tr "questions.other") :disabled (not= role "other")}] + [:& step-container {:form form :step 4 :on-next on-next :on-prev on-prev} + [:h3 {:class (stl/css :modal-subtitle)} (tr "questions.role")] + [:& fm/radio-buttons {:options [{:label (tr "questions.designer") :value "designer"} + {:label (tr "questions.developer") :value "developer"} + {:label (tr "questions.manager") :value "manager"} + {:label (tr "questions.founder") :value "founder"} + {:label (tr "questions.marketing") :value "marketing"} + {:label (tr "questions.student-teacher") :value "student-teacher"} + {:label (tr "questions.other") :value "other"}] + :name :role + :on-change on-role-change}] + [:& fm/input {:name :role-other :label "" :placeholder (tr "questions.other") :disabled (not= role "other")}] - [:div {:class (stl/css :modal-question)} - [:h3 {:class (stl/css :modal-subtitle)} (tr "questions.team-size")] - [:& fm/select {:options [{:label (tr "questions.select-option") :value "" :key "team-size" :disabled true} - {:label (tr "questions.more-than-50") :value "more-than-50" :key "more-than-50"} - {:label (tr "questions.31-50") :value "31-50" :key "31-50"} - {:label (tr "questions.11-30") :value "11-30" :key "11-30"} - {:label (tr "questions.2-10") :value "2-10" :key "2-10"} - {:label (tr "questions.freelancer") :value "freelancer" :key "freelancer"} - {:label (tr "questions.personal-project") :value "personal-project" :key "personal-project"}] - :default "" - :name :team-size}]]] - - - [:& step-container {:form form :step 4 :on-next on-next :on-prev on-prev} - [:h3 (tr "questions.role")] - [:& fm/radio-buttons {:options [{:label (tr "questions.designer") :value "designer"} - {:label (tr "questions.developer") :value "developer"} - {:label (tr "questions.manager") :value "manager"} - {:label (tr "questions.founder") :value "founder"} - {:label (tr "questions.marketing") :value "marketing"} - {:label (tr "questions.student-teacher") :value "student-teacher"} - {:label (tr "questions.other") :value "other"}] - :name :role - :on-change on-role-change}] - [:div.other - [:label (tr "questions.other")] - [:& fm/input {:name :role-other :label (tr "questions.other") :disabled (not= role "other")}]] - - [:h3 (tr "questions.team-size")] - [:& fm/select {:options [{:label (tr "questions.select-option") :value "" :key "team-size" :disabled true} - {:label (tr "questions.more-than-50") :value "more-than-50" :key "more-than-50"} - {:label (tr "questions.31-50") :value "31-50" :key "31-50"} - {:label (tr "questions.11-30") :value "11-30" :key "11-30"} - {:label (tr "questions.2-10") :value "2-10" :key "2-10"} - {:label (tr "questions.freelancer") :value "freelancer" :key "freelancer"} - {:label (tr "questions.personal-project") :value "personal-project" :key "personal-project"}] - :default "" - :name :team-size}]]))) + [:div {:class (stl/css :modal-question)} + [:h3 {:class (stl/css :modal-subtitle)} (tr "questions.team-size")] + [:& fm/select {:options [{:label (tr "questions.select-option") :value "" :key "team-size" :disabled true} + {:label (tr "questions.more-than-50") :value "more-than-50" :key "more-than-50"} + {:label (tr "questions.31-50") :value "31-50" :key "31-50"} + {:label (tr "questions.11-30") :value "11-30" :key "11-30"} + {:label (tr "questions.2-10") :value "2-10" :key "2-10"} + {:label (tr "questions.freelancer") :value "freelancer" :key "freelancer"} + {:label (tr "questions.personal-project") :value "personal-project" :key "personal-project"}] + :default "" + :name :team-size}]]])) (mf/defc questions [{:keys []}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - container (mf/use-ref) + (let [container (mf/use-ref) step (mf/use-state 1) clean-data (mf/use-state {}) @@ -336,25 +223,11 @@ (reset! clean-data questionnaire) (st/emit! (du/mark-questions-as-answered questionnaire)))))] - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-container) - :ref container} - (case @step - 1 [:& step-1 {:on-next on-next :on-prev on-prev :form step-1-form}] - 2 [:& step-2 {:on-next on-next :on-prev on-prev :form step-2-form}] - 3 [:& step-3 {:on-next on-next :on-prev on-prev :form step-3-form}] - 4 [:& step-4 {:on-next on-submit :on-prev on-prev :form step-4-form}])]] - - - [:div.modal-wrapper.questions-form - [:div.modal-overlay - [:div.modal-container.onboarding.onboarding-v2 {:ref container} - [:img.deco.left {:src "images/deco-left.png" :border 0}] - [:img.deco.right {:src "images/deco-right.png" :border 0}] - [:div.signup-questions - (case @step - 1 [:& step-1 {:on-next on-next :on-prev on-prev :form step-1-form}] - 2 [:& step-2 {:on-next on-next :on-prev on-prev :form step-2-form}] - 3 [:& step-3 {:on-next on-next :on-prev on-prev :form step-3-form}] - 4 [:& step-4 {:on-next on-submit :on-prev on-prev :form step-4-form}])]]]]))) + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container) + :ref container} + (case @step + 1 [:& step-1 {:on-next on-next :on-prev on-prev :form step-1-form}] + 2 [:& step-2 {:on-next on-next :on-prev on-prev :form step-2-form}] + 3 [:& step-3 {:on-next on-next :on-prev on-prev :form step-3-form}] + 4 [:& step-4 {:on-next on-submit :on-prev on-prev :form step-4-form}])]])) diff --git a/frontend/src/app/main/ui/onboarding/team_choice.cljs b/frontend/src/app/main/ui/onboarding/team_choice.cljs index bb2d5c977c..f850128ac6 100644 --- a/frontend/src/app/main/ui/onboarding/team_choice.cljs +++ b/frontend/src/app/main/ui/onboarding/team_choice.cljs @@ -15,7 +15,6 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.forms :as fm] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [tr]] [app.util.router :as rt] @@ -30,64 +29,38 @@ (mf/defc team-modal-right [] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - - [:div {:class (stl/css :modal-right)} - [:h2 {:class (stl/css :modal-subtitle)} - (tr "onboarding.team-modal.create-team")] - [:p {:class (stl/css :modal-text)} - (tr "onboarding.team-modal.create-team-desc")] - [:ul {:class (stl/css :team-features)} - [:li {:class (stl/css :feature)} - [:span {:class (stl/css :icon)} i/document-refactor] - [:p {:class (stl/css :modal-text)} - (tr "onboarding.team-modal.create-team-feature-1")]] - [:li {:class (stl/css :feature)} - [:span {:class (stl/css :icon)} i/move-refactor] - [:p {:class (stl/css :modal-text)} - (tr "onboarding.team-modal.create-team-feature-2")]] - [:li {:class (stl/css :feature)} - [:span {:class (stl/css :icon)} i/tree-refactor] - [:p {:class (stl/css :modal-text)} - (tr "onboarding.team-modal.create-team-feature-3")]] - [:li {:class (stl/css :feature)} - [:span {:class (stl/css :icon)} i/user-refactor] - [:p {:class (stl/css :modal-text)} - (tr "onboarding.team-modal.create-team-feature-4")]] - [:li {:class (stl/css :feature)} - [:span {:class (stl/css :icon)} i/tick-refactor] - [:p {:class (stl/css :modal-text)} - (tr "onboarding.team-modal.create-team-feature-5")]]]] - - - - [:div.team-right - [:h2.subtitle (tr "onboarding.team-modal.create-team")] - [:p.info (tr "onboarding.team-modal.create-team-desc")] - [:ul.team-features - [:li.feature - [:span.icon i/file-html] - [:p.feature-txt (tr "onboarding.team-modal.create-team-feature-1")]] - [:li.feature - [:span.icon i/pointer-inner] - [:p.feature-txt (tr "onboarding.team-modal.create-team-feature-2")]] - [:li.feature - [:span.icon i/tree] - [:p.feature-txt (tr "onboarding.team-modal.create-team-feature-3")]] - [:li.feature - [:span.icon i/user] - [:p.feature-txt (tr "onboarding.team-modal.create-team-feature-4")]] - [:li.feature - [:span.icon i/tick] - [:p.feature-txt (tr "onboarding.team-modal.create-team-feature-5")]]]]))) + [:div {:class (stl/css :modal-right)} + [:h2 {:class (stl/css :modal-subtitle)} + (tr "onboarding.team-modal.create-team")] + [:p {:class (stl/css :modal-text)} + (tr "onboarding.team-modal.create-team-desc")] + [:ul {:class (stl/css :team-features)} + [:li {:class (stl/css :feature)} + [:span {:class (stl/css :icon)} i/document-refactor] + [:p {:class (stl/css :modal-text)} + (tr "onboarding.team-modal.create-team-feature-1")]] + [:li {:class (stl/css :feature)} + [:span {:class (stl/css :icon)} i/move-refactor] + [:p {:class (stl/css :modal-text)} + (tr "onboarding.team-modal.create-team-feature-2")]] + [:li {:class (stl/css :feature)} + [:span {:class (stl/css :icon)} i/tree-refactor] + [:p {:class (stl/css :modal-text)} + (tr "onboarding.team-modal.create-team-feature-3")]] + [:li {:class (stl/css :feature)} + [:span {:class (stl/css :icon)} i/user-refactor] + [:p {:class (stl/css :modal-text)} + (tr "onboarding.team-modal.create-team-feature-4")]] + [:li {:class (stl/css :feature)} + [:span {:class (stl/css :icon)} i/tick-refactor] + [:p {:class (stl/css :modal-text)} + (tr "onboarding.team-modal.create-team-feature-5")]]]]) (mf/defc onboarding-team-modal {::mf/register modal/components ::mf/register-as :onboarding-team} [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - form (fm/use-form :spec ::team-form + (let [form (fm/use-form :spec ::team-form :initial {} :validators [(fm/validate-not-empty :name (tr "auth.name.not-all-space")) (fm/validate-length :name fm/max-length-allowed (tr "auth.name.too-long"))]) @@ -109,76 +82,43 @@ teams (mf/deref refs/teams)] - (if new-css-system - (if (< (count teams) 2) - [:div {:class (stl/css :modal-overlay)} - [:div.animated.fadeIn {:class (stl/css :modal-container)} - [:div {:class (stl/css :modal-left)} - [:div {:class (stl/css :first-block)} - [:h2 {:class (stl/css :modal-title)} - (tr "onboarding.team-modal.create-team")] - [:p {:class (stl/css :modal-text)} - (tr "onboarding.choice.team-up.create-team-desc")] - [:& fm/form {:form form - :class (stl/css :modal-form) - :on-submit on-submit} + (if (< (count teams) 2) + [:div {:class (stl/css :modal-overlay)} + [:div.animated.fadeIn {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-left)} + [:div {:class (stl/css :first-block)} + [:h2 {:class (stl/css :modal-title)} + (tr "onboarding.team-modal.create-team")] + [:p {:class (stl/css :modal-text)} + (tr "onboarding.choice.team-up.create-team-desc")] + [:& fm/form {:form form + :class (stl/css :modal-form) + :on-submit on-submit} - [:& fm/input {:type "text" - :class (stl/css :team-name-input) - :name :name - :placeholder "Team name" - :label (tr "onboarding.choice.team-up.create-team-placeholder")}] - - [:div {:class (stl/css :action-buttons)} - [:& fm/submit-button* - {:className (stl/css :accept-button) - :label (tr "onboarding.choice.team-up.continue-creating-team")}]]]] - [:div {:class (stl/css :second-block)} - [:h2 {:class (stl/css :modal-title)} - (tr "onboarding.choice.team-up.start-without-a-team")] - [:p {:class (stl/css :modal-text)} - (tr "onboarding.choice.team-up.start-without-a-team-description")] - - [:div {:class (stl/css :action-buttons)} - [:button {:class (stl/css :accept-button) - :on-click on-skip} - (tr "onboarding.choice.team-up.continue-without-a-team")]]]] - [:& team-modal-right] - [:div {:class (stl/css :paginator)} "1/2"]]] - - (st/emit! (modal/hide))) - - - (if (< (count teams) 2) - - [:div.modal-overlay - [:div.modal-container.onboarding-team.animated.fadeIn - [:div.team-left - [:h2.title (tr "onboarding.team-modal.create-team")] - [:p.info (tr "onboarding.choice.team-up.create-team-desc")] - [:& fm/form {:form form - :on-submit on-submit} - [:& fm/input {:type "text" - :name :name - :label (tr "onboarding.choice.team-up.create-team-placeholder")}] + [:& fm/input {:type "text" + :class (stl/css :team-name-input) + :name :name + :placeholder "Team name" + :label (tr "onboarding.choice.team-up.create-team-placeholder")}] + [:div {:class (stl/css :action-buttons)} [:& fm/submit-button* - {:label (tr "onboarding.choice.team-up.continue-creating-team")}]] + {:className (stl/css :accept-button) + :label (tr "onboarding.choice.team-up.continue-creating-team")}]]]] + [:div {:class (stl/css :second-block)} + [:h2 {:class (stl/css :modal-title)} + (tr "onboarding.choice.team-up.start-without-a-team")] + [:p {:class (stl/css :modal-text)} + (tr "onboarding.choice.team-up.start-without-a-team-description")] - [:h2.title (tr "onboarding.choice.team-up.start-without-a-team")] - [:p.info (tr "onboarding.choice.team-up.start-without-a-team-description")] + [:div {:class (stl/css :action-buttons)} + [:button {:class (stl/css :accept-button) + :on-click on-skip} + (tr "onboarding.choice.team-up.continue-without-a-team")]]]] + [:& team-modal-right] + [:div {:class (stl/css :paginator)} "1/2"]]] - [:div - [:button.btn-primary.btn-large {:on-click on-skip} (tr "onboarding.choice.team-up.continue-without-a-team")]]] - [:& team-modal-right] - [:div.paginator "1/2"] - - [:img.deco.square {:src "images/deco-square.svg" :border "0"}] - [:img.deco.circle {:src "images/deco-circle.svg" :border "0"}] - [:img.deco.line1 {:src "images/deco-line1.svg" :border "0"}] - [:img.deco.line2 {:src "images/deco-line2.svg" :border "0"}]]] - - (st/emit! (modal/hide)))))) + (st/emit! (modal/hide))))) (defn get-available-roles [] @@ -197,8 +137,7 @@ {::mf/register modal/components ::mf/register-as :onboarding-team-invitations} [{:keys [name] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - initial (mf/use-memo (constantly + (let [initial (mf/use-memo (constantly {:role "editor" :name name})) form (fm/use-form :spec ::invite-form @@ -265,96 +204,48 @@ (on-invite-now form) (on-invite-later form)))))] - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div.animated.fadeIn {:class (stl/css :modal-container)} - [:div {:class (stl/css :modal-left)} - [:h2 {:class (stl/css :modal-title)} (tr "onboarding.choice.team-up.invite-members")] - [:p {:class (stl/css :modal-text)} (tr "onboarding.choice.team-up.invite-members-info")] + [:div {:class (stl/css :modal-overlay)} + [:div.animated.fadeIn {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-left)} + [:h2 {:class (stl/css :modal-title)} (tr "onboarding.choice.team-up.invite-members")] + [:p {:class (stl/css :modal-text)} (tr "onboarding.choice.team-up.invite-members-info")] - [:div {:class (stl/css :modal-form)} - [:& fm/form {:form form - :on-submit on-submit} - [:div {:class (stl/css :role-select)} - [:p {:class (stl/css :role-title)} (tr "onboarding.choice.team-up.roles")] - [:& fm/select {:name :role :options roles}]] + [:div {:class (stl/css :modal-form)} + [:& fm/form {:form form + :on-submit on-submit} + [:div {:class (stl/css :role-select)} + [:p {:class (stl/css :role-title)} (tr "onboarding.choice.team-up.roles")] + [:& fm/select {:name :role :options roles}]] - [:div {:class (stl/css :invitation-row)} - [:& fm/multi-input {:type "email" - :name :emails - :auto-focus? true - :trim true - :valid-item-fn us/parse-email - :caution-item-fn #{} - :label (tr "modals.invite-member.emails") - :on-submit on-submit}]]] + [:div {:class (stl/css :invitation-row)} + [:& fm/multi-input {:type "email" + :name :emails + :auto-focus? true + :trim true + :valid-item-fn us/parse-email + :caution-item-fn #{} + :label (tr "modals.invite-member.emails") + :on-submit on-submit}]]] - [:div {:class (stl/css :action-buttons)} - [:button {:class (stl/css :back-button) - :on-click #(st/emit! (modal/show {:type :onboarding-team}) - (ptk/event ::ev/event {::ev/name "invite-members-back" - ::ev/origin "onboarding" - :name name - :step 2}))} - (tr "labels.back")] + [:div {:class (stl/css :action-buttons)} + [:button {:class (stl/css :back-button) + :on-click #(st/emit! (modal/show {:type :onboarding-team}) + (ptk/event ::ev/event {::ev/name "invite-members-back" + ::ev/origin "onboarding" + :name name + :step 2}))} + (tr "labels.back")] - [:& fm/submit-button* - {:className (stl/css :accept-button) - :label - (if (> (count emails) 0) - (tr "onboarding.choice.team-up.create-team-and-invite") - (tr "onboarding.choice.team-up.create-team-without-invite"))}]] - [:div {:class (stl/css :modal-hint)} - (tr "onboarding.choice.team-up.create-team-and-send-invites-description")]]] + [:& fm/submit-button* + {:className (stl/css :accept-button) + :label + (if (> (count emails) 0) + (tr "onboarding.choice.team-up.create-team-and-invite") + (tr "onboarding.choice.team-up.create-team-without-invite"))}]] + [:div {:class (stl/css :modal-hint)} + (tr "onboarding.choice.team-up.create-team-and-send-invites-description")]]] - [:& team-modal-right] - [:div {:class (stl/css :paginator)} "2/2"]]] - - - - [:div.modal-overlay - [:div.modal-container.onboarding-team-members.animated.fadeIn - [:div.team-left - [:h2.title (tr "onboarding.choice.team-up.invite-members")] - [:p.info (tr "onboarding.choice.team-up.invite-members-info")] - - [:& fm/form {:form form - :on-submit on-submit} - [:div.invite-row - [:div.role-wrapper - [:span.rol (tr "onboarding.choice.team-up.roles")] - [:& fm/select {:name :role :options roles}]] - - [:& fm/multi-input {:type "email" - :name :emails - :auto-focus? true - :trim true - :valid-item-fn us/parse-email - :caution-item-fn #{} - :on-submit on-submit - :label (tr "modals.invite-member.emails")}]] - - [:div.buttons - [:button.btn-secondary.btn-large - {:on-click #(st/emit! (modal/show {:type :onboarding-team}) - (ptk/event ::ev/event {::ev/name "invite-members-back" - ::ev/origin "onboarding" - :name name - :step 2}))} - (tr "labels.back")] - [:& fm/submit-button* - {:label - (if (> (count emails) 0) - (tr "onboarding.choice.team-up.create-team-and-send-invites") - (tr "onboarding.choice.team-up.create-team-without-inviting"))}]] - [:div.skip-action - (tr "onboarding.choice.team-up.create-team-and-send-invites-description")]]] - [:& team-modal-right] - [:div.paginator "2/2"] - - [:img.deco.square {:src "images/deco-square.svg" :border "0"}] - [:img.deco.circle {:src "images/deco-circle.svg" :border "0"}] - [:img.deco.line1 {:src "images/deco-line1.svg" :border "0"}] - [:img.deco.line2 {:src "images/deco-line2.svg" :border "0"}]]]))) + [:& team-modal-right] + [:div {:class (stl/css :paginator)} "2/2"]]])) diff --git a/frontend/src/app/main/ui/settings.cljs b/frontend/src/app/main/ui/settings.cljs index 946f6f1c6c..8b024332d5 100644 --- a/frontend/src/app/main/ui/settings.cljs +++ b/frontend/src/app/main/ui/settings.cljs @@ -10,7 +10,6 @@ [app.main.data.dashboard.shortcuts :as sc] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.hooks :as hooks] [app.main.ui.settings.access-tokens :refer [access-tokens-page]] [app.main.ui.settings.change-email] @@ -20,7 +19,6 @@ [app.main.ui.settings.password :refer [password-page]] [app.main.ui.settings.profile :refer [profile-page]] [app.main.ui.settings.sidebar :refer [sidebar]] - [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.router :as rt] [rumext.v2 :as mf])) @@ -28,21 +26,13 @@ (mf/defc header {::mf/wrap [mf/memo]} [] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:header {:class (stl/css :dashboard-header)} - [:div {:class (stl/css :dashboard-title)} - [:h1 {:data-test "account-title"} (tr "dashboard.your-account-title")]]] - - ;; OLD - [:header.dashboard-header - [:div.dashboard-title - [:h1 {:data-test "account-title"} (tr "dashboard.your-account-title")]]]))) + [:header {:class (stl/css :dashboard-header)} + [:div {:class (stl/css :dashboard-title)} + [:h1 {:data-test "account-title"} (tr "dashboard.your-account-title")]]]) (mf/defc settings [{:keys [route] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - section (get-in route [:data :name]) + (let [section (get-in route [:data :name]) profile (mf/deref refs/profile) locale (mf/deref i18n/locale)] @@ -52,53 +42,26 @@ #(when (nil? profile) (st/emit! (rt/nav :auth-login)))) - (if new-css-system - [:section {:class (stl/css :dashboard-layout-refactor :dashboard)} - [:& sidebar {:profile profile - :locale locale - :section section}] + [:section {:class (stl/css :dashboard-layout-refactor :dashboard)} + [:& sidebar {:profile profile + :locale locale + :section section}] - [:div {:class (stl/css :dashboard-content)} - [:& header] - [:section {:class (stl/css :dashboard-container)} - (case section - :settings-profile - [:& profile-page {:locale locale}] + [:div {:class (stl/css :dashboard-content)} + [:& header] + [:section {:class (stl/css :dashboard-container)} + (case section + :settings-profile + [:& profile-page {:locale locale}] - :settings-feedback - [:& feedback-page] + :settings-feedback + [:& feedback-page] - :settings-password - [:& password-page {:locale locale}] + :settings-password + [:& password-page {:locale locale}] - :settings-options - [:& options-page {:locale locale}] + :settings-options + [:& options-page {:locale locale}] - :settings-access-tokens - [:& access-tokens-page])]]] - - ;; OLD - [:section {:class (dom/classnames :dashboard-layout (not new-css-system) - :dashboard-layout-refactor new-css-system)} - [:& sidebar {:profile profile - :locale locale - :section section}] - - [:div.dashboard-content - [:& header] - [:section.dashboard-container - (case section - :settings-profile - [:& profile-page {:locale locale}] - - :settings-feedback - [:& feedback-page] - - :settings-password - [:& password-page {:locale locale}] - - :settings-options - [:& options-page {:locale locale}] - - :settings-access-tokens - [:& access-tokens-page])]]]))) + :settings-access-tokens + [:& access-tokens-page])]]])) diff --git a/frontend/src/app/main/ui/settings/access_tokens.cljs b/frontend/src/app/main/ui/settings/access_tokens.cljs index a044455aed..cb71674f34 100644 --- a/frontend/src/app/main/ui/settings/access_tokens.cljs +++ b/frontend/src/app/main/ui/settings/access_tokens.cljs @@ -14,7 +14,6 @@ [app.main.store :as st] [app.main.ui.components.context-menu-a11y :refer [context-menu-a11y]] [app.main.ui.components.forms :as fm] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -51,8 +50,7 @@ {::mf/register modal/components ::mf/register-as :access-token} [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - form (fm/use-form + (let [form (fm/use-form :initial initial-data :spec ::access-token-form :validators [name-validator @@ -107,176 +105,96 @@ :content (tr "dashboard.access-tokens.copied-success") :timeout 1000}))))] - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-container)} - [:& fm/form {:form form :on-submit on-submit} + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:& fm/form {:form form :on-submit on-submit} - [:div {:class (stl/css :modal-header)} - [:h2 {:class (stl/css :modal-title)} (tr "modals.create-access-token.title")] + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} (tr "modals.create-access-token.title")] - [:button {:class (stl/css :modal-close-btn) - :on-click on-close} i/close-refactor]] + [:button {:class (stl/css :modal-close-btn) + :on-click on-close} i/close-refactor]] - [:div {:class (stl/css :modal-content)} - [:div {:class (stl/css :fields-row)} - [:& fm/input {:type "text" - :auto-focus? true - :form form - :name :name - :disabled @created? - :label (tr "modals.create-access-token.name.label") - :show-success? true - :placeholder (tr "modals.create-access-token.name.placeholder")}]] + [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :fields-row)} + [:& fm/input {:type "text" + :auto-focus? true + :form form + :name :name + :disabled @created? + :label (tr "modals.create-access-token.name.label") + :show-success? true + :placeholder (tr "modals.create-access-token.name.placeholder")}]] - [:div {:class (stl/css :fields-row)} - [:div {:class (stl/css :select-title)} (tr "modals.create-access-token.expiration-date.label")] - [:& fm/select {:options [{:label (tr "dashboard.access-tokens.expiration-never") :value "never" :key "never"} - {:label (tr "dashboard.access-tokens.expiration-30-days") :value "720h" :key "720h"} - {:label (tr "dashboard.access-tokens.expiration-60-days") :value "1440h" :key "1440h"} - {:label (tr "dashboard.access-tokens.expiration-90-days") :value "2160h" :key "2160h"} - {:label (tr "dashboard.access-tokens.expiration-180-days") :value "4320h" :key "4320h"}] - :default "never" - :disabled @created? - :name :expiration-date}] - (when @created? - [:span.token-created-info - (if (:expires-at created) - (tr "dashboard.access-tokens.token-will-expire" (dt/format-date-locale (:expires-at created) {:locale locale})) - (tr "dashboard.access-tokens.token-will-not-expire"))])] + [:div {:class (stl/css :fields-row)} + [:div {:class (stl/css :select-title)} (tr "modals.create-access-token.expiration-date.label")] + [:& fm/select {:options [{:label (tr "dashboard.access-tokens.expiration-never") :value "never" :key "never"} + {:label (tr "dashboard.access-tokens.expiration-30-days") :value "720h" :key "720h"} + {:label (tr "dashboard.access-tokens.expiration-60-days") :value "1440h" :key "1440h"} + {:label (tr "dashboard.access-tokens.expiration-90-days") :value "2160h" :key "2160h"} + {:label (tr "dashboard.access-tokens.expiration-180-days") :value "4320h" :key "4320h"}] + :default "never" + :disabled @created? + :name :expiration-date}] + (when @created? + [:span {:class (stl/css :token-created-info)} + (if (:expires-at created) + (tr "dashboard.access-tokens.token-will-expire" (dt/format-date-locale (:expires-at created) {:locale locale})) + (tr "dashboard.access-tokens.token-will-not-expire"))])] - [:div {:class (stl/css :fields-row)} - (when @created? - [:div {:class (stl/css :custon-input-wrapper)} - [:input {:type "text" - :value (:token created "") - :class (stl/css :custom-input-token) - :placeholder (tr "modals.create-access-token.token") - :read-only true}] - [:button {:title (tr "modals.create-access-token.copy-token") - :class (stl/css :copy-btn) - :on-click copy-token} - i/clipboard-refactor]]) - #_(when @created? + [:div {:class (stl/css :fields-row)} + (when @created? + [:div {:class (stl/css :custon-input-wrapper)} + [:input {:type "text" + :value (:token created "") + :class (stl/css :custom-input-token) + :placeholder (tr "modals.create-access-token.token") + :read-only true}] + [:button {:title (tr "modals.create-access-token.copy-token") + :class (stl/css :copy-btn) + :on-click copy-token} + i/clipboard-refactor]]) + #_(when @created? [:button {:class (stl/css :copy-btn) :title (tr "modals.create-access-token.copy-token") :on-click copy-token} - [:span {:class (stl/css :token-value)}(:token created "")] + [:span {:class (stl/css :token-value)} (:token created "")] [:span {:class (stl/css :icon)} i/clipboard-refactor]])]] - [:div {:class (stl/css :modal-footer)} - [:div {:class (stl/css :action-buttons)} + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} - (if @created? - [:input {:class (stl/css :cancel-button) - :type "button" - :value (tr "labels.close") - :on-click #(modal/hide!)}] - [:* - [:input {:class (stl/css :cancel-button) - :type "button" - :value (tr "labels.cancel") - :on-click #(modal/hide!)}] - [:> fm/submit-button* - {:label (tr "modals.create-access-token.submit-label")}]])]]]]] - - - [:div.modal-overlay - [:div.modal-container.access-tokens-modal - [:& fm/form {:form form :on-submit on-submit} - - [:div.modal-header - [:div.modal-header-title - [:h2 (tr "modals.create-access-token.title")]] - - [:div.modal-close-button - {:on-click on-close} i/close]] - - [:div.modal-content.generic-form - [:div.fields-container - [:div.fields-row - [:& fm/input {:type "text" - :auto-focus? true - :form form - :name :name - :disabled @created? - :label (tr "modals.create-access-token.name.label") - :placeholder (tr "modals.create-access-token.name.placeholder")}]] - - [:div.fields-row - [:& fm/select {:options [{:label (tr "dashboard.access-tokens.expiration-never") :value "never" :key "never"} - {:label (tr "dashboard.access-tokens.expiration-30-days") :value "720h" :key "720h"} - {:label (tr "dashboard.access-tokens.expiration-60-days") :value "1440h" :key "1440h"} - {:label (tr "dashboard.access-tokens.expiration-90-days") :value "2160h" :key "2160h"} - {:label (tr "dashboard.access-tokens.expiration-180-days") :value "4320h" :key "4320h"}] - :label (tr "modals.create-access-token.expiration-date.label") - :default "never" - :disabled @created? - :name :expiration-date}] - (when @created? - [:span.token-created-info - (if (:expires-at created) - (tr "dashboard.access-tokens.token-will-expire" (dt/format-date-locale (:expires-at created) {:locale locale})) - (tr "dashboard.access-tokens.token-will-not-expire"))])] - - [:div.fields-row.access-token-created - (when @created? - [:div.custom-input.with-icon - [:input {:type "text" - :value (:token created "") - :placeholder (tr "modals.create-access-token.token") - :read-only true}] - [:button.help-icon {:title (tr "modals.create-access-token.copy-token") - :on-click copy-token} - i/copy]])]]] - - [:div.modal-footer - [:div.action-buttons - (if @created? - [:input.cancel-button - {:type "button" - :value (tr "labels.close") - :on-click #(modal/hide!)}] - [:* - [:input.cancel-button - {:type "button" - :value (tr "labels.cancel") - :on-click #(modal/hide!)}] - [:> fm/submit-button* - {:label (tr "modals.create-access-token.submit-label")}]])]]]]]))) + (if @created? + [:input {:class (stl/css :cancel-button) + :type "button" + :value (tr "labels.close") + :on-click #(modal/hide!)}] + [:* + [:input {:class (stl/css :cancel-button) + :type "button" + :value (tr "labels.cancel") + :on-click #(modal/hide!)}] + [:> fm/submit-button* + {:label (tr "modals.create-access-token.submit-label")}]])]]]]])) (mf/defc access-tokens-hero [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - on-click (mf/use-fn #(st/emit! (modal/show :access-token {})))] - (if new-css-system - [:div {:class (stl/css :access-tokens-hero-container)} - [:div {:class (stl/css :access-tokens-hero)} - [:div {:class (stl/css :desc)} - [:h2 (tr "dashboard.access-tokens.personal")] - [:p (tr "dashboard.access-tokens.personal.description")]] + (let [on-click (mf/use-fn #(st/emit! (modal/show :access-token {})))] + [:div {:class (stl/css :access-tokens-hero-container)} + [:div {:class (stl/css :access-tokens-hero)} + [:div {:class (stl/css :desc)} + [:h2 (tr "dashboard.access-tokens.personal")] + [:p (tr "dashboard.access-tokens.personal.description")]] - [:button - {:class (stl/css :btn-primary) - :on-click on-click} - [:span (tr "dashboard.access-tokens.create")]]]] - - ;; OLD - [:div.access-tokens-hero-container - [:div.access-tokens-hero - [:div.desc - [:h2 (tr "dashboard.access-tokens.personal")] - [:p (tr "dashboard.access-tokens.personal.description")]] - - [:button.btn-primary - {:on-click on-click} - [:span (tr "dashboard.access-tokens.create")]]]]))) + [:button + {:class (stl/css :btn-primary) + :on-click on-click} + [:span (tr "dashboard.access-tokens.create")]]]])) (mf/defc access-token-actions [{:keys [on-delete]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - local (mf/use-state {:menu-open false}) + (let [local (mf/use-state {:menu-open false}) show? (:menu-open @local) options (mf/with-memo [on-delete] [{:option-name (tr "labels.delete") @@ -302,47 +220,25 @@ (dom/stop-propagation event) (on-menu-click event))))] - (if new-css-system - [:div - {:class (stl/css :icon) - :tab-index "0" - :ref menu-ref - :on-click on-menu-click - :on-key-down on-keydown} - i/actions - [:& context-menu-a11y - {:on-close on-menu-close - :show show? - :fixed? true - :min-width? true - :top "auto" - :left "auto" - :options options}]] - - ;; OLD - [:div.icon - {:tab-index "0" - :ref menu-ref - :on-click on-menu-click - :on-key-down (fn [event] - (when (kbd/enter? event) - (dom/stop-propagation event) - (on-menu-click event)))} - i/actions - [:& context-menu-a11y - {:on-close on-menu-close - :show show? - :fixed? true - :min-width? true - :top "auto" - :left "auto" - :options options}]]))) + [:div {:class (stl/css :icon) + :tab-index "0" + :ref menu-ref + :on-click on-menu-click + :on-key-down on-keydown} + i/actions + [:& context-menu-a11y + {:on-close on-menu-close + :show show? + :fixed? true + :min-width? true + :top "auto" + :left "auto" + :options options}]])) (mf/defc access-token-item {::mf/wrap [mf/memo]} [{:keys [token] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - locale (mf/deref i18n/locale) + (let [locale (mf/deref i18n/locale) expires-at (:expires-at token) expires-txt (some-> expires-at (dt/format-date-locale {:locale locale})) expired? (and (some? expires-at) (> (dt/now) expires-at)) @@ -366,66 +262,36 @@ :accept-label (tr "modals.delete-acces-token.accept") :on-accept delete-fn}))))] - (if new-css-system - [:div {:class (stl/css :table-row)} - [:div {:class (stl/css :table-field :name)} - (str (:name token))] + [:div {:class (stl/css :table-row)} + [:div {:class (stl/css :table-field :name)} + (str (:name token))] - [:div {:class (stl/css :table-field :expiration-date)} - [:span {:class (stl/css-case :expired expired? :content true)} - (cond - (nil? expires-at) (tr "dashboard.access-tokens.no-expiration") - expired? (tr "dashboard.access-tokens.expired-on" expires-txt) - :else (tr "dashboard.access-tokens.expires-on" expires-txt))]] - [:div {:class (stl/css :table-field :actions)} - [:& access-token-actions - {:on-delete on-delete}]]] - - ;; OLD - [:div.table-row - [:div.table-field.name - (str (:name token))] - [:div.table-field.expiration-date - [:span.content {:class (when expired? "expired")} - (cond - (nil? expires-at) (tr "dashboard.access-tokens.no-expiration") - expired? (tr "dashboard.access-tokens.expired-on" expires-txt) - :else (tr "dashboard.access-tokens.expires-on" expires-txt))]] - [:div.table-field.actions - [:& access-token-actions - {:on-delete on-delete}]]]))) + [:div {:class (stl/css :table-field :expiration-date)} + [:span {:class (stl/css-case :expired expired? :content true)} + (cond + (nil? expires-at) (tr "dashboard.access-tokens.no-expiration") + expired? (tr "dashboard.access-tokens.expired-on" expires-txt) + :else (tr "dashboard.access-tokens.expires-on" expires-txt))]] + [:div {:class (stl/css :table-field :actions)} + [:& access-token-actions + {:on-delete on-delete}]]])) (mf/defc access-tokens-page [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - tokens (mf/deref tokens-ref)] + (let [tokens (mf/deref tokens-ref)] (mf/with-effect [] (dom/set-html-title (tr "title.settings.access-tokens")) (st/emit! (du/fetch-access-tokens))) - (if new-css-system - [:div {:class (stl/css :dashboard-access-tokens)} - [:div - [:& access-tokens-hero] - (if (empty? tokens) - [:div {:class (stl/css :access-tokens-empty)} - [:div (tr "dashboard.access-tokens.empty.no-access-tokens")] - [:div (tr "dashboard.access-tokens.empty.add-one")]] - [:div {:class (stl/css :dashboard-table)} - [:div {:class (stl/css :table-rows)} - (for [token tokens] - [:& access-token-item {:token token :key (:id token)}])]])]] - - ;; OLD - [:div.dashboard-access-tokens - [:div - [:& access-tokens-hero] - (if (empty? tokens) - [:div.access-tokens-empty - [:div (tr "dashboard.access-tokens.empty.no-access-tokens")] - [:div (tr "dashboard.access-tokens.empty.add-one")]] - [:div.dashboard-table - [:div.table-rows - (for [token tokens] - [:& access-token-item {:token token :key (:id token)}])]])]]))) + [:div {:class (stl/css :dashboard-access-tokens)} + [:div + [:& access-tokens-hero] + (if (empty? tokens) + [:div {:class (stl/css :access-tokens-empty)} + [:div (tr "dashboard.access-tokens.empty.no-access-tokens")] + [:div (tr "dashboard.access-tokens.empty.add-one")]] + [:div {:class (stl/css :dashboard-table)} + [:div {:class (stl/css :table-rows)} + (for [token tokens] + [:& access-token-item {:token token :key (:id token)}])]])]])) diff --git a/frontend/src/app/main/ui/settings/access_tokens.scss b/frontend/src/app/main/ui/settings/access_tokens.scss index 2e9b8ef178..50bdef0af3 100644 --- a/frontend/src/app/main/ui/settings/access_tokens.scss +++ b/frontend/src/app/main/ui/settings/access_tokens.scss @@ -12,88 +12,6 @@ font-size: $fs-16; margin-top: $s-20; width: $s-800; - - .table-header { - color: $df-secondary; - display: grid; - font-size: $fs-12; - grid-template-columns: 43% 1fr $s-108 $s-12; - height: $s-40; - max-width: $s-1000; - padding: 0 $s-16; - text-transform: uppercase; - width: 100%; - } - - .table-rows { - color: $db-secondary; - display: flex; - flex-direction: column; - margin-top: $s-16; - max-width: $s-1000; - padding-top: 0; - width: 100%; - } - - .table-row { - align-items: center; - background-color: $db-tertiary; - border-radius: $br-8; - color: $df-primary; - display: grid; - font-size: $fs-14; - grid-template-columns: 1fr 43% $s-12; - height: fit-content; - min-height: $s-40; - padding: 0 $s-16; - width: 100%; - - &:not(:first-child) { - margin-top: $s-8; - } - } - - .table-field { - display: flex; - align-items: center; - - .icon { - padding-left: $s-12; - cursor: pointer; - } - - &.name { - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; - color: $df-primary; - display: -webkit-box; - max-width: $s-480; - overflow: hidden; - text-overflow: ellipsis; - } - - &.expiration-date { - color: $df-secondary; - font-size: $fs-14; - - .content { - padding: $s-2 $s-6; - &.expired { - background-color: var(--warning-color); - border-radius: $br-4; - color: $db-secondary; - } - } - } - &.access-token-created { - word-break: break-all; - } - - &.actions { - position: relative; - } - } - svg { width: $s-12; height: $s-12; @@ -101,6 +19,87 @@ } } +.table-header { + color: $df-secondary; + display: grid; + font-size: $fs-12; + grid-template-columns: 43% 1fr $s-108 $s-12; + height: $s-40; + max-width: $s-1000; + padding: 0 $s-16; + text-transform: uppercase; + width: 100%; +} + +.table-rows { + color: $db-secondary; + display: flex; + flex-direction: column; + margin-top: $s-16; + max-width: $s-1000; + padding-top: 0; + width: 100%; +} + +.table-row { + align-items: center; + background-color: $db-tertiary; + border-radius: $br-8; + color: $df-primary; + display: grid; + font-size: $fs-14; + grid-template-columns: 1fr 43% $s-12; + height: fit-content; + min-height: $s-40; + padding: 0 $s-16; + width: 100%; + + &:not(:first-child) { + margin-top: $s-8; + } +} + +.table-field { + display: flex; + align-items: center; + + .icon { + padding-left: $s-12; + cursor: pointer; + } + + &.name { + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + color: $df-primary; + display: -webkit-box; + max-width: $s-480; + overflow: hidden; + text-overflow: ellipsis; + } + + &.expiration-date { + color: $df-secondary; + font-size: $fs-14; + + .content { + padding: $s-2 $s-6; + &.expired { + background-color: var(--warning-color); + border-radius: $br-4; + color: $db-secondary; + } + } + } + &.access-token-created { + word-break: break-all; + } + + &.actions { + position: relative; + } +} + .dashboard-access-tokens { display: flex; flex-direction: column; @@ -171,75 +170,85 @@ .modal-overlay { @extend .modal-overlay-base; - .modal-container { - @extend .modal-container-base; - min-width: $s-408; - border: $s-1 solid var(--modal-border-color); - .modal-header { - margin-bottom: $s-24; - .modal-title { - @include tabTitleTipography; - color: var(--modal-title-foreground-color); - } - .modal-close-btn { - @extend .modal-close-btn-base; - } - } +} - .modal-content { - @include flexColumn; - gap: $s-24; - @include titleTipography; - margin-bottom: $s-24; +.modal-container { + @extend .modal-container-base; + min-width: $s-408; + border: $s-1 solid var(--modal-border-color); +} - .fields-row { - @include flexColumn; - .select-title { - @include titleTipography; - color: var(--modal-title-foreground-color); - } - .custon-input-wrapper { - @include flexRow; - border-radius: $br-8; - height: $s-32; - background-color: var(--input-background-color); - } - .custom-input-token { - @extend .input-element; - margin: 0; - flex-grow: 1; - &:focus { - outline: none; - border: $s-1 solid var(--input-border-color-active); - } - } - .token-value { - @include textEllipsis; - @include titleTipography; - flex-grow: 1; - } - .copy-btn { - @include flexCenter; - @extend .button-secondary; - height: $s-28; - width: $s-28; - svg { - @extend .button-icon-small; - } - } - } - } - - .modal-footer { - .action-buttons { - @extend .modal-action-btns; - button { - @extend .modal-accept-btn; - } - .cancel-button { - @extend .modal-cancel-btn; - } - } - } +.modal-header { + margin-bottom: $s-24; + .modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); + } + .modal-close-btn { + @extend .modal-close-btn-base; } } + +.modal-content { + @include flexColumn; + gap: $s-24; + @include titleTipography; + margin-bottom: $s-24; +} + +.fields-row { + @include flexColumn; +} + +.select-title { + @include titleTipography; + color: var(--modal-title-foreground-color); +} + +.custon-input-wrapper { + @include flexRow; + border-radius: $br-8; + height: $s-32; + background-color: var(--input-background-color); +} + +.custom-input-token { + @extend .input-element; + margin: 0; + flex-grow: 1; + &:focus { + outline: none; + border: $s-1 solid var(--input-border-color-active); + } +} + +.token-value { + @include textEllipsis; + @include titleTipography; + flex-grow: 1; +} + +.copy-btn { + @include flexCenter; + @extend .button-secondary; + height: $s-28; + width: $s-28; + svg { + @extend .button-icon-small; + } +} + +.token-created-info { + color: var(--modal-text-foreground-color); +} + +.action-buttons { + @extend .modal-action-btns; + button { + @extend .modal-accept-btn; + } +} + +.cancel-button { + @extend .modal-cancel-btn; +} diff --git a/frontend/src/app/main/ui/settings/change_email.cljs b/frontend/src/app/main/ui/settings/change_email.cljs index 08ab6ea6f7..37d4f54c7f 100644 --- a/frontend/src/app/main/ui/settings/change_email.cljs +++ b/frontend/src/app/main/ui/settings/change_email.cljs @@ -16,7 +16,6 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.forms :as fm] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.ui.messages :as msgs] [app.util.i18n :as i18n :refer [tr]] @@ -76,8 +75,7 @@ {::mf/register modal/components ::mf/register-as :change-email} [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - profile (mf/deref refs/profile) + (let [profile (mf/deref refs/profile) form (fm/use-form :spec ::email-change-form :validators [email-equality] :initial profile) @@ -99,82 +97,44 @@ (when (and different-emails-error? (= email-1 email-2)) (swap! form d/dissoc-in [:errors :email-2])))))] - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-container)} - [:& fm/form {:form form - :on-submit on-submit} + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:& fm/form {:form form + :on-submit on-submit} - [:div {:class (stl/css :modal-header)} - [:h2 {:class (stl/css :modal-title) - :data-test "change-email-title"} - (tr "modals.change-email.title")] - [:button {:class (stl/css :modal-close-btn) - :on-click on-close} i/close-refactor]] + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title) + :data-test "change-email-title"} + (tr "modals.change-email.title")] + [:button {:class (stl/css :modal-close-btn) + :on-click on-close} i/close-refactor]] - [:div {:class (stl/css :modal-content)} - [:& msgs/inline-banner - {:type :info - :content (tr "modals.change-email.info" (:email profile))}] + [:div {:class (stl/css :modal-content)} + [:& msgs/inline-banner + {:type :info + :content (tr "modals.change-email.info" (:email profile))}] - [:div {:class (stl/css :fields-row)} - [:& fm/input {:type "email" - :name :email-1 - :label (tr "modals.change-email.new-email") - :trim true - :show-success? true - :on-change-value on-email-change}]] + [:div {:class (stl/css :fields-row)} + [:& fm/input {:type "email" + :name :email-1 + :label (tr "modals.change-email.new-email") + :trim true + :show-success? true + :on-change-value on-email-change}]] - [:div {:class (stl/css :fields-row)} - [:& fm/input {:type "email" - :name :email-2 - :label (tr "modals.change-email.confirm-email") - :trim true - :show-success? true - :on-change-value on-email-change}]]] + [:div {:class (stl/css :fields-row)} + [:& fm/input {:type "email" + :name :email-2 + :label (tr "modals.change-email.confirm-email") + :trim true + :show-success? true + :on-change-value on-email-change}]]] - [:div {:class (stl/css :modal-footer)} - [:div {:class (stl/css :action-buttons) - :data-test "change-email-submit"} - [:> fm/submit-button* - {:label (tr "modals.change-email.submit")}]]]]]] - - - [:div.modal-overlay - [:div.modal-container.change-email-modal.form-container - [:& fm/form {:form form - :on-submit on-submit} - - [:div.modal-header - [:div.modal-header-title - [:h2 {:data-test "change-email-title"} - (tr "modals.change-email.title")]] - [:div.modal-close-button - {:on-click on-close} i/close]] - - [:div.modal-content - [:& msgs/inline-banner - {:type :info - :content (tr "modals.change-email.info" (:email profile))}] - - [:div.fields-container - [:div.fields-row - [:& fm/input {:type "email" - :name :email-1 - :label (tr "modals.change-email.new-email") - :trim true - :on-change-value on-email-change}]] - [:div.fields-row - [:& fm/input {:type "email" - :name :email-2 - :label (tr "modals.change-email.confirm-email") - :trim true - :on-change-value on-email-change}]]]] - - [:div.modal-footer - [:div.action-buttons {:data-test "change-email-submit"} - [:> fm/submit-button* - {:label (tr "modals.change-email.submit")}]]]]]]) + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons) + :data-test "change-email-submit"} + [:> fm/submit-button* + {:label (tr "modals.change-email.submit")}]]]]]] )) diff --git a/frontend/src/app/main/ui/settings/change_email.scss b/frontend/src/app/main/ui/settings/change_email.scss index 6b6b760c99..97da12aeca 100644 --- a/frontend/src/app/main/ui/settings/change_email.scss +++ b/frontend/src/app/main/ui/settings/change_email.scss @@ -8,46 +8,50 @@ .modal-overlay { @extend .modal-overlay-base; - .modal-container { - @extend .modal-container-base; - min-width: $s-408; - border: $s-1 solid var(--modal-border-color); - .modal-header { - margin-bottom: $s-24; - .modal-title { - @include tabTitleTipography; - color: var(--modal-title-foreground-color); - } - .modal-close-btn { - @extend .modal-close-btn-base; - } - } +} - .modal-content { - @include flexColumn; - @include titleTipography; - gap: $s-24; - margin-bottom: $s-24; +.modal-container { + @extend .modal-container-base; + min-width: $s-408; + border: $s-1 solid var(--modal-border-color); +} - .fields-row { - @include flexColumn; - .select-title { - @include titleTipography; - color: var(--modal-title-foreground-color); - } - } - } +.modal-header { + margin-bottom: $s-24; +} - .modal-footer { - .action-buttons { - @extend .modal-action-btns; - button { - @extend .modal-accept-btn; - } - .cancel-button { - @extend .modal-cancel-btn; - } - } - } +.modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); +} + +.modal-close-btn { + @extend .modal-close-btn-base; +} + +.modal-content { + @include flexColumn; + @include titleTipography; + gap: $s-24; + margin-bottom: $s-24; +} + +.fields-row { + @include flexColumn; +} + +.select-title { + @include titleTipography; + color: var(--modal-title-foreground-color); +} + +.action-buttons { + @extend .modal-action-btns; + button { + @extend .modal-accept-btn; } } + +.cancel-button { + @extend .modal-cancel-btn; +} diff --git a/frontend/src/app/main/ui/settings/delete_account.cljs b/frontend/src/app/main/ui/settings/delete_account.cljs index a9c26b7305..2eddcca231 100644 --- a/frontend/src/app/main/ui/settings/delete_account.cljs +++ b/frontend/src/app/main/ui/settings/delete_account.cljs @@ -11,7 +11,6 @@ [app.main.data.modal :as modal] [app.main.data.users :as du] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.ui.messages :as msgs] [app.util.i18n :as i18n :refer [tr]] @@ -29,8 +28,7 @@ {::mf/register modal/components ::mf/register-as :delete-account} [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - on-close + (let [on-close (mf/use-callback #(st/emit! (modal/hide))) on-accept @@ -39,52 +37,28 @@ (du/request-account-deletion (with-meta {} {:on-error on-error}))))] - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} - [:div {:class (stl/css :modal-header)} + [:div {:class (stl/css :modal-header)} - [:h2 {:class (stl/css :modal-title)} (tr "modals.delete-account.title")] - [:button {:class (stl/css :modal-close-btn) - :on-click on-close} i/close-refactor]] + [:h2 {:class (stl/css :modal-title)} (tr "modals.delete-account.title")] + [:button {:class (stl/css :modal-close-btn) + :on-click on-close} i/close-refactor]] - [:div {:class (stl/css :modal-content)} - [:& msgs/inline-banner - {:type :warning - :content (tr "modals.delete-account.info")}]] + [:div {:class (stl/css :modal-content)} + [:& msgs/inline-banner + {:type :warning + :content (tr "modals.delete-account.info")}]] - [:div {:class (stl/css :modal-footer)} - [:div {:class (stl/css :action-buttons)} - [:button {:class (stl/css :cancel-button) - :on-click on-close} - (tr "modals.delete-account.cancel")] - [:button {:class (stl/css-case :accept-button true - :danger true) - :on-click on-accept - :data-test "delete-account-btn"} - (tr "modals.delete-account.confirm")]]]]] - - - - [:div.modal-overlay - [:div.modal-container.change-email-modal - [:div.modal-header - [:div.modal-header-title - [:h2 (tr "modals.delete-account.title")]] - [:div.modal-close-button - {:on-click on-close} i/close]] - - [:div.modal-content - [:& msgs/inline-banner - {:type :warning - :content (tr "modals.delete-account.info")}]] - - [:div.modal-footer - [:div.action-buttons - [:button.btn-danger.btn-large {:on-click on-accept - :data-test "delete-account-btn"} - (tr "modals.delete-account.confirm")] - [:button.btn-secondary.btn-large {:on-click on-close} - (tr "modals.delete-account.cancel")]]]]]))) + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:button {:class (stl/css :cancel-button) + :on-click on-close} + (tr "modals.delete-account.cancel")] + [:button {:class (stl/css-case :accept-button true + :danger true) + :on-click on-accept + :data-test "delete-account-btn"} + (tr "modals.delete-account.confirm")]]]]])) diff --git a/frontend/src/app/main/ui/settings/delete_account.scss b/frontend/src/app/main/ui/settings/delete_account.scss index e12ff29a8f..33e72dd4ac 100644 --- a/frontend/src/app/main/ui/settings/delete_account.scss +++ b/frontend/src/app/main/ui/settings/delete_account.scss @@ -8,50 +8,54 @@ .modal-overlay { @extend .modal-overlay-base; - .modal-container { - @extend .modal-container-base; - min-width: $s-408; - border: $s-1 solid var(--modal-border-color); - .modal-header { - margin-bottom: $s-24; - .modal-title { - @include tabTitleTipography; - color: var(--modal-title-foreground-color); - } - .modal-close-btn { - @extend .modal-close-btn-base; - } - } +} - .modal-content { - @include flexColumn; - @include titleTipography; - gap: $s-24; - margin-bottom: $s-24; +.modal-container { + @extend .modal-container-base; + min-width: $s-408; + border: $s-1 solid var(--modal-border-color); +} - .fields-row { - @include flexColumn; - .select-title { - @include titleTipography; - color: var(--modal-title-foreground-color); - } - } - } +.modal-header { + margin-bottom: $s-24; +} - .modal-footer { - .action-buttons { - @extend .modal-action-btns; +.modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); +} - .cancel-button { - @extend .modal-cancel-btn; - } - .accept-button { - @extend .modal-accept-btn; - &.danger { - @extend .modal-danger-btn; - } - } - } - } +.modal-close-btn { + @extend .modal-close-btn-base; +} + +.modal-content { + @include flexColumn; + @include titleTipography; + gap: $s-24; + margin-bottom: $s-24; +} + +.fields-row { + @include flexColumn; +} + +.select-title { + @include titleTipography; + color: var(--modal-title-foreground-color); +} + +.action-buttons { + @extend .modal-action-btns; +} + +.cancel-button { + @extend .modal-cancel-btn; +} + +.accept-button { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; } } diff --git a/frontend/src/app/main/ui/settings/feedback.cljs b/frontend/src/app/main/ui/settings/feedback.cljs index 46bbc1e1bf..82daf3c5b4 100644 --- a/frontend/src/app/main/ui/settings/feedback.cljs +++ b/frontend/src/app/main/ui/settings/feedback.cljs @@ -14,7 +14,6 @@ [app.main.repo :as rp] [app.main.store :as st] [app.main.ui.components.forms :as fm] - [app.main.ui.context :as ctx] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [beicon.v2.core :as rx] @@ -29,8 +28,7 @@ (mf/defc feedback-form [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - profile (mf/deref refs/profile) + (let [profile (mf/deref refs/profile) form (fm/use-form :spec ::feedback-form :validators [(fm/validate-length :subject fm/max-length-allowed (tr "auth.name.too-long")) (fm/validate-not-empty :subject (tr "auth.name.not-all-space"))]) @@ -62,101 +60,54 @@ (->> (rp/cmd! :send-user-feedback data) (rx/subs! on-succes on-error)))))] - (if new-css-system - [:& fm/form {:class (stl/css :feedback-form) - :on-submit on-submit - :form form} + [:& fm/form {:class (stl/css :feedback-form) + :on-submit on-submit + :form form} ;; --- Feedback section - [:h2 {:class (stl/css :field-title)} (tr "feedback.title")] - [:p {:class (stl/css :field-text)} (tr "feedback.subtitle")] + [:h2 {:class (stl/css :field-title)} (tr "feedback.title")] + [:p {:class (stl/css :field-text)} (tr "feedback.subtitle")] - [:div {:class (stl/css :fields-row)} - [:& fm/input {:label (tr "feedback.subject") - :name :subject - :show-success? true}]] - [:div {:class (stl/css :fields-row :description)} - [:& fm/textarea - {:label (tr "feedback.description") - :name :content - :rows 5}]] + [:div {:class (stl/css :fields-row)} + [:& fm/input {:label (tr "feedback.subject") + :name :subject + :show-success? true}]] + [:div {:class (stl/css :fields-row :description)} + [:& fm/textarea + {:label (tr "feedback.description") + :name :content + :rows 5}]] - [:> fm/submit-button* - {:label (if @loading (tr "labels.sending") (tr "labels.send")) - :disabled @loading}] + [:> fm/submit-button* + {:label (if @loading (tr "labels.sending") (tr "labels.send")) + :disabled @loading}] - [:hr] + [:hr] - [:h2 {:class (stl/css :field-title)} (tr "feedback.discourse-title")] - [:p {:class (stl/css :field-text)} (tr "feedback.discourse-subtitle1")] + [:h2 {:class (stl/css :field-title)} (tr "feedback.discourse-title")] + [:p {:class (stl/css :field-text)} (tr "feedback.discourse-subtitle1")] - [:a - {:class (stl/css :btn-secondary :btn-large) - :href "https://community.penpot.app" - :target "_blank"} - (tr "feedback.discourse-go-to")] - [:hr] + [:a + {:class (stl/css :btn-secondary :btn-large) + :href "https://community.penpot.app" + :target "_blank"} + (tr "feedback.discourse-go-to")] + [:hr] - [:h2 {:class (stl/css :field-title)} (tr "feedback.twitter-title")] - [:p {:class (stl/css :field-text)} (tr "feedback.twitter-subtitle1")] + [:h2 {:class (stl/css :field-title)} (tr "feedback.twitter-title")] + [:p {:class (stl/css :field-text)} (tr "feedback.twitter-subtitle1")] - [:a - {:class (stl/css :btn-secondary :btn-large) - :href "https://twitter.com/penpotapp" - :target "_blank"} - (tr "feedback.twitter-go-to")]] - - ;; OLD - [:& fm/form {:class "feedback-form" - :on-submit on-submit - :form form} - - ;; --- Feedback section - [:h2.field-title (tr "feedback.title")] - [:p.field-text (tr "feedback.subtitle")] - - [:div.fields-row - [:& fm/input {:label (tr "feedback.subject") - :name :subject}]] - [:div.fields-row - [:& fm/textarea - {:label (tr "feedback.description") - :name :content - :rows 5}]] - - [:> fm/submit-button* - {:label (if @loading (tr "labels.sending") (tr "labels.send")) - :disabled @loading}] - - [:hr] - - [:h2.field-title (tr "feedback.discourse-title")] - [:p.field-text (tr "feedback.discourse-subtitle1")] - - [:a.btn-secondary.btn-large - {:href "https://community.penpot.app" :target "_blank"} - (tr "feedback.discourse-go-to")] - [:hr] - - [:h2.field-title (tr "feedback.twitter-title")] - [:p.field-text (tr "feedback.twitter-subtitle1")] - - [:a.btn-secondary.btn-large - {:href "https://twitter.com/penpotapp" :target "_blank"} - (tr "feedback.twitter-go-to")]]))) + [:a + {:class (stl/css :btn-secondary :btn-large) + :href "https://twitter.com/penpotapp" + :target "_blank"} + (tr "feedback.twitter-go-to")]])) (mf/defc feedback-page [] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (mf/use-effect - #(dom/set-html-title (tr "title.settings.feedback"))) + (mf/use-effect + #(dom/set-html-title (tr "title.settings.feedback"))) - (if new-css-system - [:div {:class (stl/css :dashboard-settings)} - [:div {:class (stl/css :form-container)} - [:& feedback-form]]] - - ;; OLD - [:div.dashboard-settings - [:div.form-container - [:& feedback-form]]]))) + [:div {:class (stl/css :dashboard-settings)} + [:div {:class (stl/css :form-container)} + [:& feedback-form]]]) diff --git a/frontend/src/app/main/ui/settings/options.cljs b/frontend/src/app/main/ui/settings/options.cljs index 2ec2476590..34b539a84f 100644 --- a/frontend/src/app/main/ui/settings/options.cljs +++ b/frontend/src/app/main/ui/settings/options.cljs @@ -13,7 +13,6 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.forms :as fm] - [app.main.ui.context :as ctx] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [cljs.spec.alpha :as s] @@ -38,78 +37,49 @@ (mf/defc options-form {::mf/wrap-props false} [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - profile (mf/deref refs/profile) + (let [profile (mf/deref refs/profile) initial (mf/with-memo [profile] (update profile :lang #(or % ""))) form (fm/use-form :spec ::options-form :initial initial)] - (if new-css-system - [:& fm/form {:class (stl/css :options-form) - :on-submit on-submit - :form form} + [:& fm/form {:class (stl/css :options-form) + :on-submit on-submit + :form form} - [:h3 (tr "labels.language")] + [:h3 (tr "labels.language")] - [:div {:class (stl/css :fields-row)} - [:& fm/select {:options (into [{:label "Auto (browser)" :value ""}] - i18n/supported-locales) - :label (tr "dashboard.select-ui-language") - :default "" - :name :lang - :data-test "setting-lang"}]] + [:div {:class (stl/css :fields-row)} + [:& fm/select {:options (into [{:label "Auto (browser)" :value ""}] + i18n/supported-locales) + :label (tr "dashboard.select-ui-language") + :default "" + :name :lang + :data-test "setting-lang"}]] - [:h3 (tr "dashboard.theme-change")] - [:div {:class (stl/css :fields-row)} - [:& fm/select {:label (tr "dashboard.select-ui-theme") - :name :theme - :default "default" - :options [{:label "Penpot Dark (default)" :value "default"} - {:label "Penpot Light" :value "light"}] - :data-test "setting-theme"}]] + [:h3 (tr "dashboard.theme-change")] + [:div {:class (stl/css :fields-row)} + [:& fm/select {:label (tr "dashboard.select-ui-theme") + :name :theme + :default "default" + :options [{:label "Penpot Dark (default)" :value "default"} + {:label "Penpot Light" :value "light"}] + :data-test "setting-theme"}]] - [:> fm/submit-button* - {:label (tr "dashboard.update-settings") - :data-test "submit-lang-change" - :class (stl/css :btn-primary)}]] - - ;; OLD - [:& fm/form {:class "options-form" - :on-submit on-submit - :form form} - - [:h2 (tr "labels.language")] - - [:div.fields-row - [:& fm/select {:options (into [{:label "Auto (browser)" :value ""}] - i18n/supported-locales) - :label (tr "dashboard.select-ui-language") - :default "" - :name :lang - :data-test "setting-lang"}]] - - [:> fm/submit-button* - {:label (tr "dashboard.update-settings") - :data-test "submit-lang-change"}]]))) + [:> fm/submit-button* + {:label (tr "dashboard.update-settings") + :data-test "submit-lang-change" + :class (stl/css :btn-primary)}]])) ;; --- Password Page (mf/defc options-page [] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (mf/use-effect - #(dom/set-html-title (tr "title.settings.options"))) + (mf/use-effect + #(dom/set-html-title (tr "title.settings.options"))) - (if new-css-system - [:div {:class (stl/css :dashboard-settings)} - [:div {:class (stl/css :form-container) :data-test "settings-form"} - [:h2 (tr "labels.settings")] - [:& options-form {}]]] - - ;; OLD - [:div.dashboard-settings - [:div.form-container - {:data-test "settings-form"} - [:& options-form {}]]]))) + [:div {:class (stl/css :dashboard-settings)} + [:div {:class (stl/css :form-container) :data-test "settings-form"} + [:h2 (tr "labels.settings")] + [:& options-form {}]]]) diff --git a/frontend/src/app/main/ui/settings/password.cljs b/frontend/src/app/main/ui/settings/password.cljs index 0b9b1b5a0a..f96edbc306 100644 --- a/frontend/src/app/main/ui/settings/password.cljs +++ b/frontend/src/app/main/ui/settings/password.cljs @@ -12,7 +12,6 @@ [app.main.data.users :as udu] [app.main.store :as st] [app.main.ui.components.forms :as fm] - [app.main.ui.context :as ctx] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [t tr]] [cljs.spec.alpha :as s] @@ -71,88 +70,51 @@ (mf/defc password-form [{:keys [locale] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - initial (mf/use-memo (constantly {:password-old nil})) + (let [initial (mf/use-memo (constantly {:password-old nil})) form (fm/use-form :spec ::password-form :validators [(fm/validate-not-all-spaces :password-old (tr "auth.password-not-empty")) (fm/validate-not-empty :password-1 (tr "auth.password-not-empty")) (fm/validate-not-empty :password-2 (tr "auth.password-not-empty")) password-equality] :initial initial)] - (if new-css-system - [:& fm/form {:class (stl/css :password-form) - :on-submit on-submit - :form form} + [:& fm/form {:class (stl/css :password-form) + :on-submit on-submit + :form form} - [:div {:class (stl/css :fields-row)} - [:& fm/input - {:type "password" - :name :password-old - :auto-focus? true - :label (t locale "labels.old-password")}]] + [:div {:class (stl/css :fields-row)} + [:& fm/input + {:type "password" + :name :password-old + :auto-focus? true + :label (t locale "labels.old-password")}]] - [:div {:class (stl/css :fields-row)} - [:& fm/input - {:type "password" - :name :password-1 - :show-success? true - :label (t locale "labels.new-password")}]] + [:div {:class (stl/css :fields-row)} + [:& fm/input + {:type "password" + :name :password-1 + :show-success? true + :label (t locale "labels.new-password")}]] - [:div {:class (stl/css :fields-row)} - [:& fm/input - {:type "password" - :name :password-2 - :show-success? true - :label (t locale "labels.confirm-password")}]] + [:div {:class (stl/css :fields-row)} + [:& fm/input + {:type "password" + :name :password-2 + :show-success? true + :label (t locale "labels.confirm-password")}]] - [:> fm/submit-button* - {:label (t locale "dashboard.update-settings") - :data-test "submit-password" - :class (stl/css :update-btn)}]] - - ;; OLD - [:& fm/form {:class "password-form" - :on-submit on-submit - :form form} - [:h2 (t locale "dashboard.password-change")] - [:div.fields-row - [:& fm/input - {:type "password" - :name :password-old - :auto-focus? true - :label (t locale "labels.old-password")}]] - - [:div.fields-row - [:& fm/input - {:type "password" - :name :password-1 - :label (t locale "labels.new-password")}]] - - [:div.fields-row - [:& fm/input - {:type "password" - :name :password-2 - :label (t locale "labels.confirm-password")}]] - - [:> fm/submit-button* - {:label (t locale "dashboard.update-settings") - :data-test "submit-password"}]]))) + [:> fm/submit-button* + {:label (t locale "dashboard.update-settings") + :data-test "submit-password" + :class (stl/css :update-btn)}]])) ;; --- Password Page (mf/defc password-page [{:keys [locale]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (mf/use-effect - #(dom/set-html-title (tr "title.settings.password"))) + (mf/use-effect + #(dom/set-html-title (tr "title.settings.password"))) - (if new-css-system - [:section {:class (stl/css :dashboard-settings)} - [:div {:class (stl/css :form-container)} - [:h2 (t locale "dashboard.password-change")] - [:& password-form {:locale locale}]]] - - ;; old - [:section.dashboard-settings.form-container - [:div.form-container - [:& password-form {:locale locale}]]]))) + [:section {:class (stl/css :dashboard-settings)} + [:div {:class (stl/css :form-container)} + [:h2 (t locale "dashboard.password-change")] + [:& password-form {:locale locale}]]]) diff --git a/frontend/src/app/main/ui/settings/profile.cljs b/frontend/src/app/main/ui/settings/profile.cljs index b946b68462..c3bd7fe5bd 100644 --- a/frontend/src/app/main/ui/settings/profile.cljs +++ b/frontend/src/app/main/ui/settings/profile.cljs @@ -16,8 +16,6 @@ [app.main.store :as st] [app.main.ui.components.file-uploader :refer [file-uploader]] [app.main.ui.components.forms :as fm] - [app.main.ui.context :as ctx] - [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [cljs.spec.alpha :as s] @@ -43,9 +41,7 @@ (mf/defc profile-form [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - - profile (mf/deref refs/profile) + (let [profile (mf/deref refs/profile) form (fm/use-form :spec ::profile-form :initial profile :validators [(fm/validate-length :fullname fm/max-length-allowed (tr "auth.name.too-long")) @@ -59,78 +55,43 @@ (mf/use-callback #(modal/show! :delete-account {}))] - (if new-css-system - [:& fm/form {:on-submit on-submit - :form form - :class (stl/css :profile-form)} - [:div {:class (stl/css :fields-row)} - [:& fm/input - {:type "text" - :name :fullname - :label (tr "dashboard.your-name")}]] + [:& fm/form {:on-submit on-submit + :form form + :class (stl/css :profile-form)} + [:div {:class (stl/css :fields-row)} + [:& fm/input + {:type "text" + :name :fullname + :label (tr "dashboard.your-name")}]] - [:div {:class (stl/css :fields-row) - :on-click handle-show-change-email} - [:& fm/input - {:type "email" - :name :email - :disabled true - :label (tr "dashboard.your-email")}] + [:div {:class (stl/css :fields-row) + :on-click handle-show-change-email} + [:& fm/input + {:type "email" + :name :email + :disabled true + :label (tr "dashboard.your-email")}] - [:div {:class (stl/css :options)} - [:div.change-email - [:a {:on-click handle-show-change-email} - (tr "dashboard.change-email")]]]] + [:div {:class (stl/css :options)} + [:div.change-email + [:a {:on-click handle-show-change-email} + (tr "dashboard.change-email")]]]] - [:> fm/submit-button* - {:label (tr "dashboard.save-settings") - :disabled (empty? (:touched @form)) - :className (stl/css :btn-primary)}] + [:> fm/submit-button* + {:label (tr "dashboard.save-settings") + :disabled (empty? (:touched @form)) + :className (stl/css :btn-primary)}] - [:div {:class (stl/css :links)} - [:div {:class (stl/css :link-item)} - [:a {:on-click handle-show-delete-account - :data-test "remove-acount-btn"} - (tr "dashboard.remove-account")]]]] - - ;; OLD - [:& fm/form {:on-submit on-submit - :form form - :class "profile-form"} - [:div.fields-row - [:& fm/input - {:type "text" - :name :fullname - :label (tr "dashboard.your-name")}]] - - [:div.fields-row - [:& fm/input - {:type "email" - :name :email - :disabled true - :help-icon i/at - :label (tr "dashboard.your-email")}] - - [:div.options - [:div.change-email - [:a {:on-click #(modal/show! :change-email {})} - (tr "dashboard.change-email")]]]] - - [:> fm/submit-button* - {:label (tr "dashboard.save-settings") - :disabled (empty? (:touched @form))}] - - [:div.links - [:div.link-item - [:a {:on-click #(modal/show! :delete-account {}) - :data-test "remove-acount-btn"} - (tr "dashboard.remove-account")]]]]))) + [:div {:class (stl/css :links)} + [:div {:class (stl/css :link-item)} + [:a {:on-click handle-show-delete-account + :data-test "remove-acount-btn"} + (tr "dashboard.remove-account")]]]])) ;; --- Profile Photo Form (mf/defc profile-photo-form [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - file-input (mf/use-ref nil) + (let [file-input (mf/use-ref nil) profile (mf/deref refs/profile) photo (cf/resolve-profile-photo-url profile) on-image-click #(dom/click (mf/ref-val file-input)) @@ -139,44 +100,25 @@ (fn [file] (st/emit! (du/update-photo file)))] - (if new-css-system - [:form {:class (stl/css :avatar-form)} - [:div {:class (stl/css :image-change-field)} - [:span {:class (stl/css :update-overlay) - :on-click on-image-click} (tr "labels.update")] - [:img {:src photo}] - [:& file-uploader {:accept "image/jpeg,image/png" - :multi false - :ref file-input - :on-selected on-file-selected - :data-test "profile-image-input"}]]] - ;; OLD - [:form.avatar-form - [:div.image-change-field - [:span.update-overlay {:on-click on-image-click} (tr "labels.update")] - [:img {:src photo}] - [:& file-uploader {:accept "image/jpeg,image/png" - :multi false - :ref file-input - :on-selected on-file-selected - :data-test "profile-image-input"}]]]))) + [:form {:class (stl/css :avatar-form)} + [:div {:class (stl/css :image-change-field)} + [:span {:class (stl/css :update-overlay) + :on-click on-image-click} (tr "labels.update")] + [:img {:src photo}] + [:& file-uploader {:accept "image/jpeg,image/png" + :multi false + :ref file-input + :on-selected on-file-selected + :data-test "profile-image-input"}]]])) ;; --- Profile Page (mf/defc profile-page [] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (mf/with-effect [] - (dom/set-html-title (tr "title.settings.profile"))) - (if new-css-system - [:div {:class (stl/css :dashboard-settings)} - [:div {:class (stl/css :form-container)} - [:h2 (tr "labels.profile")] - [:& profile-photo-form] - [:& profile-form]]] - - ;; OLD - [:div.dashboard-settings - [:div.form-container.two-columns - [:& profile-photo-form] - [:& profile-form]]]))) + (mf/with-effect [] + (dom/set-html-title (tr "title.settings.profile"))) + [:div {:class (stl/css :dashboard-settings)} + [:div {:class (stl/css :form-container)} + [:h2 (tr "labels.profile")] + [:& profile-photo-form] + [:& profile-form]]]) diff --git a/frontend/src/app/main/ui/settings/sidebar.cljs b/frontend/src/app/main/ui/settings/sidebar.cljs index 5d3fc8c8b7..c82664b115 100644 --- a/frontend/src/app/main/ui/settings/sidebar.cljs +++ b/frontend/src/app/main/ui/settings/sidebar.cljs @@ -12,7 +12,6 @@ [app.main.data.modal :as modal] [app.main.data.users :as du] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.dashboard.sidebar :refer [profile-section]] [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [tr]] @@ -23,8 +22,7 @@ (mf/defc sidebar-content [{:keys [profile section] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - profile? (= section :settings-profile) + (let [profile? (= section :settings-profile) password? (= section :settings-password) options? (= section :settings-options) feedback? (= section :settings-feedback) @@ -69,107 +67,52 @@ (st/emit! (modal/show {:type :onboarding})) (st/emit! (modal/show {:type :release-notes :version version}))))))] - (if new-css-system - [:div {:class (stl/css :sidebar-content)} - [:div {:class (stl/css :sidebar-content-section)} - [:div {:class (stl/css :back-to-dashboard) - :on-click go-dashboard} - [:span {:class (stl/css :icon)} i/arrow-down] - [:span {:class (stl/css :text)} (tr "labels.dashboard")]]] + [:div {:class (stl/css :sidebar-content)} + [:div {:class (stl/css :sidebar-content-section)} + [:div {:class (stl/css :back-to-dashboard) + :on-click go-dashboard} + [:span {:class (stl/css :icon)} i/arrow-down] + [:span {:class (stl/css :text)} (tr "labels.dashboard")]]] + [:hr] + + [:div {:class (stl/css :sidebar-content-section)} + [:ul {:class (stl/css :sidebar-nav :no-overflow)} + [:li {:class (when profile? (stl/css :current)) + :on-click go-settings-profile} + [:span {:class (stl/css :element-title)} (tr "labels.profile")]] + + [:li {:class (when password? (stl/css :current)) + :on-click go-settings-password} + [:span {:class (stl/css :element-title)} (tr "labels.password")]] + + [:li {:class (when options? (stl/css :current)) + :on-click go-settings-options + :data-test "settings-profile"} + [:span {:class (stl/css :element-title)} (tr "labels.settings")]] + + (when (contains? cf/flags :access-tokens) + [:li {:class (when access-tokens? (stl/css :current)) + :on-click go-settings-access-tokens + :data-test "settings-access-tokens"} + [:span {:class (stl/css :element-title)} (tr "labels.access-tokens")]]) + [:hr] - [:div {:class (stl/css :sidebar-content-section)} - [:ul {:class (stl/css :sidebar-nav :no-overflow)} - [:li {:class (when profile? (stl/css :current)) - :on-click go-settings-profile} - [:span {:class (stl/css :element-title)} (tr "labels.profile")]] + [:li {:on-click show-release-notes :data-test "release-notes"} + [:span {:class (stl/css :element-title)} (tr "labels.release-notes")]] - [:li {:class (when password? (stl/css :current)) - :on-click go-settings-password} - [:span {:class (stl/css :element-title)} (tr "labels.password")]] - - [:li {:class (when options? (stl/css :current)) - :on-click go-settings-options - :data-test "settings-profile"} - [:span {:class (stl/css :element-title)} (tr "labels.settings")]] - - (when (contains? cf/flags :access-tokens) - [:li {:class (when access-tokens? (stl/css :current)) - :on-click go-settings-access-tokens - :data-test "settings-access-tokens"} - [:span {:class (stl/css :element-title)} (tr "labels.access-tokens")]]) - - [:hr] - - [:li {:on-click show-release-notes :data-test "release-notes"} - [:span {:class (stl/css :element-title)} (tr "labels.release-notes")]] - - (when (contains? cf/flags :user-feedback) - [:li {:class (when feedback? (stl/css :current)) - :on-click go-settings-feedback} - i/msg-info - [:span {:class (stl/css :element-title)} (tr "labels.give-feedback")]])]]] - - ;; OLD - [:div.sidebar-content - [:div.sidebar-content-section - [:div.back-to-dashboard {:on-click go-dashboard} - [:span.icon i/arrow-down] - [:span.text (tr "labels.dashboard")]]] - [:hr] - - [:div.sidebar-content-section - [:ul.sidebar-nav.no-overflow - [:li {:class (when profile? "current") - :on-click go-settings-profile} - i/user - [:span.element-title (tr "labels.profile")]] - - [:li {:class (when password? "current") - :on-click go-settings-password} - i/lock - [:span.element-title (tr "labels.password")]] - - [:li {:class (when options? "current") - :on-click go-settings-options - :data-test "settings-profile"} - i/tree - [:span.element-title (tr "labels.settings")]] - - (when (contains? cf/flags :access-tokens) - [:li {:class (when access-tokens? "current") - :on-click go-settings-access-tokens - :data-test "settings-access-tokens"} - i/icon-key - [:span.element-title (tr "labels.access-tokens")]]) - - [:hr] - - [:li {:on-click show-release-notes :data-test "release-notes"} - i/pencil - [:span.element-title (tr "labels.release-notes")]] - - (when (contains? cf/flags :user-feedback) - [:li {:class (when feedback? "current") - :on-click go-settings-feedback} - i/msg-info - [:span.element-title (tr "labels.give-feedback")]])]]]))) + (when (contains? cf/flags :user-feedback) + [:li {:class (when feedback? (stl/css :current)) + :on-click go-settings-feedback} + i/msg-info + [:span {:class (stl/css :element-title)} (tr "labels.give-feedback")]])]]])) (mf/defc sidebar {::mf/wrap [mf/memo]} [{:keys [profile locale section]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:div {:class (stl/css :dashboard-sidebar :settings)} - [:& sidebar-content {:profile profile - :section section}] - [:& profile-section {:profile profile - :locale locale}]] - - ;; OLD - [:div.dashboard-sidebar.settings - [:& sidebar-content {:profile profile - :section section}] - [:& profile-section {:profile profile - :locale locale}]]))) + [:div {:class (stl/css :dashboard-sidebar :settings)} + [:& sidebar-content {:profile profile + :section section}] + [:& profile-section {:profile profile + :locale locale}]]) diff --git a/frontend/src/app/main/ui/shapes/attrs.cljs b/frontend/src/app/main/ui/shapes/attrs.cljs index 14bee97258..e2bcf1156b 100644 --- a/frontend/src/app/main/ui/shapes/attrs.cljs +++ b/frontend/src/app/main/ui/shapes/attrs.cljs @@ -124,7 +124,7 @@ (not= :inner alignment) (not= :outer alignment) (not= :dotted style)) - (obj/set! attrs "strokeLinecap" caps-start) + (obj/set! attrs "strokeLinecap" (name caps-start)) (= :dotted style) (obj/set! attrs "strokeLinecap" "round")) diff --git a/frontend/src/app/main/ui/static.cljs b/frontend/src/app/main/ui/static.cljs index 916d2fa38d..04ae734912 100644 --- a/frontend/src/app/main/ui/static.cljs +++ b/frontend/src/app/main/ui/static.cljs @@ -20,112 +20,64 @@ (mf/defc static-header {::mf/wrap-props false} [props] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - children (obj/get props "children") + (let [children (obj/get props "children") on-click (mf/use-callback #(set! (.-href globals/location) "/"))] - (if new-css-system - [:section {:class (stl/css :exception-layout)} - [:button - {:class (stl/css :exception-header) - :on-click on-click} - i/logo-icon] - [:div {:class (stl/css :deco-before)} i/logo-error-screen] + [:section {:class (stl/css :exception-layout)} + [:button + {:class (stl/css :exception-header) + :on-click on-click} + i/logo-icon] + [:div {:class (stl/css :deco-before)} i/logo-error-screen] - [:div {:class (stl/css :exception-content)} - [:div {:class (stl/css :container)} children]] + [:div {:class (stl/css :exception-content)} + [:div {:class (stl/css :container)} children]] - [:div {:class (stl/css :deco-after)} i/logo-error-screen]] - [:section.exception-layout - [:div.exception-header - {:on-click on-click} - i/logo] - [:div.exception-content - [:div.container children]]]))) + [:div {:class (stl/css :deco-after)} i/logo-error-screen]])) (mf/defc invalid-token [] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:> static-header {} - [:div {:class (stl/css :main-message)} (tr "errors.invite-invalid")] - [:div {:class (stl/css :desc-message)} (tr "errors.invite-invalid.info")]] - - [:> static-header {} - [:div.image i/unchain] - [:div.main-message (tr "errors.invite-invalid")] - [:div.desc-message (tr "errors.invite-invalid.info")]]))) + [:> static-header {} + [:div {:class (stl/css :main-message)} (tr "errors.invite-invalid")] + [:div {:class (stl/css :desc-message)} (tr "errors.invite-invalid.info")]]) (mf/defc not-found [] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:> static-header {} - [:div {:class (stl/css :main-message)} (tr "labels.not-found.main-message")] - [:div {:class (stl/css :desc-message)} (tr "labels.not-found.desc-message")]] - - [:> static-header {} - [:div.image i/icon-empty] - [:div.main-message (tr "labels.not-found.main-message")] - [:div.desc-message (tr "labels.not-found.desc-message")]]))) + [:> static-header {} + [:div {:class (stl/css :main-message)} (tr "labels.not-found.main-message")] + [:div {:class (stl/css :desc-message)} (tr "labels.not-found.desc-message")]]) (mf/defc bad-gateway [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - handle-retry + (let [handle-retry (mf/use-callback (fn [] (st/emit! (rt/assign-exception nil))))] - (if new-css-system - [:> static-header {} - [:div {:class (stl/css :main-message)} (tr "labels.bad-gateway.main-message")] - [:div {:class (stl/css :desc-message)} (tr "labels.bad-gateway.desc-message")] - [:div {:class (stl/css :sign-info)} - [:button {:on-click handle-retry} (tr "labels.retry")]]] - - [:> static-header {} - [:div.image i/icon-empty] - [:div.main-message (tr "labels.bad-gateway.main-message")] - [:div.desc-message (tr "labels.bad-gateway.desc-message")] - [:div.sign-info - [:a.btn-primary.btn-small {:on-click handle-retry} (tr "labels.retry")]]]))) + [:> static-header {} + [:div {:class (stl/css :main-message)} (tr "labels.bad-gateway.main-message")] + [:div {:class (stl/css :desc-message)} (tr "labels.bad-gateway.desc-message")] + [:div {:class (stl/css :sign-info)} + [:button {:on-click handle-retry} (tr "labels.retry")]]])) (mf/defc service-unavailable [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - handle-retry + (let [handle-retry (mf/use-callback (fn [] (st/emit! (rt/assign-exception nil))))] - (if new-css-system - [:> static-header {} - [:div {:class (stl/css :main-message)} (tr "labels.service-unavailable.main-message")] - [:div {:class (stl/css :desc-message)} (tr "labels.service-unavailable.desc-message")] - [:div {:class (stl/css :sign-info)} - [:button {:on-click handle-retry} (tr "labels.retry")]]] - - [:> static-header {} - [:div.main-message (tr "labels.service-unavailable.main-message")] - [:div.desc-message (tr "labels.service-unavailable.desc-message")] - [:div.sign-info - [:a.btn-primary.btn-small {:on-click handle-retry} (tr "labels.retry")]]]))) + [:> static-header {} + [:div {:class (stl/css :main-message)} (tr "labels.service-unavailable.main-message")] + [:div {:class (stl/css :desc-message)} (tr "labels.service-unavailable.desc-message")] + [:div {:class (stl/css :sign-info)} + [:button {:on-click handle-retry} (tr "labels.retry")]]])) (mf/defc internal-error [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - handle-retry + (let [handle-retry (mf/use-callback (fn [] (st/emit! (rt/assign-exception nil))))] - (if new-css-system - [:> static-header {} - [:div {:class (stl/css :main-message)} (tr "labels.internal-error.main-message")] - [:div {:class (stl/css :desc-message)} (tr "labels.internal-error.desc-message")] - [:div {:class (stl/css :sign-info)} - [:button {:on-click handle-retry} (tr "labels.retry")]]] - - [:> static-header {} - [:div.image i/icon-empty] - [:div.main-message (tr "labels.internal-error.main-message")] - [:div.desc-message (tr "labels.internal-error.desc-message")] - [:div.sign-info - [:a.btn-primary.btn-small {:on-click handle-retry} (tr "labels.retry")]]]))) + [:> static-header {} + [:div {:class (stl/css :main-message)} (tr "labels.internal-error.main-message")] + [:div {:class (stl/css :desc-message)} (tr "labels.internal-error.desc-message")] + [:div {:class (stl/css :sign-info)} + [:button {:on-click handle-retry} (tr "labels.retry")]]])) (mf/defc exception-page [{:keys [data] :as props}] diff --git a/frontend/src/app/main/ui/viewer/inspect/attributes.cljs b/frontend/src/app/main/ui/viewer/inspect/attributes.cljs index 74bf4bcf98..9798af6777 100644 --- a/frontend/src/app/main/ui/viewer/inspect/attributes.cljs +++ b/frontend/src/app/main/ui/viewer/inspect/attributes.cljs @@ -8,7 +8,6 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.types.components-list :as ctkl] - [app.main.ui.context :as ctx] [app.main.ui.hooks :as hooks] [app.main.ui.viewer.inspect.annotation :refer [annotation]] [app.main.ui.viewer.inspect.attributes.blur :refer [blur-panel]] @@ -36,65 +35,35 @@ (mf/defc attributes [{:keys [page-id file-id shapes frame from libraries share-id objects]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - shapes (hooks/use-equal-memo shapes) + (let [shapes (hooks/use-equal-memo shapes) type (if (= (count shapes) 1) (-> shapes first :type) :multiple) options (type->options type) content (when (= (count shapes) 1) (ctkl/get-component-annotation (first shapes) libraries))] - (if new-css-system - [:div {:class (stl/css :element-options)} - (for [[idx option] (map-indexed vector options)] - [:> (case option - :geometry geometry-panel - :layout layout-panel - :layout-element layout-element-panel - :fill fill-panel - :stroke stroke-panel - :shadow shadow-panel - :blur blur-panel - :image image-panel - :text text-panel - :svg svg-panel) - {:key idx - :shapes shapes - :objects objects - :frame frame - :from from}]) - (when content - [:& annotation {:content content}]) - [:& exports - {:shapes shapes - :type type - :page-id page-id - :file-id file-id - :share-id share-id}]] - - - [:div.element-options - (for [[idx option] (map-indexed vector options)] - [:> (case option - :geometry geometry-panel - :layout layout-panel - :layout-element layout-element-panel - :fill fill-panel - :stroke stroke-panel - :shadow shadow-panel - :blur blur-panel - :image image-panel - :text text-panel - :svg svg-panel) - {:key idx - :shapes shapes - :objects objects - :frame frame - :from from}]) - (when content - [:& annotation {:content content}]) - [:& exports - {:shapes shapes - :type type - :page-id page-id - :file-id file-id - :share-id share-id}]]))) + [:div {:class (stl/css :element-options)} + (for [[idx option] (map-indexed vector options)] + [:> (case option + :geometry geometry-panel + :layout layout-panel + :layout-element layout-element-panel + :fill fill-panel + :stroke stroke-panel + :shadow shadow-panel + :blur blur-panel + :image image-panel + :text text-panel + :svg svg-panel) + {:key idx + :shapes shapes + :objects objects + :frame frame + :from from}]) + (when content + [:& annotation {:content content}]) + [:& exports + {:shapes shapes + :type type + :page-id page-id + :file-id file-id + :share-id share-id}]])) diff --git a/frontend/src/app/main/ui/viewer/inspect/code.cljs b/frontend/src/app/main/ui/viewer/inspect/code.cljs index b80df099dd..205ff04f32 100644 --- a/frontend/src/app/main/ui/viewer/inspect/code.cljs +++ b/frontend/src/app/main/ui/viewer/inspect/code.cljs @@ -20,7 +20,6 @@ [app.main.ui.components.code-block :refer [code-block]] [app.main.ui.components.copy-button :refer [copy-button]] [app.main.ui.components.select :refer [select]] - [app.main.ui.context :as ctx] [app.main.ui.hooks :as hooks] [app.main.ui.hooks.resize :refer [use-resize-hook]] [app.main.ui.icons :as i] @@ -100,8 +99,7 @@ (mf/defc code [{:keys [shapes frame on-expand from]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - style-type* (mf/use-state "css") + (let [style-type* (mf/use-state "css") markup-type* (mf/use-state "html") fontfaces-css* (mf/use-state nil) images-data* (mf/use-state nil) @@ -232,135 +230,80 @@ (fn [result] (reset! images-data* result))))) - (if new-css-system - [:div {:class (stl/css :element-options)} - [:div {:class (stl/css :attributes-block)} - [:button {:class (stl/css :download-button) - :on-click handle-copy-all-code} - "Copy all code"]] + [:div {:class (stl/css :element-options)} + [:div {:class (stl/css :attributes-block)} + [:button {:class (stl/css :download-button) + :on-click handle-copy-all-code} + "Copy all code"]] - #_[:div.attributes-block - [:button.download-button {:on-click handle-open-review} - "Preview"]] + #_[:div.attributes-block + [:button.download-button {:on-click handle-open-review} + "Preview"]] - [:div {:class (stl/css :code-block)} - [:div {:class (stl/css :code-row-lang)} - [:button {:class (stl/css :toggle-btn) - :data-type "css" - :on-click handle-collapse} - [:span {:class (stl/css-case - :collapsabled-icon true - :rotated collapsed-css?)} - i/arrow-refactor]] + [:div {:class (stl/css :code-block)} + [:div {:class (stl/css :code-row-lang)} + [:button {:class (stl/css :toggle-btn) + :data-type "css" + :on-click handle-collapse} + [:span {:class (stl/css-case + :collapsabled-icon true + :rotated collapsed-css?)} + i/arrow-refactor]] - [:& select {:default-value style-type - :class (stl/css :code-lang-select) - :options [{:label "CSS" :value "css"}]}] + [:& select {:default-value style-type + :class (stl/css :code-lang-select) + :on-change set-style + :options [{:label "CSS" :value "css"}]}] - [:div {:class (stl/css :action-btns)} - [:button {:class (stl/css :expand-button) - :on-click on-expand} - i/code-refactor] + [:div {:class (stl/css :action-btns)} + [:button {:class (stl/css :expand-button) + :on-click on-expand} + i/code-refactor] - [:& copy-button {:data #(replace-map style-code images-data) - :on-copied on-style-copied}]]] + [:& copy-button {:data #(replace-map style-code images-data) + :on-copied on-style-copied}]]] - (when-not collapsed-css? - [:div {:class (stl/css :code-row-display) - :style #js {"--code-height" (str (or style-size 400) "px")}} - [:& code-block {:type style-type - :code style-code}]]) - - [:div {:class (stl/css :resize-area) - :on-pointer-down on-style-pointer-down - :on-lost-pointer-capture on-style-lost-pointer-capture - :on-pointer-move on-style-pointer-move}]] - - [:div {:class (stl/css :code-block)} - [:div {:class (stl/css :code-row-lang)} - [:button {:class (stl/css :toggle-btn) - :data-type "markup" - :on-click handle-collapse} - [:span {:class (stl/css-case - :collapsabled-icon true - :rotated collapsed-markup?)} - i/arrow-refactor]] - [:& select {:default-value markup-type - :class (stl/css :code-lang-select) - :options [{:label "HTML" :value "html"} - {:label "SVG" :value "svg"}] - :on-change set-markup}] - - [:div {:class (stl/css :action-btns)} - [:button {:class (stl/css :expand-button) - :on-click on-expand} - i/code-refactor] - - [:& copy-button {:data #(replace-map markup-code images-data) - :on-copied on-markup-copied}]]] - - (when-not collapsed-markup? - [:div {:class (stl/css :code-row-display) - :style #js {"--code-height" (str (or markup-size 400) "px")}} - [:& code-block {:type markup-type - :code markup-code}]]) - - [:div {:class (stl/css :resize-area) - :on-pointer-down on-markup-pointer-down - :on-lost-pointer-capture on-markup-lost-pointer-capture - :on-pointer-move on-markup-pointer-move}]]] - - - - [:div.element-options - [:div.attributes-block - [:button.download-button {:on-click handle-copy-all-code} - "Copy all code"]] - - #_[:div.attributes-block - [:button.download-button {:on-click handle-open-review} - "Preview"]] - - [:div.code-block - [:div.code-row-lang - [:& select {:default-value style-type - :class "custom-select" - :options [{:label "CSS" :value "css"}] - :on-change set-style}] - [:button.expand-button - {:on-click on-expand} - i/full-screen] - - [:& copy-button {:data #(replace-map style-code images-data) - :on-copied on-style-copied}]] - - [:div.code-row-display {:style #js {"--code-height" (str (or style-size 400) "px")}} + (when-not collapsed-css? + [:div {:class (stl/css :code-row-display) + :style #js {"--code-height" (str (or style-size 400) "px")}} [:& code-block {:type style-type - :code style-code}]] + :code style-code}]]) - [:div.resize-area {:on-pointer-down on-style-pointer-down - :on-lost-pointer-capture on-style-lost-pointer-capture - :on-pointer-move on-style-pointer-move}]] + [:div {:class (stl/css :resize-area) + :on-pointer-down on-style-pointer-down + :on-lost-pointer-capture on-style-lost-pointer-capture + :on-pointer-move on-style-pointer-move}]] - [:div.code-block - [:div.code-row-lang - [:& select {:default-value markup-type - :class "input-option" - :options [{:label "HTML" :value "html"} - {:label "SVG" :value "svg"}] - :on-change set-markup}] + [:div {:class (stl/css :code-block)} + [:div {:class (stl/css :code-row-lang)} + [:button {:class (stl/css :toggle-btn) + :data-type "markup" + :on-click handle-collapse} + [:span {:class (stl/css-case + :collapsabled-icon true + :rotated collapsed-markup?)} + i/arrow-refactor]] + [:& select {:default-value markup-type + :class (stl/css :code-lang-select) + :options [{:label "HTML" :value "html"} + {:label "SVG" :value "svg"}] + :on-change set-markup}] - [:button.expand-button - {:on-click on-expand} - i/full-screen] + [:div {:class (stl/css :action-btns)} + [:button {:class (stl/css :expand-button) + :on-click on-expand} + i/code-refactor] - [:& copy-button {:data #(replace-map markup-code images-data) - :on-copied on-markup-copied}]] + [:& copy-button {:data #(replace-map markup-code images-data) + :on-copied on-markup-copied}]]] - [:div.code-row-display {:style #js {"--code-height" (str (or markup-size 400) "px")}} + (when-not collapsed-markup? + [:div {:class (stl/css :code-row-display) + :style #js {"--code-height" (str (or markup-size 400) "px")}} [:& code-block {:type markup-type - :code markup-code}]] + :code markup-code}]]) - [:div.resize-area {:on-pointer-down on-markup-pointer-down - :on-lost-pointer-capture on-markup-lost-pointer-capture - :on-pointer-move on-markup-pointer-move}]]]))) + [:div {:class (stl/css :resize-area) + :on-pointer-down on-markup-pointer-down + :on-lost-pointer-capture on-markup-lost-pointer-capture + :on-pointer-move on-markup-pointer-move}]]])) diff --git a/frontend/src/app/main/ui/viewer/inspect/code.scss b/frontend/src/app/main/ui/viewer/inspect/code.scss index 3524f40a03..9f7c37b5e7 100644 --- a/frontend/src/app/main/ui/viewer/inspect/code.scss +++ b/frontend/src/app/main/ui/viewer/inspect/code.scss @@ -12,80 +12,85 @@ height: 100%; overflow: hidden; padding-bottom: $s-16; +} - .attributes-block { - .download-button { - @extend .button-secondary; - @include tabTitleTipography; - height: $s-32; - width: 100%; - margin: $s-8 0; - } - } - .code-block { - @include codeTypography; - display: flex; - flex-direction: column; - flex: 1; +.download-button { + @extend .button-secondary; + @include tabTitleTipography; + height: $s-32; + width: 100%; + margin: $s-8 0; +} + +.code-block { + @include codeTypography; + display: flex; + flex-direction: column; + flex: 1; + height: 100%; + min-height: 0; + overflow: hidden; + padding: 0 $s-4 $s-8 0; + + pre { + border-radius: $br-8; + padding: $s-16; + max-height: var(--code-height); + overflow: auto; height: 100%; - min-height: 0; - overflow: hidden; - padding: 0 $s-4 $s-8 0; + } - pre { - border-radius: $br-8; - padding: $s-16; - max-height: var(--code-height); - overflow: auto; - height: 100%; - } + // Overrides background setted in the theme + :global(.hljs) { + background: $db-tertiary; + } +} - // Overrides background setted in the theme - :global(.hljs) { - background: $db-tertiary; - } - } - .code-row-lang { - display: flex; - justify-content: space-between; - gap: $s-4; - width: 100%; - } - .code-lang { - @include tabTitleTipography; - display: flex; - align-items: center; - } - .action-btns { - display: flex; - gap: $s-4; - flex: 1; - justify-content: end; - .expand-button { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - stroke: var(--icon-foreground); - } - } - } - .code-lang-select { - @include tabTitleTipography; - width: $s-72; - border: $s-1 solid transparent; - background-color: transparent; - color: var(--menu-foreground-color-disabled); - } - .code-row-display { - flex: 1; - min-height: 0; - overflow: hidden; - padding-bottom: $s-8; +.code-row-lang { + display: flex; + justify-content: space-between; + gap: $s-4; + width: 100%; +} + +.code-lang { + @include tabTitleTipography; + display: flex; + align-items: center; +} + +.action-btns { + display: flex; + gap: $s-4; + flex: 1; + justify-content: end; +} + +.expand-button { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); } } +.code-lang-select { + @include tabTitleTipography; + width: $s-72; + border: $s-1 solid transparent; + background-color: transparent; + color: var(--menu-foreground-color-disabled); +} + +.code-row-display { + flex: 1; + min-height: 0; + overflow: hidden; + padding-bottom: $s-8; +} + .toggle-btn { @include buttonStyle; display: flex; diff --git a/frontend/src/app/main/ui/viewer/inspect/right_sidebar.cljs b/frontend/src/app/main/ui/viewer/inspect/right_sidebar.cljs index d5fe584b74..ea62c3111b 100644 --- a/frontend/src/app/main/ui/viewer/inspect/right_sidebar.cljs +++ b/frontend/src/app/main/ui/viewer/inspect/right_sidebar.cljs @@ -10,11 +10,8 @@ [app.common.data.macros :as dm] [app.common.types.component :as ctk] [app.main.refs :as refs] - [app.main.ui.components.shape-icon :as si] [app.main.ui.components.shape-icon-refactor :as sir] [app.main.ui.components.tab-container :refer [tab-container tab-element]] - [app.main.ui.components.tabs-container :refer [tabs-container tabs-element]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.ui.viewer.inspect.attributes :refer [attributes]] [app.main.ui.viewer.inspect.code :refer [code]] @@ -44,8 +41,7 @@ (mf/defc right-sidebar [{:keys [frame page objects file selected shapes page-id file-id share-id from on-change-section on-expand] :or {from :inspect}}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - section (mf/use-state :info #_:code) + (let [section (mf/use-state :info #_:code) objects (or objects (:objects page)) shapes (or shapes (resolve-shapes objects selected)) @@ -86,113 +82,60 @@ (when-not (seq shapes) (handle-change-tab :info)))) - (if new-css-system - [:aside {:class (stl/css-case :settings-bar-right true - :viewer-code (= from :inspect))} - (if (seq shapes) - [:div {:class (stl/css :tool-windows)} - [:div {:class (stl/css :shape-row)} - (if (> (count shapes) 1) - [:* - [:span {:class (stl/css :layers-icon)} i/layers-refactor] - [:span {:class (stl/css :layer-title)} (tr "inspect.tabs.code.selected.multiple" (count shapes))]] - [:* - [:span {:class (stl/css :shape-icon)} - [:& sir/element-icon-refactor {:shape first-shape :main-instance? main-instance?}]] - ;; Execution time translation strings: - ;; inspect.tabs.code.selected.circle - ;; inspect.tabs.code.selected.component - ;; inspect.tabs.code.selected.curve - ;; inspect.tabs.code.selected.frame - ;; inspect.tabs.code.selected.group - ;; inspect.tabs.code.selected.image - ;; inspect.tabs.code.selected.mask - ;; inspect.tabs.code.selected.path - ;; inspect.tabs.code.selected.rect - ;; inspect.tabs.code.selected.svg-raw - ;; inspect.tabs.code.selected.text - [:span {:class (stl/css :layer-title)} (:name first-shape)]])] - [:div {:class (stl/css :inspect-content)} - [:& tab-container {:on-change-tab handle-change-tab - :selected @section} - [:& tab-element {:id :info :title (tr "inspect.tabs.info")} - [:& attributes {:page-id page-id - :objects objects - :file-id file-id - :frame frame - :shapes shapes - :from from - :libraries libraries - :share-id share-id}]] + [:aside {:class (stl/css-case :settings-bar-right true + :viewer-code (= from :inspect))} + (if (seq shapes) + [:div {:class (stl/css :tool-windows)} + [:div {:class (stl/css :shape-row)} + (if (> (count shapes) 1) + [:* + [:span {:class (stl/css :layers-icon)} i/layers-refactor] + [:span {:class (stl/css :layer-title)} (tr "inspect.tabs.code.selected.multiple" (count shapes))]] + [:* + [:span {:class (stl/css :shape-icon)} + [:& sir/element-icon-refactor {:shape first-shape :main-instance? main-instance?}]] + ;; Execution time translation strings: + ;; inspect.tabs.code.selected.circle + ;; inspect.tabs.code.selected.component + ;; inspect.tabs.code.selected.curve + ;; inspect.tabs.code.selected.frame + ;; inspect.tabs.code.selected.group + ;; inspect.tabs.code.selected.image + ;; inspect.tabs.code.selected.mask + ;; inspect.tabs.code.selected.path + ;; inspect.tabs.code.selected.rect + ;; inspect.tabs.code.selected.svg-raw + ;; inspect.tabs.code.selected.text + [:span {:class (stl/css :layer-title)} (:name first-shape)]])] + [:div {:class (stl/css :inspect-content)} + [:& tab-container {:on-change-tab handle-change-tab + :selected @section} + [:& tab-element {:id :info :title (tr "inspect.tabs.info")} + [:& attributes {:page-id page-id + :objects objects + :file-id file-id + :frame frame + :shapes shapes + :from from + :libraries libraries + :share-id share-id}]] - [:& tab-element {:id :code :title (tr "inspect.tabs.code")} - [:& code {:frame frame - :shapes shapes - :on-expand handle-expand - :from from}]]]]] - [:div {:class (stl/css :empty)} - [:div {:class (stl/css :code-info)} - [:span {:class (stl/css :placeholder-icon)} - i/code-refactor] - [:span {:class (stl/css :placeholder-label)} - (tr "inspect.empty.select")]] - [:div {:class (stl/css :help-info)} - [:span {:class (stl/css :placeholder-icon)} - i/help-refactor] - [:span {:class (stl/css :placeholder-label)} - (tr "inspect.empty.help")]] - [:button {:class (stl/css :more-info-btn) - :on-click navigate-to-help} - (tr "inspect.empty.more-info")]])] - - - [:aside.settings-bar.settings-bar-right - [:div.settings-bar-inside - (if (seq shapes) - [:div.tool-window - [:div.tool-window-bar.big - (if (> (count shapes) 1) - [:* - [:span.tool-window-bar-icon i/layers] - [:span.tool-window-bar-title (tr "inspect.tabs.code.selected.multiple" (count shapes))]] - [:* - [:span.tool-window-bar-icon - [:& si/element-icon {:shape first-shape :main-instance? main-instance?}]] - ;; Execution time translation strings: - ;; inspect.tabs.code.selected.circle - ;; inspect.tabs.code.selected.component - ;; inspect.tabs.code.selected.curve - ;; inspect.tabs.code.selected.frame - ;; inspect.tabs.code.selected.group - ;; inspect.tabs.code.selected.image - ;; inspect.tabs.code.selected.mask - ;; inspect.tabs.code.selected.path - ;; inspect.tabs.code.selected.rect - ;; inspect.tabs.code.selected.svg-raw - ;; inspect.tabs.code.selected.text - [:span.tool-window-bar-title (:name first-shape)]])] - [:div.tool-window-content.inspect - [:& tabs-container {:on-change-tab handle-change-tab - :selected @section} - [:& tabs-element {:id :info :title (tr "inspect.tabs.info")} - [:& attributes {:page-id page-id - :objects objects - :file-id file-id - :frame frame - :shapes shapes - :from from - :libraries libraries - :share-id share-id}]] - - [:& tabs-element {:id :code :title (tr "inspect.tabs.code")} - [:& code {:frame frame - :shapes shapes - :on-expand handle-expand - :from from}]]]]] - [:div.empty - [:span.tool-window-bar-icon i/code] - [:div (tr "inspect.empty.select")] - [:span.tool-window-bar-icon i/help] - [:div (tr "inspect.empty.help")] - [:button.btn-primary.action {:on-click navigate-to-help} (tr "inspect.empty.more-info")]])]]) - )) + [:& tab-element {:id :code :title (tr "inspect.tabs.code")} + [:& code {:frame frame + :shapes shapes + :on-expand handle-expand + :from from}]]]]] + [:div {:class (stl/css :empty)} + [:div {:class (stl/css :code-info)} + [:span {:class (stl/css :placeholder-icon)} + i/code-refactor] + [:span {:class (stl/css :placeholder-label)} + (tr "inspect.empty.select")]] + [:div {:class (stl/css :help-info)} + [:span {:class (stl/css :placeholder-icon)} + i/help-refactor] + [:span {:class (stl/css :placeholder-label)} + (tr "inspect.empty.help")]] + [:button {:class (stl/css :more-info-btn) + :on-click navigate-to-help} + (tr "inspect.empty.more-info")]])])) 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 13bf37facb..c5267edbfc 100644 --- a/frontend/src/app/main/ui/viewer/inspect/right_sidebar.scss +++ b/frontend/src/app/main/ui/viewer/inspect/right_sidebar.scss @@ -16,61 +16,73 @@ grid-area: right-sidebar; padding-top: $s-8; padding-left: $s-12; - .tool-windows { - height: 100%; - display: flex; - flex-direction: column; - .shape-row { - display: flex; - gap: $s-8; - align-items: center; - margin-bottom: $s-16; - .layers-icon, - .shape-icon { - @include flexCenter; - svg { - @extend .button-icon-small; - stroke: var(--icon-foreground); - } - } - .layer-title { - @include titleTipography; - } - } - } - .empty { - display: flex; - flex-direction: column; - align-items: center; - gap: $s-40; - padding-top: $s-24; - .code-info, - .help-info { - @include flexColumn; - align-items: center; - justify-content: flex-start; - gap: $s-8; - .placeholder-icon { - @extend .empty-icon; - } - .placeholder-label { - @include titleTipography; - text-align: center; - width: $s-200; - } - } - .more-info-btn { - @extend .button-secondary; - @include tabTitleTipography; - height: $s-32; - padding: $s-8 $s-24; - } - } - .inspect-content { - flex: 1; - overflow: hidden; - } &.viewer-code { height: calc(100vh - $s-48); } } + +.tool-windows { + height: 100%; + display: flex; + flex-direction: column; +} + +.shape-row { + display: flex; + gap: $s-8; + align-items: center; + margin-bottom: $s-16; +} + +.layers-icon, +.shape-icon { + @include flexCenter; + svg { + @extend .button-icon-small; + stroke: var(--icon-foreground); + } +} + +.layer-title { + @include titleTipography; + color: $df-primary; +} + +.empty { + display: flex; + flex-direction: column; + align-items: center; + gap: $s-40; + padding-top: $s-24; +} + +.code-info, +.help-info { + @include flexColumn; + align-items: center; + justify-content: flex-start; + gap: $s-8; +} + +.placeholder-icon { + @extend .empty-icon; +} + +.placeholder-label { + @include titleTipography; + text-align: center; + width: $s-200; + color: var(--empty-message-foreground-color); +} + +.more-info-btn { + @extend .button-secondary; + @include tabTitleTipography; + height: $s-32; + padding: $s-8 $s-24; +} + +.inspect-content { + flex: 1; + overflow: hidden; +} diff --git a/frontend/src/app/main/ui/viewer/login.cljs b/frontend/src/app/main/ui/viewer/login.cljs index 375066e137..3341fac27c 100644 --- a/frontend/src/app/main/ui/viewer/login.cljs +++ b/frontend/src/app/main/ui/viewer/login.cljs @@ -14,7 +14,6 @@ [app.main.ui.auth.login :refer [login-methods]] [app.main.ui.auth.recovery-request :refer [recovery-request-page]] [app.main.ui.auth.register :refer [register-methods register-validate-form register-success-page]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -27,8 +26,7 @@ {::mf/register modal/components ::mf/register-as :login-register} [_] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - uri (. (. js/document -location) -href) + (let [uri (. (. js/document -location) -href) user-email (mf/use-state "") register-token (mf/use-state "") @@ -71,114 +69,58 @@ (mf/with-effect [] (swap! storage assoc :redirect-url uri)) - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-container)} - [:div {:class (stl/css :modal-header)} - [:h2 {:class (stl/css :modal-title)} (tr "labels.continue-with-penpot")] - [:button {:class (stl/css :modal-close-btn) - :title (tr "labels.close") - :on-click close} i/close-refactor]] + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} (tr "labels.continue-with-penpot")] + [:button {:class (stl/css :modal-close-btn) + :title (tr "labels.close") + :on-click close} i/close-refactor]] - [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :modal-content)} - (case current-section - :login - [:div {:class (stl/css :form-container)} - [:& login-methods {:on-success-callback success-login :origin :viewer}] - [:div {:class (stl/css :links)} - [:div {:class (stl/css :link-entry)} - [:a {:on-click set-section - :data-value :recovery-request} - (tr "auth.forgot-password")]] - [:div {:class (stl/css :link-entry)} - [:span (tr "auth.register") " "] - [:a {:on-click set-section - :data-value :register} - (tr "auth.register-submit")]]]] + (case current-section + :login + [:div {:class (stl/css :form-container)} + [:& login-methods {:on-success-callback success-login :origin :viewer}] + [:div {:class (stl/css :links)} + [:div {:class (stl/css :link-entry)} + [:a {:on-click set-section + :data-value :recovery-request} + (tr "auth.forgot-password")]] + [:div {:class (stl/css :link-entry)} + [:span (tr "auth.register") " "] + [:a {:on-click set-section + :data-value :register} + (tr "auth.register-submit")]]]] - :register - [:div {:class (stl/css :form-container)} - [:& register-methods {:on-success-callback success-register}] - [:div {:class (stl/css :links)} - [:div {:class (stl/css :link-entry)} - [:span (tr "auth.already-have-account") " "] - [:a {:on-click set-section - :data-value :login} - (tr "auth.login-here")]]]] + :register + [:div {:class (stl/css :form-container)} + [:& register-methods {:on-success-callback success-register}] + [:div {:class (stl/css :links)} + [:div {:class (stl/css :link-entry)} + [:span (tr "auth.already-have-account") " "] + [:a {:on-click set-section + :data-value :login} + (tr "auth.login-here")]]]] - :register-validate - [:div {:class (stl/css :form-container)} - [:& register-validate-form {:params {:token @register-token} - :on-success-callback success-email-sent}] - [:div {:class (stl/css :links)} - [:div {:class (stl/css :link-entry)} - [:a {:on-click set-section - :data-value :register} - (tr "labels.go-back")]]]] - - :recovery-request - [:& recovery-request-page {:go-back-callback go-back-to-login + :register-validate + [:div {:class (stl/css :form-container)} + [:& register-validate-form {:params {:token @register-token} :on-success-callback success-email-sent}] - :email-sent - [:div {:class (stl/css :form-container)} - [:& register-success-page {:params {:email @user-email}}]]) + [:div {:class (stl/css :links)} + [:div {:class (stl/css :link-entry)} + [:a {:on-click set-section + :data-value :register} + (tr "labels.go-back")]]]] - (when main-section - [:div {:class (stl/css :links)} - [:& terms-login]])]]] + :recovery-request + [:& recovery-request-page {:go-back-callback go-back-to-login + :on-success-callback success-email-sent}] + :email-sent + [:div {:class (stl/css :form-container)} + [:& register-success-page {:params {:email @user-email}}]]) - - ;;OLD - [:div.modal-overlay - [:div.modal-container.login-register - [:div.title - [:div.modal-close-button {:on-click close :title (tr "labels.close")} - i/close] - (when main-section - [:h2 (tr "labels.continue-with-penpot")])] - - [:div.modal-bottom.auth-content - - (case current-section - :login - [:div.generic-form.login-form - [:div.form-container - [:& login-methods {:on-success-callback success-login}] - [:div.links - [:div.link-entry - [:a {:on-click #(set-current-section :recovery-request)} - (tr "auth.forgot-password")]] - [:div.link-entry - [:span (tr "auth.register") " "] - [:a {:on-click #(set-current-section :register)} - (tr "auth.register-submit")]]]]] - - :register - [:div.form-container - [:& register-methods {:on-success-callback success-register}] - [:div.links - [:div.link-entry - [:span (tr "auth.already-have-account") " "] - [:a {:on-click #(set-current-section :login)} - (tr "auth.login-here")]]]] - - :register-validate - [:div.form-container - [:& register-validate-form {:params {:token @register-token} - :on-success-callback success-email-sent}] - [:div.links - [:div.link-entry - [:a {:on-click #(set-current-section :register)} - (tr "labels.go-back")]]]] - - :recovery-request - [:& recovery-request-page {:go-back-callback #(set-current-section :login) - :on-success-callback success-email-sent}] - :email-sent - [:div.form-container - [:& register-success-page {:params {:email @user-email}}]])] - - (when main-section - [:div.modal-footer.links - [:& terms-login]])]]))) + (when main-section + [:div {:class (stl/css :links)} + [:& terms-login]])]]])) diff --git a/frontend/src/app/main/ui/viewer/share_link.cljs b/frontend/src/app/main/ui/viewer/share_link.cljs index 41bd3ed204..0c2769cc90 100644 --- a/frontend/src/app/main/ui/viewer/share_link.cljs +++ b/frontend/src/app/main/ui/viewer/share_link.cljs @@ -18,7 +18,6 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.select :refer [select]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -40,8 +39,7 @@ ::mf/register-as :share-link ::mf/wrap-props false} [{:keys [file page]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - current-page page + (let [current-page page current-page-id (:id page) slinks (mf/deref refs/share-links) router (mf/deref refs/router) @@ -165,274 +163,151 @@ (reset! confirm* false) (swap! options* assoc :who-comment value))] - (if new-css-system - [:div {:class (stl/css :share-modal)} - [:div {:class (stl/css :share-link-dialog)} - [:div {:class (stl/css :share-link-header)} - [:h2 {:class (stl/css :share-link-title)} - (tr "common.share-link.title")] - [:button {:class (stl/css :modal-close-button) - :on-click on-close - :title (tr "labels.close")} - i/close-refactor]] - [:div {:class (stl/css :modal-content)} - [:div {:class (stl/css :share-link-section)} - (when (and (not confirm?) (some? current-link)) - [:div {:class (stl/css :custon-input-wrapper)} - [:input {:class (stl/css :input-text) - :type "text" - :value (or current-link "") - :placeholder (tr "common.share-link.placeholder") - :read-only true}] - - [:button {:class (stl/css :copy-button) - :title (tr "viewer.header.share.copy-link") - :on-click copy-link} - i/clipboard-refactor]]) - - [:div {:class (stl/css :hint-wrapper)} - (when (not ^boolean confirm?) - [:div {:class (stl/css :hint)} (tr "common.share-link.permissions-hint")]) - (cond - (true? confirm?) - [:div {:class (stl/css :confirm-dialog)} - [:div {:class (stl/css :description)} - (tr "common.share-link.confirm-deletion-link-description")] - [:div {:class (stl/css :actions)} - [:input {:type "button" - :class (stl/css :button-cancel) - :on-click #(reset! confirm* false) - :value (tr "labels.cancel")}] - [:input {:type "button" - :class (stl/css :button-danger) - :on-click delete-link - :value (tr "common.share-link.destroy-link")}]]] - - (some? current-link) - [:input - {:type "button" - :class (stl/css :button-danger) - :on-click try-delete-link - :value (tr "common.share-link.destroy-link")}] - - :else - [:input - {:type "button" - :class (stl/css :button-active) - :on-click create-link - :value (tr "common.share-link.get-link")}])]] + [:div {:class (stl/css :share-modal)} + [:div {:class (stl/css :share-link-dialog)} + [:div {:class (stl/css :share-link-header)} + [:h2 {:class (stl/css :share-link-title)} + (tr "common.share-link.title")] + [:button {:class (stl/css :modal-close-button) + :on-click on-close + :title (tr "labels.close")} + i/close-refactor]] + [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :share-link-section)} + (when (and (not confirm?) (some? current-link)) + [:div {:class (stl/css :custon-input-wrapper)} + [:input {:class (stl/css :input-text) + :type "text" + :value (or current-link "") + :placeholder (tr "common.share-link.placeholder") + :read-only true}] + [:button {:class (stl/css :copy-button) + :title (tr "viewer.header.share.copy-link") + :on-click copy-link} + i/clipboard-refactor]]) + [:div {:class (stl/css :hint-wrapper)} (when (not ^boolean confirm?) - [:div {:class (stl/css :permissions-section)} - [:button {:class (stl/css :manage-permissions) - :on-click toggle-perms-visibility} - [:span {:class (stl/css-case :icon true - :rotated perms-visible?)} - i/arrow-refactor] - (tr "common.share-link.manage-ops")] + [:div {:class (stl/css :hint)} (tr "common.share-link.permissions-hint")]) + (cond + (true? confirm?) + [:div {:class (stl/css :confirm-dialog)} + [:div {:class (stl/css :description)} + (tr "common.share-link.confirm-deletion-link-description")] + [:div {:class (stl/css :actions)} + [:input {:type "button" + :class (stl/css :button-cancel) + :on-click #(reset! confirm* false) + :value (tr "labels.cancel")}] + [:input {:type "button" + :class (stl/css :button-danger) + :on-click delete-link + :value (tr "common.share-link.destroy-link")}]]] - (when ^boolean perms-visible? - [:* - (let [all-selected? (:all-pages options) - pages (->> (get-in file [:data :pages]) - (map #(get-in file [:data :pages-index %]))) - selected (:pages options)] - [:div {:class (stl/css :view-mode)} - [:div {:class (stl/css :subtitle)} - (tr "common.share-link.permissions-pages")] - [:div {:class (stl/css :items)} - (if (= 1 (count pages)) + (some? current-link) + [:input + {:type "button" + :class (stl/css :button-danger) + :on-click try-delete-link + :value (tr "common.share-link.destroy-link")}] - [:div {:class (stl/css :checkbox-wrapper)} - [:input {:type "checkbox" - :id (dm/str "page-" current-page-id) - :data-page-id (dm/str current-page-id) - :on-change on-mark-checked-page - :checked true}] - [:label {:for (str "page-" current-page-id)} (:name current-page)] - [:span {:class (stl/css-case :checkobox-tick true - :global/checked true)} - i/status-tick-refactor] - [:span (str " " (tr "common.share-link.current-tag"))]] + :else + [:input + {:type "button" + :class (stl/css :button-active) + :on-click create-link + :value (tr "common.share-link.get-link")}])]] - [:* - [:div {:class (stl/css :select-all-row)} - [:div {:class (stl/css :checkbox-wrapper)} - [:label {:for "view-all" - :class (stl/css :select-all-label)} - [:span {:class (stl/css-case :global/checked all-selected?)} - (when all-selected? - i/status-tick-refactor)] - (tr "common.share-link.view-all") - [:input {:type "checkbox" - :id "view-all" - :checked all-selected? - :name "pages-mode" - :on-change on-toggle-all}]]] - [:span {:class (stl/css :count-pages)} - (tr "common.share-link.page-shared" (i18n/c (count selected)))]] + (when (not ^boolean confirm?) + [:div {:class (stl/css :permissions-section)} + [:button {:class (stl/css :manage-permissions) + :on-click toggle-perms-visibility} + [:span {:class (stl/css-case :icon true + :rotated perms-visible?)} + i/arrow-refactor] + (tr "common.share-link.manage-ops")] - [:ul {:class (stl/css :pages-selection)} - (for [{:keys [id name]} pages] - [:li {:class (stl/css :checkbox-wrapper) - :key (dm/str id)} - [:label {:for (dm/str "page-" id)} - [:span {:class (stl/css-case :global/checked (contains? selected id))} - (when (contains? selected id) - i/status-tick-refactor)] - name - (when (= current-page-id id) - [:div {:class (stl/css :current-tag)} (dm/str " " (tr "common.share-link.current-tag"))]) - [:input {:type "checkbox" - :id (dm/str "page-" id) - :data-page-id (dm/str id) - :on-change on-mark-checked-page - :checked (contains? selected id)}]]])]])]]) - - [:div {:class (stl/css :access-mode)} + (when ^boolean perms-visible? + [:* + (let [all-selected? (:all-pages options) + pages (->> (get-in file [:data :pages]) + (map #(get-in file [:data :pages-index %]))) + selected (:pages options)] + [:div {:class (stl/css :view-mode)} [:div {:class (stl/css :subtitle)} - (tr "common.share-link.permissions-can-comment")] - [:div {:class (stl/css :items)} - [:& select - {:class (stl/css :who-comment-select) - :default-value (dm/str (:who-comment options)) - :options [{:value "team" :label (tr "common.share-link.team-members")} - {:value "all" :label (tr "common.share-link.all-users")}] - :on-change on-comment-change}]]] - [:div {:class (stl/css :inspect-mode)} - [:div {:class (stl/css :subtitle)} - (tr "common.share-link.permissions-can-inspect")] - [:div {:class (stl/css :items)} - [:& select - {:class (stl/css :who-inspect-select) - :default-value (dm/str (:who-inspect options)) - :options [{:value "team" :label (tr "common.share-link.team-members")} - {:value "all" :label (tr "common.share-link.all-users")}] - :on-change on-inspect-change}]]]])])]]] - - - ;;OLD - [:div.modal-overlay.transparent.share-modal - [:div.modal-container.share-link-dialog - [:div.modal-content.initial - [:div.title - [:h2 (tr "common.share-link.title")] - [:div.modal-close-button - {:on-click on-close - :title (tr "labels.close")} - i/close]]] - [:div.modal-content - [:div.share-link-section - (when (and (not confirm?) (some? current-link)) - [:div.custom-input.with-icon - [:input {:type "text" - :value (or current-link "") - :placeholder (tr "common.share-link.placeholder") - :read-only true}] - [:div.help-icon {:title (tr "viewer.header.share.copy-link") - :on-click copy-link} - i/copy]]) - [:div.hint-wrapper - (when (not ^boolean confirm?) - [:div.hint (tr "common.share-link.permissions-hint")]) - (cond - (true? confirm?) - [:div.confirm-dialog - [:div.description (tr "common.share-link.confirm-deletion-link-description")] - [:div.actions - [:input.btn-secondary - {:type "button" - :on-click #(reset! confirm* false) - :value (tr "labels.cancel")}] - [:input.btn-danger - {:type "button" - :on-click delete-link - :value (tr "common.share-link.destroy-link")}]]] - - (some? current-link) - [:input.btn-secondary - {:type "button" - :class "primary" - :on-click try-delete-link - :value (tr "common.share-link.destroy-link")}] - - :else - [:input.btn-primary - {:type "button" - :class "primary" - :on-click create-link - :value (tr "common.share-link.get-link")}])]]] - [:div.modal-content.ops-section - [:div.manage-permissions - {:on-click toggle-perms-visibility} - [:span.icon i/picker-hsv] - [:div.title (tr "common.share-link.manage-ops")]] - (when ^boolean perms-visible? - [:* - (let [all-selected? (:all-pages options) - pages (->> (get-in file [:data :pages]) - (map #(get-in file [:data :pages-index %]))) - selected (:pages options)] - [:* - [:div.view-mode - [:div.subtitle - [:span.icon i/play] (tr "common.share-link.permissions-pages")] - [:div.items + [:div {:class (stl/css :items)} (if (= 1 (count pages)) - [:div.input-checkbox.check-primary + + [:div {:class (stl/css :checkbox-wrapper)} [:input {:type "checkbox" :id (dm/str "page-" current-page-id) :data-page-id (dm/str current-page-id) :on-change on-mark-checked-page :checked true}] [:label {:for (str "page-" current-page-id)} (:name current-page)] + [:span {:class (stl/css-case :checkobox-tick true + :global/checked true)} + i/status-tick-refactor] [:span (str " " (tr "common.share-link.current-tag"))]] [:* - [:div.row - [:div.input-checkbox.check-primary - [:input {:type "checkbox" - :id "view-all" - :checked all-selected? - :name "pages-mode" - :on-change on-toggle-all}] - [:label {:for "view-all"} (tr "common.share-link.view-all")]] - [:span.count-pages (tr "common.share-link.page-shared" (i18n/c (count selected)))]] + [:div {:class (stl/css :select-all-row)} + [:div {:class (stl/css :checkbox-wrapper)} + [:label {:for "view-all" + :class (stl/css :select-all-label)} + [:span {:class (stl/css-case :global/checked all-selected?)} + (when all-selected? + i/status-tick-refactor)] + (tr "common.share-link.view-all") + [:input {:type "checkbox" + :id "view-all" + :checked all-selected? + :name "pages-mode" + :on-change on-toggle-all}]]] - [:ul.pages-selection + [:span {:class (stl/css :count-pages)} + (tr "common.share-link.page-shared" (i18n/c (count selected)))]] + + [:ul {:class (stl/css :pages-selection)} (for [{:keys [id name]} pages] - [:li.input-checkbox.check-primary {:key (dm/str id)} - [:input {:type "checkbox" - :id (dm/str "page-" id) - :data-page-id (dm/str id) - :on-change on-mark-checked-page - :checked (contains? selected id)}] - (if (= current-page-id id) - [:* - [:label {:for (dm/str "page-" id)} name] - [:span.current-tag (dm/str " " (tr "common.share-link.current-tag"))]] - [:label {:for (dm/str "page-" id)} name])])]])]]]) - [:div.access-mode - [:div.subtitle - [:span.icon i/chat] - (tr "common.share-link.permissions-can-comment")] - [:div.items - [:select.input-select {:on-change on-comment-change - :value (:who-comment options)} - [:option {:value "team"} (tr "common.share-link.team-members")] - [:option {:value "all"} (tr "common.share-link.all-users")]]]] - [:div.inspect-mode - [:div.subtitle - [:span.icon i/code] - (tr "common.share-link.permissions-can-inspect")] - [:div.items - [:select.input-select {:on-change on-inspect-change - :value (:who-inspect options)} - [:option {:value "team"} (tr "common.share-link.team-members")] - [:option {:value "all"} (tr "common.share-link.all-users")]]]]])]]]))) + [:li {:class (stl/css :checkbox-wrapper) + :key (dm/str id)} + [:label {:for (dm/str "page-" id)} + [:span {:class (stl/css-case :global/checked (contains? selected id))} + (when (contains? selected id) + i/status-tick-refactor)] + name + (when (= current-page-id id) + [:div {:class (stl/css :current-tag)} (dm/str " " (tr "common.share-link.current-tag"))]) + [:input {:type "checkbox" + :id (dm/str "page-" id) + :data-page-id (dm/str id) + :on-change on-mark-checked-page + :checked (contains? selected id)}]]])]])]]) + + [:div {:class (stl/css :access-mode)} + [:div {:class (stl/css :subtitle)} + (tr "common.share-link.permissions-can-comment")] + [:div {:class (stl/css :items)} + [:& select + {:class (stl/css :who-comment-select) + :default-value (dm/str (:who-comment options)) + :options [{:value "team" :label (tr "common.share-link.team-members")} + {:value "all" :label (tr "common.share-link.all-users")}] + :on-change on-comment-change}]]] + [:div {:class (stl/css :inspect-mode)} + [:div {:class (stl/css :subtitle)} + (tr "common.share-link.permissions-can-inspect")] + [:div {:class (stl/css :items)} + [:& select + {:class (stl/css :who-inspect-select) + :default-value (dm/str (:who-inspect options)) + :options [{:value "team" :label (tr "common.share-link.team-members")} + {:value "all" :label (tr "common.share-link.all-users")}] + :on-change on-inspect-change}]]]])])]]])) diff --git a/frontend/src/app/main/ui/viewer/share_link.scss b/frontend/src/app/main/ui/viewer/share_link.scss index dc0fbc7b4e..da9ce16d71 100644 --- a/frontend/src/app/main/ui/viewer/share_link.scss +++ b/frontend/src/app/main/ui/viewer/share_link.scss @@ -48,6 +48,7 @@ } .hint { flex-grow: 1; + color: var(--modal-text-foreground-color); } .custon-input-wrapper { @include flexRow; @@ -68,6 +69,7 @@ border: $s-1 solid var(--input-border-color-active); } } + .copy-button { @extend .button-secondary; @include flexRow; @@ -93,9 +95,11 @@ .button-active { @extend .modal-accept-btn; } + .button-cancel { @extend .modal-cancel-btn; } + .button-danger { @extend .modal-danger-btn; } @@ -126,12 +130,20 @@ transform: rotate(90deg); } } + .view-mode, .access-mode, .inspect-mode { display: flex; width: 100%; } + +.view-mode { + max-height: $s-248; + overflow: hidden auto; + scrollbar-gutter: stable; +} + .subtitle { color: var(--modal-text-foreground-color); display: flex; diff --git a/frontend/src/app/main/ui/workspace.cljs b/frontend/src/app/main/ui/workspace.cljs index 045d1d533c..ff447eb017 100644 --- a/frontend/src/app/main/ui/workspace.cljs +++ b/frontend/src/app/main/ui/workspace.cljs @@ -90,12 +90,12 @@ {:key (dm/str "workspace-" page-id) :ref node-ref} - [:section.workspace-viewport + [:section {:class (stl/css :workspace-viewport)} (when (dbg/enabled? :coordinates) [:& coordinates/coordinates {:colorpalette? colorpalette?}]) (when (dbg/enabled? :history-overlay) - [:div.history-debug-overlay + [:div {:class (stl/css :history-debug-overlay)} [:button {:on-click #(st/emit! dw/reinitialize-undo)} "CLEAR"] [:& history-toolbox]]) diff --git a/frontend/src/app/main/ui/workspace.scss b/frontend/src/app/main/ui/workspace.scss index 72633f95ce..410c831862 100644 --- a/frontend/src/app/main/ui/workspace.scss +++ b/frontend/src/app/main/ui/workspace.scss @@ -40,3 +40,25 @@ } } } + +.workspace-content { + grid-area: viewport; +} + +.history-debug-overlay { + bottom: 0; + max-height: $s-500; + width: $s-500; + overflow-y: auto; + position: absolute; + z-index: $z-index-modal; +} + +.workspace-viewport { + overflow: hidden; + transition: none; + display: grid; + grid-template-rows: $s-20 1fr; + grid-template-columns: $s-20 1fr; + flex: 1; +} diff --git a/frontend/src/app/main/ui/workspace/colorpicker.cljs b/frontend/src/app/main/ui/workspace/colorpicker.cljs index c4e1f8b3e2..4972589ed6 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker.cljs @@ -20,7 +20,6 @@ [app.main.ui.components.file-uploader :refer [file-uploader]] [app.main.ui.components.select :refer [select]] [app.main.ui.components.tab-container :refer [tab-container tab-element]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.ui.workspace.colorpicker.color-inputs :refer [color-inputs]] [app.main.ui.workspace.colorpicker.gradients :refer [gradients]] @@ -52,8 +51,7 @@ (mf/defc colorpicker [{:keys [data disable-gradient disable-opacity disable-image on-change on-accept]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - state (mf/deref refs/colorpicker) + (let [state (mf/deref refs/colorpicker) node-ref (mf/use-ref) ;; TODO: I think we need to put all this picking state under @@ -267,235 +265,123 @@ :h h :s s :v v :alpha (/ alpha 255)})))) - (if new-css-system - [:div {:class (stl/css :colorpicker) - :ref node-ref - :style {:touch-action "none"}} - [:div {:class (stl/css :top-actions)} - (when (or (not disable-gradient) (not disable-image)) - [:div {:class (stl/css :select)} - [:& select - {:default-value selected-mode - :options options - :on-change handle-change-mode}]]) - (when (not= selected-mode :image) - [:button {:class (stl/css-case :picker-btn true - :selected picking-color?) - :on-click handle-click-picker} - i/picker-refactor])] + [:div {:class (stl/css :colorpicker) + :ref node-ref + :style {:touch-action "none"}} + [:div {:class (stl/css :top-actions)} + (when (or (not disable-gradient) (not disable-image)) + [:div {:class (stl/css :select)} + [:& select + {:default-value selected-mode + :options options + :on-change handle-change-mode}]]) + (when (not= selected-mode :image) + [:button {:class (stl/css-case :picker-btn true + :selected picking-color?) + :on-click handle-click-picker} + i/picker-refactor])] - (when (or (= selected-mode :linear-gradient) + (when (or (= selected-mode :linear-gradient) (= selected-mode :radial-gradient)) - [:& gradients - {:stops (:stops state) - :editing-stop (:editing-stop state) - :on-select-stop handle-change-stop}]) + [:& gradients + {:stops (:stops state) + :editing-stop (:editing-stop state) + :on-select-stop handle-change-stop}]) - (if (= selected-mode :image) - (let [uri (cfg/resolve-file-media (:image current-color))] - [:div {:class (stl/css :select-image)} - [:div {:class (stl/css :content)} - (when (:image current-color) - [:img {:src uri}])] - [:button - {:class (stl/css :choose-image) - :title (tr "media.choose-image") - :aria-label (tr "media.choose-image") - :on-click on-fill-image-click} - (tr "media.choose-image") - [:& file-uploader - {:input-id "fill-image-upload" - :accept "image/jpeg,image/png" - :multi false - :ref fill-image-ref - :on-selected on-fill-image-selected}]]]) - [:* - [:div {:class (stl/css :colorpicker-tabs)} - [:& tab-container - {:on-change-tab set-tab! - :selected @active-color-tab - :collapsable? false} - - [:& tab-element {:id :ramp :title i/rgba-refactor} - (if picking-color? - [:div {:class (stl/css :picker-detail-wrapper)} - [:div {:class (stl/css :center-circle)}] - [:canvas#picker-detail {:width 256 :height 140}]] - [:& ramp-selector - {:color current-color - :disable-opacity disable-opacity - :on-change handle-change-color - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}])] - - [:& tab-element {:id :harmony :title i/rgba-complementary-refactor} - (if picking-color? - [:div {:class (stl/css :picker-detail-wrapper)} - [:div {:class (stl/css :center-circle)}] - [:canvas#picker-detail {:width 256 :height 140}]] - [:& harmony-selector - {:color current-color - :disable-opacity disable-opacity - :on-change handle-change-color - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}])] - - [:& tab-element {:id :hsva :title i/hsva-refactor} - (if picking-color? - [:div {:class (stl/css :picker-detail-wrapper)} - [:div {:class (stl/css :center-circle)}] - [:canvas#picker-detail {:width 256 :height 140}]] - [:& hsva-selector - {:color current-color - :disable-opacity disable-opacity - :on-change handle-change-color - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}])]]] - - [:& color-inputs - {:type (if (= @active-color-tab :hsva) :hsv :rgb) - :disable-opacity disable-opacity - :color current-color - :on-change handle-change-color}] - - [:& libraries - {:state state - :current-color current-color - :disable-gradient disable-gradient - :disable-opacity disable-opacity - :disable-image disable-image - :on-select-color on-select-library-color - :on-add-library-color on-add-library-color}]]) - - (when on-accept - [:div {:class (stl/css :actions)} - [:button {:class (stl/css-case - :accept-color true - :btn-disabled disabled-color-accept?) - :on-click on-color-accept - :disabled disabled-color-accept?} - (tr "workspace.libraries.colors.save-color")]])] - - [:div.colorpicker {:ref node-ref - :style {:touch-action "none"}} - [:div.colorpicker-content - [:div.top-actions - (when (or (not disable-gradient) (not disable-image)) - [:div.element-set-content - [:& select - {:default-value selected-mode - :options options - :on-change handle-change-mode}]]) - (when (not= selected-mode :image) - [:button.picker-btn - {:class (when picking-color? "active") - :on-click handle-click-picker} - i/picker])] - - (when (or (= (:type state) :linear-gradient) - (= (:type state) :radial-gradient)) - - [:& gradients - {:stops (:stops state) - :editing-stop (:editing-stop state) - :on-select-stop handle-change-stop}]) - - (if (= selected-mode :image) - (let [uri (cfg/resolve-file-media (:image current-color))] - [:div.select-image - [:div.content - (when (:image current-color) - [:img {:src uri}])] - [:button.btn-secondary - {:title (tr "media.choose-image") - :aria-label (tr "media.choose-image") - :on-click on-fill-image-click} - (tr "media.choose-image") - [:& file-uploader - {:input-id "fill-image-upload" - :accept "image/jpeg,image/png" - :multi false - :ref fill-image-ref - :on-selected on-fill-image-selected}]]]) - [:* - [:div.colorpicker-tabs - [:div.colorpicker-tab.tooltip.tooltip-bottom.tooltip-expand - {:class (when (= @active-color-tab :ramp) "active") - :alt (tr "workspace.libraries.colors.rgba") - :on-click set-tab! - :data-tab "ramp"} i/picker-ramp] - [:div.colorpicker-tab.tooltip.tooltip-bottom.tooltip-expand - {:class (when (= @active-color-tab :harmony) "active") - :alt (tr "workspace.libraries.colors.rgb-complementary") - :on-click set-tab! - :data-tab "harmony"} i/picker-harmony] - [:div.colorpicker-tab.tooltip.tooltip-bottom.tooltip-expand - {:class (when (= @active-color-tab :hsva) "active") - :alt (tr "workspace.libraries.colors.hsv") - :on-click set-tab! - :data-tab "hsva"} i/picker-hsv]] + (if (= selected-mode :image) + (let [uri (cfg/resolve-file-media (:image current-color))] + [:div {:class (stl/css :select-image)} + [:div {:class (stl/css :content)} + (when (:image current-color) + [:img {:src uri}])] + [:button + {:class (stl/css :choose-image) + :title (tr "media.choose-image") + :aria-label (tr "media.choose-image") + :on-click on-fill-image-click} + (tr "media.choose-image") + [:& file-uploader + {:input-id "fill-image-upload" + :accept "image/jpeg,image/png" + :multi false + :ref fill-image-ref + :on-selected on-fill-image-selected}]]]) + [:* + [:div {:class (stl/css :colorpicker-tabs)} + [:& tab-container + {:on-change-tab set-tab! + :selected @active-color-tab + :collapsable? false} + [:& tab-element {:id :ramp :title i/rgba-refactor} (if picking-color? - [:div.picker-detail-wrapper - [:div.center-circle] - [:canvas#picker-detail {:width 200 :height 160}]] - (case @active-color-tab - :ramp - [:& ramp-selector - {:color current-color - :disable-opacity disable-opacity - :on-change handle-change-color - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}] - :harmony - [:& harmony-selector - {:color current-color - :disable-opacity disable-opacity - :on-change handle-change-color - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}] - :hsva - [:& hsva-selector - {:color current-color - :disable-opacity disable-opacity - :on-change handle-change-color - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}] - nil)) + [:div {:class (stl/css :picker-detail-wrapper)} + [:div {:class (stl/css :center-circle)}] + [:canvas#picker-detail {:width 256 :height 140}]] + [:& ramp-selector + {:color current-color + :disable-opacity disable-opacity + :on-change handle-change-color + :on-start-drag on-start-drag + :on-finish-drag on-finish-drag}])] - [:& color-inputs - {:type (if (= @active-color-tab :hsva) :hsv :rgb) - :disable-opacity disable-opacity - :color current-color - :on-change handle-change-color}] + [:& tab-element {:id :harmony :title i/rgba-complementary-refactor} + (if picking-color? + [:div {:class (stl/css :picker-detail-wrapper)} + [:div {:class (stl/css :center-circle)}] + [:canvas#picker-detail {:width 256 :height 140}]] + [:& harmony-selector + {:color current-color + :disable-opacity disable-opacity + :on-change handle-change-color + :on-start-drag on-start-drag + :on-finish-drag on-finish-drag}])] - [:& libraries - {:state state - :current-color current-color - :disable-gradient disable-gradient - :disable-opacity disable-opacity - :disable-image disable-image - :on-select-color on-select-library-color - :on-add-library-color on-add-library-color}]]) + [:& tab-element {:id :hsva :title i/hsva-refactor} + (if picking-color? + [:div {:class (stl/css :picker-detail-wrapper)} + [:div {:class (stl/css :center-circle)}] + [:canvas#picker-detail {:width 256 :height 140}]] + [:& hsva-selector + {:color current-color + :disable-opacity disable-opacity + :on-change handle-change-color + :on-start-drag on-start-drag + :on-finish-drag on-finish-drag}])]]] - (when on-accept - [:div.actions - [:button.btn-primary.btn-large - {:on-click on-color-accept - :disabled disabled-color-accept? - :class (dom/classnames - :btn-disabled disabled-color-accept?)} - (tr "workspace.libraries.colors.save-color")]])]]))) + [:& color-inputs + {:type (if (= @active-color-tab :hsva) :hsv :rgb) + :disable-opacity disable-opacity + :color current-color + :on-change handle-change-color}] + + [:& libraries + {:state state + :current-color current-color + :disable-gradient disable-gradient + :disable-opacity disable-opacity + :disable-image disable-image + :on-select-color on-select-library-color + :on-add-library-color on-add-library-color}]]) + + (when on-accept + [:div {:class (stl/css :actions)} + [:button {:class (stl/css-case + :accept-color true + :btn-disabled disabled-color-accept?) + :on-click on-color-accept + :disabled disabled-color-accept?} + (tr "workspace.libraries.colors.save-color")]])])) (defn calculate-position "Calculates the style properties for the given coordinates and position" - [{vh :height} position x y new-css-system] + [{vh :height} position x y] (let [;; picker height in pixels - h(if new-css-system 510 430) + h 510 ;; Checks for overflow outside the viewport height overflow-fix (max 0 (+ y (- 50) h (- vh))) - x-pos (if new-css-system 325 250)] + x-pos 325] (cond (or (nil? x) (nil? y)) {:left "auto" :right "16rem" :top "4rem"} (= position :left) {:left (str (- x x-pos) "px") @@ -512,12 +398,11 @@ disable-opacity disable-image on-change on-close on-accept] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - vport (mf/deref viewport) + (let [vport (mf/deref viewport) dirty? (mf/use-var false) last-change (mf/use-var nil) position (or position :left) - style (calculate-position vport position x y new-css-system) + style (calculate-position vport position x y) handle-change (fn [new-data] @@ -531,7 +416,7 @@ #(when (and @dirty? @last-change on-close) (on-close @last-change)))) - [:div {:class (stl/css new-css-system :colorpicker-tooltip) + [:div {:class (stl/css :colorpicker-tooltip) :style (clj->js style)} [:& colorpicker {:data data :disable-gradient disable-gradient diff --git a/frontend/src/app/main/ui/workspace/colorpicker.scss b/frontend/src/app/main/ui/workspace/colorpicker.scss index ae18f6abd6..4abe58b463 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker.scss +++ b/frontend/src/app/main/ui/workspace/colorpicker.scss @@ -11,118 +11,126 @@ top: $s-100; left: calc(10 * $s-140); width: auto; - .colorpicker { - border-radius: $br-8; - width: $s-260; - & > * { - width: $s-260; - } - .top-actions { - display: flex; - align-items: flex-start; - flex-direction: row-reverse; - justify-content: space-between; - height: $s-40; - .picker-btn { - @include buttonStyle; - @include flexCenter; - border-radius: $br-8; - background-color: transparent; - border: $s-1 solid transparent; - height: $s-20; - width: $s-20; - border-radius: $br-4; - padding: 0; - margin-top: $s-4; - svg { - @extend .button-icon; - stroke: var(--button-tertiary-foreground-color-rest); - } - &:hover { - svg { - stroke: var(--button-tertiary-foreground-color-focus); - } - } - &:focus, - &:focus-visible { - outline: none; - svg { - stroke: var(--button-secondary-foreground-color-hover); - } - } - &:active { - outline: none; - border: $s-1 solid transparent; - svg { - stroke: var(--button-tertiary-foreground-color-active); - } - } - &.selected { - svg { - stroke: var(--button-tertiary-foreground-color-active); - } - } - } - .gradient-buttons { - display: flex; - align-items: center; - gap: $s-8; - .gradient-btn { - @extend .button-tertiary; - height: $s-20; - width: $s-20; - border-radius: $br-4; - border: $s-2 solid transparent; - &:hover { - border: $s-2 solid var(--colorpicker-details-color-selected); - } - } - .linear-gradient-btn { - background: linear-gradient(180deg, var(--color-foreground-secondary), transparent); - &.selected { - background: linear-gradient(to bottom, rgba(126, 255, 245, 1) 0%, rgba(126, 255, 245, 0.2) 100%); - border: $s-2 solid var(--colorpicker-details-color-selected); - } - } +} - .radial-gradient-btn { - background: radial-gradient(transparent, var(--color-foreground-secondary)); - &.selected { - background: radial-gradient(rgba(126, 255, 245, 1) 0%, rgba(126, 255, 245, 0.2) 100%); - border: $s-2 solid var(--colorpicker-details-color-selected); - } - } - } - } - .actions { - display: flex; - gap: $s-4; - .accept-color { - @include tabTitleTipography; - @extend .button-secondary; - width: 100%; - height: $s-32; - margin-top: $s-8; - } +.colorpicker { + border-radius: $br-8; + width: $s-260; + & > * { + width: $s-260; + } +} + +.top-actions { + display: flex; + align-items: flex-start; + flex-direction: row-reverse; + justify-content: space-between; + height: $s-40; +} + +.picker-btn { + @include buttonStyle; + @include flexCenter; + border-radius: $br-8; + background-color: transparent; + border: $s-1 solid transparent; + height: $s-20; + width: $s-20; + border-radius: $br-4; + padding: 0; + margin-top: $s-4; + svg { + @extend .button-icon; + stroke: var(--button-tertiary-foreground-color-rest); + } + &:hover { + svg { + stroke: var(--button-tertiary-foreground-color-focus); } } - .colorpicker-tabs { - .picker-detail-wrapper { - @include flexCenter; - position: relative; - margin: $s-12 0 $s-8 0; - .center-circle { - width: $s-24; - height: $s-24; - border: $s-2 solid var(--colorpicker-details-color); - border-radius: $br-circle; - position: absolute; - left: 50%; - top: 50%; - transform: translate(calc(-1 * $s-12), calc(-1 * $s-12)); - } + &:focus, + &:focus-visible { + outline: none; + svg { + stroke: var(--button-secondary-foreground-color-hover); } } + &:active { + outline: none; + border: $s-1 solid transparent; + svg { + stroke: var(--button-tertiary-foreground-color-active); + } + } + &.selected { + svg { + stroke: var(--button-tertiary-foreground-color-active); + } + } +} + +.gradient-buttons { + display: flex; + align-items: center; + gap: $s-8; +} + +.gradient-btn { + @extend .button-tertiary; + height: $s-20; + width: $s-20; + border-radius: $br-4; + border: $s-2 solid transparent; + &:hover { + border: $s-2 solid var(--colorpicker-details-color-selected); + } +} + +.linear-gradient-btn { + background: linear-gradient(180deg, var(--color-foreground-secondary), transparent); + &.selected { + background: linear-gradient(to bottom, rgba(126, 255, 245, 1) 0%, rgba(126, 255, 245, 0.2) 100%); + border: $s-2 solid var(--colorpicker-details-color-selected); + } +} + +.radial-gradient-btn { + background: radial-gradient(transparent, var(--color-foreground-secondary)); + &.selected { + background: radial-gradient(rgba(126, 255, 245, 1) 0%, rgba(126, 255, 245, 0.2) 100%); + border: $s-2 solid var(--colorpicker-details-color-selected); + } +} + +.actions { + display: flex; + gap: $s-4; +} + +.accept-color { + @include tabTitleTipography; + @extend .button-secondary; + width: 100%; + height: $s-32; + margin-top: $s-8; +} + +.picker-detail-wrapper { + @include flexCenter; + position: relative; + margin: $s-12 0 $s-8 0; +} + +.center-circle { + width: $s-24; + height: $s-24; + border: $s-2 solid var(--colorpicker-details-color); + border-radius: $br-circle; + position: absolute; + left: 50%; + top: 50%; + transform: translate(calc(-1 * $s-12), calc(-1 * $s-12)); } .select { @@ -131,29 +139,31 @@ .select-image { margin-top: $s-4; - .content { - border-radius: $br-8; - display: flex; - justify-content: center; - background-image: url("/images/colorpicker-no-image.png"); - background-position: center; - background-size: auto $s-140; - height: $s-140; - margin-bottom: $s-6; - margin-right: $s-1; - img { - height: fit-content; - width: fit-content; - max-height: 100%; - max-width: 100%; - margin: auto; - } - } - .choose-image { - @extend .button-secondary; - @include tabTitleTipography; - width: 100%; - margin-top: $s-12; - height: $s-32; +} + +.content { + border-radius: $br-8; + display: flex; + justify-content: center; + background-image: url("/images/colorpicker-no-image.png"); + background-position: center; + background-size: auto $s-140; + height: $s-140; + margin-bottom: $s-6; + margin-right: $s-1; + img { + height: fit-content; + width: fit-content; + max-height: 100%; + max-width: 100%; + margin: auto; } } + +.choose-image { + @extend .button-secondary; + @include tabTitleTipography; + width: 100%; + margin-top: $s-12; + height: $s-32; +} diff --git a/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs b/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs index 531b67ccbf..53fb08dfd0 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs @@ -10,7 +10,6 @@ [app.common.colors :as cc] [app.common.data :as d] [app.common.math :as mth] - [app.main.ui.context :as ctx] [app.util.dom :as dom] [rumext.v2 :as mf])) @@ -29,8 +28,7 @@ (* (/ val 255) 100)) (mf/defc color-inputs [{:keys [type color disable-opacity on-change]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - {red :r green :g blue :b + (let [{red :r green :g blue :b hue :h saturation :s value :v hex :hex alpha :alpha} color @@ -115,133 +113,52 @@ property-val)] (dom/set-value! node new-val)))))))) - (if new-css-system - [:div {:class (stl/css-case :color-values true - :disable-opacity disable-opacity)} + [:div {:class (stl/css-case :color-values true + :disable-opacity disable-opacity)} - [:div {:class (stl/css :colors-row)} - (if (= type :rgb) - [:* - [:div {:class (stl/css :input-wrapper)} - [:span {:class (stl/css :input-label)} "R"] - [:input {:id "red-value" - :ref (:r refs) - :type "number" - :min 0 - :max 255 - :default-value red - :on-change (on-change-property :r 255)}]] - [:div {:class (stl/css :input-wrapper)} - [:span {:class (stl/css :input-label)} "G"] - [:input {:id "green-value" - :ref (:g refs) - :type "number" - :min 0 - :max 255 - :default-value green - :on-change (on-change-property :g 255)}]] - [:div {:class (stl/css :input-wrapper)} - [:span {:class (stl/css :input-label)} "B"] - [:input {:id "blue-value" - :ref (:b refs) - :type "number" - :min 0 - :max 255 - :default-value blue - :on-change (on-change-property :b 255)}]]] - - [:* - [:div {:class (stl/css :input-wrapper)} - [:span {:class (stl/css :input-label)} "H"] - [:input {:id "hue-value" - :ref (:h refs) - :type "number" - :min 0 - :max 360 - :default-value hue - :on-change (on-change-property :h 360)}]] - [:div {:class (stl/css :input-wrapper)} - [:span {:class (stl/css :input-label)} "S"] - [:input {:id "saturation-value" - :ref (:s refs) - :type "number" - :min 0 - :max 100 - :step 1 - :default-value saturation - :on-change (on-change-property :s 100)}]] - [:div {:class (stl/css :input-wrapper)} - [:span {:class (stl/css :input-label)} "V"] - [:input {:id "value-value" - :ref (:v refs) - :type "number" - :min 0 - :max 100 - :default-value value - :on-change (on-change-property :v 100)}]]])] - [:div {:class (stl/css :hex-alpha-wrapper)} - [:div {:class (stl/css-case :input-wrapper true - :hex true)} - [:span {:class (stl/css :input-label)} "HEX"] - [:input {:id "hex-value" - :ref (:hex refs) - :default-value hex - :on-change on-change-hex - :on-blur on-blur-hex}]] - (when (not disable-opacity) - [:div {:class (stl/css-case :input-wrapper true)} - [:span {:class (stl/css :input-label)} "A"] - [:input {:id "alpha-value" - :ref (:alpha refs) - :type "number" - :min 0 - :step 1 - :max 100 - :default-value (if (= alpha :multiple) "" alpha) - :on-change on-change-opacity}]])]] - - [:div.color-values - {:class (when disable-opacity "disable-opacity")} - [:input {:id "hex-value" - :ref (:hex refs) - :default-value hex - :on-change on-change-hex - :on-blur on-blur-hex}] - - (if (= type :rgb) - [:* + [:div {:class (stl/css :colors-row)} + (if (= type :rgb) + [:* + [:div {:class (stl/css :input-wrapper)} + [:span {:class (stl/css :input-label)} "R"] [:input {:id "red-value" :ref (:r refs) :type "number" :min 0 :max 255 :default-value red - :on-change (on-change-property :r 255)}] - + :on-change (on-change-property :r 255)}]] + [:div {:class (stl/css :input-wrapper)} + [:span {:class (stl/css :input-label)} "G"] [:input {:id "green-value" :ref (:g refs) :type "number" :min 0 :max 255 :default-value green - :on-change (on-change-property :g 255)}] - + :on-change (on-change-property :g 255)}]] + [:div {:class (stl/css :input-wrapper)} + [:span {:class (stl/css :input-label)} "B"] [:input {:id "blue-value" :ref (:b refs) :type "number" :min 0 :max 255 :default-value blue - :on-change (on-change-property :b 255)}]] - [:* + :on-change (on-change-property :b 255)}]]] + + [:* + [:div {:class (stl/css :input-wrapper)} + [:span {:class (stl/css :input-label)} "H"] [:input {:id "hue-value" :ref (:h refs) :type "number" :min 0 :max 360 :default-value hue - :on-change (on-change-property :h 360)}] - + :on-change (on-change-property :h 360)}]] + [:div {:class (stl/css :input-wrapper)} + [:span {:class (stl/css :input-label)} "S"] [:input {:id "saturation-value" :ref (:s refs) :type "number" @@ -249,35 +166,33 @@ :max 100 :step 1 :default-value saturation - :on-change (on-change-property :s 100)}] - + :on-change (on-change-property :s 100)}]] + [:div {:class (stl/css :input-wrapper)} + [:span {:class (stl/css :input-label)} "V"] [:input {:id "value-value" :ref (:v refs) :type "number" :min 0 :max 100 :default-value value - :on-change (on-change-property :v 100)}]]) - - (when (not disable-opacity) - [:input.alpha-value {:id "alpha-value" - :ref (:alpha refs) - :type "number" - :min 0 - :step 1 - :max 100 - :default-value (if (= alpha :multiple) "" alpha) - :on-change on-change-opacity}]) - - [:label.hex-label {:for "hex-value"} "HEX"] - (if (= type :rgb) - [:* - [:label.red-label {:for "red-value"} "R"] - [:label.green-label {:for "green-value"} "G"] - [:label.blue-label {:for "blue-value"} "B"]] - [:* - [:label.red-label {:for "hue-value"} "H"] - [:label.green-label {:for "saturation-value"} "S"] - [:label.blue-label {:for "value-value"} "V"]]) - (when (not disable-opacity) - [:label.alpha-label {:for "alpha-value"} "A"])]))) + :on-change (on-change-property :v 100)}]]])] + [:div {:class (stl/css :hex-alpha-wrapper)} + [:div {:class (stl/css-case :input-wrapper true + :hex true)} + [:span {:class (stl/css :input-label)} "HEX"] + [:input {:id "hex-value" + :ref (:hex refs) + :default-value hex + :on-change on-change-hex + :on-blur on-blur-hex}]] + (when (not disable-opacity) + [:div {:class (stl/css-case :input-wrapper true)} + [:span {:class (stl/css :input-label)} "A"] + [:input {:id "alpha-value" + :ref (:alpha refs) + :type "number" + :min 0 + :step 1 + :max 100 + :default-value (if (= alpha :multiple) "" alpha) + :on-change on-change-opacity}]])]])) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/gradients.cljs b/frontend/src/app/main/ui/workspace/colorpicker/gradients.cljs index 4242861de6..9764b4eaec 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/gradients.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/gradients.cljs @@ -8,7 +8,6 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] - [app.main.ui.context :as ctx] [cuerdas.core :as str] [rumext.v2 :as mf])) @@ -22,40 +21,22 @@ (mf/defc gradients [{:keys [stops editing-stop on-select-stop]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:div {:class (stl/css :gradient-stops)} - [:div {:class (stl/css :gradient-background-wrapper)} - [:div {:class (stl/css :gradient-background) - :style {:background (gradient->string stops)}}]] + [:div {:class (stl/css :gradient-stops)} + [:div {:class (stl/css :gradient-background-wrapper)} + [:div {:class (stl/css :gradient-background) + :style {:background (gradient->string stops)}}]] - [:div {:class (stl/css :gradient-stop-wrapper)} - (for [{:keys [offset hex r g b alpha] :as value} stops] - [:button {:class (stl/css-case :gradient-stop true - :selected (= editing-stop offset)) - :data-value offset - :on-click on-select-stop - :style {:left (dm/str (* offset 100) "%") - :backgroundColor hex} - :key (dm/str offset)} + [:div {:class (stl/css :gradient-stop-wrapper)} + (for [{:keys [offset hex r g b alpha] :as value} stops] + [:button {:class (stl/css-case :gradient-stop true + :selected (= editing-stop offset)) + :data-value offset + :on-click on-select-stop + :style {:left (dm/str (* offset 100) "%") + :backgroundColor hex} + :key (dm/str offset)} - [:div {:class (stl/css :gradient-stop-color) - :style {:background-color hex}}] - [:div {:class (stl/css :gradient-stop-alpha) - :style {:background-color (str/ffmt "rgba(%1, %2, %3, %4)" r g b alpha)}}]])]] - - [:div.gradient-stops - [:div.gradient-background-wrapper - [:div.gradient-background {:style {:background (gradient->string stops)}}]] - - [:div.gradient-stop-wrapper - (for [{:keys [offset hex r g b alpha] :as value} stops] - [:div.gradient-stop - {:class (when (= editing-stop offset) "active") - :data-value offset - :on-click on-select-stop - :style {:left (dm/str (* offset 100) "%")} - :key (dm/str offset)} - - [:div.gradient-stop-color {:style {:background-color hex}}] - [:div.gradient-stop-alpha {:style {:background-color (str/ffmt "rgba(%1, %2, %3, %4)" r g b alpha)}}]])]]))) + [:div {:class (stl/css :gradient-stop-color) + :style {:background-color hex}}] + [:div {:class (stl/css :gradient-stop-alpha) + :style {:background-color (str/ffmt "rgba(%1, %2, %3, %4)" r g b alpha)}}]])]]) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/gradients.scss b/frontend/src/app/main/ui/workspace/colorpicker/gradients.scss index 78932650dd..a9b4107eeb 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/gradients.scss +++ b/frontend/src/app/main/ui/workspace/colorpicker/gradients.scss @@ -13,39 +13,41 @@ margin: $s-12 0; background-color: var(--colorpicker-handlers-color); border-radius: $br-6; +} - .gradient-background-wrapper { - height: 100%; - width: 100%; - border-radius: $br-6; - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=") - left center; +.gradient-background-wrapper { + height: 100%; + width: 100%; + border-radius: $br-6; + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=") + left center; +} - .gradient-background { - height: 100%; - width: 100%; - border-radius: $br-6; - border: $s-2 solid var(--colorpicker-details-color); - } - } - .gradient-stop-wrapper { - position: absolute; - width: calc(100% - 2rem); - .gradient-stop { - position: absolute; - display: grid; - grid-template-columns: 50% 50%; - width: $s-16; - height: $s-24; - border-radius: $br-4; - margin-top: calc(-1 * $s-2); - margin-left: calc(-1 * $s-8); - border: $s-2 solid var(--colorpicker-handlers-color); - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=") - left center; - &.selected { - border: $s-2 solid var(--colorpicker-details-color-selected); - } - } +.gradient-background { + height: 100%; + width: 100%; + border-radius: $br-6; + border: $s-2 solid var(--colorpicker-details-color); +} + +.gradient-stop-wrapper { + position: absolute; + width: calc(100% - 2rem); +} + +.gradient-stop { + position: absolute; + display: grid; + grid-template-columns: 50% 50%; + width: $s-16; + height: $s-24; + border-radius: $br-4; + margin-top: calc(-1 * $s-2); + margin-left: calc(-1 * $s-8); + border: $s-2 solid var(--colorpicker-handlers-color); + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=") + left center; + &.selected { + border: $s-2 solid var(--colorpicker-details-color-selected); } } diff --git a/frontend/src/app/main/ui/workspace/colorpicker/harmony.cljs b/frontend/src/app/main/ui/workspace/colorpicker/harmony.cljs index 2bc19b28bc..7ca720bd2a 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/harmony.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/harmony.cljs @@ -10,7 +10,6 @@ [app.common.colors :as cc] [app.common.geom.point :as gpt] [app.common.math :as mth] - [app.main.ui.context :as ctx] [app.main.ui.workspace.colorpicker.slider-selector :refer [slider-selector]] [app.util.dom :as dom] [app.util.object :as obj] @@ -59,11 +58,8 @@ (gpt/point x y))) (mf/defc harmony-selector [{:keys [color disable-opacity on-change on-start-drag on-finish-drag]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - canvas-ref (mf/use-ref nil) - canvas-side (if new-css-system - 192 - 152) + (let [canvas-ref (mf/use-ref nil) + canvas-side 192 {hue :h saturation :s value :v alpha :alpha} color pos-current (color->point canvas-side hue saturation) @@ -128,83 +124,44 @@ (mf/deps canvas-ref) (fn [] (when canvas-ref (create-color-wheel (mf/ref-val canvas-ref))))) - (if new-css-system - [:div {:class (stl/css :harmony-selector)} - [:div {:class (stl/css :handlers-wrapper)} - [:& slider-selector {:type :value - :vertical? true - :reverse? true - :value value - :max-value 255 - :vertical true - :on-change on-change-value - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}] - (when (not disable-opacity) - [[:& slider-selector {:type :opacity - :vertical? true - :value alpha - :max-value 1 - :vertical true - :on-change on-change-opacity - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}]])] + [:div {:class (stl/css :harmony-selector)} + [:div {:class (stl/css :handlers-wrapper)} + [:& slider-selector {:type :value + :vertical? true + :reverse? true + :value value + :max-value 255 + :vertical true + :on-change on-change-value + :on-start-drag on-start-drag + :on-finish-drag on-finish-drag}] + (when (not disable-opacity) + [[:& slider-selector {:type :opacity + :vertical? true + :value alpha + :max-value 1 + :vertical true + :on-change on-change-opacity + :on-start-drag on-start-drag + :on-finish-drag on-finish-drag}]])] - [:div {:class (stl/css :hue-wheel-wrapper)} - [:canvas {:class (stl/css :hue-wheel) - :ref canvas-ref - :width canvas-side - :height canvas-side - :on-pointer-down handle-start-drag - :on-pointer-up handle-stop-drag - :on-lost-pointer-capture handle-stop-drag - :on-click calculate-pos - :on-pointer-move #(when @dragging? (calculate-pos %))}] - [:div {:class (stl/css :handler) - :style {:pointer-events "none" - :left (:x pos-current) - :top (:y pos-current)}}] - [:div {:class (stl/css-case :handler true - :complement true) - :style {:left (:x pos-complement) - :top (:y pos-complement) - :cursor "pointer"} - :on-click on-complement-click}]]] - - [:div.harmony-selector - [:div.hue-wheel-wrapper - [:canvas.hue-wheel - {:ref canvas-ref - :width canvas-side - :height canvas-side - :on-pointer-down handle-start-drag - :on-pointer-up handle-stop-drag - :on-lost-pointer-capture handle-stop-drag - :on-click calculate-pos - :on-pointer-move #(when @dragging? (calculate-pos %))}] - [:div.handler {:style {:pointer-events "none" - :left (:x pos-current) - :top (:y pos-current)}}] - [:div.handler.complement {:style {:left (:x pos-complement) - :top (:y pos-complement) - :cursor "pointer"} - :on-click on-complement-click}]] - [:div.handlers-wrapper - [:& slider-selector {:class "value" - :vertical? true - :reverse? true - :value value - :max-value 255 - :vertical true - :on-change on-change-value - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}] - (when (not disable-opacity) - [:& slider-selector {:class "opacity" - :vertical? true - :value alpha - :max-value 1 - :vertical true - :on-change on-change-opacity - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}])]]))) + [:div {:class (stl/css :hue-wheel-wrapper)} + [:canvas {:class (stl/css :hue-wheel) + :ref canvas-ref + :width canvas-side + :height canvas-side + :on-pointer-down handle-start-drag + :on-pointer-up handle-stop-drag + :on-lost-pointer-capture handle-stop-drag + :on-click calculate-pos + :on-pointer-move #(when @dragging? (calculate-pos %))}] + [:div {:class (stl/css :handler) + :style {:pointer-events "none" + :left (:x pos-current) + :top (:y pos-current)}}] + [:div {:class (stl/css-case :handler true + :complement true) + :style {:left (:x pos-complement) + :top (:y pos-complement) + :cursor "pointer"} + :on-click on-complement-click}]]])) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/harmony.scss b/frontend/src/app/main/ui/workspace/colorpicker/harmony.scss index 15b9f54d5e..04bc1d46ac 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/harmony.scss +++ b/frontend/src/app/main/ui/workspace/colorpicker/harmony.scss @@ -12,32 +12,33 @@ gap: $s-8; margin-top: $s-12; margin-bottom: $s-8; - .hue-wheel-wrapper { - @include flexCenter; - position: relative; - - .hue-wheel { - width: $s-196; - height: $s-196; - } - - .handler { - @extend .colorpicker-handler; - height: $s-16; - width: $s-16; - border: $s-2 solid var(--colorpicker-handlers-color); - } - - .handler.complement { - background-color: var(--colorpicker-handlers-color); - border: $s-2 solid var(--colorpicker-handlers-color); - } - } - - .handlers-wrapper { - @include flexRow; - height: $s-200; - width: $s-52; - flex-grow: 1; - } +} + +.hue-wheel-wrapper { + @include flexCenter; + position: relative; +} + +.hue-wheel { + width: $s-196; + height: $s-196; +} + +.handler { + @extend .colorpicker-handler; + height: $s-16; + width: $s-16; + border: $s-2 solid var(--colorpicker-handlers-color); +} + +.handler.complement { + background-color: var(--colorpicker-handlers-color); + border: $s-2 solid var(--colorpicker-handlers-color); +} + +.handlers-wrapper { + @include flexRow; + height: $s-200; + width: $s-52; + flex-grow: 1; } diff --git a/frontend/src/app/main/ui/workspace/colorpicker/hsva.cljs b/frontend/src/app/main/ui/workspace/colorpicker/hsva.cljs index 9236371144..cf4554b6e3 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/hsva.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/hsva.cljs @@ -8,13 +8,11 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.colors :as cc] - [app.main.ui.context :as ctx] [app.main.ui.workspace.colorpicker.slider-selector :refer [slider-selector]] [rumext.v2 :as mf])) (mf/defc hsva-selector [{:keys [color disable-opacity on-change on-start-drag on-finish-drag]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - {hue :h saturation :s value :v alpha :alpha} color + (let [{hue :h saturation :s value :v alpha :alpha} color handle-change-slider (fn [key] (fn [new-value] (let [change (hash-map key new-value) @@ -25,87 +23,46 @@ {:hex hex :r r :g g :b b}))))) on-change-opacity (fn [new-alpha] (on-change {:alpha new-alpha}))] - (if new-css-system - [:div {:class (stl/css :hsva-selector)} + [:div {:class (stl/css :hsva-selector)} + [:div {:class (stl/css :hsva-row)} + [:span {:class (stl/css :hsva-selector-label)} "H"] + [:& slider-selector + {:class (stl/css :hsva-bar) + :type :hue + :max-value 360 + :value hue + :on-change (handle-change-slider :h) + :on-start-drag on-start-drag + :on-finish-drag on-finish-drag}]] + [:div {:class (stl/css :hsva-row)} + [:span {:class (stl/css :hsva-selector-label)} "S"] + [:& slider-selector + {:class (stl/css :hsva-bar) + :type :saturation + :max-value 1 + :value saturation + :on-change (handle-change-slider :s) + :on-start-drag on-start-drag + :on-finish-drag on-finish-drag}]] + [:div {:class (stl/css :hsva-row)} + [:span {:class (stl/css :hsva-selector-label)} "V"] + [:& slider-selector + {:class (stl/css :hsva-bar) + :type :value + :reverse? false + :max-value 255 + :value value + :on-change (handle-change-slider :v) + :on-start-drag on-start-drag + :on-finish-drag on-finish-drag}]] + (when (not disable-opacity) [:div {:class (stl/css :hsva-row)} - [:span {:class (stl/css :hsva-selector-label)} "H"] + [:span {:class (stl/css :hsva-selector-label)} "A"] [:& slider-selector {:class (stl/css :hsva-bar) - :type :hue - :max-value 360 - :value hue - :on-change (handle-change-slider :h) - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}]] - [:div {:class (stl/css :hsva-row)} - [:span {:class (stl/css :hsva-selector-label)} "S"] - [:& slider-selector - {:class (stl/css :hsva-bar) - :type :saturation + :type :opacity :max-value 1 - :value saturation - :on-change (handle-change-slider :s) + :value alpha + :on-change on-change-opacity :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}]] - [:div {:class (stl/css :hsva-row)} - [:span {:class (stl/css :hsva-selector-label)} "V"] - [:& slider-selector - {:class (stl/css :hsva-bar) - :type :value - :reverse? false - :max-value 255 - :value value - :on-change (handle-change-slider :v) - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}]] - (when (not disable-opacity) - [:div {:class (stl/css :hsva-row)} - [:span {:class (stl/css :hsva-selector-label)} "A"] - [:& slider-selector - {:class (stl/css :hsva-bar) - :type :opacity - :max-value 1 - :value alpha - :on-change on-change-opacity - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}]])] - - [:div.hsva-selector - [:span.hsva-selector-label "H"] - [:& slider-selector - {:class "hue" - :max-value 360 - :value hue - :on-change (handle-change-slider :h) - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}] - - [:span.hsva-selector-label "S"] - [:& slider-selector - {:class "saturation" - :max-value 1 - :value saturation - :on-change (handle-change-slider :s) - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}] - [:span.hsva-selector-label "V"] - [:& slider-selector - {:class "value" - :reverse? false - :max-value 255 - :value value - :on-change (handle-change-slider :v) - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}] - - (when (not disable-opacity) - [:* - [:span.hsva-selector-label "A"] - [:& slider-selector - {:class "opacity" - :max-value 1 - :value alpha - :on-change on-change-opacity - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}]])]) - )) + :on-finish-drag on-finish-drag}]])])) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/hsva.scss b/frontend/src/app/main/ui/workspace/colorpicker/hsva.scss index 03e2d66b68..be13a492b5 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/hsva.scss +++ b/frontend/src/app/main/ui/workspace/colorpicker/hsva.scss @@ -11,18 +11,21 @@ padding: $s-4; grid-row-gap: $s-8; margin-bottom: $s-8; - .hsva-row { - display: flex; - align-items: center; - .hsva-selector-label { - @include tabTitleTipography; - display: flex; - align-items: center; - justify-content: flex-start; - width: $s-32; - } - .hsva-bar { - width: $s-228; - } - } +} + +.hsva-row { + display: flex; + align-items: center; +} + +.hsva-selector-label { + @include tabTitleTipography; + display: flex; + align-items: center; + justify-content: flex-start; + width: $s-32; +} + +.hsva-bar { + width: $s-228; } diff --git a/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs b/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs index d184ba8b4e..5970835a0b 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs @@ -14,10 +14,8 @@ [app.main.data.workspace.colors :as mdc] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.components.color-bullet :refer [color-bullet]] [app.main.ui.components.color-bullet-new :as cb] [app.main.ui.components.select :refer [select]] - [app.main.ui.context :as ctx] [app.main.ui.hooks :as h] [app.main.ui.hooks.resize :as r] [app.main.ui.icons :as i] @@ -28,8 +26,7 @@ (mf/defc libraries [{:keys [state on-select-color on-add-library-color disable-gradient disable-opacity disable-image]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - selected (h/use-shared-state mdc/colorpicker-selected-broadcast-key :recent) + (let [selected (h/use-shared-state mdc/colorpicker-selected-broadcast-key :recent) current-colors (mf/use-state []) shared-libs (mf/deref refs/workspace-libraries) @@ -40,14 +37,11 @@ on-library-change (mf/use-fn (fn [event] - (let [val (if new-css-system - event - (dom/get-target-val event))] - (reset! selected - (if (or (= val "recent") - (= val "file")) - (keyword val) - (parse-uuid val)))))) + (reset! selected + (if (or (= event "recent") + (= event "file")) + (keyword event) + (parse-uuid event))))) check-valid-color? (fn [color] @@ -103,59 +97,26 @@ (let [colors (vals file-colors)] (reset! current-colors (into [] (filter check-valid-color?) colors))))) - (if new-css-system - [:div {:class (stl/css :libraries)} - [:div {:class (stl/css :select-wrapper)} - [:& select - {:class (stl/css :shadow-type-select) - :default-value (or (name @selected) "recent") - :options options - :on-change on-library-change}]] + [:div {:class (stl/css :libraries)} + [:div {:class (stl/css :select-wrapper)} + [:& select + {:class (stl/css :shadow-type-select) + :default-value (or (name @selected) "recent") + :options options + :on-change on-library-change}]] - [:div {:class (stl/css :selected-colors)} - (when (= @selected :file) - [:button {:class (stl/css :add-color-btn) - :on-click on-add-library-color} - i/add-refactor]) + [:div {:class (stl/css :selected-colors)} + (when (= @selected :file) + [:button {:class (stl/css :add-color-btn) + :on-click on-add-library-color} + i/add-refactor]) - [:button {:class (stl/css :palette-btn) - :on-click toggle-palette} - i/swatches-refactor] + [:button {:class (stl/css :palette-btn) + :on-click toggle-palette} + i/swatches-refactor] - (for [[idx color] (map-indexed vector @current-colors)] - [:& cb/color-bullet - {:key (dm/str "color-" idx) - :color color - :on-click on-color-click}])]] - - - [:div.libraries - [:select {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :on-change on-library-change - :value (name @selected)} - [:option {:value "recent"} (tr "workspace.libraries.colors.recent-colors")] - [:option {:value "file"} (tr "workspace.libraries.colors.file-library")] - - (for [[_ {:keys [name id]}] shared-libs] - [:option {:key id :value id} name])] - - [:div.selected-colors - (when (= @selected :file) - [:div.color-bullet.button.plus-button {:style {:background-color "var(--color-white)"} - :on-click on-add-library-color} - i/plus]) - - [:div.color-bullet.button {:style {:background-color "var(--color-white)"} - :on-click (fn [] - (r/set-resize-type! :bottom) - (dom/add-class! (dom/get-element-by-class "color-palette") "fade-out-down") - (ts/schedule 300 #(st/emit! (dw/remove-layout-flag :textpalette) - (-> (dw/toggle-layout-flag :colorpalette) - (vary-meta assoc ::ev/origin "workspace-colorpicker")))))} - i/palette] - - (for [[idx color] (map-indexed vector @current-colors)] - [:& color-bullet - {:key (dm/str "color-" idx) - :color color - :on-click on-color-click}])]]))) + (for [[idx color] (map-indexed vector @current-colors)] + [:& cb/color-bullet + {:key (dm/str "color-" idx) + :color color + :on-click on-color-click}])]])) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/libraries.scss b/frontend/src/app/main/ui/workspace/colorpicker/libraries.scss index 483aa2bae8..63a0f83987 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/libraries.scss +++ b/frontend/src/app/main/ui/workspace/colorpicker/libraries.scss @@ -9,32 +9,34 @@ .libraries { margin-top: $s-8; width: 100%; +} - .selected-colors { - display: grid; - grid-template-columns: repeat(8, 1fr); - gap: $s-4; - justify-content: space-between; - overflow: auto; - margin-top: $s-8; - .add-color-btn, - .palette-btn { - @extend .button-secondary; - height: $s-24; - width: $s-24; - border-radius: $br-circle; - padding: 0; - svg { - @extend .button-icon; - } - } - } - .selected-colors::after { - content: ""; - flex: auto; - } +.selected-colors { + display: grid; + grid-template-columns: repeat(8, 1fr); + gap: $s-4; + justify-content: space-between; + overflow: auto; + margin-top: $s-8; +} - .select-wrapper { - overflow: initial; +.add-color-btn, +.palette-btn { + @extend .button-secondary; + height: $s-24; + width: $s-24; + border-radius: $br-circle; + padding: 0; + svg { + @extend .button-icon; } } + +.selected-colors::after { + content: ""; + flex: auto; +} + +.select-wrapper { + overflow: initial; +} diff --git a/frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs b/frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs index 4063c40158..8b5ee82a30 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs @@ -9,16 +9,13 @@ (:require [app.common.colors :as cc] [app.common.math :as mth] - [app.main.ui.components.color-bullet :refer [color-bullet]] [app.main.ui.components.color-bullet-new :as cb] - [app.main.ui.context :as ctx] [app.main.ui.workspace.colorpicker.slider-selector :refer [slider-selector]] [app.util.dom :as dom] [rumext.v2 :as mf])) (mf/defc value-saturation-selector [{:keys [saturation value on-change on-start-drag on-finish-drag]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - dragging? (mf/use-state false) + (let [dragging? (mf/use-state false) calculate-pos (fn [ev] (let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect) @@ -42,32 +39,20 @@ (dom/release-pointer event) (reset! dragging? false) (on-finish-drag)))] - (if new-css-system - [:div {:class (stl/css :value-saturation-selector) - :on-pointer-down handle-start-drag - :on-pointer-up handle-stop-drag - :on-lost-pointer-capture handle-stop-drag - :on-click calculate-pos - :on-pointer-move #(when @dragging? (calculate-pos %))} - [:div {:class (stl/css :handler) - :style {:pointer-events "none" - :left (str (* 100 saturation) "%") - :top (str (* 100 (- 1 (/ value 255))) "%")}}]] - - [:div.value-saturation-selector - {:on-pointer-down handle-start-drag - :on-pointer-up handle-stop-drag - :on-lost-pointer-capture handle-stop-drag - :on-click calculate-pos - :on-pointer-move #(when @dragging? (calculate-pos %))} - [:div.handler {:style {:pointer-events "none" - :left (str (* 100 saturation) "%") - :top (str (* 100 (- 1 (/ value 255))) "%")}}]]))) + [:div {:class (stl/css :value-saturation-selector) + :on-pointer-down handle-start-drag + :on-pointer-up handle-stop-drag + :on-lost-pointer-capture handle-stop-drag + :on-click calculate-pos + :on-pointer-move #(when @dragging? (calculate-pos %))} + [:div {:class (stl/css :handler) + :style {:pointer-events "none" + :left (str (* 100 saturation) "%") + :top (str (* 100 (- 1 (/ value 255))) "%")}}]])) (mf/defc ramp-selector [{:keys [color disable-opacity on-change on-start-drag on-finish-drag]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - {hex :hex + (let [{hex :hex hue :h saturation :s value :v alpha :alpha} color on-change-value-saturation @@ -90,60 +75,32 @@ on-change-opacity (fn [new-opacity] (on-change {:alpha new-opacity}))] - (if new-css-system - [:* - [:& value-saturation-selector - {:hue hue - :saturation saturation - :value value - :on-change on-change-value-saturation - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}] + [:* + [:& value-saturation-selector + {:hue hue + :saturation saturation + :value value + :on-change on-change-value-saturation + :on-start-drag on-start-drag + :on-finish-drag on-finish-drag}] - [:div {:class (stl/css new-css-system :shade-selector) - :style #js {"--bullet-size" "52px"}} - [:& cb/color-bullet {:color {:color hex - :opacity alpha} - :area true}] - [:div {:class (stl/css :sliders-wrapper)} - [:& slider-selector {:type :hue - :max-value 360 - :value hue - :on-change on-change-hue + [:div {:class (stl/css :shade-selector) + :style #js {"--bullet-size" "52px"}} + [:& cb/color-bullet {:color {:color hex + :opacity alpha} + :area true}] + [:div {:class (stl/css :sliders-wrapper)} + [:& slider-selector {:type :hue + :max-value 360 + :value hue + :on-change on-change-hue + :on-start-drag on-start-drag + :on-finish-drag on-finish-drag}] + + (when (not disable-opacity) + [:& slider-selector {:type :opacity + :max-value 1 + :value alpha + :on-change on-change-opacity :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}] - - (when (not disable-opacity) - [:& slider-selector {:type :opacity - :max-value 1 - :value alpha - :on-change on-change-opacity - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}])]]] - - [:* - [:& value-saturation-selector - {:hue hue - :saturation saturation - :value value - :on-change on-change-value-saturation - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}] - - [:div.shade-selector - [:& color-bullet {:color {:color hex - :opacity alpha}}] - [:& slider-selector {:class "hue" - :max-value 360 - :value hue - :on-change on-change-hue - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}] - - (when (not disable-opacity) - [:& slider-selector {:class "opacity" - :max-value 1 - :value alpha - :on-change on-change-opacity - :on-start-drag on-start-drag - :on-finish-drag on-finish-drag}])]]))) + :on-finish-drag on-finish-drag}])]]])) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/ramp.scss b/frontend/src/app/main/ui/workspace/colorpicker/ramp.scss index 450dcd9e57..512739d8fd 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/ramp.scss +++ b/frontend/src/app/main/ui/workspace/colorpicker/ramp.scss @@ -15,13 +15,6 @@ margin-bottom: $s-12; cursor: pointer; - .handler { - @extend .colorpicker-handler; - height: $s-16; - width: $s-16; - border: $s-2 solid var(--colorpicker-handlers-color); - } - &::before { content: ""; position: absolute; @@ -38,12 +31,21 @@ background: linear-gradient(to top, #000, rgba(0, 0, 0, 0)); } } + +.handler { + @extend .colorpicker-handler; + height: $s-16; + width: $s-16; + border: $s-2 solid var(--colorpicker-handlers-color); +} + .shade-selector { display: flex; gap: $s-4; height: $s-52; cursor: pointer; } + .sliders-wrapper { @include flexColumn; } diff --git a/frontend/src/app/main/ui/workspace/colorpicker/slider_selector.cljs b/frontend/src/app/main/ui/workspace/colorpicker/slider_selector.cljs index ddf475744d..1fa07db8a4 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/slider_selector.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/slider_selector.cljs @@ -9,15 +9,13 @@ (:require [app.common.data.macros :as dm] [app.common.math :as mth] - [app.main.ui.context :as ctx] [app.util.dom :as dom] [app.util.object :as obj] [rumext.v2 :as mf])) (mf/defc slider-selector [{:keys [value class min-value max-value vertical? reverse? on-change on-start-drag on-finish-drag type]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - min-value (or min-value 0) + (let [min-value (or min-value 0) max-value (or max-value 1) dragging? (mf/use-state false) @@ -53,49 +51,27 @@ value (+ min-value (* unit-value (- max-value min-value)))] (on-change value))))] - (if new-css-system - [:div {:class (stl/css-case :opacity-wrapper (= type :opacity))} - [:div {:class (dm/str class (stl/css-case :vertical vertical? - :slider-selector true - :hue (= type :hue) - :opacity (= type :opacity) - :value (= type :value))) - :on-pointer-down handle-start-drag - :on-pointer-up handle-stop-drag - :on-lost-pointer-capture handle-stop-drag - :on-click calculate-pos - :on-pointer-move #(when @dragging? (calculate-pos %))} - (let [value-percent (* (/ (- value min-value) - (- max-value min-value)) 100) + [:div {:class (stl/css-case :opacity-wrapper (= type :opacity))} + [:div {:class (dm/str class (stl/css-case :vertical vertical? + :slider-selector true + :hue (= type :hue) + :opacity (= type :opacity) + :value (= type :value))) + :on-pointer-down handle-start-drag + :on-pointer-up handle-stop-drag + :on-lost-pointer-capture handle-stop-drag + :on-click calculate-pos + :on-pointer-move #(when @dragging? (calculate-pos %))} + (let [value-percent (* (/ (- value min-value) + (- max-value min-value)) 100) - value-percent (if reverse? - (mth/abs (- value-percent 100)) - value-percent) - value-percent-str (str value-percent "%") + value-percent (if reverse? + (mth/abs (- value-percent 100)) + value-percent) + value-percent-str (str value-percent "%") - style-common #js {:pointerEvents "none"} - style-horizontal (obj/merge! #js {:left value-percent-str} style-common) - style-vertical (obj/merge! #js {:bottom value-percent-str} style-common)] - [:div {:class (stl/css :handler) - :style (if vertical? style-vertical style-horizontal)}])]] - - [:div.slider-selector - {:class (str (if vertical? "vertical " "") class) - :on-pointer-down handle-start-drag - :on-pointer-up handle-stop-drag - :on-lost-pointer-capture handle-stop-drag - :on-click calculate-pos - :on-pointer-move #(when @dragging? (calculate-pos %))} - - (let [value-percent (* (/ (- value min-value) - (- max-value min-value)) 100) - - value-percent (if reverse? - (mth/abs (- value-percent 100)) - value-percent) - value-percent-str (str value-percent "%") - - style-common #js {:pointerEvents "none"} - style-horizontal (obj/merge! #js {:left value-percent-str} style-common) - style-vertical (obj/merge! #js {:bottom value-percent-str} style-common)] - [:div.handler {:style (if vertical? style-vertical style-horizontal)}])]))) + style-common #js {:pointerEvents "none"} + style-horizontal (obj/merge! #js {:left value-percent-str} style-common) + style-vertical (obj/merge! #js {:bottom value-percent-str} style-common)] + [:div {:class (stl/css :handler) + :style (if vertical? style-vertical style-horizontal)}])]])) diff --git a/frontend/src/app/main/ui/workspace/comments.cljs b/frontend/src/app/main/ui/workspace/comments.cljs index 121ca11d98..35df73bdd8 100644 --- a/frontend/src/app/main/ui/workspace/comments.cljs +++ b/frontend/src/app/main/ui/workspace/comments.cljs @@ -28,8 +28,7 @@ (mf/defc sidebar-options [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - {cmode :mode cshow :show} (mf/deref refs/comments-local) + (let [{cmode :mode cshow :show} (mf/deref refs/comments-local) update-mode (mf/use-fn (fn [event] @@ -45,53 +44,30 @@ (let [mode (if (= :pending cshow) :all :pending)] (st/emit! (dcm/update-filters {:show mode})))))] - (if new-css-system - [:ul {:class (stl/css :comment-mode-dropdown)} - [:li {:class (stl/css-case :dropdown-item true - :selected (or (= :all cmode) (nil? cmode))) - :data-value "all" - :on-click update-mode} + [:ul {:class (stl/css :comment-mode-dropdown)} + [:li {:class (stl/css-case :dropdown-item true + :selected (or (= :all cmode) (nil? cmode))) + :data-value "all" + :on-click update-mode} - [:span {:class (stl/css :label)} (tr "labels.show-all-comments")] - [:span {:class (stl/css :icon)} i/tick-refactor]] - [:li {:class (stl/css-case :dropdown-item true - :selected (= :yours cmode)) - :data-value "yours" - :on-click update-mode} - [:span {:class (stl/css :label)} (tr "labels.show-your-comments")] - [:span {:class (stl/css :icon)} i/tick-refactor]] - [:li {:class (stl/css :separator)}] - [:li {:class (stl/css-case :dropdown-item true - :selected (= :pending cshow)) - :on-click update-show} - [:span {:class (stl/css :label)} (tr "labels.hide-resolved-comments")] - [:span {:class (stl/css :icon)} i/tick-refactor]]] - - - [:ul.dropdown.with-check - [:li {:class (dom/classnames :selected (or (= :all cmode) (nil? cmode))) - :data-value "all" - :on-click update-mode} - [:span.icon i/tick] - [:span.label (tr "labels.show-all-comments")]] - - [:li {:class (dom/classnames :selected (= :yours cmode)) - :data-value "yours" - :on-click update-mode} - [:span.icon i/tick] - [:span.label (tr "labels.show-your-comments")]] - - [:hr] - - [:li {:class (dom/classnames :selected (= :pending cshow)) - :on-click update-show} - [:span.icon i/tick] - [:span.label (tr "labels.hide-resolved-comments")]]]))) + [:span {:class (stl/css :label)} (tr "labels.show-all-comments")] + [:span {:class (stl/css :icon)} i/tick-refactor]] + [:li {:class (stl/css-case :dropdown-item true + :selected (= :yours cmode)) + :data-value "yours" + :on-click update-mode} + [:span {:class (stl/css :label)} (tr "labels.show-your-comments")] + [:span {:class (stl/css :icon)} i/tick-refactor]] + [:li {:class (stl/css :separator)}] + [:li {:class (stl/css-case :dropdown-item true + :selected (= :pending cshow)) + :on-click update-show} + [:span {:class (stl/css :label)} (tr "labels.hide-resolved-comments")] + [:span {:class (stl/css :icon)} i/tick-refactor]]])) (mf/defc comments-sidebar [{:keys [users threads page-id from-viewer]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - threads-map (mf/deref refs/threads-ref) + (let [threads-map (mf/deref refs/threads-ref) profile (mf/deref refs/profile) users-refs (mf/deref refs/current-file-comments-users) users (or users users-refs) @@ -139,75 +115,41 @@ (dwcm/center-to-comment-thread thread) (-> (dcm/open-thread thread) (with-meta {::ev/origin "workspace"})))))))] - (if new-css-system - [:div {:class (stl/css :comments-section)} - [:div {:class (stl/css :comments-section-title)} - [:span (tr "labels.comments")] - [:button {:class (stl/css :close-button) - :on-click close-section} - i/close-refactor]] + [:div {:class (stl/css :comments-section)} + [:div {:class (stl/css :comments-section-title)} + [:span (tr "labels.comments")] + [:button {:class (stl/css :close-button) + :on-click close-section} + i/close-refactor]] - [:button {:class (stl/css :mode-dropdown-wrapper) - :on-click toggle-mode-selector} + [:button {:class (stl/css :mode-dropdown-wrapper) + :on-click toggle-mode-selector} - [:span {:class (stl/css :mode-label)} (case (:mode local) - (nil :all) (tr "labels.show-all-comments") - :yours (tr "labels.show-your-comments"))] - [:div {:class (stl/css :icon)} i/arrow-refactor]] + [:span {:class (stl/css :mode-label)} (case (:mode local) + (nil :all) (tr "labels.show-all-comments") + :yours (tr "labels.show-your-comments"))] + [:div {:class (stl/css :icon)} i/arrow-refactor]] - [:& dropdown {:show options? - :on-close #(reset! state* false)} - [:& sidebar-options {:local local}]] + [:& dropdown {:show options? + :on-close #(reset! state* false)} + [:& sidebar-options {:local local}]] - [:div {:class (stl/css :comments-section-content)} + [:div {:class (stl/css :comments-section-content)} - (if (seq tgroups) - [:div {:class (stl/css :thread-groups)} + (if (seq tgroups) + [:div {:class (stl/css :thread-groups)} + [:& cmt/comment-thread-group + {:group (first tgroups) + :on-thread-click on-thread-click + :users users}] + (for [tgroup (rest tgroups)] [:& cmt/comment-thread-group - {:group (first tgroups) + {:group tgroup :on-thread-click on-thread-click - :users users}] - (for [tgroup (rest tgroups)] - [:& cmt/comment-thread-group - {:group tgroup - :on-thread-click on-thread-click - :users users - :key (:page-id tgroup)}])] + :users users + :key (:page-id tgroup)}])] - [:div {:class (stl/css :thread-group-placeholder)} - [:span {:class (stl/css :placeholder-icon)} i/comments-refactor] - [:span {:class (stl/css :placeholder-label)} - (tr "labels.no-comments-available")]])]] - - - [:div.comments-section.comment-threads-section - [:div.workspace-comment-threads-sidebar-header - [:div.label (tr "labels.comments")] - [:div.options {:on-click toggle-mode-selector} - [:div.label (case (:mode local) - (nil :all) (tr "labels.all") - :yours (tr "labels.only-yours"))] - [:div.icon i/arrow-down]] - - [:& dropdown {:show options? - :on-close #(reset! state* false)} - [:& sidebar-options {:local local}]]] - - (if (seq tgroups) - [:div.thread-groups - [:& cmt/comment-thread-group - {:group (first tgroups) - :on-thread-click on-thread-click - :users users}] - (for [tgroup (rest tgroups)] - [:* - [:hr] - [:& cmt/comment-thread-group - {:group tgroup - :on-thread-click on-thread-click - :users users - :key (:page-id tgroup)}]])] - - [:div.thread-groups-placeholder - i/chat - (tr "labels.no-comments-available")])]))) + [:div {:class (stl/css :thread-group-placeholder)} + [:span {:class (stl/css :placeholder-icon)} i/comments-refactor] + [:span {:class (stl/css :placeholder-label)} + (tr "labels.no-comments-available")]])]])) diff --git a/frontend/src/app/main/ui/workspace/comments.scss b/frontend/src/app/main/ui/workspace/comments.scss index 7c45491e67..b742ea5890 100644 --- a/frontend/src/app/main/ui/workspace/comments.scss +++ b/frontend/src/app/main/ui/workspace/comments.scss @@ -133,6 +133,7 @@ @include titleTipography; text-align: center; width: $s-184; + color: var(--empty-message-foreground-color); } } } diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index b028ad0014..af84fffa69 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -6,7 +6,7 @@ (ns app.main.ui.workspace.context-menu "A workspace specific context menu (mouse right click)." - (:require-macros [app.main.style :refer [css]]) + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] @@ -14,6 +14,7 @@ [app.common.types.component :as ctk] [app.common.types.container :as ctn] [app.common.types.page :as ctp] + [app.common.types.shape.layout :as ctl] [app.common.uuid :as uuid] [app.main.data.events :as ev] [app.main.data.modal :as modal] @@ -30,7 +31,6 @@ [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.components.shape-icon-refactor :as sic] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.ui.workspace.sidebar.assets.common :as cmm] [app.util.dom :as dom] @@ -48,10 +48,9 @@ (dom/stop-propagation event)) (mf/defc menu-entry - [{:keys [title shortcut on-click on-pointer-enter on-pointer-leave on-unmount children selected? icon] :as props}] + [{:keys [title shortcut on-click on-pointer-enter on-pointer-leave on-unmount children selected? icon disabled] :as props}] (let [submenu-ref (mf/use-ref nil) hovering? (mf/use-ref false) - new-css-system (mf/use-ctx ctx/new-css-system) on-pointer-enter (mf/use-callback (fn [] @@ -85,69 +84,44 @@ (constantly on-unmount)) (if icon - [:li {:class (if new-css-system - (dom/classnames (css :icon-menu-item) true) - (dom/classnames :icon-menu-item true)) + [:li {:class (stl/css :icon-menu-item) + :disabled disabled :ref set-dom-node :on-click on-click :on-pointer-enter on-pointer-enter :on-pointer-leave on-pointer-leave} [:span - {:class (if new-css-system - (dom/classnames (css :icon-wrapper) true) - (dom/classnames :icon-wrapper true))} - (if selected? [:span {:class (if new-css-system - (dom/classnames (css :selected-icon) true) - (dom/classnames :selected-icon true))} - (if new-css-system - i/tick-refactor - i/tick)] - [:span {:class (if new-css-system - (dom/classnames (css :selected-icon) true) - (dom/classnames :selected-icon true))}]) - [:span {:class (if new-css-system - (dom/classnames (css :shape-icon) true) - (dom/classnames :shape-icon true))} icon]] - [:span {:class (if new-css-system - (dom/classnames (css :title) true) - (dom/classnames :title true))} title]] - [:li {:class (dom/classnames (css :context-menu-item) new-css-system) + {:class (stl/css :icon-wrapper)} + (if selected? [:span {:class (stl/css :selected-icon)} + i/tick-refactor] + [:span {:class (stl/css :selected-icon)}]) + [:span {:class (stl/css :shape-icon)} icon]] + [:span {:class (stl/css :title)} title]] + [:li {:class (stl/css :context-menu-item) + :disabled disabled :ref set-dom-node :on-click on-click :on-pointer-enter on-pointer-enter :on-pointer-leave on-pointer-leave} - [:span {:class (if new-css-system - (dom/classnames (css :title) true) - (dom/classnames :title true))} title] + [:span {:class (stl/css :title)} title] (when shortcut - [:span {:class (if new-css-system - (dom/classnames (css :shortcut) true) - (dom/classnames :shortcut true))} - (if new-css-system - (for [sc (scd/split-sc shortcut)] - [:span {:class (dom/classnames (css :shortcut-key) true)} sc]) - (or shortcut ""))]) + [:span {:class (stl/css :shortcut)} + (for [sc (scd/split-sc shortcut)] + [:span {:class (stl/css :shortcut-key)} sc])]) (when (> (count children) 1) - (if new-css-system - [:span {:class (dom/classnames (css :submenu-icon) true)} i/arrow-refactor] - [:span.submenu-icon i/arrow-slide])) + [:span {:class (stl/css :submenu-icon)} i/arrow-refactor]) (when (> (count children) 1) - [:ul - {:class (if new-css-system - (dom/classnames (css :workspace-context-submenu) true) - (dom/classnames :workspace-context-menu true)) - :ref submenu-ref - :style {:display "none" :left 250} - :on-context-menu prevent-default} + [:ul {:class (stl/css :workspace-context-submenu) + :ref submenu-ref + :style {:display "none" :left 250} + :on-context-menu prevent-default} children])]))) + (mf/defc menu-separator [] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - [:li {:class (if new-css-system - (dom/classnames (css :separator) true) - (dom/classnames :separator true))}])) + [:li {:class (stl/css :separator)}]) (mf/defc context-menu-edit [_] @@ -479,8 +453,8 @@ [:* [:& menu-separator] [:& menu-entry {:title (tr "workspace.shape.menu.delete") - :shortcut (sc/get-tooltip :delete) - :on-click do-delete}]])) + :shortcut (sc/get-tooltip :delete) + :on-click do-delete}]])) (mf/defc shape-context-menu {::mf/wrap [mf/memo]} @@ -549,13 +523,97 @@ :shortcut (sc/get-tooltip :toggle-focus-mode) :on-click do-toggle-focus-mode}])])) +(mf/defc grid-track-context-menu + [{:keys [mdata] :as props}] + (let [{:keys [type index grid-id]} mdata + do-delete-track + (mf/use-callback + (mf/deps grid-id type index) + (fn [] + (st/emit! (dwsl/remove-layout-track [grid-id] type index)))) + + do-add-track-before + (mf/use-callback + (mf/deps grid-id type index) + (fn [] + (st/emit! (dwsl/add-layout-track [grid-id] type ctl/default-track-value index)))) + + do-add-track-after + (mf/use-callback + (mf/deps grid-id type index) + (fn [] + (st/emit! (dwsl/add-layout-track [grid-id] type ctl/default-track-value (inc index))))) + + do-duplicate-track + (mf/use-callback + (mf/deps grid-id type index) + (fn [] + (st/emit! (dwsl/duplicate-layout-track [grid-id] type index)))) + + do-delete-track-shapes + (mf/use-callback + (mf/deps grid-id type index) + (fn [] + (st/emit! (dwsl/remove-layout-track [grid-id] type index {:with-shapes? true}))))] + + (if (= type :column) + [:* + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.column.duplicate") :on-click do-duplicate-track}] + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.column.add-before") :on-click do-add-track-before}] + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.column.add-after") :on-click do-add-track-after}] + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.column.delete") :on-click do-delete-track}] + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.column.delete-shapes") :on-click do-delete-track-shapes}]] + + [:* + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.row.duplicate") :on-click do-duplicate-track}] + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.row.add-before") :on-click do-add-track-before}] + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.row.add-after") :on-click do-add-track-after}] + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.row.delete") :on-click do-delete-track}] + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.row.delete-shapes") :on-click do-delete-track-shapes}]]))) + +(mf/defc grid-cells-context-menu + [{:keys [mdata] :as props}] + (let [{:keys [grid cells]} mdata + + single? (= (count cells) 1) + + can-merge? + (mf/use-memo + (mf/deps cells) + #(ctl/valid-area-cells? cells)) + + do-merge-cells + (mf/use-callback + (mf/deps grid cells) + (fn [] + (st/emit! (dwsl/merge-cells (:id grid) (map :id cells))))) + + do-create-board + (mf/use-callback + (mf/deps grid cells) + (fn [] + (st/emit! (dwsl/create-cell-board (:id grid) (map :id cells)))))] + [:* + (when (not single?) + [:& menu-entry {:title (tr "workspace.context-menu.grid-cells.merge") + :on-click do-merge-cells + :disabled (not can-merge?)}]) + + (when single? + [:& menu-entry {:title (tr "workspace.context-menu.grid-cells.area") + :on-click do-merge-cells}]) + + [:& menu-entry {:title (tr "workspace.context-menu.grid-cells.create-board") + :on-click do-create-board + :disabled (and (not single?) (not can-merge?))}]])) + + (mf/defc context-menu [] (let [mdata (mf/deref menu-ref) top (- (get-in mdata [:position :y]) 20) left (get-in mdata [:position :x]) - dropdown-ref (mf/use-ref) - new-css-system (mf/use-ctx ctx/new-css-system)] + dropdown-ref (mf/use-ref)] (mf/use-effect (mf/deps mdata) @@ -573,9 +631,7 @@ [:& dropdown {:show (boolean mdata) :on-close #(st/emit! dw/hide-context-menu)} [:ul - {:class (if new-css-system - (dom/classnames (css :workspace-context-menu) true) - (dom/classnames :workspace-context-menu true)) + {:class (stl/css :workspace-context-menu) :ref dropdown-ref :style {:top top :left left} :on-context-menu prevent-default} @@ -583,6 +639,8 @@ (case (:kind mdata) :shape [:& shape-context-menu {:mdata mdata}] :page [:& page-item-context-menu {:mdata mdata}] + :grid-track [:& grid-track-context-menu {:mdata mdata}] + :grid-cells [:& grid-cells-context-menu {:mdata mdata}] [:& viewport-context-menu {:mdata mdata}])]])) diff --git a/frontend/src/app/main/ui/workspace/context_menu.scss b/frontend/src/app/main/ui/workspace/context_menu.scss index 94f73018ef..4a178c88f8 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.scss +++ b/frontend/src/app/main/ui/workspace/context_menu.scss @@ -19,94 +19,102 @@ border-radius: $br-8; background-color: var(--menu-background-color); z-index: $z-index-3; - .separator { - height: $s-12; - } - .context-menu-item { - display: flex; - align-items: center; - justify-content: space-between; - height: $s-28; - width: 100%; - padding: $s-6; - border-radius: $br-8; - cursor: pointer; +} - .title { +.separator { + height: $s-12; +} + +.context-menu-item { + display: flex; + align-items: center; + justify-content: space-between; + height: $s-28; + width: 100%; + padding: $s-6; + border-radius: $br-8; + cursor: pointer; + + .title { + @include titleTipography; + color: var(--menu-foreground-color); + } + .shortcut { + @include flexCenter; + gap: $s-2; + color: var(--menu-shortcut-foreground-color); + .shortcut-key { @include titleTipography; - color: var(--menu-foreground-color); + @include flexCenter; + height: $s-20; + padding: $s-2 $s-6; + border-radius: $br-6; + background-color: var(--menu-shortcut-background-color); + } + } + + .submenu-icon { + position: absolute; + right: $s-16; + svg { + @extend .button-icon-small; + stroke: var(--menu-foreground-color); + } + } + &:hover { + background-color: var(--menu-background-color-hover); + .title { + color: var(--menu-foreground-color-hover); } .shortcut { - @include flexCenter; - gap: $s-2; - color: var(--menu-shortcut-foreground-color); - .shortcut-key { - @include titleTipography; - @include flexCenter; - height: $s-20; - padding: $s-2 $s-6; - border-radius: $br-6; - background-color: var(--menu-shortcut-background-color); - } - } - - .submenu-icon { - position: absolute; - right: $s-16; - svg { - @extend .button-icon-small; - stroke: var(--menu-foreground-color); - } - } - &:hover { - background-color: var(--menu-background-color-hover); - .title { - color: var(--menu-foreground-color-hover); - } - .shortcut { - color: var(--menu-shortcut-foreground-color-hover); - } - } - &:focus { - border: 1px solid var(--menu-border-color-focus); - background-color: var(--menu-background-color-focus); + color: var(--menu-shortcut-foreground-color-hover); } } - - .icon-menu-item { - display: flex; - justify-content: flex-start; - align-items: center; - height: $s-28; - padding: $s-6; - border-radius: $br-8; - &:hover { - background-color: var(--menu-background-color-hover); - } - - span.title { - margin-left: $s-6; - } - - .selected-icon { - svg { - @extend .button-icon-small; - stroke: var(--menu-foreground-color); - } - } - - .shape-icon { - margin-left: $s-2; - svg { - @extend .button-icon-small; - stroke: var(--menu-foreground-color); - } - } - - .icon-wrapper { - display: grid; - grid-template-columns: 1fr 1fr; - margin: 0; - } + &:focus { + border: 1px solid var(--menu-border-color-focus); + background-color: var(--menu-background-color-focus); } } + +.icon-menu-item { + display: flex; + justify-content: flex-start; + align-items: center; + height: $s-28; + padding: $s-6; + border-radius: $br-8; + &:hover { + background-color: var(--menu-background-color-hover); + } + + span.title { + margin-left: $s-6; + } + + .selected-icon { + svg { + @extend .button-icon-small; + stroke: var(--menu-foreground-color); + } + } + + .shape-icon { + margin-left: $s-2; + svg { + @extend .button-icon-small; + stroke: var(--menu-foreground-color); + } + } + + .icon-wrapper { + display: grid; + grid-template-columns: 1fr 1fr; + margin: 0; + } +} + +.icon-menu-item[disabled], +.context-menu-item[disabled] { + pointer-events: none; + opacity: 0.6; +} diff --git a/frontend/src/app/main/ui/workspace/left_header.scss b/frontend/src/app/main/ui/workspace/left_header.scss index 7b34ccb0aa..d527a04e9c 100644 --- a/frontend/src/app/main/ui/workspace/left_header.scss +++ b/frontend/src/app/main/ui/workspace/left_header.scss @@ -19,7 +19,8 @@ height: $s-32; margin-right: $s-4; svg { - height: $s-32; + min-height: $s-32; + width: $s-32; fill: var(--icon-foreground-hover); } } diff --git a/frontend/src/app/main/ui/workspace/libraries.scss b/frontend/src/app/main/ui/workspace/libraries.scss index 6b7825bf6c..8ebf89b741 100644 --- a/frontend/src/app/main/ui/workspace/libraries.scss +++ b/frontend/src/app/main/ui/workspace/libraries.scss @@ -39,9 +39,11 @@ stroke: var(--icon-foreground); } } + .modal-title { @include tabTitleTipography; margin-bottom: $s-16; + color: var(--modal-title-foreground-color); } .modal-content { @@ -161,6 +163,7 @@ .section-list-empty { @include titleTipography; @include flexCenter; + color: var(--empty-message-foreground-color); svg { @extend .button-icon-small; diff --git a/frontend/src/app/main/ui/workspace/nudge.cljs b/frontend/src/app/main/ui/workspace/nudge.cljs index 6e59f46188..868e294fdd 100644 --- a/frontend/src/app/main/ui/workspace/nudge.cljs +++ b/frontend/src/app/main/ui/workspace/nudge.cljs @@ -12,7 +12,6 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.numeric-input :refer [numeric-input*]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -32,8 +31,7 @@ {::mf/register modal/components ::mf/register-as :nudge-option} [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - profile (mf/deref refs/profile) + (let [profile (mf/deref refs/profile) nudge (or (get-in profile [:props :nudge]) {:big 10 :small 1}) update-big (mf/use-fn #(st/emit! (dw/update-nudge {:big %}))) update-small (mf/use-fn #(st/emit! (dw/update-nudge {:small %}))) @@ -43,45 +41,24 @@ (->> (events/listen js/document EventType.KEYDOWN on-keydown) (partial events/unlistenByKey))) - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-container)} - [:div {:class (stl/css :modal-header)} - [:h2 {:class (stl/css :modal-title)} (tr "modals.nudge-title")] - [:button {:class (stl/css :modal-close-btn) - :on-click on-close} i/close-refactor]] - [:div {:class (stl/css :modal-content)} - [:div {:class (stl/css :input-wrapper)} - [:label {:class (stl/css :modal-msg) - :for "nudge-small"} (tr "modals.small-nudge")] - [:> numeric-input* {:min 0.01 - :id "nudge-small" - :value (:small nudge) - :on-change update-small}]] - [:div {:class (stl/css :input-wrapper)} - [:label {:class (stl/css :modal-msg) - :for "nudge-big"} (tr "modals.big-nudge")] - [:> numeric-input* {:min 0.01 - :id "nudge-big" - :value (:big nudge) - :on-change update-big}]]]]] - - - [:div.nudge-modal-overlay - [:div.nudge-modal-container - [:div.nudge-modal-header - [:p.nudge-modal-title (tr "modals.nudge-title")] - [:button.modal-close-button {:on-click on-close} i/close]] - [:div.nudge-modal-body - [:div.input-wrapper - [:span - [:p.nudge-subtitle (tr "modals.small-nudge")] - [:> numeric-input* {:min 0.01 - :value (:small nudge) - :on-change update-small}]]] - [:div.input-wrapper - [:span - [:p.nudge-subtitle (tr "modals.big-nudge")] - [:> numeric-input* {:min 0.01 - :value (:big nudge) - :on-change update-big}]]]]]]))) + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} (tr "modals.nudge-title")] + [:button {:class (stl/css :modal-close-btn) + :on-click on-close} i/close-refactor]] + [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :input-wrapper)} + [:label {:class (stl/css :modal-msg) + :for "nudge-small"} (tr "modals.small-nudge")] + [:> numeric-input* {:min 0.01 + :id "nudge-small" + :value (:small nudge) + :on-change update-small}]] + [:div {:class (stl/css :input-wrapper)} + [:label {:class (stl/css :modal-msg) + :for "nudge-big"} (tr "modals.big-nudge")] + [:> numeric-input* {:min 0.01 + :id "nudge-big" + :value (:big nudge) + :on-change update-big}]]]]])) diff --git a/frontend/src/app/main/ui/workspace/nudge.scss b/frontend/src/app/main/ui/workspace/nudge.scss index ff8f775a28..28d4985791 100644 --- a/frontend/src/app/main/ui/workspace/nudge.scss +++ b/frontend/src/app/main/ui/workspace/nudge.scss @@ -8,33 +8,37 @@ .modal-overlay { @extend .modal-overlay-base; - .modal-container { - @extend .modal-container-base; - min-width: $s-408; - border: $s-1 solid var(--modal-border-color); - .modal-header { - margin-bottom: $s-24; - .modal-title { - @include tabTitleTipography; - color: var(--modal-title-foreground-color); - } - .modal-close-btn { - @extend .modal-close-btn-base; - } - } +} - .modal-content { - @include flexColumn; - gap: $s-24; - @include titleTipography; - margin-bottom: $s-24; - .input-wrapper { - @extend .input-with-label; - label { - text-transform: none; - } - } - } +.modal-container { + @extend .modal-container-base; + min-width: $s-408; + border: $s-1 solid var(--modal-border-color); +} + +.modal-header { + margin-bottom: $s-24; +} + +.modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); +} +.modal-close-btn { + @extend .modal-close-btn-base; +} + +.modal-content { + @include flexColumn; + gap: $s-24; + @include titleTipography; + margin-bottom: $s-24; +} + +.input-wrapper { + @extend .input-with-label; + label { + text-transform: none; } } diff --git a/frontend/src/app/main/ui/workspace/sidebar.cljs b/frontend/src/app/main/ui/workspace/sidebar.cljs index e0433a1762..ddda4cf013 100644 --- a/frontend/src/app/main/ui/workspace/sidebar.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar.cljs @@ -12,10 +12,7 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.tab-container :refer [tab-container tab-element]] - [app.main.ui.components.tabs-container :refer [tabs-container tabs-element]] - [app.main.ui.context :as ctx] [app.main.ui.hooks.resize :refer [use-resize-hook]] - [app.main.ui.icons :as i] [app.main.ui.workspace.comments :refer [comments-sidebar]] [app.main.ui.workspace.left-header :refer [left-header]] [app.main.ui.workspace.right-header :refer [right-header]] @@ -26,7 +23,6 @@ [app.main.ui.workspace.sidebar.options :refer [options-toolbox]] [app.main.ui.workspace.sidebar.shortcuts :refer [shortcuts-container]] [app.main.ui.workspace.sidebar.sitemap :refer [sitemap]] - [app.util.dom :as dom] [app.util.i18n :refer [tr]] [app.util.object :as obj] [rumext.v2 :as mf])) @@ -47,7 +43,6 @@ (contains? layout :assets) :assets) shortcuts? (contains? layout :shortcuts) show-debug? (contains? layout :debug-panel) - new-css-system (mf/use-ctx ctx/new-css-system) {on-pointer-down :on-pointer-down on-lost-pointer-capture :on-lost-pointer-capture on-pointer-move :on-pointer-move parent-ref :parent-ref size :size} (use-resize-hook :left-sidebar 275 275 500 :x false :left) @@ -65,25 +60,17 @@ [:aside {:ref parent-ref :id "left-sidebar-aside" :data-size size - :class (stl/css-case new-css-system - :global/settings-bar (not new-css-system) - :global/settings-bar-left (not new-css-system) - :left-settings-bar true + :class (stl/css-case :left-settings-bar true :global/two-row (<= size 300) :global/three-row (and (> size 300) (<= size 400)) :global/four-row (> size 400)) :style #js {"--width" (dm/str size "px")}} - (when new-css-system - [:& left-header {:file file :layout layout :project project :page-id page-id}]) + [:& left-header {:file file :layout layout :project project :page-id page-id}] [:div {:on-pointer-down on-pointer-down :on-lost-pointer-capture on-lost-pointer-capture :on-pointer-move on-pointer-move - :class (if ^boolean new-css-system - (stl/css :resize-area) - (dom/classnames :resize-area true))}] - [:div {:class (if ^boolean new-css-system - (stl/css :settings-bar-inside) - (dom/classnames :settings-bar-inside true))} + :class (stl/css :resize-area)}] + [:div {:class (stl/css :settings-bar-inside)} (cond (true? shortcuts?) [:& shortcuts-container] @@ -92,64 +79,32 @@ [:& debug-panel] :else - (if ^boolean new-css-system - [:div {:class (stl/css :tabs-wrapper)} - [:& tab-container - {:on-change-tab on-tab-change - :selected section - :shortcuts? shortcuts? - :collapsable? true - :handle-collapse handle-collapse - :class (stl/css :tab-spacing)} - [:& tab-element {:id :layers :title (tr "workspace.sidebar.layers")} - [:div {:class (stl/css :layers-tab) - :style #js {"--height" (str size-pages "px")}} - [:& sitemap {:layout layout - :toggle-pages toggle-pages - :show-pages? @show-pages? - :size size-pages}] - (when @show-pages? - [:div {:class (stl/css :resize-area-horiz) - :on-pointer-down on-pointer-down-pages - :on-lost-pointer-capture on-lost-pointer-capture-pages - :on-pointer-move on-pointer-move-pages}]) - [:& layers-toolbox {:size-parent size - :size size-pages}]]] + [:div {:class (stl/css :tabs-wrapper)} + [:& tab-container + {:on-change-tab on-tab-change + :selected section + :shortcuts? shortcuts? + :collapsable? true + :handle-collapse handle-collapse + :class (stl/css :tab-spacing)} + [:& tab-element {:id :layers :title (tr "workspace.sidebar.layers")} + [:div {:class (stl/css :layers-tab) + :style #js {"--height" (str size-pages "px")}} + [:& sitemap {:layout layout + :toggle-pages toggle-pages + :show-pages? @show-pages? + :size size-pages}] + (when @show-pages? + [:div {:class (stl/css :resize-area-horiz) + :on-pointer-down on-pointer-down-pages + :on-lost-pointer-capture on-lost-pointer-capture-pages + :on-pointer-move on-pointer-move-pages}]) + [:& layers-toolbox {:size-parent size + :size size-pages}]]] - (when-not ^boolean mode-inspect? - [:& tab-element {:id :assets :title (tr "workspace.toolbar.assets")} - [:& assets-toolbox]])]] - - [:* - [:button.collapse-sidebar - {:on-click handle-collapse - :aria-label (tr "workspace.sidebar.collapse")} - i/arrow-slide] - - [:& tabs-container - {:on-change-tab on-tab-change - :selected section - :shortcuts? shortcuts? - :collapsable? true - :handle-collapse handle-collapse} - - [:& tabs-element {:id :layers :title (tr "workspace.sidebar.layers")} - [:div {:class :layers-tab - :style #js {"--height" (str size-pages "px")}} - [:& sitemap {:layout layout - :toggle-pages toggle-pages - :show-pages? @show-pages? - :size size-pages}] - (when @show-pages? - [:div.resize-area-horiz - {:on-pointer-down on-pointer-down-pages - :on-lost-pointer-capture on-lost-pointer-capture-pages - :on-pointer-move on-pointer-move-pages}]) - [:& layers-toolbox {:size-parent size}]]] - - (when-not ^boolean mode-inspect? - [:& tabs-element {:id :assets :title (tr "workspace.toolbar.assets")} - [:& assets-toolbox]])]]))]])) + (when-not ^boolean mode-inspect? + [:& tab-element {:id :assets :title (tr "workspace.toolbar.assets")} + [:& assets-toolbox]])]])]])) ;; --- Right Sidebar (Component) @@ -158,7 +113,6 @@ ::mf/wrap [mf/memo]} [{:keys [layout section file page-id ] :as props}] (let [drawing-tool (:tool (mf/deref refs/workspace-drawing)) - new-css-system (mf/use-ctx ctx/new-css-system) is-comments? (= drawing-tool :comments) is-history? (contains? layout :document-history) @@ -195,10 +149,7 @@ (obj/set! "on-change-section" handle-change-section) (obj/set! "on-expand" handle-expand))] - [:aside {:class (stl/css-case new-css-system - :global/settings-bar (not new-css-system) - :global/settings-bar-right (not new-css-system) - :right-settings-bar true + [:aside {:class (stl/css-case :right-settings-bar true :not-expand (not can-be-expanded?) :expanded (> size 276)) @@ -206,14 +157,13 @@ :data-size size :style #js {"--width" (when can-be-expanded? (dm/str size "px"))}} (when can-be-expanded? - [:div {:class (stl/css new-css-system :resize-area) + [:div {:class (stl/css :resize-area) :on-pointer-down on-pointer-down :on-lost-pointer-capture on-lost-pointer-capture :on-pointer-move on-pointer-move}]) - (when new-css-system - [:& right-header {:file file :layout layout :page-id page-id}]) + [:& right-header {:file file :layout layout :page-id page-id}] - [:div {:class (stl/css new-css-system :settings-bar-inside)} + [:div {:class (stl/css :settings-bar-inside)} (cond (true? is-comments?) [:& comments-sidebar] diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index f82f6bb8d3..cb67bf83df 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -19,7 +19,6 @@ [app.main.ui.workspace.sidebar.assets.file-library :refer [file-library]] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] - [app.util.keyboard :as kbd] [cuerdas.core :as str] [rumext.v2 :as mf])) @@ -71,7 +70,6 @@ [] (let [components-v2 (mf/use-ctx ctx/components-v2) read-only? (mf/use-ctx ctx/workspace-read-only?) - new-css-system (mf/use-ctx ctx/new-css-system) filters* (mf/use-state {:term "" :section "all" @@ -104,17 +102,8 @@ on-search-term-change (mf/use-fn - (mf/deps new-css-system) (fn [event] - ;; NOTE: When old-css-system is removed this function will recibe value and event - ;; Let won't be necessary any more - (let [value (if ^boolean new-css-system - event - (dom/get-target-val event))] - (swap! filters* assoc :term value)))) - - on-search-clear-click - (mf/use-fn #(swap! filters* assoc :term "")) + (swap! filters* assoc :term event))) on-section-filter-change (mf/use-fn @@ -125,23 +114,12 @@ (dom/get-attribute $ "data-test")))] (swap! filters* assoc :section value :open-menu false)))) - handle-key-down - (mf/use-fn - (fn [event] - (let [enter? (kbd/enter? event) - esc? (kbd/esc? event) - node (dom/get-target event)] - - (when ^boolean enter? (dom/blur! node)) - (when ^boolean esc? (dom/blur! node))))) - show-libraries-dialog (mf/use-fn (fn [] (modal/show! :libraries-dialog {}) (modal/allow-click-outside!))) - on-open-menu (mf/use-fn #(swap! filters* update :open-menu not)) @@ -175,86 +153,43 @@ :option-handler on-section-filter-change :data-test "typographies"}]))] - (if ^boolean new-css-system - [:div {:class (stl/css :assets-bar)} - [:div {:class (stl/css :assets-header)} - (when-not ^boolean read-only? - [:button {:class (stl/css :libraries-button) - :on-click show-libraries-dialog} - [:span {:class (stl/css :libraries-icon)} - i/library-refactor] - (tr "workspace.assets.libraries")]) + [:div {:class (stl/css :assets-bar)} + [:div {:class (stl/css :assets-header)} + (when-not ^boolean read-only? + [:button {:class (stl/css :libraries-button) + :on-click show-libraries-dialog} + [:span {:class (stl/css :libraries-icon)} + i/library-refactor] + (tr "workspace.assets.libraries")]) - [:div {:class (stl/css :search-wrapper)} - [:& search-bar {:on-change on-search-term-change - :value term - :placeholder (tr "workspace.assets.search")} - [:button - {:on-click on-open-menu - :class (stl/css :section-button)} - i/filter-refactor]] - [:& context-menu-a11y - {:on-close on-menu-close - :selectable true - :selected section - :show menu-open? - :fixed? true - :min-width? true - :top 152 - :left 64 - :options options - :workspace? true}] - [:button {:class (stl/css :sort-button) - :on-click toggle-ordering} - (if reverse-sort? - i/asc-sort-refactor - i/desc-sort-refactor)]]] + [:div {:class (stl/css :search-wrapper)} + [:& search-bar {:on-change on-search-term-change + :value term + :placeholder (tr "workspace.assets.search")} + [:button + {:on-click on-open-menu + :class (stl/css :section-button)} + i/filter-refactor]] + [:& context-menu-a11y + {:on-close on-menu-close + :selectable true + :selected section + :show menu-open? + :fixed? true + :min-width? true + :top 152 + :left 64 + :options options + :workspace? true}] + [:button {:class (stl/css :sort-button) + :on-click toggle-ordering} + (if reverse-sort? + i/asc-sort-refactor + i/desc-sort-refactor)]]] - [:& (mf/provider cmm/assets-filters) {:value filters} - [:& (mf/provider cmm/assets-toggle-ordering) {:value toggle-ordering} - [:& (mf/provider cmm/assets-toggle-list-style) {:value toggle-list-style} - [:div {:class (stl/css :libraries-wrapper)} - [:& assets-local-library {:filters filters}] - [:& assets-libraries {:filters filters}]]]]]] - - [:div.assets-bar - [:div.tool-window - [:div.tool-window-content - [:div.assets-bar-title - (tr "workspace.assets.assets") - - (when-not ^boolean read-only? - [:div.libraries-button {:on-click show-libraries-dialog} - i/text-align-justify - (tr "workspace.assets.libraries")])] - [:div.search-block - [:input.search-input - {:placeholder (tr "workspace.assets.search") - :type "text" - :value term - :on-change on-search-term-change - :on-key-down handle-key-down}] - - (if ^boolean (str/empty? term) - [:div.search-icon - i/search] - [:div.search-icon.close - {:on-click on-search-clear-click} - i/close])] - - [:select.input-select {:value (:section filters) - :data-mousetrap-dont-stop true - :on-change on-section-filter-change} - [:option {:value "all"} (tr "workspace.assets.box-filter-all")] - [:option {:value "components"} (tr "workspace.assets.components")] - (when-not components-v2 - [:option {:value "graphics"} (tr "workspace.assets.graphics")]) - [:option {:value "colors"} (tr "workspace.assets.colors")] - [:option {:value "typographies"} (tr "workspace.assets.typography")]]]] - - [:& (mf/provider cmm/assets-filters) {:value filters} - [:& (mf/provider cmm/assets-toggle-ordering) {:value toggle-ordering} - [:& (mf/provider cmm/assets-toggle-list-style) {:value toggle-list-style} - [:div.libraries-wrapper - [:& assets-local-library {:filters filters}] - [:& assets-libraries {:filters filters}]]]]]]))) + [:& (mf/provider cmm/assets-filters) {:value filters} + [:& (mf/provider cmm/assets-toggle-ordering) {:value toggle-ordering} + [:& (mf/provider cmm/assets-toggle-list-style) {:value toggle-list-style} + [:div {:class (stl/css :libraries-wrapper)} + [:& assets-local-library {:filters filters}] + [:& assets-libraries {:filters filters}]]]]]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.scss b/frontend/src/app/main/ui/workspace/sidebar/assets.scss index 82f81a7ac5..dd8ebf217a 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.scss @@ -10,121 +10,129 @@ position: relative; height: 100%; overflow: hidden; +} - .libraries-button { - @include tabTitleTipography; - @extend .button-secondary; - gap: $s-2; - height: $s-32; - width: 100%; - border-radius: $s-8; - margin-bottom: $s-4; - .libraries-icon { - @include flexCenter; - width: $s-24; - height: 100%; - svg { - @include flexCenter; - @extend .button-icon; - stroke: var(--icon-foreground); - } - } - &:hover { - background-color: var(--button-secondary-background-color-hover); - color: var(--button-secondary-foreground-color-hover); - border: $s-1 solid var(--button-secondary-border-color-hover); - svg { - stroke: var(--button-secondary-foreground-color-hover); - } - } - &:focus { - background-color: var(--button-secondary-background-color-focus); - color: var(--button-secondary-foreground-color-focus); - border: $s-1 solid var(--button-secondary-border-color-focus); - svg { - stroke: var(--button-secondary-foreground-color-focus); - } - } - } - .section-button { +.libraries-button { + @include tabTitleTipography; + @extend .button-secondary; + gap: $s-2; + height: $s-32; + width: 100%; + border-radius: $s-8; + margin-bottom: $s-4; + .libraries-icon { @include flexCenter; - @include buttonStyle; - height: $s-32; - width: $s-32; - margin: 0; - border: 1px solid var(--color-background-tertiary); - border-radius: $br-8 $br-2 $br-2 $br-8; - background-color: var(--color-background-tertiary); + width: $s-24; + height: 100%; svg { - height: $s-16; - width: $s-16; + @include flexCenter; + @extend .button-icon; stroke: var(--icon-foreground); } - &:focus { - border: 1px solid var(--input-border-color-focus); - outline: 0; - background-color: var(--input-background-color-active); - color: var(--input-foreground-color-active); - svg { - background-color: var(--input-background-color-active); - } - } - &:hover { - border: 1px solid var(--input-background-color-hover); - background-color: var(--input-background-color-hover); - svg { - background-color: var(--input-background-color-hover); - stroke: var(--button-foreground-hover); - } + } + &:hover { + background-color: var(--button-secondary-background-color-hover); + color: var(--button-secondary-foreground-color-hover); + border: $s-1 solid var(--button-secondary-border-color-hover); + svg { + stroke: var(--button-secondary-foreground-color-hover); } } - .sections-container { - @include menuShadow; - @include flexColumn; - position: absolute; - top: $s-84; - left: $s-12; - width: $s-192; - padding: $s-4; - border-radius: $br-8; - background-color: var(--menu-background-color); - z-index: $z-index-2; - .section-item { - @include titleTipography; - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - padding: $s-6; - border-radius: $br-8; - .section-btn { - @include buttonStyle; - } - } - } - .libraries-wrapper { - overflow-x: hidden; - overflow-y: auto; - scrollbar-gutter: stable; - display: flex; - flex-direction: column; - padding-left: $s-8; - height: calc(100vh - $s-180); - } - .assets-header { - padding: $s-8 $s-12 $s-12 $s-12; - .search-wrapper { - display: flex; - gap: $s-4; - .sort-button { - @extend .button-secondary; - width: $s-32; - border-radius: $br-8; - svg { - @extend .button-icon-small; - stroke: var(--icon-foreground); - } - } + &:focus { + background-color: var(--button-secondary-background-color-focus); + color: var(--button-secondary-foreground-color-focus); + border: $s-1 solid var(--button-secondary-border-color-focus); + svg { + stroke: var(--button-secondary-foreground-color-focus); } } } + +.section-button { + @include flexCenter; + @include buttonStyle; + height: $s-32; + width: $s-32; + margin: 0; + border: 1px solid var(--color-background-tertiary); + border-radius: $br-8 $br-2 $br-2 $br-8; + background-color: var(--color-background-tertiary); + svg { + height: $s-16; + width: $s-16; + stroke: var(--icon-foreground); + } + &:focus { + border: 1px solid var(--input-border-color-focus); + outline: 0; + background-color: var(--input-background-color-active); + color: var(--input-foreground-color-active); + svg { + background-color: var(--input-background-color-active); + } + } + &:hover { + border: 1px solid var(--input-background-color-hover); + background-color: var(--input-background-color-hover); + svg { + background-color: var(--input-background-color-hover); + stroke: var(--button-foreground-hover); + } + } +} + +.sections-container { + @include menuShadow; + @include flexColumn; + position: absolute; + top: $s-84; + left: $s-12; + width: $s-192; + padding: $s-4; + border-radius: $br-8; + background-color: var(--menu-background-color); + z-index: $z-index-2; +} + +.section-item { + @include titleTipography; + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + padding: $s-6; + border-radius: $br-8; +} + +.section-btn { + @include buttonStyle; +} + +.libraries-wrapper { + overflow-x: hidden; + overflow-y: auto; + scrollbar-gutter: stable; + display: flex; + flex-direction: column; + padding-left: $s-8; + height: calc(100vh - $s-180); +} + +.assets-header { + padding: $s-8 $s-12 $s-12 $s-12; +} + +.search-wrapper { + display: flex; + gap: $s-4; +} + +.sort-button { + @extend .button-secondary; + width: $s-32; + border-radius: $br-8; + svg { + @extend .button-icon-small; + stroke: var(--icon-foreground); + } +} 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 9ea7a1f6c8..d819943033 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/colors.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/colors.cljs @@ -5,7 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.workspace.sidebar.assets.colors - (:require-macros [app.main.style :refer [css]]) + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] @@ -18,7 +18,6 @@ [app.main.data.workspace.undo :as dwu] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.components.color-bullet :as bc] [app.main.ui.components.color-bullet-new :as cb] [app.main.ui.context :as ctx] [app.main.ui.icons :as i] @@ -59,7 +58,6 @@ menu-state (mf/use-state cmm/initial-context-menu-state) read-only? (mf/use-ctx ctx/workspace-read-only?) - new-css-system (mf/use-ctx ctx/new-css-system) default-name (cond (:gradient color) (uc/gradient-type->string (dm/get-in color [:gradient :type])) @@ -193,113 +191,67 @@ (dom/select-text! input) nil))) - (if ^boolean new-css-system - [:div {:class (dom/classnames (css :asset-list-item) true - (css :selected) (contains? selected (:id color)) - (css :editing) editing?) - :style #js {"--bullet-size" "16px"} - :on-context-menu on-context-menu - :on-click (when-not editing? on-click) - :ref item-ref - :draggable (and (not read-only?) (not editing?)) - :on-drag-start on-color-drag-start - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} + [:div {:class (stl/css-case :asset-list-item true + :selected (contains? selected (:id color)) + :editing editing?) + :style #js {"--bullet-size" "16px"} + :on-context-menu on-context-menu + :on-click (when-not editing? on-click) + :ref item-ref + :draggable (and (not read-only?) (not editing?)) + :on-drag-start on-color-drag-start + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} - [:div {:class (dom/classnames (css :bullet-block) true)} - [:& cb/color-bullet {:color color - :mini? true}]] + [:div {:class (stl/css :bullet-block)} + [:& cb/color-bullet {:color color + :mini? true}]] - (if ^boolean editing? - [:input - {:type "text" - :class (dom/classnames (css :element-name) true) - :ref input-ref - :on-blur input-blur - :on-key-down input-key-down - :auto-focus true - :default-value (cfh/merge-path-item (:path color) (:name color))}] + (if ^boolean editing? + [:input + {:type "text" + :class (stl/css :element-name) + :ref input-ref + :on-blur input-blur + :on-key-down input-key-down + :auto-focus true + :default-value (cfh/merge-path-item (:path color) (:name color))}] - [:div {:title (:name color) - :class (dom/classnames (css :name-block) true) - :on-double-click rename-color-clicked} + [:div {:title (:name color) + :class (stl/css :name-block) + :on-double-click rename-color-clicked} - (if (= (:name color) default-name) - [:span {:class (dom/classnames (css :default-name-only) true)} default-name] - [:* - [:span {:class (dom/classnames (css :name) true)} (:name color)] - [:span {:class (dom/classnames (css :default-name) true)} default-name]])]) + (if (= (:name color) default-name) + [:span {:class (stl/css :default-name-only)} default-name] + [:* + [:span {:class (stl/css :name)} (:name color)] + [:span {:class (stl/css :default-name)} default-name]])]) - (when local? - [:& cmm/assets-context-menu - {:on-close on-close-menu - :state @menu-state - :options [(when-not (or multi-colors? multi-assets?) - {:option-name (tr "workspace.assets.rename") - :id "assets-rename-color" - :option-handler rename-color-clicked}) - (when-not (or multi-colors? multi-assets?) - {:option-name (tr "workspace.assets.edit") - :id "assets-edit-color" - :option-handler edit-color-clicked}) + (when local? + [:& cmm/assets-context-menu + {:on-close on-close-menu + :state @menu-state + :options [(when-not (or multi-colors? multi-assets?) + {:option-name (tr "workspace.assets.rename") + :id "assets-rename-color" + :option-handler rename-color-clicked}) + (when-not (or multi-colors? multi-assets?) + {:option-name (tr "workspace.assets.edit") + :id "assets-edit-color" + :option-handler edit-color-clicked}) - {:option-name (tr "workspace.assets.delete") - :id "assets-delete-color" - :option-handler delete-color} - (when-not multi-assets? - {:option-name (tr "workspace.assets.group") - :id "assets-group-color" - :option-handler (on-group (:id color))})]}]) + {:option-name (tr "workspace.assets.delete") + :id "assets-delete-color" + :option-handler delete-color} + (when-not multi-assets? + {:option-name (tr "workspace.assets.group") + :id "assets-group-color" + :option-handler (on-group (:id color))})]}]) - (when ^boolean dragging? - [:div {:class (dom/classnames (css :dragging) true)}])] - - [:div.asset-list-item - {:class-name (dom/classnames - :selected (contains? selected (:id color))) - :on-context-menu on-context-menu - :on-click (when-not editing? on-click) - :ref item-ref - :draggable (and (not read-only?) (not editing?)) - :on-drag-start on-color-drag-start - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - - [:& bc/color-bullet {:color color}] - - (if ^boolean editing? - [:input.element-name - {:type "text" - :ref input-ref - :on-blur input-blur - :on-key-down input-key-down - :auto-focus true - :default-value (cfh/merge-path-item (:path color) (:name color))}] - - [:div.name-block {:title (:name color) - :on-double-click rename-color-clicked} - (:name color) - (when-not (= (:name color) default-name) - [:span default-name])]) - - (when local? - [:& cmm/assets-context-menu - {:on-close on-close-menu - :state @menu-state - :options [(when-not (or multi-colors? multi-assets?) - [(tr "workspace.assets.rename") rename-color-clicked]) - (when-not (or multi-colors? multi-assets?) - [(tr "workspace.assets.edit") edit-color-clicked]) - [(tr "workspace.assets.delete") delete-color] - (when-not multi-assets? - [(tr "workspace.assets.group") (on-group (:id color))])]}]) - - (when ^boolean dragging? - [:div.dragging])]))) + (when ^boolean dragging? + [:div {:class (stl/css :dragging)}])])) (mf/defc colors-group [{:keys [file-id prefix groups open-groups force-open? local? selected @@ -308,7 +260,6 @@ selected-full]}] (let [group-open? (or ^boolean force-open? ^boolean (get open-groups prefix (if (= prefix "") true false))) - new-css-system (mf/use-ctx ctx/new-css-system) dragging* (mf/use-state false) dragging? (deref dragging*) @@ -338,136 +289,71 @@ (fn [event] (cmm/on-drop-asset-group event dragging* prefix selected-paths selected-full move-color)))] - (if ^boolean new-css-system - [:div {:class (dom/classnames (css :colors-group) true) - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - [:& grp/asset-group-title {:file-id file-id - :section :colors - :path prefix - :group-open? group-open? - :on-rename on-rename-group - :on-ungroup on-ungroup}] - (when group-open? - [:* - (let [colors (get groups "" [])] - [:div {:class (dom/classnames (css :asset-list) true) - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} + [:div {:class (stl/css :colors-group) + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + [:& grp/asset-group-title {:file-id file-id + :section :colors + :path prefix + :group-open? group-open? + :on-rename on-rename-group + :on-ungroup on-ungroup}] + (when group-open? + [:* + (let [colors (get groups "" [])] + [:div {:class (stl/css :asset-list) + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} - (when ^boolean dragging? - [:div {:class (dom/classnames (css :grid-placeholder) true)} - "\u00A0"]) + (when ^boolean dragging? + [:div {:class (stl/css :grid-placeholder)} + "\u00A0"]) - (when (and (empty? colors) - (some? groups)) - [:div {:class (dom/classnames (css :drop-space) true)}]) + (when (and (empty? colors) + (some? groups)) + [:div {:class (stl/css :drop-space)}]) - (for [color colors] - [:& color-item {:key (dm/str (:id color)) - :color color - :file-id file-id - :local? local? - :selected selected - :multi-colors? multi-colors? - :multi-assets? multi-assets? - :on-asset-click on-asset-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection - :on-group on-group - :colors colors - :selected-full selected-full - :selected-paths selected-paths - :move-color move-color}])]) + (for [color colors] + [:& color-item {:key (dm/str (:id color)) + :color color + :file-id file-id + :local? local? + :selected selected + :multi-colors? multi-colors? + :multi-assets? multi-assets? + :on-asset-click on-asset-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection + :on-group on-group + :colors colors + :selected-full selected-full + :selected-paths selected-paths + :move-color move-color}])]) - (for [[path-item content] groups] - (when-not (empty? path-item) - [:& colors-group {:file-id file-id - :prefix (cfh/merge-path-item prefix path-item) - :key (dm/str "group-" path-item) - :groups content - :open-groups open-groups - :force-open? force-open? - :local? local? - :selected selected - :multi-colors? multi-colors? - :multi-assets? multi-assets? - :on-asset-click on-asset-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection - :on-group on-group - :on-rename-group on-rename-group - :on-ungroup on-ungroup - :colors colors - :selected-full selected-full}]))])] - - - [:div {:on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - [:& grp/asset-group-title {:file-id file-id - :section :colors - :path prefix - :group-open? group-open? - :on-rename on-rename-group - :on-ungroup on-ungroup}] - (when group-open? - [:* - (let [colors (get groups "" [])] - [:div.asset-list {:on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - - (when ^boolean dragging? - [:div.grid-placeholder "\u00A0"]) - - (when (and (empty? colors) - (some? groups)) - [:div.drop-space]) - - (for [color colors] - [:& color-item {:key (dm/str (:id color)) - :color color - :file-id file-id - :local? local? - :selected selected - :multi-colors? multi-colors? - :multi-assets? multi-assets? - :on-asset-click on-asset-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection - :on-group on-group - :colors colors - :selected-full selected-full - :selected-paths selected-paths - :move-color move-color}])]) - - (for [[path-item content] groups] - (when-not (empty? path-item) - [:& colors-group {:file-id file-id - :prefix (cfh/merge-path-item prefix path-item) - :key (dm/str "group-" path-item) - :groups content - :open-groups open-groups - :force-open? force-open? - :local? local? - :selected selected - :multi-colors? multi-colors? - :multi-assets? multi-assets? - :on-asset-click on-asset-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection - :on-group on-group - :on-rename-group on-rename-group - :on-ungroup on-ungroup - :colors colors - :selected-full selected-full}]))])]))) + (for [[path-item content] groups] + (when-not (empty? path-item) + [:& colors-group {:file-id file-id + :prefix (cfh/merge-path-item prefix path-item) + :key (dm/str "group-" path-item) + :groups content + :open-groups open-groups + :force-open? force-open? + :local? local? + :selected selected + :multi-colors? multi-colors? + :multi-assets? multi-assets? + :on-asset-click on-asset-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection + :on-group on-group + :on-rename-group on-rename-group + :on-ungroup on-ungroup + :colors colors + :selected-full selected-full}]))])])) (mf/defc colors-section [{:keys [file-id local? colors open? force-open? open-status-ref selected reverse-sort? @@ -491,7 +377,6 @@ (grp/group-assets colors reverse-sort?)) read-only? (mf/use-ctx ctx/workspace-read-only?) - new-css-system (mf/use-ctx ctx/new-css-system) add-color (mf/use-fn (fn [value _] @@ -589,19 +474,12 @@ :section :colors :assets-count (count colors) :open? open?} - (if ^boolean new-css-system - (when local? - [:& cmm/asset-section-block {:role :title-button} - (when-not read-only? - [:button {:class (dom/classnames (css :assets-btn) true) - :on-click add-color-clicked} - i/add-refactor])]) - - (when local? - [:& cmm/asset-section-block {:role :title-button} - (when-not read-only? - [:div.assets-button {:on-click add-color-clicked} - i/plus])])) + (when local? + [:& cmm/asset-section-block {:role :title-button} + (when-not read-only? + [:button {:class (stl/css :assets-btn) + :on-click add-color-clicked} + i/add-refactor])]) [:& cmm/asset-section-block {:role :content} diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/colors.scss b/frontend/src/app/main/ui/workspace/sidebar/assets/colors.scss index aed7ab1197..28dced5b49 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/colors.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/colors.scss @@ -9,7 +9,7 @@ .assets-btn { @extend .button-tertiary; height: $s-32; - width: calc($s-24 + $s-4); + width: $s-28; padding: 0; border-radius: $br-8; svg { @@ -20,83 +20,92 @@ .colors-group { margin-top: $s-4; - .asset-list { - padding: 0 0 0 $s-4; - .asset-list-item { - position: relative; - display: flex; - align-items: center; - height: $s-32; - padding: $s-8; - margin-bottom: $s-4; - border-radius: $br-8; - background-color: var(--assets-item-background-color); - cursor: pointer; - .bullet-block { - @include flexCenter; - height: 100%; - justify-content: flex-start; - margin-right: $s-4; - } - .name-block { - @include titleTipography; - display: grid; - grid-template-columns: auto 1fr; - margin: 0; - overflow: hidden; - .default-name-only, - .name { - color: var(--assets-item-name-foreground-color-hover); - margin-right: $s-6; - @include textEllipsis; - } - .default-name { - min-width: 0; - color: var(--assets-item-name-foreground-color); - } - } - .element-name { - @include textEllipsis; - color: var(--color-foreground-primary); - } - &.selected { - border: $s-1 solid var(--assets-item-border-color); - } +} - &.editing { - border: $s-1 solid var(--input-border-color-focus); - input.element-name { - @include textEllipsis; - @include titleTipography; - @include removeInputStyle; - flex-grow: 1; - height: $s-28; - max-width: calc(var(--parent-size) - (var(--depth) * var(--layer-indentation-size))); - margin: 0; - color: var(--layer-row-foreground-color); - } - } - &:hover { - background-color: var(--assets-item-background-color-hover); - } +.asset-list { + padding: 0 0 0 $s-4; +} + +.asset-list-item { + position: relative; + display: flex; + align-items: center; + height: $s-32; + padding: $s-8; + margin-bottom: $s-4; + border-radius: $br-8; + background-color: var(--assets-item-background-color); + cursor: pointer; + + &.selected { + border: $s-1 solid var(--assets-item-border-color); + } + + &.editing { + border: $s-1 solid var(--input-border-color-focus); + input.element-name { + @include textEllipsis; + @include titleTipography; + @include removeInputStyle; + flex-grow: 1; + height: $s-28; + max-width: calc(var(--parent-size) - (var(--depth) * var(--layer-indentation-size))); + margin: 0; + color: var(--layer-row-foreground-color); } } - .grid-placeholder { - height: $s-2; - margin-bottom: $s-2; - background-color: var(--color-accent-primary); - } - .drop-space { - height: $s-12; - } - .dragging { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - border-radius: $s-8; - background-color: var(--assets-item-background-color-drag); - border: $s-2 solid var(--assets-item-border-color-drag); + &:hover { + background-color: var(--assets-item-background-color-hover); } } + +.bullet-block { + @include flexCenter; + height: 100%; + justify-content: flex-start; + margin-right: $s-4; +} + +.name-block { + @include titleTipography; + display: grid; + grid-template-columns: auto 1fr; + margin: 0; + overflow: hidden; + .default-name-only, + .name { + color: var(--assets-item-name-foreground-color-hover); + margin-right: $s-6; + @include textEllipsis; + } + .default-name { + min-width: 0; + color: var(--assets-item-name-foreground-color); + } +} + +.element-name { + @include textEllipsis; + color: var(--color-foreground-primary); +} + +.grid-placeholder { + height: $s-2; + margin-bottom: $s-2; + background-color: var(--color-accent-primary); +} + +.drop-space { + height: $s-12; +} + +.dragging { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: $s-8; + background-color: var(--assets-item-background-color-drag); + border: $s-2 solid var(--assets-item-border-color-drag); +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs index 141a83a68f..c6d63c7d3e 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs @@ -6,7 +6,7 @@ (ns app.main.ui.workspace.sidebar.assets.common - (:require-macros [app.main.style :refer [css]]) + (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] [app.common.files.helpers :as cfh] @@ -22,7 +22,6 @@ [app.main.refs :as refs] [app.main.render :refer [component-svg component-svg-thumbnail]] [app.main.store :as st] - [app.main.ui.components.context-menu :refer [context-menu]] [app.main.ui.components.context-menu-a11y :refer [context-menu-a11y]] [app.main.ui.components.title-bar :refer [title-bar]] [app.main.ui.context :as ctx] @@ -111,24 +110,14 @@ (mf/defc assets-context-menu {::mf/wrap-props false} [{:keys [options state on-close]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:& context-menu-a11y - {:show (:open? state) - :fixed? (or (not= (:top state) 0) (not= (:left state) 0)) - :on-close on-close - :top (:top state) - :left (:left state) - :options options - :workspace? true}] - - [:& context-menu - {:selectable false - :show (:open? state) - :on-close on-close - :top (:top state) - :left (:left state) - :options options}]))) + [:& context-menu-a11y + {:show (:open? state) + :fixed? (or (not= (:top state) 0) (not= (:left state) 0)) + :on-close on-close + :top (:top state) + :left (:left state) + :options options + :workspace? true}]) (mf/defc section-icon [{:keys [section] :as props}] @@ -145,34 +134,24 @@ (filter some?)) get-role #(.. % -props -role) title-buttons (filter #(= (get-role %) :title-button) children) - content (filter #(= (get-role %) :content) children) - new-css-system (mf/use-ctx ctx/new-css-system)] - (if ^boolean new-css-system - [:div {:class (dom/classnames (css :asset-section) true)} - [:& title-bar {:collapsable? true - :collapsed? (not open?) - :clickable-all? true - :on-collapsed #(st/emit! (dw/set-assets-section-open file-id section (not open?))) - :class (css :title-spacing) - :title (mf/html [:span {:class (dom/classnames (css :title-name) true)} - [:span {:class (dom/classnames (css :section-icon) true)} - [:& section-icon {:section section}]] - [:span {:class (dom/classnames (css :section-name) true)} - title] + content (filter #(= (get-role %) :content) children)] + [:div {:class (stl/css :asset-section)} + [:& title-bar {:collapsable? true + :collapsed? (not open?) + :clickable-all? true + :on-collapsed #(st/emit! (dw/set-assets-section-open file-id section (not open?))) + :class (stl/css :title-spacing) + :title (mf/html [:span {:class (stl/css :title-name)} + [:span {:class (stl/css :section-icon)} + [:& section-icon {:section section}]] + [:span {:class (stl/css :section-name)} + title] - [:span {:class (dom/classnames (css :num-assets) true)} - assets-count]])} - title-buttons] - (when ^boolean open? - content)] - [:div.asset-section - [:div.asset-title {:class (when (not ^boolean open?) "closed")} - [:span {:on-click #(st/emit! (dw/set-assets-section-open file-id section (not open?)))} - i/arrow-slide title] - [:span.num-assets (dm/str "\u00A0(") assets-count ")"] ;; Unicode 00A0 is non-breaking space - title-buttons] - (when ^boolean open? - content)]))) + [:span {:class (stl/css :num-assets)} + assets-count]])} + title-buttons] + (when ^boolean open? + content)])) (mf/defc asset-section-block [{:keys [children]}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/common.scss b/frontend/src/app/main/ui/workspace/sidebar/assets/common.scss index 21da5c603c..a546e71a7d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/common.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/common.scss @@ -6,36 +6,38 @@ @import "refactor/common-refactor.scss"; -.asset-section { - .title-name { - @include tabTitleTipography; - display: flex; - align-items: center; - flex-grow: 1; - width: 100%; - .section-icon { - @include flexCenter; - padding-right: $s-2; - svg { - @include flexCenter; - height: $s-16; - width: $s-16; - color: transparent; - fill: none; - } - } - .section-name { - display: flex; - align-items: center; - margin: 0 $s-2; - } - .num-assets { - @include flexCenter; - height: 100%; - padding-left: $s-8; - } +.title-name { + @include tabTitleTipography; + display: flex; + align-items: center; + flex-grow: 1; + width: 100%; +} + +.section-icon { + @include flexCenter; + padding-right: $s-2; + svg { + @include flexCenter; + height: $s-16; + width: $s-16; + color: transparent; + fill: none; } } + +.section-name { + display: flex; + align-items: center; + margin: 0 $s-2; +} + +.num-assets { + @include flexCenter; + height: 100%; + padding-left: $s-8; +} + .title-spacing { margin-bottom: $s-4; } diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs index 964b403802..833c573f7a 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs @@ -5,7 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.workspace.sidebar.assets.components - (:require-macros [app.main.style :as stl :refer [css]]) + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] @@ -77,7 +77,6 @@ read-only? (mf/use-ctx ctx/workspace-read-only?) components-v2 (mf/use-ctx ctx/components-v2) - new-css-system (mf/use-ctx ctx/new-css-system) component-id (:id component) visible? (h/use-visible item-ref :once? true) @@ -144,89 +143,45 @@ (mf/deps on-context-menu component-id) (partial on-context-menu component-id))] - (if ^boolean new-css-system - [:div {:ref item-ref - :class (dom/classnames - (css :selected) (contains? selected (:id component)) - (css :grid-cell) listing-thumbs? - (css :enum-item) (not listing-thumbs?)) - :id (dm/str "component-shape-id-" (:id component)) - :draggable (not read-only?) - :on-click on-component-click - :on-double-click on-component-double-click - :on-context-menu on-context-menu - :on-drag-start on-component-drag-start - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - (when (and (some? root-shape) - (some? container)) - [:* + [:div {:ref item-ref + :class (stl/css-case :selected (contains? selected (:id component)) + :grid-cell listing-thumbs? + :enum-item (not listing-thumbs?)) + :id (dm/str "component-shape-id-" (:id component)) + :draggable (not read-only?) + :on-click on-component-click + :on-double-click on-component-double-click + :on-context-menu on-context-menu + :on-drag-start on-component-drag-start + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + (when (and (some? root-shape) + (some? container)) + [:* + (let [renaming? (= renaming (:id component))] + [:* + [:& editable-label + {:class (stl/css-case :cell-name listing-thumbs? + :item-name (not listing-thumbs?) + :editing renaming?) + :value (cfh/merge-path-item (:path component) (:name component)) + :tooltip (cfh/merge-path-item (:path component) (:name component)) + :display-value (:name component) + :editing renaming? + :disable-dbl-click true + :on-change do-rename + :on-cancel cancel-rename}] - (let [renaming? (= renaming (:id component))] - [:* - [:& editable-label - {:class (dom/classnames - (css :cell-name) listing-thumbs? - (css :item-name) (not listing-thumbs?) - (css :editing) renaming?) - :value (cfh/merge-path-item (:path component) (:name component)) - :tooltip (cfh/merge-path-item (:path component) (:name component)) - :display-value (:name component) - :editing renaming? - :disable-dbl-click true - :on-change do-rename - :on-cancel cancel-rename}] + (when ^boolean dragging? + [:div {:class (stl/css :dragging)}])]) - (when ^boolean dragging? - [:div {:class (dom/classnames (css :dragging) true)}])]) + (when visible? [:& cmm/component-item-thumbnail {:file-id file-id :root-shape root-shape :component component - :container container}]])] - - [:div {:ref item-ref - :class (dom/classnames - :selected (contains? selected (:id component)) - :grid-cell listing-thumbs? - :enum-item (not listing-thumbs?)) - :id (dm/str "component-shape-id-" (:id component)) - :draggable (not read-only?) - :on-click on-component-click - :on-double-click on-component-double-click - :on-context-menu on-context-menu - :on-drag-start on-component-drag-start - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - - (when (and (some? root-shape) - (some? container)) - [:* - (when visible? - [:& cmm/component-item-thumbnail {:file-id file-id - :root-shape root-shape - :component component - :container container}]) - (let [renaming? (= renaming (:id component))] - [:* - [:& editable-label - {:class (dom/classnames - :cell-name listing-thumbs? - :item-name (not listing-thumbs?) - :editing renaming?) - :value (cfh/merge-path-item (:path component) (:name component)) - :tooltip (cfh/merge-path-item (:path component) (:name component)) - :display-value (:name component) - :editing renaming? - :disable-dbl-click true - :on-change do-rename - :on-cancel cancel-rename}] - - (when ^boolean dragging? - [:div.dragging])])])]))) + :container container}])])])) (mf/defc components-group {::mf/wrap-props false} @@ -236,7 +191,6 @@ (let [group-open? (or ^boolean force-open? ^boolean (get open-groups prefix (if (= prefix "") true false))) - new-css-system (mf/use-ctx ctx/new-css-system) dragging* (mf/use-state false) dragging? (deref dragging*) @@ -266,162 +220,82 @@ (when (and local (:local? @drag-data*)) (cmm/on-drop-asset-group event dragging* prefix selected-paths selected-full dwl/rename-component-and-main-instance))))] - (if ^boolean new-css-system - [:div {:class (dom/classnames (css :component-group) true) - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - [:& grp/asset-group-title - {:file-id file-id - :section :components - :path prefix - :group-open? group-open? - :on-rename on-rename-group - :on-ungroup on-ungroup}] + [:div {:class (stl/css :component-group) + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + [:& grp/asset-group-title + {:file-id file-id + :section :components + :path prefix + :group-open? group-open? + :on-rename on-rename-group + :on-ungroup on-ungroup}] - (when group-open? - [:* - (let [components (get groups "" [])] - [:div {:class-name (dom/classnames - (css :asset-grid) listing-thumbs? - (css :asset-enum) (not listing-thumbs?) - (css :drop-space) (and - (empty? components) - (some? groups) - (not dragging?) - local)) - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} + (when group-open? + [:* + (let [components (get groups "" [])] + [:div {:class-name (stl/css-case :asset-grid listing-thumbs? + :asset-enum (not listing-thumbs?) + :drop-space (and + (empty? components) + (some? groups) + (not dragging?) + local)) + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} - (when ^boolean dragging? - [:div {:class (dom/classnames (css :grid-placeholder) true)} "\u00A0"]) + (when ^boolean dragging? + [:div {:class (stl/css :grid-placeholder)} "\u00A0"]) - (when (and (empty? components) - (some? groups) - local) - [:div {:class (dom/classnames (css :drop-space) true)}]) + (when (and (empty? components) + (some? groups) + local) + [:div {:class (stl/css :drop-space)}]) - (for [component components] - [:& components-item - {:component component - :key (dm/str "component-" (:id component)) - :renaming renaming - :listing-thumbs? listing-thumbs? - :file-id file-id - :selected selected - :selected-full selected-full - :selected-paths selected-paths - :on-asset-click on-asset-click - :on-context-menu on-context-menu - :on-drag-start on-drag-start - :on-group on-group - :do-rename do-rename - :cancel-rename cancel-rename - :local local}])]) + (for [component components] + [:& components-item + {:component component + :key (dm/str "component-" (:id component)) + :renaming renaming + :listing-thumbs? listing-thumbs? + :file-id file-id + :selected selected + :selected-full selected-full + :selected-paths selected-paths + :on-asset-click on-asset-click + :on-context-menu on-context-menu + :on-drag-start on-drag-start + :on-group on-group + :do-rename do-rename + :cancel-rename cancel-rename + :local local}])]) - (for [[path-item content] groups] - (when-not (empty? path-item) - [:& components-group {:file-id file-id - :key path-item - :prefix (cfh/merge-path-item prefix path-item) - :groups content - :open-groups open-groups - :force-open? force-open? - :renaming renaming - :listing-thumbs? listing-thumbs? - :selected selected - :on-asset-click on-asset-click - :on-drag-start on-drag-start - :do-rename do-rename - :cancel-rename cancel-rename - :on-rename-group on-rename-group - :on-ungroup on-ungroup - :on-context-menu on-context-menu - :selected-full selected-full - :local local}]))])] - - - [:div {:on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - - [:& grp/asset-group-title - {:file-id file-id - :section :components - :path prefix - :group-open? group-open? - :on-rename on-rename-group - :on-ungroup on-ungroup}] - - (when group-open? - [:* - (let [components (get groups "" [])] - [:div {:class-name (dom/classnames - :asset-grid listing-thumbs? - :big listing-thumbs? - :asset-enum (not listing-thumbs?) - :drop-space (and - (empty? components) - (some? groups) - (not dragging?) - local)) - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - - (when ^boolean dragging? - [:div.grid-placeholder "\u00A0"]) - - (when (and (empty? components) - (some? groups) - local) - [:div.drop-space]) - - (for [component components] - [:& components-item - {:component component - :key (dm/str "component-" (:id component)) - :renaming renaming - :listing-thumbs? listing-thumbs? - :file-id file-id - :selected selected - :selected-full selected-full - :selected-paths selected-paths - :on-asset-click on-asset-click - :on-context-menu on-context-menu - :on-drag-start on-drag-start - :on-group on-group - :do-rename do-rename - :cancel-rename cancel-rename - :local local}])]) - - (for [[path-item content] groups] - (when-not (empty? path-item) - [:& components-group {:file-id file-id - :key path-item - :prefix (cfh/merge-path-item prefix path-item) - :groups content - :open-groups open-groups - :force-open? force-open? - :renaming renaming - :listing-thumbs? listing-thumbs? - :selected selected - :on-asset-click on-asset-click - :on-drag-start on-drag-start - :do-rename do-rename - :cancel-rename cancel-rename - :on-rename-group on-rename-group - :on-ungroup on-ungroup - :on-context-menu on-context-menu - :selected-full selected-full - :local local}]))])]))) + (for [[path-item content] groups] + (when-not (empty? path-item) + [:& components-group {:file-id file-id + :key path-item + :prefix (cfh/merge-path-item prefix path-item) + :groups content + :open-groups open-groups + :force-open? force-open? + :renaming renaming + :listing-thumbs? listing-thumbs? + :selected selected + :on-asset-click on-asset-click + :on-drag-start on-drag-start + :do-rename do-rename + :cancel-rename cancel-rename + :on-rename-group on-rename-group + :on-ungroup on-ungroup + :on-context-menu on-context-menu + :selected-full selected-full + :local local}]))])])) (mf/defc components-section {::mf/wrap-props false} @@ -446,7 +320,6 @@ menu-state (mf/use-state cmm/initial-context-menu-state) read-only? (mf/use-ctx ctx/workspace-read-only?) components-v2 (mf/use-ctx ctx/components-v2) - new-css-system (mf/use-ctx ctx/new-css-system) toggle-list-style (mf/use-ctx cmm/assets-toggle-list-style) selected (:components selected) @@ -623,38 +496,28 @@ :section :components :assets-count (count components) :open? open?} - (if ^boolean new-css-system - [:& cmm/asset-section-block {:role :title-button} - [:* - (when open? - [:div {:class (stl/css :listing-options)} - [:& radio-buttons {:selected (if listing-thumbs? "grid" "list") - :on-change toggle-list-style - :name "listing-style"} - [:& radio-button {:icon i/view-as-list-refactor - :value "list" - :id "opt-list"}] - [:& radio-button {:icon i/flex-grid-refactor - :value "grid" - :id "opt-grid"}]]]) + [:& cmm/asset-section-block {:role :title-button} + [:* + (when open? + [:div {:class (stl/css :listing-options)} + [:& radio-buttons {:selected (if listing-thumbs? "grid" "list") + :on-change toggle-list-style + :name "listing-style"} + [:& radio-button {:icon i/view-as-list-refactor + :value "list" + :id "opt-list"}] + [:& radio-button {:icon i/flex-grid-refactor + :value "grid" + :id "opt-grid"}]]]) - (when (and components-v2 (not read-only?) local?) - [:div {:on-click add-component - :class (dom/classnames (css :add-component) true)} - i/add-refactor - [:& file-uploader {:accept cm/str-image-types - :multi true - :ref input-ref - :on-selected on-file-selected}]])]] - (when local? - [:& cmm/asset-section-block {:role :title-button} - (when (and components-v2 (not read-only?)) - [:div.assets-button {:on-click add-component} - i/plus - [:& file-uploader {:accept cm/str-image-types - :multi true - :ref input-ref - :on-selected on-file-selected}]])])) + (when (and components-v2 (not read-only?) local?) + [:div {:on-click add-component + :class (stl/css :add-component)} + i/add-refactor + [:& file-uploader {:accept cm/str-image-types + :multi true + :ref input-ref + :on-selected on-file-selected}]])]] [:& cmm/asset-section-block {:role :content} (when ^boolean open? @@ -680,42 +543,28 @@ [:& cmm/assets-context-menu {:on-close on-close-menu :state @menu-state - :options (if new-css-system - [(when (and local? (not (or multi-components? multi-assets? read-only?))) - {:option-name (tr "workspace.assets.rename") - :id "assets-rename-component" - :option-handler on-rename}) - (when (and local? (not (or multi-assets? read-only?))) - {:option-name (if components-v2 - (tr "workspace.assets.duplicate-main") - (tr "workspace.assets.duplicate")) - :id "assets-duplicate-component" - :option-handler on-duplicate}) + :options [(when (and local? (not (or multi-components? multi-assets? read-only?))) + {:option-name (tr "workspace.assets.rename") + :id "assets-rename-component" + :option-handler on-rename}) + (when (and local? (not (or multi-assets? read-only?))) + {:option-name (if components-v2 + (tr "workspace.assets.duplicate-main") + (tr "workspace.assets.duplicate")) + :id "assets-duplicate-component" + :option-handler on-duplicate}) - (when (and local? (not read-only?)) - {:option-name (tr "workspace.assets.delete") - :id "assets-delete-component" - :option-handler on-delete}) - (when (and local? (not (or multi-assets? read-only?))) - {:option-name (tr "workspace.assets.group") - :id "assets-group-component" - :option-handler on-group}) + (when (and local? (not read-only?)) + {:option-name (tr "workspace.assets.delete") + :id "assets-delete-component" + :option-handler on-delete}) + (when (and local? (not (or multi-assets? read-only?))) + {:option-name (tr "workspace.assets.group") + :id "assets-group-component" + :option-handler on-group}) - (when (and components-v2 (not multi-assets?)) - {:option-name (tr "workspace.shape.menu.show-main") - :id "assets-show-main-component" - :option-handler on-show-main})] - - [(when (and local? (not (or multi-components? multi-assets? read-only?))) - [(tr "workspace.assets.rename") on-rename]) - (when (and local? (not (or multi-assets? read-only?))) - [(if components-v2 - (tr "workspace.assets.duplicate-main") - (tr "workspace.assets.duplicate")) on-duplicate]) - (when (and local? (not read-only?)) - [(tr "workspace.assets.delete") on-delete]) - (when (and local? (not (or multi-assets? read-only?))) - [(tr "workspace.assets.group") on-group]) - (when (and components-v2 (not multi-assets?)) - [(tr "workspace.shape.menu.show-main") on-show-main])])}]]])) + (when (and components-v2 (not multi-assets?)) + {:option-name (tr "workspace.shape.menu.show-main") + :id "assets-show-main-component" + :option-handler on-show-main})]}]]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/components.scss b/frontend/src/app/main/ui/workspace/sidebar/assets/components.scss index 82fa009bc6..6496b917eb 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/components.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/components.scss @@ -6,213 +6,217 @@ @import "refactor/common-refactor.scss"; -.component-group { - .drop-space { - height: $s-12; - } - .asset-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - grid-auto-rows: calc(10vh + $s-16); - gap: $s-4; - margin-left: $s-8; - .grid-cell { - @include flexCenter; - position: relative; - padding: $s-8; - border: $s-4 solid transparent; - border-radius: $br-8; - background-color: var(--assets-component-background-color); - overflow: hidden; - cursor: pointer; - img { - height: auto; - width: auto; - max-height: 100%; - max-width: 100%; - pointer-events: none; - } - svg { - height: 10vh; - } - .cell-name { - @include titleTipography; - @include textEllipsis; - display: none; - position: absolute; - left: 0; - bottom: 0; - width: 100%; - padding: $s-2; - &.editing { - display: flex; - align-items: center; - height: $s-32; - border: $s-1 solid var(--input-border-color-focus); - border-radius: $br-8; - background-color: var(--input-background-color); - input { - @include textEllipsis; - @include titleTipography; - @include removeInputStyle; - flex-grow: 1; - height: $s-28; - max-width: calc(var(--parent-size) - (var(--depth) * var(--layer-indentation-size))); - padding-left: $s-6; - margin: 0; - border-radius: $br-8; - color: var(--input-foreground-color); - } - span { - @include flexCenter; - height: $s-28; - background-color: transparent; - border-radius: $br-8; - svg { - @extend .button-icon-small; - stroke: var(--input-foreground-color); - transform: rotate(90deg); - } - } - } - } +.drop-space { + height: $s-12; +} +.asset-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-auto-rows: calc(10vh + $s-16); + gap: $s-4; + margin-left: $s-8; +} - &:hover { - background-color: var(--assets-item-background-color-hover); - .cell-name { - display: block; - color: var(--assets-item-name-foreground-color-hover); - background: linear-gradient(to top, rgba(52, 57, 59, 1) 0%, rgba(52, 57, 59, 0) 100%); - &.editing { - display: flex; - background: var(--input-background-color); - input { - color: var(--input-foreground-color-active); - } - span svg { - stroke: var(--input-foreground-color-active); - } - } - } - } - - &.selected { - border: $s-4 solid var(--assets-item-border-color); - } - } - .grid-placeholder { - width: 100%; - border-radius: $br-8; - background-color: var(--assets-item-background-color-drag); - border: $s-2 solid var(--assets-item-border-color-drag); - } +.grid-cell { + @include flexCenter; + position: relative; + padding: $s-8; + border: $s-4 solid transparent; + border-radius: $br-8; + background-color: var(--assets-component-background-color); + overflow: hidden; + cursor: pointer; + img { + height: auto; + width: auto; + max-height: 100%; + max-width: 100%; + pointer-events: none; } - .asset-enum { - margin: 0 $s-12; - .enum-item { - position: relative; + svg { + height: 10vh; + } + .cell-name { + @include titleTipography; + @include textEllipsis; + display: none; + position: absolute; + left: 0; + bottom: 0; + width: 100%; + padding: $s-2; + &.editing { display: flex; align-items: center; - height: $s-36; - margin-bottom: $s-4; - padding: $s-2; + height: $s-32; + border: $s-1 solid var(--input-border-color-focus); border-radius: $br-8; - background-color: var(--assets-item-background-color); - cursor: pointer; - - svg, - img { - @include flexCenter; - flex-shrink: 0; - padding: $s-2; - height: $s-32; - width: $s-32; - border-radius: $br-6; - background-color: var(--color-foreground-secondary); - } - - .item-name { - @include titleTipography; + background-color: var(--input-background-color); + input { @include textEllipsis; - padding-left: $s-8; - color: var(--assets-item-name-foreground-color); - &.editing { - display: flex; - align-items: center; - height: $s-32; - border: $s-1 solid var(--input-border-color-focus); - border-radius: $br-8; - background-color: var(--input-background-color); - input { - @include textEllipsis; - @include titleTipography; - @include removeInputStyle; - flex-grow: 1; - height: $s-28; - max-width: calc(var(--parent-size) - (var(--depth) * var(--layer-indentation-size))); - padding-left: $s-6; - margin: 0; - border-radius: $br-8; - color: var(--input-foreground-color); - } - span { - @include flexCenter; - height: $s-28; - background-color: transparent; - border-radius: $br-8; - svg { - @extend .button-icon-small; - stroke: var(--input-foreground-color); - transform: rotate(90deg); - } - } + @include titleTipography; + @include removeInputStyle; + flex-grow: 1; + height: $s-28; + max-width: calc(var(--parent-size) - (var(--depth) * var(--layer-indentation-size))); + padding-left: $s-6; + margin: 0; + border-radius: $br-8; + color: var(--input-foreground-color); + } + span { + @include flexCenter; + height: $s-28; + background-color: transparent; + border-radius: $br-8; + svg { + @extend .button-icon-small; + stroke: var(--input-foreground-color); + transform: rotate(90deg); } } - - &:hover { - background-color: var(--assets-item-background-color-hover); - .item-name { - color: var(--assets-item-name-foreground-color-hover); - &.editing { - background: var(--input-background-color); - input { - color: var(--input-foreground-color-active); - } - span svg { - stroke: var(--input-foreground-color-active); - } - } - } - } - &.selected { - border: $s-1 solid var(--assets-item-border-color); - } - } - .grid-placeholder { - height: $s-2; - width: 100%; - background-color: var(--color-accent-primary); } } + + &:hover { + background-color: var(--assets-item-background-color-hover); + .cell-name { + display: block; + color: var(--assets-item-name-foreground-color-hover); + background: linear-gradient(to top, rgba(52, 57, 59, 1) 0%, rgba(52, 57, 59, 0) 100%); + &.editing { + display: flex; + background: var(--input-background-color); + input { + color: var(--input-foreground-color-active); + } + span svg { + stroke: var(--input-foreground-color-active); + } + } + } + } + + &.selected { + border: $s-4 solid var(--assets-item-border-color); + } } + +.grid-placeholder { + width: 100%; + border-radius: $br-8; + background-color: var(--assets-item-background-color-drag); + border: $s-2 solid var(--assets-item-border-color-drag); +} + +.asset-enum { + margin: 0 $s-12; +} +.enum-item { + position: relative; + display: flex; + align-items: center; + height: $s-36; + margin-bottom: $s-4; + padding: $s-2; + border-radius: $br-8; + background-color: var(--assets-item-background-color); + cursor: pointer; + + svg, + img { + @include flexCenter; + flex-shrink: 0; + padding: $s-2; + height: $s-32; + width: $s-32; + border-radius: $br-6; + background-color: var(--color-foreground-secondary); + } + + .item-name { + @include titleTipography; + @include textEllipsis; + padding-left: $s-8; + color: var(--assets-item-name-foreground-color); + &.editing { + display: flex; + align-items: center; + height: $s-32; + border: $s-1 solid var(--input-border-color-focus); + border-radius: $br-8; + background-color: var(--input-background-color); + input { + @include textEllipsis; + @include titleTipography; + @include removeInputStyle; + flex-grow: 1; + height: $s-28; + max-width: calc(var(--parent-size) - (var(--depth) * var(--layer-indentation-size))); + padding-left: $s-6; + margin: 0; + border-radius: $br-8; + color: var(--input-foreground-color); + } + span { + @include flexCenter; + height: $s-28; + background-color: transparent; + border-radius: $br-8; + svg { + @extend .button-icon-small; + stroke: var(--input-foreground-color); + transform: rotate(90deg); + } + } + } + } + + &:hover { + background-color: var(--assets-item-background-color-hover); + .item-name { + color: var(--assets-item-name-foreground-color-hover); + &.editing { + background: var(--input-background-color); + input { + color: var(--input-foreground-color-active); + } + span svg { + stroke: var(--input-foreground-color-active); + } + } + } + } + &.selected { + border: $s-1 solid var(--assets-item-border-color); + } +} + +.grid-placeholder { + height: $s-2; + width: 100%; + background-color: var(--color-accent-primary); +} + .listing-options { display: flex; align-items: center; +} - .listing-option-btn { - @include flexCenter; - cursor: pointer; - background-color: var(--button-radio-background-color-rest); +.listing-option-btn { + @include flexCenter; + cursor: pointer; + background-color: var(--button-radio-background-color-rest); - &.first { - margin-left: auto; - } + &.first { + margin-left: auto; + } - svg { - @extend .button-icon; - } + svg { + @extend .button-icon; } } + .add-component { @extend .button-tertiary; height: $s-32; @@ -224,6 +228,7 @@ stroke: var(--icon-foreground); } } + :global(.three-row) { .asset-grid { grid-template-columns: repeat(3, 1fr); diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs index 116ebd2abb..f9fcbc82a3 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs @@ -5,7 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.workspace.sidebar.assets.file-library - (:require-macros [app.main.style :refer [css]]) + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] @@ -40,62 +40,41 @@ (mf/defc file-library-title {::mf/wrap-props false} - [{:keys [open? local? shared? project-id file-id page-id file-name]}] + [{:keys [open? local? project-id file-id page-id file-name]}] (let [router (mf/deref refs/router) url (rt/resolve router :workspace {:project-id project-id :file-id file-id} {:page-id page-id}) - new-css-system (mf/use-ctx ctx/new-css-system) toggle-open (mf/use-fn (mf/deps file-id open?) (fn [] (st/emit! (dw/set-assets-section-open file-id :library (not open?)))))] - (if ^boolean new-css-system - [:div {:class (dom/classnames (css :library-title) true)} - [:& title-bar {:collapsable? true - :collapsed? (not open?) - :clickable-all? true - :on-collapsed toggle-open - :title (if local? - (mf/html [:div {:class (dom/classnames (css :special-title) true)} (tr "workspace.assets.local-library")]) + [:div {:class (stl/css :library-title)} + [:& title-bar {:collapsable? true + :collapsed? (not open?) + :clickable-all? true + :on-collapsed toggle-open + :title (if local? + (mf/html [:div {:class (stl/css :special-title)} + (tr "workspace.assets.local-library")]) + ;; Do we need to add shared info here? - (mf/html [:div {:class (dom/classnames (css :special-title) true)} file-name]))} - (when-not local? - [:span.tool-link.tooltip.tooltip-left {:alt "Open library file"} - [:a {:class (dom/classnames (css :file-link) true) - :href (str "#" url) - :target "_blank" - :on-click dom/stop-propagation} - i/open-link-refactor]])]] - - [:div.tool-window-bar.library-bar - {:on-click toggle-open} - [:div.collapse-library - {:class (dom/classnames :open open?)} - i/arrow-slide] - - (if local? - [:* - [:span.library-title (tr "workspace.assets.local-library")] - (when shared? - [:span.shared-library {:alt (tr "workspace.assets.shared-library") :title (tr "workspace.assets.shared-library")} - i/library] - )] - [:* - [:span.library-title {:title file-name} file-name] - [:span.tool-link {:alt (tr "workspace.assets.open-library") :title (tr "workspace.assets.open-library")} - [:a {:href (str "#" url) - :target "_blank" - :on-click dom/stop-propagation} - i/chain]]])]))) + (mf/html [:div {:class (stl/css :special-title)} + file-name]))} + (when-not local? + [:span {:title "Open library file"} + [:a {:class (stl/css :file-link) + :href (str "#" url) + :target "_blank" + :on-click dom/stop-propagation} + i/open-link-refactor]])]])) (mf/defc file-library-content {::mf/wrap-props false} [{:keys [file local? open-status-ref on-clear-selection]}] (let [components-v2 (mf/use-ctx ctx/components-v2) - new-css-system (mf/use-ctx ctx/new-css-system) open-status (mf/deref open-status-ref) file-id (:id file) @@ -111,9 +90,6 @@ reverse-sort? (= :desc filters-ordering) listing-thumbs? (= :thumbs filters-list-style) - toggle-ordering (mf/use-ctx cmm/assets-toggle-ordering) - toggle-list-style (mf/use-ctx cmm/assets-toggle-list-style) - library-ref (mf/with-memo [file-id] (create-file-library-ref file-id)) @@ -155,10 +131,6 @@ (l/derived lens:selected))) selected (mf/deref selected-lens) - selected-count (+ (count (get selected :components)) - (count (get selected :graphics)) - (count (get selected :colors)) - (count (get selected :typographies))) has-term? (not ^boolean (str/empty? filters-term)) force-open-components? (when ^boolean has-term? (> 60 (count components))) @@ -246,166 +218,80 @@ (st/emit! (dwu/commit-undo-transaction undo-id)))))] - (if ^boolean new-css-system - [:div {:class (dom/classnames (css :library-content) true)} - (when ^boolean show-components? - [:& components-section - {:file-id file-id - :local? local? - :components components - :listing-thumbs? listing-thumbs? - :open? (or ^boolean force-open-components? - ^boolean (get open-status :components false)) - :force-open? force-open-components? - :open-status-ref open-status-ref - :reverse-sort? reverse-sort? - :selected selected - :on-asset-click on-component-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection}]) + [:div {:class (stl/css :library-content)} + (when ^boolean show-components? + [:& components-section + {:file-id file-id + :local? local? + :components components + :listing-thumbs? listing-thumbs? + :open? (or ^boolean force-open-components? + ^boolean (get open-status :components false)) + :force-open? force-open-components? + :open-status-ref open-status-ref + :reverse-sort? reverse-sort? + :selected selected + :on-asset-click on-component-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection}]) - (when ^boolean show-graphics? - [:& graphics-section - {:file-id file-id - :project-id project-id - :local? local? - :objects media - :listing-thumbs? listing-thumbs? - :open? (or ^boolean force-open-graphics? - ^boolean (get open-status :graphics false)) - :force-open? force-open-graphics? - :open-status-ref open-status-ref - :reverse-sort? reverse-sort? - :selected selected - :on-asset-click on-graphics-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection}]) + (when ^boolean show-graphics? + [:& graphics-section + {:file-id file-id + :project-id project-id + :local? local? + :objects media + :listing-thumbs? listing-thumbs? + :open? (or ^boolean force-open-graphics? + ^boolean (get open-status :graphics false)) + :force-open? force-open-graphics? + :open-status-ref open-status-ref + :reverse-sort? reverse-sort? + :selected selected + :on-asset-click on-graphics-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection}]) - (when ^boolean show-colors? - [:& colors-section - {:file-id file-id - :local? local? - :colors colors - :open? (or ^boolean force-open-colors? - ^boolean (get open-status :colors false)) - :force-open? force-open-colors? - :open-status-ref open-status-ref - :reverse-sort? reverse-sort? - :selected selected - :on-asset-click on-colors-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection}]) + (when ^boolean show-colors? + [:& colors-section + {:file-id file-id + :local? local? + :colors colors + :open? (or ^boolean force-open-colors? + ^boolean (get open-status :colors false)) + :force-open? force-open-colors? + :open-status-ref open-status-ref + :reverse-sort? reverse-sort? + :selected selected + :on-asset-click on-colors-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection}]) - (when ^boolean show-typography? - [:& typographies-section - {:file file - :file-id (:id file) - :local? local? - :typographies typographies - :open? (or ^boolean force-open-typographies? - ^boolean (get open-status :typographies false)) - :force-open? force-open-typographies? - :open-status-ref open-status-ref - :reverse-sort? reverse-sort? - :selected selected - :on-asset-click on-typography-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection}]) + (when ^boolean show-typography? + [:& typographies-section + {:file file + :file-id (:id file) + :local? local? + :typographies typographies + :open? (or ^boolean force-open-typographies? + ^boolean (get open-status :typographies false)) + :force-open? force-open-typographies? + :open-status-ref open-status-ref + :reverse-sort? reverse-sort? + :selected selected + :on-asset-click on-typography-click + :on-assets-delete on-assets-delete + :on-clear-selection on-clear-selection}]) - (when (and (not ^boolean show-components?) - (not ^boolean show-graphics?) - (not ^boolean show-colors?) - (not ^boolean show-typography?)) - [:div {:class (css :asset-title)} - [:span {:class (css :no-found-icon)} - i/search-refactor] - [:span {:class (css :no-found-text)} - (tr "workspace.assets.not-found")]])] - [:div.tool-window-content - [:div.listing-options - (when (> selected-count 0) - [:span.selected-count - (tr "workspace.assets.selected-count" (i18n/c selected-count))]) - [:div.listing-option-btn.first {:on-click toggle-ordering} - (if reverse-sort? - i/sort-ascending - i/sort-descending)] - [:div.listing-option-btn {:on-click toggle-list-style} - (if listing-thumbs? - i/listing-enum - i/listing-thumbs)]] - - (when ^boolean show-components? - [:& components-section - {:file-id file-id - :local? local? - :components components - :listing-thumbs? listing-thumbs? - :open? (or ^boolean force-open-components? - ^boolean (get open-status :components false)) - :force-open? force-open-components? - :open-status-ref open-status-ref - :reverse-sort? reverse-sort? - :selected selected - :on-asset-click on-component-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection}]) - - (when ^boolean show-graphics? - [:& graphics-section - {:file-id file-id - :project-id project-id - :local? local? - :objects media - :listing-thumbs? listing-thumbs? - :open? (or ^boolean force-open-graphics? - ^boolean (get open-status :graphics false)) - :force-open? force-open-graphics? - :open-status-ref open-status-ref - :reverse-sort? reverse-sort? - :selected selected - :on-asset-click on-graphics-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection}]) - - (when ^boolean show-colors? - [:& colors-section - {:file-id file-id - :local? local? - :colors colors - :open? (or ^boolean force-open-colors? - ^boolean (get open-status :colors false)) - :force-open? force-open-colors? - :open-status-ref open-status-ref - :reverse-sort? reverse-sort? - :selected selected - :on-asset-click on-colors-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection}]) - - (when ^boolean show-typography? - [:& typographies-section - {:file file - :file-id (:id file) - :local? local? - :typographies typographies - :open? (or ^boolean force-open-typographies? - ^boolean (get open-status :typographies false)) - :force-open? force-open-typographies? - :open-status-ref open-status-ref - :reverse-sort? reverse-sort? - :selected selected - :on-asset-click on-typography-click - :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection}]) - - (when (and (not ^boolean show-components?) - (not ^boolean show-graphics?) - (not ^boolean show-colors?) - (not ^boolean show-typography?)) - [:div.asset-section - [:div.asset-title - (tr "workspace.assets.not-found")]])]))) + (when (and (not ^boolean show-components?) + (not ^boolean show-graphics?) + (not ^boolean show-colors?) + (not ^boolean show-typography?)) + [:div {:class (stl/css :asset-title)} + [:span {:class (stl/css :no-found-icon)} + i/search-refactor] + [:span {:class (stl/css :no-found-text)} + (tr "workspace.assets.not-found")]])])) (mf/defc file-library @@ -416,7 +302,6 @@ shared? (:is-shared file) project-id (:project-id file) page-id (dm/get-in file [:data :pages 0]) - new-css-system (mf/use-ctx ctx/new-css-system) open-status-ref (mf/with-memo [file-id] (-> (l/key file-id) @@ -429,8 +314,7 @@ (mf/deps file-id) (fn [] (st/emit! (dw/unselect-all-assets file-id))))] - [:div {:class (dom/classnames (css :tool-window) new-css-system - :tool-window (not new-css-system)) + [:div {:class (stl/css :tool-window) :on-context-menu dom/prevent-default :on-click unselect-all} [:& file-library-title diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.scss b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.scss index 68b50d27c0..0044dbcd18 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.scss @@ -8,35 +8,35 @@ .tool-window { margin-bottom: $s-24; } -.library-title { - .file-name { - @include titleTipography; - display: flex; - justify-content: flex-start; - align-items: center; - flex-grow: 100; - height: 100%; - } - .special-title { - @include textEllipsis; - color: var(--title-foreground-color-hover); - margin-left: $s-2; - text-align: left; - } +.file-name { + @include titleTipography; + display: flex; + justify-content: flex-start; + align-items: center; + flex-grow: 100; + height: 100%; +} - .file-link { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - border-radius: $br-8; - svg { - @extend .button-icon; - stroke: var(--icon-foreground); - fill: var(--title-foreground-color-hover); - } +.special-title { + @include textEllipsis; + color: var(--title-foreground-color-hover); + margin-left: $s-2; + text-align: left; +} + +.file-link { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + border-radius: $br-8; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + fill: var(--title-foreground-color-hover); } } + .library-content { display: flex; flex-direction: column; @@ -53,22 +53,23 @@ flex-direction: column; align-items: center; gap: $s-8; - .no-found-icon { - @include flexCenter; - background-color: var(--not-found-background-color); - border-radius: $br-circle; - height: $s-48; - width: $s-48; - svg { - @extend .button-icon; - height: $s-24; - width: $s-24; - stroke: var(--not-found-foreground-color); - } - } +} - .no-found-text { - @include titleTipography; - color: var(--not-found-foreground-color); +.no-found-icon { + @include flexCenter; + background-color: var(--not-found-background-color); + border-radius: $br-circle; + height: $s-48; + width: $s-48; + svg { + @extend .button-icon; + height: $s-24; + width: $s-24; + stroke: var(--not-found-foreground-color); } } + +.no-found-text { + @include titleTipography; + color: var(--not-found-foreground-color); +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/groups.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.cljs index 0dd0b0b47e..2705c891cb 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/groups.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.cljs @@ -14,7 +14,6 @@ [app.main.store :as st] [app.main.ui.components.forms :as fm] [app.main.ui.components.title-bar :refer [title-bar]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.ui.workspace.sidebar.assets.common :as cmm] [app.util.dom :as dom] @@ -27,7 +26,6 @@ (when-not (empty? path) (let [[other-path last-path truncated] (cfh/compact-path path 35 true) menu-state (mf/use-state cmm/initial-context-menu-state) - new-css-system (mf/use-ctx ctx/new-css-system) on-fold-group (mf/use-fn (mf/deps file-id section path group-open?) @@ -46,45 +44,28 @@ on-close-menu (mf/use-fn #(swap! menu-state cmm/close-context-menu))] - (if new-css-system - [:div {:class (stl/css :group-title) - :on-context-menu on-context-menu} - [:& title-bar {:collapsable? true - :collapsed? (not group-open?) - :clickable-all? true - :on-collapsed on-fold-group - :title (mf/html [:* (when-not (empty? other-path) - [:span {:class (stl/css :pre-path) - :title (when truncated path)} - other-path "\u00A0\u2022\u00A0"]) - [:span {:class (stl/css :path) - :title (when truncated path)} - last-path]])}] - [:& cmm/assets-context-menu - {:on-close on-close-menu - :state @menu-state - :options [{:option-name (tr "workspace.assets.rename") - :id "assets-rename-group" - :option-handler #(on-rename % path last-path)} - {:option-name (tr "workspace.assets.ungroup") - :id "assets-ungroup-group" - :option-handler #(on-ungroup path)}]}]] - - - [:div.group-title {:class (when-not group-open? "closed") - :on-click on-fold-group - :on-context-menu on-context-menu} - [:span i/arrow-slide] - (when-not (empty? other-path) - [:span.dim {:title (when truncated path)} - other-path "\u00A0/\u00A0"]) - [:span {:title (when truncated path)} - last-path] - [:& cmm/assets-context-menu - {:on-close on-close-menu - :state @menu-state - :options [[(tr "workspace.assets.rename") #(on-rename % path last-path)] - [(tr "workspace.assets.ungroup") #(on-ungroup path)]]}]])))) + [:div {:class (stl/css :group-title) + :on-context-menu on-context-menu} + [:& title-bar {:collapsable? true + :collapsed? (not group-open?) + :clickable-all? true + :on-collapsed on-fold-group + :title (mf/html [:* (when-not (empty? other-path) + [:span {:class (stl/css :pre-path) + :title (when truncated path)} + other-path "\u00A0\u2022\u00A0"]) + [:span {:class (stl/css :path) + :title (when truncated path)} + last-path]])}] + [:& cmm/assets-context-menu + {:on-close on-close-menu + :state @menu-state + :options [{:option-name (tr "workspace.assets.rename") + :id "assets-rename-group" + :option-handler #(on-rename % path last-path)} + {:option-name (tr "workspace.assets.ungroup") + :id "assets-ungroup-group" + :option-handler #(on-ungroup path)}]}]]))) (defn group-assets "Convert a list of assets in a nested structure like this: @@ -120,8 +101,7 @@ ::mf/register-as :name-group-dialog} [{:keys [path last-path accept] :as ctx :or {path "" last-path ""}}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - initial (mf/use-memo + (let [initial (mf/use-memo (mf/deps last-path) (constantly {:asset-name last-path})) form (fm/use-form :spec ::name-group-form @@ -142,69 +122,36 @@ (accept asset-name) (accept path asset-name)) (modal/hide!))))] - (if new-css-system - [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-container)} - [:div {:class (stl/css :modal-header)} - [:h2 {:class (stl/css :modal-title)} - (if create? - (tr "workspace.assets.create-group") - (tr "workspace.assets.rename-group"))] - [:button {:class (stl/css :modal-close-btn) - :on-click on-close} i/close-refactor]] + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} + (if create? + (tr "workspace.assets.create-group") + (tr "workspace.assets.rename-group"))] + [:button {:class (stl/css :modal-close-btn) + :on-click on-close} i/close-refactor]] - [:div {:class (stl/css :modal-content)} - [:& fm/form {:form form :on-submit on-accept} - [:& fm/input {:name :asset-name - :class (stl/css :input-wrapper) - :auto-focus? true - :label (tr "workspace.assets.group-name") - :hint (tr "workspace.assets.create-group-hint")}]]] + [:div {:class (stl/css :modal-content)} + [:& fm/form {:form form :on-submit on-accept} + [:& fm/input {:name :asset-name + :class (stl/css :input-wrapper) + :auto-focus? true + :label (tr "workspace.assets.group-name") + :hint (tr "workspace.assets.create-group-hint")}]]] - [:div {:class (stl/css :modal-footer)} - [:div {:class (stl/css :action-buttons)} - [:input - {:class (stl/css :cancel-button) - :type "button" - :value (tr "labels.cancel") - :on-click on-close}] + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:input + {:class (stl/css :cancel-button) + :type "button" + :value (tr "labels.cancel") + :on-click on-close}] - [:input - {:type "button" - :class (stl/css-case :accept-btn true - :global/disabled (not (:valid @form) )) - :disabled (not (:valid @form)) - :value (if create? (tr "labels.create") (tr "labels.rename")) - :on-click on-accept}]]]]] - - - [:div.modal-overlay - [:div.modal-container.confirm-dialog - [:div.modal-header - [:div.modal-header-title - [:h2 (if create? - (tr "workspace.assets.create-group") - (tr "workspace.assets.rename-group"))]] - [:div.modal-close-button - {:on-click on-close} i/close]] - - [:div.modal-content.generic-form - [:& fm/form {:form form :on-submit on-accept} - [:& fm/input {:name :asset-name - :auto-focus? true - :label (tr "workspace.assets.group-name") - :hint (tr "workspace.assets.create-group-hint")}]]] - - [:div.modal-footer - [:div.action-buttons - [:input.cancel-button - {:type "button" - :value (tr "labels.cancel") - :on-click on-close}] - - [:input.accept-button.primary - {:type "button" - :class (when-not (:valid @form) "btn-disabled") - :disabled (not (:valid @form)) - :value (if create? (tr "labels.create") (tr "labels.rename")) - :on-click on-accept}]]]]]))) + [:input + {:type "button" + :class (stl/css-case :accept-btn true + :global/disabled (not (:valid @form))) + :disabled (not (:valid @form)) + :value (if create? (tr "labels.create") (tr "labels.rename")) + :on-click on-accept}]]]]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/groups.scss b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.scss index 93235d3bae..c3091a7cb2 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/groups.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.scss @@ -8,52 +8,57 @@ .group-title { padding-left: $s-4; - .pre-path { - margin-left: $s-2; - text-transform: initial; - color: var(--title-foreground-color); - } - .path { - margin-left: $s-2; - text-transform: initial; - color: var(--title-foreground-color-hover); - } +} + +.pre-path { + margin-left: $s-2; + text-transform: initial; + color: var(--title-foreground-color); +} + +.path { + margin-left: $s-2; + text-transform: initial; + color: var(--title-foreground-color-hover); } .modal-overlay { @extend .modal-overlay-base; - .modal-container { - @extend .modal-container-base; - .modal-header { - margin-bottom: $s-24; - .modal-title { - @include tabTitleTipography; - color: var(--modal-title-foreground-color); - } - .modal-close-btn { - @extend .modal-close-btn-base; - } - } - .modal-content { - @include titleTipography; - margin-bottom: $s-24; - .input-wrapper { - @extend .input-with-label; - } - } - .modal-footer { - .action-buttons { - @extend .modal-action-btns; - .cancel-button { - @extend .modal-cancel-btn; - } - .accept-btn { - @extend .modal-accept-btn; - &.danger { - @extend .modal-danger-btn; - } - } - } - } +} + +.modal-container { + @extend .modal-container-base; +} + +.modal-header { + margin-bottom: $s-24; +} + +.modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); +} +.modal-close-btn { + @extend .modal-close-btn-base; +} + +.modal-content { + @include titleTipography; + margin-bottom: $s-24; +} +.input-wrapper { + @extend .input-with-label; +} +.action-buttons { + @extend .modal-action-btns; +} +.cancel-button { + @extend .modal-cancel-btn; +} + +.accept-btn { + @extend .modal-accept-btn; + &.danger { + @extend .modal-danger-btn; } } diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.cljs index 5708709d33..d85b1979cb 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.cljs @@ -46,7 +46,6 @@ dragging? (deref dragging*) read-only? (mf/use-ctx ctx/workspace-read-only?) - new-css-system (mf/use-ctx ctx/new-css-system) editing? (= editing-id (:id typography)) renaming? (= renaming-id (:id typography)) @@ -100,52 +99,29 @@ (mf/deps typography apply-typography on-asset-click) (partial on-asset-click typography-id apply-typography))] - (if ^boolean new-css-system - [:div {:class (stl/css :typography-item) - :ref item-ref - :draggable (and (not read-only?) (not open?)) - :on-drag-start on-typography-drag-start - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} + [:div {:class (stl/css :typography-item) + :ref item-ref + :draggable (and (not read-only?) (not open?)) + :on-drag-start on-typography-drag-start + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} - [:& typography-entry - {:typography typography - :local? local? - :on-context-menu on-context-menu - :on-change handle-change - :selected? (contains? selected typography-id) - :on-click on-asset-click - :editing? editing? - :renaming? renaming? - :focus-name? rename? - :external-open* open* - :file-id file-id}] - (when ^boolean dragging? - [:div {:class (stl/css :dragging)}])] - - [:div.typography-container {:ref item-ref - :draggable (and (not read-only?) (not open?)) - :on-drag-start on-typography-drag-start - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - [:& typography-entry - {:typography typography - :local? local? - :on-context-menu on-context-menu - :on-change handle-change - :selected? (contains? selected typography-id) - :on-click on-asset-click - :editing? editing? - :focus-name? rename? - :external-open* open* - :file-id file-id}] - - (when ^boolean dragging? - [:div.dragging])]))) + [:& typography-entry + {:typography typography + :local? local? + :on-context-menu on-context-menu + :on-change handle-change + :selected? (contains? selected typography-id) + :on-click on-asset-click + :editing? editing? + :renaming? renaming? + :focus-name? rename? + :external-open* open* + :file-id file-id}] + (when ^boolean dragging? + [:div {:class (stl/css :dragging)}])])) (mf/defc typographies-group {::mf/wrap-props false} @@ -155,7 +131,6 @@ (let [group-open? (get open-groups prefix true) dragging* (mf/use-state false) dragging? (deref dragging*) - new-css-system (mf/use-ctx ctx/new-css-system) selected-paths (mf/with-memo [selected-full] (into #{} (comp (map :path) (d/nilv "")) @@ -183,134 +158,72 @@ (fn [event] (cmm/on-drop-asset-group event dragging* prefix selected-paths selected-full move-typography)))] - (if ^boolean new-css-system - [:div {:class (stl/css :typographies-group) - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - [:& grp/asset-group-title {:file-id file-id - :section :typographies - :path prefix - :group-open? group-open? - :on-rename on-rename-group - :on-ungroup on-ungroup}] + [:div {:class (stl/css :typographies-group) + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} + [:& grp/asset-group-title {:file-id file-id + :section :typographies + :path prefix + :group-open? group-open? + :on-rename on-rename-group + :on-ungroup on-ungroup}] - (when group-open? - [:* - (let [typographies (get groups "" [])] - [:div {:class (stl/css :assets-list) - :on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} + (when group-open? + [:* + (let [typographies (get groups "" [])] + [:div {:class (stl/css :assets-list) + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over dom/prevent-default + :on-drop on-drop} - (when ^boolean dragging? - [:div {:class (stl/css :grid-placeholder)} "\u00A0"]) + (when ^boolean dragging? + [:div {:class (stl/css :grid-placeholder)} "\u00A0"]) - (when (and - (empty? typographies) - (some? groups)) - [:div {:class (stl/css :drop-space)}]) - (for [{:keys [id] :as typography} typographies] - [:& typography-item {:typography typography - :key (dm/str "typography-" id) - :file-id file-id + (when (and + (empty? typographies) + (some? groups)) + [:div {:class (stl/css :drop-space)}]) + (for [{:keys [id] :as typography} typographies] + [:& typography-item {:typography typography + :key (dm/str "typography-" id) + :file-id file-id + :local? local? + :handle-change handle-change + :selected selected + :apply-typography apply-typography + :editing-id editing-id + :renaming-id renaming-id + :rename? (= (:rename-typography local-data) id) + :on-asset-click on-asset-click + :on-context-menu on-context-menu + :selected-full selected-full + :selected-paths selected-paths + :move-typography move-typography}])]) + + (for [[path-item content] groups] + (when-not (empty? path-item) + [:& typographies-group {:file-id file-id + :prefix (cfh/merge-path-item prefix path-item) + :key (dm/str "group-" path-item) + :groups content + :open-groups open-groups + :force-open? force-open? + :file file :local? local? - :handle-change handle-change :selected selected - :apply-typography apply-typography :editing-id editing-id :renaming-id renaming-id - :rename? (= (:rename-typography local-data) id) + :local-data local-data :on-asset-click on-asset-click - :on-context-menu on-context-menu - :selected-full selected-full - :selected-paths selected-paths - :move-typography move-typography}])]) - - (for [[path-item content] groups] - (when-not (empty? path-item) - [:& typographies-group {:file-id file-id - :prefix (cfh/merge-path-item prefix path-item) - :key (dm/str "group-" path-item) - :groups content - :open-groups open-groups - :force-open? force-open? - :file file - :local? local? - :selected selected - :editing-id editing-id - :renaming-id renaming-id - :local-data local-data - :on-asset-click on-asset-click - :handle-change handle-change - :apply-typography apply-typography - :on-rename-group on-rename-group - :on-ungroup on-ungroup - :on-context-menu on-context-menu - :selected-full selected-full}]))])] - [:div {:on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - [:& grp/asset-group-title {:file-id file-id - :section :typographies - :path prefix - :group-open? group-open? - :on-rename on-rename-group - :on-ungroup on-ungroup}] - (when group-open? - [:* - (let [typographies (get groups "" [])] - [:div.asset-list {:on-drag-enter on-drag-enter - :on-drag-leave on-drag-leave - :on-drag-over dom/prevent-default - :on-drop on-drop} - - (when ^boolean dragging? - [:div.grid-placeholder "\u00A0"]) - - (when (and - (empty? typographies) - (some? groups)) - [:div.drop-space]) - (for [{:keys [id] :as typography} typographies] - [:& typography-item {:typography typography - :key (dm/str "typography-" id) - :file-id file-id - :local? local? :handle-change handle-change - :selected selected :apply-typography apply-typography - :editing-id editing-id - :rename? (= (:rename-typography local-data) id) - :on-asset-click on-asset-click + :on-rename-group on-rename-group + :on-ungroup on-ungroup :on-context-menu on-context-menu - :selected-full selected-full - :selected-paths selected-paths - :move-typography move-typography}])]) - - (for [[path-item content] groups] - (when-not (empty? path-item) - [:& typographies-group {:file-id file-id - :prefix (cfh/merge-path-item prefix path-item) - :key (dm/str "group-" path-item) - :groups content - :open-groups open-groups - :force-open? force-open? - :file file - :local? local? - :selected selected - :editing-id editing-id - :local-data local-data - :on-asset-click on-asset-click - :handle-change handle-change - :apply-typography apply-typography - :on-rename-group on-rename-group - :on-ungroup on-ungroup - :on-context-menu on-context-menu - :selected-full selected-full}]))])]))) + :selected-full selected-full}]))])])) (mf/defc typographies-section {::mf/wrap-props false} @@ -320,7 +233,6 @@ local-data (mf/deref lens:typography-section-state) read-only? (mf/use-ctx ctx/workspace-read-only?) - new-css-system (mf/use-ctx ctx/new-css-system) menu-state (mf/use-state cmm/initial-context-menu-state) typographies (mf/with-memo [typographies] (mapv dwl/extract-path-if-missing typographies)) @@ -465,10 +377,7 @@ (dwl/sync-file file-id file-id :typographies (:id @state)) (dwu/commit-undo-transaction undo-id)))))) - editing-id (if new-css-system - (:edit-typography local-data) - (or (:rename-typography local-data) - (:edit-typography local-data))) + editing-id (:edit-typography local-data) renaming-id (:rename-typography local-data) @@ -478,10 +387,8 @@ (partial on-asset-click groups))] (mf/use-effect - (mf/deps local-data new-css-system) + (mf/deps local-data ) (fn [] - (when (and (not new-css-system)(:rename-typography local-data)) - (st/emit! #(update % :workspace-global dissoc :rename-typography))) (when (:edit-typography local-data) (st/emit! #(update % :workspace-global dissoc :edit-typography))))) @@ -491,19 +398,12 @@ :section :typographies :assets-count (count typographies) :open? open?} - (if ^boolean new-css-system - (when local? - [:& cmm/asset-section-block {:role :title-button} - (when-not read-only? - [:button {:class (stl/css :assets-btn) - :on-click add-typography} - i/add-refactor])]) - - (when local? - [:& cmm/asset-section-block {:role :title-button} - (when-not read-only? - [:div.assets-button {:on-click add-typography} - i/plus])])) + (when local? + [:& cmm/asset-section-block {:role :title-button} + (when-not read-only? + [:button {:class (stl/css :assets-btn) + :on-click add-typography} + i/add-refactor])]) [:& cmm/asset-section-block {:role :content} [:& typographies-group {:file-id file-id @@ -530,38 +430,28 @@ [:& cmm/assets-context-menu {:on-close on-close-menu :state @menu-state - :options (if new-css-system - [(when-not (or multi-typographies? multi-assets?) - {:option-name (tr "workspace.assets.rename") - :id "assets-rename-typography" - :option-handler handle-rename-typography-clicked}) - - (when-not (or multi-typographies? multi-assets?) - {:option-name (tr "workspace.assets.edit") - :id "assets-edit-typography" - :option-handler handle-edit-typography-clicked}) - - {:option-name (tr "workspace.assets.delete") - :id "assets-delete-typography" - :option-handler handle-delete-typography} - - (when-not multi-assets? - {:option-name (tr "workspace.assets.group") - :id "assets-group-typography" - :option-handler on-group})] - - [(when-not (or multi-typographies? multi-assets?) - [(tr "workspace.assets.rename") handle-rename-typography-clicked]) - (when-not (or multi-typographies? multi-assets?) - [(tr "workspace.assets.edit") handle-edit-typography-clicked]) - [(tr "workspace.assets.delete") handle-delete-typography] - (when-not multi-assets? - [(tr "workspace.assets.group") on-group])])}] - - (when new-css-system - [:& cmm/assets-context-menu - {:on-close on-close-menu - :state @menu-state - :options [{:option-name "show info" + :options [(when-not (or multi-typographies? multi-assets?) + {:option-name (tr "workspace.assets.rename") :id "assets-rename-typography" - :option-handler handle-edit-typography-clicked}]}]))]]])) + :option-handler handle-rename-typography-clicked}) + + (when-not (or multi-typographies? multi-assets?) + {:option-name (tr "workspace.assets.edit") + :id "assets-edit-typography" + :option-handler handle-edit-typography-clicked}) + + {:option-name (tr "workspace.assets.delete") + :id "assets-delete-typography" + :option-handler handle-delete-typography} + + (when-not multi-assets? + {:option-name (tr "workspace.assets.group") + :id "assets-group-typography" + :option-handler on-group})]}] + + [:& cmm/assets-context-menu + {:on-close on-close-menu + :state @menu-state + :options [{:option-name "show info" + :id "assets-rename-typography" + :option-handler handle-edit-typography-clicked}]}])]]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.scss b/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.scss index 01a0bb172d..e0ad31a23a 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/typographies.scss @@ -17,34 +17,37 @@ stroke: var(--icon-foreground); } } -.typographies-group { - .assets-list { - padding: 0 0 0 $s-4; - .drop-space { - height: $s-12; - } - .grid-placeholder { - height: $s-2; - width: 100%; - background-color: var(--color-accent-primary); - } - .typography-item { - position: relative; - display: flex; - align-items: center; - margin-bottom: $s-4; - border-radius: $br-8; - background-color: var(--assets-item-background-color); - } - .dragging { - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; - border: $s-2 solid var(--assets-item-border-color-drag); - border-radius: $s-8; - background-color: var(--assets-item-background-color-drag); - } - } + +.assets-list { + padding: 0 0 0 $s-4; +} + +.drop-space { + height: $s-12; +} + +.grid-placeholder { + height: $s-2; + width: 100%; + background-color: var(--color-accent-primary); +} + +.typography-item { + position: relative; + display: flex; + align-items: center; + margin-bottom: $s-4; + border-radius: $br-8; + background-color: var(--assets-item-background-color); +} + +.dragging { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + border: $s-2 solid var(--assets-item-border-color-drag); + border-radius: $s-8; + background-color: var(--assets-item-background-color-drag); } diff --git a/frontend/src/app/main/ui/workspace/sidebar/collapsable_button.cljs b/frontend/src/app/main/ui/workspace/sidebar/collapsable_button.cljs index f292e9ad4c..643114f07f 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/collapsable_button.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/collapsable_button.cljs @@ -5,31 +5,23 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.workspace.sidebar.collapsable-button - (:require-macros [app.main.style :refer [css]]) + (:require-macros [app.main.style :as stl]) (:require [app.main.data.workspace :as dw] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] - [app.util.dom :as dom] [app.util.i18n :refer [tr]] [rumext.v2 :as mf])) (mf/defc collapsed-button {::mf/wrap-props false} [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - on-click (mf/use-fn #(st/emit! (dw/toggle-layout-flag :collapse-left-sidebar)))] - (if ^boolean new-css-system - [:div {:id "left-sidebar-aside" - :data-size 0 - :class (dom/classnames (css :collapsed-sidebar) true)} - [:div {:class (dom/classnames (css :collapsed-title) true)} - [:button {:class (dom/classnames (css :collapsed-button) true) - :on-click on-click - :aria-label (tr "workspace.sidebar.expand")} - i/arrow-refactor]]] - [:button.collapse-sidebar.collapsed - {:on-click on-click - :aria-label (tr "workspace.sidebar.expand")} - i/arrow-slide]))) + (let [on-click (mf/use-fn #(st/emit! (dw/toggle-layout-flag :collapse-left-sidebar)))] + [:div {:id "left-sidebar-aside" + :data-size 0 + :class (stl/css :collapsed-sidebar)} + [:div {:class (stl/css :collapsed-title)} + [:button {:class (stl/css :collapsed-button) + :on-click on-click + :aria-label (tr "workspace.sidebar.expand")} + i/arrow-refactor]]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/debug.cljs b/frontend/src/app/main/ui/workspace/sidebar/debug.cljs index 5d7932a898..71adc0d998 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/debug.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/debug.cljs @@ -5,6 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.workspace.sidebar.debug + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.main.data.workspace :as dw] @@ -24,26 +25,27 @@ (dbg/toggle! option) (js* "app.main.reinit()"))) - on-close + handle-close (mf/use-fn (fn [] (st/emit! (dw/remove-layout-flag :debug-panel))))] - [:div.debug-panel - [:div.debug-panel-header - [:div.debug-panel-close-button - {:on-click on-close} i/close] - [:div.debug-panel-title "Debugging tools"]] - [:div.debug-panel-inner + [:div {:class (stl/css :debug-panel)} + [:div {:class (stl/css :panel-title)} + [:span "Debugging tools"] + [:div {:class (stl/css :close-button) :on-click handle-close} + i/close-refactor]] + + [:div {:class (stl/css :debug-panel-inner)} (for [option (sort-by d/name dbg/options)] - [:div.debug-option {:key (d/name option) - :on-click #(on-toggle-enabled % option)} + [:div {:class (stl/css :checkbox-wrapper)} + [:span {:class (stl/css-case :checkbox-icon true :global/checked (dbg/enabled? option)) + :on-click #(on-toggle-enabled % option)} + (when (dbg/enabled? option) i/status-tick-refactor)] + [:input {:type "checkbox" :id (d/name option) + :key (d/name option) :on-change #(on-toggle-enabled % option) :checked (dbg/enabled? option)}] - [:div.field.check - (if (dbg/enabled? option) - [:span.checked i/checkbox-checked] - [:span.unchecked i/checkbox-unchecked])] [:label {:for (d/name option)} (d/name option)]])]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/debug.scss b/frontend/src/app/main/ui/workspace/sidebar/debug.scss new file mode 100644 index 0000000000..96364d88ee --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/debug.scss @@ -0,0 +1,59 @@ +// 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 + +@use "common/refactor/common-refactor.scss" as *; + +.debug-panel { + display: flex; + flex-direction: column; + background-color: var(--panel-background-color); +} + +.panel-title { + @include flexCenter; + @include tabTitleTipography; + position: relative; + height: $s-32; + min-height: $s-32; + margin: $s-8 $s-8 0 $s-8; + border-radius: $br-8; + background-color: var(--panel-title-background-color); + + span { + @include flexCenter; + flex-grow: 1; + color: var(--title-foreground-color-hover); + } +} + +.close-button { + @extend .button-tertiary; + position: absolute; + right: $s-2; + top: $s-2; + height: $s-28; + width: $s-28; + border-radius: $br-6; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } +} + +.checkbox-wrapper { + @extend .input-checkbox; + height: $s-32; + padding: 0; +} + +.checkbox-icon { + @extend .checkbox-icon; + cursor: pointer; +} + +.debug-panel-inner { + padding: $s-16 $s-8; +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/history.cljs b/frontend/src/app/main/ui/workspace/sidebar/history.cljs index 67cb6075d8..4c292d905d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/history.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/history.cljs @@ -5,7 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.workspace.sidebar.history - (:require-macros [app.main.style :refer [css]]) + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.main.data.events :as ev] @@ -13,9 +13,7 @@ [app.main.data.workspace.common :as dwc] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] - [app.util.dom :as dom] [app.util.i18n :refer [t] :as i18n] [cuerdas.core :as str] [okulary.core :as l] @@ -149,23 +147,6 @@ (t locale "workspace.undo.entry.unknown" value)))) (defn entry->icon [{:keys [type]}] - (case type - :page i/file-html - :shape i/layers - :rect i/box - :circle i/circle - :text i/text - :path i/curve - :frame i/artboard - :group i/folder - :color i/palette - :typography i/titlecase - :component i/component - :media i/image - :image i/image - i/layers)) - -(defn entry->icon-refactor [{:keys [type]}] (case type :page i/document-refactor :shape i/svg-refactor @@ -277,103 +258,58 @@ (mf/defc history-entry-details [{:keys [entry]}] (let [{entries :items} (mf/deref workspace-undo) - new-css-system (mf/use-ctx ctx/new-css-system) objects (mf/deref refs/workspace-page-objects)] - (if new-css-system - [:div {:class (css :history-entry-detail)} - (case (:operation entry) - :new - (:name (get-object (:detail entry) entries objects)) + [:div {:class (stl/css :history-entry-detail)} + (case (:operation entry) + :new + (:name (get-object (:detail entry) entries objects)) - :delete - [:ul {:class (css :ul.history-entry-details-list)} - (for [id (:detail entry)] - (let [shape-name (:name (get-object id entries objects))] - [:li {:key id} shape-name]))] + :delete + [:ul {:class (stl/css :history-entry-details-list)} + (for [id (:detail entry)] + (let [shape-name (:name (get-object id entries objects))] + [:li {:key id} shape-name]))] - :modify - [:ul {:class (css :ul.history-entry-details-list)} - (for [[id attributes] (:detail entry)] - (let [shape-name (:name (get-object id entries objects))] - [:li {:key id} - [:div shape-name] - [:div (str/join ", " attributes)]]))] + :modify + [:ul {:class (stl/css :history-entry-details-list)} + (for [[id attributes] (:detail entry)] + (let [shape-name (:name (get-object id entries objects))] + [:li {:key id} + [:div shape-name] + [:div (str/join ", " attributes)]]))] - nil)] - - [:div.history-entry-detail - (case (:operation entry) - :new - (:name (get-object (:detail entry) entries objects)) - - :delete - [:ul.history-entry-details-list - (for [id (:detail entry)] - (let [shape-name (:name (get-object id entries objects))] - [:li {:key id} shape-name]))] - - - :modify - [:ul.history-entry-details-list - (for [[id attributes] (:detail entry)] - (let [shape-name (:name (get-object id entries objects))] - [:li {:key id} - [:div shape-name] - [:div (str/join ", " attributes)]]))] - - nil)] - ))) + nil)])) (mf/defc history-entry [{:keys [locale entry idx-entry disabled? current?]}] (let [hover? (mf/use-state false) - new-css-system (mf/use-ctx ctx/new-css-system) show-detail? (mf/use-state false)] - (if new-css-system - [:div {:class (dom/classnames (css :history-entry) true - (css :disabled) disabled? - (css :current) current? - (css :hover) @hover? - (css :show-detail) @show-detail?) - :on-pointer-enter #(reset! hover? true) - :on-pointer-leave #(reset! hover? false) - :on-click #(st/emit! (dwc/undo-to-index idx-entry))} - [:div {:class (dom/classnames (css :history-entry-summary) true)} - [:div {:class (dom/classnames (css :history-entry-summary-icon) true)} (entry->icon-refactor entry)] - [:div {:class (dom/classnames (css :history-entry-summary-text) true)} (entry->message locale entry)] - (when (:detail entry) - [:div {:class (dom/classnames (css :history-entry-summary-button) true - (css :button-opened) @show-detail?) - :on-click #(when (:detail entry) - (swap! show-detail? not))} - i/arrow-refactor])] + [:div {:class (stl/css-case :history-entry true + :disabled disabled? + :current current? + :hover @hover? + :show-detail @show-detail?) + :on-pointer-enter #(reset! hover? true) + :on-pointer-leave #(reset! hover? false) + :on-click #(st/emit! (dwc/undo-to-index idx-entry))} - (when @show-detail? - [:& history-entry-details {:entry entry}])] + [:div {:class (stl/css :history-entry-summary)} + [:div {:class (stl/css :history-entry-summary-icon)} + (entry->icon entry)] + [:div {:class (stl/css :history-entry-summary-text)} (entry->message locale entry)] + (when (:detail entry) + [:div {:class (stl/css-case :history-entry-summary-button true + :button-opened @show-detail?) + :on-click #(when (:detail entry) + (swap! show-detail? not))} + i/arrow-refactor])] - [:div.history-entry {:class (dom/classnames - :disabled disabled? - :current current? - :hover @hover? - :show-detail @show-detail?) - :on-pointer-enter #(reset! hover? true) - :on-pointer-leave #(reset! hover? false) - :on-click #(st/emit! (dwc/undo-to-index idx-entry))} - [:div.history-entry-summary - [:div.history-entry-summary-icon (entry->icon entry)] - [:div.history-entry-summary-text (entry->message locale entry)] - (when (:detail entry) - [:div.history-entry-summary-button {:on-click #(when (:detail entry) - (swap! show-detail? not))} - i/arrow-slide])] - - (when show-detail? - [:& history-entry-details {:entry entry}])]))) + (when @show-detail? + [:& history-entry-details {:entry entry}])])) (mf/defc history-toolbox [] (let [locale (mf/deref i18n/locale) - new-css-system (mf/use-ctx ctx/new-css-system) objects (mf/deref refs/workspace-page-objects) {:keys [items index]} (mf/deref workspace-undo) entries (parse-entries items objects) @@ -381,40 +317,24 @@ (mf/use-fn #(st/emit! (-> (dw/toggle-layout-flag :document-history) (vary-meta assoc ::ev/origin "history-toolbox"))))] - (if new-css-system - [:div {:class (css :history-toolbox)} - [:div {:class (css :history-toolbox-title)} - [:span (t locale "workspace.undo.title")] - [:div {:class (css :close-button) - :on-click toggle-history} - i/close-refactor]] - (if (empty? entries) - [:div {:class (css :history-entry-empty)} - [:div {:class (css :history-entry-empty-icon)} i/history-refactor] - [:div {:class (css :history-entry-empty-msg)} (t locale "workspace.undo.empty")]] - [:ul {:class (css :history-entries)} - (for [[idx-entry entry] (->> entries (map-indexed vector) reverse)] #_[i (range 0 10)] - [:& history-entry {:key (str "entry-" idx-entry) - :locale locale - :entry entry - :idx-entry idx-entry - :current? (= idx-entry index) - :disabled? (> idx-entry index)}])])] - - [:div.history-toolbox - [:div.history-toolbox-title (t locale "workspace.undo.title")] - (if (empty? entries) - [:div.history-entry-empty - [:div.history-entry-empty-icon i/recent] - [:div.history-entry-empty-msg (t locale "workspace.undo.empty")]] - [:ul.history-entries - (for [[idx-entry entry] (->> entries (map-indexed vector) reverse)] #_[i (range 0 10)] - [:& history-entry {:key (str "entry-" idx-entry) - :locale locale - :entry entry - :idx-entry idx-entry - :current? (= idx-entry index) - :disabled? (> idx-entry index)}])])]))) + [:div {:class (stl/css :history-toolbox)} + [:div {:class (stl/css :history-toolbox-title)} + [:span (t locale "workspace.undo.title")] + [:div {:class (stl/css :close-button) + :on-click toggle-history} + i/close-refactor]] + (if (empty? entries) + [:div {:class (stl/css :history-entry-empty)} + [:div {:class (stl/css :history-entry-empty-icon)} i/history-refactor] + [:div {:class (stl/css :history-entry-empty-msg)} (t locale "workspace.undo.empty")]] + [:ul {:class (stl/css :history-entries)} + (for [[idx-entry entry] (->> entries (map-indexed vector) reverse)] #_[i (range 0 10)] + [:& history-entry {:key (str "entry-" idx-entry) + :locale locale + :entry entry + :idx-entry idx-entry + :current? (= idx-entry index) + :disabled? (> idx-entry index)}])])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/history.scss b/frontend/src/app/main/ui/workspace/sidebar/history.scss index 1870531c2f..5cebfbcb0d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/history.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/history.scss @@ -10,138 +10,139 @@ display: flex; flex-direction: column; background-color: var(--panel-background-color); +} - .history-toolbox-title { +.history-toolbox-title { + @include flexCenter; + @include tabTitleTipography; + position: relative; + height: $s-32; + min-height: $s-32; + margin: $s-8 $s-8 0 $s-8; + border-radius: $br-8; + background-color: var(--panel-title-background-color); + + span { @include flexCenter; - @include tabTitleTipography; - position: relative; - height: $s-32; - min-height: $s-32; - margin: $s-8 $s-8 0 $s-8; - border-radius: $br-8; - background-color: var(--panel-title-background-color); + flex-grow: 1; + color: var(--title-foreground-color-hover); + } +} - span { - @include flexCenter; - flex-grow: 1; - color: var(--title-foreground-color-hover); - } +.close-button { + @extend .button-tertiary; + position: absolute; + right: $s-2; + top: $s-2; + height: $s-28; + width: $s-28; + border-radius: $br-6; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } +} - .close-button { - @extend .button-tertiary; - position: absolute; - right: $s-2; - top: $s-2; - height: $s-28; - width: $s-28; - border-radius: $br-6; +.history-entry-empty { + @include flexCenter; + flex-direction: column; + gap: $s-16; + padding: $s-28 $s-16; + text-align: center; +} + +.history-entry-empty-icon { + @extend .empty-icon; + svg { + margin-left: calc(-1 * $s-2); + } +} + +.history-entry-empty-msg { + @include titleTipography; + color: var(--empty-message-foreground-color); +} + +.history-entries { + height: 100%; + padding: $s-12; + overflow-x: hidden; + overflow-y: auto; + font-size: $fs-12; +} + +.history-entry { + display: flex; + justify-content: center; + flex-direction: column; + min-height: $s-32; + margin: $s-4; + padding: $s-4 $s-8; + border: $s-2 solid transparent; + border-radius: $s-8; + background-color: var(--entry-background-color); + cursor: pointer; + transition: border 0.2s; + + .history-entry-summary { + display: flex; + align-items: center; + .history-entry-summary-icon { svg { - @extend .button-icon; - stroke: var(--icon-foreground); + @extend .button-icon-small; + stroke: var(--entry-foreground-color); + } + } + .history-entry-summary-text { + margin: 0 $s-8; + color: $df-primary; + } + .history-entry-summary-button { + opacity: $op-0; + margin-left: auto; + &.button-opened { + svg { + transform: rotate(90deg); + } + } + svg { + @extend .button-icon-small; + stroke: var(--entry-foreground-color); } } } - .history-entry-empty { - @include flexCenter; - flex-direction: column; - gap: $s-16; - padding: $s-28 $s-16; - text-align: center; - - .history-entry-empty-icon { - @extend .empty-icon; - svg { - margin-left: calc(-1 * $s-2); - } - } - - .history-entry-empty-msg { - @include titleTipography; - color: var(--title-foreground-secondary); + .history-entry-detail { + display: block; + padding-top: $s-16; + color: var(--modal-text-foreground-color); + .history-entry-details-list { + margin: 0; } } - ul.history-entries { - height: 100%; - padding: $s-12; - overflow-x: hidden; - overflow-y: auto; - font-size: $fs-12; + &.disabled { + border-color: var(--entry-border-color-disabled); + background-color: var(--entry-background-color-disabled); + } - .history-entry { - display: flex; - justify-content: center; - flex-direction: column; - min-height: $s-32; - margin: $s-4; - padding: $s-4 $s-8; - border: $s-2 solid transparent; - border-radius: $s-8; - background-color: var(--entry-background-color); - cursor: pointer; - transition: border 0.2s; - - &.disabled { - border-color: var(--entry-border-color-disabled); - background-color: var(--entry-background-color-disabled); - } - - &.hover, - &:hover { - background-color: var(--entry-background-color-hover); - color: var(--entry-foreground-color-hover); - .history-entry-summary { - .history-entry-summary-icon { - svg { - stroke: var(--entry-foreground-color-hover); - } - } - .history-entry-summary-button { - opacity: $op-10; - &.button-opened { - svg { - transform: rotate(90deg); - } - } - } + &.hover, + &:hover { + background-color: var(--entry-background-color-hover); + color: var(--entry-foreground-color-hover); + .history-entry-summary { + .history-entry-summary-icon { + svg { + stroke: var(--entry-foreground-color-hover); } } - - .history-entry-summary { - display: flex; - align-items: center; - .history-entry-summary-icon { + .history-entry-summary-button { + opacity: $op-10; + &.button-opened { svg { - @extend .button-icon-small; - stroke: var(--entry-foreground-color); + transform: rotate(90deg); } } - .history-entry-summary-text { - margin: 0 $s-8; - } - .history-entry-summary-button { - opacity: $op-0; - margin-left: auto; - &.button-opened { - svg { - transform: rotate(90deg); - } - } - svg { - @extend .button-icon-small; - stroke: var(--entry-foreground-color); - } - } - } - - .history-entry-detail { - display: block; - padding-top: $s-16; - - ul.history-entry-details-list { - margin: 0; - } } } } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options.cljs b/frontend/src/app/main/ui/workspace/sidebar/options.cljs index 76886baa48..763adb7fbb 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options.cljs @@ -16,7 +16,6 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.tab-container :refer [tab-container tab-element]] - [app.main.ui.components.tabs-container :refer [tabs-container tabs-element]] [app.main.ui.context :as ctx] [app.main.ui.viewer.inspect.right-sidebar :as hrs] [app.main.ui.workspace.sidebar.options.menus.align :refer [align-options]] @@ -78,8 +77,7 @@ (mf/defc options-content {::mf/wrap [mf/memo]} [{:keys [selected section shapes shapes-with-children page-id file-id on-change-section on-expand]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - drawing (mf/deref refs/workspace-drawing) + (let [drawing (mf/deref refs/workspace-drawing) objects (mf/deref refs/workspace-page-objects) shared-libs (mf/deref refs/workspace-libraries) edition (mf/deref refs/selected-edition) @@ -100,144 +98,74 @@ (if (= options-mode :inspect) (st/emit! :interrupt (udw/set-workspace-read-only true)) (st/emit! :interrupt (udw/set-workspace-read-only false))))] - (if ^boolean new-css-system - [:div {:class (stl/css :tool-window)} - [:& tab-container - {:on-change-tab on-change-tab - :selected section - :collapsable? false - :content-class (stl/css :content-class) - :class (stl/css :tab-spacing)} - [:& tab-element {:id :design - :title (tr "workspace.options.design")} - [:div {:class (stl/css :element-options)} - [:& align-options] - [:& bool-options] - (cond - (and edit-grid? (d/not-empty? selected-cells)) - [:& grid-cell/options - {:shape (get objects edition) - :cells selected-cells}] + [:div {:class (stl/css :tool-window)} + [:& tab-container + {:on-change-tab on-change-tab + :selected section + :collapsable? false + :content-class (stl/css :content-class) + :class (stl/css :tab-spacing)} + [:& tab-element {:id :design + :title (tr "workspace.options.design")} + [:div {:class (stl/css :element-options)} + [:& align-options] + [:& bool-options] - edit-grid? - [:& layout-container/grid-layout-edition - {:ids [edition] - :values (get objects edition)}] + (cond + (and edit-grid? (d/not-empty? selected-cells)) + [:& grid-cell/options + {:shape (get objects edition) + :cells selected-cells}] - (not (nil? sp-panel)) - [:& specialized-panel {:panel sp-panel}] + edit-grid? + [:& layout-container/grid-layout-edition + {:ids [edition] + :values (get objects edition)}] - (d/not-empty? drawing) - [:& shape-options - {:shape (:object drawing) - :page-id page-id - :file-id file-id - :shared-libs shared-libs}] + (not (nil? sp-panel)) + [:& specialized-panel {:panel sp-panel}] - (= 0 (count selected)) - [:& page/options] + (d/not-empty? drawing) + [:& shape-options + {:shape (:object drawing) + :page-id page-id + :file-id file-id + :shared-libs shared-libs}] - (= 1 (count selected)) - [:& shape-options - {:shape (first selected-shapes) - :page-id page-id - :file-id file-id - :shared-libs shared-libs - :shapes-with-children shapes-with-children}] + (= 0 (count selected)) + [:& page/options] - :else - [:& multiple/options - {:shapes-with-children shapes-with-children - :shapes selected-shapes - :page-id page-id - :file-id file-id - :shared-libs shared-libs}])]] - [:& tab-element {:id :prototype - :title (tr "workspace.options.prototype")} - [:div {:class (stl/css :element-options)} - [:& interactions-menu {:shape (first shapes)}]]] - [:& tab-element {:id :inspect - :title (tr "workspace.options.inspect")} - [:div {:class (stl/css :element-options)} - [:& hrs/right-sidebar {:page-id page-id - :objects objects - :file-id file-id - :frame shape-parent-frame - :shapes selected-shapes - :on-change-section on-change-section - :on-expand on-expand - :from :workspace}]]]]] + (= 1 (count selected)) + [:& shape-options + {:shape (first selected-shapes) + :page-id page-id + :file-id file-id + :shared-libs shared-libs + :shapes-with-children shapes-with-children}] - [:div.tool-window - [:div.tool-window-content - [:& tabs-container {:on-change-tab on-change-tab - :selected section} - [:& tabs-element {:id :design - :title (tr "workspace.options.design")} - [:div.element-options - [:& align-options] - [:& bool-options] - (cond - (d/not-empty? selected-cells) - [:& grid-cell/options - {:shape (get objects edition) - :cells selected-cells}] - - edit-grid? - [:& layout-container/grid-layout-edition - {:ids [edition] - :values (get objects edition)}] - - sp-panel - [:& specialized-panel {:panel sp-panel}] - - - (d/not-empty? drawing) - [:& shape-options - {:shape (:object drawing) - :page-id page-id - :file-id file-id - :shared-libs shared-libs}] - - (= 0 (count selected)) - [:& page/options] - - (= 1 (count selected)) - [:& shape-options - {:shape (first selected-shapes) - :page-id page-id - :file-id file-id - :shared-libs shared-libs - :shapes-with-children shapes-with-children}] - - :else - [:& multiple/options - {:shapes-with-children shapes-with-children - :shapes selected-shapes - :page-id page-id - :file-id file-id - :shared-libs shared-libs}])]] - - [:& tabs-element - {:id :prototype - :title (tr "workspace.options.prototype")} - - [:div.element-options - [:& interactions-menu {:shape (first shapes)}]]] - - [:& tabs-element {:id :inspect - :title (tr "workspace.options.inspect")} - - [:div.element-options.element-options-inspect - [:& hrs/right-sidebar {:page-id page-id - :objects objects - :file-id file-id - :frame shape-parent-frame - :shapes selected-shapes - :on-change-section on-change-section - :on-expand on-expand - :from :workspace}]]]]]]))) + :else + [:& multiple/options + {:shapes-with-children shapes-with-children + :shapes selected-shapes + :page-id page-id + :file-id file-id + :shared-libs shared-libs}])]] + [:& tab-element {:id :prototype + :title (tr "workspace.options.prototype")} + [:div {:class (stl/css :element-options)} + [:& interactions-menu {:shape (first shapes)}]]] + [:& tab-element {:id :inspect + :title (tr "workspace.options.inspect")} + [:div {:class (stl/css :element-options)} + [:& hrs/right-sidebar {:page-id page-id + :objects objects + :file-id file-id + :frame shape-parent-frame + :shapes selected-shapes + :on-change-section on-change-section + :on-expand on-expand + :from :workspace}]]]]])) ;; TODO: this need optimizations, selected-objects and ;; selected-objects-with-children are derefed always but they only diff --git a/frontend/src/app/main/ui/workspace/sidebar/options.scss b/frontend/src/app/main/ui/workspace/sidebar/options.scss index 46aeb77458..7b67bcf8c6 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options.scss @@ -13,24 +13,24 @@ width: 100%; height: 100%; padding-left: $s-12; - - .tab-spacing { - margin-right: $s-12; - margin-bottom: $s-8; - } - - .content-class { - overflow-y: auto; - overflow-x: hidden; - height: calc(100vh - $s-96); - scrollbar-gutter: stable; - } - - .element-options { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - gap: $s-8; - } +} + +.tab-spacing { + margin-right: $s-12; + margin-bottom: $s-8; +} + +.content-class { + overflow-y: auto; + overflow-x: hidden; + height: calc(100vh - $s-96); + scrollbar-gutter: stable; +} + +.element-options { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + gap: $s-8; } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/common.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/common.cljs index dbe756c224..1efb38e5a0 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/common.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/common.cljs @@ -8,27 +8,19 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] - [app.main.ui.context :as ctx] [app.util.dom :as dom] [rumext.v2 :as mf])) (mf/defc advanced-options [{:keys [visible? class children]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - ref (mf/use-ref nil)] + (let [ref (mf/use-ref nil)] (mf/use-effect (mf/deps visible?) (fn [] (when-let [node (mf/ref-val ref)] (when visible? (dom/scroll-into-view-if-needed! node))))) - (if new-css-system - (when visible? - [:div {:class (dm/str class " " (stl/css :advanced-options-wrapper)) - :ref ref} - children]) - - (when visible? - [:div.advanced-options-wrapper {:ref ref} - [:div.advanced-options {} - children]])))) + (when visible? + [:div {:class (dm/str class " " (stl/css :advanced-options-wrapper)) + :ref ref} + children]))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.cljs index 1632e7b51a..fc7758d99f 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.cljs @@ -11,7 +11,6 @@ [app.main.data.workspace.shortcuts :as sc] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -19,9 +18,8 @@ (mf/defc align-options [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - selected (mf/deref refs/selected-shapes) - ;; don't need to watch objects, only read the value + (let [selected (mf/deref refs/selected-shapes) + ;; don't need to watch objects, only read the value objects (deref refs/workspace-page-objects) disabled-align (not (dw/can-align? selected objects)) @@ -44,130 +42,70 @@ (st/emit! (dw/distribute-objects value)))))] (when (not (and disabled-align disabled-distribute)) - (if new-css-system - [:div {:class (stl/css :align-options)} - [:div {:class (stl/css :align-group)} - [:button {:class (stl/css-case :align-button true - :disabled disabled-align) - :disabled disabled-align - :title (tr "workspace.align.hleft" (sc/get-tooltip :align-left)) - :data-value :hleft - :on-click align-objects} - i/align-left-refactor] + [:div {:class (stl/css :align-options)} + [:div {:class (stl/css :align-group)} + [:button {:class (stl/css-case :align-button true + :disabled disabled-align) + :disabled disabled-align + :title (tr "workspace.align.hleft" (sc/get-tooltip :align-left)) + :data-value :hleft + :on-click align-objects} + i/align-left-refactor] - [:button {:class (stl/css-case :align-button true - :disabled disabled-align) - :disabled disabled-align - :title (tr "workspace.align.hcenter" (sc/get-tooltip :align-hcenter)) - :data-value :hcenter - :on-click align-objects} - i/align-horizontal-center-refactor] + [:button {:class (stl/css-case :align-button true + :disabled disabled-align) + :disabled disabled-align + :title (tr "workspace.align.hcenter" (sc/get-tooltip :align-hcenter)) + :data-value :hcenter + :on-click align-objects} + i/align-horizontal-center-refactor] - [:button {:class (stl/css-case :align-button true - :disabled disabled-align) - :disabled disabled-align - :title (tr "workspace.align.hright" (sc/get-tooltip :align-right)) - :data-value :hright - :on-click align-objects} - i/align-right-refactor] + [:button {:class (stl/css-case :align-button true + :disabled disabled-align) + :disabled disabled-align + :title (tr "workspace.align.hright" (sc/get-tooltip :align-right)) + :data-value :hright + :on-click align-objects} + i/align-right-refactor] - [:button {:class (stl/css-case :align-button true - :disabled disabled-distribute) - :disabled disabled-distribute - :title (tr "workspace.align.hdistribute" (sc/get-tooltip :h-distribute)) - :data-value :horizontal - :on-click distribute-objects} - i/distribute-horizontally-refactor]] + [:button {:class (stl/css-case :align-button true + :disabled disabled-distribute) + :disabled disabled-distribute + :title (tr "workspace.align.hdistribute" (sc/get-tooltip :h-distribute)) + :data-value :horizontal + :on-click distribute-objects} + i/distribute-horizontally-refactor]] - [:div {:class (stl/css :align-group)} - [:button {:class (stl/css-case :align-button true - :disabled disabled-align) - :disabled disabled-align - :title (tr "workspace.align.vtop" (sc/get-tooltip :align-top)) - :data-value :vtop - :on-click align-objects} - i/align-top-refactor] + [:div {:class (stl/css :align-group)} + [:button {:class (stl/css-case :align-button true + :disabled disabled-align) + :disabled disabled-align + :title (tr "workspace.align.vtop" (sc/get-tooltip :align-top)) + :data-value :vtop + :on-click align-objects} + i/align-top-refactor] - [:button {:class (stl/css-case :align-button true - :disabled disabled-align) - :disabled disabled-align - :title (tr "workspace.align.vcenter" (sc/get-tooltip :align-vcenter)) - :data-value :vcenter - :on-click align-objects} - i/align-vertical-center-refactor] + [:button {:class (stl/css-case :align-button true + :disabled disabled-align) + :disabled disabled-align + :title (tr "workspace.align.vcenter" (sc/get-tooltip :align-vcenter)) + :data-value :vcenter + :on-click align-objects} + i/align-vertical-center-refactor] - [:button {:class (stl/css-case :align-button true - :disabled disabled-align) - :disabled disabled-align - :title (tr "workspace.align.vbottom" (sc/get-tooltip :align-bottom)) - :data-value :vbottom - :on-click align-objects} - i/align-bottom-refactor] + [:button {:class (stl/css-case :align-button true + :disabled disabled-align) + :disabled disabled-align + :title (tr "workspace.align.vbottom" (sc/get-tooltip :align-bottom)) + :data-value :vbottom + :on-click align-objects} + i/align-bottom-refactor] - [:button {:title (tr "workspace.align.vdistribute" (sc/get-tooltip :v-distribute)) - :class (stl/css-case :align-button true - :disabled disabled-distribute) - :disabled disabled-distribute - :data-value :vertical - :on-click distribute-objects} - i/distribute-vertical-spacing-refactor]]] - - [:div.align-options - [:div.align-group - [:div.align-button.tooltip.tooltip-bottom - {:alt (tr "workspace.align.hleft" (sc/get-tooltip :align-left)) - :class (when disabled-align "disabled") - :data-value :hleft - :on-click align-objects} - i/shape-halign-left] - - [:div.align-button.tooltip.tooltip-bottom - {:alt (tr "workspace.align.hcenter" (sc/get-tooltip :align-hcenter)) - :class (when disabled-align "disabled") - :data-value :hcenter - :on-click align-objects} - i/shape-halign-center] - - [:div.align-button.tooltip.tooltip-bottom - {:alt (tr "workspace.align.hright" (sc/get-tooltip :align-right)) - :class (when disabled-align "disabled") - :data-value :hright - :on-click align-objects} - i/shape-halign-right] - - [:div.align-button.tooltip.tooltip-bottom - {:alt (tr "workspace.align.hdistribute" (sc/get-tooltip :h-distribute)) - :class (when disabled-distribute "disabled") - :data-value :horizontal - :on-click distribute-objects} - i/shape-hdistribute]] - - [:div.align-group - [:div.align-button.tooltip.tooltip-bottom-left - {:alt (tr "workspace.align.vtop" (sc/get-tooltip :align-top)) - :class (when disabled-align "disabled") - :data-value :vtop - :on-click align-objects} - i/shape-valign-top] - - [:div.align-button.tooltip.tooltip-bottom-left - {:alt (tr "workspace.align.vcenter" (sc/get-tooltip :align-vcenter)) - :class (when disabled-align "disabled") - :data-value :vcenter - :on-click align-objects} - i/shape-valign-center] - - [:div.align-button.tooltip.tooltip-bottom-left - {:alt (tr "workspace.align.vbottom" (sc/get-tooltip :align-bottom)) - :class (when disabled-align "disabled") - :data-value :vbottom - :on-click align-objects} - i/shape-valign-bottom] - - [:div.align-button.tooltip.tooltip-bottom-left - {:alt (tr "workspace.align.vdistribute" (sc/get-tooltip :v-distribute)) - :class (when disabled-distribute "disabled") - :data-value :vertical - :on-click distribute-objects} - i/shape-vdistribute]]])))) + [:button {:title (tr "workspace.align.vdistribute" (sc/get-tooltip :v-distribute)) + :class (stl/css-case :align-button true + :disabled disabled-distribute) + :disabled disabled-distribute + :data-value :vertical + :on-click distribute-objects} + i/distribute-vertical-spacing-refactor]]]))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.scss index d141e7fd59..8294f1d8b7 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.scss @@ -11,29 +11,30 @@ gap: $s-4; height: $s-32; margin: 0 calc(-1 * $s-2); - .align-group { - @include flexRow; - .align-button { - @extend .button-tertiary; - height: $s-28; - width: $s-28; - padding: 0; - border-radius: $br-8; +} +.align-group { + @include flexRow; +} + +.align-button { + @extend .button-tertiary; + height: $s-28; + width: $s-28; + padding: 0; + border-radius: $br-8; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } + &.disabled { + cursor: default; + svg { + stroke: var(--button-background-color-disabled); + } + &:hover { + background-color: var(--panel-background-color); svg { - @extend .button-icon; - stroke: var(--icon-foreground); - } - &.disabled { - cursor: default; - svg { - stroke: var(--button-background-color-disabled); - } - &:hover { - background-color: var(--panel-background-color); - svg { - stroke: var(--button-background-color-disabled); - } - } + stroke: var(--button-background-color-disabled); } } } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/blur.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/blur.cljs index d24bce38a2..989d262b11 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/blur.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/blur.cljs @@ -12,9 +12,7 @@ [app.main.store :as st] [app.main.ui.components.numeric-input :refer [numeric-input*]] [app.main.ui.components.title-bar :refer [title-bar]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] - [app.main.ui.workspace.sidebar.options.rows.input-row :refer [input-row]] [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) @@ -28,10 +26,8 @@ :hidden false})) (mf/defc blur-menu [{:keys [ids type values]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - blur (:blur values) + (let [blur (:blur values) has-value? (not (nil? blur)) - multiple? (= blur :multiple) state* (mf/use-state {:show-content true :show-more-options false}) @@ -79,76 +75,47 @@ (fn [] (change! #(update-in % [:blur :hidden] not))))] - (if new-css-system - [:div {:class (stl/css :element-set)} - [:div {:class (stl/css :element-title)} - [:& title-bar {:collapsable? has-value? - :collapsed? (not open?) - :on-collapsed toggle-content - :title (case type - :multiple (tr "workspace.options.blur-options.title.multiple") - :group (tr "workspace.options.blur-options.title.group") - (tr "workspace.options.blur-options.title")) - :class (stl/css-case :title-spacing-blur (not has-value?))} - (when-not has-value? - [:button {:class (stl/css :add-blur) - :on-click handle-add} i/add-refactor])]] - (when (and open? has-value?) - [:div {:class (stl/css :element-set-content)} - [:div {:class (stl/css-case :first-row true - :hidden hidden?)} - [:div {:class (stl/css :blur-info)} - [:button {:class (stl/css-case :show-more true - :selected more-options?) - :on-click toggle-more-options} - i/menu-refactor] - [:span {:class (stl/css :label)} - (tr "workspace.options.blur-options.title")]] - [:div {:class (stl/css :actions)} - [:button {:class (stl/css :action-btn) - :on-click handle-toggle-visibility} - (if hidden? - i/hide-refactor - i/shown-refactor)] - [:button {:class (stl/css :action-btn) - :on-click handle-delete} i/remove-refactor]]] - (when more-options? - [:div {:class (stl/css :second-row)} - [:label {:class (stl/css :label) - :for "blur-input-sidebar"} - (tr "inspect.attributes.blur.value")] - [:> numeric-input* - {:className (stl/css :numeric-input) - :placeholder "--" - :id "blur-input-sidebar" - :min "0" - :on-change handle-change - :value (:value blur)}]])])] - - - [:div.element-set - [:div.element-set-title - [:span - (case type - :multiple (tr "workspace.options.blur-options.title.multiple") - :group (tr "workspace.options.blur-options.title.group") - (tr "workspace.options.blur-options.title"))] - - [:div.element-set-title-actions - (when (and has-value? (not multiple?)) - [:div.add-page {:on-click handle-toggle-visibility} - (if hidden? i/eye-closed i/eye)]) - - (if has-value? - [:div.add-page {:on-click handle-delete} i/minus] - [:div.add-page {:on-click handle-add} i/close])]] - - (cond - has-value? - [:div.element-set-content - [:& input-row {:label "Value" - :class "pixels" - :min "0" - :value (:value blur) - :placeholder (tr "settings.multiple") - :on-change handle-change}]])]))) + [:div {:class (stl/css :element-set)} + [:div {:class (stl/css :element-title)} + [:& title-bar {:collapsable? has-value? + :collapsed? (not open?) + :on-collapsed toggle-content + :title (case type + :multiple (tr "workspace.options.blur-options.title.multiple") + :group (tr "workspace.options.blur-options.title.group") + (tr "workspace.options.blur-options.title")) + :class (stl/css-case :title-spacing-blur (not has-value?))} + (when-not has-value? + [:button {:class (stl/css :add-blur) + :on-click handle-add} i/add-refactor])]] + (when (and open? has-value?) + [:div {:class (stl/css :element-set-content)} + [:div {:class (stl/css-case :first-row true + :hidden hidden?)} + [:div {:class (stl/css :blur-info)} + [:button {:class (stl/css-case :show-more true + :selected more-options?) + :on-click toggle-more-options} + i/menu-refactor] + [:span {:class (stl/css :label)} + (tr "workspace.options.blur-options.title")]] + [:div {:class (stl/css :actions)} + [:button {:class (stl/css :action-btn) + :on-click handle-toggle-visibility} + (if hidden? + i/hide-refactor + i/shown-refactor)] + [:button {:class (stl/css :action-btn) + :on-click handle-delete} i/remove-refactor]]] + (when more-options? + [:div {:class (stl/css :second-row)} + [:label {:class (stl/css :label) + :for "blur-input-sidebar"} + (tr "inspect.attributes.blur.value")] + [:> numeric-input* + {:className (stl/css :numeric-input) + :placeholder "--" + :id "blur-input-sidebar" + :min "0" + :on-change handle-change + :value (:value blur)}]])])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/blur.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/blur.scss index 6559fdd1d4..6bed44856a 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/blur.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/blur.scss @@ -8,13 +8,73 @@ .element-set { margin: 0; - .element-title { - .title-spacing-blur { - padding-left: $s-2; - margin: 0; +} + +.title-spacing-blur { + padding-left: $s-2; + margin: 0; +} + +.add-blur { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } +} + +.element-set-content { + @include flexColumn; + margin-bottom: $s-8; +} + +.first-row { + @include flexRow; + width: 100%; + .blur-info { + display: flex; + align-items: center; + gap: $s-1; + flex-grow: 1; + border-radius: $br-8; + background-color: var(--input-details-color); + .show-more { + @extend .button-secondary; + height: $s-32; + width: $s-28; + border-radius: $br-8 0 0 $br-8; + box-sizing: border-box; + border: $s-1 solid var(--button-secondary-background-color-rest); + svg { + @extend .button-icon; + } + &.selected { + background-color: var(--button-radio-background-color-active); + svg { + stroke: var(--button-radio-foreground-color-active); + } + } } - .add-blur { + .label { + @include titleTipography; + flex-grow: 1; + display: flex; + align-items: center; + height: $s-32; + padding: 0 $s-8; + border-radius: 0 $br-8 $br-8 0; + background-color: var(--input-background-color); + color: var(--menu-foreground-color); + box-sizing: border-box; + border: $s-1 solid var(--input-background-color); + } + } + .actions { + @include flexRow; + .action-btn { @extend .button-tertiary; + box-sizing: border-box; height: $s-32; width: $s-28; svg { @@ -22,85 +82,27 @@ } } } - .element-set-content { - @include flexColumn; - margin-bottom: $s-8; - .first-row { - @include flexRow; - width: 100%; - .blur-info { - display: flex; - align-items: center; - gap: $s-1; - flex-grow: 1; - border-radius: $br-8; - background-color: var(--input-details-color); - .show-more { - @extend .button-secondary; - height: $s-32; - width: $s-28; - border-radius: $br-8 0 0 $br-8; - box-sizing: border-box; - border: $s-1 solid var(--button-secondary-background-color-rest); - svg { - @extend .button-icon; - } - &.selected { - background-color: var(--button-radio-background-color-active); - svg { - stroke: var(--button-radio-foreground-color-active); - } - } - } - .label { - @include titleTipography; - flex-grow: 1; - display: flex; - align-items: center; - height: $s-32; - padding: 0 $s-8; - border-radius: 0 $br-8 $br-8 0; - background-color: var(--input-background-color); - color: var(--menu-foreground-color); - box-sizing: border-box; - border: $s-1 solid var(--input-background-color); - } - } - .actions { - @include flexRow; - .action-btn { - @extend .button-tertiary; - box-sizing: border-box; - border: $s-1 solid var(--button-tertiary-background-color-rest); - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - } - } - &.hidden { - .blur-info { - @include hiddenElement; - .show-more { - @include hiddenElement; - border: $s-1 solid var(--input-border-color-disabled); - } - .label { - @include hiddenElement; - border: $s-1 solid var(--input-border-color-disabled); - } - } + &.hidden { + .blur-info { + @include hiddenElement; + .show-more { + @include hiddenElement; + border: $s-1 solid var(--input-border-color-disabled); } - } - .second-row { - @extend .input-element; - width: $s-92; .label { - padding-left: $s-8; - width: $s-60; + @include hiddenElement; + border: $s-1 solid var(--input-border-color-disabled); } } } } + +.second-row { + @extend .input-element; + width: $s-92; + .label { + padding-left: $s-8; + width: $s-60; + } +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.cljs index bae6919402..7a25e1bf55 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.cljs @@ -13,19 +13,13 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] - [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) - - (mf/defc bool-options [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - - selected (mf/deref refs/selected-objects) + (let [selected (mf/deref refs/selected-objects) head (first selected) selected-with-children (mf/deref refs/selected-shapes-with-children) has-invalid-shapes? (->> selected-with-children @@ -42,39 +36,10 @@ disabled-flatten (or (empty? selected) has-invalid-shapes?) set-bool - (mf/use-fn - (mf/deps head head-bool-type selected) - (fn [event] - (let [bool-type (-> (dom/get-current-target event) - (dom/get-data "value") - (keyword))] - (cond - (> (count selected) 1) - (st/emit! (dw/create-bool (if new-css-system - (keyword bool-type) - bool-type))) - - (and (= (count selected) 1) is-group?) - (st/emit! (dw/group-to-bool (:id head) (if new-css-system - (keyword bool-type) - bool-type))) - - (and (= (count selected) 1) is-bool?) - (if (= head-bool-type (if new-css-system - (keyword bool-type) - bool-type)) - (st/emit! (dw/bool-to-group (:id head))) - (st/emit! (dw/change-bool-type (:id head) (if new-css-system - (keyword bool-type) - bool-type)))))))) - - set-bool-refactor (mf/use-fn (mf/deps selected is-group? is-bool?) (fn [bool-type] - (let [bool-type (if new-css-system - (keyword bool-type) - bool-type)] + (let [bool-type (keyword bool-type)] (cond (> (count selected) 1) (st/emit! (dw/create-bool bool-type)) @@ -90,82 +55,40 @@ flatten-objects (mf/use-fn #(st/emit! (dw/convert-selected-to-path)))] (when (not (and disabled-bool-btns disabled-flatten)) - (if new-css-system - [:div {:class (stl/css :boolean-options)} - [:div {:class (stl/css :bool-group)} - [:& radio-buttons {:selected (d/name head-bool-type) - :class (stl/css :boolean-radio-btn) - :on-change set-bool-refactor - :name "bool-options"} - [:& radio-button {:icon i/boolean-union-refactor - :value "union" - :disabled disabled-bool-btns - :title (str (tr "workspace.shape.menu.union") " (" (sc/get-tooltip :bool-union) ")") - :id "bool-opt-union"}] - [:& radio-button {:icon i/boolean-difference-refactor - :value "difference" - :disabled disabled-bool-btns - :title (str (tr "workspace.shape.menu.difference") " (" (sc/get-tooltip :bool-difference) ")") - :id "bool-opt-differente"}] - [:& radio-button {:icon i/boolean-intersection-refactor - :value "intersection" - :disabled disabled-bool-btns - :title (str (tr "intersection") " (" (sc/get-tooltip :bool-intersection) ")") - :id "bool-opt-intersection"}] - [:& radio-button {:icon i/boolean-exclude-refactor - :value "exclude" - :disabled disabled-bool-btns - :title (str (tr "exclude") " (" (sc/get-tooltip :bool-exclude) ")") - :id "bool-opt-exclude"}]]] + [:div {:class (stl/css :boolean-options)} + [:div {:class (stl/css :bool-group)} + [:& radio-buttons {:selected (d/name head-bool-type) + :class (stl/css :boolean-radio-btn) + :on-change set-bool + :name "bool-options"} + [:& radio-button {:icon i/boolean-union-refactor + :value "union" + :disabled disabled-bool-btns + :title (str (tr "workspace.shape.menu.union") " (" (sc/get-tooltip :bool-union) ")") + :id "bool-opt-union"}] + [:& radio-button {:icon i/boolean-difference-refactor + :value "difference" + :disabled disabled-bool-btns + :title (str (tr "workspace.shape.menu.difference") " (" (sc/get-tooltip :bool-difference) ")") + :id "bool-opt-differente"}] + [:& radio-button {:icon i/boolean-intersection-refactor + :value "intersection" + :disabled disabled-bool-btns + :title (str (tr "intersection") " (" (sc/get-tooltip :bool-intersection) ")") + :id "bool-opt-intersection"}] + [:& radio-button {:icon i/boolean-exclude-refactor + :value "exclude" + :disabled disabled-bool-btns + :title (str (tr "exclude") " (" (sc/get-tooltip :bool-exclude) ")") + :id "bool-opt-exclude"}]]] - [:div {:class (stl/css :bool-group)} - [:button - {:title (tr "workspace.shape.menu.flatten") - :class (stl/css-case - :flatten true - :disabled disabled-flatten) - :disabled disabled-flatten - :on-click flatten-objects} - i/boolean-flatten-refactor]]] - - [:div.align-options - [:div.align-group - [:div.align-button.tooltip.tooltip-bottom - {:alt (str (tr "workspace.shape.menu.union") " (" (sc/get-tooltip :bool-union) ")") - :class (dom/classnames :disabled disabled-bool-btns - :selected (= head-bool-type :union)) - :data-value :union - :on-click set-bool} - i/bool-union] - - [:div.align-button.tooltip.tooltip-bottom - {:alt (str (tr "workspace.shape.menu.difference") " (" (sc/get-tooltip :bool-difference) ")") - :class (dom/classnames :disabled disabled-bool-btns - :selected (= head-bool-type :difference)) - :data-value :difference - :on-click set-bool} - i/bool-difference] - - [:div.align-button.tooltip.tooltip-bottom - {:alt (str (tr "workspace.shape.menu.intersection") " (" (sc/get-tooltip :bool-intersection) ")") - :class (dom/classnames :disabled disabled-bool-btns - :selected (= head-bool-type :intersection)) - :data-value :intersection - :on-click set-bool} - i/bool-intersection] - - [:div.align-button.tooltip.tooltip-bottom - {:alt (str (tr "workspace.shape.menu.exclude") " (" (sc/get-tooltip :bool-exclude) ")") - :class (dom/classnames :disabled disabled-bool-btns - :selected (= head-bool-type :exclude)) - :data-value :exclude - :on-click set-bool} - i/bool-exclude]] - - [:div.align-group - [:div.align-button.tooltip.tooltip-bottom - {:alt (tr "workspace.shape.menu.flatten") - :class (dom/classnames :disabled disabled-flatten) - :on-click flatten-objects} - i/bool-flatten]]])))) + [:div {:class (stl/css :bool-group)} + [:button + {:title (tr "workspace.shape.menu.flatten") + :class (stl/css-case + :flatten true + :disabled disabled-flatten) + :disabled disabled-flatten + :on-click flatten-objects} + i/boolean-flatten-refactor]]]))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.scss index bf08806f5e..3cf1fa8fe6 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.scss @@ -11,33 +11,36 @@ gap: $s-4; height: $s-32; margin: 0 calc(-1 * $s-2); - .bool-group { - display: flex; - align-items: center; - .flatten { - @extend .button-tertiary; - height: $s-28; - width: $s-28; - border-radius: $br-8; - svg { - @extend .button-icon; - stroke: var(--icon-foreground); - } - &.disabled { - cursor: default; - svg { - stroke: var(--button-background-color-disabled); - } - &:hover { - background-color: var(--panel-background-color); - svg { - stroke: var(--button-background-color-disabled); - } - } - } +} + +.bool-group { + display: flex; + align-items: center; +} + +.flatten { + @extend .button-tertiary; + height: $s-28; + width: $s-28; + border-radius: $br-8; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } + &.disabled { + cursor: default; + svg { + stroke: var(--button-background-color-disabled); } - .boolean-radio-btn { - background-color: transparent; + &:hover { + background-color: var(--panel-background-color); + svg { + stroke: var(--button-background-color-disabled); + } } } } + +.boolean-radio-btn { + background-color: transparent; +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs index 47aac34fb4..6dce1cc000 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs @@ -14,8 +14,6 @@ [app.main.data.workspace.selection :as dws] [app.main.store :as st] [app.main.ui.components.title-bar :refer [title-bar]] - [app.main.ui.context :as ctx] - [app.main.ui.icons :as i] [app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]] [app.util.i18n :as i18n :refer [tr]] [cuerdas.core :as str] @@ -158,7 +156,6 @@ [{:keys [shapes file-id shared-libs] :as props}] (let [{:keys [grouped-colors library-colors colors]} (mf/with-memo [shapes file-id shared-libs] (prepare-colors shapes file-id shared-libs)) - new-css-system (mf/use-ctx ctx/new-css-system) state* (mf/use-state true) open? (deref state*) @@ -216,71 +213,17 @@ (mf/with-effect [grouped-colors] (reset! grouped-colors* grouped-colors)) - (if new-css-system - [:div {:class (stl/css :element-set)} - [:div {:class (stl/css :element-title)} - [:& title-bar {:collapsable? has-colors? - :collapsed? (not open?) - :on-collapsed toggle-content - :title (tr "workspace.options.selection-color") - :class (stl/css-case :title-spacing-selected-colors (not has-colors?))}]] + [:div {:class (stl/css :element-set)} + [:div {:class (stl/css :element-title)} + [:& title-bar {:collapsable? has-colors? + :collapsed? (not open?) + :on-collapsed toggle-content + :title (tr "workspace.options.selection-color") + :class (stl/css-case :title-spacing-selected-colors (not has-colors?))}]] - (when open? - [:div {:class (stl/css :element-content)} - [:div {:class (stl/css :selected-color-group)} - (for [[index color] (d/enumerate (take 3 library-colors))] - [:& color-row {:key (dm/str "library-color-" (:color color)) - :color color - :index index - :on-detach on-detach - :select-only select-only - :on-change #(on-change %1 color %2) - :on-open on-open - :on-close on-close}]) - (when (and (false? @expand-lib-color) (< 3 (count library-colors))) - [:button {:class (stl/css :more-colors-btn) - :on-click #(reset! expand-lib-color true)} - (tr "workspace.options.more-lib-colors")]) - (when @expand-lib-color - (for [[index color] (d/enumerate (drop 3 library-colors))] - [:& color-row {:key (dm/str "library-color-" (:color color)) - :color color - :index index - :on-detach on-detach - :select-only select-only - :on-change #(on-change %1 color %2) - :on-open on-open - :on-close on-close}]))] - [:div {:class (stl/css :selected-color-group)} - (for [[index color] (d/enumerate (take 3 colors))] - [:& color-row {:key (dm/str "color-" index) - :color color - :index index - :select-only select-only - :on-change #(on-change %1 color %2) - :on-open on-open - :on-close on-close}]) - (when (and (false? @expand-color) (< 3 (count colors))) - [:button {:class (stl/css :more-colors-btn) - :on-click #(reset! expand-color true)} - (tr "workspace.options.more-colors")]) - - (when @expand-color - (for [[index color] (d/enumerate (drop 3 colors))] - [:& color-row {:key (dm/str "color-" (:color color)) - :color color - :index index - :select-only select-only - :on-change #(on-change %1 color %2) - :on-open on-open - :on-close on-close}]))]])] - - - [:div.element-set - [:div.element-set-title - [:span (tr "workspace.options.selection-color")]] - [:div.element-set-content - [:div.selected-colors + (when open? + [:div {:class (stl/css :element-content)} + [:div {:class (stl/css :selected-color-group)} (for [[index color] (d/enumerate (take 3 library-colors))] [:& color-row {:key (dm/str "library-color-" (:color color)) :color color @@ -291,9 +234,9 @@ :on-open on-open :on-close on-close}]) (when (and (false? @expand-lib-color) (< 3 (count library-colors))) - [:div.expand-colors {:on-click #(reset! expand-lib-color true)} - [:span i/actions] - [:span.text (tr "workspace.options.more-lib-colors")]]) + [:button {:class (stl/css :more-colors-btn) + :on-click #(reset! expand-lib-color true)} + (tr "workspace.options.more-lib-colors")]) (when @expand-lib-color (for [[index color] (d/enumerate (drop 3 library-colors))] [:& color-row {:key (dm/str "library-color-" (:color color)) @@ -304,8 +247,7 @@ :on-change #(on-change %1 color %2) :on-open on-open :on-close on-close}]))] - - [:div.selected-colors + [:div {:class (stl/css :selected-color-group)} (for [[index color] (d/enumerate (take 3 colors))] [:& color-row {:key (dm/str "color-" index) :color color @@ -315,9 +257,10 @@ :on-open on-open :on-close on-close}]) (when (and (false? @expand-color) (< 3 (count colors))) - [:div.expand-colors {:on-click #(reset! expand-color true)} - [:span i/actions] - [:span.text (tr "workspace.options.more-colors")]]) + [:button {:class (stl/css :more-colors-btn) + :on-click #(reset! expand-color true)} + (tr "workspace.options.more-colors")]) + (when @expand-color (for [[index color] (d/enumerate (drop 3 colors))] [:& color-row {:key (dm/str "color-" (:color color)) @@ -326,4 +269,4 @@ :select-only select-only :on-change #(on-change %1 color %2) :on-open on-open - :on-close on-close}]))]]]))) + :on-close on-close}]))]])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.scss index eb8ca850c3..56e096768f 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.scss @@ -8,30 +8,33 @@ .element-set { margin: 0; - .element-title { - .title-spacing-selected-colors { - padding-left: $s-2; - margin: 0; - } - .add-fill { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - } - } - .element-content { - @include flexColumn; - margin-bottom: $s-8; - .selected-color-group { - @include flexColumn; - .more-colors-btn { - @extend .button-secondary; - @include tabTitleTipography; - height: $s-32; - } - } +} + +.title-spacing-selected-colors { + padding-left: $s-2; + margin: 0; +} + +.add-fill { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; } } + +.element-content { + @include flexColumn; + margin-bottom: $s-8; +} + +.selected-color-group { + @include flexColumn; +} + +.more-colors-btn { + @extend .button-secondary; + @include tabTitleTipography; + height: $s-32; +} 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 c000ac90c8..40cef86426 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 @@ -147,7 +147,6 @@ (when (or @editing? creating?) [:div.counter (str @size "/300")])]]))) - (mf/defc component-swap-item {::mf/wrap-props false} [{:keys [item loop shapes file-id root-shape container component-id is-search listing-thumbs] :as props}] @@ -190,7 +189,6 @@ [:span {:class (stl/css :component-group-name)} (cfh/last-path group-name)]] [:span i/arrow-slide]])) - (mf/defc component-swap [{:keys [shapes] :as props}] (let [single? (= 1 (count shapes)) @@ -405,7 +403,6 @@ [:span {:class (stl/css :dropdown-label)} (tr (:msg entry))]])]])) - (mf/defc component-menu [{:keys [shapes swap-opened?] :as props}] (let [current-file-id (mf/use-ctx ctx/current-file-id) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs index a61c6314a3..35b72810ef 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs @@ -16,7 +16,6 @@ [app.main.store :as st] [app.main.ui.components.select :refer [select]] [app.main.ui.components.title-bar :refer [title-bar]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -31,9 +30,7 @@ (mf/defc constraints-menu [{:keys [ids values] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - - state* (mf/use-state true) + (let [state* (mf/use-state true) open? (deref state*) toggle-content (mf/use-fn #(swap! state* not)) @@ -109,19 +106,6 @@ ids #(assoc % constraint new-value)))))) - on-constraint-select-changed - (mf/use-fn - (mf/deps ids) - (fn [event] - (let [constraint (-> (dom/get-current-target event) - (dom/get-data "value") - (keyword)) - value (-> (dom/get-target-val event) (keyword))] - (when-not (str/empty? value) - (st/emit! (dch/update-shapes - ids - #(assoc % constraint value))))))) - on-constraint-h-select-changed (mf/use-fn (mf/deps ids) @@ -171,154 +155,79 @@ ;; CONSTRAINTS (when in-frame? - (if new-css-system - [:div {:class (stl/css :element-set)} - [:div {:class (stl/css :element-title)} - [:& title-bar {:collapsable? true - :collapsed? (not open?) - :on-collapsed toggle-content - :title (tr "workspace.options.constraints")}]] - (when open? - [:div {:class (stl/css :element-set-content)} - [:div {:class (stl/css :constraints-widget)} - [:div {:class (stl/css :constraints-top)} - [:button {:class (stl/css-case :constraint-btn true - :active (or (= constraints-v :top) - (= constraints-v :topbottom))) - :data-value :top - :on-click on-constraint-button-clicked} - [:span {:class (stl/css :resalted-area)}]]] - [:div {:class (stl/css :constraints-left)} - [:button {:class (stl/css-case :constraint-btn true - :constraint-btn-rotated true - :active (or (= constraints-h :left) - (= constraints-h :leftright))) - :data-value :left - :on-click on-constraint-button-clicked} - [:span {:class (stl/css :resalted-area)}]]] - [:div {:class (stl/css :constraints-center)} - [:button {:class (stl/css-case :constraint-btn true - :active (= constraints-h :center)) - :data-value :centerh - :on-click on-constraint-button-clicked} - [:span {:class (stl/css :resalted-area)}]] - [:button {:class (stl/css-case :constraint-btn-special true - :constraint-btn-rotated true - :active (= constraints-v :center)) - :data-value :centerv - :on-click on-constraint-button-clicked} - [:span {:class (stl/css :resalted-area)}]]] - [:div {:class (stl/css :constraints-right)} - [:button {:class (stl/css-case :constraint-btn true - :constraint-btn-rotated true - :active (or (= constraints-h :right) - (= constraints-h :leftright))) - :data-value :right - :on-click on-constraint-button-clicked} - [:span {:class (stl/css :resalted-area)}]]] - [:div {:class (stl/css :constraints-bottom)} - [:button {:class (stl/css-case :constraint-btn true - :active (or (= constraints-v :bottom) - (= constraints-v :topbottom))) - :data-value :bottom - :on-click on-constraint-button-clicked} - [:span {:class (stl/css :resalted-area)}]]]] - [:div {:class (stl/css :contraints-selects)} - [:div {:class (stl/css :horizontal-select)} - [:& select - {:default-value (d/name constraints-h "scale") - :options options-h - :on-change on-constraint-h-select-changed}]] - [:div {:class (stl/css :vertical-select)} - [:& select - {:default-value (d/name constraints-v "scale") - :options options-v - :on-change on-constraint-v-select-changed}]] - (when first-level? - [:div {:class (stl/css :checkbox)} + [:div {:class (stl/css :element-set)} + [:div {:class (stl/css :element-title)} + [:& title-bar {:collapsable? true + :collapsed? (not open?) + :on-collapsed toggle-content + :title (tr "workspace.options.constraints")}]] + (when open? + [:div {:class (stl/css :element-set-content)} + [:div {:class (stl/css :constraints-widget)} + [:div {:class (stl/css :constraints-top)} + [:button {:class (stl/css-case :constraint-btn true + :active (or (= constraints-v :top) + (= constraints-v :topbottom))) + :data-value :top + :on-click on-constraint-button-clicked} + [:span {:class (stl/css :resalted-area)}]]] + [:div {:class (stl/css :constraints-left)} + [:button {:class (stl/css-case :constraint-btn true + :constraint-btn-rotated true + :active (or (= constraints-h :left) + (= constraints-h :leftright))) + :data-value :left + :on-click on-constraint-button-clicked} + [:span {:class (stl/css :resalted-area)}]]] + [:div {:class (stl/css :constraints-center)} + [:button {:class (stl/css-case :constraint-btn true + :active (= constraints-h :center)) + :data-value :centerh + :on-click on-constraint-button-clicked} + [:span {:class (stl/css :resalted-area)}]] + [:button {:class (stl/css-case :constraint-btn-special true + :constraint-btn-rotated true + :active (= constraints-v :center)) + :data-value :centerv + :on-click on-constraint-button-clicked} + [:span {:class (stl/css :resalted-area)}]]] + [:div {:class (stl/css :constraints-right)} + [:button {:class (stl/css-case :constraint-btn true + :constraint-btn-rotated true + :active (or (= constraints-h :right) + (= constraints-h :leftright))) + :data-value :right + :on-click on-constraint-button-clicked} + [:span {:class (stl/css :resalted-area)}]]] + [:div {:class (stl/css :constraints-bottom)} + [:button {:class (stl/css-case :constraint-btn true + :active (or (= constraints-v :bottom) + (= constraints-v :topbottom))) + :data-value :bottom + :on-click on-constraint-button-clicked} + [:span {:class (stl/css :resalted-area)}]]]] + [:div {:class (stl/css :contraints-selects)} + [:div {:class (stl/css :horizontal-select)} + [:& select + {:default-value (d/name constraints-h "scale") + :options options-h + :on-change on-constraint-h-select-changed}]] + [:div {:class (stl/css :vertical-select)} + [:& select + {:default-value (d/name constraints-v "scale") + :options options-v + :on-change on-constraint-v-select-changed}]] + (when first-level? + [:div {:class (stl/css :checkbox)} - [:label {:for "fixed-on-scroll" - :class (stl/css-case :checked (:fixed-scroll values))} - [:span {:class (stl/css-case :check-mark true - :checked (:fixed-scroll values))} - (when (:fixed-scroll values) - i/status-tick-refactor)] - (tr "workspace.options.constraints.fix-when-scrolling") - [:input {:type "checkbox" - :id "fixed-on-scroll" - :checked (:fixed-scroll values) - :on-change on-fixed-scroll-clicked}]]])]])] - - - [:div.element-set - [:div.element-set-title - [:span (tr "workspace.options.constraints")]] - - [:div.element-set-content - [:div.row-flex.align-top - [:div.constraints-widget - [:div.constraints-box] - [:div.constraint-button.top - {:class (dom/classnames :active (or (= constraints-v :top) - (= constraints-v :topbottom))) - :data-value :top - :on-click on-constraint-button-clicked}] - [:div.constraint-button.bottom - {:class (dom/classnames :active (or (= constraints-v :bottom) - (= constraints-v :topbottom))) - :data-value :bottom - :on-click on-constraint-button-clicked}] - [:div.constraint-button.left - {:class (dom/classnames :active (or (= constraints-h :left) - (= constraints-h :leftright))) - :data-value :left - :on-click on-constraint-button-clicked}] - [:div.constraint-button.right - {:class (dom/classnames :active (or (= constraints-h :right) - (= constraints-h :leftright))) - :data-value :right - :on-click on-constraint-button-clicked}] - [:div.constraint-button.centerv - {:class (dom/classnames :active (= constraints-v :center)) - :data-value :centerv - :on-click on-constraint-button-clicked}] - [:div.constraint-button.centerh - {:class (dom/classnames :active (= constraints-h :center)) - :data-value :centerh - :on-click on-constraint-button-clicked}]] - - [:div.constraints-form - [:div.row-flex - [:span.left-right i/full-screen] - [:select.input-select {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :data-value :constraints-h - :on-change on-constraint-select-changed - :value (d/name constraints-h "scale")} - (when (= constraints-h :multiple) - [:option {:value ""} (tr "settings.multiple")]) - [:option {:value "left"} (tr "workspace.options.constraints.left")] - [:option {:value "right"} (tr "workspace.options.constraints.right")] - [:option {:value "leftright"} (tr "workspace.options.constraints.leftright")] - [:option {:value "center"} (tr "workspace.options.constraints.center")] - [:option {:value "scale"} (tr "workspace.options.constraints.scale")]]] - [:div.row-flex - [:span.top-bottom i/full-screen] - [:select.input-select {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :data-value :constraints-v - :on-change on-constraint-select-changed - :value (d/name constraints-v "scale")} - (when (= constraints-v :multiple) - [:option {:value ""} (tr "settings.multiple")]) - [:option {:value "top"} (tr "workspace.options.constraints.top")] - [:option {:value "bottom"} (tr "workspace.options.constraints.bottom")] - [:option {:value "topbottom"} (tr "workspace.options.constraints.topbottom")] - [:option {:value "center"} (tr "workspace.options.constraints.center")] - [:option {:value "scale"} (tr "workspace.options.constraints.scale")]]] - (when first-level? - [:div.row-flex - [:div.fix-when {:class (dom/classnames :active (:fixed-scroll values)) - :on-click on-fixed-scroll-clicked} - (if (:fixed-scroll values) - i/pin-fill - i/pin) - [:span (tr "workspace.options.constraints.fix-when-scrolling")]]])]]]])))) + [:label {:for "fixed-on-scroll" + :class (stl/css-case :checked (:fixed-scroll values))} + [:span {:class (stl/css-case :check-mark true + :checked (:fixed-scroll values))} + (when (:fixed-scroll values) + i/status-tick-refactor)] + (tr "workspace.options.constraints.fix-when-scrolling") + [:input {:type "checkbox" + :id "fixed-on-scroll" + :checked (:fixed-scroll values) + :on-change on-fixed-scroll-clicked}]]])]])]))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/constraints.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/constraints.scss index 736bde4e40..cf275fba93 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/constraints.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/constraints.scss @@ -8,149 +8,156 @@ .element-set { margin: 0; - .element-set-content { - display: flex; - gap: $s-4; - .constraints-widget { - background-color: var(--constraint-widget-background-color); - display: grid; - grid-template-columns: $s-24 $s-60 $s-24; - grid-template-rows: $s-24 $s-60 $s-24; - grid-template-areas: - "top top top" - "left center right" - "bottom bottom bottom"; - height: $s-108; - width: $s-108; +} + +.element-set-content { + display: flex; + gap: $s-4; +} + +.constraints-widget { + background-color: var(--constraint-widget-background-color); + display: grid; + grid-template-columns: $s-24 $s-60 $s-24; + grid-template-rows: $s-24 $s-60 $s-24; + grid-template-areas: + "top top top" + "left center right" + "bottom bottom bottom"; + height: $s-108; + width: $s-108; + border-radius: $br-8; +} + +.constraints-top, +.constraints-left, +.constraints-center, +.constraints-right, +.constraints-bottom { + @include flexCenter; + grid-area: top; + .constraint-btn, + .constraint-btn-special, + .constraint-btn-rotated { + @include buttonStyle; + @include flexCenter; + width: 100%; + height: 100%; + .resalted-area { + width: $s-32; + height: $s-3; border-radius: $br-8; - .constraints-top, - .constraints-left, - .constraints-center, - .constraints-right, - .constraints-bottom { - @include flexCenter; - grid-area: top; - .constraint-btn, - .constraint-btn-special, - .constraint-btn-rotated { - @include buttonStyle; - @include flexCenter; - width: 100%; - height: 100%; - .resalted-area { - width: $s-32; - height: $s-3; - border-radius: $br-8; - background-color: var(--button-constraint-background-color-rest); - padding: 0; - margin: 0; - } - &.active .resalted-area { - outline: $s-4 solid var(--button-constraint-border-color-hover); - background-color: var(--button-constraint-background-color-hover); - } - &:hover .resalted-area, - &:focus .resalted-area { - outline: $s-4 solid var(--button-constraint-border-color-hover); - background-color: var(--button-constraint-background-color-hover); - } - } - } - .constraints-left { - grid-area: left; - .constraint-btn-rotated { - height: $s-60; - width: $s-24; - .resalted-area { - height: $s-32; - width: $s-3; - } - } - } - .constraints-center { - grid-area: center; - position: relative; - background-color: var(--constraint-center-area-background-color); - border-radius: $br-8; - .constraint-btn { - width: $s-60; - height: $s-24; - .resalted-area { - width: $s-32; - height: $s-3; - } - } - .constraint-btn-special { - position: absolute; - height: $s-60; - width: $s-24; - .resalted-area { - height: $s-32; - width: $s-3; - } - } - } - .constraints-right { - grid-area: right; - .constraint-btn-rotated { - height: $s-72; - width: $s-24; - .resalted-area { - height: $s-32; - width: $s-3; - } - } - } - .constraints-bottom { - grid-area: bottom; - } + background-color: var(--button-constraint-background-color-rest); + padding: 0; + margin: 0; } - .contraints-selects { - @include flexColumn; + &.active .resalted-area { + outline: $s-4 solid var(--button-constraint-border-color-hover); + background-color: var(--button-constraint-background-color-hover); + } + &:hover .resalted-area, + &:focus .resalted-area { + outline: $s-4 solid var(--button-constraint-border-color-hover); + background-color: var(--button-constraint-background-color-hover); + } + } +} +.constraints-left { + grid-area: left; + .constraint-btn-rotated { + height: $s-60; + width: $s-24; + .resalted-area { + height: $s-32; + width: $s-3; + } + } +} +.constraints-center { + grid-area: center; + position: relative; + background-color: var(--constraint-center-area-background-color); + border-radius: $br-8; + .constraint-btn { + width: $s-60; + height: $s-24; + .resalted-area { + width: $s-32; + height: $s-3; + } + } + .constraint-btn-special { + position: absolute; + height: $s-60; + width: $s-24; + .resalted-area { + height: $s-32; + width: $s-3; + } + } +} - .horizontal-select, - .vertical-select { - width: $s-124; - padding: 0; +.constraints-right { + grid-area: right; + .constraint-btn-rotated { + height: $s-72; + width: $s-24; + .resalted-area { + height: $s-32; + width: $s-3; + } + } +} + +.constraints-bottom { + grid-area: bottom; +} + +.contraints-selects { + @include flexColumn; +} + +.horizontal-select, +.vertical-select { + width: $s-124; + padding: 0; +} + +.checkbox { + display: flex; + align-items: center; + margin-bottom: $s-8; + margin-top: $s-8; + padding-left: 0; + input { + margin: 0; + } + + label { + @include titleTipography; + display: flex; + align-items: center; + gap: $s-2; + cursor: pointer; + color: var(--input-checkbox-text-foreground-color); + .check-mark { + @include flexCenter; + width: $s-16; + height: $s-16; + border-radius: $br-6; + background-color: var(--input-checkbox-inactive-background-color); + &.checked { + background-color: var(--input-checkbox-active-background-color); + svg { + @extend .button-icon-small; + stroke: var(--input-details-color); + } } - - .checkbox { - display: flex; - align-items: center; - margin-bottom: $s-8; - margin-top: $s-8; - padding-left: 0; - input { - margin: 0; - } - label { - @include titleTipography; - display: flex; - align-items: center; - gap: $s-2; - cursor: pointer; - color: var(--input-checkbox-text-foreground-color); - .check-mark { - @include flexCenter; - width: $s-16; - height: $s-16; - border-radius: $br-6; - background-color: var(--input-checkbox-inactive-background-color); - &.checked { - background-color: var(--input-checkbox-active-background-color); - svg { - @extend .button-icon-small; - stroke: var(--input-details-color); - } - } - &:hover { - border-color: var(--input-checkbox-border-color-hover); - } - &:focus { - border-color: var(--input-checkbox-border-color-focus); - } - } - } + &:hover { + border-color: var(--input-checkbox-border-color-hover); + } + &:focus { + border-color: var(--input-checkbox-border-color-focus); } } } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs index f45dffb83e..72556669d0 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs @@ -15,7 +15,6 @@ [app.main.store :as st] [app.main.ui.components.select :refer [select]] [app.main.ui.components.title-bar :refer [title-bar]] - [app.main.ui.context :as ctx] [app.main.ui.export] [app.main.ui.icons :as i] [app.util.dom :as dom] @@ -30,8 +29,7 @@ (mf/defc exports-menu {::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type" "page-id" "file-id"]))]} [{:keys [ids type values page-id file-id] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - exports (:exports values []) + (let [exports (:exports values []) has-exports? (or (= :multiple exports) (some? (seq exports))) comp-state* (mf/use-state true) @@ -118,11 +116,7 @@ (mf/use-fn (mf/deps ids) (fn [index event] - (let [scale (if new-css-system - (d/parse-double event) - (-> event - (dom/get-target-val) - (d/parse-double)))] + (let [scale (d/parse-double event)] (st/emit! (dch/update-shapes ids (fn [shape] (assoc-in shape [:exports index :scale] scale))))))) @@ -143,11 +137,7 @@ (mf/use-fn (mf/deps ids) (fn [index event] - (let [type (if new-css-system - (keyword event) - (-> event - (dom/get-target-val) - (keyword)))] + (let [type (keyword event)] (st/emit! (dch/update-shapes ids (fn [shape] (assoc-in shape [:exports index :type] type))))))) @@ -179,125 +169,68 @@ {:value "svg" :label "SVG"} {:value "pdf" :label "PDF"}]] - (if new-css-system - [:div {:class (stl/css :element-set)} - [:div {:class (stl/css :element-title)} - [:& title-bar {:collapsable? has-exports? - :collapsed? (not open?) - :on-collapsed toggle-content - :title (tr (if (> (count ids) 1) "workspace.options.export-multiple" "workspace.options.export")) - :class (stl/css-case :title-spacing-export (not has-exports?))} - [:button {:class (stl/css :add-export) - :on-click add-export} i/add-refactor]]] - (when open? - [:div {:class (stl/css :element-set-content)} + [:div {:class (stl/css :element-set)} + [:div {:class (stl/css :element-title)} + [:& title-bar {:collapsable? has-exports? + :collapsed? (not open?) + :on-collapsed toggle-content + :title (tr (if (> (count ids) 1) "workspace.options.export-multiple" "workspace.options.export")) + :class (stl/css-case :title-spacing-export (not has-exports?))} + [:button {:class (stl/css :add-export) + :on-click add-export} i/add-refactor]]] + (when open? + [:div {:class (stl/css :element-set-content)} - (cond - (= :multiple exports) - [:div {:class (stl/css :multiple-exports)} - [:div {:class (stl/css :label)} (tr "settings.multiple")] - [:div {:class (stl/css :actions)} - [:button {:class (stl/css :action-btn) - :on-click on-remove-all} - i/remove-refactor]]] + (cond + (= :multiple exports) + [:div {:class (stl/css :multiple-exports)} + [:div {:class (stl/css :label)} (tr "settings.multiple")] + [:div {:class (stl/css :actions)} + [:button {:class (stl/css :action-btn) + :on-click on-remove-all} + i/remove-refactor]]] - (seq exports) - [:* - (for [[index export] (d/enumerate exports)] - [:div {:class (stl/css :element-group) - :key index} - [:div {:class (stl/css :input-wrapper)} - [:div {:class (stl/css :format-select)} + (seq exports) + [:* + (for [[index export] (d/enumerate exports)] + [:div {:class (stl/css :element-group) + :key index} + [:div {:class (stl/css :input-wrapper)} + [:div {:class (stl/css :format-select)} + [:& select + {:default-value (d/name (:type export)) + :options format-options + :dropdown-class (stl/css :dropdown-upwards) + :on-change (partial on-type-change index)}]] + (when (scale-enabled? export) + [:div {:class (stl/css :size-select)} [:& select - {:default-value (d/name (:type export)) - :options format-options + {:default-value (str (:scale export)) + :options size-options :dropdown-class (stl/css :dropdown-upwards) - :on-change (partial on-type-change index)}]] - (when (scale-enabled? export) - [:div {:class (stl/css :size-select)} - [:& select - {:default-value (str (:scale export)) - :options size-options - :dropdown-class (stl/css :dropdown-upwards) - :on-change (partial on-scale-change index)}]]) - [:label {:class (stl/css :suffix-input) - :for "suffix-export-input"} - [:input {:class (stl/css :type-input) - :id "suffix-export-input" - :type "text" - :value (:suffix export) - :placeholder (tr "workspace.options.export.suffix") - :data-value index - :on-change on-suffix-change - :on-key-down manage-key-down}]]] + :on-change (partial on-scale-change index)}]]) + [:label {:class (stl/css :suffix-input) + :for "suffix-export-input"} + [:input {:class (stl/css :type-input) + :id "suffix-export-input" + :type "text" + :value (:suffix export) + :placeholder (tr "workspace.options.export.suffix") + :data-value index + :on-change on-suffix-change + :on-key-down manage-key-down}]]] - [:button {:class (stl/css :action-btn) - :on-click (partial delete-export index)} - i/remove-refactor]])]) + [:button {:class (stl/css :action-btn) + :on-click (partial delete-export index)} + i/remove-refactor]])]) - (when (or (= :multiple exports) (seq exports)) - [:button - {:on-click (when-not in-progress? on-download) - :class (stl/css-case - :export-btn true - :btn-disabled in-progress?) - :disabled in-progress?} - (if in-progress? - (tr "workspace.options.exporting-object") - (tr "workspace.options.export-object" (c (count shapes-with-exports))))])])] - - - [:div.element-set.exports-options - [:div.element-set-title - [:span (tr (if (> (count ids) 1) "workspace.options.export-multiple" "workspace.options.export"))] - (when (not (= :multiple exports)) - [:div.add-page {:on-click add-export} i/close])] - - (cond - (= :multiple exports) - [:div.element-set-options-group - [:div.element-set-label (tr "settings.multiple")] - [:div.element-set-actions - [:div.element-set-actions-button {:on-click on-remove-all} - i/minus]]] - - (seq exports) - [:div.element-set-content - (for [[index export] (d/enumerate exports)] - [:div.element-set-options-group - {:key index} - (when (scale-enabled? export) - [:select.input-select {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :on-change (partial on-scale-change index) - :value (:scale export)} - [:option {:value "0.5"} "0.5x"] - [:option {:value "0.75"} "0.75x"] - [:option {:value "1"} "1x"] - [:option {:value "1.5"} "1.5x"] - [:option {:value "2"} "2x"] - [:option {:value "4"} "4x"] - [:option {:value "6"} "6x"]]) - [:input.input-text {:value (:suffix export) - :placeholder (tr "workspace.options.export.suffix") - :data-value index - :on-change on-suffix-change - :on-key-down manage-key-down}] - [:select.input-select {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :value (d/name (:type export)) - :on-change (partial on-type-change index)} - [:option {:value "png"} "PNG"] - [:option {:value "jpeg"} "JPEG"] - [:option {:value "svg"} "SVG"] - [:option {:value "pdf"} "PDF"]] - [:div.delete-icon {:on-click (partial delete-export index)} - i/minus]])]) - - (when (or (= :multiple exports) (seq exports)) - [:div.btn-icon-dark.download-button - {:on-click (when-not in-progress? on-download) - :class (dom/classnames - :btn-disabled in-progress?) - :disabled in-progress?} - (if in-progress? - (tr "workspace.options.exporting-object") - (tr "workspace.options.export-object" (c (count shapes-with-exports))))])]))) + (when (or (= :multiple exports) (seq exports)) + [:button + {:on-click (when-not in-progress? on-download) + :class (stl/css-case + :export-btn true + :btn-disabled in-progress?) + :disabled in-progress?} + (if in-progress? + (tr "workspace.options.exporting-object") + (tr "workspace.options.export-object" (c (count shapes-with-exports))))])])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.scss index 414d9f7cbc..6bbb39afd9 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.scss @@ -8,12 +8,35 @@ .element-set { margin: 0; - .element-title { - .title-spacing-export { - padding-left: $s-2; - margin: 0; - } - .add-export { +} + +.title-spacing-export { + padding-left: $s-2; + margin: 0; +} + +.add-export { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } +} + +.element-set-content { + @include flexColumn; + margin: $s-4 0 $s-8 0; +} + +.multiple-exports { + @include flexRow; + .label { + @extend .mixed-bar; + } + .actions { + @include flexRow; + .action-btn { @extend .button-tertiary; height: $s-32; width: $s-28; @@ -22,68 +45,58 @@ } } } - .element-set-content { - @include flexColumn; - margin: $s-4 0 $s-8 0; - .multiple-exports { - @include flexRow; - .label { - @extend .mixed-bar; - } - .actions { - @include flexRow; - .action-btn { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - } - } - } - .element-group { - @include flexRow; - .input-wrapper { - @include flexRow; - .format-select { - width: $s-60; - padding: 0; - .dropdown-upwards { - bottom: $s-36; - width: $s-80; - top: unset; - } - } - .size-select { - width: $s-60; - padding: 0; - .dropdown-upwards { - bottom: $s-36; - top: unset; - width: $s-80; - } - } - .suffix-input { - @extend .input-element; - min-width: $s-92; - flex-grow: 1; - } - } - .action-btn { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - } - } - .export-btn { - @extend .button-secondary; - @include tabTitleTipography; - height: $s-32; - width: $s-252; +} + +.element-group { + display: grid; + grid-template-columns: repeat(8, 1fr); + column-gap: $s-4; + + .action-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; } } } + +.input-wrapper { + grid-column: span 7; + display: grid; + grid-template-columns: subgrid; +} + +.format-select { + grid-column: span 2; + padding: 0; + + .dropdown-upwards { + bottom: $s-36; + width: $s-80; + top: unset; + } +} + +.size-select { + grid-column: span 2; + padding: 0; + .dropdown-upwards { + bottom: $s-36; + top: unset; + width: $s-80; + } +} + +.suffix-input { + grid-column: span 3; + @extend .input-element; +} + +.export-btn { + @extend .button-secondary; + @include tabTitleTipography; + height: $s-32; + width: $s-252; +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs index 948c5228f4..58fe4f1e92 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs @@ -13,7 +13,6 @@ [app.main.data.workspace.colors :as dc] [app.main.store :as st] [app.main.ui.components.title-bar :refer [title-bar]] - [app.main.ui.context :as ctx] [app.main.ui.hooks :as h] [app.main.ui.icons :as i] [app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]] @@ -44,8 +43,7 @@ (mf/defc fill-menu {::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values"]))]} [{:keys [ids type values disable-remove?] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - label (case type + (let [label (case type :multiple (tr "workspace.options.selection-fill") :group (tr "workspace.options.group-fill") (tr "workspace.options.fill")) @@ -137,83 +135,28 @@ (dom/set-attribute! checkbox "indeterminate" true) (dom/remove-attribute! checkbox "indeterminate"))))) - (if new-css-system - [:div {:class (stl/css :element-set)} - [:div {:class (stl/css :element-title)} - [:& title-bar {:collapsable? has-fills? - :collapsed? (not open?) - :on-collapsed toggle-content - :title label - :class (stl/css-case :title-spacing-fill (not has-fills?))} + [:div {:class (stl/css :element-set)} + [:div {:class (stl/css :element-title)} + [:& title-bar {:collapsable? has-fills? + :collapsed? (not open?) + :on-collapsed toggle-content + :title label + :class (stl/css-case :title-spacing-fill (not has-fills?))} - (when (and (not disable-remove?) (not (= :multiple fills))) - [:button {:class (stl/css :add-fill) - :on-click on-add} i/add-refactor])]] - - (when open? - [:div {:class (stl/css :element-content)} - (cond - (= :multiple fills) - [:div {:class (stl/css :element-set-options-group)} - [:div {:class (stl/css :group-label)} - (tr "settings.multiple")] - [:button {:on-click on-remove-all - :class (stl/css :remove-btn)} - i/remove-refactor]] - - (seq fills) - [:& h/sortable-container {} - (for [[index value] (d/enumerate (:fills values []))] - [:& color-row {:color {:color (:fill-color value) - :opacity (:fill-opacity value) - :id (:fill-color-ref-id value) - :file-id (:fill-color-ref-file value) - :gradient (:fill-color-gradient value) - :image (:fill-image value)} - :key index - :index index - :title (tr "workspace.options.fill") - :on-change (on-change index) - :on-reorder (on-reorder index) - :on-detach (on-detach index) - :on-remove (on-remove index) - :disable-drag disable-drag - :on-focus on-focus - :select-on-focus (not @disable-drag) - :on-blur on-blur}])]) - - (when (or (= type :frame) - (and (= type :multiple) (some? (:hide-fill-on-export values)))) - [:div {:class (stl/css :checkbox)} - [:label {:for "show-fill-on-export" - :class (stl/css-case :global/checked (not hide-fill-on-export?))} - [:span {:class (stl/css-case :check-mark true - :checked (not hide-fill-on-export?))} - (when (not hide-fill-on-export?) - i/status-tick-refactor)] - (tr "workspace.options.show-fill-on-export") - [:input {:type "checkbox" - :id "show-fill-on-export" - :ref checkbox-ref - :checked (not hide-fill-on-export?) - :on-change on-change-show-fill-on-export}]]])])] - - ;; OLD - [:div.element-set - [:div.element-set-title - [:span label] - (when (and (not disable-remove?) (not (= :multiple fills))) - [:div.add-page {:on-click on-add} i/close])] - - [:div.element-set-content + (when (and (not disable-remove?) (not (= :multiple fills))) + [:button {:class (stl/css :add-fill) + :on-click on-add} i/add-refactor])]] + (when open? + [:div {:class (stl/css :element-content)} (cond (= :multiple fills) - [:div.element-set-options-group - [:div.element-set-label (tr "settings.multiple")] - [:div.element-set-actions - [:div.element-set-actions-button {:on-click on-remove-all} - i/minus]]] + [:div {:class (stl/css :element-set-options-group)} + [:div {:class (stl/css :group-label)} + (tr "settings.multiple")] + [:button {:on-click on-remove-all + :class (stl/css :remove-btn)} + i/remove-refactor]] (seq fills) [:& h/sortable-container {} @@ -238,12 +181,16 @@ (when (or (= type :frame) (and (= type :multiple) (some? (:hide-fill-on-export values)))) - [:div.input-checkbox - [:input {:type "checkbox" - :id "show-fill-on-export" - :ref checkbox-ref - :checked (not hide-fill-on-export?) - :on-change on-change-show-fill-on-export}] - - [:label {:for "show-fill-on-export"} - (tr "workspace.options.show-fill-on-export")]])]]))) + [:div {:class (stl/css :checkbox)} + [:label {:for "show-fill-on-export" + :class (stl/css-case :global/checked (not hide-fill-on-export?))} + [:span {:class (stl/css-case :check-mark true + :checked (not hide-fill-on-export?))} + (when (not hide-fill-on-export?) + i/status-tick-refactor)] + (tr "workspace.options.show-fill-on-export") + [:input {:type "checkbox" + :id "show-fill-on-export" + :ref checkbox-ref + :checked (not hide-fill-on-export?) + :on-change on-change-show-fill-on-export}]]])])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.scss index 7bcc45db94..08b5581dbd 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.scss @@ -8,50 +8,58 @@ .element-set { margin: 0; - .element-title { - margin: 0; - .title-spacing-fill { - padding-left: $s-2; - margin: 0; - } - .add-fill { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - } +} + +.element-title { + margin: 0; +} + +.title-spacing-fill { + padding-left: $s-2; + margin: 0; +} + +.add-fill { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; } - .element-content { - display: flex; - flex-direction: column; - gap: $s-12; - margin: $s-4 0 $s-8 0; - .element-set-options-group { - @include flexRow; - .group-label { - @extend .mixed-bar; - } - .remove-btn { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - } - } - .checkbox { - @extend .input-checkbox; - padding-left: $s-8; - span.checked { - background-color: var(--input-border-color-active); - svg { - @extend .button-icon-small; - stroke: var(--input-details-color); - } - } +} + +.element-content { + display: flex; + flex-direction: column; + gap: $s-12; + margin: $s-4 0 $s-8 0; +} + +.element-set-options-group { + @include flexRow; +} + +.group-label { + @extend .mixed-bar; +} + +.remove-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } +} + +.checkbox { + @extend .input-checkbox; + padding-left: $s-8; + span.checked { + background-color: var(--input-border-color-active); + svg { + @extend .button-icon-small; + stroke: var(--input-details-color); } } } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs index 2f326dddfb..8191cf0908 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs @@ -15,11 +15,9 @@ [app.main.ui.components.numeric-input :refer [numeric-input*]] [app.main.ui.components.select :refer [select]] [app.main.ui.components.title-bar :refer [title-bar]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.ui.workspace.sidebar.options.common :refer [advanced-options]] [app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]] - [app.main.ui.workspace.sidebar.options.rows.input-row :refer [input-row input-row-v2]] [app.util.i18n :as i18n :refer [tr]] [okulary.core :as l] [rumext.v2 :as mf])) @@ -35,8 +33,7 @@ (mf/defc grid-options {::mf/wrap [mf/memo]} [{:keys [shape-id index grid frame-width frame-height default-grid-params]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - on-change (mf/use-fn (mf/deps shape-id index) #(st/emit! (dw/set-frame-grid shape-id index %))) + (let [on-change (mf/use-fn (mf/deps shape-id index) #(st/emit! (dw/set-frame-grid shape-id index %))) on-remove (mf/use-fn (mf/deps shape-id index) #(st/emit! (dw/remove-frame-grid shape-id index))) on-save-default (mf/use-fn #(st/emit! (dw/set-default-grid (:type %) (:params %)))) @@ -136,215 +133,87 @@ is-default (= (->> grid :params) (->> grid :type default-grid-params))] - (if new-css-system - [:div {:class (stl/css :grid-option)} - [:div {:class (stl/css :grid-title)} - [:div {:class (stl/css-case :option-row true - :hidden is-hidden?)} - [:button {:class (stl/css-case :show-options true - :selected open?) - :on-click toggle-advanced-options} - i/menu-refactor] - [:div {:class (stl/css :type-select-wrapper)} - [:& select - {:class (stl/css :grid-type-select) - :default-value type - :options [{:value :square :label (tr "workspace.options.grid.square")} - {:value :column :label (tr "workspace.options.grid.column")} - {:value :row :label (tr "workspace.options.grid.row")}] - :on-change handle-change-type}]] - (if (= type :square) - [:div {:class (stl/css :grid-size) - :title (tr "workspace.options.size")} - [:> numeric-input* {:min 0.01 - :value (or (:size params) "") - :no-validate true - :className (stl/css :numeric-input) - :on-change (handle-change :params :size)}]] - - [:div {:class (stl/css :editable-select-wrapper)} - [:& editable-select {:value (:size params) - :type "number" - :class (stl/css :column-select) - :input-class (stl/css :numeric-input) - :min 1 - :options size-options - :placeholder "Auto" - :on-change handle-change-size}]])] - - [:div {:class (stl/css :actions)} - [:button {:class (stl/css :action-btn) - :on-click handle-toggle-visibility} - (if display i/shown-refactor i/hide-refactor)] - [:button {:class (stl/css :action-btn) - :on-click on-remove} - i/remove-refactor]]] - - (when (:display grid) - [:& advanced-options {:class (stl/css :grid-advanced-options) - :visible? open? - :on-close toggle-advanced-options} - ;; square - (when (= :square type) - [:div {:class (stl/css :square-row)} - [:div {:class (stl/css :advanced-row)} - [:& color-row {:color (:color params) - :title (tr "workspace.options.grid.params.color") - :disable-gradient true - :disable-image true - :on-change handle-change-color - :on-detach handle-detach-color}] - [:button {:class (stl/css-case :show-more-options true - :selected show-more-options?) - :on-click toggle-more-options} - i/menu-refactor]] - (when show-more-options? - [:div {:class (stl/css :second-row)} - [:button {:class (stl/css-case :btn-options true - :disabled is-default) - :disabled is-default - :on-click handle-use-default} - [:span (tr "workspace.options.grid.params.use-default")]] - [:button {:class (stl/css-case :btn-options true - :disabled is-default) - :disabled is-default - :on-click handle-set-as-default} - [:span (tr "workspace.options.grid.params.set-default")]]])]) - - (when (or (= :column type) (= :row type)) - [:div {:class (stl/css :column-row)} - [:div {:class (stl/css :advanced-row)} - [:div {:class (stl/css :orientation-select-wrapper)} - [:& select {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :default-value (:type params) - :class (stl/css :orientation-select) - :options [{:value :stretch :label (tr "workspace.options.grid.params.type.stretch")} - {:value :left :label (if (= type :row) - (tr "workspace.options.grid.params.type.top") - (tr "workspace.options.grid.params.type.left"))} - {:value :center :label (tr "workspace.options.grid.params.type.center")} - {:value :right :label (if (= type :row) - (tr "workspace.options.grid.params.type.bottom") - (tr "workspace.options.grid.params.type.right"))}] - :on-change (handle-change :params :type)}]] - - [:div {:class (stl/css :color-wrapper)} - [:& color-row {:color (:color params) - :title (tr "workspace.options.grid.params.color") - :disable-gradient true - :disable-image true - :on-change handle-change-color - :on-detach handle-detach-color}]]] - - [:div {:class (stl/css :advanced-row)} - [:div {:class (stl/css :height) - :title (if (= :row type) - (tr "workspace.options.grid.params.height") - (tr "workspace.options.grid.params.width"))} - [:span {:class (stl/css :icon-text)} - (if (= :row type) - "H" - "W")] - [:> numeric-input* {:placeholder "Auto" - :on-change handle-change-item-length - :nillable true - :className (stl/css :numeric-input) - :value (or (:item-length params) "")}]] - - [:div {:class (stl/css :gutter) - :title (tr "workspace.options.grid.params.gutter")} - [:span {:class (stl/css-case :icon true - :rotated (= type :row))} i/gap-horizontal-refactor] - [:> numeric-input* {:placeholder "0" - :on-change (handle-change :params :gutter) - :nillable true - :className (stl/css :numeric-input) - :value (or (:gutter params) 0)}]] - - [:div {:class (stl/css :margin) - :title (tr "workspace.options.grid.params.margin")} - [:span {:class (stl/css-case :icon true - :rotated (= type :column))} i/grid-margin-refactor] - [:> numeric-input* {:placeholder "0" - :on-change (handle-change :params :margin) - :nillable true - :className (stl/css :numeric-input) - :value (or (:margin params) 0)}]] - - [:button {:class (stl/css-case :show-more-options true - :selected show-more-options?) - :on-click toggle-more-options} - i/menu-refactor] - (when show-more-options? - [:div {:class (stl/css :more-options)} - [:button {:disabled is-default - :class (stl/css :option-btn) - :on-click handle-use-default} (tr "workspace.options.grid.params.use-default")] - [:button {:disabled is-default - :class (stl/css :option-btn) - :on-click handle-set-as-default} (tr "workspace.options.grid.params.set-default")]])]])])] - - [:div.grid-option - [:div.grid-option-main {:style {:display (when open? "none")}} - [:button.custom-button {:class (when open? "is-active") - :on-click toggle-advanced-options} i/actions] + [:div {:class (stl/css :grid-option)} + [:div {:class (stl/css :grid-title)} + [:div {:class (stl/css-case :option-row true + :hidden is-hidden?)} + [:button {:class (stl/css-case :show-options true + :selected open?) + :on-click toggle-advanced-options} + i/menu-refactor] + [:div {:class (stl/css :type-select-wrapper)} [:& select - {:class "flex-grow" + {:class (stl/css :grid-type-select) :default-value type :options [{:value :square :label (tr "workspace.options.grid.square")} {:value :column :label (tr "workspace.options.grid.column")} {:value :row :label (tr "workspace.options.grid.row")}] - :on-change handle-change-type}] - - (if (= type :square) - [:div.input-element.pixels {:title (tr "workspace.options.size")} - [:> numeric-input* {:min 0.01 - :value (or (:size params) "") - :no-validate true - :on-change (handle-change :params :size)}]] + :on-change handle-change-type}]] + (if (= type :square) + [:div {:class (stl/css :grid-size) + :title (tr "workspace.options.size")} + [:> numeric-input* {:min 0.01 + :value (or (:size params) "") + :no-validate true + :className (stl/css :numeric-input) + :on-change (handle-change :params :size)}]] + [:div {:class (stl/css :editable-select-wrapper)} [:& editable-select {:value (:size params) :type "number" - :class "input-option" + :class (stl/css :column-select) + :input-class (stl/css :numeric-input) :min 1 :options size-options :placeholder "Auto" - :on-change handle-change-size}]) + :on-change handle-change-size}]])] - [:div.grid-option-main-actions - [:button.custom-button {:on-click handle-toggle-visibility} (if display i/eye i/eye-closed)] - [:button.custom-button {:on-click on-remove} i/minus]]] + [:div {:class (stl/css :actions)} + [:button {:class (stl/css :action-btn) + :on-click handle-toggle-visibility} + (if display i/shown-refactor i/hide-refactor)] + [:button {:class (stl/css :action-btn) + :on-click on-remove} + i/remove-refactor]]] - [:& advanced-options {:visible? open? :on-close toggle-advanced-options} - [:button.custom-button {:on-click toggle-advanced-options} i/actions] + (when (:display grid) + [:& advanced-options {:class (stl/css :grid-advanced-options) + :visible? open? + :on-close toggle-advanced-options} + ;; square (when (= :square type) - [:& input-row {:label (tr "workspace.options.grid.params.size") - :class "pixels" - :min 0.01 - :value (:size params) - :on-change (handle-change :params :size)}]) + [:div {:class (stl/css :square-row)} + [:div {:class (stl/css :advanced-row)} + [:& color-row {:color (:color params) + :title (tr "workspace.options.grid.params.color") + :disable-gradient true + :disable-image true + :on-change handle-change-color + :on-detach handle-detach-color}] + [:button {:class (stl/css-case :show-more-options true + :selected show-more-options?) + :on-click toggle-more-options} + i/menu-refactor]] + (when show-more-options? + [:div {:class (stl/css :second-row)} + [:button {:class (stl/css-case :btn-options true + :disabled is-default) + :disabled is-default + :on-click handle-use-default} + [:span (tr "workspace.options.grid.params.use-default")]] + [:button {:class (stl/css-case :btn-options true + :disabled is-default) + :disabled is-default + :on-click handle-set-as-default} + [:span (tr "workspace.options.grid.params.set-default")]]])]) - (when (= :row type) - [:& input-row {:label (tr "workspace.options.grid.params.rows") - :type :editable-select - :options size-options - :value (:size params) - :min 1 - :placeholder "Auto" - :on-change handle-change-size}]) - - (when (= :column type) - [:& input-row {:label (tr "workspace.options.grid.params.columns") - :type :editable-select - :options size-options - :value (:size params) - :min 1 - :placeholder "Auto" - :on-change handle-change-size}]) - - (when (#{:row :column} type) - [:& input-row {:label (tr "workspace.options.grid.params.type") - :type :select + (when (or (= :column type) (= :row type)) + [:div {:class (stl/css :column-row)} + [:div {:class (stl/css :advanced-row)} + [:div {:class (stl/css :orientation-select-wrapper)} + [:& select {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element + :default-value (:type params) + :class (stl/css :orientation-select) :options [{:value :stretch :label (tr "workspace.options.grid.params.type.stretch")} {:value :left :label (if (= type :row) (tr "workspace.options.grid.params.type.top") @@ -353,56 +222,67 @@ {:value :right :label (if (= type :row) (tr "workspace.options.grid.params.type.bottom") (tr "workspace.options.grid.params.type.right"))}] - :value (:type params) - :on-change (handle-change :params :type)}]) + :on-change (handle-change :params :type)}]] - (when (#{:row :column} type) - [:& input-row-v2 - {:class "pixels" - :label (if (= :row type) - (tr "workspace.options.grid.params.height") - (tr "workspace.options.grid.params.width"))} - [:> numeric-input* - {:placeholder "Auto" - :value (or (:item-length params) "") - :nillable true - :on-change handle-change-item-length}]]) + [:div {:class (stl/css :color-wrapper)} + [:& color-row {:color (:color params) + :title (tr "workspace.options.grid.params.color") + :disable-gradient true + :disable-image true + :on-change handle-change-color + :on-detach handle-detach-color}]]] - (when (#{:row :column} type) - [:* - [:& input-row {:label (tr "workspace.options.grid.params.gutter") - :class "pixels" - :value (:gutter params) - :min 0 - :nillable true - :default 0 - :placeholder "0" - :on-change (handle-change :params :gutter)}] - [:& input-row {:label (tr "workspace.options.grid.params.margin") - :class "pixels" - :min 0 - :nillable true - :default 0 - :placeholder "0" - :value (:margin params) - :on-change (handle-change :params :margin)}]]) + [:div {:class (stl/css :advanced-row)} + [:div {:class (stl/css :height) + :title (if (= :row type) + (tr "workspace.options.grid.params.height") + (tr "workspace.options.grid.params.width"))} + [:span {:class (stl/css :icon-text)} + (if (= :row type) + "H" + "W")] + [:> numeric-input* {:placeholder "Auto" + :on-change handle-change-item-length + :nillable true + :className (stl/css :numeric-input) + :value (or (:item-length params) "")}]] - [:& color-row {:color (:color params) - :title (tr "workspace.options.grid.params.color") - :disable-gradient true - :disable-image true - :on-change handle-change-color - :on-detach handle-detach-color}] - [:div.row-flex - [:button.btn-options {:disabled is-default - :on-click handle-use-default} (tr "workspace.options.grid.params.use-default")] - [:button.btn-options {:disabled is-default - :on-click handle-set-as-default} (tr "workspace.options.grid.params.set-default")]]]]))) + [:div {:class (stl/css :gutter) + :title (tr "workspace.options.grid.params.gutter")} + [:span {:class (stl/css-case :icon true + :rotated (= type :row))} i/gap-horizontal-refactor] + [:> numeric-input* {:placeholder "0" + :on-change (handle-change :params :gutter) + :nillable true + :className (stl/css :numeric-input) + :value (or (:gutter params) 0)}]] + + [:div {:class (stl/css :margin) + :title (tr "workspace.options.grid.params.margin")} + [:span {:class (stl/css-case :icon true + :rotated (= type :column))} i/grid-margin-refactor] + [:> numeric-input* {:placeholder "0" + :on-change (handle-change :params :margin) + :nillable true + :className (stl/css :numeric-input) + :value (or (:margin params) 0)}]] + + [:button {:class (stl/css-case :show-more-options true + :selected show-more-options?) + :on-click toggle-more-options} + i/menu-refactor] + (when show-more-options? + [:div {:class (stl/css :more-options)} + [:button {:disabled is-default + :class (stl/css :option-btn) + :on-click handle-use-default} (tr "workspace.options.grid.params.use-default")] + [:button {:disabled is-default + :class (stl/css :option-btn) + :on-click handle-set-as-default} (tr "workspace.options.grid.params.set-default")]])]])])])) (mf/defc frame-grid [{:keys [shape]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - state* (mf/use-state true) + (let [state* (mf/use-state true) open? (deref state*) frame-grids (:grids shape) has-frame-grids? (or (= :multiple frame-grids) (some? (seq frame-grids))) @@ -413,44 +293,26 @@ default-grid-params (mf/use-memo (mf/deps saved-grids) #(merge dw/default-grid-params saved-grids)) handle-create-grid (mf/use-fn (mf/deps id) #(st/emit! (dw/add-frame-grid id)))] - (if new-css-system - [:div {:class (stl/css :element-set)} - [:& title-bar {:collapsable? has-frame-grids? - :collapsed? (not open?) - :on-collapsed toggle-content - :class (stl/css-case :title-spacing-board-grid (not has-frame-grids?)) - :title (tr "workspace.options.guides.title")} + [:div {:class (stl/css :element-set)} + [:& title-bar {:collapsable? has-frame-grids? + :collapsed? (not open?) + :on-collapsed toggle-content + :class (stl/css-case :title-spacing-board-grid (not has-frame-grids?)) + :title (tr "workspace.options.guides.title")} - [:button {:on-click handle-create-grid - :class (stl/css :add-grid)} - i/add-refactor]] + [:button {:on-click handle-create-grid + :class (stl/css :add-grid)} + i/add-refactor]] - (when (and open? (seq frame-grids)) - [:div {:class (stl/css :element-set-content)} - (for [[index grid] (map-indexed vector frame-grids)] - [:& grid-options {:key (str id "-" index) - :shape-id id - :grid grid - :index index - :frame-width (:width shape) - :frame-height (:height shape) - :default-grid-params default-grid-params}])])] - - - [:div.element-set - [:div.element-set-title - [:span (tr "workspace.options.grid.grid-title")] - [:div.add-page {:on-click handle-create-grid} i/close]] - - (when (seq frame-grids) - [:div.element-set-content - (for [[index grid] (map-indexed vector frame-grids)] - [:& grid-options {:key (str id "-" index) - :shape-id id - :grid grid - :index index - :frame-width (:width shape) - :frame-height (:height shape) - :default-grid-params default-grid-params}])])]))) + (when (and open? (seq frame-grids)) + [:div {:class (stl/css :element-set-content)} + (for [[index grid] (map-indexed vector frame-grids)] + [:& grid-options {:key (str id "-" index) + :shape-id id + :grid grid + :index index + :frame-width (:width shape) + :frame-height (:height shape) + :default-grid-params default-grid-params}])])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.scss index 0435242e82..4a23132e71 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.scss @@ -8,241 +8,250 @@ .element-set { margin: 0; - .title-spacing-board-grid { - padding-left: $s-2; - margin: 0; +} + +.title-spacing-board-grid { + padding-left: $s-2; + margin: 0; +} + +.add-grid { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; } - .add-grid { - @extend .button-tertiary; +} + +.element-set-content { + @include flexColumn; + margin: $s-4 0 $s-8 0; +} + +.grid-title { + @include flexRow; +} + +.option-row { + display: flex; + align-items: center; + gap: $s-1; + border-radius: $br-8; + background-color: var(--input-details-color); + .show-options { + @extend .button-secondary; height: $s-32; width: $s-28; + border-radius: $br-8 0 0 $br-8; + box-sizing: border-box; + border: $s-1 solid var(--input-background-color); svg { @extend .button-icon; } + &.selected { + @extend .button-icon-selected; + } } - .element-set-content { - @include flexColumn; - margin: $s-4 0 $s-8 0; - .grid-option { - .grid-title { - @include flexRow; - .option-row { - display: flex; - align-items: center; - gap: $s-1; - border-radius: $br-8; - background-color: var(--input-details-color); - .show-options { - @extend .button-secondary; - height: $s-32; - width: $s-28; - border-radius: $br-8 0 0 $br-8; - box-sizing: border-box; - border: $s-1 solid var(--input-background-color); - svg { - @extend .button-icon; - } - &.selected { - @extend .button-icon-selected; - } - } - .type-select-wrapper { - width: $s-96; - padding: 0; - border-radius: 0; - height: $s-32; - .grid-type-select { - border-radius: 0; - height: 100%; - box-sizing: border-box; - border: $s-1 solid var(--input-background-color); - &:hover { - border: $s-1 solid var(--input-background-color-hover); - } - } - } - .grid-size { - @extend .asset-element; - width: $s-60; - margin: 0; - padding: 0; - padding-left: $s-8; - border-radius: 0 $br-8 $br-8 0; - .numeric-input { - @extend .input-base; - } - } - .editable-select-wrapper { - @extend .asset-element; - width: $s-60; - margin: 0; - padding: 0; - position: relative; - border-radius: 0 $br-8 $br-8 0; - .column-select { - height: $s-32; - border-radius: 0 $br-8 $br-8 0; - box-sizing: border-box; - border: $s-1 solid var(--input-background-color); - .numeric-input { - @extend .input-base; - margin: 0; - padding: 0; - } - span { - @include flexCenter; - svg { - @extend .button-icon; - } - } - } - } - - &.hidden { - .show-options { - @include hiddenElement; - border: $s-1 solid var(--input-border-color-disabled); - } - .type-select-wrapper, - .editable-select-wrapper { - @include hiddenElement; - .column-select, - .grid-type-select { - @include hiddenElement; - border: $s-1 solid var(--input-border-color-disabled); - } - .column-select { - @include hiddenElement; - border-radius: 0 $br-8 $br-8 0; - .numeric-input { - @include hiddenElement; - } - } - } - .grid-size { - @include hiddenElement; - border: $s-1 solid var(--input-border-color-disabled); - .icon { - stroke: var(--input-foreground-color-disabled); - } - .numeric-input { - color: var(--input-foreground-color-disabled); - } - } - .actions { - .hidden-btn, - .lock-btn { - background-color: transparent; - svg { - stroke: var(--input-foreground-color-disabled); - } - } - } - } - } - - .actions { - @include flexRow; - .action-btn { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - } + .type-select-wrapper { + width: $s-96; + padding: 0; + border-radius: 0; + height: $s-32; + .grid-type-select { + border-radius: 0; + height: 100%; + box-sizing: border-box; + border: $s-1 solid var(--input-background-color); + &:hover { + border: $s-1 solid var(--input-background-color-hover); + } + } + } + .grid-size { + @extend .asset-element; + width: $s-60; + margin: 0; + padding: 0; + padding-left: $s-8; + border-radius: 0 $br-8 $br-8 0; + .numeric-input { + @extend .input-base; + } + } + .editable-select-wrapper { + @extend .asset-element; + width: $s-60; + margin: 0; + padding: 0; + position: relative; + border-radius: 0 $br-8 $br-8 0; + .column-select { + height: $s-32; + border-radius: 0 $br-8 $br-8 0; + box-sizing: border-box; + border: $s-1 solid var(--input-background-color); + .numeric-input { + @extend .input-base; + margin: 0; + padding: 0; + } + span { + @include flexCenter; + svg { + @extend .button-icon; } } } - .grid-advanced-options { - @include flexColumn; - margin-top: $s-4; - .column-row, - .square-row { - @include flexColumn; - position: relative; - .advanced-row { - position: relative; - display: flex; - gap: $s-4; - .orientation-select-wrapper { - width: $s-92; - padding: 0; - } - .color-wrapper { - width: $s-156; - } - .show-more-options { - @extend .button-tertiary; - height: $s-32; - width: $s-32; - svg { - @extend .button-icon; - } - &.selected { - @extend .button-icon-selected; - } - } - .height { - @extend .input-element; - width: $s-108; - .icon-text { - padding-top: $s-1; - } - } - .gutter, - .margin { - @extend .input-element; - width: $s-108; - .icon { - &.rotated svg { - transform: rotate(90deg); - } - } - } - .more-options { - @include menuShadow; - @include flexColumn; - position: absolute; - top: calc($s-2 + $s-28); - right: 0; - width: $s-156; - max-height: $s-300; - padding: $s-2; - margin: 0 0 $s-40 0; - margin-top: $s-4; - border-radius: $br-8; - z-index: $z-index-3; - overflow-y: auto; - background-color: var(--menu-background-color); - .option-btn { - @include buttonStyle; - display: flex; - align-items: center; - height: $s-32; - padding: 0 $s-8; - border-radius: $br-6; - color: var(--menu-foreground-color); + } - &:hover { - background-color: var(--menu-background-color-hover); - color: var(--menu-foreground-color-hover); - } - } - } + &.hidden { + .show-options { + @include hiddenElement; + border: $s-1 solid var(--input-border-color-disabled); + } + .type-select-wrapper, + .editable-select-wrapper { + @include hiddenElement; + .column-select, + .grid-type-select { + @include hiddenElement; + border: $s-1 solid var(--input-border-color-disabled); + } + .column-select { + @include hiddenElement; + border-radius: 0 $br-8 $br-8 0; + .numeric-input { + @include hiddenElement; } - .second-row { - @extend .dropdown-wrapper; - left: unset; - right: 0; - width: $s-108; - .btn-options { - @include buttonStyle; - @extend .dropdown-element-base; - width: 100%; - } + } + } + .grid-size { + @include hiddenElement; + border: $s-1 solid var(--input-border-color-disabled); + .icon { + stroke: var(--input-foreground-color-disabled); + } + .numeric-input { + color: var(--input-foreground-color-disabled); + } + } + .actions { + .hidden-btn, + .lock-btn { + background-color: transparent; + svg { + stroke: var(--input-foreground-color-disabled); } } } } } + +.actions { + @include flexRow; +} + +.action-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } +} + +.grid-advanced-options { + @include flexColumn; + margin-top: $s-4; +} + +.column-row, +.square-row { + @include flexColumn; + position: relative; +} + +.advanced-row { + position: relative; + display: flex; + gap: $s-4; + .orientation-select-wrapper { + width: $s-92; + padding: 0; + } + .color-wrapper { + width: $s-156; + } + .show-more-options { + @extend .button-tertiary; + height: $s-32; + width: $s-32; + svg { + @extend .button-icon; + } + &.selected { + @extend .button-icon-selected; + } + } + .height { + @extend .input-element; + width: $s-108; + .icon-text { + padding-top: $s-1; + } + } + .gutter, + .margin { + @extend .input-element; + width: $s-108; + .icon { + &.rotated svg { + transform: rotate(90deg); + } + } + } + + .more-options { + @include menuShadow; + @include flexColumn; + position: absolute; + top: calc($s-2 + $s-28); + right: 0; + width: $s-156; + max-height: $s-300; + padding: $s-2; + margin: 0 0 $s-40 0; + margin-top: $s-4; + border-radius: $br-8; + z-index: $z-index-3; + overflow-y: auto; + background-color: var(--menu-background-color); + .option-btn { + @include buttonStyle; + display: flex; + align-items: center; + height: $s-32; + padding: 0 $s-8; + border-radius: $br-6; + color: var(--menu-foreground-color); + + &:hover { + background-color: var(--menu-background-color-hover); + color: var(--menu-foreground-color-hover); + } + } + } +} + +.second-row { + @extend .dropdown-wrapper; + left: unset; + right: 0; + width: $s-108; + .btn-options { + @include buttonStyle; + @extend .dropdown-element-base; + width: 100%; + } +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs index ffd4f82d6f..7d9da91219 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs @@ -18,10 +18,8 @@ [app.main.ui.components.numeric-input :refer [numeric-input*]] [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] [app.main.ui.components.title-bar :refer [title-bar]] - [app.main.ui.context :as ctx] [app.main.ui.hooks :as hooks] [app.main.ui.icons :as i] - [app.main.ui.workspace.sidebar.options.menus.layout-container :as lyc] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) @@ -38,9 +36,7 @@ (mf/defc set-self-alignment [{:keys [is-col? alignment set-alignment] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - dir-v [:auto :start :center :end :stretch #_:baseline] - alignment (or alignment :auto) + (let [alignment (or alignment :auto) type (if is-col? "col" "row") handle-set-alignment @@ -49,8 +45,7 @@ (fn [value] (set-alignment (-> value keyword))))] - (if new-css-system - [:div {:class (stl/css :self-align-menu)} + [:div {:class (stl/css :self-align-menu)} [:& radio-buttons {:selected (d/name alignment) :on-change handle-set-alignment :name (dm/str "flex-align-items-" type)} @@ -72,27 +67,14 @@ [:& radio-button {:value "stretch" :icon (if is-col? i/align-self-row-strech i/align-self-column-strech) :title "Align self stretch" - :id (dm/str "align-self-stretch-" type)}]]] - - [:div.align-self-style - (for [align dir-v] - [:button.align-self.tooltip.tooltip-bottom - {:class (dom/classnames :active (= alignment align) - :tooltip-bottom-left (not= align :start) - :tooltip-bottom (= align :start)) - :alt (dm/str "Align self " (d/name align)) ;; TODO fix this tooltip - :on-click #(set-alignment align) - :key (str "align-self" align)} - (lyc/get-layout-flex-icon :align-self align is-col?)])]))) + :id (dm/str "align-self-stretch-" type)}]]])) (mf/defc options {::mf/wrap [mf/memo]} [{:keys [shape cell cells] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - - state* (mf/use-state {:open true}) + (let [state* (mf/use-state {:open true}) open? (:open @state*) cells (hooks/use-equal-memo cells) @@ -181,208 +163,104 @@ (dwge/clear-selection (:id shape)))))] - (if new-css-system - [:div {:class (stl/css :grid-cell-menu)} - [:div {:class (stl/css :grid-cell-menu-title)} - [:& title-bar {:collapsable? true - :collapsed? (not open?) - :on-collapsed #(swap! state* update :open not) - :title "Grid cell"}]] + [:div {:class (stl/css :grid-cell-menu)} + [:div {:class (stl/css :grid-cell-menu-title)} + [:& title-bar {:collapsable? true + :collapsed? (not open?) + :on-collapsed #(swap! state* update :open not) + :title "Grid cell"}]] - (when open? - [:div {:class (stl/css :grid-cell-menu-container)} - [:div {:class (stl/css :cell-mode :row)} - [:& radio-buttons {:selected (d/name cell-mode) - :on-change set-cell-mode - :name "cell-mode" - :wide true} - [:& radio-button {:value "auto" :id :auto}] - [:& radio-button {:value "manual" :id :manual}] - [:& radio-button {:value "area" :id :area}]]] - - (when (= :area cell-mode) - [:div {:class (stl/css :row)} - [:input - {:class (stl/css :area-input) - :key (dm/str "name-" (:id cell)) - :id "grid-area-name" - :type "text" - :aria-label "grid-area-name" - :placeholder "Area name" - :default-value area-name - :auto-complete "off" - :on-change on-area-name-change}]]) - - (when (and (not multiple?) (= :auto cell-mode)) - [:div {:class (stl/css :row)} - [:div {:class (stl/css :grid-coord-group)} - [:span {:class (stl/css :icon)} i/flex-vertical-refactor] - [:div {:class (stl/css :coord-input)} - [:> numeric-input* - {:placeholder "--" - :title "Column" - :on-click #(dom/select-target %) - :on-change (partial on-grid-coordinates :all :column) - :value column}]]] - - [:div {:class (stl/css :grid-coord-group)} - [:span {:class (stl/css :icon)} i/flex-horizontal-refactor] - [:div {:class (stl/css :coord-input)} - [:> numeric-input* - {:placeholder "--" - :title "Row" - :on-click #(dom/select-target %) - :on-change (partial on-grid-coordinates :all :row) - :value row}]]]]) - - (when (and (not multiple?) (or (= :manual cell-mode) (= :area cell-mode))) - [:div {:class (stl/css :row)} - [:div {:class (stl/css :grid-coord-group)} - [:span {:class (stl/css :icon)} i/layout-rows] - [:div {:class (stl/css :coord-input)} - [:> numeric-input* - {:placeholder "--" - :on-pointer-down #(dom/select-target %) - :on-change (partial on-grid-coordinates :start :column) - :value column}]] - [:div {:class (stl/css :coord-input)} - [:> numeric-input* - {:placeholder "--" - :on-pointer-down #(dom/select-target %) - :on-change (partial on-grid-coordinates :end :column) - :value column-end}]]] - - [:div {:class (stl/css :grid-coord-group)} - [:span {:class (stl/css :icon)} i/layout-columns] - [:div {:class (stl/css :coord-input :double)} - [:> numeric-input* - {:placeholder "--" - :on-pointer-down #(dom/select-target %) - :on-change (partial on-grid-coordinates :start :row) - :value row}]] - [:div {:class (stl/css :coord-input)} - [:> numeric-input* - {:placeholder "--" - :on-pointer-down #(dom/select-target %) - :on-change (partial on-grid-coordinates :end :row) - :value row-end}]]]]) + (when open? + [:div {:class (stl/css :grid-cell-menu-container)} + [:div {:class (stl/css :cell-mode :row)} + [:& radio-buttons {:selected (d/name cell-mode) + :on-change set-cell-mode + :name "cell-mode" + :wide true} + [:& radio-button {:value "auto" :id :auto}] + [:& radio-button {:value "manual" :id :manual}] + [:& radio-button {:value "area" + :id :area + :disabled (not valid-area-cells?)}]]] + (when (= :area cell-mode) [:div {:class (stl/css :row)} - [:& set-self-alignment {:is-col? false - :alignment align-self - :set-alignment set-alignment}] - [:& set-self-alignment {:is-col? true - :alignment justify-self - :set-alignment set-justify-self}]] + [:input + {:class (stl/css :area-input) + :key (dm/str "name-" (:id cell)) + :id "grid-area-name" + :type "text" + :aria-label "grid-area-name" + :placeholder "Area name" + :default-value area-name + :auto-complete "off" + :on-change on-area-name-change}]]) + (when (and (not multiple?) (= :auto cell-mode)) [:div {:class (stl/css :row)} - [:button - {:class (stl/css :edit-grid-btn) - :alt (tr "workspace.layout_grid.editor.options.edit-grid") - :on-click toggle-edit-mode} - (tr "workspace.layout_grid.editor.options.edit-grid")]]])] + [:div {:class (stl/css :grid-coord-group)} + [:span {:class (stl/css :icon)} i/flex-vertical-refactor] + [:div {:class (stl/css :coord-input)} + [:> numeric-input* + {:placeholder "--" + :title "Column" + :on-click #(dom/select-target %) + :on-change (partial on-grid-coordinates :all :column) + :value column}]]] + [:div {:class (stl/css :grid-coord-group)} + [:span {:class (stl/css :icon)} i/flex-horizontal-refactor] + [:div {:class (stl/css :coord-input)} + [:> numeric-input* + {:placeholder "--" + :title "Row" + :on-click #(dom/select-target %) + :on-change (partial on-grid-coordinates :all :row) + :value row}]]]]) - [:div.element-set - [:div.element-set-title - [:span "Grid Cell"]] + (when (and (not multiple?) (or (= :manual cell-mode) (= :area cell-mode))) + [:div {:class (stl/css :row)} + [:div {:class (stl/css :grid-coord-group)} + [:span {:class (stl/css :icon)} i/layout-rows] + [:div {:class (stl/css :coord-input)} + [:> numeric-input* + {:placeholder "--" + :on-pointer-down #(dom/select-target %) + :on-change (partial on-grid-coordinates :start :column) + :value column}]] + [:div {:class (stl/css :coord-input)} + [:> numeric-input* + {:placeholder "--" + :on-pointer-down #(dom/select-target %) + :on-change (partial on-grid-coordinates :end :column) + :value column-end}]]] - [:div.element-set-content.layout-grid-item-menu - [:div.layout-row - [:div.row-title.sizing "Position"] - [:div.position-wrapper - [:button.position-btn - {:on-click #(set-cell-mode :auto) - :class (dom/classnames :active (= :auto cell-mode))} "Auto"] - (when-not multiple? - [:button.position-btn - {:on-click #(set-cell-mode :manual) - :class (dom/classnames :active (= :manual cell-mode))} "Manual"]) - [:button.position-btn - {:on-click #(set-cell-mode :area) - :disabled (not valid-area-cells?) - :class (dom/classnames :active (= :area cell-mode))} "Area"]]] + [:div {:class (stl/css :grid-coord-group)} + [:span {:class (stl/css :icon)} i/layout-columns] + [:div {:class (stl/css :coord-input :double)} + [:> numeric-input* + {:placeholder "--" + :on-pointer-down #(dom/select-target %) + :on-change (partial on-grid-coordinates :start :row) + :value row}]] + [:div {:class (stl/css :coord-input)} + [:> numeric-input* + {:placeholder "--" + :on-pointer-down #(dom/select-target %) + :on-change (partial on-grid-coordinates :end :row) + :value row-end}]]]]) - [:div.manage-grid-columns - (when (and (not multiple?) (= :auto cell-mode)) - [:div.grid-auto - [:div.grid-columns-auto - [:span.icon i/layout-rows] - [:div.input-wrapper - [:> numeric-input* - {:placeholder "--" - :on-click #(dom/select-target %) - :on-change (partial on-grid-coordinates :all :column) - :value column}]]] - [:div.grid-rows-auto - [:span.icon i/layout-columns] - [:div.input-wrapper - [:> numeric-input* - {:placeholder "--" - :on-click #(dom/select-target %) - :on-change (partial on-grid-coordinates :all :row) - :value row}]]]]) + [:div {:class (stl/css :row)} + [:& set-self-alignment {:is-col? false + :alignment align-self + :set-alignment set-alignment}] + [:& set-self-alignment {:is-col? true + :alignment justify-self + :set-alignment set-justify-self}]] - (when (= :area cell-mode) - [:div.input-wrapper - [:input.input-text - {:key (dm/str "name-" (:id cell)) - :id "grid-area-name" - :type "text" - :aria-label "grid-area-name" - :placeholder "--" - :default-value area-name - :auto-complete "off" - :on-change on-area-name-change}]]) - - (when (and (not multiple?) (or (= :manual cell-mode) (= :area cell-mode))) - [:div.grid-manual - [:div.grid-columns-auto - [:span.icon i/layout-rows] - [:div.input-wrapper - [:> numeric-input* - {:placeholder "--" - :on-pointer-down #(dom/select-target %) - :on-change (partial on-grid-coordinates :start :column) - :value column}] - [:> numeric-input* - {:placeholder "--" - :on-pointer-down #(dom/select-target %) - :on-change (partial on-grid-coordinates :end :column) - :value column-end}]]] - [:div.grid-rows-auto - [:span.icon i/layout-columns] - [:div.input-wrapper - [:> numeric-input* - {:placeholder "--" - :on-pointer-down #(dom/select-target %) - :on-change (partial on-grid-coordinates :start :row) - :value row}] - [:> numeric-input* - {:placeholder "--" - :on-pointer-down #(dom/select-target %) - :on-change (partial on-grid-coordinates :end :row) - :value row-end}]]]])] - - [:div.layout-row - [:div.row-title "Align"] - [:div.btn-wrapper - [:& set-self-alignment {:is-col? false - :alignment align-self - :set-alignment set-alignment}]]] - [:div.layout-row - [:div.row-title "Justify"] - [:div.btn-wrapper - [:& set-self-alignment {:is-col? true - :alignment justify-self - :set-alignment set-justify-self}]]] - - [:div.layout-row.single-button - [:div.btn-wrapper - [:div.edit-mode - [:button.tooltip.tooltip-bottom-left - {:alt "Grid edit mode" - :on-click toggle-edit-mode - :style {:padding 0}} - "Edit grid" - i/grid-layout-mode]]]]]]))) + [:div {:class (stl/css :row)} + [:button + {:class (stl/css :edit-grid-btn) + :alt (tr "workspace.layout_grid.editor.options.edit-grid") + :on-click toggle-edit-mode} + (tr "workspace.layout_grid.editor.options.edit-grid")]]])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.scss index 66fa82d4e6..a2a3449f4e 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.scss @@ -6,53 +6,51 @@ @import "refactor/common-refactor.scss"; -.grid-cell-menu { - .grid-cell-menu-container { - @include flexColumn; - margin-top: $s-8; - gap: $s-16; - } +.grid-cell-menu-container { + @include flexColumn; + margin-top: $s-8; + gap: $s-16; +} - .grid-cell-menu-title { - font-size: $fs-11; - } +.grid-cell-menu-title { + font-size: $fs-11; +} - .row { - @include flexRow; - } +.row { + @include flexRow; +} - .cell-mode :global(label) { - padding: 0 $s-12; - } +.cell-mode :global(label) { + padding: 0 $s-12; +} - .edit-grid-btn { - @extend .button-secondary; - @include tabTitleTipography; - width: 100%; - padding: $s-8; - } +.edit-grid-btn { + @extend .button-secondary; + @include tabTitleTipography; + width: 100%; + padding: $s-8; +} - .area-input { - @extend .input-element; - width: 100%; - padding: $s-8; - } +.area-input { + @extend .input-element; + width: 100%; + padding: $s-8; } .grid-coord-group { @include flexRow; - border-radius: $br-8; padding-left: $s-4; background-color: var(--input-background-color); - - .icon svg { - @extend .button-icon; - stroke: var(--icon-foreground); - } - .coord-input { - @extend .input-element; - border-radius: 0 $br-8 $br-8 0; - border-left: 1px solid var(--panel-background-color); - } +} + +.icon svg { + @extend .button-icon; + stroke: var(--icon-foreground); +} + +.coord-input { + @extend .input-element; + border-radius: 0 $br-8 $br-8 0; + border-left: 1px solid var(--panel-background-color); } 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 d99ca6ba1b..67138d85e0 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 @@ -22,7 +22,6 @@ [app.main.ui.components.radio-buttons :refer [radio-buttons radio-button]] [app.main.ui.components.select :refer [select]] [app.main.ui.components.title-bar :refer [title-bar]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -45,15 +44,6 @@ [interaction] (get (event-type-names) (:event-type interaction) "--")) -(defn- action-type-names - [] - {:navigate (tr "workspace.options.interaction-navigate-to") - :open-overlay (tr "workspace.options.interaction-open-overlay") - :toggle-overlay (tr "workspace.options.interaction-toggle-overlay") - :close-overlay (tr "workspace.options.interaction-close-overlay") - :prev-screen (tr "workspace.options.interaction-prev-screen") - :open-url (tr "workspace.options.interaction-open-url")}) - (defn- action-summary [interaction destination] (case (:action-type interaction) @@ -69,34 +59,6 @@ :open-url (tr "workspace.options.interaction-open-url") "--")) -(defn- overlay-pos-type-names - [] - {:manual (tr "workspace.options.interaction-pos-manual") - :center (tr "workspace.options.interaction-pos-center") - :top-left (tr "workspace.options.interaction-pos-top-left") - :top-right (tr "workspace.options.interaction-pos-top-right") - :top-center (tr "workspace.options.interaction-pos-top-center") - :bottom-left (tr "workspace.options.interaction-pos-bottom-left") - :bottom-right (tr "workspace.options.interaction-pos-bottom-right") - :bottom-center (tr "workspace.options.interaction-pos-bottom-center")}) - -(defn- animation-type-names - [interaction] - (cond-> - {:dissolve (tr "workspace.options.interaction-animation-dissolve") - :slide (tr "workspace.options.interaction-animation-slide")} - - (ctsi/allow-push? (:action-type interaction)) - (assoc :push (tr "workspace.options.interaction-animation-push")))) - -(defn- easing-names - [] - {:linear (tr "workspace.options.interaction-easing-linear") - :ease (tr "workspace.options.interaction-easing-ease") - :ease-in (tr "workspace.options.interaction-easing-ease-in") - :ease-out (tr "workspace.options.interaction-easing-ease-out") - :ease-in-out (tr "workspace.options.interaction-easing-ease-in-out")}) - (defn- get-frames-options [frames shape] (->> frames @@ -115,8 +77,7 @@ (mf/defc flow-item [{:keys [flow]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - editing? (mf/use-state false) + (let [editing? (mf/use-state false) flow-for-rename (mf/deref flow-for-rename-ref) name-ref (mf/use-ref) @@ -144,10 +105,10 @@ (mf/deps flow) #(st/emit! (dw/select-shape (:starting-frame flow)))) - rename-flow - (mf/use-fn - (mf/deps flow) - #(st/emit! (dwi/start-rename-flow (:id flow)))) + ;; rename-flow + ;; (mf/use-fn + ;; (mf/deps flow) + ;; #(st/emit! (dwi/start-rename-flow (:id flow)))) remove-flow (mf/use-fn @@ -171,95 +132,59 @@ (let [name-input (mf/ref-val name-ref)] (dom/select-text! name-input)) nil)) - (if new-css-system - [:div {:class (stl/css :flow-element)} - [:span {:class (stl/css :flow-info)} - [:span {:class (stl/css :flow-name-wrapper)} - [:button {:class (stl/css :start-flow-btn) - :on-click start-flow} - [:span {:class (stl/css :button-icon)} - i/play-refactor]] - [:span {:class (stl/css :flow-input-wrapper)} - [:input - {:class (stl/css :flow-input) - :type "text" - :ref name-ref - :on-blur accept-edit - :on-key-down on-key-down - :default-value (:name flow "")}]]]] - [:button {:class (stl/css :remove-flow-btn) - :on-click remove-flow} - i/remove-refactor]] + [:div {:class (stl/css :flow-element)} + [:span {:class (stl/css :flow-info)} + [:span {:class (stl/css :flow-name-wrapper)} + [:button {:class (stl/css :start-flow-btn) + :on-click start-flow} + [:span {:class (stl/css :button-icon)} + i/play-refactor]] + [:span {:class (stl/css :flow-input-wrapper)} + [:input + {:class (stl/css :flow-input) + :type "text" + :ref name-ref + :on-blur accept-edit + :on-key-down on-key-down + :default-value (:name flow "")}]]]] - [:div.flow-element - [:div.flow-button {:on-click start-flow} i/play] - (if @editing? - [:input.element-name - {:type "text" - :ref name-ref - :on-blur accept-edit - :on-key-down on-key-down - :auto-focus true - :default-value (:name flow "")}] - [:span.element-label.flow-name - {:on-double-click rename-flow} - (:name flow)]) - [:div.add-page {:on-click remove-flow} i/minus]]))) + [:button {:class (stl/css :remove-flow-btn) + :on-click remove-flow} + i/remove-refactor]])) (mf/defc page-flows [{:keys [flows]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - (when (seq flows) - [:div {:class (stl/css :interaction-options)} - [:& title-bar {:collapsable? false - :title (tr "workspace.options.flows.flow-starts") - :class (stl/css :title-spacing-layout-flow)}] - (for [flow flows] - [:& flow-item {:flow flow :key (str (:id flow))}])]) - - (when (seq flows) - [:div.element-set.interactions-options - [:div.element-set-title - [:span (tr "workspace.options.flows.flow-starts")]] - (for [flow flows] - [:& flow-item {:flow flow :key (str (:id flow))}])])))) + (when (seq flows) + [:div {:class (stl/css :interaction-options)} + [:& title-bar {:collapsable? false + :title (tr "workspace.options.flows.flow-starts") + :class (stl/css :title-spacing-layout-flow)}] + (for [flow flows] + [:& flow-item {:flow flow :key (str (:id flow))}])])) (mf/defc shape-flows [{:keys [flows shape]}] (when (= (:type shape) :frame) - (let [new-css-system (mf/use-ctx ctx/new-css-system) - flow (ctp/get-frame-flow flows (:id shape)) + (let [flow (ctp/get-frame-flow flows (:id shape)) add-flow (mf/use-fn #(st/emit! (dwi/add-flow-selected-frame)))] - (if new-css-system - [:div {:class (stl/css :element-set)} - [:& title-bar {:collapsable? false - :title (tr "workspace.options.flows.flow") - :class (stl/css :title-spacing-layout-flow)} - (when (nil? flow) - [:button {:class (stl/css :add-flow-btn) - :title (tr "workspace.options.flows.add-flow-start") - :on-click add-flow} - i/add-refactor])] - (when flow - [:& flow-item {:flow flow :key (str (:id flow))}])] + [:div {:class (stl/css :element-set)} + [:& title-bar {:collapsable? false + :title (tr "workspace.options.flows.flow") + :class (stl/css :title-spacing-layout-flow)} + (when (nil? flow) + [:button {:class (stl/css :add-flow-btn) + :title (tr "workspace.options.flows.add-flow-start") + :on-click add-flow} + i/add-refactor])] - [:div.element-set.interactions-options - [:div.element-set-title - [:span (tr "workspace.options.flows.flow-start")]] - (if (nil? flow) - [:div.flow-element - [:span.element-label (tr "workspace.options.flows.add-flow-start")] - [:div.add-page {:on-click add-flow} - i/plus]] - [:& flow-item {:flow flow :key (str (:id flow))}])])))) + (when flow + [:& flow-item {:flow flow :key (str (:id flow))}])]))) (mf/defc interaction-entry [{:keys [index shape interaction update-interaction remove-interaction]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - objects (deref refs/workspace-page-objects) + (let [objects (deref refs/workspace-page-objects) destination (get objects (:destination interaction)) frames (mf/with-memo [objects] (ctt/get-viewer-frames objects {:all-frames? true})) @@ -286,18 +211,14 @@ (mf/use-fn (mf/deps index) (fn [event] - (let [value (if new-css-system - (keyword event) - (-> event dom/get-target dom/get-value d/read-string))] + (let [value (keyword event)] (update-interaction index #(ctsi/set-event-type % value shape))))) change-action-type (mf/use-fn (mf/deps index) (fn [event] - (let [value (if new-css-system - (keyword event) - (-> event dom/get-target dom/get-value d/read-string))] + (let [value (keyword event)] (update-interaction index #(ctsi/set-action-type % value))))) change-delay @@ -310,9 +231,7 @@ (mf/use-fn (mf/deps index) (fn [event] - (let [value (if new-css-system - event - (-> event dom/get-target dom/get-value)) + (let [value event value (when (not= value "") (uuid/uuid value))] (update-interaction index #(ctsi/set-destination % value))))) @@ -320,12 +239,7 @@ (mf/use-fn (mf/deps index) (fn [event] - (let [value (if new-css-system - (uuid/uuid event) - (-> event - dom/get-target - dom/get-value - uuid/uuid))] + (let [value (uuid/uuid event)] (update-interaction index #(ctsi/set-position-relative-to % value))))) change-preserve-scroll @@ -357,11 +271,8 @@ change-overlay-pos-type (mf/use-fn (mf/deps shape) - (fn [event] - (let [shape-id (:id shape) - value (if new-css-system - event - (-> event dom/get-target dom/get-value d/read-string))] + (fn [value] + (let [shape-id (:id shape)] (update-interaction index #(ctsi/set-overlay-pos-type % value shape objects)) (when (= value :manual) (update-interaction index #(ctsi/set-position-relative-to % shape-id)))))) @@ -393,11 +304,9 @@ (mf/use-fn (mf/deps index) (fn [event] - (let [value (if new-css-system - (if (= "" event) - nil - (keyword event)) - (-> event dom/get-target dom/get-value d/read-string))] + (let [value (if (= "" event) + nil + (keyword event))] (update-interaction index #(ctsi/set-animation-type % value))))) change-duration @@ -408,30 +317,21 @@ (mf/use-fn (mf/deps index) (fn [event] - (let [value (if new-css-system - (keyword event) - (-> event dom/get-target dom/get-value d/read-string))] + (let [value (keyword event)] (update-interaction index #(ctsi/set-easing % value))))) change-way (mf/use-fn (mf/deps index) (fn [event] - (let [value (if new-css-system - (keyword event) - (-> event dom/get-target dom/get-value d/read-string))] + (let [value (keyword event)] (update-interaction index #(ctsi/set-way % value))))) change-direction (mf/use-fn (mf/deps index) (fn [event] - (let [value (if new-css-system - (keyword event) - (-> event - dom/get-target - (dom/get-data "value") - keyword))] + (let [value (keyword event)] (update-interaction index #(ctsi/set-direction % value))))) change-offset-effect @@ -502,565 +402,293 @@ {:icon :easing-ease-in-out-refactor :value :ease-in-out :label (tr "workspace.options.interaction-easing-ease-in-out")}]] - (if new-css-system - [:div {:class (stl/css-case :element-set-options-group true - :open extended-open?)} - ; Summary - [:div {:class (stl/css :interactions-summary)} - [:div {:class (stl/css-case :extend-btn true - :extended extended-open?) - :on-click toggle-extended} - i/menu-refactor] + [:div {:class (stl/css-case :element-set-options-group true + :open extended-open?)} + ; Summary + [:div {:class (stl/css :interactions-summary)} + [:div {:class (stl/css-case :extend-btn true + :extended extended-open?) + :on-click toggle-extended} + i/menu-refactor] - [:div {:class (stl/css :interactions-info) - :on-click toggle-extended} - [:div {:class (stl/css :trigger-name)} (event-type-name interaction)] - [:div {:class (stl/css :action-summary)} (action-summary interaction destination)]] - [:button {:class (stl/css :remove-btn) - :data-value index - :on-click #(remove-interaction index)} - i/remove-refactor]] + [:div {:class (stl/css :interactions-info) + :on-click toggle-extended} + [:div {:class (stl/css :trigger-name)} (event-type-name interaction)] + [:div {:class (stl/css :action-summary)} (action-summary interaction destination)]] + [:button {:class (stl/css :remove-btn) + :data-value index + :on-click #(remove-interaction index)} + i/remove-refactor]] - (when extended-open? - [:div {:class (stl/css :extended-options)} - ;; Trigger select + (when extended-open? + [:div {:class (stl/css :extended-options)} + ;; Trigger select + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} + (tr "workspace.options.interaction-trigger")] + [:div {:class (stl/css :select-wrapper)} + [:& select {:class (stl/css :interaction-type-select) + :default-value (:event-type interaction) + :options event-type-options + :on-change change-event-type}]]] + + ;; Delay + (when (ctsi/has-delay interaction) [:div {:class (stl/css :property-row)} [:span {:class (stl/css :interaction-name)} - (tr "workspace.options.interaction-trigger")] - [:div {:class (stl/css :select-wrapper)} - [:& select {:class (stl/css :interaction-type-select) - :default-value (:event-type interaction) - :options event-type-options - :on-change change-event-type}]]] + (tr "workspace.options.interaction-delay")] + [:div {:class (stl/css :input-element-wrapper) + :title (tr "workspace.options.interaction-ms")} + [:span.after (tr "workspace.options.interaction-ms")] + [:> numeric-input* {:ref ext-delay-ref + :className (stl/css :numeric-input) + :on-change change-delay + :value (:delay interaction) + :title (tr "workspace.options.interaction-ms")}]]]) - ;; Delay - (when (ctsi/has-delay interaction) - [:div {:class (stl/css :property-row)} - [:span {:class (stl/css :interaction-name)} - (tr "workspace.options.interaction-delay")] - [:div {:class (stl/css :input-element-wrapper) - :title (tr "workspace.options.interaction-ms")} - [:span.after (tr "workspace.options.interaction-ms")] - [:> numeric-input* {:ref ext-delay-ref - :className (stl/css :numeric-input) - :on-change change-delay - :value (:delay interaction) - :title (tr "workspace.options.interaction-ms")}]]]) + ;; Action select + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-action")] + [:div {:class (stl/css :select-wrapper)} + [:& select {:class (stl/css :interaction-type-select) + :default-value (:action-type interaction) + :options action-type-options + :on-change change-action-type}]]] - ;; Action select + ;; Destination + (when (ctsi/has-destination interaction) [:div {:class (stl/css :property-row)} - [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-action")] + [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-destination")] [:div {:class (stl/css :select-wrapper)} [:& select {:class (stl/css :interaction-type-select) - :default-value (:action-type interaction) - :options action-type-options - :on-change change-action-type}]]] + :default-value (str (:destination interaction)) + :options destination-options + :on-change change-destination}]]]) - ;; Destination - (when (ctsi/has-destination interaction) - [:div {:class (stl/css :property-row)} - [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-destination")] - [:div {:class (stl/css :select-wrapper)} - [:& select {:class (stl/css :interaction-type-select) - :default-value (str (:destination interaction)) - :options destination-options - :on-change change-destination}]]]) + ;; Preserve scroll + (when (ctsi/has-preserve-scroll interaction) + [:div {:class (stl/css :property-row)} + [:div {:class (stl/css :checkbox-option)} + [:label {:for (str "preserve-" index) + :class (stl/css-case :global/checked preserve-scroll?)} + [:span {:class (stl/css-case :global/checked preserve-scroll?)} + (when preserve-scroll? + i/status-tick-refactor)] + (tr "workspace.options.interaction-preserve-scroll") + [:input {:type "checkbox" + :id (str "preserve-" index) + :checked preserve-scroll? + :on-change change-preserve-scroll}]]]]) - ;; Preserve scroll - (when (ctsi/has-preserve-scroll interaction) - [:div {:class (stl/css :property-row)} - [:div {:class (stl/css :checkbox-option)} - [:label {:for (str "preserve-" index) - :class (stl/css-case :global/checked preserve-scroll?)} - [:span {:class (stl/css-case :global/checked preserve-scroll?)} - (when preserve-scroll? - i/status-tick-refactor)] - (tr "workspace.options.interaction-preserve-scroll") - [:input {:type "checkbox" - :id (str "preserve-" index) - :checked preserve-scroll? - :on-change change-preserve-scroll}]]]]) + ;; URL + (when (ctsi/has-url interaction) + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-url")] + [:div {:class (stl/css :input-element-wrapper)} + [:input {:class (stl/css :input-text) + :type "url" + :placeholder "http://example.com" + :default-value (str (:url interaction)) + :on-blur change-url}]]]) - ;; URL - (when (ctsi/has-url interaction) - [:div {:class (stl/css :property-row)} - [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-url")] - [:div {:class (stl/css :input-element-wrapper)} - [:input {:class (stl/css :input-text) - :type "url" - :placeholder "http://example.com" - :default-value (str (:url interaction)) - :on-blur change-url}]]]) + (when (ctsi/has-overlay-opts interaction) + [:* + ;; Overlay position relative-to (select) + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-relative-to")] + [:div {:class (stl/css :select-wrapper)} + [:& select {:class (stl/css :interaction-type-select) + :default-value (str (:position-relative-to interaction)) + :options relative-to-opts + :on-change change-position-relative-to}]]] - (when (ctsi/has-overlay-opts interaction) - [:* - ;; Overlay position relative-to (select) - [:div {:class (stl/css :property-row)} - [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-relative-to")] - [:div {:class (stl/css :select-wrapper)} - [:& select {:class (stl/css :interaction-type-select) - :default-value (str (:position-relative-to interaction)) - :options relative-to-opts - :on-change change-position-relative-to}]]] + ;; Overlay position (select) + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-position")] + [:div {:class (stl/css :select-wrapper)} + [:& select {:class (stl/css :interaction-type-select) + :default-value (:overlay-pos-type interaction) + :options overlay-position-opts + :on-change change-overlay-pos-type}]]] - ;; Overlay position (select) - [:div {:class (stl/css :property-row)} - [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-position")] - [:div {:class (stl/css :select-wrapper)} - [:& select {:class (stl/css :interaction-type-select) - :default-value (:overlay-pos-type interaction) - :options overlay-position-opts - :on-change change-overlay-pos-type}]]] + ;; Overlay position (buttons) + [:div {:class (stl/css-case :property-row true + :big-row true)} + [:div {:class (stl/css :position-btns-wrapper)} + [:button {:class (stl/css-case :direction-btn true + :center-btn true + :active (= overlay-pos-type :center)) + :data-value :center + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]] + [:button {:class (stl/css-case :direction-btn true + :top-left-btn true + :active (= overlay-pos-type :top-left)) + :data-value :top-left + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]] + [:button {:class (stl/css-case :direction-btn true + :top-right-btn true + :active (= overlay-pos-type :top-right)) + :data-value :top-right + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]] - ;; Overlay position (buttons) - [:div {:class (stl/css-case :property-row true - :big-row true)} - [:div {:class (stl/css :position-btns-wrapper)} - [:button {:class (stl/css-case :direction-btn true - :center-btn true - :active (= overlay-pos-type :center)) - :data-value :center - :on-click toggle-overlay-pos-type} - [:span {:class (stl/css :rectangle)}]] - [:button {:class (stl/css-case :direction-btn true - :top-left-btn true - :active (= overlay-pos-type :top-left)) - :data-value :top-left - :on-click toggle-overlay-pos-type} - [:span {:class (stl/css :rectangle)}]] - [:button {:class (stl/css-case :direction-btn true - :top-right-btn true - :active (= overlay-pos-type :top-right)) - :data-value :top-right - :on-click toggle-overlay-pos-type} - [:span {:class (stl/css :rectangle)}]] + [:button {:class (stl/css-case :direction-btn true + :top-center-btn true + :active (= overlay-pos-type :top-center)) + :data-value :top-center + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]] + [:button {:class (stl/css-case :direction-btn true + :bottom-left-btn true + :active (= overlay-pos-type :bottom-left)) + :data-value :bottom-left + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]] + [:button {:class (stl/css-case :direction-btn true + :bottom-left-btn true + :active (= overlay-pos-type :bottom-left)) + :data-value :bottom-left + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]] - [:button {:class (stl/css-case :direction-btn true - :top-center-btn true - :active (= overlay-pos-type :top-center)) - :data-value :top-center - :on-click toggle-overlay-pos-type} - [:span {:class (stl/css :rectangle)}]] - [:button {:class (stl/css-case :direction-btn true - :bottom-left-btn true - :active (= overlay-pos-type :bottom-left)) - :data-value :bottom-left - :on-click toggle-overlay-pos-type} - [:span {:class (stl/css :rectangle)}]] - [:button {:class (stl/css-case :direction-btn true - :bottom-left-btn true - :active (= overlay-pos-type :bottom-left)) - :data-value :bottom-left - :on-click toggle-overlay-pos-type} - [:span {:class (stl/css :rectangle)}]] + [:button {:class (stl/css-case :direction-btn true + :bottom-left-btn true + :active (= overlay-pos-type :bottom-left)) + :data-value :bottom-left + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]] + [:button {:class (stl/css-case :direction-btn true + :bottom-right-btn true + :active (= overlay-pos-type :bottom-right)) + :data-value :bottom-right + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]] + [:button {:class (stl/css-case :direction-btn true + :bottom-center-btn true + :active (= overlay-pos-type :bottom-center)) + :data-value :bottom-center + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]]]] - [:button {:class (stl/css-case :direction-btn true - :bottom-left-btn true - :active (= overlay-pos-type :bottom-left)) - :data-value :bottom-left - :on-click toggle-overlay-pos-type} - [:span {:class (stl/css :rectangle)}]] - [:button {:class (stl/css-case :direction-btn true - :bottom-right-btn true - :active (= overlay-pos-type :bottom-right)) - :data-value :bottom-right - :on-click toggle-overlay-pos-type} - [:span {:class (stl/css :rectangle)}]] - [:button {:class (stl/css-case :direction-btn true - :bottom-center-btn true - :active (= overlay-pos-type :bottom-center)) - :data-value :bottom-center - :on-click toggle-overlay-pos-type} - [:span {:class (stl/css :rectangle)}]]]] - - ;; Overlay click outside - [:div {:class (stl/css :property-row)} - [:div {:class (stl/css :checkbox-option)} - [:label {:for (str "close-" index) - :class (stl/css-case :global/checked close-click-outside?)} - [:span {:class (stl/css-case :global/checked close-click-outside?)} - (when close-click-outside? - i/status-tick-refactor)] - (tr "workspace.options.interaction-close-outside") - [:input {:type "checkbox" - :id (str "close-" index) - :checked close-click-outside? - :on-change change-close-click-outside}]]]] - - ;; Overlay background - [:div {:class (stl/css :property-row)} - [:div {:class (stl/css :checkbox-option)} - [:label {:for (str "background-" index) - :class (stl/css-case :global/checked background-overlay?)} - [:span {:class (stl/css-case :global/checked background-overlay?)} - (when background-overlay? - i/status-tick-refactor)] - (tr "workspace.options.interaction-background") - [:input {:type "checkbox" - :id (str "background-" index) - :checked background-overlay? - :on-change change-background-overlay}]]]]]) - - (when (ctsi/has-animation? interaction) - [:* - ;; Animation select - [:div {:class (stl/css :property-row)} - [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-animation")] - [:div {:class (stl/css :select-wrapper)} - [:& select {:class (stl/css :animation-select) - :default-value (or (-> interaction :animation :animation-type) "") - :options animation-opts - :on-change change-animation-type}]]] - - ;; Direction - (when (ctsi/has-way? interaction) - [:div {:class (stl/css :property-row)} - [:div {:class (stl/css :inputs-wrapper)} - - [:& radio-buttons {:selected (d/name way) - :on-change change-way - :name "animation-way"} - [:& radio-button {:value "in" - :id "animation-way-in"}] - [:& radio-button {:id "animation-way-out" - :value "out"}]]]]) - - ;; Direction - (when (ctsi/has-direction? interaction) - [:div {:class (stl/css :property-row)} - [:div {:class (stl/css :buttons-wrapper)} - [:& radio-buttons {:selected (d/name direction) - :on-change change-direction - :name "animation-direction"} - [:& radio-button {:icon i/column-refactor - :icon-class (stl/css :right) - :value "right" - :id "animation-right"}] - [:& radio-button {:icon i/column-refactor - :icon-class (stl/css :left) - :id "animation-left" - :value "left"}] - [:& radio-button {:icon i/column-refactor - :icon-class (stl/css :down) - :id "animation-down" - :value "down"}] - [:& radio-button {:icon i/column-refactor - :icon-class (stl/css :up) - :id "animation-up" - :value "up"}]]]]) - - ;; Duration - (when (ctsi/has-duration? interaction) - [:div {:class (stl/css :property-row)} - [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-duration")] - [:div {:class (stl/css :input-element-wrapper) - :title (tr "workspace.options.interaction-ms")} - [:span.after (tr "workspace.options.interaction-ms")] - [:> numeric-input* {:ref ext-duration-ref - :on-change change-duration - :value (-> interaction :animation :duration) - :title (tr "workspace.options.interaction-ms")}]]]) - - ;; Easing - (when (ctsi/has-easing? interaction) - [:div {:class (stl/css :property-row)} - [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-easing")] - [:div {:class (stl/css :select-wrapper)} - [:& select {:class (stl/css :easing-select) - :dropdown-class (stl/css :dropdown-upwards) - :default-value (-> interaction :animation :easing) - :options easing-options - :on-change change-easing}]]]) - - ;; Offset effect - (when (ctsi/has-offset-effect? interaction) - [:div {:class (stl/css :property-row)} - [:div {:class (stl/css :checkbox-option)} - [:label {:for (str "offset-effect-" index) - :class (stl/css-case :global/checked (-> interaction :animation :offset-effect))} - [:span {:class (stl/css-case :global/checked (-> interaction :animation :offset-effect))} - (when (-> interaction :animation :offset-effect) - i/status-tick-refactor)] - (tr "workspace.options.interaction-offset-effect") - [:input {:type "checkbox" - :id (str "offset-effect-" index) - :checked (-> interaction :animation :offset-effect) - :on-change change-offset-effect}]]]])])])] - - - [:div.element-set-options-group {:class (dom/classnames - :open extended-open?)} - ; Summary - [:div.element-set-actions-button {:on-click toggle-extended} - i/actions] - [:div.interactions-summary {:on-click toggle-extended} - [:div.trigger-name (event-type-name interaction)] - [:div.action-summary (action-summary interaction destination)]] - [:div.element-set-actions {:on-click #(remove-interaction index)} - [:div.element-set-actions-button i/minus]] - - (when extended-open? - [:div.element-set-content - - ;; Trigger select - [:div.interactions-element.separator - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-trigger")] - [:select.input-select - {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :value (str (:event-type interaction)) - :on-change change-event-type} - (for [[value name] (event-type-names)] - (when-not (and (= value :after-delay) - (not= (:type shape) :frame)) - [:option {:key (dm/str value) - :value (dm/str value)} name]))]] - - ;; Delay - (when (ctsi/has-delay interaction) - [:div.interactions-element - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-delay")] - [:div.input-element {:title (tr "workspace.options.interaction-ms")} - [:> numeric-input* {:ref ext-delay-ref - :on-change change-delay - :value (:delay interaction) - :title (tr "workspace.options.interaction-ms")}] - [:span.after (tr "workspace.options.interaction-ms")]]]) - - ;; Action select - [:div.interactions-element.separator - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-action")] - [:select.input-select - {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :value (str (:action-type interaction)) - :on-change change-action-type} - (for [[value name] (action-type-names)] - [:option {:key (dm/str "action-" value) - :value (str value)} name])]] - - ;; Destination - (when (ctsi/has-destination interaction) - [:div.interactions-element - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-destination")] - [:select.input-select - {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :value (str (:destination interaction)) - :on-change change-destination} - (if (= (:action-type interaction) :close-overlay) - [:option {:value ""} (tr "workspace.options.interaction-self")] - [:option {:value ""} (tr "workspace.options.interaction-none")]) - (for [frame frames] - (when (and (not= (:id frame) (:id shape)) ; A frame cannot navigate to itself - (not= (:id frame) (:frame-id shape))) ; nor a shape to its container frame - [:option {:key (dm/str "destination-" (:id frame)) - :value (str (:id frame))} (:name frame)]))]]) - - ;; Preserve scroll - (when (ctsi/has-preserve-scroll interaction) - [:div.interactions-element - [:div.input-checkbox + ;; Overlay click outside + [:div {:class (stl/css :property-row)} + [:div {:class (stl/css :checkbox-option)} + [:label {:for (str "close-" index) + :class (stl/css-case :global/checked close-click-outside?)} + [:span {:class (stl/css-case :global/checked close-click-outside?)} + (when close-click-outside? + i/status-tick-refactor)] + (tr "workspace.options.interaction-close-outside") [:input {:type "checkbox" - :id (str "preserve-" index) - :checked preserve-scroll? - :on-change change-preserve-scroll}] - [:label {:for (str "preserve-" index)} - (tr "workspace.options.interaction-preserve-scroll")]]]) + :id (str "close-" index) + :checked close-click-outside? + :on-change change-close-click-outside}]]]] - ;; URL - (when (ctsi/has-url interaction) - [:div.interactions-element - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-url")] - [:input.input-text {:type "url" - :placeholder "http://example.com" - :default-value (str (:url interaction)) - :on-blur change-url}]]) + ;; Overlay background + [:div {:class (stl/css :property-row)} + [:div {:class (stl/css :checkbox-option)} + [:label {:for (str "background-" index) + :class (stl/css-case :global/checked background-overlay?)} + [:span {:class (stl/css-case :global/checked background-overlay?)} + (when background-overlay? + i/status-tick-refactor)] + (tr "workspace.options.interaction-background") + [:input {:type "checkbox" + :id (str "background-" index) + :checked background-overlay? + :on-change change-background-overlay}]]]]]) - (when (ctsi/has-overlay-opts interaction) - [:* - ;; Overlay position relative-to (select) - [:div.interactions-element - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-relative-to")] - [:select.input-select - {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :value (str (:position-relative-to interaction)) - :on-change change-position-relative-to} - (when (not= (:overlay-pos-type interaction) :manual) - [:* - [:option {:value ""} (tr "workspace.options.interaction-auto")] - (for [frame shape-parents] - [:option {:key (dm/str "position-relative-to-" (:id frame)) - :value (str (:id frame))} (:name frame)])]) - [:option {:key (dm/str "position-relative-to-" (:id shape)) - :value (str (:id shape))} (:name shape) " (" (tr "workspace.options.interaction-self") ")"]]] + (when (ctsi/has-animation? interaction) + [:* + ;; Animation select + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-animation")] + [:div {:class (stl/css :select-wrapper)} + [:& select {:class (stl/css :animation-select) + :default-value (or (-> interaction :animation :animation-type) "") + :options animation-opts + :on-change change-animation-type}]]] - ;; Overlay position (select) - [:div.interactions-element - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-position")] - [:select.input-select - {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :value (str (:overlay-pos-type interaction)) - :on-change change-overlay-pos-type} - (for [[value name] (overlay-pos-type-names)] - [:option {:value (str value)} name])]] + ;; Direction + (when (ctsi/has-way? interaction) + [:div {:class (stl/css :property-row)} + [:div {:class (stl/css :inputs-wrapper)} - ;; Overlay position (buttons) - [:div.interactions-element.interactions-pos-buttons - [:div.element-set-actions-button - {:class (dom/classnames :active (= overlay-pos-type :center)) - :data-value :center - :on-click toggle-overlay-pos-type} - i/position-center] - [:div.element-set-actions-button - {:class (dom/classnames :active (= overlay-pos-type :top-left)) - :data-value :top-left - :on-click toggle-overlay-pos-type} - i/position-top-left] - [:div.element-set-actions-button - {:class (dom/classnames :active (= overlay-pos-type :top-right)) - :data-value :top-right - :on-click toggle-overlay-pos-type} - i/position-top-right] - [:div.element-set-actions-button - {:class (dom/classnames :active (= overlay-pos-type :top-center)) - :data-value :top-center - :on-click toggle-overlay-pos-type} - i/position-top-center] - [:div.element-set-actions-button - {:class (dom/classnames :active (= overlay-pos-type :bottom-left)) - :data-value :bottom-center - :on-click toggle-overlay-pos-type} - i/position-bottom-left] - [:div.element-set-actions-button - {:class (dom/classnames :active (= overlay-pos-type :bottom-right)) - :data-value :bottom-right - :on-click toggle-overlay-pos-type} - i/position-bottom-right] - [:div.element-set-actions-button - {:class (dom/classnames :active (= overlay-pos-type :bottom-center)) - :data-value :bottom-center - :on-click toggle-overlay-pos-type} - i/position-bottom-center]] + [:& radio-buttons {:selected (d/name way) + :on-change change-way + :name "animation-way"} + [:& radio-button {:value "in" + :id "animation-way-in"}] + [:& radio-button {:id "animation-way-out" + :value "out"}]]]]) - ;; Overlay click outside - [:div.interactions-element - [:div.input-checkbox - [:input {:type "checkbox" - :id (str "close-" index) - :checked close-click-outside? - :on-change change-close-click-outside}] - [:label {:for (str "close-" index)} - (tr "workspace.options.interaction-close-outside")]]] + ;; Direction + (when (ctsi/has-direction? interaction) + [:div {:class (stl/css :property-row)} + [:div {:class (stl/css :buttons-wrapper)} + [:& radio-buttons {:selected (d/name direction) + :on-change change-direction + :name "animation-direction"} + [:& radio-button {:icon i/column-refactor + :icon-class (stl/css :right) + :value "right" + :id "animation-right"}] + [:& radio-button {:icon i/column-refactor + :icon-class (stl/css :left) + :id "animation-left" + :value "left"}] + [:& radio-button {:icon i/column-refactor + :icon-class (stl/css :down) + :id "animation-down" + :value "down"}] + [:& radio-button {:icon i/column-refactor + :icon-class (stl/css :up) + :id "animation-up" + :value "up"}]]]]) - ;; Overlay background - [:div.interactions-element - [:div.input-checkbox - [:input {:type "checkbox" - :id (str "background-" index) - :checked background-overlay? - :on-change change-background-overlay}] - [:label {:for (str "background-" index)} - (tr "workspace.options.interaction-background")]]]]) + ;; Duration + (when (ctsi/has-duration? interaction) + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-duration")] + [:div {:class (stl/css :input-element-wrapper) + :title (tr "workspace.options.interaction-ms")} + [:span.after (tr "workspace.options.interaction-ms")] + [:> numeric-input* {:ref ext-duration-ref + :on-change change-duration + :value (-> interaction :animation :duration) + :title (tr "workspace.options.interaction-ms")}]]]) - (when (ctsi/has-animation? interaction) - [:* - ;; Animation select - [:div.interactions-element.separator - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-animation")] - [:select.input-select - {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :value (str (-> interaction :animation :animation-type)) - :on-change change-animation-type} - [:option {:value ""} (tr "workspace.options.interaction-animation-none")] - (for [[value name] (animation-type-names interaction)] - [:option {:value (str value)} name])]] + ;; Easing + (when (ctsi/has-easing? interaction) + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-easing")] + [:div {:class (stl/css :select-wrapper)} + [:& select {:class (stl/css :easing-select) + :dropdown-class (stl/css :dropdown-upwards) + :default-value (-> interaction :animation :easing) + :options easing-options + :on-change change-easing}]]]) - ;; Direction - (when (ctsi/has-way? interaction) - [:div.interactions-element.interactions-way-buttons - [:div.input-radio - [:input {:type "radio" - :id "way-in" - :checked (= :in way) - :name "animation-way" - :value ":in" - :on-change change-way}] - [:label {:for "way-in"} (tr "workspace.options.interaction-in")]] - [:div.input-radio - [:input {:type "radio" - :id "way-out" - :checked (= :out way) - :name "animation-way" - :value ":out" - :on-change change-way}] - [:label {:for "way-out"} (tr "workspace.options.interaction-out")]]]) - - ;; Direction - (when (ctsi/has-direction? interaction) - [:div.interactions-element.interactions-direction-buttons - [:div.element-set-actions-button - {:class (dom/classnames :active (= direction :right)) - :data-value :right - :on-click change-direction} - i/animate-right] - [:div.element-set-actions-button - {:class (dom/classnames :active (= direction :down)) - :data-value :up - :on-click change-direction} - i/animate-down] - [:div.element-set-actions-button - {:class (dom/classnames :active (= direction :left)) - :data-value :left - :on-click change-direction} - i/animate-left] - [:div.element-set-actions-button - {:class (dom/classnames :active (= direction :up)) - :data-value :down - :on-click change-direction} - i/animate-up]]) - - ;; Duration - (when (ctsi/has-duration? interaction) - [:div.interactions-element - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-duration")] - [:div.input-element {:title (tr "workspace.options.interaction-ms")} - [:> numeric-input* {:ref ext-duration-ref - :on-change change-duration - :value (-> interaction :animation :duration) - :title (tr "workspace.options.interaction-ms")}] - [:span.after (tr "workspace.options.interaction-ms")]]]) - - ;; Easing - (when (ctsi/has-easing? interaction) - [:div.interactions-element - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-easing")] - [:select.input-select - {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :value (str (-> interaction :animation :easing)) - :on-change change-easing} - (for [[value name] (easing-names)] - [:option {:value (str value)} name])] - [:div.interactions-easing-icon - (case (-> interaction :animation :easing) - :linear i/easing-linear - :ease i/easing-ease - :ease-in i/easing-ease-in - :ease-out i/easing-ease-out - :ease-in-out i/easing-ease-in-out)]]) - - ;; Offset effect - (when (ctsi/has-offset-effect? interaction) - [:div.interactions-element - [:div.input-checkbox - [:input {:type "checkbox" - :id (str "offset-effect-" index) - :checked (-> interaction :animation :offset-effect) - :on-change change-offset-effect}] - [:label {:for (str "offset-effect-" index)} - (tr "workspace.options.interaction-offset-effect")]]])])])]))) + ;; Offset effect + (when (ctsi/has-offset-effect? interaction) + [:div {:class (stl/css :property-row)} + [:div {:class (stl/css :checkbox-option)} + [:label {:for (str "offset-effect-" index) + :class (stl/css-case :global/checked (-> interaction :animation :offset-effect))} + [:span {:class (stl/css-case :global/checked (-> interaction :animation :offset-effect))} + (when (-> interaction :animation :offset-effect) + i/status-tick-refactor)] + (tr "workspace.options.interaction-offset-effect") + [:input {:type "checkbox" + :id (str "offset-effect-" index) + :checked (-> interaction :animation :offset-effect) + :on-change change-offset-effect}]]]])])])])) (mf/defc interactions-menu [{:keys [shape] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - interactions (get shape :interactions []) + (let [interactions (get shape :interactions []) options (mf/deref refs/workspace-page-options) flows (:flows options) @@ -1076,76 +704,43 @@ update-interaction (fn [index update-fn] (st/emit! (dwi/update-interaction shape index update-fn)))] - (if new-css-system - [:div {:class (stl/css :interactions-content)} - (if shape - [:& shape-flows {:flows flows - :shape shape}] - [:& page-flows {:flows flows}]) - [:div {:class (stl/css :interaction-options)} - (when (and shape (not (cfh/unframed-shape? shape))) - [:div {:class (stl/css :element-title)} - [:& title-bar {:collapsable? false - :title (tr "workspace.options.interactions") - :class (stl/css :title-spacing-layout-interactions)} + [:div {:class (stl/css :interactions-content)} + (if shape + [:& shape-flows {:flows flows + :shape shape}] + [:& page-flows {:flows flows}]) + [:div {:class (stl/css :interaction-options)} + (when (and shape (not (cfh/unframed-shape? shape))) + [:div {:class (stl/css :element-title)} + [:& title-bar {:collapsable? false + :title (tr "workspace.options.interactions") + :class (stl/css :title-spacing-layout-interactions)} - [:button {:class (stl/css :add-interaction-btn) - :on-click add-interaction} - i/add-refactor]]]) - [:div {:class (stl/css :help-content)} - (when (= (count interactions) 0) - [:* - (when (and shape (not (cfh/unframed-shape? shape))) - [:div {:class (stl/css :help-group)} - [:div {:class (stl/css :interactions-help-icon)} i/add-refactor] - [:div {:class (stl/css :interactions-help)} - (tr "workspace.options.add-interaction")]]) + [:button {:class (stl/css :add-interaction-btn) + :on-click add-interaction} + i/add-refactor]]]) + [:div {:class (stl/css :help-content)} + (when (= (count interactions) 0) + [:* + (when (and shape (not (cfh/unframed-shape? shape))) [:div {:class (stl/css :help-group)} - [:div {:class (stl/css :interactions-help-icon)} i/interaction-refactor] + [:div {:class (stl/css :interactions-help-icon)} i/add-refactor] [:div {:class (stl/css :interactions-help)} - (tr "workspace.options.select-a-shape")]] - [:div {:class (stl/css :help-group)} - [:div {:class (stl/css :interactions-help-icon)} i/play-refactor] - [:div {:class (stl/css :interactions-help)} - (tr "workspace.options.use-play-button")]]])] - [:div {:class (stl/css :groups)} - (for [[index interaction] (d/enumerate interactions)] - [:& interaction-entry {:key (dm/str (:id shape) "-" index) - :index index - :shape shape - :interaction interaction - :update-interaction update-interaction - :remove-interaction remove-interaction}])]]] - - [:* - (if shape - [:& shape-flows {:flows flows - :shape shape}] - [:& page-flows {:flows flows}]) - - [:div.element-set.interactions-options - (when (and shape (not (cfh/unframed-shape? shape))) - [:div.element-set-title - [:span (tr "workspace.options.interactions")] - [:div.add-page {:on-click add-interaction} - i/plus]]) - [:div.element-set-content - (when (= (count interactions) 0) - [:* - (when (and shape (not (cfh/unframed-shape? shape))) - [:* - [:div.interactions-help-icon i/plus] - [:div.interactions-help.separator (tr "workspace.options.add-interaction")]]) - [:div.interactions-help-icon i/interaction] - [:div.interactions-help (tr "workspace.options.select-a-shape")] - [:div.interactions-help-icon i/play] - [:div.interactions-help (tr "workspace.options.use-play-button")]])] - [:div.groups - (for [[index interaction] (d/enumerate interactions)] - [:& interaction-entry {:key (dm/str (:id shape) "-" index) - :index index - :shape shape - :interaction interaction - :update-interaction update-interaction - :remove-interaction remove-interaction}])]]]))) + (tr "workspace.options.add-interaction")]]) + [:div {:class (stl/css :help-group)} + [:div {:class (stl/css :interactions-help-icon)} i/interaction-refactor] + [:div {:class (stl/css :interactions-help)} + (tr "workspace.options.select-a-shape")]] + [:div {:class (stl/css :help-group)} + [:div {:class (stl/css :interactions-help-icon)} i/play-refactor] + [:div {:class (stl/css :interactions-help)} + (tr "workspace.options.use-play-button")]]])] + [:div {:class (stl/css :groups)} + (for [[index interaction] (d/enumerate interactions)] + [:& interaction-entry {:key (dm/str (:id shape) "-" index) + :index index + :shape shape + :interaction interaction + :update-interaction update-interaction + :remove-interaction remove-interaction}])]]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.scss index 87eb4017a9..9ab6a9d811 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.scss @@ -10,69 +10,76 @@ display: flex; flex-direction: column; gap: $s-8; - .interaction-options { - @include flexColumn; - .element-title { - .add-interaction-btn { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - stroke: var(--icon-foreground); - } - } - } +} - .help-content { - padding: $s-20; - .help-group { - margin-bottom: $s-40; - .interactions-help-icon { - @include flexCenter; - width: $s-48; - height: $s-48; - border-radius: $br-circle; - background-color: var(--pill-background-color); - margin: 0 auto $s-12 auto; - svg { - @extend .button-icon; - stroke: var(--icon-foreground); - height: $s-32; - width: $s-32; - } - } - .interactions-help { - @include titleTipography; - text-align: center; - } - } - } +.interaction-options { + @include flexColumn; +} + +.add-interaction-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); } - .element-set { - @include flexColumn; - .add-flow-btn { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - } +} + +.help-content { + padding: $s-20; +} + +.help-group { + margin-bottom: $s-40; +} + +.interactions-help-icon { + @include flexCenter; + width: $s-48; + height: $s-48; + border-radius: $br-circle; + background-color: var(--pill-background-color); + margin: 0 auto $s-12 auto; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + height: $s-32; + width: $s-32; + } +} + +.interactions-help { + @include titleTipography; + text-align: center; + color: var(--title-foreground-color); +} + +.element-set { + @include flexColumn; +} + +.add-flow-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; } } .interactions-info { flex-grow: 1; display: grid; +} - .trigger-name { - color: var(--color-foreground-primary); - } - .action-summary { - color: var(--color-foreground-secondary); - @include textEllipsis; - } +.trigger-name { + color: var(--color-foreground-primary); +} + +.action-summary { + color: var(--color-foreground-secondary); + @include textEllipsis; } .groups { @@ -96,6 +103,7 @@ width: $s-92; margin: auto 0; grid-area: name; + color: var(--title-foreground-color); } .select-wrapper { display: flex; @@ -231,91 +239,97 @@ .flow-element { @include flexRow; - .flow-info { - display: flex; - align-items: center; - gap: $s-2; - border-radius: $s-8; - background-color: var(--input-details-color); - height: $s-32; - width: 100%; - flex-grow: 1; - .flow-name-wrapper { - @include titleTipography; - @include focusInput; - display: flex; - align-items: center; - gap: $s-4; - flex-grow: 1; - height: $s-32; - width: 100%; - border-radius: $br-8; - padding: 0; - margin-right: 0; - background-color: var(--input-background-color); - border: $s-1 solid var(--input-background-color); - color: var(--input-foreground-color); - .start-flow-btn { - @include buttonStyle; - height: $s-32; - width: $s-28; - padding: 0 $s-2 0 $s-8; - border-radius: $br-8 0 0 $br-8; - background-color: transparent; - svg { - @extend .button-icon; - stroke: var(--icon-foreground); - &:hover { - stroke: var(--input-foreground-color-active); - } - } - } - .flow-input { - @extend .input-base; - background-color: transparent; - height: $s-28; - } - .flow-input-wrapper { - @include titleTipography; - display: flex; - align-items: center; - height: $s-28; - padding: 0; - width: 100%; - margin: 0; - flex-grow: 1; - background-color: transparent; - color: var(--input-foreground-color); - border-radius: $br-8; - } - &:hover { - background-color: var(--input-background-color-hover); - border: $s-1 solid var(--input-background-color-hover); - &:active { - background-color: var(--input-background-color-active); - .flow-input-wrapper { - background-color: var(--input-background-color-active); - } - } - } - &:focus, - &:focus-within { - background-color: var(--input-background-color-active); - } +} - &.editing { - background-color: var(--input-background-color-active); - } - } - } +.flow-info { + display: flex; + align-items: center; + gap: $s-2; + border-radius: $s-8; + background-color: var(--input-details-color); + height: $s-32; + width: 100%; + flex-grow: 1; +} - .remove-flow-btn { - @extend .button-tertiary; +.flow-name-wrapper { + @include titleTipography; + @include focusInput; + display: flex; + align-items: center; + gap: $s-4; + flex-grow: 1; + height: $s-32; + width: 100%; + border-radius: $br-8; + padding: 0; + margin-right: 0; + background-color: var(--input-background-color); + border: $s-1 solid var(--input-background-color); + color: var(--input-foreground-color); + .start-flow-btn { + @include buttonStyle; height: $s-32; width: $s-28; - min-width: $s-28; + padding: 0 $s-2 0 $s-8; + border-radius: $br-8 0 0 $br-8; + background-color: transparent; svg { @extend .button-icon; + stroke: var(--icon-foreground); + &:hover { + stroke: var(--input-foreground-color-active); + } } } + + .flow-input { + @extend .input-base; + background-color: transparent; + height: $s-28; + } + + .flow-input-wrapper { + @include titleTipography; + display: flex; + align-items: center; + height: $s-28; + padding: 0; + width: 100%; + margin: 0; + flex-grow: 1; + background-color: transparent; + color: var(--input-foreground-color); + border-radius: $br-8; + } + + &:hover { + background-color: var(--input-background-color-hover); + border: $s-1 solid var(--input-background-color-hover); + &:active { + background-color: var(--input-background-color-active); + .flow-input-wrapper { + background-color: var(--input-background-color-active); + } + } + } + + &:focus, + &:focus-within { + background-color: var(--input-background-color-active); + } + + &.editing { + background-color: var(--input-background-color-active); + } +} + +.remove-flow-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + min-width: $s-28; + svg { + @extend .button-icon; + } } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs index 7dedfd571d..bdfec509f4 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs @@ -14,7 +14,6 @@ [app.main.store :as st] [app.main.ui.components.numeric-input :refer [numeric-input*]] [app.main.ui.components.select :refer [select]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) @@ -33,9 +32,7 @@ (mf/defc layer-menu {::mf/wrap-props false} [props] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - ids (unchecked-get props "ids") - type (unchecked-get props "type") + (let [ids (unchecked-get props "ids") values (unchecked-get props "values") hidden? (:hidden values) @@ -148,90 +145,46 @@ preview-complete?)) (swap! state* assoc :selected-blend-mode current-blend-mode))) - (if new-css-system - [:div {:class (stl/css :element-set)} - [:div {:class (stl/css-case :element-set-content true - :hidden hidden?)} - [:div {:class (stl/css :select)} - [:& select - {:default-value selected-blend-mode - :options options - :on-change handle-change-blend-mode - :is-open? option-highlighted? - :class (stl/css-case :hidden-select hidden?) - :on-pointer-enter-option handle-blend-mode-enter - :on-pointer-leave-option handle-blend-mode-leave}]] - [:div {:class (stl/css :input) - :title (tr "workspace.options.opacity")} - [:span {:class (stl/css :icon)} "%"] - [:> numeric-input* - {:value (opacity->string current-opacity) - :placeholder (tr "settings.multiple") - :on-change handle-opacity-change - :min 0 - :max 100 - :className (stl/css :numeric-input)}]] + [:div {:class (stl/css :element-set)} + [:div {:class (stl/css-case :element-set-content true + :hidden hidden?)} + [:div {:class (stl/css :select)} + [:& select + {:default-value selected-blend-mode + :options options + :on-change handle-change-blend-mode + :is-open? option-highlighted? + :class (stl/css-case :hidden-select hidden?) + :on-pointer-enter-option handle-blend-mode-enter + :on-pointer-leave-option handle-blend-mode-leave}]] + [:div {:class (stl/css :input) + :title (tr "workspace.options.opacity")} + [:span {:class (stl/css :icon)} "%"] + [:> numeric-input* + {:value (opacity->string current-opacity) + :placeholder (tr "settings.multiple") + :on-change handle-opacity-change + :min 0 + :max 100 + :className (stl/css :numeric-input)}]] - [:div {:class (stl/css :actions)} - (cond - (or (= :multiple hidden?) (not hidden?)) - [:button {:on-click handle-set-hidden - :class (stl/css :hidden-btn)} i/shown-refactor] + [:div {:class (stl/css :actions)} + (cond + (or (= :multiple hidden?) (not hidden?)) + [:button {:on-click handle-set-hidden + :class (stl/css :hidden-btn)} i/shown-refactor] - :else - [:button {:on-click handle-set-visible - :class (stl/css :hidden-btn)} i/hide-refactor]) + :else + [:button {:on-click handle-set-visible + :class (stl/css :hidden-btn)} i/hide-refactor]) - (cond - (or (= :multiple blocked?) (not blocked?)) - [:button {:on-click handle-set-blocked - :class (stl/css :lock-btn)} i/unlock-refactor] + (cond + (or (= :multiple blocked?) (not blocked?)) + [:button {:on-click handle-set-blocked + :class (stl/css :lock-btn)} i/unlock-refactor] - :else - [:button {:on-click handle-set-unblocked - :class (stl/css-case :lock-btn true - :locked blocked?)} i/lock-refactor])]]] - - [:div.element-set - [:div.element-set-title - [:span - (case type - :multiple (tr "workspace.options.layer-options.title.multiple") - :group (tr "workspace.options.layer-options.title.group") - (tr "workspace.options.layer-options.title"))]] - - [:div.element-set-content - [:div.row-flex - [:& select - {:class "flex-grow no-check" - :default-value selected-blend-mode - :options options - :on-change handle-change-blend-mode - :is-open? option-highlighted? - :on-pointer-enter-option handle-blend-mode-enter - :on-pointer-leave-option handle-blend-mode-leave}] - - [:div.input-element {:title (tr "workspace.options.opacity") - :class "percentail"} - [:> numeric-input* - {:value (opacity->string current-opacity) - :placeholder (tr "settings.multiple") - :on-change handle-opacity-change - :min 0 - :max 100}]] - - [:div.element-set-actions.layer-actions - (cond - (or (= :multiple hidden?) (not hidden?)) - [:div.element-set-actions-button {:on-click handle-set-hidden} i/eye] - - :else - [:div.element-set-actions-button {:on-click handle-set-visible} i/eye-closed]) - - (cond - (or (= :multiple blocked?) (not blocked?)) - [:div.element-set-actions-button {:on-click handle-set-blocked} i/unlock] - - :else - [:div.element-set-actions-button {:on-click handle-set-unblocked} i/lock])]]]]))) + :else + [:button {:on-click handle-set-unblocked + :class (stl/css-case :lock-btn true + :locked blocked?)} i/lock-refactor])]]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.scss index 7d8f31d768..93be5aa25c 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.scss @@ -8,48 +8,48 @@ .element-set { margin-bottom: $s-8; - .element-set-content { +} +.element-set-content { + display: flex; + height: $s-32; + gap: $s-4; + .select { + width: $s-124; + padding: 0; + } + .input { + @extend .input-element; + width: $s-60; + } + .actions { display: flex; - height: $s-32; gap: $s-4; - .select { - width: $s-124; - padding: 0; + .hidden-btn, + .lock-btn { + @extend .button-tertiary; + border-radius: $br-8; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } + } + } + + &.hidden { + .hidden-select { + @include hiddenElement; + border: $s-1 solid var(--input-border-color-disabled); } .input { - @extend .input-element; - width: $s-60; - } - .actions { - display: flex; - gap: $s-4; - .hidden-btn, - .lock-btn { - @extend .button-tertiary; - border-radius: $br-8; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - stroke: var(--icon-foreground); - } + @include hiddenElement; + border: $s-1 solid var(--input-border-color-disabled); + .icon { + stroke: var(--input-foreground-color-disabled); } - } - - &.hidden { - .hidden-select { - @include hiddenElement; - border: $s-1 solid var(--input-border-color-disabled); - } - .input { - @include hiddenElement; - border: $s-1 solid var(--input-border-color-disabled); - .icon { - stroke: var(--input-foreground-color-disabled); - } - .numeric-input { - color: var(--input-foreground-color-disabled); - } + .numeric-input { + color: var(--input-foreground-color-disabled); } } } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs index 8138f0a3e4..c9069edbbf 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs @@ -23,7 +23,6 @@ [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] [app.main.ui.components.select :refer [select]] [app.main.ui.components.title-bar :refer [title-bar]] - [app.main.ui.context :as ctx] [app.main.ui.formats :as fmt] [app.main.ui.hooks :as h] [app.main.ui.icons :as i] @@ -41,29 +40,6 @@ :column i/column-refactor :column-reverse i/column-reverse-refactor)) -(mf/defc direction-btn - [{:keys [dir saved-dir on-click icon?] :as props}] - (let [handle-on-click - (mf/use-fn - (mf/deps on-click dir) - (fn [] - (when (some? on-click) - (on-click dir))))] - - [:button.dir.tooltip.tooltip-bottom - {:class (dom/classnames :active (= saved-dir dir) - :row (= :row dir) - :row-reverse (= :row-reverse dir) - :column-reverse (= :column-reverse dir) - :column (= :column dir)) - :key (dm/str "direction-" dir) - :alt (str/replace (str/capital (d/name dir)) "-" " ") - :on-click handle-on-click} - (if icon? - i/auto-direction - (str/replace (str/capital (d/name dir)) "-" " "))])) - - ;; FLEX COMPONENTS (def layout-container-flex-attrs @@ -85,86 +61,6 @@ :layout-grid-rows]) (defn get-layout-flex-icon - [type val is-col?] - (case type - :align-items - (if is-col? - (case val - :start i/align-items-column-start - :end i/align-items-column-end - :center i/align-items-column-center - :stretch i/align-items-column-strech - :baseline i/align-items-column-baseline) - (case val - :start i/align-items-row-start - :end i/align-items-row-end - :center i/align-items-row-center - :stretch i/align-items-row-strech - :baseline i/align-items-row-baseline)) - - :justify-content - (if is-col? - (case val - :start i/justify-content-column-start - :end i/justify-content-column-end - :center i/justify-content-column-center - :space-around i/justify-content-column-around - :space-evenly i/justify-content-column-evenly - :space-between i/justify-content-column-between) - (case val - :start i/justify-content-row-start - :end i/justify-content-row-end - :center i/justify-content-row-center - :space-around i/justify-content-row-around - :space-evenly i/justify-content-row-evenly - :space-between i/justify-content-row-between)) - - :align-content - (if is-col? - (case val - :start i/align-content-column-start - :end i/align-content-column-end - :center i/align-content-column-center - :space-around i/align-content-column-around - :space-evenly i/align-content-column-evenly - :space-between i/align-content-column-between - :stretch nil) - - (case val - :start i/align-content-row-start - :end i/align-content-row-end - :center i/align-content-row-center - :space-around i/align-content-row-around - :space-evenly i/align-content-row-evenly - :space-between i/align-content-row-between - :stretch nil)) - - (case val - :start i/align-content-row-start - :end i/align-content-row-end - :center i/align-content-row-center - :space-around i/align-content-row-around - :space-between i/align-content-row-between - :stretch nil) - - :align-self - (if is-col? - (case val - :auto i/minus - :start i/align-self-row-left - :end i/align-self-row-right - :center i/align-self-row-center - :stretch i/align-self-row-strech - :baseline i/align-self-row-baseline) - (case val - :auto i/minus - :start i/align-self-column-top - :end i/align-self-column-bottom - :center i/align-self-column-center - :stretch i/align-self-column-strech - :baseline i/align-self-column-baseline)))) - -(defn get-layout-flex-icon-refactor [type val is-col?] (case type :align-items @@ -276,249 +172,118 @@ (mf/defc direction-row-flex [{:keys [saved-dir on-change] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:& radio-buttons {:selected (d/name saved-dir) - :on-change on-change - :name "flex-direction"} - [:& radio-button {:value "row" - :id "flex-direction-row" - :title "Row" - :icon (dir-icons-refactor :row)}] - [:& radio-button {:value "row-reverse" - :id "flex-direction-row-reverse" - :title "Row reverse" - :icon (dir-icons-refactor :row-reverse)}] - [:& radio-button {:value "column" - :id "flex-direction-column" - :title "Column" - :icon (dir-icons-refactor :column)}] - [:& radio-button {:value "column-reverse" - :id "flex-direction-column-reverse" - :title "Column reverse" - :icon (dir-icons-refactor :column-reverse)}]] - [:* - (for [dir [:row :row-reverse :column :column-reverse]] - [:& direction-btn {:key (d/name dir) - :dir dir - :saved-dir saved-dir - :on-click on-change - :icon? true}])]))) + [:& radio-buttons {:selected (d/name saved-dir) + :on-change on-change + :name "flex-direction"} + [:& radio-button {:value "row" + :id "flex-direction-row" + :title "Row" + :icon (dir-icons-refactor :row)}] + [:& radio-button {:value "row-reverse" + :id "flex-direction-row-reverse" + :title "Row reverse" + :icon (dir-icons-refactor :row-reverse)}] + [:& radio-button {:value "column" + :id "flex-direction-column" + :title "Column" + :icon (dir-icons-refactor :column)}] + [:& radio-button {:value "column-reverse" + :id "flex-direction-column-reverse" + :title "Column reverse" + :icon (dir-icons-refactor :column-reverse)}]]) (mf/defc wrap-row [{:keys [wrap-type on-click] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:button {:class (stl/css-case :wrap-button true - :selected (= wrap-type :wrap)) - :title (if (= :wrap wrap-type) - "No wrap" - "Wrap") - :on-click on-click} - i/wrap-refactor] - - [:* - [:button.tooltip.tooltip-bottom - {:class (dom/classnames :active (= wrap-type :nowrap)) - :alt "No wrap" - :data-value :nowrap - :on-click on-click - :style {:padding 0}} - [:span.no-wrap i/minus]] - [:button.wrap.tooltip.tooltip-bottom - {:class (dom/classnames :active (= wrap-type :wrap)) - :alt "Wrap" - :data-value :wrap - :on-click on-click} - i/auto-wrap]]))) + [:button {:class (stl/css-case :wrap-button true + :selected (= wrap-type :wrap)) + :title (if (= :wrap wrap-type) + "No wrap" + "Wrap") + :on-click on-click} + i/wrap-refactor]) (mf/defc align-row [{:keys [is-col? align-items on-change] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:& radio-buttons {:selected (d/name align-items) - :on-change on-change - :name "flex-align-items"} - [:& radio-button {:value "start" - :icon (get-layout-flex-icon-refactor :align-items :start is-col?) - :title "Align items start" - :id "align-items-start"}] - [:& radio-button {:value "center" - :icon (get-layout-flex-icon-refactor :align-items :center is-col?) - :title "Align items center" - :id "align-items-center"}] - [:& radio-button {:value "end" - :icon (get-layout-flex-icon-refactor :align-items :end is-col?) - :title "Align items end" - :id "align-items-end"}]] - - [:div.align-items-style - [:button.align-start.tooltip.tooltip-bottom - {:class (dom/classnames :active (= align-items :start)) - :alt "Align items start" - :data-value :start - :on-click on-change} - (get-layout-flex-icon :align-items :start is-col?)] - [:button.align-start.tooltip.tooltip-bottom-left - {:class (dom/classnames :active (= align-items :center)) - :alt "Align items center" - :data-value :center - :on-click on-change} - (get-layout-flex-icon :align-items :center is-col?)] - [:button.align-start.tooltip.tooltip-bottom-left - {:class (dom/classnames :active (= align-items :end)) - :alt "Align items end" - :data-value :end - :on-click on-change} - (get-layout-flex-icon :align-items :end is-col?)]]))) + [:& radio-buttons {:selected (d/name align-items) + :on-change on-change + :name "flex-align-items"} + [:& radio-button {:value "start" + :icon (get-layout-flex-icon :align-items :start is-col?) + :title "Align items start" + :id "align-items-start"}] + [:& radio-button {:value "center" + :icon (get-layout-flex-icon :align-items :center is-col?) + :title "Align items center" + :id "align-items-center"}] + [:& radio-button {:value "end" + :icon (get-layout-flex-icon :align-items :end is-col?) + :title "Align items end" + :id "align-items-end"}]]) (mf/defc align-content-row [{:keys [is-col? align-content on-change] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:& radio-buttons {:selected (d/name align-content) - :on-change on-change - :name "flex-align-content"} - [:& radio-button {:value "start" - :icon (get-layout-flex-icon-refactor :align-content :start is-col?) - :title "Align content start" - :id "align-content-start"}] - [:& radio-button {:value "center" - :icon (get-layout-flex-icon-refactor :align-content :center is-col?) - :title "Align content center" - :id "align-content-center"}] - [:& radio-button {:value "end" - :icon (get-layout-flex-icon-refactor :align-content :end is-col?) - :title "Align content end" - :id "align-content-end"}] - [:& radio-button {:value "space-between" - :icon (get-layout-flex-icon-refactor :align-content :space-between is-col?) - :title "Align content space-between" - :id "align-content-space-between"}] - [:& radio-button {:value "space-around" - :icon (get-layout-flex-icon-refactor :align-content :space-around is-col?) - :title "Align content space-around" - :id "align-content-space-around"}] - [:& radio-button {:value "space-evenly" - :icon (get-layout-flex-icon-refactor :align-content :space-evenly is-col?) - :title "Align content space-evenly" - :id "align-content-space-evenly"}]] - [:* - [:div.align-content-style - [:button.align-content.tooltip.tooltip-bottom - {:class (dom/classnames :active (= align-content :start)) - :alt "Align content start" - :data-value :start - :on-click on-change} - (get-layout-flex-icon :align-content :start is-col?)] - [:button.align-content.tooltip.tooltip-bottom-left - {:class (dom/classnames :active (= align-content :center)) - :alt "Align content center" - :data-value :center - :on-click on-change} - (get-layout-flex-icon :align-content :center is-col?)] - [:button.align-content.tooltip.tooltip-bottom-left - {:class (dom/classnames :active (= align-content :end)) - :alt "Align content end" - :data-value :end - :on-click on-change} - (get-layout-flex-icon :align-content :end is-col?)]] - [:div.align-content-style - [:button.align-content.tooltip.tooltip-bottom - {:class (dom/classnames :active (= align-content :space-between)) - :alt "Align content space-between" - :data-value :space-between - :on-click on-change} - (get-layout-flex-icon :align-content :space-between is-col?)] - [:button.align-content.tooltip.tooltip-bottom-left - {:class (dom/classnames :active (= align-content :space-around)) - :alt "Align content space-around" - :data-value :space-around - :on-click on-change} - (get-layout-flex-icon :align-content :space-around is-col?)] - [:button.align-content.tooltip.tooltip-bottom-left - {:class (dom/classnames :active (= align-content :space-evenly)) - :alt "Align content space-evenly" - :data-value :space-evenly - :on-click on-change} - (get-layout-flex-icon :align-content :space-evenly is-col?)]]]))) + [:& radio-buttons {:selected (d/name align-content) + :on-change on-change + :name "flex-align-content"} + [:& radio-button {:value "start" + :icon (get-layout-flex-icon :align-content :start is-col?) + :title "Align content start" + :id "align-content-start"}] + [:& radio-button {:value "center" + :icon (get-layout-flex-icon :align-content :center is-col?) + :title "Align content center" + :id "align-content-center"}] + [:& radio-button {:value "end" + :icon (get-layout-flex-icon :align-content :end is-col?) + :title "Align content end" + :id "align-content-end"}] + [:& radio-button {:value "space-between" + :icon (get-layout-flex-icon :align-content :space-between is-col?) + :title "Align content space-between" + :id "align-content-space-between"}] + [:& radio-button {:value "space-around" + :icon (get-layout-flex-icon :align-content :space-around is-col?) + :title "Align content space-around" + :id "align-content-space-around"}] + [:& radio-button {:value "space-evenly" + :icon (get-layout-flex-icon :align-content :space-evenly is-col?) + :title "Align content space-evenly" + :id "align-content-space-evenly"}]]) (mf/defc justify-content-row [{:keys [is-col? justify-content on-change] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:& radio-buttons {:selected (d/name justify-content) - :on-change on-change - :name "flex-justify"} - [:& radio-button {:value "start" - :icon (get-layout-flex-icon-refactor :justify-content :start is-col?) - :title "Justify content start" - :id "justify-content-start"}] - [:& radio-button {:value "center" - :icon (get-layout-flex-icon-refactor :justify-content :center is-col?) - :title "Justify content center" - :id "justify-content-center"}] - [:& radio-button {:value "end" - :icon (get-layout-flex-icon-refactor :justify-content :end is-col?) - :title "Justify content end" - :id "justify-content-end"}] - [:& radio-button {:value "space-between" - :icon (get-layout-flex-icon-refactor :justify-content :space-between is-col?) - :title "Justify content space-between" - :id "justify-content-space-between"}] - [:& radio-button {:value "space-around" - :icon (get-layout-flex-icon-refactor :justify-content :space-around is-col?) - :title "Justify content space-around" - :id "justify-content-space-around"}] - [:& radio-button {:value "space-evenly" - :icon (get-layout-flex-icon-refactor :justify-content :space-evenly is-col?) - :title "Justify content space-evenly" - :id "justify-content-space-evenly"}]] - [:* - [:div.justify-content-style - [:button.justify.tooltip.tooltip-bottom - {:class (dom/classnames :active (= justify-content :start)) - :alt "Justify content start" - :data-value :start - :on-click on-change} - (get-layout-flex-icon :justify-content :start is-col?)] - [:button.justify.tooltip.tooltip-bottom-left - {:class (dom/classnames :active (= justify-content :center)) - :data-value :center - :alt "Justify content center" - :on-click on-change} - (get-layout-flex-icon :justify-content :center is-col?)] - [:button.justify.tooltip.tooltip-bottom-left - {:class (dom/classnames :active (= justify-content :end)) - :alt "Justify content end" - :data-value :end - :on-click on-change} - (get-layout-flex-icon :justify-content :end is-col?)]] - [:div.justify-content-style - [:button.justify.tooltip.tooltip-bottom - {:class (dom/classnames :active (= justify-content :space-between)) - :alt "Justify content space-between" - :data-value :space-between - :on-click on-change} - (get-layout-flex-icon :justify-content :space-between is-col?)] - [:button.justify.tooltip.tooltip-bottom-left - {:class (dom/classnames :active (= justify-content :space-around)) - :alt "Justify content space-around" - :data-value :space-around - :on-click on-change} - (get-layout-flex-icon :justify-content :space-around is-col?)] - [:button.justify.tooltip.tooltip-bottom-left - {:class (dom/classnames :active (= justify-content :space-evenly)) - :alt "Justify content space-evenly" - :data-value :space-evenly - :on-click on-change} - (get-layout-flex-icon :justify-content :space-evenly is-col?)]]]))) + [:& radio-buttons {:selected (d/name justify-content) + :on-change on-change + :name "flex-justify"} + [:& radio-button {:value "start" + :icon (get-layout-flex-icon :justify-content :start is-col?) + :title "Justify content start" + :id "justify-content-start"}] + [:& radio-button {:value "center" + :icon (get-layout-flex-icon :justify-content :center is-col?) + :title "Justify content center" + :id "justify-content-center"}] + [:& radio-button {:value "end" + :icon (get-layout-flex-icon :justify-content :end is-col?) + :title "Justify content end" + :id "justify-content-end"}] + [:& radio-button {:value "space-between" + :icon (get-layout-flex-icon :justify-content :space-between is-col?) + :title "Justify content space-between" + :id "justify-content-space-between"}] + [:& radio-button {:value "space-around" + :icon (get-layout-flex-icon :justify-content :space-around is-col?) + :title "Justify content space-around" + :id "justify-content-space-around"}] + [:& radio-button {:value "space-evenly" + :icon (get-layout-flex-icon :justify-content :space-evenly is-col?) + :title "Justify content space-evenly" + :id "justify-content-space-evenly"}]]) (mf/defc padding-section [{:keys [values on-change-style on-change] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - padding-type (:layout-padding-type values) + (let [padding-type (:layout-padding-type values) toggle-padding-mode (mf/use-fn @@ -551,171 +316,110 @@ ;;on destroy component (select-paddings false false false false)))) - (if new-css-system - [:div {:class (stl/css :padding-group)} - [:div {:class (stl/css :padding-inputs)} - (cond - (= padding-type :simple) - [:div {:class (stl/css :paddings-simple)} - [:div {:class (stl/css :padding-simple) - :title "Vertical padding"} - [:span {:class (stl/css :icon)} - i/padding-top-bottom-refactor] - [:> numeric-input* - {:className (stl/css :numeric-input) - :placeholder "--" - :on-change (partial on-change :simple :p1) - :on-focus #(do - (dom/select-target %) - (select-paddings true false true false)) - :nillable true - :min 0 - :value p1}]] - [:div {:class (stl/css :padding-simple) - :title "Horizontal padding"} + [:div {:class (stl/css :padding-group)} + [:div {:class (stl/css :padding-inputs)} + (cond + (= padding-type :simple) + [:div {:class (stl/css :paddings-simple)} + [:div {:class (stl/css :padding-simple) + :title "Vertical padding"} + [:span {:class (stl/css :icon)} + i/padding-top-bottom-refactor] + [:> numeric-input* + {:className (stl/css :numeric-input) + :placeholder "--" + :on-change (partial on-change :simple :p1) + :on-focus #(do + (dom/select-target %) + (select-paddings true false true false)) + :nillable true + :min 0 + :value p1}]] + [:div {:class (stl/css :padding-simple) + :title "Horizontal padding"} - [:span {:class (stl/css :icon)} - i/padding-left-right-refactor] - [:> numeric-input* - {:className (stl/css :numeric-input) - :placeholder "--" - :on-change (partial on-change :simple :p2) - :on-focus #(do (dom/select-target %) - (select-paddings false true false true)) - :on-blur #(select-paddings false false false false) - :nillable true - :min 0 - :value p2}]]] - (= padding-type :multiple) - [:div {:class (stl/css :paddings-multiple)} + [:span {:class (stl/css :icon)} + i/padding-left-right-refactor] + [:> numeric-input* + {:className (stl/css :numeric-input) + :placeholder "--" + :on-change (partial on-change :simple :p2) + :on-focus #(do (dom/select-target %) + (select-paddings false true false true)) + :on-blur #(select-paddings false false false false) + :nillable true + :min 0 + :value p2}]]] + (= padding-type :multiple) + [:div {:class (stl/css :paddings-multiple)} - [:div {:class (stl/css :padding-multiple) - :title "Top padding"} - [:span {:class (stl/css :icon)} - i/padding-top-refactor] - [:> numeric-input* - {:className (stl/css :numeric-input) - :placeholder "--" - :on-change (partial on-change :multiple :p1) - :on-focus #(do (dom/select-target %) - (select-padding :p1)) - :on-blur #(select-paddings false false false false) - :nillable true - :min 0 - :value (:p1 (:layout-padding values))}]] + [:div {:class (stl/css :padding-multiple) + :title "Top padding"} + [:span {:class (stl/css :icon)} + i/padding-top-refactor] + [:> numeric-input* + {:className (stl/css :numeric-input) + :placeholder "--" + :on-change (partial on-change :multiple :p1) + :on-focus #(do (dom/select-target %) + (select-padding :p1)) + :on-blur #(select-paddings false false false false) + :nillable true + :min 0 + :value (:p1 (:layout-padding values))}]] - [:div {:class (stl/css :padding-multiple) - :title "Right padding"} - [:span {:class (stl/css :icon)} - i/padding-right-refactor] - [:> numeric-input* - {:className (stl/css :numeric-input) - :placeholder "--" - :on-change (partial on-change :multiple :p2) - :on-focus #(do (dom/select-target %) - (select-padding :p2)) - :on-blur #(select-paddings false false false false) - :nillable true - :min 0 - :value (:p2 (:layout-padding values))}]] + [:div {:class (stl/css :padding-multiple) + :title "Right padding"} + [:span {:class (stl/css :icon)} + i/padding-right-refactor] + [:> numeric-input* + {:className (stl/css :numeric-input) + :placeholder "--" + :on-change (partial on-change :multiple :p2) + :on-focus #(do (dom/select-target %) + (select-padding :p2)) + :on-blur #(select-paddings false false false false) + :nillable true + :min 0 + :value (:p2 (:layout-padding values))}]] - [:div {:class (stl/css :padding-multiple) - :title "Bottom padding"} - [:span {:class (stl/css :icon)} - i/padding-bottom-refactor] - [:> numeric-input* - {:className (stl/css :numeric-input) - :placeholder "--" - :on-change (partial on-change :multiple :p3) - :on-focus #(do (dom/select-target %) - (select-padding :p3)) - :on-blur #(select-paddings false false false false) - :nillable true - :min 0 - :value (:p3 (:layout-padding values))}]] + [:div {:class (stl/css :padding-multiple) + :title "Bottom padding"} + [:span {:class (stl/css :icon)} + i/padding-bottom-refactor] + [:> numeric-input* + {:className (stl/css :numeric-input) + :placeholder "--" + :on-change (partial on-change :multiple :p3) + :on-focus #(do (dom/select-target %) + (select-padding :p3)) + :on-blur #(select-paddings false false false false) + :nillable true + :min 0 + :value (:p3 (:layout-padding values))}]] - [:div {:class (stl/css :padding-multiple) - :title "Left padding"} - [:span {:class (stl/css :icon)} - i/padding-left-refactor] - [:> numeric-input* - {:className (stl/css :numeric-input) - :placeholder "--" - :on-change (partial on-change :multiple :p4) - :on-focus #(do (dom/select-target %) - (select-padding :p4)) - :on-blur #(select-paddings false false false false) - :nillable true - :min 0 - :value (:p4 (:layout-padding values))}]]])] - [:button {:class (stl/css-case :padding-toggle true - :selected (= padding-type :multiple)) - :on-click toggle-padding-mode} - i/padding-extended-refactor]] - - [:div.padding-row - (cond - (= padding-type :simple) - - [:div.padding-group - [:div.padding-item.tooltip.tooltip-bottom-left - {:alt "Vertical padding"} - [:span.icon.rotated i/auto-padding-both-sides] - [:> numeric-input* - {:placeholder "--" - :on-change (partial on-change :simple :p1) - :on-focus #(do - (dom/select-target %) - (select-paddings true false true false)) - :nillable true - :min 0 - :value p1}]] - - [:div.padding-item.tooltip.tooltip-bottom-left - {:alt "Horizontal padding"} - [:span.icon i/auto-padding-both-sides] - [:> numeric-input* - {:placeholder "--" - :on-change (partial on-change :simple :p2) - :on-focus #(do (dom/select-target %) - (select-paddings false true false true)) - :on-blur #(select-paddings false false false false) - :nillable true - :min 0 - :value p2}]]] - - (= padding-type :multiple) - [:div.wrapper - (for [num [:p1 :p2 :p3 :p4]] - [:div.tooltip.tooltip-bottom - {:key (dm/str "padding-" (d/name num)) - :alt (case num - :p1 "Top" - :p2 "Right" - :p3 "Bottom" - :p4 "Left")} - [:div.input-element.auto - [:> numeric-input* - {:placeholder "--" - :on-change (partial on-change :multiple num) - :on-focus #(do (dom/select-target %) - (select-padding num)) - :on-blur #(select-paddings false false false false) - :nillable true - :min 0 - :value (num (:layout-padding values))}]]])]) - - [:div.padding-icons - [:div.padding-icon.tooltip.tooltip-bottom-left - {:class (dom/classnames :selected (= padding-type :multiple)) - :alt "Independent paddings" - :on-click toggle-padding-mode} - i/auto-padding-side]]]))) + [:div {:class (stl/css :padding-multiple) + :title "Left padding"} + [:span {:class (stl/css :icon)} + i/padding-left-refactor] + [:> numeric-input* + {:className (stl/css :numeric-input) + :placeholder "--" + :on-change (partial on-change :multiple :p4) + :on-focus #(do (dom/select-target %) + (select-padding :p4)) + :on-blur #(select-paddings false false false false) + :nillable true + :min 0 + :value (:p4 (:layout-padding values))}]]])] + [:button {:class (stl/css-case :padding-toggle true + :selected (= padding-type :multiple)) + :on-click toggle-padding-mode} + i/padding-extended-refactor]])) (mf/defc gap-section [{:keys [is-col? wrap-type gap-selected? on-change gap-value]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - select-gap + (let [select-gap (fn [gap] (st/emit! (udw/set-gap-selected gap)))] @@ -725,89 +429,47 @@ ;;on destroy component (select-gap nil)))) - (if new-css-system - [:div {:class (stl/css :gap-group)} - [:div {:class (stl/css-case :row-gap true - :disabled (and (= :nowrap wrap-type) (not is-col?))) - :title "Row gap"} - [:span {:class (stl/css :icon)} i/gap-vertical-refactor] - [:> numeric-input* {:className (stl/css :numeric-input true) - :no-validate true - :placeholder "--" - :on-focus (fn [event] - (select-gap :row-gap) - (reset! gap-selected? :row-gap) - (dom/select-target event)) - :on-change (partial on-change (= :nowrap wrap-type) :row-gap) - :on-blur (fn [_] - (select-gap nil) - (reset! gap-selected? :none)) - :nillable true - :min 0 - :value (:row-gap gap-value) - :disabled (and (= :nowrap wrap-type) (not is-col?))}]] + [:div {:class (stl/css :gap-group)} + [:div {:class (stl/css-case :row-gap true + :disabled (and (= :nowrap wrap-type) (not is-col?))) + :title "Row gap"} + [:span {:class (stl/css :icon)} i/gap-vertical-refactor] + [:> numeric-input* {:className (stl/css :numeric-input true) + :no-validate true + :placeholder "--" + :on-focus (fn [event] + (select-gap :row-gap) + (reset! gap-selected? :row-gap) + (dom/select-target event)) + :on-change (partial on-change (= :nowrap wrap-type) :row-gap) + :on-blur (fn [_] + (select-gap nil) + (reset! gap-selected? :none)) + :nillable true + :min 0 + :value (:row-gap gap-value) + :disabled (and (= :nowrap wrap-type) (not is-col?))}]] - [:div {:class (stl/css-case :column-gap true - :disabled (and (= :nowrap wrap-type) is-col?)) - :title "Column gap"} - [:span {:class (stl/css :icon)} - i/gap-horizontal-refactor] - [:> numeric-input* {:className (stl/css :numeric-input) - :no-validate true - :placeholder "--" - :on-focus (fn [event] - (select-gap :column-gap) - (reset! gap-selected? :column-gap) - (dom/select-target event)) - :on-change (partial on-change (= :nowrap wrap-type) :column-gap) - :on-blur (fn [_] - (select-gap nil) - (reset! gap-selected? :none)) - :nillable true - :min 0 - :value (:column-gap gap-value) - :disabled (and (= :nowrap wrap-type) is-col?)}]]] - - [:div.layout-row - [:div.gap.row-title "Gap"] - [:div.gap-group - [:div.gap-row.tooltip.tooltip-bottom-left - {:alt "Column gap"} - [:span.icon - i/auto-gap] - [:> numeric-input* {:no-validate true - :placeholder "--" - :on-focus (fn [event] - (select-gap :column-gap) - (reset! gap-selected? :column-gap) - (dom/select-target event)) - :on-change (partial on-change (= :nowrap wrap-type) :column-gap) - :on-blur (fn [_] - (select-gap nil) - (reset! gap-selected? :none)) - :nillable true - :min 0 - :value (:column-gap gap-value) - :disabled (and (= :nowrap wrap-type) is-col?)}]] - - [:div.gap-row.tooltip.tooltip-bottom-left - {:alt "Row gap"} - [:span.icon.rotated - i/auto-gap] - [:> numeric-input* {:no-validate true - :placeholder "--" - :on-focus (fn [event] - (select-gap :row-gap) - (reset! gap-selected? :row-gap) - (dom/select-target event)) - :on-change (partial on-change (= :nowrap wrap-type) :row-gap) - :on-blur (fn [_] - (select-gap nil) - (reset! gap-selected? :none)) - :nillable true - :min 0 - :value (:row-gap gap-value) - :disabled (and (= :nowrap wrap-type) (not is-col?))}]]]]))) + [:div {:class (stl/css-case :column-gap true + :disabled (and (= :nowrap wrap-type) is-col?)) + :title "Column gap"} + [:span {:class (stl/css :icon)} + i/gap-horizontal-refactor] + [:> numeric-input* {:className (stl/css :numeric-input) + :no-validate true + :placeholder "--" + :on-focus (fn [event] + (select-gap :column-gap) + (reset! gap-selected? :column-gap) + (dom/select-target event)) + :on-change (partial on-change (= :nowrap wrap-type) :column-gap) + :on-blur (fn [_] + (select-gap nil) + (reset! gap-selected? :none)) + :nillable true + :min 0 + :value (:column-gap gap-value) + :disabled (and (= :nowrap wrap-type) is-col?)}]]])) ;; GRID COMPONENTS @@ -835,36 +497,22 @@ :space-evenly i/grid-justify-content-row-between)))) (mf/defc direction-row-grid - [{:keys [saved-dir on-change on-click] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:& radio-buttons {:selected (d/name saved-dir) - :on-change on-change - :name "grid-direction"} - [:& radio-button {:value "row" - :id "grid-direction-row" - :title "Row" - :icon (dir-icons-refactor :row)}] - [:& radio-button {:value "column" - :id "grid-direction-column" - :title "Column" - :icon (dir-icons-refactor :column)}]] - [:* - [:& direction-btn {:key "grid-direction-row" - :dir :row - :saved-dir saved-dir - :on-click on-click - :icon? true}] - [:& direction-btn {:key "grid-direction-column" - :dir :column - :saved-dir saved-dir - :on-click on-click - :icon? true}]]))) + [{:keys [saved-dir on-change] :as props}] + [:& radio-buttons {:selected (d/name saved-dir) + :on-change on-change + :name "grid-direction"} + [:& radio-button {:value "row" + :id "grid-direction-row" + :title "Row" + :icon (dir-icons-refactor :row)}] + [:& radio-button {:value "column" + :id "grid-direction-column" + :title "Column" + :icon (dir-icons-refactor :column)}]]) (mf/defc grid-edit-mode [{:keys [id] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - edition (mf/deref refs/selected-edition) + (let [edition (mf/deref refs/selected-edition) active? (= id edition) toggle-edit-mode @@ -874,81 +522,43 @@ (if-not active? (st/emit! (udw/start-edition-mode id)) (st/emit! :interrupt))))] - (if new-css-system - [:button - {:class (stl/css :edit-mode-btn) - :alt "Grid edit mode" - :on-click toggle-edit-mode} - (tr "workspace.layout_grid.editor.options.edit-grid")] - - [:button.tooltip.tooltip-bottom-left - {:class (dom/classnames :active active?) - :alt "Grid edit mode" - :on-click toggle-edit-mode - :style {:padding 0}} - (tr "workspace.layout_grid.editor.options.edit-grid") - i/grid-layout-mode]))) + [:button + {:class (stl/css :edit-mode-btn) + :alt "Grid edit mode" + :on-click toggle-edit-mode} + (tr "workspace.layout_grid.editor.options.edit-grid")])) (mf/defc align-grid-row [{:keys [is-col? align-items set-align] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - type (if is-col? :column :row)] - (if new-css-system - [:& radio-buttons {:selected (d/name align-items) - :on-change #(set-align % type) - :name (dm/str "flex-align-items-" (d/name type))} - [:& radio-button {:value "start" - :icon (get-layout-grid-icon-refactor :align-items :start is-col?) - :title "Align items start" - :id (dm/str "align-items-start-" (d/name type))}] - [:& radio-button {:value "center" - :icon (get-layout-grid-icon-refactor :align-items :center is-col?) - :title "Align items center" - :id (dm/str "align-items-center-" (d/name type))}] - [:& radio-button {:value "end" - :icon (get-layout-grid-icon-refactor :align-items :end is-col?) - :title "Align items end" - :id (dm/str "align-items-end-" (d/name type))}]] - [:div.align-items-style - (for [align [:start :center :end]] - [:button.align-start.tooltip - {:class (dom/classnames :active (= align-items align) - :tooltip-bottom-left (not= align :start) - :tooltip-bottom (= align :start)) - :alt (if is-col? - (dm/str "justify-items: " (d/name align)) - (dm/str "align-items: " (d/name align))) - :on-click #(set-align align type) - :key (dm/str "align-items" (d/name align))} - (get-layout-flex-icon :align-items align is-col?)])]))) + (let [type (if is-col? :column :row)] + [:& radio-buttons {:selected (d/name align-items) + :on-change #(set-align % type) + :name (dm/str "flex-align-items-" (d/name type))} + [:& radio-button {:value "start" + :icon (get-layout-grid-icon-refactor :align-items :start is-col?) + :title "Align items start" + :id (dm/str "align-items-start-" (d/name type))}] + [:& radio-button {:value "center" + :icon (get-layout-grid-icon-refactor :align-items :center is-col?) + :title "Align items center" + :id (dm/str "align-items-center-" (d/name type))}] + [:& radio-button {:value "end" + :icon (get-layout-grid-icon-refactor :align-items :end is-col?) + :title "Align items end" + :id (dm/str "align-items-end-" (d/name type))}]])) (mf/defc justify-grid-row [{:keys [is-col? justify-items set-justify] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - type (if is-col? :column :row)] + (let [type (if is-col? :column :row)] - (if new-css-system - [:& radio-buttons {:selected (d/name justify-items) - :on-change #(set-justify % type) - :name (dm/str "grid-justify-items-" (d/name type))} - (for [justify [:start :center :end :space-around :space-between :stretch]] - [:& radio-button {:value (d/name justify) - :icon (get-layout-grid-icon-refactor :justify-items justify is-col?) - :title (dm/str "Justify items " (d/name justify)) - :id (dm/str "justify-items-" (d/name justify) "-" (d/name type))}])] - - [:div.justify-content-style - (for [align [:start :center :end :space-around :space-between :stretch]] - [:button.align-start.tooltip - {:class (dom/classnames :active (= justify-items align) - :tooltip-bottom-left (not= align :start) - :tooltip-bottom (= align :start)) - :alt (if is-col? - (dm/str "align-content: " (d/name align)) - (dm/str "justify-content: " (d/name align))) - :on-click #(set-justify align type) - :key (dm/str "justify-content" (d/name align))} - (get-layout-grid-icon :justify-items align is-col?)])]))) + [:& radio-buttons {:selected (d/name justify-items) + :on-change #(set-justify % type) + :name (dm/str "grid-justify-items-" (d/name type))} + (for [justify [:start :center :end :space-around :space-between :stretch]] + [:& radio-button {:value (d/name justify) + :icon (get-layout-grid-icon-refactor :justify-items justify is-col?) + :title (dm/str "Justify items " (d/name justify)) + :id (dm/str "justify-items-" (d/name justify) "-" (d/name type))}])])) (defn manage-values [{:keys [value type]}] (case type @@ -959,14 +569,22 @@ value)) (mf/defc grid-track-info - [{:keys [is-col? type index column set-column-value set-column-type remove-element reorder-track hover-track on-select-track]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) + [{:keys [is-col? + type + index + column + set-column-value + set-column-type + remove-element + reorder-track + hover-track + on-select-track]}] - drop-track + (let [drop-track (mf/use-fn (mf/deps type reorder-track index) (fn [drop-position data event] - (reorder-track type (:index data) (if (= :top drop-position) (dec index) index) (kbd/mod? event)))) + (reorder-track type (:index data) (if (= :top drop-position) (dec index) index) (not (kbd/mod? event))))) pointer-enter (mf/use-fn @@ -996,89 +614,50 @@ :column column} :draggable? true)] - (if new-css-system - [:div {:class - (stl/css-case :track-info true - :dnd-over-top (or (= (:over dprops) :top) - (= (:over dprops) :center)) - :dnd-over-bot (= (:over dprops) :bot)) - :ref dref - :on-pointer-enter pointer-enter - :on-pointer-leave pointer-leave} + [:div {:class (stl/css-case :track-info true + :dnd-over-top (or (= (:over dprops) :top) + (= (:over dprops) :center)) + :dnd-over-bot (= (:over dprops) :bot)) + :ref dref + :on-pointer-enter pointer-enter + :on-pointer-leave pointer-leave} - [:div {:class (stl/css :track-info-container)} - [:div {:class (stl/css :track-info-dir-icon) - :on-click handle-select-track} - (if is-col? i/flex-vertical-refactor i/flex-horizontal-refactor)] + [:div {:class (stl/css :track-info-container)} + [:div {:class (stl/css :track-info-dir-icon) + :on-click handle-select-track} + (if is-col? i/flex-vertical-refactor i/flex-horizontal-refactor)] - [:div {:class (stl/css :track-info-value)} - [:> numeric-input* {:no-validate true - :value (:value column) - :on-change #(set-column-value type index %) - :placeholder "--" - :min 0 - :disabled (= :auto (:type column))}]] + [:div {:class (stl/css :track-info-value)} + [:> numeric-input* {:no-validate true + :value (:value column) + :on-change #(set-column-value type index %) + :placeholder "--" + :min 0 + :disabled (= :auto (:type column))}]] - [:div {:class (stl/css :track-info-unit)} - [:& select - {:class (stl/css :track-info-unit-selector) - :default-value (:type column) - :options [{:value :flex :label "FR"} - {:value :auto :label "AUTO"} - {:value :fixed :label "PX"} - {:value :percent :label "%"}] - :on-change #(set-column-type type index %)}]]] + [:div {:class (stl/css :track-info-unit)} + [:& select {:class (stl/css :track-info-unit-selector) + :default-value (:type column) + :options [{:value :flex :label "FR"} + {:value :auto :label "AUTO"} + {:value :fixed :label "PX"} + {:value :percent :label "%"}] + :on-change #(set-column-type type index %)}]]] - [:button - {:class (stl/css :remove-track-btn) - :on-click #(remove-element type index)} - i/remove-refactor]] - - [:div.column-info - {:ref dref - :class (dom/classnames - :dnd-over-top (or (= (:over dprops) :top) - (= (:over dprops) :center)) - :dnd-over-bot (= (:over dprops) :bot)) - :on-pointer-enter pointer-enter - :on-pointer-leave pointer-leave} - [:div.direction-grid-icon - (if is-col? - i/layout-rows - i/layout-columns)] - - [:div.grid-column-value - [:> numeric-input* {:no-validate true - :value (:value column) - :on-change #(set-column-value type index %) - :placeholder "--" - :disabled (= :auto (:type column))}]] - [:div.grid-column-unit - [:& select - {:class "grid-column-unit-selector" - :default-value (:type column) - :options [{:value :flex :label "FR"} - {:value :auto :label "AUTO"} - {:value :fixed :label "PX"} - {:value :percent :label "%"}] - :on-change #(set-column-type type index %)}]] - [:button.remove-grid-column - {:on-click #(remove-element type index)} - i/minus]]))) + [:button {:class (stl/css :remove-track-btn) + :on-click #(remove-element type index)} + i/remove-refactor]])) (mf/defc grid-columns-row [{:keys [is-col? expanded? column-values toggle add-new-element set-column-value set-column-type remove-element reorder-track hover-track on-select-track] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - - column-num (count column-values) + (let [column-num (count column-values) direction (if (> column-num 1) (if is-col? "Columns " "Rows ") (if is-col? "Column " "Row ")) track-name (dm/str direction (if (= column-num 0) " - empty" column-num)) track-detail (str/join ", " (map manage-values column-values)) - generated-name (dm/str direction (if (= column-num 0) " - empty" (dm/str column-num " (" track-detail ")"))) type (if is-col? :column :row) @@ -1087,61 +666,36 @@ (when-not expanded? (toggle)) (add-new-element type ctl/default-track-value))] - (if new-css-system - [:div {:class (stl/css :grid-tracks)} - [:div {:class (stl/css :grid-track-header)} - [:button {:class (stl/css :expand-icon) :on-click toggle} i/menu-refactor] - [:div {:class (stl/css :track-title) :on-click toggle} - [:div {:class (stl/css :track-name) :title track-name} track-name] - [:div {:class (stl/css :track-detail) :title track-detail} track-detail]] - [:button {:class (stl/css :add-column) :on-click add-track} i/add-refactor]] + [:div {:class (stl/css :grid-tracks)} + [:div {:class (stl/css :grid-track-header)} + [:button {:class (stl/css :expand-icon) :on-click toggle} i/menu-refactor] + [:div {:class (stl/css :track-title) :on-click toggle} + [:div {:class (stl/css :track-name) :title track-name} track-name] + [:div {:class (stl/css :track-detail) :title track-detail} track-detail]] + [:button {:class (stl/css :add-column) :on-click add-track} i/add-refactor]] - (when expanded? - [:& h/sortable-container {} - [:div {:class (stl/css :grid-tracks-info-container)} - (for [[index column] (d/enumerate column-values)] - [:& grid-track-info {:key (dm/str index "-" (name type)) - :type type - :is-col? is-col? - :index index - :column column - :set-column-value set-column-value - :set-column-type set-column-type - :remove-element remove-element - :reorder-track reorder-track - :hover-track hover-track - :on-select-track on-select-track}])]])] - - [:div.grid-columns - [:div.grid-columns-header - [:button.expand-icon {:on-click toggle} i/actions] - [:div.columns-info {:title generated-name :on-click toggle} generated-name] - [:button.add-column {:on-click add-track} i/plus]] - - (when expanded? - [:& h/sortable-container {} - [:div.columns-info-wrapper - (for [[index column] (d/enumerate column-values)] - [:& grid-track-info {:key (dm/str index "-" (name type)) - :type type - :is-col? is-col? - :index index - :column column - :set-column-value set-column-value - :set-column-type set-column-type - :remove-element remove-element - :reorder-track reorder-track - :hover-track hover-track - :on-select-track on-select-track}])]])]))) + (when expanded? + [:& h/sortable-container {} + [:div {:class (stl/css :grid-tracks-info-container)} + (for [[index column] (d/enumerate column-values)] + [:& grid-track-info {:key (dm/str index "-" (name type)) + :type type + :is-col? is-col? + :index index + :column column + :set-column-value set-column-value + :set-column-type set-column-type + :remove-element remove-element + :reorder-track reorder-track + :hover-track hover-track + :on-select-track on-select-track}])]])])) ;; LAYOUT COMPONENT (mf/defc layout-container-menu {::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "multiple"]))]} [{:keys [ids values multiple] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - - ;; Display + (let [;; Display layout-type (:layout values) has-layout? (some? layout-type) @@ -1194,9 +748,9 @@ set-direction-refactor (mf/use-fn - (mf/deps new-css-system layout-type ids) + (mf/deps layout-type ids) (fn [dir] - (let [dir (if new-css-system (keyword dir) dir)] + (let [dir (keyword dir)] (if (= :flex layout-type) (st/emit! (dwsl/update-layout ids {:layout-flex-dir dir})) (st/emit! (dwsl/update-layout ids {:layout-grid-dir dir})))))) @@ -1205,7 +759,7 @@ wrap-type (:layout-wrap-type values) - toggle-wrap-refactor + toggle-wrap (mf/use-fn (mf/deps wrap-type ids) (fn [] @@ -1214,30 +768,12 @@ :wrap)] (st/emit! (dwsl/update-layout ids {:layout-wrap-type type}))))) - set-wrap - (mf/use-fn - (mf/deps ids) - (fn [event] - (let [type (-> (dom/get-current-target event) - (dom/get-data "value") - (keyword))] - (st/emit! (dwsl/update-layout ids {:layout-wrap-type type}))))) - ;; Align items align-items (:layout-align-items values) set-align-items - (mf/use-fn - (mf/deps ids) - (fn [event] - (let [value (-> (dom/get-current-target event) - (dom/get-data "value") - (keyword))] - (st/emit! (dwsl/update-layout ids {:layout-align-items value}))))) - - set-align-items-refactor (mf/use-fn (mf/deps ids) (fn [value] @@ -1248,15 +784,6 @@ justify-content (:layout-justify-content values) set-justify-content - (mf/use-fn - (mf/deps ids) - (fn [event] - (let [value (-> (dom/get-current-target event) - (dom/get-data "value") - (keyword))] - (st/emit! (dwsl/update-layout ids {:layout-justify-content value}))))) - - set-justify-content-refactor (mf/use-fn (mf/deps ids) (fn [value] @@ -1267,17 +794,6 @@ align-content (:layout-align-content values) set-align-content - (mf/use-fn - (mf/deps ids align-content) - (fn [event] - (let [value (-> (dom/get-current-target event) - (dom/get-data "value") - (keyword))] - (if (= align-content value) - (st/emit! (dwsl/update-layout ids {:layout-align-content :stretch})) - (st/emit! (dwsl/update-layout ids {:layout-align-content value})))))) - - set-align-content-refactor (mf/use-fn (mf/deps ids) (fn [value align-content] @@ -1324,9 +840,9 @@ set-direction (mf/use-fn - (mf/deps layout-type ids new-css-system) + (mf/deps layout-type ids) (fn [dir] - (let [dir (cond-> dir new-css-system keyword)] + (let [dir (keyword dir)] (if (= :flex layout-type) (st/emit! (dwsl/update-layout ids {:layout-grid-dir dir})) (st/emit! (dwsl/update-layout ids {:layout-grid-dir dir})))))) @@ -1337,9 +853,9 @@ set-align-grid (mf/use-fn - (mf/deps ids new-css-system) + (mf/deps ids) (fn [value type] - (let [value (cond-> value new-css-system keyword)] + (let [value (keyword value)] (if (= type :row) (st/emit! (dwsl/update-layout ids {:layout-align-items value})) (st/emit! (dwsl/update-layout ids {:layout-justify-items value})))))) @@ -1352,9 +868,9 @@ set-justify-grid (mf/use-fn - (mf/deps ids new-css-system) + (mf/deps ids) (fn [value type] - (let [value (cond-> value new-css-system keyword)] + (let [value (keyword value)] (if (= type :row) (st/emit! (dwsl/update-layout ids {:layout-justify-content value})) (st/emit! (dwsl/update-layout ids {:layout-align-content value})))))) @@ -1379,246 +895,127 @@ (fn [] (st/emit! (dom/open-new-window cf/grid-help-uri))))] - (if new-css-system - [:div {:class (stl/css :element-set)} - [:div {:class (stl/css :element-title)} - [:& title-bar {:collapsable? has-layout? - :collapsed? (not open?) - :on-collapsed toggle-content - :title "Layout" - :class (stl/css-case :title-spacing-layout (not has-layout?))} - (if (and (not multiple) (:layout values)) - [:div {:class (stl/css :title-actions)} - (when ^boolean grid-enabled? - [:* - [:button {:class (stl/css :add-layout) - :on-click handle-show-layout-dropdown} - i/menu-refactor] + [:div {:class (stl/css :element-set)} + [:div {:class (stl/css :element-title)} + [:& title-bar {:collapsable? has-layout? + :collapsed? (not open?) + :on-collapsed toggle-content + :title "Layout" + :class (stl/css-case :title-spacing-layout (not has-layout?))} + (if (and (not multiple) (:layout values)) + [:div {:class (stl/css :title-actions)} + (when ^boolean grid-enabled? + [:* + [:button {:class (stl/css :add-layout) + :on-click handle-show-layout-dropdown} + i/menu-refactor] - [:& dropdown {:show show-layout-dropdown? :on-close handle-close-layout-options} - [:div {:class (stl/css :layout-options)} - [:button {:class (stl/css :layout-option) :on-click set-flex} "Flex layout"] - [:button {:class (stl/css :layout-option) :on-click set-grid} "Grid layout"]]]]) + [:& dropdown {:show show-layout-dropdown? :on-close handle-close-layout-options} + [:div {:class (stl/css :layout-options)} + [:button {:class (stl/css :layout-option) :on-click set-flex} "Flex layout"] + [:button {:class (stl/css :layout-option) :on-click set-grid} "Grid layout"]]]]) - [:button {:class (stl/css :remove-layout) - :on-click on-remove-layout} - i/remove-refactor]] + [:button {:class (stl/css :remove-layout) + :on-click on-remove-layout} + i/remove-refactor]] - [:div {:class (stl/css :title-actions)} - (if ^boolean grid-enabled? - [:* - [:button {:class (stl/css :add-layout) - :on-click handle-show-layout-dropdown} - i/add-refactor] + [:div {:class (stl/css :title-actions)} + (if ^boolean grid-enabled? + [:* + [:button {:class (stl/css :add-layout) + :on-click handle-show-layout-dropdown} + i/add-refactor] - [:& dropdown {:show show-layout-dropdown? :on-close handle-close-layout-options} - [:div {:class (stl/css :layout-options)} - [:button {:class (stl/css :layout-option) :on-click set-flex} "Flex layout"] - [:button {:class (stl/css :layout-option) :on-click set-grid} "Grid layout"]]]] + [:& dropdown {:show show-layout-dropdown? :on-close handle-close-layout-options} + [:div {:class (stl/css :layout-options)} + [:button {:class (stl/css :layout-option) :on-click set-flex} "Flex layout"] + [:button {:class (stl/css :layout-option) :on-click set-grid} "Grid layout"]]]] - [:button {:class (stl/css :add-layout) - :data-value :flex - :on-click on-set-layout} - i/add-refactor])])]] + [:button {:class (stl/css :add-layout) + :data-value :flex + :on-click on-set-layout} + i/add-refactor])])]] - (when (and open? has-layout?) - (when (not= :multiple layout-type) - (case layout-type - :flex - [:div {:class (stl/css :flex-layout-menu)} - [:div {:class (stl/css :first-row)} - [:& align-row {:is-col? is-col? - :align-items align-items - :on-change set-align-items-refactor}] + (when (and open? has-layout?) + (when (not= :multiple layout-type) + (case layout-type + :flex + [:div {:class (stl/css :flex-layout-menu)} + [:div {:class (stl/css :first-row)} + [:& align-row {:is-col? is-col? + :align-items align-items + :on-change set-align-items}] - [:& direction-row-flex {:on-change set-direction-refactor - :saved-dir saved-dir}] + [:& direction-row-flex {:on-change set-direction-refactor + :saved-dir saved-dir}] - [:& wrap-row {:wrap-type wrap-type - :on-click toggle-wrap-refactor}]] + [:& wrap-row {:wrap-type wrap-type + :on-click toggle-wrap}]] - [:div {:class (stl/css :second-row :help-button-wrapper)} - [:& justify-content-row {:is-col? is-col? - :justify-content justify-content - :on-change set-justify-content-refactor}] + [:div {:class (stl/css :second-row :help-button-wrapper)} + [:& justify-content-row {:is-col? is-col? + :justify-content justify-content + :on-change set-justify-content}] - [:button {:on-click handle-open-flex-help - :class (stl/css :help-button)} i/help-refactor]] - (when (= :wrap wrap-type) - [:div {:class (stl/css :third-row)} - [:& align-content-row {:is-col? is-col? - :align-content align-content - :on-change set-align-content-refactor}]]) - [:div {:class (stl/css :forth-row)} - [:& gap-section {:is-col? is-col? - :wrap-type wrap-type - :gap-selected? gap-selected? - :on-change set-gap - :gap-value (:layout-gap values)}] + [:button {:on-click handle-open-flex-help + :class (stl/css :help-button)} i/help-refactor]] + (when (= :wrap wrap-type) + [:div {:class (stl/css :third-row)} + [:& align-content-row {:is-col? is-col? + :align-content align-content + :on-change set-align-content}]]) + [:div {:class (stl/css :forth-row)} + [:& gap-section {:is-col? is-col? + :wrap-type wrap-type + :gap-selected? gap-selected? + :on-change set-gap + :gap-value (:layout-gap values)}] - [:& padding-section {:values values - :on-change-style change-padding-type - :on-change on-padding-change}]]] + [:& padding-section {:values values + :on-change-style change-padding-type + :on-change on-padding-change}]]] - :grid - [:div {:class (stl/css :grid-layout-menu)} - (when (= 1 (count ids)) - [:div {:class (stl/css :edit-grid-wrapper)} - [:& grid-edit-mode {:id (first ids)}] - [:button {:on-click handle-open-grid-help - :class (stl/css :help-button)} i/help-refactor]]) - [:div {:class (stl/css :row :first-row)} - [:div {:class (stl/css :direction-edit)} - [:div {:class (stl/css :direction)} - [:& direction-row-grid {:saved-dir saved-grid-dir - :on-change set-direction}]]] + :grid + [:div {:class (stl/css :grid-layout-menu)} + (when (= 1 (count ids)) + [:div {:class (stl/css :edit-grid-wrapper)} + [:& grid-edit-mode {:id (first ids)}] + [:button {:on-click handle-open-grid-help + :class (stl/css :help-button)} i/help-refactor]]) + [:div {:class (stl/css :row :first-row)} + [:div {:class (stl/css :direction-edit)} + [:div {:class (stl/css :direction)} + [:& direction-row-grid {:saved-dir saved-grid-dir + :on-change set-direction}]]] - [:& align-grid-row {:is-col? false - :align-items align-items-row - :set-align set-align-grid}] + [:& align-grid-row {:is-col? false + :align-items align-items-row + :set-align set-align-grid}] - [:& align-grid-row {:is-col? true - :align-items align-items-column - :set-align set-align-grid}]] + [:& align-grid-row {:is-col? true + :align-items align-items-column + :set-align set-align-grid}]] - [:div {:class (stl/css :row :grid-layout-align)} - [:& justify-grid-row {:is-col? true - :justify-items grid-justify-content-column - :set-justify set-justify-grid}] - [:& justify-grid-row {:is-col? false - :justify-items grid-justify-content-row - :set-justify set-justify-grid}]]] - nil)))] - - [:div.element-set - [:div.element-set-title - [:* - [:span "Layout"] - - (if ^boolean grid-enabled? - [:div.title-actions - [:div.layout-btns - [:button {:on-click set-flex - :class (dom/classnames - :active (= :flex layout-type))} "Flex"] - [:button {:on-click set-grid - :class (dom/classnames - :active (= :grid layout-type))} "Grid"]] - - (when (and (not multiple) (:layout values)) - [:button.remove-layout {:on-click on-remove-layout} i/minus])] - - [:div.title-actions - (if (and (not multiple) (:layout values)) - [:button.remove-layout {:on-click on-remove-layout} i/minus] - [:button.add-page {:data-value :flex - :on-click on-set-layout} i/close])])]] - - (when (:layout values) - (when (not= :multiple layout-type) - (case layout-type - :flex - - [:div.element-set-content.layout-menu - [:div.layout-row - [:div.direction-wrap.row-title "Direction"] - [:div.btn-wrapper - [:div.direction - [:& direction-row-flex {:on-change set-direction - :saved-dir saved-dir}]] - - [:div.wrap-type - [:& wrap-row {:wrap-type wrap-type - :on-click set-wrap}]]]] - - (when (= :wrap wrap-type) - [:div.layout-row - [:div.align-content.row-title "Content"] - [:div.btn-wrapper.align-content - [:& align-content-row {:is-col? is-col? - :align-content align-content - :on-change set-align-content}]]]) - - [:div.layout-row - [:div.align-items.row-title "Align"] - [:div.btn-wrapper - [:& align-row {:is-col? is-col? - :align-items align-items - :on-change set-align-items}]]] - - [:div.layout-row - [:div.justify-content.row-title "Justify"] - [:div.btn-wrapper.justify-content - [:& justify-content-row {:is-col? is-col? - :justify-content justify-content - :on-change set-justify-content}]]] - - [:& gap-section {:is-col? is-col? - :wrap-type wrap-type - :gap-selected? gap-selected? - :on-change set-gap - :gap-value (:layout-gap values)}] - - [:& padding-section {:values values - :on-change-style change-padding-type - :on-change on-padding-change}]] - - :grid - - [:div.element-set-content.layout-menu - [:div.layout-row - [:div.direction-wrap.row-title "Direction"] - [:div.btn-wrapper - [:div.direction - [:* - (for [dir [:row :column]] - [:& direction-btn {:key (d/name dir) - :dir dir - :saved-dir saved-grid-dir - :on-click set-direction - :icon? true}])]] - - (when (= 1 (count ids)) - [:div.edit-mode - [:& grid-edit-mode {:id (first ids)}]])]] - - [:div.layout-row - [:div.align-items-grid.row-title "Items"] - [:div.btn-wrapper.align-grid-items - [:& align-grid-row {:is-col? false - :align-items align-items-row - :set-align set-align-grid}] - - [:& align-grid-row {:is-col? true - :align-items align-items-column - :set-align set-align-grid}]]] - - [:div.layout-row - [:div.jusfiy-content-grid.row-title "Content"] - [:div.btn-wrapper.align-grid-content - [:& justify-grid-row {:is-col? true - :justify-items grid-justify-content-column - :set-justify set-justify-grid}] - [:& justify-grid-row {:is-col? false - :justify-items grid-justify-content-row - :set-justify set-justify-grid}]]]] - - ;; Default if not grid or flex - nil)))]))) + [:div {:class (stl/css :row :grid-layout-align)} + [:& justify-grid-row {:is-col? true + :justify-items grid-justify-content-column + :set-justify set-justify-grid}] + [:& justify-grid-row {:is-col? false + :justify-items grid-justify-content-row + :set-justify set-justify-grid}]]] + nil)))])) (mf/defc grid-layout-edition {::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values"]))]} [{:keys [ids values] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - - ;; Gap + (let [;; Gap gap-selected? (mf/use-state :none) saved-grid-dir (:layout-grid-dir values) set-direction (fn [dir] - (let [dir (if new-css-system (keyword dir) dir)] + (let [dir (keyword dir)] (st/emit! (dwsl/update-layout ids {:layout-grid-dir dir})))) set-gap @@ -1650,9 +1047,9 @@ set-align-grid (mf/use-fn - (mf/deps ids new-css-system) + (mf/deps ids) (fn [value type] - (let [value (cond-> value new-css-system keyword)] + (let [value (keyword value)] (if (= type :row) (st/emit! (dwsl/update-layout ids {:layout-align-items value})) (st/emit! (dwsl/update-layout ids {:layout-justify-items value})))))) @@ -1664,9 +1061,9 @@ set-justify-grid (mf/use-fn - (mf/deps ids new-css-system) + (mf/deps ids) (fn [value type] - (let [value (cond-> value new-css-system keyword)] + (let [value (keyword value)] (if (= type :row) (st/emit! (dwsl/update-layout ids {:layout-justify-content value})) (st/emit! (dwsl/update-layout ids {:layout-align-content value})))))) @@ -1743,141 +1140,70 @@ (fn [] (st/emit! (dwge/locate-board (first ids)))))] - (if new-css-system - [:div {:class (stl/css :grid-layout-menu)} - [:div {:class (stl/css :row)} - [:div {:class (stl/css :grid-layout-menu-title)} "GRID LAYOUT"] - [:button {:on-click handle-open-grid-help - :class (stl/css :help-button)} i/help-refactor] - [:button {:class (stl/css :exit-btn) - :on-click #(st/emit! udw/clear-edition-mode)} - (tr "workspace.layout_grid.editor.options.exit")]] + [:div {:class (stl/css :grid-layout-menu)} + [:div {:class (stl/css :row)} + [:div {:class (stl/css :grid-layout-menu-title)} "GRID LAYOUT"] + [:button {:on-click handle-open-grid-help + :class (stl/css :help-button)} i/help-refactor] + [:button {:class (stl/css :exit-btn) + :on-click #(st/emit! udw/clear-edition-mode)} + (tr "workspace.layout_grid.editor.options.exit")]] - [:div {:class (stl/css :row :first-row)} - [:div {:class (stl/css :direction-edit)} - [:div {:class (stl/css :direction)} - [:& direction-row-grid {:saved-dir saved-grid-dir - :on-change set-direction}]]] + [:div {:class (stl/css :row :first-row)} + [:div {:class (stl/css :direction-edit)} + [:div {:class (stl/css :direction)} + [:& direction-row-grid {:saved-dir saved-grid-dir + :on-change set-direction}]]] - [:& align-grid-row {:is-col? false - :align-items align-items-row - :set-align set-align-grid}] + [:& align-grid-row {:is-col? false + :align-items align-items-row + :set-align set-align-grid}] - [:& align-grid-row {:is-col? true - :align-items align-items-column - :set-align set-align-grid}]] + [:& align-grid-row {:is-col? true + :align-items align-items-column + :set-align set-align-grid}]] - [:div {:class (stl/css :row :grid-layout-align)} - [:& justify-grid-row {:is-col? true - :justify-items grid-justify-content-column - :set-justify set-justify-grid}] - [:& justify-grid-row {:is-col? false - :justify-items grid-justify-content-row - :set-justify set-justify-grid}] + [:div {:class (stl/css :row :grid-layout-align)} + [:& justify-grid-row {:is-col? true + :justify-items grid-justify-content-column + :set-justify set-justify-grid}] + [:& justify-grid-row {:is-col? false + :justify-items grid-justify-content-row + :set-justify set-justify-grid}] - [:button {:on-click handle-locate-grid - :class (stl/css :locate-button)} - i/locate-refactor]] - [:div {:class (stl/css :row :grid-tracks-row)} - [:& grid-columns-row {:is-col? true - :expanded? @grid-columns-open? - :toggle toggle-columns-info - :column-values column-grid-values - :add-new-element add-new-element - :set-column-value set-column-value - :set-column-type set-column-type - :remove-element remove-element - :reorder-track reorder-track - :hover-track hover-track - :on-select-track handle-select-track}] + [:button {:on-click handle-locate-grid + :class (stl/css :locate-button)} + i/locate-refactor]] + [:div {:class (stl/css :row :grid-tracks-row)} + [:& grid-columns-row {:is-col? true + :expanded? @grid-columns-open? + :toggle toggle-columns-info + :column-values column-grid-values + :add-new-element add-new-element + :set-column-value set-column-value + :set-column-type set-column-type + :remove-element remove-element + :reorder-track reorder-track + :hover-track hover-track + :on-select-track handle-select-track}] - [:& grid-columns-row {:is-col? false - :expanded? @grid-rows-open? - :toggle toggle-rows-info - :column-values rows-grid-values - :add-new-element add-new-element - :set-column-value set-column-value - :set-column-type set-column-type - :remove-element remove-element - :reorder-track reorder-track - :hover-track hover-track - :on-select-track handle-select-track}]] - [:div {:class (stl/css :row)} - [:& gap-section {:gap-selected? gap-selected? - :on-change set-gap - :gap-value (:layout-gap values)}]] + [:& grid-columns-row {:is-col? false + :expanded? @grid-rows-open? + :toggle toggle-rows-info + :column-values rows-grid-values + :add-new-element add-new-element + :set-column-value set-column-value + :set-column-type set-column-type + :remove-element remove-element + :reorder-track reorder-track + :hover-track hover-track + :on-select-track handle-select-track}]] + [:div {:class (stl/css :row)} + [:& gap-section {:gap-selected? gap-selected? + :on-change set-gap + :gap-value (:layout-gap values)}]] - [:div {:class (stl/css :row :padding-section)} - [:& padding-section {:values values - :on-change-style change-padding-type - :on-change on-padding-change}]]] - - [:div.element-set - [:div.element-set-title - [:span "Grid Layout"]] - - [:div.element-set-content.layout-menu - [:div.layout-row - [:div.direction-wrap.row-title "Direction"] - [:div.btn-wrapper - [:div.direction - (for [dir [:row :column]] - [:& direction-btn {:key (d/name dir) - :dir dir - :saved-dir saved-grid-dir - :on-click set-direction - :icon? true}])] - - (when (= 1 (count ids)) - [:div.edit-mode - [:& grid-edit-mode {:id (first ids)}]])]] - - [:div.layout-row - [:div.align-items-grid.row-title "Items"] - [:div.btn-wrapper.align-grid - [:& align-grid-row {:is-col? false - :align-items align-items-row - :set-align set-align-grid}] - - [:& align-grid-row {:is-col? true - :align-items align-items-column - :set-align set-align-grid}]]] - - [:div.layout-row - [:div.jusfiy-content-grid.row-title "Content"] - [:div.btn-wrapper.align-grid-content - [:& justify-grid-row {:is-col? true - :justify-items grid-justify-content-row - :set-justify set-justify-grid}] - [:& justify-grid-row {:is-col? false - :justify-items grid-justify-content-column - :set-justify set-justify-grid}]]] - [:& grid-columns-row {:is-col? true - :expanded? @grid-columns-open? - :toggle toggle-columns-info - :column-values column-grid-values - :add-new-element add-new-element - :set-column-value set-column-value - :set-column-type set-column-type - :remove-element remove-element - :reorder-track reorder-track - :hover-track hover-track}] - - [:& grid-columns-row {:is-col? false - :expanded? @grid-rows-open? - :toggle toggle-rows-info - :column-values rows-grid-values - :add-new-element add-new-element - :set-column-value set-column-value - :set-column-type set-column-type - :remove-element remove-element - :reorder-track reorder-track - :hover-track hover-track}] - - [:& gap-section {:gap-selected? gap-selected? - :on-change set-gap - :gap-value (:layout-gap values)}] - - [:& padding-section {:values values - :on-change-style change-padding-type - :on-change on-padding-change}]]]))) + [:div {:class (stl/css :row :padding-section)} + [:& padding-section {:values values + :on-change-style change-padding-type + :on-change on-padding-change}]]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.scss index 4c66d2c375..cc51a740bd 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.scss @@ -174,6 +174,7 @@ .grid-layout-menu-title { flex: 1; font-size: $fs-11; + color: var(--title-foreground-color-hover); } .edit-mode-btn { @@ -245,9 +246,6 @@ border-right: $s-1 solid var(--panel-background-color); } - .track-info-unit { - } - .track-info-unit-selector { border-radius: 0 $br-8 $br-8 0; width: $s-96; @@ -289,7 +287,7 @@ } .track-name { - color: var(--color-foreground-secondary); + color: $df-primary; } .track-detail { @@ -297,6 +295,7 @@ white-space: nowrap; text-overflow: ellipsis; width: 100%; + color: $df-secondary; } .expand-icon { diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs index 4162175b7a..8a028b2ff0 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs @@ -17,9 +17,8 @@ [app.main.ui.components.numeric-input :refer [numeric-input*]] [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] [app.main.ui.components.title-bar :refer [title-bar]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] - [app.main.ui.workspace.sidebar.options.menus.layout-container :refer [get-layout-flex-icon get-layout-flex-icon-refactor]] + [app.main.ui.workspace.sidebar.options.menus.layout-container :refer [get-layout-flex-icon]] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) @@ -40,8 +39,7 @@ (mf/defc margin-section [{:keys [values change-margin-style on-margin-change] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - margin-type (or (:layout-item-margin-type values) :simple) + (let [margin-type (or (:layout-item-margin-type values) :simple) m1 (when (and (not (= :multiple (:layout-item-margin values))) (= (dm/get-in values [:layout-item-margin :m1]) (dm/get-in values [:layout-item-margin :m3]))) @@ -63,158 +61,99 @@ ;;on destroy component (select-margins false false false false)))) - (if new-css-system - [:div {:class (stl/css :margin-row)} - [:div {:class (stl/css :inputs-wrapper)} - (cond - (= margin-type :simple) - [:div {:class (stl/css :margin-simple)} - [:div {:class (stl/css :vertical-margin) - :title "Vertical margin"} - [:span {:class (stl/css :icon)} - i/margin-top-bottom-refactor] - [:> numeric-input* {:className (stl/css :numeric-input) - :placeholder "--" - :nillable true - :value m1 - :on-focus (fn [event] - (select-margins true false true false) - (dom/select-target event)) - :on-change (partial on-margin-change :simple :m1) - :on-blur #(select-margins false false false false)}]] + [:div {:class (stl/css :margin-row)} + [:div {:class (stl/css :inputs-wrapper)} + (cond + (= margin-type :simple) + [:div {:class (stl/css :margin-simple)} + [:div {:class (stl/css :vertical-margin) + :title "Vertical margin"} + [:span {:class (stl/css :icon)} + i/margin-top-bottom-refactor] + [:> numeric-input* {:className (stl/css :numeric-input) + :placeholder "--" + :nillable true + :value m1 + :on-focus (fn [event] + (select-margins true false true false) + (dom/select-target event)) + :on-change (partial on-margin-change :simple :m1) + :on-blur #(select-margins false false false false)}]] - [:div {:class (stl/css :horizontal-margin) - :title "Horizontal margin"} - [:span {:class (stl/css :icon)} - i/margin-left-right-refactor] - [:> numeric-input* {:className (stl/css :numeric-input) - :placeholder "--" - :on-focus (fn [event] - (select-margins false true false true) - (dom/select-target event)) - :on-change (partial on-margin-change :simple :m2) - :on-blur #(select-margins false false false false) - :nillable true - :value m2}]]] + [:div {:class (stl/css :horizontal-margin) + :title "Horizontal margin"} + [:span {:class (stl/css :icon)} + i/margin-left-right-refactor] + [:> numeric-input* {:className (stl/css :numeric-input) + :placeholder "--" + :on-focus (fn [event] + (select-margins false true false true) + (dom/select-target event)) + :on-change (partial on-margin-change :simple :m2) + :on-blur #(select-margins false false false false) + :nillable true + :value m2}]]] - (= margin-type :multiple) - [:div {:class (stl/css :margin-multiple)} - [:div {:class (stl/css :top-margin) - :title "Top margin"} - [:span {:class (stl/css :icon)} - i/margin-top-refactor] - [:> numeric-input* {:className (stl/css :numeric-input) - :placeholder "--" - :on-focus (fn [event] - (select-margin :m1) - (dom/select-target event)) - :on-change (partial on-margin-change :multiple :m1) - :on-blur #(select-margins false false false false) - :nillable true - :value (:m1 (:layout-item-margin values))}]] - [:div {:class (stl/css :right-margin) - :title "Right margin"} - [:span {:class (stl/css :icon)} - i/margin-right-refactor] - [:> numeric-input* {:className (stl/css :numeric-input) - :placeholder "--" - :on-focus (fn [event] - (select-margin :m2) - (dom/select-target event)) - :on-change (partial on-margin-change :multiple :m2) - :on-blur #(select-margins false false false false) - :nillable true - :value (:m2 (:layout-item-margin values))}]] + (= margin-type :multiple) + [:div {:class (stl/css :margin-multiple)} + [:div {:class (stl/css :top-margin) + :title "Top margin"} + [:span {:class (stl/css :icon)} + i/margin-top-refactor] + [:> numeric-input* {:className (stl/css :numeric-input) + :placeholder "--" + :on-focus (fn [event] + (select-margin :m1) + (dom/select-target event)) + :on-change (partial on-margin-change :multiple :m1) + :on-blur #(select-margins false false false false) + :nillable true + :value (:m1 (:layout-item-margin values))}]] + [:div {:class (stl/css :right-margin) + :title "Right margin"} + [:span {:class (stl/css :icon)} + i/margin-right-refactor] + [:> numeric-input* {:className (stl/css :numeric-input) + :placeholder "--" + :on-focus (fn [event] + (select-margin :m2) + (dom/select-target event)) + :on-change (partial on-margin-change :multiple :m2) + :on-blur #(select-margins false false false false) + :nillable true + :value (:m2 (:layout-item-margin values))}]] - [:div {:class (stl/css :bottom-margin) - :title "Bottom margin"} - [:span {:class (stl/css :icon)} - i/margin-bottom-refactor] - [:> numeric-input* {:className (stl/css :numeric-input) - :placeholder "--" - :on-focus (fn [event] - (select-margin :m3) - (dom/select-target event)) - :on-change (partial on-margin-change :multiple :m3) - :on-blur #(select-margins false false false false) - :nillable true - :value (:m3 (:layout-item-margin values))}]] - [:div {:class (stl/css :left-margin) - :title "Left margin"} - [:span {:class (stl/css :icon)} - i/margin-left-refactor] - [:> numeric-input* {:className (stl/css :numeric-input) - :placeholder "--" - :on-focus (fn [event] - (select-margin :m4) - (dom/select-target event)) - :on-change (partial on-margin-change :multiple :m4) - :on-blur #(select-margins false false false false) - :nillable true - :value (:m4 (:layout-item-margin values))}]]])] - [:button {:class (stl/css-case :margin-mode true - :selected (= margin-type :multiple)) - :title "Margin - multiple" - :on-click #(change-margin-style (if (= margin-type :multiple) :simple :multiple))} - i/margin-refactor]] - [:div.margin-row - (cond - (= margin-type :simple) - - [:div.margin-item-group - [:div.margin-item.tooltip.tooltip-bottom-left - {:alt "Vertical margin"} - [:span.icon i/auto-margin-both-sides] - [:> numeric-input* - {:placeholder "--" - :on-focus (fn [event] - (select-margins true false true false) - (dom/select-target event)) - :on-change (partial on-margin-change :simple :m1) - :on-blur #(select-margins false false false false) - :nillable true - :value m1}]] - - [:div.margin-item.tooltip.tooltip-bottom-left - {:alt "Horizontal margin"} - [:span.icon.rotated i/auto-margin-both-sides] - [:> numeric-input* - {:placeholder "--" - :on-focus (fn [event] - (select-margins false true false true) - (dom/select-target event)) - :on-change (partial on-margin-change :simple :m2) - :on-blur #(select-margins false false false false) - :nillable true - :value m2}]]] - - (= margin-type :multiple) - [:div.wrapper - (for [num [:m1 :m2 :m3 :m4]] - [:div.tooltip.tooltip-bottom - {:key (dm/str "margin-" (d/name num)) - :alt (case num - :m1 "Top" - :m2 "Right" - :m3 "Bottom" - :m4 "Left")} - [:div.input-element.auto - [:> numeric-input* - {:placeholder "--" - :on-focus (fn [event] - (select-margin num) - (dom/select-target event)) - :on-change (partial on-margin-change :multiple num) - :on-blur #(select-margins false false false false) - :nillable true - :value (num (:layout-item-margin values))}]]])]) - - [:div.margin-item-icons - [:div.margin-item-icon.tooltip.tooltip-bottom-left - {:class (dom/classnames :selected (= margin-type :multiple)) - :alt "Margin - multiple" - :on-click #(change-margin-style (if (= margin-type :multiple) :simple :multiple))} - i/auto-margin]]]))) + [:div {:class (stl/css :bottom-margin) + :title "Bottom margin"} + [:span {:class (stl/css :icon)} + i/margin-bottom-refactor] + [:> numeric-input* {:className (stl/css :numeric-input) + :placeholder "--" + :on-focus (fn [event] + (select-margin :m3) + (dom/select-target event)) + :on-change (partial on-margin-change :multiple :m3) + :on-blur #(select-margins false false false false) + :nillable true + :value (:m3 (:layout-item-margin values))}]] + [:div {:class (stl/css :left-margin) + :title "Left margin"} + [:span {:class (stl/css :icon)} + i/margin-left-refactor] + [:> numeric-input* {:className (stl/css :numeric-input) + :placeholder "--" + :on-focus (fn [event] + (select-margin :m4) + (dom/select-target event)) + :on-change (partial on-margin-change :multiple :m4) + :on-blur #(select-margins false false false false) + :nillable true + :value (:m4 (:layout-item-margin values))}]]])] + [:button {:class (stl/css-case :margin-mode true + :selected (= margin-type :multiple)) + :title "Margin - multiple" + :on-click #(change-margin-style (if (= margin-type :multiple) :simple :multiple))} + i/margin-refactor]])) (mf/defc element-behaviour-horizontal [{:keys [auto? fill? layout-item-sizing on-change] :as props}] @@ -270,112 +209,46 @@ :id "behaviour-v-auto"}])]]) (mf/defc element-behaviour - [{:keys [auto? fill? layout-item-h-sizing layout-item-v-sizing on-change-behaviour-h-refactor on-change-behaviour-v-refactor on-change] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - - (if new-css-system - [:div {:class (stl/css-case :behaviour-menu true - :wrap (and fill? auto?))} - [:& element-behaviour-horizontal {:auto? auto? - :fill? fill? - :layout-item-sizing layout-item-h-sizing - :on-change on-change-behaviour-h-refactor}] - [:& element-behaviour-vertical {:auto? auto? - :fill? fill? - :layout-item-sizing layout-item-v-sizing - :on-change on-change-behaviour-v-refactor}]] - - [:div.btn-wrapper - {:class (when (and fill? auto?) "wrap")} - [:div.layout-behavior.horizontal - [:button.behavior-btn.tooltip.tooltip-bottom - {:alt "Fix width" - :class (dom/classnames :active (= layout-item-h-sizing :fix)) - :data-direction :h - :data-value :fix - :on-click on-change} - i/auto-fix-layout] - (when fill? - [:button.behavior-btn.tooltip.tooltip-bottom - {:alt "Width 100%" - :class (dom/classnames :active (= layout-item-h-sizing :fill)) - :data-direction :h - :data-value :fill - :on-click on-change} - i/auto-fill]) - (when auto? - [:button.behavior-btn.tooltip.tooltip-bottom - {:alt "Fit content" - :class (dom/classnames :active (= layout-item-h-sizing :auto)) - :data-direction :h - :data-value :auto - :on-click on-change} - i/auto-hug])] - - [:div.layout-behavior - [:button.behavior-btn.tooltip.tooltip-bottom - {:alt "Fix height" - :class (dom/classnames :active (= layout-item-v-sizing :fix)) - :data-direction :v - :data-value :fix - :on-click on-change} - i/auto-fix-layout] - (when fill? - [:button.behavior-btn.tooltip.tooltip-bottom-left - {:alt "Height 100%" - :class (dom/classnames :active (= layout-item-v-sizing :fill)) - :data-direction :v - :data-value :fill - :on-click on-change} - i/auto-fill]) - (when auto? - [:button.behavior-btn.tooltip.tooltip-bottom-left - {:alt "Fit content" - :class (dom/classnames :active (= layout-item-v-sizing :auto)) - :data-direction :v - :data-value :auto - :on-click on-change} - i/auto-hug])]]))) + [{:keys [auto? + fill? + layout-item-h-sizing + layout-item-v-sizing + on-change-behaviour-h-refactor + on-change-behaviour-v-refactor] :as props}] + [:div {:class (stl/css-case :behaviour-menu true + :wrap (and fill? auto?))} + [:& element-behaviour-horizontal {:auto? auto? + :fill? fill? + :layout-item-sizing layout-item-h-sizing + :on-change on-change-behaviour-h-refactor}] + [:& element-behaviour-vertical {:auto? auto? + :fill? fill? + :layout-item-sizing layout-item-v-sizing + :on-change on-change-behaviour-v-refactor}]]) (mf/defc align-self-row [{:keys [is-col? align-self on-change] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - dir-v [:start :center :end #_:stretch #_:baseline]] - (if new-css-system - [:& radio-buttons {:selected (d/name align-self) - :on-change on-change - :name "flex-align-self"} - [:& radio-button {:value "start" - :icon (get-layout-flex-icon-refactor :align-self :start is-col?) - :title "Align self start" - :id "align-self-start"}] - [:& radio-button {:value "center" - :icon (get-layout-flex-icon-refactor :align-self :center is-col?) - :title "Align self center" - :id "align-self-center"}] - [:& radio-button {:value "end" - :icon (get-layout-flex-icon-refactor :align-self :end is-col?) - :title "Align self end" - :id "align-self-end"}]] - - [:div.align-self-style - (for [align dir-v] - [:button.align-self.tooltip.tooltip-bottom - {:class (dom/classnames :active (= align-self align) - :tooltip-bottom-left (not= align :start) - :tooltip-bottom (= align :start)) - :alt (dm/str "Align self " (d/name align)) - :data-value align - :on-click on-change - :key (str "align-self" align)} - (get-layout-flex-icon :align-self align is-col?)])]))) + [:& radio-buttons {:selected (d/name align-self) + :on-change on-change + :name "flex-align-self"} + [:& radio-button {:value "start" + :icon (get-layout-flex-icon :align-self :start is-col?) + :title "Align self start" + :id "align-self-start"}] + [:& radio-button {:value "center" + :icon (get-layout-flex-icon :align-self :center is-col?) + :title "Align self center" + :id "align-self-center"}] + [:& radio-button {:value "end" + :icon (get-layout-flex-icon :align-self :end is-col?) + :title "Align self end" + :id "align-self-end"}]]) (mf/defc layout-item-menu {::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type" "is-layout-child?" "is-grid-parent?" "is-flex-parent?"]))]} [{:keys [ids values is-layout-child? is-layout-container? is-grid-parent? is-flex-parent?] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - selection-parents-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids)) + (let [selection-parents-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids)) selection-parents (mf/deref selection-parents-ref) is-absolute? (:layout-item-absolute values) @@ -408,17 +281,6 @@ "Layout element") set-align-self - (mf/use-fn - (mf/deps ids align-self) - (fn [event] - (let [value (-> (dom/get-current-target event) - (dom/get-data "value") - (d/read-string))] - (if (= align-self value) - (st/emit! (dwsl/update-layout-child ids {:layout-item-align-self nil})) - (st/emit! (dwsl/update-layout-child ids {:layout-item-align-self value})))))) - - set-align-self-refactor (mf/use-fn (mf/deps ids align-self) (fn [value] @@ -464,7 +326,7 @@ (mf/use-fn (mf/deps ids) (fn [value] - (let [value (if new-css-system (keyword value) value)] + (let [value (keyword value)] (st/emit! (dwsl/update-layout-child ids {:layout-item-h-sizing value}))))) @@ -472,7 +334,7 @@ (mf/use-fn (mf/deps ids) (fn [value] - (let [value (if new-css-system (keyword value) value)] + (let [value (keyword value)] (st/emit! (dwsl/update-layout-child ids {:layout-item-v-sizing value}))))) ;; Size and position @@ -485,7 +347,7 @@ (mf/use-fn (mf/deps ids) (fn [value] - (let [value (if new-css-system (keyword value) value)] + (let [value (keyword value)] (when (= value :static) (st/emit! (dwsl/update-layout-child ids {:layout-item-z-index nil}))) (st/emit! (dwsl/update-layout-child ids {:layout-item-absolute (= value :absolute)}))))) @@ -498,19 +360,18 @@ (fn [value] (st/emit! (dwsl/update-layout-child ids {:layout-item-z-index value}))))] - (if new-css-system - [:div {:class (stl/css :element-set)} - [:div {:class (stl/css :element-title)} - [:& title-bar {:collapsable? has-content? - :collapsed? (not open?) - :on-collapsed toggle-content - :title title - :class (stl/css-case :title-spacing-layout-element true - :title-spacing-empty (not has-content?))}]] - (when open? - [:div {:class (stl/css :flex-element-menu)} - (when (or is-layout-child? is-absolute?) - [:div {:class (stl/css :row)} + [:div {:class (stl/css :element-set)} + [:div {:class (stl/css :element-title)} + [:& title-bar {:collapsable? has-content? + :collapsed? (not open?) + :on-collapsed toggle-content + :title title + :class (stl/css-case :title-spacing-layout-element true + :title-spacing-empty (not has-content?))}]] + (when open? + [:div {:class (stl/css :flex-element-menu)} + (when (or is-layout-child? is-absolute?) + [:div {:class (stl/css :row)} [:div {:class (stl/css :position-options)} [:& radio-buttons {:selected (if is-absolute? "absolute" "static") :on-change on-change-position @@ -535,187 +396,93 @@ :nillable true :value (:layout-item-z-index values)}]])]) + [:div {:class (stl/css :row)} + [:& element-behaviour {:fill? is-layout-child? + :auto? is-layout-container? + :layout-item-v-sizing (or (:layout-item-v-sizing values) :fix) + :layout-item-h-sizing (or (:layout-item-h-sizing values) :fix) + :on-change-behaviour-h-refactor on-change-behaviour-h + :on-change-behaviour-v-refactor on-change-behaviour-v + :on-change on-change-behaviour}]] + + (when (and is-layout-child? is-flex-parent?) [:div {:class (stl/css :row)} - [:& element-behaviour {:fill? is-layout-child? - :auto? is-layout-container? - :layout-item-v-sizing (or (:layout-item-v-sizing values) :fix) - :layout-item-h-sizing (or (:layout-item-h-sizing values) :fix) - :on-change-behaviour-h-refactor on-change-behaviour-h - :on-change-behaviour-v-refactor on-change-behaviour-v - :on-change on-change-behaviour}]] + [:& align-self-row {:is-col? is-col? + :align-self align-self + :on-changer set-align-self}]]) - (when (and is-layout-child? is-flex-parent?) - [:div {:class (stl/css :row)} - [:& align-self-row {:is-col? is-col? - :align-self align-self - :on-changer set-align-self-refactor}]]) - - (when is-layout-child? - [:div {:class (stl/css :row)} - [:& margin-section {:values values - :change-margin-style change-margin-style - :on-margin-change on-margin-change}]]) - - (when (or (= (:layout-item-h-sizing values) :fill) - (= (:layout-item-v-sizing values) :fill)) - [:div {:class (stl/css :row)} - [:div {:class (stl/css :advanced-options)} - (when (= (:layout-item-h-sizing values) :fill) - [:div {:class (stl/css :horizontal-fill)} - [:div {:class (stl/css :layout-item-min-w) - :title (tr "workspace.options.layout-item.layout-item-min-w")} - - [:span {:class (stl/css :icon-text)} - "MIN W"] - [:> numeric-input* - {:className (stl/css :numeric-input) - :no-validate true - :min 0 - :data-wrap true - :placeholder "--" - :on-focus #(dom/select-target %) - :on-change (partial on-size-change :layout-item-min-w) - :value (get values :layout-item-min-w) - :nillable true}]] - - [:div {:class (stl/css :layout-item-max-w) - :title (tr "workspace.options.layout-item.layout-item-max-w")} - [:span {:class (stl/css :icon-text)} - "MAX W"] - [:> numeric-input* - {:className (stl/css :numeric-input) - :no-validate true - :min 0 - :data-wrap true - :placeholder "--" - :on-focus #(dom/select-target %) - :on-change (partial on-size-change :layout-item-max-w) - :value (get values :layout-item-max-w) - :nillable true}]]]) - (when (= (:layout-item-v-sizing values) :fill) - [:div {:class (stl/css :vertical-fill)} - [:div {:class (stl/css :layout-item-min-h) - :title (tr "workspace.options.layout-item.layout-item-min-h")} - - [:span {:class (stl/css :icon-text)} - "MIN H"] - [:> numeric-input* - {:className (stl/css :numeric-input) - :no-validate true - :min 0 - :data-wrap true - :placeholder "--" - :on-focus #(dom/select-target %) - :on-change (partial on-size-change :layout-item-min-h) - :value (get values :layout-item-min-h) - :nillable true}]] - - [:div {:class (stl/css :layout-item-max-h) - :title (tr "workspace.options.layout-item.layout-item-max-h")} - - [:span {:class (stl/css :icon-text)} - "MAX H"] - [:> numeric-input* - {:className (stl/css :numeric-input) - :no-validate true - :min 0 - :data-wrap true - :placeholder "--" - :on-focus #(dom/select-target %) - :on-change (partial on-size-change :layout-item-max-h) - :value (get values :layout-item-max-h) - :nillable true}]]])]])])] - - - [:div.element-set - [:div.element-set-title - [:span (cond - (and is-layout-container? (not is-layout-child?)) - "Flex board" - - is-flex-parent? - "Flex element" - - is-grid-parent? - "Grid element" - - :else - "Layout element")]] - - [:div.element-set-content.layout-item-menu - (when (or is-layout-child? is-absolute?) - [:div.layout-row - [:div.row-title.sizing "Position"] - [:div.btn-wrapper - [:div.absolute - [:button.behavior-btn.tooltip.tooltip-bottom - {:alt "Static" - :class (dom/classnames :active (not (:layout-item-absolute values))) - :on-click #(on-change-position :static)} - "Static"] - [:button.behavior-btn.tooltip.tooltip-bottom - {:alt "Absolute" - :class (dom/classnames :active (and (:layout-item-absolute values) (not= :multiple (:layout-item-absolute values)))) - :on-click #(on-change-position :absolute)} - "Absolute"]] - - [:div.tooltip.tooltip-bottom-left.z-index {:alt "z-index"} - i/layers - [:> numeric-input* - {:placeholder "--" - :on-focus #(dom/select-target %) - :on-change #(on-change-z-index %) - :nillable true - :value (:layout-item-z-index values)}]]]]) - - [:* - [:div.layout-row - [:div.row-title.sizing "Sizing"] - [:& element-behaviour {:fill? is-layout-child? - :auto? is-layout-container? - :layout-item-v-sizing (or (:layout-item-v-sizing values) :fix) - :layout-item-h-sizing (or (:layout-item-h-sizing values) :fix) - :on-change-behaviour-h-refactor on-change-behaviour-h - :on-change-behaviour-v-refactor on-change-behaviour-v - :on-change on-change-behaviour}]] - - (when (and is-layout-child? is-flex-parent?) - [:div.layout-row - [:div.row-title "Align"] - [:div.btn-wrapper - [:& align-self-row {:is-col? is-col? - :align-self align-self - :on-change set-align-self}]]]) - - (when is-layout-child? + (when is-layout-child? + [:div {:class (stl/css :row)} [:& margin-section {:values values :change-margin-style change-margin-style - :on-margin-change on-margin-change}]) + :on-margin-change on-margin-change}]]) - [:div.advanced-ops-body - [:div.input-wrapper - (for [item (cond-> [] - (= (:layout-item-h-sizing values) :fill) - (conj :layout-item-min-w :layout-item-max-w) + (when (or (= (:layout-item-h-sizing values) :fill) + (= (:layout-item-v-sizing values) :fill)) + [:div {:class (stl/css :row)} + [:div {:class (stl/css :advanced-options)} + (when (= (:layout-item-h-sizing values) :fill) + [:div {:class (stl/css :horizontal-fill)} + [:div {:class (stl/css :layout-item-min-w) + :title (tr "workspace.options.layout-item.layout-item-min-w")} - (= (:layout-item-v-sizing values) :fill) - (conj :layout-item-min-h :layout-item-max-h))] + [:span {:class (stl/css :icon-text)} + "MIN W"] + [:> numeric-input* + {:className (stl/css :numeric-input) + :no-validate true + :min 0 + :data-wrap true + :placeholder "--" + :on-focus #(dom/select-target %) + :on-change (partial on-size-change :layout-item-min-w) + :value (get values :layout-item-min-w) + :nillable true}]] - [:div.tooltip.tooltip-bottom - {:key (d/name item) - :alt (tr (dm/str "workspace.options.layout-item.title." (d/name item))) - :class (dom/classnames "maxH" (= item :layout-item-max-h) - "minH" (= item :layout-item-min-h) - "maxW" (= item :layout-item-max-w) - "minW" (= item :layout-item-min-w))} - [:div.input-element - {:alt (tr (dm/str "workspace.options.layout-item." (d/name item)))} - [:> numeric-input* - {:no-validate true - :min 0 - :data-wrap true - :placeholder "--" - :on-focus #(dom/select-target %) - :on-change (partial on-size-change item) - :value (get values item) - :nillable true}]]])]]]]]))) + [:div {:class (stl/css :layout-item-max-w) + :title (tr "workspace.options.layout-item.layout-item-max-w")} + [:span {:class (stl/css :icon-text)} + "MAX W"] + [:> numeric-input* + {:className (stl/css :numeric-input) + :no-validate true + :min 0 + :data-wrap true + :placeholder "--" + :on-focus #(dom/select-target %) + :on-change (partial on-size-change :layout-item-max-w) + :value (get values :layout-item-max-w) + :nillable true}]]]) + (when (= (:layout-item-v-sizing values) :fill) + [:div {:class (stl/css :vertical-fill)} + [:div {:class (stl/css :layout-item-min-h) + :title (tr "workspace.options.layout-item.layout-item-min-h")} + + [:span {:class (stl/css :icon-text)} + "MIN H"] + [:> numeric-input* + {:className (stl/css :numeric-input) + :no-validate true + :min 0 + :data-wrap true + :placeholder "--" + :on-focus #(dom/select-target %) + :on-change (partial on-size-change :layout-item-min-h) + :value (get values :layout-item-min-h) + :nillable true}]] + + [:div {:class (stl/css :layout-item-max-h) + :title (tr "workspace.options.layout-item.layout-item-max-h")} + + [:span {:class (stl/css :icon-text)} + "MAX H"] + [:> numeric-input* + {:className (stl/css :numeric-input) + :no-validate true + :min 0 + :data-wrap true + :placeholder "--" + :on-focus #(dom/select-target %) + :on-change (partial on-size-change :layout-item-max-h) + :value (get values :layout-item-max-h) + :nillable true}]]])]])])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.scss index 6899664041..a88b10213d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.scss @@ -8,125 +8,132 @@ .element-set { margin: 0; - .element-title { - .title-spacing-layout-element { - margin: 0 0 $s-4 0; - } - .title-spacing-empty { - padding-left: $s-2; - } +} + +.title-spacing-layout-element { + margin: 0 0 $s-4 0; +} + +.title-spacing-empty { + padding-left: $s-2; +} + +.flex-element-menu { + @include flexColumn; + gap: $s-12; +} + +.behaviour-menu { + display: flex; + gap: $s-4; +} + +.horizontal-behaviour { + &.one-element { + width: $s-28; } - .flex-element-menu { - @include flexColumn; - gap: $s-12; + &.two-element { + width: $s-60; + } + &.three-element { + width: $s-92; + } +} - .behaviour-menu { - display: flex; - gap: $s-4; - .horizontal-behaviour { - &.one-element { - width: $s-28; - } - &.two-element { - width: $s-60; - } - &.three-element { - width: $s-92; - } - } - .vertical-behaviour { - .rotated { - transform: rotate(90deg); - } - &.one-element { - width: $s-28; - } - &.two-element { - width: $s-60; - } - &.three-element { - width: $s-92; - } - } - } +.vertical-behaviour { + .rotated { + transform: rotate(90deg); + } + &.one-element { + width: $s-28; + } + &.two-element { + width: $s-60; + } + &.three-element { + width: $s-92; + } +} - .z-index-wrapper { - @extend .input-element; - width: $s-60; - } +.z-index-wrapper { + @extend .input-element; + width: $s-60; +} - .row { - display: flex; - gap: $s-4; - } +.row { + display: flex; + gap: $s-4; +} - .position-options { - width: $s-188; - } +.position-options { + width: $s-188; +} - .margin-row { - display: flex; - align-items: flex-start; - gap: $s-4; - .margin-mode { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - &.selected { - background-color: var(--button-tertiary-background-color-active); - color: var(--button-tertiary-foreground-color-active); - svg { - stroke: var(--button-tertiary-foreground-color-active); - } - } - } - .inputs-wrapper { - .margin-simple { - display: flex; - gap: $s-4; - .vertical-margin, - .horizontal-margin { - @extend .input-element; - width: $s-108; - } - } - .margin-multiple { - display: grid; - grid-template-columns: 1fr 1fr; - gap: $s-4; - .top-margin, - .bottom-margin, - .left-margin, - .right-margin { - @extend .input-element; - width: $s-108; - } - } - } - } +.margin-row { + display: flex; + align-items: flex-start; + gap: $s-4; +} - .advanced-options { - @include flexColumn; - .horizontal-fill, - .vertical-fill { - display: flex; - gap: $s-4; - .layout-item-min-w, - .layout-item-min-h, - .layout-item-max-w, - .layout-item-max-h { - @extend .input-element; - width: $s-108; - .icon-text { - justify-content: flex-start; - width: $s-80; - padding-top: $s-2; - } - } - } +.margin-mode { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } + &.selected { + background-color: var(--button-tertiary-background-color-active); + color: var(--button-tertiary-foreground-color-active); + svg { + stroke: var(--button-tertiary-foreground-color-active); } } } + +.margin-simple { + display: flex; + gap: $s-4; + .vertical-margin, + .horizontal-margin { + @extend .input-element; + width: $s-108; + } +} + +.margin-multiple { + display: grid; + grid-template-columns: 1fr 1fr; + gap: $s-4; +} + +.top-margin, +.bottom-margin, +.left-margin, +.right-margin { + @extend .input-element; + width: $s-108; +} + +.advanced-options { + @include flexColumn; +} + +.horizontal-fill, +.vertical-fill { + display: flex; + gap: $s-4; +} + +.layout-item-min-w, +.layout-item-min-h, +.layout-item-max-w, +.layout-item-max-h { + @extend .input-element; + width: $s-108; + .icon-text { + justify-content: flex-start; + width: $s-80; + padding-top: $s-2; + } +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs index fc408f592e..0cc61833c6 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs @@ -21,7 +21,6 @@ [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.components.numeric-input :refer [numeric-input*]] [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] - [app.main.ui.context :as ctx] [app.main.ui.hooks :as hooks] [app.main.ui.icons :as i] [app.util.dom :as dom] @@ -75,8 +74,7 @@ {::mf/wrap-props false ::mf/wrap [mf/memo]} [{:keys [ids ids-with-children values type all-types shape]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - options (if (= type :multiple) + (let [options (if (= type :multiple) (reduce #(union %1 %2) (map #(get type->options %) all-types)) (get type->options type)) @@ -193,16 +191,7 @@ :else :vert)) - on-orientation-clicked - (mf/use-fn - (mf/deps ids) - (fn [event] - (let [orientation (-> (dom/get-current-target event) - (dom/get-data "value") - (keyword))] - (st/emit! (udw/change-orientation ids orientation))))) - - on-orientation-change-refactor + on-orientation-change (mf/use-fn (mf/deps ids) (fn [orientation] @@ -348,30 +337,29 @@ ;; restore focus to the newly created numeric-input (let [radius-input (mf/ref-val radius-input-ref)] (dom/focus! radius-input))))) - (if new-css-system - [:div {:class (stl/css :element-set)} - (when (and (options :presets) - (or (nil? all-types) (= (count all-types) 1))) - [:div {:class (stl/css :presets)} - [:div {:class (stl/css-case :presets-wrapper true - :opened show-presets-dropdown?) - :on-click open-presets} - [:span {:class (stl/css :select-name)}(tr "workspace.options.size-presets")] - [:span {:class (stl/css :collapsed-icon)} i/arrow-refactor] + [:div {:class (stl/css :element-set)} + (when (and (options :presets) + (or (nil? all-types) (= (count all-types) 1))) + [:div {:class (stl/css :presets)} + [:div {:class (stl/css-case :presets-wrapper true + :opened show-presets-dropdown?) + :on-click open-presets} + [:span {:class (stl/css :select-name)} (tr "workspace.options.size-presets")] + [:span {:class (stl/css :collapsed-icon)} i/arrow-refactor] - [:& dropdown {:show show-presets-dropdown? - :on-close close-presets} - [:ul {:class (stl/css :custom-select-dropdown)} - (for [size-preset size-presets] - (if-not (:width size-preset) + [:& dropdown {:show show-presets-dropdown? + :on-close close-presets} + [:ul {:class (stl/css :custom-select-dropdown)} + (for [size-preset size-presets] + (if-not (:width size-preset) + [:li {:key (:name size-preset) + :class (stl/css-case :dropdown-element true + :disabled true)} + [:span {:class (stl/css :preset-name)} (:name size-preset)]] + + (let [preset-match (and (= (:width size-preset) (d/parse-integer (:width values) 0)) + (= (:height size-preset) (d/parse-integer (:height values) 0)))] [:li {:key (:name size-preset) - :class (stl/css-case :dropdown-element true - :disabled true)} - [:span {:class (stl/css :preset-name)} (:name size-preset)]] - - (let [preset-match (and (= (:width size-preset) (d/parse-integer (:width values) 0)) - (= (:height size-preset) (d/parse-integer (:height values) 0)))] - [:li {:key (:name size-preset) :class (stl/css-case :dropdown-element true :match preset-match) :data-width (:width size-preset) @@ -383,366 +371,189 @@ (when preset-match [:span {:class (stl/css :check-icon)} i/tick-refactor])])))]]] - [:& radio-buttons {:selected (or (d/name orientation) "") - :on-change on-orientation-change-refactor - :name "frame-otientation"} - [:& radio-button {:icon i/size-vertical-refactor - :value "vert" - :id "size-vertical"}] - [:& radio-button {:icon i/size-horizontal-refactor - :value "horiz" - :id "size-horizontal"}]]]) - (when (options :size) - [:div {:class (stl/css :size)} - [:div {:class (stl/css-case :width true - :disabled disabled-width-sizing?) - :title (tr "workspace.options.width")} - [:span {:class (stl/css :icon-text)} "W"] - [:> numeric-input* {:min 0.01 - :no-validate true - :placeholder "--" - :on-change on-width-change - :disabled disabled-width-sizing? - :className (stl/css :numeric-input) - :value (:width values)}]] - [:div {:class (stl/css-case :height true - :disabled disabled-height-sizing?) - :title (tr "workspace.options.height")} - [:span {:class (stl/css :icon-text)} "H"] - [:> numeric-input* {:min 0.01 - :no-validate true - :placeholder "--" - :on-change on-height-change - :disabled disabled-height-sizing? - :className (stl/css :numeric-input) - :value (:height values)}]] - [:button {:class (stl/css-case - :lock-size-btn true - :selected (true? proportion-lock) - :disabled (= proportion-lock :multiple)) - :on-click on-proportion-lock-change} - (if proportion-lock - i/lock-refactor - i/unlock-refactor)]]) - (when (options :position) - [:div {:class (stl/css :position)} - [:div {:class (stl/css-case :x-position true - :disabled disabled-position-x?) - :title (tr "workspace.options.x")} - [:span {:class (stl/css :icon-text)} "X"] - [:> numeric-input* {:no-validate true - :placeholder "--" - :on-change on-pos-x-change - :disabled disabled-position-x? - :className (stl/css :numeric-input) - :value (:x values)}]] + [:& radio-buttons {:selected (or (d/name orientation) "") + :on-change on-orientation-change + :name "frame-otientation"} + [:& radio-button {:icon i/size-vertical-refactor + :value "vert" + :id "size-vertical"}] + [:& radio-button {:icon i/size-horizontal-refactor + :value "horiz" + :id "size-horizontal"}]]]) + (when (options :size) + [:div {:class (stl/css :size)} + [:div {:class (stl/css-case :width true + :disabled disabled-width-sizing?) + :title (tr "workspace.options.width")} + [:span {:class (stl/css :icon-text)} "W"] + [:> numeric-input* {:min 0.01 + :no-validate true + :placeholder "--" + :on-change on-width-change + :disabled disabled-width-sizing? + :className (stl/css :numeric-input) + :value (:width values)}]] + [:div {:class (stl/css-case :height true + :disabled disabled-height-sizing?) + :title (tr "workspace.options.height")} + [:span {:class (stl/css :icon-text)} "H"] + [:> numeric-input* {:min 0.01 + :no-validate true + :placeholder "--" + :on-change on-height-change + :disabled disabled-height-sizing? + :className (stl/css :numeric-input) + :value (:height values)}]] + [:button {:class (stl/css-case + :lock-size-btn true + :selected (true? proportion-lock) + :disabled (= proportion-lock :multiple)) + :on-click on-proportion-lock-change} + (if proportion-lock + i/lock-refactor + i/unlock-refactor)]]) + (when (options :position) + [:div {:class (stl/css :position)} + [:div {:class (stl/css-case :x-position true + :disabled disabled-position-x?) + :title (tr "workspace.options.x")} + [:span {:class (stl/css :icon-text)} "X"] + [:> numeric-input* {:no-validate true + :placeholder "--" + :on-change on-pos-x-change + :disabled disabled-position-x? + :className (stl/css :numeric-input) + :value (:x values)}]] - [:div {:class (stl/css-case :y-position true - :disabled disabled-position-y?) - :title (tr "workspace.options.y")} - [:span {:class (stl/css :icon-text)} "Y"] - [:> numeric-input* {:no-validate true - :placeholder "--" - :disabled disabled-position-y? - :on-change on-pos-y-change - :className (stl/css :numeric-input) - :value (:y values)}]]]) - (when (or (options :rotation) (options :radius)) - [:div {:class (stl/css :rotation-radius)} + [:div {:class (stl/css-case :y-position true + :disabled disabled-position-y?) + :title (tr "workspace.options.y")} + [:span {:class (stl/css :icon-text)} "Y"] + [:> numeric-input* {:no-validate true + :placeholder "--" + :disabled disabled-position-y? + :on-change on-pos-y-change + :className (stl/css :numeric-input) + :value (:y values)}]]]) + (when (or (options :rotation) (options :radius)) + [:div {:class (stl/css :rotation-radius)} - (when (options :rotation) - [:div {:class (stl/css :rotation) - :title (tr "workspace.options.rotation")} - [:span {:class (stl/css :icon)} i/rotation-refactor] - [:> numeric-input* - {:no-validate true - :min 0 - :max 359 - :data-wrap true - :placeholder "--" - :on-change on-rotation-change - :className (stl/css :numeric-input) - :value (:rotation values)}]]) - - (when (options :radius) - [:div {:class (stl/css :radius)} - [:div {:class (stl/css :radius-inputs)} - (cond - (= radius-mode :radius-1) - [:div {:class (stl/css :radius-1) - :title (tr "workspace.options.radius")} - [:span {:class (stl/css :icon)} i/corner-radius-refactor] - [:> numeric-input* - {:placeholder "Mixed" - :ref radius-input-ref - :min 0 - :on-change on-radius-1-change - :className (stl/css :numeric-input) - :value (:rx values)}]] - - @radius-multi? - [:div {:class (stl/css :radius-1) - :title (tr "workspace.options.radius")} - [:span {:class (stl/css :icon)} i/corner-radius-refactor] - [:input.input-text - {:type "number" - :placeholder "Mixed" - :min 0 - :on-change on-radius-multi-change - :className (stl/css :numeric-input) - :value (if all-equal? (:rx values) nil)}]] - - - (= radius-mode :radius-4) - [:div {:class (stl/css :radius-4)} - [:div {:class (stl/css :small-input) - :title (tr "workspace.options.radius-top-left")} - [:> numeric-input* - {:placeholder "--" - :min 0 - :on-change on-radius-r1-change - :className (stl/css :numeric-input) - :value (:r1 values)}]] - - [:div {:class (stl/css :small-input) - :title (tr "workspace.options.radius-top-right")} - [:> numeric-input* - {:placeholder "--" - :min 0 - :on-change on-radius-r2-change - :className (stl/css :numeric-input) - :value (:r2 values)}]] - - [:div {:class (stl/css :small-input) - :title (tr "workspace.options.radius-bottom-left")} - [:> numeric-input* - {:placeholder "--" - :min 0 - :on-change on-radius-r4-change - :className (stl/css :numeric-input) - :value (:r4 values)}]] - - [:div {:class (stl/css :small-input) - :title (tr "workspace.options.radius-bottom-right")} - [:> numeric-input* - {:placeholder "--" - :min 0 - :on-change on-radius-r3-change - :className (stl/css :numeric-input) - :value (:r3 values)}]] - ])] - [:button {:class (stl/css-case :radius-mode true - :selected (= radius-mode :radius-4)) - :title (if(= radius-mode :radius-4) - (tr "workspace.options.radius.all-corners") - (tr "workspace.options.radius.single-corners")) - :on-click toggle-radius-mode} - i/corner-radius-refactor]])]) - (when (or (options :clip-content) (options :show-in-viewer)) - [:div {:class (stl/css :clip-show)} - (when (options :clip-content) - [:div {:class (stl/css :clip-content)} - [:input {:type "checkbox" - :id "clip-content" - :ref clip-content-ref - :class (stl/css :clip-content-input) - :checked (not (:show-content values)) - :on-change on-change-clip-content}] - - [:label {:for "clip-content" - :title (tr "workspace.options.clip-content") - :class (stl/css-case :clip-content-label true - :selected (not (:show-content values)))} - [:span {:class (stl/css :icon)} - i/clip-content-refactor] - ]]) - (when (options :show-in-viewer) - [:div {:class (stl/css :clip-content)} - [:input {:type "checkbox" - :id "show-in-viewer" - :ref show-in-viewer-ref - :class (stl/css :clip-content-input) - :checked (not (:hide-in-viewer values)) - :on-change on-change-show-in-viewer}] - - [:label {:for "show-in-viewer" - :title (tr "workspace.options.show-in-viewer") - :class (stl/css-case :clip-content-label true - :selected (not (:hide-in-viewer values)))} - [:span {:class (stl/css :icon)} - i/play-refactor]]])])] - - [:* - [:div.element-set - [:div.element-set-content - - ;; FRAME PRESETS - (when (and (options :presets) - (or (nil? all-types) (= (count all-types) 1))) ;; Don't show presets if multi selected - [:div.row-flex ;; some frames and some non frames - [:div.presets.custom-select.flex-grow {:class (when show-presets-dropdown? "opened") - :on-click open-presets} - [:span (tr "workspace.options.size-presets")] - [:span.dropdown-button i/arrow-down] - [:& dropdown {:show show-presets-dropdown? - :on-close close-presets} - [:ul.custom-select-dropdown - (for [size-preset size-presets] - (if-not (:width size-preset) - [:li.dropdown-label {:key (:name size-preset)} - [:span (:name size-preset)]] - [:li {:key (:name size-preset) - :data-width (:width size-preset) - :data-height (:height size-preset) - :on-click on-preset-selected} - (:name size-preset) - [:span (:width size-preset) " x " (:height size-preset)]]))]]] - [:span.orientation-icon {:data-value :vert - :on-click on-orientation-clicked} i/size-vert] - [:span.orientation-icon {:data-value :horiz - :on-click on-orientation-clicked} i/size-horiz]]) - - ;; WIDTH & HEIGHT - (when (options :size) - [:div.row-flex - [:span.element-set-subtitle (tr "workspace.options.size")] - [:div.input-element.width {:title (tr "workspace.options.width")} - [:> numeric-input* {:min 0.01 - :no-validate true - :placeholder "--" - :on-change on-width-change - :disabled disabled-width-sizing? - :value (:width values)}]] - - [:div.input-element.height {:title (tr "workspace.options.height")} - [:> numeric-input* {:min 0.01 - :no-validate true - :placeholder "--" - :on-change on-height-change - :disabled disabled-height-sizing? - :value (:height values)}]] - - [:div.lock-size {:class (dom/classnames - :selected (true? proportion-lock) - :disabled (= proportion-lock :multiple)) - :on-click on-proportion-lock-change} - (if proportion-lock - i/lock - i/unlock)]]) - - ;; POSITION - (when (options :position) - [:div.row-flex - [:span.element-set-subtitle (tr "workspace.options.position")] - [:div.input-element.Xaxis {:title (tr "workspace.options.x")} - [:> numeric-input* {:no-validate true - :placeholder "--" - :on-change on-pos-x-change - :disabled disabled-position-x? - :value (:x values)}]] - [:div.input-element.Yaxis {:title (tr "workspace.options.y")} - [:> numeric-input* {:no-validate true - :placeholder "--" - :disabled disabled-position-y? - :on-change on-pos-y-change - :value (:y values)}]]]) - - ;; ROTATION - (when (options :rotation) - [:div.row-flex - [:span.element-set-subtitle (tr "workspace.options.rotation")] - [:div.input-element.degrees {:title (tr "workspace.options.rotation")} - [:> numeric-input* - {:no-validate true - :min 0 - :max 359 - :data-wrap true - :placeholder "--" - :on-change on-rotation-change - :value (:rotation values)}]]]) - - ;; RADIUS - (when (options :radius) - [:div.row-flex - [:div.radius-options - [:div.radius-icon.tooltip.tooltip-bottom - {:class (dom/classnames - :selected (or (= radius-mode :radius-1) @radius-multi?)) - :alt (tr "workspace.options.radius.all-corners") - :on-click on-switch-to-radius-1} - i/radius-1] - [:div.radius-icon.tooltip.tooltip-bottom - {:class (dom/classnames - :selected (and (= radius-mode :radius-4) (not @radius-multi?))) - :alt (tr "workspace.options.radius.single-corners") - :on-click on-switch-to-radius-4} - i/radius-4]] + (when (options :rotation) + [:div {:class (stl/css :rotation) + :title (tr "workspace.options.rotation")} + [:span {:class (stl/css :icon)} i/rotation-refactor] + [:> numeric-input* + {:no-validate true + :min 0 + :max 359 + :data-wrap true + :placeholder "--" + :on-change on-rotation-change + :className (stl/css :numeric-input) + :value (:rotation values)}]]) + (when (options :radius) + [:div {:class (stl/css :radius)} + [:div {:class (stl/css :radius-inputs)} (cond (= radius-mode :radius-1) - [:div.input-element.mini {:title (tr "workspace.options.radius")} + [:div {:class (stl/css :radius-1) + :title (tr "workspace.options.radius")} + [:span {:class (stl/css :icon)} i/corner-radius-refactor] [:> numeric-input* - {:placeholder "--" + {:placeholder "Mixed" :ref radius-input-ref :min 0 :on-change on-radius-1-change + :className (stl/css :numeric-input) :value (:rx values)}]] @radius-multi? - [:div.input-element.mini {:title (tr "workspace.options.radius")} + [:div {:class (stl/css :radius-1) + :title (tr "workspace.options.radius")} + [:span {:class (stl/css :icon)} i/corner-radius-refactor] [:input.input-text {:type "number" - :placeholder "--" + :placeholder "Mixed" :min 0 :on-change on-radius-multi-change - :value ""}]] + :className (stl/css :numeric-input) + :value (if all-equal? (:rx values) nil)}]] + (= radius-mode :radius-4) - [:* - [:div.input-element.mini {:title (tr "workspace.options.radius-top-left")} + [:div {:class (stl/css :radius-4)} + [:div {:class (stl/css :small-input) + :title (tr "workspace.options.radius-top-left")} [:> numeric-input* {:placeholder "--" :min 0 :on-change on-radius-r1-change + :className (stl/css :numeric-input) :value (:r1 values)}]] - [:div.input-element.mini {:title (tr "workspace.options.radius-top-right")} + [:div {:class (stl/css :small-input) + :title (tr "workspace.options.radius-top-right")} [:> numeric-input* {:placeholder "--" :min 0 :on-change on-radius-r2-change + :className (stl/css :numeric-input) :value (:r2 values)}]] - [:div.input-element.mini {:title (tr "workspace.options.radius-bottom-right")} - [:> numeric-input* - {:placeholder "--" - :min 0 - :on-change on-radius-r3-change - :value (:r3 values)}]] - - [:div.input-element.mini {:title (tr "workspace.options.radius-bottom-left")} + [:div {:class (stl/css :small-input) + :title (tr "workspace.options.radius-bottom-left")} [:> numeric-input* {:placeholder "--" :min 0 :on-change on-radius-r4-change - :value (:r4 values)}]]])]) - (when (options :clip-content) - [:div.input-checkbox - [:input {:type "checkbox" - :id "clip-content" - :ref clip-content-ref - :checked (not (:show-content values)) - :on-change on-change-clip-content}] + :className (stl/css :numeric-input) + :value (:r4 values)}]] - [:label {:for "clip-content"} - (tr "workspace.options.clip-content")]]) - (when (options :show-in-viewer) - [:div.input-checkbox - [:input {:type "checkbox" - :id "show-in-viewer" - :ref show-in-viewer-ref - :checked (not (:hide-in-viewer values)) - :on-change on-change-show-in-viewer}] + [:div {:class (stl/css :small-input) + :title (tr "workspace.options.radius-bottom-right")} + [:> numeric-input* + {:placeholder "--" + :min 0 + :on-change on-radius-r3-change + :className (stl/css :numeric-input) + :value (:r3 values)}]]])] + [:button {:class (stl/css-case :radius-mode true + :selected (= radius-mode :radius-4)) + :title (if (= radius-mode :radius-4) + (tr "workspace.options.radius.all-corners") + (tr "workspace.options.radius.single-corners")) + :on-click toggle-radius-mode} + i/corner-radius-refactor]])]) + (when (or (options :clip-content) (options :show-in-viewer)) + [:div {:class (stl/css :clip-show)} + (when (options :clip-content) + [:div {:class (stl/css :clip-content)} + [:input {:type "checkbox" + :id "clip-content" + :ref clip-content-ref + :class (stl/css :clip-content-input) + :checked (not (:show-content values)) + :on-change on-change-clip-content}] - [:label {:for "show-in-viewer"} - (tr "workspace.options.show-in-viewer")]])]]]))) + [:label {:for "clip-content" + :title (tr "workspace.options.clip-content") + :class (stl/css-case :clip-content-label true + :selected (not (:show-content values)))} + [:span {:class (stl/css :icon)} + i/clip-content-refactor]]]) + (when (options :show-in-viewer) + [:div {:class (stl/css :clip-content)} + [:input {:type "checkbox" + :id "show-in-viewer" + :ref show-in-viewer-ref + :class (stl/css :clip-content-input) + :checked (not (:hide-in-viewer values)) + :on-change on-change-show-in-viewer}] + + [:label {:for "show-in-viewer" + :title (tr "workspace.options.show-in-viewer") + :class (stl/css-case :clip-content-label true + :selected (not (:hide-in-viewer values)))} + [:span {:class (stl/css :icon)} + i/play-refactor]]])])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.scss index b0a5cb1a65..f05c3e555e 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.scss @@ -9,216 +9,235 @@ .element-set { @include flexColumn; margin-bottom: $s-8; - .presets { - display: flex; - align-items: flex-start; - gap: $s-4; - .presets-wrapper { - @extend .asset-element; - position: relative; - display: flex; - height: $s-32; - width: $s-188; - padding: $s-8; - border-radius: $br-8; - .select-name { - @include titleTipography; - display: flex; - justify-content: flex-start; - align-items: center; - flex-grow: 1; - cursor: pointer; - } - .collapsed-icon { - @include flexCenter; - cursor: pointer; - svg { - @extend .button-icon-small; - stroke: var(--icon-foreground); - transform: rotate(90deg); - } - } - .custom-select-dropdown { - @extend .dropdown-wrapper; - margin-top: $s-2; - width: $s-252; - .dropdown-element { - @extend .dropdown-element-base; - .name-wrapper { - display: flex; - gap: $s-8; - flex-grow: 1; - .preset-name { - color: var(--menu-foreground-color-rest); - } - .preset-size { - color: var(--menu-foreground-color-rest); - } - } +} - .check-icon { - @include flexCenter; - svg { - @extend .button-icon-small; - stroke: var(--icon-foreground); - } - } +.presets { + display: flex; + align-items: flex-start; + gap: $s-4; +} - &.disabled { - pointer-events: none; - cursor: default; - .preset-name { - color: var(--menu-foreground-color); - } - } +.presets-wrapper { + @extend .asset-element; + position: relative; + display: flex; + height: $s-32; + width: $s-188; + padding: $s-8; + border-radius: $br-8; - &.match { - .name-wrapper .preset-name { - color: var(--menu-foreground-color-hover); - } - .check-icon svg { - stroke: var(--menu-foreground-color-hover); - } - } - - &:hover { - background-color: var(--menu-background-color-hover); - .name-wrapper .preset-name { - color: var(--menu-foreground-color-hover); - } - .check-icon svg { - stroke: var(--menu-foreground-color-hover); - } - } - } - } - &:hover { - .collapsed-icon svg { - stroke: var(--input-foreground-color-active); - } - } + .collapsed-icon { + @include flexCenter; + cursor: pointer; + svg { + @extend .button-icon-small; + stroke: var(--icon-foreground); + transform: rotate(90deg); } } - .size { - @include flexRow; - .height, - .width { - @extend .input-element; - width: $s-108; - .icon-text { - padding-top: $s-1; + + &:hover { + .collapsed-icon svg { + stroke: var(--input-foreground-color-active); + } + } +} + +.select-name { + @include titleTipography; + display: flex; + justify-content: flex-start; + align-items: center; + flex-grow: 1; + cursor: pointer; +} + +.custom-select-dropdown { + @extend .dropdown-wrapper; + margin-top: $s-2; + width: $s-252; + .dropdown-element { + @extend .dropdown-element-base; + .name-wrapper { + display: flex; + gap: $s-8; + flex-grow: 1; + .preset-name { + color: var(--menu-foreground-color-rest); } - &.disabled { - @extend .disabled-input; + .preset-size { + color: var(--menu-foreground-color-rest); } } - .lock-size-btn { - @extend .button-tertiary; - border-radius: $br-8; - height: $s-32; - width: $s-28; + + .check-icon { + @include flexCenter; svg { - @extend .button-icon; + @extend .button-icon-small; stroke: var(--icon-foreground); } - &.selected { - @extend .button-icon-selected; + } + + &.disabled { + pointer-events: none; + cursor: default; + .preset-name { + color: var(--menu-foreground-color); } } - } - .position { - @include flexRow; - .x-position, - .y-position { - @extend .input-element; - width: $s-108; - .icon-text { - padding-top: $s-1; + + &.match { + .name-wrapper .preset-name { + color: var(--menu-foreground-color-hover); } - &.disabled { - @extend .disabled-input; + .check-icon svg { + stroke: var(--menu-foreground-color-hover); } } - } - .rotation-radius { - display: flex; - align-items: flex-start; - justify-content: flex-start; - gap: $s-4; - .rotation { - @extend .input-element; - width: $s-108; - .icon-text { - padding-top: $s-1; + + &:hover { + background-color: var(--menu-background-color-hover); + .name-wrapper .preset-name { + color: var(--menu-foreground-color-hover); } - } - .radius { - display: flex; - align-items: flex-start; - justify-content: flex-start; - gap: $s-4; - .radius-inputs { - display: flex; - .radius-1 { - @extend .input-element; - width: $s-108; - } - .radius-4 { - display: grid; - grid-template-columns: 1fr 1fr; - gap: $s-4; - .small-input { - @extend .input-element; - width: $s-52; - } - } - } - .radius-mode { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - border-radius: $br-8; - svg { - @extend .button-icon; - stroke: var(--icon-foreground); - } - &.selected { - background-color: var(--button-tertiary-background-color-hover); - svg { - stroke: var(--button-tertiary-foreground-color-active); - } - } - } - } - } - .clip-show { - display: flex; - align-items: flex-start; - justify-content: flex-start; - gap: $s-4; - .clip-content, - .show-in-viewer { - .clip-content-input { - display: none; - } - .clip-content-label { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - border-radius: $br-8; - .icon { - @include flexCenter; - svg { - @extend .button-icon; - stroke: var(--icon-foreground); - } - } - &.selected { - background-color: var(--button-tertiary-background-color-hover); - svg { - stroke: var(--button-tertiary-foreground-color-active); - } - } + .check-icon svg { + stroke: var(--menu-foreground-color-hover); + } + } + } +} + +.size { + @include flexRow; +} + +.height, +.width { + @extend .input-element; + width: $s-108; + .icon-text { + padding-top: $s-1; + } + &.disabled { + @extend .disabled-input; + } +} + +.lock-size-btn { + @extend .button-tertiary; + border-radius: $br-8; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } + &.selected { + @extend .button-icon-selected; + } +} + +.position { + @include flexRow; +} + +.x-position, +.y-position { + @extend .input-element; + width: $s-108; + .icon-text { + padding-top: $s-1; + } + &.disabled { + @extend .disabled-input; + } +} + +.rotation-radius { + display: flex; + align-items: flex-start; + justify-content: flex-start; + gap: $s-4; +} + +.rotation { + @extend .input-element; + width: $s-108; + .icon-text { + padding-top: $s-1; + } +} +.radius { + display: flex; + align-items: flex-start; + justify-content: flex-start; + gap: $s-4; +} + +.radius-inputs { + display: flex; +} + +.radius-1 { + @extend .input-element; + width: $s-108; +} + +.radius-4 { + display: grid; + grid-template-columns: 1fr 1fr; + gap: $s-4; + .small-input { + @extend .input-element; + width: $s-52; + } +} + +.radius-mode { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + border-radius: $br-8; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } + &.selected { + background-color: var(--button-tertiary-background-color-hover); + svg { + stroke: var(--button-tertiary-foreground-color-active); + } + } +} + +.clip-show { + display: flex; + align-items: flex-start; + justify-content: flex-start; + gap: $s-4; +} + +.clip-content, +.show-in-viewer { + .clip-content-input { + display: none; + } + .clip-content-label { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + border-radius: $br-8; + .icon { + @include flexCenter; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } + } + &.selected { + background-color: var(--button-tertiary-background-color-hover); + svg { + stroke: var(--button-tertiary-foreground-color-active); } } } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs index a7246b2494..874e4628a5 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs @@ -19,7 +19,6 @@ [app.main.ui.components.numeric-input :refer [numeric-input*]] [app.main.ui.components.select :refer [select]] [app.main.ui.components.title-bar :refer [title-bar]] - [app.main.ui.context :as ctx] [app.main.ui.hooks :as h] [app.main.ui.icons :as i] [app.main.ui.workspace.sidebar.options.common :refer [advanced-options]] @@ -51,8 +50,7 @@ (mf/defc shadow-entry [{:keys [ids index value on-reorder disable-drag? on-blur open-state-ref]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - basic-offset-x-ref (mf/use-ref nil) + (let [basic-offset-x-ref (mf/use-ref nil) basic-offset-y-ref (mf/use-ref nil) basic-blur-ref (mf/use-ref nil) @@ -61,9 +59,7 @@ adv-blur-ref (mf/use-ref nil) adv-spread-ref (mf/use-ref nil) - shadow-style (if new-css-system - (:style value) - (dm/str (:style value))) + shadow-style (:style value) shadow-id (:id value) @@ -143,9 +139,7 @@ (mf/use-fn (mf/deps ids index) (fn [event] - (let [value (if new-css-system - (keyword event) - (-> event dom/get-target-val d/read-string))] + (let [value (keyword event)] (st/emit! (dch/update-shapes ids #(assoc-in % [:shadow index :style] value)))))) type-options [{:value "drop-shadow" :label (tr "workspace.options.shadow-options.drop-shadow")} @@ -155,187 +149,91 @@ manage-on-close #(st/emit! (dwu/commit-undo-transaction :color-row))] - [:div.shadow-option {:class (stl/css-case new-css-system - :global/shadow-option true - :shadow-element true - :dnd-over-top (= (:over dprops) :top) - :dnd-over-bot (= (:over dprops) :bot)) - :ref dref} - (if new-css-system - [:* - [:div {:class (stl/css :basic-options)} - [:div {:class (stl/css-case :shadow-info true - :hidden hidden?)} - [:button {:class (stl/css-case :more-options true - :selected open-shadow) - :on-click on-toggle-open-shadow} - i/menu-refactor] - [:div {:class (stl/css :type-select)} - [:& select - {:class (stl/css :shadow-type-select) - :default-value (d/name shadow-style) - :options type-options - :on-change on-type-change}]]] - [:div {:class (stl/css :actions)} - [:button {:class (stl/css :action-btn) - :on-click toggle-visibility} - (if hidden? - i/hide-refactor - i/shown-refactor)] - [:button {:class (stl/css :action-btn) - :on-click on-remove-shadow} - i/remove-refactor]]] - (when open-shadow - [:& advanced-options {:class (stl/css :shadow-advanced-options) - :visible? open-shadow - :on-close on-toggle-open-shadow} - - [:div {:class (stl/css :first-row)} - [:div {:class (stl/css :offset-x-input) - :title (tr "workspace.options.shadow-options.offsetx")} - [:span {:class (stl/css :input-label)} - "X"] - [:> numeric-input* {:className (stl/css :numeric-input) - :ref adv-offset-x-ref - :no-validate true - :placeholder "--" - :on-change (update-attr index :offset-x basic-offset-x-ref) - :on-blur on-blur - :value (:offset-x value)}]] - - [:div {:class (stl/css :blur-input) - :title (tr "workspace.options.shadow-options.blur")} - [:span {:class (stl/css :input-label)} - (tr "workspace.options.shadow-options.blur")] - [:> numeric-input* {:ref adv-blur-ref - :className (stl/css :numeric-input) - :no-validate true - :placeholder "--" - :on-change (update-attr index :blur basic-blur-ref) - :on-blur on-blur - :min 0 - :value (:blur value)}]] - - [:div {:class (stl/css :spread-input) - :title (tr "workspace.options.shadow-options.spread")} - [:span {:class (stl/css :input-label)} - (tr "workspace.options.shadow-options.spread")] - [:> numeric-input* {:ref adv-spread-ref - :className (stl/css :numeric-input) - :no-validate true - :placeholder "--" - :on-change (update-attr index :spread) - :on-blur on-blur - :value (:spread value)}]]] - - [:div {:class (stl/css :second-row)} - [:div {:class (stl/css :offset-y-input) - :title (tr "workspace.options.shadow-options.offsety")} - [:span {:class (stl/css :input-label)} - "Y"] - [:> numeric-input* {:ref adv-offset-y-ref - :className (stl/css :numeric-input) - :no-validate true - :placeholder "--" - :on-change (update-attr index :offset-y basic-offset-y-ref) - :on-blur on-blur - :value (:offset-y value)}]] - [:& color-row {:color (if (string? (:color value)) - ;; Support for old format colors - {:color (:color value) :opacity (:opacity value)} - (:color value)) - :title (tr "workspace.options.shadow-options.color") - :disable-gradient true - :disable-image true - :on-change update-color - :on-detach detach-color - :on-open manage-on-open - :on-close manage-on-close}]]])] - - [:* - [:div.shadow-option-main {:style {:display (when open-shadow "none")}} - [:div.element-set-actions-button - {:on-click on-toggle-open-shadow} - i/actions] - - [:select.input-select - {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :default-value shadow-style - :on-change on-type-change} - [:option {:value ":drop-shadow" - :selected (when (= shadow-style ":drop-shadow") "selected")} - (tr "workspace.options.shadow-options.drop-shadow")] - [:option {:value ":inner-shadow" - :selected (when (= shadow-style ":inner-shadow") "selected")} - (tr "workspace.options.shadow-options.inner-shadow")]] - - [:div.shadow-option-main-actions - [:div.element-set-actions-button {:on-click toggle-visibility} - (if hidden? i/eye-closed i/eye)] - [:div.element-set-actions-button - {:data-index index - :on-click on-remove-shadow} - i/minus]]] - - [:& advanced-options {:visible? open-shadow + [:div {:class (stl/css-case :global/shadow-option true + :shadow-element true + :dnd-over-top (= (:over dprops) :top) + :dnd-over-bot (= (:over dprops) :bot)) + :ref dref} + [:* + [:div {:class (stl/css :basic-options)} + [:div {:class (stl/css-case :shadow-info true + :hidden hidden?)} + [:button {:class (stl/css-case :more-options true + :selected open-shadow) + :on-click on-toggle-open-shadow} + i/menu-refactor] + [:div {:class (stl/css :type-select)} + [:& select + {:class (stl/css :shadow-type-select) + :default-value (d/name shadow-style) + :options type-options + :on-change on-type-change}]]] + [:div {:class (stl/css :actions)} + [:button {:class (stl/css :action-btn) + :on-click toggle-visibility} + (if hidden? + i/hide-refactor + i/shown-refactor)] + [:button {:class (stl/css :action-btn) + :on-click on-remove-shadow} + i/remove-refactor]]] + (when open-shadow + [:& advanced-options {:class (stl/css :shadow-advanced-options) + :visible? open-shadow :on-close on-toggle-open-shadow} - [:div.color-data - [:div.element-set-actions-button - {:on-click on-toggle-open-shadow} - i/actions] - [:select.input-select - {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :default-value shadow-style - :on-change on-type-change} - [:option {:value ":drop-shadow" - :selected (when (= shadow-style ":drop-shadow") "selected")} - (tr "workspace.options.shadow-options.drop-shadow")] - [:option {:value ":inner-shadow" - :selected (when (= shadow-style ":inner-shadow") "selected")} - (tr "workspace.options.shadow-options.inner-shadow")]]] - [:div.row-grid-2 - [:div.input-element {:title (tr "workspace.options.shadow-options.offsetx")} - [:> numeric-input* {:ref adv-offset-x-ref + [:div {:class (stl/css :first-row)} + [:div {:class (stl/css :offset-x-input) + :title (tr "workspace.options.shadow-options.offsetx")} + [:span {:class (stl/css :input-label)} + "X"] + [:> numeric-input* {:className (stl/css :numeric-input) + :ref adv-offset-x-ref :no-validate true :placeholder "--" :on-change (update-attr index :offset-x basic-offset-x-ref) :on-blur on-blur - :value (:offset-x value)}] - [:span.after (tr "workspace.options.shadow-options.offsetx")]] + :value (:offset-x value)}]] - [:div.input-element {:title (tr "workspace.options.shadow-options.offsety")} - [:> numeric-input* {:ref adv-offset-y-ref - :no-validate true - :placeholder "--" - :on-change (update-attr index :offset-y basic-offset-y-ref) - :on-blur on-blur - :value (:offset-y value)}] - [:span.after (tr "workspace.options.shadow-options.offsety")]]] - - [:div.row-grid-2 - [:div.input-element {:title (tr "workspace.options.shadow-options.blur")} + [:div {:class (stl/css :blur-input) + :title (tr "workspace.options.shadow-options.blur")} + [:span {:class (stl/css :input-label)} + (tr "workspace.options.shadow-options.blur")] [:> numeric-input* {:ref adv-blur-ref + :className (stl/css :numeric-input) :no-validate true :placeholder "--" :on-change (update-attr index :blur basic-blur-ref) :on-blur on-blur :min 0 - :value (:blur value)}] - [:span.after (tr "workspace.options.shadow-options.blur")]] + :value (:blur value)}]] - [:div.input-element {:title (tr "workspace.options.shadow-options.spread")} + [:div {:class (stl/css :spread-input) + :title (tr "workspace.options.shadow-options.spread")} + [:span {:class (stl/css :input-label)} + (tr "workspace.options.shadow-options.spread")] [:> numeric-input* {:ref adv-spread-ref + :className (stl/css :numeric-input) :no-validate true :placeholder "--" :on-change (update-attr index :spread) :on-blur on-blur - :value (:spread value)}] - [:span.after (tr "workspace.options.shadow-options.spread")]]] + :value (:spread value)}]]] - [:div.color-row-wrap + [:div {:class (stl/css :second-row)} + [:div {:class (stl/css :offset-y-input) + :title (tr "workspace.options.shadow-options.offsety")} + [:span {:class (stl/css :input-label)} + "Y"] + [:> numeric-input* {:ref adv-offset-y-ref + :className (stl/css :numeric-input) + :no-validate true + :placeholder "--" + :on-change (update-attr index :offset-y basic-offset-y-ref) + :on-blur on-blur + :value (:offset-y value)}]] [:& color-row {:color (if (string? (:color value)) - ;; Support for old format colors + ;; Support for old format colors {:color (:color value) :opacity (:opacity value)} (:color value)) :title (tr "workspace.options.shadow-options.color") @@ -344,13 +242,12 @@ :on-change update-color :on-detach detach-color :on-open manage-on-open - :on-close manage-on-close}]]]])])) + :on-close manage-on-close}]]])]])) (mf/defc shadow-menu {::mf/wrap-props false} [props] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - ids (unchecked-get props "ids") + (let [ids (unchecked-get props "ids") type (unchecked-get props "type") values (unchecked-get props "values") @@ -389,70 +286,35 @@ (mf/deps ids) #(st/emit! (dc/add-shadow ids (create-shadow))))] - (if new-css-system - [:div {:class (stl/css :element-set)} - [:div {:class (stl/css :element-title)} - [:& title-bar {:collapsable? has-shadows? - :collapsed? (not open?) - :on-collapsed toggle-content - :title (case type - :multiple (tr "workspace.options.shadow-options.title.multiple") - :group (tr "workspace.options.shadow-options.title.group") - (tr "workspace.options.shadow-options.title")) - :class (stl/css-case :title-spacing-shadow (not has-shadows?))} + [:div {:class (stl/css :element-set)} + [:div {:class (stl/css :element-title)} + [:& title-bar {:collapsable? has-shadows? + :collapsed? (not open?) + :on-collapsed toggle-content + :title (case type + :multiple (tr "workspace.options.shadow-options.title.multiple") + :group (tr "workspace.options.shadow-options.title.group") + (tr "workspace.options.shadow-options.title")) + :class (stl/css-case :title-spacing-shadow (not has-shadows?))} - (when-not (= :multiple shadows) - [:button {:class (stl/css :add-shadow) - :on-click on-add-shadow} i/add-refactor])]] - - (when open? - (cond - (= :multiple shadows) - [:div {:class (stl/css :element-set-content)} - [:div {:class (stl/css :multiple-shadows)} - [:div {:class (stl/css :label)} (tr "settings.multiple")] - [:div {:class (stl/css :actions)} - [:button {:class (stl/css :action-btn) - :on-click on-remove-all} - i/remove-refactor]]]] - - (seq shadows) - [:& h/sortable-container {} - [:div {:class (stl/css :element-set-content)} - (for [[index value] (d/enumerate shadows)] - [:& shadow-entry - {:key (dm/str "shadow-" index) - :ids ids - :value value - :on-reorder handle-reorder - :disable-drag? disable-drag? - :on-blur on-blur - :index index - :open-state-ref open-state-ref}])]]))] - - [:div.element-set.shadow-options - [:div.element-set-title - [:span - (case type - :multiple (tr "workspace.options.shadow-options.title.multiple") - :group (tr "workspace.options.shadow-options.title.group") - (tr "workspace.options.shadow-options.title"))] - - (when-not (= :multiple shadows) - [:div.add-page {:on-click on-add-shadow} i/close])] + (when-not (= :multiple shadows) + [:button {:class (stl/css :add-shadow) + :on-click on-add-shadow} i/add-refactor])]] + (when open? (cond (= :multiple shadows) - [:div.element-set-content - [:div.element-set-options-group - [:div.element-set-label (tr "settings.multiple")] - [:div.element-set-actions - [:div.element-set-actions-button {:on-click on-remove-all} - i/minus]]]] + [:div {:class (stl/css :element-set-content)} + [:div {:class (stl/css :multiple-shadows)} + [:div {:class (stl/css :label)} (tr "settings.multiple")] + [:div {:class (stl/css :actions)} + [:button {:class (stl/css :action-btn) + :on-click on-remove-all} + i/remove-refactor]]]] (seq shadows) [:& h/sortable-container {} - [:div.element-set-content + [:div {:class (stl/css :element-set-content)} (for [[index value] (d/enumerate shadows)] [:& shadow-entry {:key (dm/str "shadow-" index) @@ -462,4 +324,4 @@ :disable-drag? disable-drag? :on-blur on-blur :index index - :open-state-ref open-state-ref}])]])]))) + :open-state-ref open-state-ref}])]]))])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.scss index e5c27782a3..a9ef8ebd20 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.scss @@ -8,129 +8,127 @@ .element-set { margin: 0; - .element-title { - .title-spacing-shadow { - margin: 0; - padding-left: $s-2; +} + +.title-spacing-shadow { + margin: 0; + padding-left: $s-2; +} +.add-shadow { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } +} + +.element-set-content { + margin-top: $s-4; + @include flexColumn; +} + +.multiple-shadows { + @include flexRow; +} + +.label { + @extend .mixed-bar; +} + +.actions { + @include flexRow; +} + +.action-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } +} + +.shadow-element { + @include flexColumn; +} + +.basic-options { + @include flexRow; +} + +.shadow-info { + display: flex; + align-items: center; + gap: $s-1; + width: $s-188; + .more-options { + @extend .button-secondary; + height: $s-32; + width: $s-28; + border-radius: $br-8 0 0 $br-8; + svg { + @extend .button-icon; } - .add-shadow { - @extend .button-tertiary; - height: $s-32; - width: $s-28; + &.selected { + background-color: var(--button-radio-background-color-active); svg { - @extend .button-icon; + stroke: var(--button-radio-foreground-color-active); } } } - .element-set-content { - margin-top: $s-4; - @include flexColumn; - .multiple-shadows { - @include flexRow; - .label { - @extend .mixed-bar; - } - .actions { - @include flexRow; - .action-btn { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - } - } + .type-select { + padding: 0; + border-radius: 0 $br-8 $br-8 0; + flex-grow: 1; + .shadow-type-select { + flex-grow: 1; + border-radius: 0 $br-8 $br-8 0; } - .shadow-element { - @include flexColumn; - .basic-options { - @include flexRow; - .shadow-info { - display: flex; - align-items: center; - gap: $s-1; - width: $s-188; - .more-options { - @extend .button-secondary; - height: $s-32; - width: $s-28; - border-radius: $br-8 0 0 $br-8; - svg { - @extend .button-icon; - } - &.selected { - background-color: var(--button-radio-background-color-active); - svg { - stroke: var(--button-radio-foreground-color-active); - } - } - } - .type-select { - padding: 0; - border-radius: 0 $br-8 $br-8 0; - flex-grow: 1; - .shadow-type-select { - flex-grow: 1; - border-radius: 0 $br-8 $br-8 0; - } - } + } - &.hidden { - .more-options { - @include hiddenElement; - border: $s-1 solid var(--input-border-color-disabled); - } - .type-select { - @include hiddenElement; - .shadow-type-select { - @include hiddenElement; - border: $s-1 solid var(--input-border-color-disabled); - } - } - } - } - .actions { - @include flexRow; - .action-btn { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - } - } - } - .shadow-advanced-options { - @include flexColumn; - .first-row, - .second-row { - @include flexRow; - .offset-x-input, - .blur-input, - .spread-input, - .offset-y-input { - @extend .input-element; - width: $s-60; - min-width: $s-60; - align-items: baseline; - input { - width: $s-32; - } - } - .blur-input, - .spread-input { - width: $s-92; - .input-label { - width: $s-44; - } - } - .spread-input { - gap: $s-8; - } - } + &.hidden { + .more-options { + @include hiddenElement; + border: $s-1 solid var(--input-border-color-disabled); + } + .type-select { + @include hiddenElement; + .shadow-type-select { + @include hiddenElement; + border: $s-1 solid var(--input-border-color-disabled); } } } } + +.shadow-advanced-options { + @include flexColumn; +} + +.first-row, +.second-row { + @include flexRow; + .offset-x-input, + .blur-input, + .spread-input, + .offset-y-input { + @extend .input-element; + width: $s-60; + min-width: $s-60; + align-items: baseline; + input { + width: $s-32; + } + } + .blur-input, + .spread-input { + width: $s-92; + .input-label { + width: $s-44; + } + } + .spread-input { + gap: $s-8; + } +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs index 90d4d9ac33..093434da9b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs @@ -13,7 +13,6 @@ [app.main.data.workspace.colors :as dc] [app.main.store :as st] [app.main.ui.components.title-bar :refer [title-bar]] - [app.main.ui.context :as ctx] [app.main.ui.hooks :as h] [app.main.ui.icons :as i] [app.main.ui.workspace.sidebar.options.rows.stroke-row :refer [stroke-row]] @@ -38,8 +37,7 @@ (mf/defc stroke-menu {::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type" "show-caps"]))]} [{:keys [ids type values show-caps disable-stroke-style] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - label (case type + (let [label (case type :multiple (tr "workspace.options.selection-stroke") :group (tr "workspace.options.group-stroke") (tr "workspace.options.stroke")) @@ -53,27 +51,15 @@ strokes (:strokes values) has-strokes? (or (= :multiple strokes) (some? (seq strokes))) - handle-change-stroke-color - (mf/use-fn - (mf/deps ids) - (fn [index] - (fn [color] - (st/emit! (dc/change-stroke ids color index))))) - on-color-change-refactor + on-color-change (mf/use-fn (mf/deps ids) (fn [index color] (st/emit! (dc/change-stroke ids color index)))) - handle-remove - (mf/use-fn - (mf/deps ids) - (fn [index] - (fn [] - (st/emit! (dc/remove-stroke ids index))))) - on-remove-refactor + on-remove (mf/use-fn (mf/deps ids) (fn [index] @@ -85,16 +71,7 @@ (fn [_] (st/emit! (dc/remove-all-strokes ids)))) - handle-detach - (mf/use-fn - (mf/deps ids) - (fn [index] - (fn [color] - (let [color (-> color - (assoc :id nil :file-id nil))] - (st/emit! (dc/change-stroke ids color index)))))) - - on-color-detach-refactor + on-color-detach (mf/use-fn (mf/deps ids) (fn [index color] @@ -110,42 +87,19 @@ (st/emit! (dc/reorder-strokes ids index new-index))))) on-stroke-style-change - (fn [index] - (fn [event] - (let [value (-> (dom/get-target event) - (dom/get-value) - (d/read-string))] - (st/emit! (dc/change-stroke ids {:stroke-style value} index))))) - - on-stroke-style-change-refactor (mf/use-fn (mf/deps ids) (fn [index value] (st/emit! (dc/change-stroke ids {:stroke-style value} index)))) - on-stroke-alignment-change - (fn [index] - (fn [event] - (let [value (-> (dom/get-target event) - (dom/get-value) - (d/read-string))] - (when-not (str/empty? value) - (st/emit! (dc/change-stroke ids {:stroke-alignment value} index)))))) - - on-stroke-alignment-change-refactor (fn [index value] (when-not (str/empty? value) (st/emit! (dc/change-stroke ids {:stroke-alignment value} index)))) - on-stroke-width-change - (fn [index] - (fn [value] - (when-not (str/empty? value) - (st/emit! (dc/change-stroke ids {:stroke-width value} index))))) - on-stroke-width-change-refactor + on-stroke-width-change (fn [index value] (when-not (str/empty? value) (st/emit! (dc/change-stroke ids {:stroke-width value} index)))) @@ -207,70 +161,27 @@ on-blur (fn [_] (reset! disable-drag false))] - (if new-css-system - [:div {:class (stl/css :element-set)} - [:div {:class (stl/css :element-title)} - [:& title-bar {:collapsable? has-strokes? - :collapsed? (not open?) - :on-collapsed toggle-content - :title label - :class (stl/css-case :title-spacing-stroke (not has-strokes?))} + [:div {:class (stl/css :element-set)} + [:div {:class (stl/css :element-title)} + [:& title-bar {:collapsable? has-strokes? + :collapsed? (not open?) + :on-collapsed toggle-content + :title label + :class (stl/css-case :title-spacing-stroke (not has-strokes?))} - [:button {:class (stl/css :add-stroke) - :on-click on-add-stroke} i/add-refactor]]] - (when open? - [:div {:class (stl/css-case :element-content true - :empty-content (not has-strokes?))} - (cond - (= :multiple strokes) - [:div {:class (stl/css :element-set-options-group)} - [:div {:class (stl/css :group-label)} - (tr "settings.multiple")] - [:button {:on-click handle-remove-all - :class (stl/css :remove-btn)} - i/remove-refactor]] - (seq strokes) - [:& h/sortable-container {} - (for [[index value] (d/enumerate (:strokes values []))] - [:& stroke-row {:key (dm/str "stroke-" index) - :stroke value - :title (tr "workspace.options.stroke-color") - :index index - :show-caps show-caps - :on-color-change on-color-change-refactor - :on-color-detach on-color-detach-refactor - :on-stroke-width-change on-stroke-width-change-refactor - :on-stroke-style-change on-stroke-style-change-refactor - :on-stroke-alignment-change on-stroke-alignment-change-refactor - :open-caps-select open-caps-select - :close-caps-select close-caps-select - :on-stroke-cap-start-change on-stroke-cap-start-change - :on-stroke-cap-end-change on-stroke-cap-end-change - :on-stroke-cap-switch on-stroke-cap-switch - :on-remove on-remove-refactor - :on-reorder (handle-reorder index) - :disable-drag disable-drag - :on-focus on-focus - :select-on-focus (not @disable-drag) - :on-blur on-blur - :disable-stroke-style disable-stroke-style}])])])] - - - [:div.element-set - [:div.element-set-title - [:span label] - [:div.add-page {:on-click on-add-stroke} i/close]] - - [:div.element-set-content + [:button {:class (stl/css :add-stroke) + :on-click on-add-stroke} i/add-refactor]]] + (when open? + [:div {:class (stl/css-case :element-content true + :empty-content (not has-strokes?))} (cond (= :multiple strokes) - [:div.element-set-options-group - [:div.element-set-label (tr "settings.multiple")] - [:div.element-set-actions - [:div.element-set-actions-button {:on-click handle-remove-all} - i/minus]]] - - + [:div {:class (stl/css :element-set-options-group)} + [:div {:class (stl/css :group-label)} + (tr "settings.multiple")] + [:button {:on-click handle-remove-all + :class (stl/css :remove-btn)} + i/remove-refactor]] (seq strokes) [:& h/sortable-container {} (for [[index value] (d/enumerate (:strokes values []))] @@ -279,8 +190,8 @@ :title (tr "workspace.options.stroke-color") :index index :show-caps show-caps - :on-color-change handle-change-stroke-color - :on-color-detach handle-detach + :on-color-change on-color-change + :on-color-detach on-color-detach :on-stroke-width-change on-stroke-width-change :on-stroke-style-change on-stroke-style-change :on-stroke-alignment-change on-stroke-alignment-change @@ -289,10 +200,10 @@ :on-stroke-cap-start-change on-stroke-cap-start-change :on-stroke-cap-end-change on-stroke-cap-end-change :on-stroke-cap-switch on-stroke-cap-switch - :on-remove handle-remove + :on-remove on-remove :on-reorder (handle-reorder index) :disable-drag disable-drag :on-focus on-focus :select-on-focus (not @disable-drag) :on-blur on-blur - :disable-stroke-style disable-stroke-style}])])]]))) + :disable-stroke-style disable-stroke-style}])])])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.scss index 252334692e..53f6af64e0 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.scss @@ -8,43 +8,48 @@ .element-set { margin: 0; - .element-title { - margin: 0; - .title-spacing-stroke { - padding-left: $s-2; - margin: 0; - } - .add-stroke { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - } - } +} - .element-content { - display: flex; - flex-direction: column; - gap: $s-12; - margin: $s-4 0 $s-8 0; - .element-set-options-group { - @include flexRow; - .group-label { - @extend .mixed-bar; - } - .remove-btn { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - } - } - &.empty-content { - margin: 0; - } +.element-title { + margin: 0; +} + +.title-spacing-stroke { + padding-left: $s-2; + margin: 0; +} +.add-stroke { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } +} + +.element-content { + display: flex; + flex-direction: column; + gap: $s-12; + margin: $s-4 0 $s-8 0; + &.empty-content { + margin: 0; + } +} + +.element-set-options-group { + @include flexRow; +} + +.group-label { + @extend .mixed-bar; +} + +.remove-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; } } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs index 792b83f943..2092e55101 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs @@ -11,16 +11,13 @@ [app.main.data.workspace.changes :as dch] [app.main.store :as st] [app.main.ui.components.title-bar :refer [title-bar]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] - [app.main.ui.workspace.sidebar.options.rows.input-row :refer [input-row]] [app.util.dom :as dom] [app.util.i18n :refer [tr]] [rumext.v2 :as mf])) (mf/defc attribute-value [{:keys [attr value on-change on-delete] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - handle-change + (let [handle-change (mf/use-fn (mf/deps attr on-change) (fn [event] @@ -33,56 +30,31 @@ (on-delete attr))) label (->> attr last d/name)] - (if new-css-system - [:* - (if (string? value) - [:div {:class (stl/css :attr-content)} - [:span {:class (stl/css :attr-name)} label] - [:div {:class (stl/css :attr-input)} - [:input {:value value - :class "input-text" - :on-change handle-change}]] - [:div {:class (stl/css :attr-actions)} - [:button {:class (stl/css :attr-action-btn) - :on-click handle-delete} - i/remove-refactor]]] - [:div {:class (stl/css :attr-nested-content)} - [:div {:class (stl/css :attr-title)} - (str (d/name (last attr)))] - (for [[key value] value] - [:div {:class (stl/css :attr-row) :key key} - [:& attribute-value {:key key - :attr (conj attr key) - :value value - :on-change on-change - :on-delete on-delete}]])])] - [:div.element-set-content - (if (string? value) - [:div.row-flex.row-flex-removable - [:& input-row {:label label - :type :text - :class "large" - :value (str value) - :on-change handle-change}] - [:div.element-set-actions - [:div.element-set-actions-button {:on-click handle-delete} - i/minus]]] - - [:* - [:div.element-set-title - {:style {:border-bottom "1px solid #444" :margin-bottom "0.5rem"}} - [:span (str (d/name (last attr)))]] - - (for [[key value] value] - [:& attribute-value {:key key - :attr (conj attr key) - :value value - :on-change on-change - :on-delete on-delete}])])]))) + [:* + (if (string? value) + [:div {:class (stl/css :attr-content)} + [:span {:class (stl/css :attr-name)} label] + [:div {:class (stl/css :attr-input)} + [:input {:value value + :class "input-text" + :on-change handle-change}]] + [:div {:class (stl/css :attr-actions)} + [:button {:class (stl/css :attr-action-btn) + :on-click handle-delete} + i/remove-refactor]]] + [:div {:class (stl/css :attr-nested-content)} + [:div {:class (stl/css :attr-title)} + (str (d/name (last attr)))] + (for [[key value] value] + [:div {:class (stl/css :attr-row) :key key} + [:& attribute-value {:key key + :attr (conj attr key) + :value value + :on-change on-change + :on-delete on-delete}]])])])) (mf/defc svg-attrs-menu [{:keys [ids values]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - state* (mf/use-state true) + (let [state* (mf/use-state true) open? (deref state*) attrs (:svg-attrs values) has-attributes? (or (= :multiple attrs) (some? (seq attrs))) @@ -112,30 +84,18 @@ (st/emit! (dch/update-shapes ids update-fn)))))] (when-not (empty? attrs) - (if new-css-system - [:div {:class (stl/css :element-set)} - [:div {:class (stl/css :element-set-title)} - [:& title-bar {:collapsable? has-attributes? - :collapsed? (not open?) - :on-collapsed toggle-content - :title (tr "workspace.sidebar.options.svg-attrs.title") - :class (stl/css-case :title-spacing-svg-attrs (not has-attributes?))}]] - (when open? - [:div {:class (stl/css :element-set-content)} + [:div {:class (stl/css :element-set)} + [:div {:class (stl/css :element-set-title)} + [:& title-bar {:collapsable? has-attributes? + :collapsed? (not open?) + :on-collapsed toggle-content + :title (tr "workspace.sidebar.options.svg-attrs.title") + :class (stl/css-case :title-spacing-svg-attrs (not has-attributes?))}]] + (when open? + [:div {:class (stl/css :element-set-content)} (for [[attr-key attr-value] attrs] [:& attribute-value {:key attr-key :attr [attr-key] :value attr-value :on-change handle-change - :on-delete handle-delete}])])] - - [:div.element-set - [:div.element-set-title - [:span (tr "workspace.sidebar.options.svg-attrs.title")]] - - (for [[attr-key attr-value] attrs] - [:& attribute-value {:key attr-key - :attr [attr-key] - :value attr-value - :on-change handle-change - :on-delete handle-delete}])])))) + :on-delete handle-delete}])])]))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.scss index 5926c782b8..536f589c99 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.scss @@ -8,41 +8,49 @@ .element-set { margin: 0; - .title-spacing-svg-attrs { - padding-left: $s-2; - margin: 0; - } - .element-set-content { - @include flexColumn; - margin: $s-4 0 0 0; - .attr-content { - display: flex; - gap: $s-4; - .attr-name { - @include titleTipography; - @include twoLineTextEllipsis; - width: $s-88; - margin: auto $s-4; - margin-right: 0; - display: inline-block; - } - .attr-input { - @extend .input-element; - width: $s-124; - } - .attr-actions { - display: flex; - gap: $s-4; - .attr-action-btn { - @extend .button-tertiary; - width: $s-28; - height: $s-32; - svg { - @extend .button-icon; - } - } - } - } +} + +.title-spacing-svg-attrs { + padding-left: $s-2; + margin: 0; +} + +.element-set-content { + @include flexColumn; + margin: $s-4 0 0 0; +} + +.attr-content { + display: flex; + gap: $s-4; +} + +.attr-name { + @include titleTipography; + @include twoLineTextEllipsis; + width: $s-88; + margin: auto $s-4; + margin-right: 0; + display: inline-block; + color: var(--title-foreground-color); +} + +.attr-input { + @extend .input-element; + width: $s-124; +} + +.attr-actions { + display: flex; + gap: $s-4; +} + +.attr-action-btn { + @extend .button-tertiary; + width: $s-28; + height: $s-32; + svg { + @extend .button-icon; } } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs index 8e393ae2de..913630f049 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs @@ -30,181 +30,101 @@ (mf/defc text-align-options [{:keys [values on-change on-blur] :as props}] (let [{:keys [text-align]} values - new-css-system (mf/use-ctx ctx/new-css-system) - handle-change (mf/use-fn + (mf/deps on-blur) (fn [value] - (let [new-align (if new-css-system - value - (-> (dom/get-current-target value) - (dom/get-data "value")))] - (on-change {:text-align new-align}) - (when (some? on-blur) (on-blur)))))] + (on-change {:text-align value}) + (when (some? on-blur) (on-blur))))] ;; --- Align - (if new-css-system - [:div {:class (stl/css :align-options)} - [:& radio-buttons {:selected text-align - :on-change handle-change - :name "align-text-options"} - [:& radio-button {:value "left" - :id "text-align-left" - :title (tr "workspace.options.text-options.text-align-left" (sc/get-tooltip :text-align-left)) - :icon i/text-align-left-refactor}] - [:& radio-button {:value "center" - :id "text-align-center" - :title (tr "workspace.options.text-options.text-align-center" (sc/get-tooltip :text-align-center)) - :icon i/text-align-center-refactor}] - [:& radio-button {:value "right" - :id "text-align-right" - :title (tr "workspace.options.text-options.text-align-right" (sc/get-tooltip :text-align-right)) - :icon i/text-align-right-refactor}] - [:& radio-button {:value "justify" - :id "text-align-justify" - :title (tr "workspace.options.text-options.text-align-justify" (sc/get-tooltip :text-align-justify)) - :icon i/text-justify-refactor}]]] - [:div.align-icons - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.text-align-left" (sc/get-tooltip :text-align-left)) - :class (dom/classnames :current (= "left" text-align)) - :data-value "left" - :on-click handle-change} - i/text-align-left] - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.text-align-center" (sc/get-tooltip :text-align-center)) - :class (dom/classnames :current (= "center" text-align)) - :data-value "center" - :on-click handle-change} - i/text-align-center] - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.text-align-right" (sc/get-tooltip :text-align-right)) - :class (dom/classnames :current (= "right" text-align)) - :data-value "right" - :on-click handle-change} - i/text-align-right] - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.text-align-justify" (sc/get-tooltip :text-align-justify)) - :class (dom/classnames :current (= "justify" text-align)) - :data-value "justify" - :on-click handle-change} - i/text-align-justify]]))) + [:div {:class (stl/css :align-options)} + [:& radio-buttons {:selected text-align + :on-change handle-change + :name "align-text-options"} + [:& radio-button {:value "left" + :id "text-align-left" + :title (tr "workspace.options.text-options.text-align-left" (sc/get-tooltip :text-align-left)) + :icon i/text-align-left-refactor}] + [:& radio-button {:value "center" + :id "text-align-center" + :title (tr "workspace.options.text-options.text-align-center" (sc/get-tooltip :text-align-center)) + :icon i/text-align-center-refactor}] + [:& radio-button {:value "right" + :id "text-align-right" + :title (tr "workspace.options.text-options.text-align-right" (sc/get-tooltip :text-align-right)) + :icon i/text-align-right-refactor}] + [:& radio-button {:value "justify" + :id "text-align-justify" + :title (tr "workspace.options.text-options.text-align-justify" (sc/get-tooltip :text-align-justify)) + :icon i/text-justify-refactor}]]])) (mf/defc text-direction-options [{:keys [values on-change on-blur] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - direction (:text-direction values) + (let [direction (:text-direction values) handle-change (mf/use-fn (mf/deps direction) (fn [value] - (let [val (if new-css-system - value - (-> (dom/get-current-target value) - (dom/get-data "value"))) - dir (if (= val direction) + (let [dir (if (= value direction) "none" - val)] + value)] (on-change {:text-direction dir}) (when (some? on-blur) (on-blur)))))] - (if new-css-system - [:div {:class (stl/css :text-direction-options)} - [:& radio-buttons {:selected direction - :on-change handle-change - :name "text-direction-options"} - [:& radio-button {:value "ltr" - :type "checkbox" - :id "ltr-text-direction" - :title (tr "workspace.options.text-options.direction-ltr") - :icon i/text-ltr-refactor}] - [:& radio-button {:value "rtl" - :type "checkbox" - :id "rtl-text-direction" - :title (tr "workspace.options.text-options.direction-rtl") - :icon i/text-rtl-refactor}]]] - ;; --- Align - [:div.align-icons - [:span.tooltip.tooltip-bottom-left - {:alt (tr "workspace.options.text-options.direction-ltr") - :class (dom/classnames :current (= "ltr" direction)) - :data-value "ltr" - :on-click handle-change} - i/text-direction-ltr] - [:span.tooltip.tooltip-bottom-left - {:alt (tr "workspace.options.text-options.direction-rtl") - :class (dom/classnames :current (= "rtl" direction)) - :data-value "rtl" - :on-click handle-change} - i/text-direction-rtl]]))) + [:div {:class (stl/css :text-direction-options)} + [:& radio-buttons {:selected direction + :on-change handle-change + :name "text-direction-options"} + [:& radio-button {:value "ltr" + :type "checkbox" + :id "ltr-text-direction" + :title (tr "workspace.options.text-options.direction-ltr") + :icon i/text-ltr-refactor}] + [:& radio-button {:value "rtl" + :type "checkbox" + :id "rtl-text-direction" + :title (tr "workspace.options.text-options.direction-rtl") + :icon i/text-rtl-refactor}]]])) (mf/defc vertical-align [{:keys [values on-change on-blur] :as props}] (let [{:keys [vertical-align]} values - new-css-system (mf/use-ctx ctx/new-css-system) vertical-align (or vertical-align "top") handle-change (mf/use-fn + (mf/deps on-blur) (fn [value] - (let [new-align (if new-css-system - value - (-> (dom/get-current-target value) - (dom/get-data "value")))] - (on-change {:vertical-align new-align}) - (when (some? on-blur) (on-blur)))))] + (on-change {:vertical-align value}) + (when (some? on-blur) (on-blur))))] - (if new-css-system - [:div {:class (stl/css :vertical-align-options)} - [:& radio-buttons {:selected vertical-align - :on-change handle-change - :name "vertical-align-text-options"} - [:& radio-button {:value "top" - :id "vertical-text-align-top" - :title (tr "workspace.options.text-options.align-top") - :icon i/text-top-refactor}] - [:& radio-button {:value "center" - :id "vertical-text-align-center" - :title (tr "workspace.options.text-options.align-middle") - :icon i/text-middle-refactor}] - [:& radio-button {:value "bottom" - :id "vertical-text-align-bottom" - :title (tr "workspace.options.text-options.align-bottom") - :icon i/text-bottom-refactor}]]] - [:div.align-icons - [:span.tooltip.tooltip-bottom-left - {:alt (tr "workspace.options.text-options.align-top") - :class (dom/classnames :current (= "top" vertical-align)) - :data-value "top" - :on-click handle-change} - i/align-top] - [:span.tooltip.tooltip-bottom-left - {:alt (tr "workspace.options.text-options.align-middle") - :class (dom/classnames :current (= "center" vertical-align)) - :data-value "center" - :on-click handle-change} - i/align-middle] - [:span.tooltip.tooltip-bottom-left - {:alt (tr "workspace.options.text-options.align-bottom") - :class (dom/classnames :current (= "bottom" vertical-align)) - :data-value "bottom" - :on-click handle-change} - i/align-bottom]]))) + [:div {:class (stl/css :vertical-align-options)} + [:& radio-buttons {:selected vertical-align + :on-change handle-change + :name "vertical-align-text-options"} + [:& radio-button {:value "top" + :id "vertical-text-align-top" + :title (tr "workspace.options.text-options.align-top") + :icon i/text-top-refactor}] + [:& radio-button {:value "center" + :id "vertical-text-align-center" + :title (tr "workspace.options.text-options.align-middle") + :icon i/text-middle-refactor}] + [:& radio-button {:value "bottom" + :id "vertical-text-align-bottom" + :title (tr "workspace.options.text-options.align-bottom") + :icon i/text-bottom-refactor}]]])) (mf/defc grow-options [{:keys [ids values on-blur] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - grow-type (:grow-type values) + (let [grow-type (:grow-type values) handle-change-grow (mf/use-fn - (mf/deps ids new-css-system) + (mf/deps ids on-blur) (fn [value] (let [uid (js/Symbol) - grow-type (if new-css-system - (keyword value) - (-> (dom/get-current-target value) - (dom/get-data "value") - (keyword)))] + grow-type (keyword value)] (st/emit! (dwu/start-undo-transaction uid) (dch/update-shapes ids #(assoc % :grow-type grow-type))) @@ -212,105 +132,55 @@ (ts/schedule #(st/emit! (dwu/commit-undo-transaction uid)))) (when (some? on-blur) (on-blur))))] - (if new-css-system - [:div {:class (stl/css :grow-options)} - [:& radio-buttons {:selected (d/name grow-type) - :on-change handle-change-grow - :name "grow-text-options"} - [:& radio-button {:value "fixed" - :id "text-fixed-grow" - :title (tr "workspace.options.text-options.grow-fixed") - :icon i/text-fixed-refactor}] - [:& radio-button {:value "auto-width" - :id "text-auto-width-grow" - :title (tr "workspace.options.text-options.grow-auto-width") - :icon i/text-auto-width-refactor}] - [:& radio-button {:value "auto-height" - :id "text-auto-height-grow" - :title (tr "workspace.options.text-options.grow-auto-height") - :icon i/text-auto-height-refactor}]]] - - [:div.align-icons - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.grow-fixed") - :class (dom/classnames :current (= :fixed grow-type)) - :data-value "fixed" - :on-click handle-change-grow} - i/auto-fix] - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.grow-auto-width") - :data-value "auto-width" - :class (dom/classnames :current (= :auto-width grow-type)) - :on-click handle-change-grow} - i/auto-width] - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.grow-auto-height") - :class (dom/classnames :current (= :auto-height grow-type)) - :data-value "auto-height" - :on-click handle-change-grow} - i/auto-height]]))) + [:div {:class (stl/css :grow-options)} + [:& radio-buttons {:selected (d/name grow-type) + :on-change handle-change-grow + :name "grow-text-options"} + [:& radio-button {:value "fixed" + :id "text-fixed-grow" + :title (tr "workspace.options.text-options.grow-fixed") + :icon i/text-fixed-refactor}] + [:& radio-button {:value "auto-width" + :id "text-auto-width-grow" + :title (tr "workspace.options.text-options.grow-auto-width") + :icon i/text-auto-width-refactor}] + [:& radio-button {:value "auto-height" + :id "text-auto-height-grow" + :title (tr "workspace.options.text-options.grow-auto-height") + :icon i/text-auto-height-refactor}]]])) (mf/defc text-decoration-options [{:keys [values on-change on-blur] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - text-decoration (or (:text-decoration values) "none") + (let [text-decoration (or (:text-decoration values) "none") handle-change (mf/use-fn (mf/deps text-decoration) (fn [value] - (let [val (if new-css-system - value - (-> (dom/get-current-target value) - (dom/get-data "value"))) - decoration (if (= val text-decoration) + (let [decoration (if (= value text-decoration) "none" - val)] + value)] (on-change {:text-decoration decoration}) (when (some? on-blur) (on-blur)))))] - (if new-css-system - [:div {:class (stl/css :text-decoration-options)} - [:& radio-buttons {:selected text-decoration - :on-change handle-change - :name "text-decoration-options"} - [:& radio-button {:value "underline" - :type "checkbox" - :id "underline-text-decoration" - :title (tr "workspace.options.text-options.underline" (sc/get-tooltip :underline)) - :icon i/text-underlined-refactor}] - [:& radio-button {:value "line-through" - :type "checkbox" - :id "line-through-text-decoration" - :title (tr "workspace.options.text-options.strikethrough" (sc/get-tooltip :line-through)) - :icon i/text-stroked-refactor}]]] - - [:div.align-icons - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.none") - :class (dom/classnames :current (= "none" text-decoration)) - :data-value "none" - :on-click handle-change} - i/minus] - - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.underline" (sc/get-tooltip :underline)) - :class (dom/classnames :current (= "underline" text-decoration)) - :data-value "underline" - :on-click handle-change} - i/underline] - - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.strikethrough" (sc/get-tooltip :line-through)) - :class (dom/classnames :current (= "line-through" text-decoration)) - :data-value "line-through" - :on-click handle-change} - i/strikethrough]]))) + [:div {:class (stl/css :text-decoration-options)} + [:& radio-buttons {:selected text-decoration + :on-change handle-change + :name "text-decoration-options"} + [:& radio-button {:value "underline" + :type "checkbox" + :id "underline-text-decoration" + :title (tr "workspace.options.text-options.underline" (sc/get-tooltip :underline)) + :icon i/text-underlined-refactor}] + [:& radio-button {:value "line-through" + :type "checkbox" + :id "line-through-text-decoration" + :title (tr "workspace.options.text-options.strikethrough" (sc/get-tooltip :line-through)) + :icon i/text-stroked-refactor}]]])) (mf/defc text-menu {::mf/wrap [mf/memo]} [{:keys [ids type values] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - file-id (mf/use-ctx ctx/current-file-id) + (let [file-id (mf/use-ctx ctx/current-file-id) typographies (mf/deref refs/workspace-file-typography) shared-libs (mf/deref refs/workspace-libraries) label (case type @@ -411,87 +281,48 @@ (let [node (dom/get-element-by-class "public-DraftEditor-content")] (dom/focus! node))))))}] - (if new-css-system - [:div {:class (stl/css :element-set)} - [:div {:class (stl/css :element-title)} - [:& title-bar {:collapsable? true - :collapsed? (not main-menu-open?) - :on-collapsed toggle-main-menu - :title label - :class (stl/css :title-spacing-text)} - (when (and (not typography) (not multiple?)) - [:button {:class (stl/css :add-typography) - :on-click on-convert-to-typography} - i/add-refactor])]] + [:div {:class (stl/css :element-set)} + [:div {:class (stl/css :element-title)} + [:& title-bar {:collapsable? true + :collapsed? (not main-menu-open?) + :on-collapsed toggle-main-menu + :title label + :class (stl/css :title-spacing-text)} + (when (and (not typography) (not multiple?)) + [:button {:class (stl/css :add-typography) + :on-click on-convert-to-typography} + i/add-refactor])]] - (when main-menu-open? - [:div {:class (stl/css :element-content)} - (cond - typography - [:& typography-entry {:typography typography - :local? (= typography-file file-id) - :file (get shared-libs typography-file) - :on-detach handle-detach-typography - :on-change handle-change-typography}] + (when main-menu-open? + [:div {:class (stl/css :element-content)} + (cond + typography + [:& typography-entry {:typography typography + :local? (= typography-file file-id) + :file (get shared-libs typography-file) + :on-detach handle-detach-typography + :on-change handle-change-typography}] - (= typography-id :multiple) - [:div {:class (stl/css :multiple-typography)} - [:span {:class (stl/css :multiple-text)} (tr "workspace.libraries.text.multiple-typography")] - [:div {:class (stl/css :multiple-typography-button) - :on-click handle-detach-typography - :title (tr "workspace.libraries.text.multiple-typography-tooltip")} - i/detach-refactor]] + (= typography-id :multiple) + [:div {:class (stl/css :multiple-typography)} + [:span {:class (stl/css :multiple-text)} (tr "workspace.libraries.text.multiple-typography")] + [:div {:class (stl/css :multiple-typography-button) + :on-click handle-detach-typography + :title (tr "workspace.libraries.text.multiple-typography-tooltip")} + i/detach-refactor]] - :else - [:> text-options opts]) + :else + [:> text-options opts]) - [:div {:class (stl/css :text-align-options)} - [:> text-align-options opts] - [:> grow-options opts] - [:button {:class (stl/css :more-options) - :on-click toggle-more-options} - i/menu-refactor]] - - (when more-options-open? - [:div {:class (stl/css :text-decoration-options)} - [:> vertical-align opts] - [:> text-decoration-options opts] - [:> text-direction-options opts]])])] - - - [:div.element-set - [:div.element-set-title - [:span label] - (when (and (not typography) (not multiple?)) - [:div.add-page {:on-click on-convert-to-typography} i/close])] - - (cond - typography - [:& typography-entry {:typography typography - :local? (= typography-file file-id) - :file (get shared-libs typography-file) - :on-detach handle-detach-typography - :on-change handle-change-typography}] - - (= typography-id :multiple) - [:div.multiple-typography - [:div.multiple-typography-text (tr "workspace.libraries.text.multiple-typography")] - [:div.multiple-typography-button {:on-click handle-detach-typography - :title (tr "workspace.libraries.text.multiple-typography-tooltip")} i/unchain]] - - :else - [:> text-options opts]) - - [:div.element-set-content - - [:div.row-flex + [:div {:class (stl/css :text-align-options)} [:> text-align-options opts] - [:> vertical-align opts]] - - [:div.row-flex - [:> text-decoration-options opts] - [:> text-direction-options opts]] - - [:div.row-flex [:> grow-options opts] - [:div.align-icons]]]]))) + [:button {:class (stl/css :more-options) + :on-click toggle-more-options} + i/menu-refactor]] + + (when more-options-open? + [:div {:class (stl/css :text-decoration-options)} + [:> vertical-align opts] + [:> text-decoration-options opts] + [:> text-direction-options opts]])])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.scss index 2504fa5e78..2e0c982989 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.scss @@ -8,58 +8,68 @@ .element-set { margin: 0; - .element-title { - margin: 0; - .add-typography { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - } - } - .element-content { - @include flexColumn; - margin-top: $s-4; - .multiple-typography { - @extend .mixed-bar; - .multiple-text { - @include titleTipography; - flex-grow: 1; - color: var(--input-foreground-color-active); - } - .multiple-typography-button { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - } - } - .text-align-options { - display: flex; - gap: $s-4; - .align-options, - .text-direction-options, - .vertical-align-options, - .grow-options, - .text-decoration-options { - height: $s-32; - } - .more-options { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - } - } - .text-decoration-options { - display: flex; - gap: $s-4; - } +} + +.element-title { + margin: 0; +} + +.add-typography { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; } } + +.element-content { + @include flexColumn; + margin-top: $s-4; +} + +.multiple-typography { + @extend .mixed-bar; +} + +.multiple-text { + @include titleTipography; + flex-grow: 1; + color: var(--input-foreground-color-active); +} + +.multiple-typography-button { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } +} + +.text-align-options { + display: flex; + gap: $s-4; +} + +.align-options, +.text-direction-options, +.vertical-align-options, +.grow-options, +.text-decoration-options { + height: $s-32; +} + +.more-options { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } +} + +.text-decoration-options { + display: flex; + gap: $s-4; +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs index 122c32ea8a..c54d3a13d5 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs @@ -25,7 +25,6 @@ [app.main.ui.components.select :refer [select]] [app.main.ui.context :as ctx] [app.main.ui.icons :as i] - [app.main.ui.workspace.sidebar.options.common :refer [advanced-options]] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] @@ -61,8 +60,7 @@ {::mf/wrap [mf/memo]} [{:keys [font current? on-click style]}] (let [item-ref (mf/use-ref) - on-click (mf/use-fn (mf/deps font) #(on-click font)) - new-css-system (mf/use-ctx ctx/new-css-system)] + on-click (mf/use-fn (mf/deps font) #(on-click font))] (mf/use-effect (mf/deps current?) @@ -72,22 +70,14 @@ (when-not (dom/is-in-viewport? element) (dom/scroll-into-view! element)))))) - (if new-css-system - [:div {:class (stl/css :font-wrapper) - :style style - :ref item-ref - :on-click on-click} - [:div {:class (stl/css-case :font-item true - :selected current?)} - [:span {:class (stl/css :label)} (:name font)] - [:span {:class (stl/css :icon)} (when current? i/tick-refactor)]]] - - [:div.font-item {:ref item-ref - :style style - :class (when current? "selected") - :on-click on-click} - [:span.icon (when current? i/tick)] - [:span.label (:name font)]]))) + [:div {:class (stl/css :font-wrapper) + :style style + :ref item-ref + :on-click on-click} + [:div {:class (stl/css-case :font-item true + :selected current?)} + [:span {:class (stl/css :label)} (:name font)] + [:span {:class (stl/css :icon)} (when current? i/tick-refactor)]]])) (declare row-renderer) @@ -104,8 +94,7 @@ (mf/defc font-selector [{:keys [on-select on-close current-font show-recent] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - selected (mf/use-state current-font) + (let [selected (mf/use-state current-font) state (mf/use-state {:term "" :backends #{}}) flist (mf/use-ref) @@ -143,12 +132,8 @@ on-filter-change (mf/use-fn - (mf/deps new-css-system) (fn [event] - (let [value (if new-css-system - event - (dom/get-target-val event))] - (swap! state assoc :term value)))) + (swap! state assoc :term event))) on-select-and-close (mf/use-fn @@ -184,72 +169,35 @@ #(let [offset (.getOffsetForRow ^js inst #js {:alignment "center" :index index})] (.scrollToPosition ^js inst offset))))) + [:div {:class (stl/css :font-selector)} + [:div {:class (stl/css :font-selector-dropdown)} + [:div {:class (stl/css :header)} + [:& search-bar {:on-change on-filter-change + :value (:term @state) + :placeholder (tr "workspace.options.search-font")}] + (when (and recent-fonts show-recent) + [* + [:p {:class (stl/css :title)} (tr "workspace.options.recent-fonts")] + (for [[idx font] (d/enumerate recent-fonts)] + [:& font-item {:key (dm/str "font-" idx) + :font font + :style {} + :on-click on-select-and-close + :current? (= (:id font) (:id @selected))}])])] - (if new-css-system - [:div {:class (stl/css :font-selector)} - [:div {:class (stl/css :font-selector-dropdown)} - [:div {:class (stl/css :header)} - [:& search-bar {:on-change on-filter-change - :value (:term @state) - :placeholder (tr "workspace.options.search-font")}] - (when (and recent-fonts show-recent) - [* - [:p {:class (stl/css :title)} (tr "workspace.options.recent-fonts")] - (for [[idx font] (d/enumerate recent-fonts)] - [:& font-item {:key (dm/str "font-" idx) - :font font - :style {} - :on-click on-select-and-close - :current? (= (:id font) (:id @selected))}])])] - - [:div {:class (stl/css :fonts-list)} - [:> rvt/AutoSizer {} - (fn [props] - (let [width (unchecked-get props "width") - height (unchecked-get props "height") - render #(row-renderer fonts @selected on-select-and-close %)] - (mf/html - [:> rvt/List #js {:height height - :ref flist - :width width - :rowCount (count fonts) - :rowHeight 36 - :rowRenderer render}])))]]]] - - [:div.font-selector - [:div.font-selector-dropdown - [:header - [:input {:placeholder (tr "workspace.options.search-font") - :value (:term @state) - :ref input - :spell-check false - :on-change on-filter-change}] - (when (and recent-fonts show-recent) - [:* - [:hr] - [:p.title (tr "workspace.options.recent-fonts")] - (for [[idx font] (d/enumerate recent-fonts)] - [:& font-item {:key (dm/str "font-" idx) - :font font - :style {} - :on-click on-select-and-close - :current? (= (:id font) (:id @selected))}])])] - - [:hr] - - [:div.fonts-list - [:> rvt/AutoSizer {} - (fn [props] - (let [width (unchecked-get props "width") - height (unchecked-get props "height") - render #(row-renderer fonts @selected on-select-and-close %)] - (mf/html - [:> rvt/List #js {:height height - :ref flist - :width width - :rowCount (count fonts) - :rowHeight 32 - :rowRenderer render}])))]]]]))) + [:div {:class (stl/css :fonts-list)} + [:> rvt/AutoSizer {} + (fn [props] + (let [width (unchecked-get props "width") + height (unchecked-get props "height") + render #(row-renderer fonts @selected on-select-and-close %)] + (mf/html + [:> rvt/List #js {:height height + :ref flist + :width width + :rowCount (count fonts) + :rowHeight 36 + :rowRenderer render}])))]]]])) (defn row-renderer [fonts selected on-select props] @@ -272,7 +220,6 @@ font-id (or font-id (:font-id txt/default-text-attrs)) font-size (or font-size (:font-size txt/default-text-attrs)) font-variant-id (or font-variant-id (:font-variant-id txt/default-text-attrs)) - new-css-system (mf/use-ctx ctx/new-css-system) fonts (mf/deref fonts/fontsdb) font (get fonts font-id) @@ -304,17 +251,14 @@ on-font-variant-change (mf/use-fn (mf/deps font on-change) - (fn [event] - (let [new-variant-id (if new-css-system - event - (dom/get-target-val event)) - variant (d/seek #(= new-variant-id (:id %)) (:variants font))] + (fn [new-variant-id] + (let [variant (d/seek #(= new-variant-id (:id %)) (:variants font))] (on-change {:font-id (:id font) :font-family (:family font) :font-variant-id new-variant-id :font-weight (:weight variant) :font-style (:style variant)}) - (dom/blur! (dom/get-target event))))) + (dom/blur! (dom/get-target new-variant-id))))) on-font-select (mf/use-fn @@ -335,114 +279,65 @@ (when (mf/ref-val last-font) (st/emit! (fts/add-recent-font (mf/ref-val last-font))))))] - (if new-css-system - [:* - (when @open-selector? - [:& font-selector - {:current-font font - :on-close on-font-selector-close - :on-select on-font-select - :show-recent show-recent}]) + [:* + (when @open-selector? + [:& font-selector + {:current-font font + :on-close on-font-selector-close + :on-select on-font-select + :show-recent show-recent}]) - [:div {:class (stl/css :font-option) - :on-click #(reset! open-selector? true)} - (cond - (= :multiple font-id) - "--" + [:div {:class (stl/css :font-option) + :on-click #(reset! open-selector? true)} + (cond + (= :multiple font-id) + "--" - (some? font) - [:* - [:span {:class (stl/css :name)} - (:name font)] - [:span {:class (stl/css :icon)} - i/arrow-refactor]] + (some? font) + [:* + [:span {:class (stl/css :name)} + (:name font)] + [:span {:class (stl/css :icon)} + i/arrow-refactor]] - :else - (tr "dashboard.fonts.deleted-placeholder"))] + :else + (tr "dashboard.fonts.deleted-placeholder"))] - [:div {:class (stl/css :font-modifiers)} - [:div {:class (stl/css :font-size-options)} - (let [size-options [8 9 10 11 12 14 16 18 24 36 48 72] - size-options (if (= font-size :multiple) (into [""] size-options) size-options)] - [:& editable-select - {:value (attr->string font-size) - :class (stl/css :font-size-select) - :input-class (stl/css :numeric-input) - :options size-options - :type "number" - :placeholder "--" - :min 3 - :max 1000 - :on-change on-font-size-change - :on-blur on-blur}])] + [:div {:class (stl/css :font-modifiers)} + [:div {:class (stl/css :font-size-options)} + (let [size-options [8 9 10 11 12 14 16 18 24 36 48 72] + size-options (if (= font-size :multiple) (into [""] size-options) size-options)] + [:& editable-select + {:value (attr->string font-size) + :class (stl/css :font-size-select) + :input-class (stl/css :numeric-input) + :options size-options + :type "number" + :placeholder "--" + :min 3 + :max 1000 + :on-change on-font-size-change + :on-blur on-blur}])] - [:div {:class (stl/css :font-variant-options)} - (let [basic-variant-options (->> (:variants font) - (map (fn [variant] - {:value (:id variant) - :key (pr-str variant) - :label (:name variant)}) )) - variant-options (if (= font-size :multiple) - (conj basic-variant-options - {:value :multiple - :key :multiple-variants - :label "--"} ) - basic-variant-options)] - ;; TODO Add disabled mode - [:& select - {:class (stl/css :font-variant-select) - :default-value (attr->string font-variant-id) - :options variant-options - :on-change on-font-variant-change - :on-blur on-blur}])]]] - - [:* - (when @open-selector? - [:& font-selector - {:current-font font - :on-close on-font-selector-close - :on-select on-font-select - :show-recent show-recent}]) - - [:div.row-flex - [:div.input-select.font-option - {:on-click #(reset! open-selector? true)} - (cond - (= :multiple font-id) - "--" - - (some? font) - (:name font) - - :else - (tr "dashboard.fonts.deleted-placeholder"))]] - - [:div.row-flex - (let [size-options [8 9 10 11 12 14 16 18 24 36 48 72] - size-options (if (= font-size :multiple) (into [""] size-options) size-options)] - [:& editable-select - {:value (attr->string font-size) - :class "input-option size-option" - :options size-options - :type "number" - :placeholder "--" - :min 3 - :max 1000 - :on-change on-font-size-change - :on-blur on-blur}]) - - [:select.input-select.variant-option - {:disabled (= font-id :multiple) - :data-mousetrap-dont-stop true - :value (attr->string font-variant-id) - :on-change on-font-variant-change - :on-blur on-blur} - (when (or (= font-id :multiple) (= font-variant-id :multiple)) - [:option {:value ""} "--"]) - (for [variant (:variants font)] - [:option {:value (:id variant) - :key (pr-str variant)} - (:name variant)])]]]))) + [:div {:class (stl/css :font-variant-options)} + (let [basic-variant-options (->> (:variants font) + (map (fn [variant] + {:value (:id variant) + :key (pr-str variant) + :label (:name variant)}))) + variant-options (if (= font-size :multiple) + (conj basic-variant-options + {:value :multiple + :key :multiple-variants + :label "--"}) + basic-variant-options)] + ;; TODO Add disabled mode + [:& select + {:class (stl/css :font-variant-select) + :default-value (attr->string font-variant-id) + :options variant-options + :on-change on-font-variant-change + :on-blur on-blur}])]]])) (mf/defc spacing-options {::mf/wrap-props false} @@ -452,152 +347,87 @@ line-height (or line-height "1.2") letter-spacing (or letter-spacing "0") - new-css-system (mf/use-ctx ctx/new-css-system) line-height-nillable (if (= (str line-height) "1.2") false true) handle-change (fn [value attr] (on-change {attr (str value)}))] - (if new-css-system - [:div {:class (stl/css :spacing-options)} - [:div {:class (stl/css :line-height)} - [:span {:class (stl/css :icon) - :alt (tr "workspace.options.text-options.line-height")} - i/text-lineheight-refactor] - [:> numeric-input* - {:min -200 - :max 200 - :step 0.1 - :default "1.2" - :class (stl/css :line-height-input) - :value (attr->string line-height) - :placeholder (tr "settings.multiple") - :nillable line-height-nillable - :on-change #(handle-change % :line-height) - :on-blur on-blur}]] - [:div {:class (stl/css :letter-spacing)} - [:span - {:class (stl/css :icon) - :alt (tr "workspace.options.text-options.letter-spacing")} - i/text-letterspacing-refactor] - [:> numeric-input* - {:min -200 - :max 200 - :step 0.1 - :class (stl/css :letter-spacing-input) - :value (attr->string letter-spacing) - :placeholder (tr "settings.multiple") - :on-change #(handle-change % :letter-spacing) - :on-blur on-blur}]]] + [:div {:class (stl/css :spacing-options)} + [:div {:class (stl/css :line-height)} + [:span {:class (stl/css :icon) + :alt (tr "workspace.options.text-options.line-height")} + i/text-lineheight-refactor] + [:> numeric-input* + {:min -200 + :max 200 + :step 0.1 + :default "1.2" + :class (stl/css :line-height-input) + :value (attr->string line-height) + :placeholder (tr "settings.multiple") + :nillable line-height-nillable + :on-change #(handle-change % :line-height) + :on-blur on-blur}]] - - [:div.spacing-options - [:div.input-icon - [:span.icon-before.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.line-height")} - i/line-height] - [:> numeric-input* - {:min -200 - :max 200 - :step 0.1 - :default "1.2" - :value (attr->string line-height) - :placeholder (tr "settings.multiple") - :nillable line-height-nillable - :on-change #(handle-change % :line-height) - :on-blur on-blur}]] - - [:div.input-icon - [:span.icon-before.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.letter-spacing")} - i/letter-spacing] - [:> numeric-input* - {:min -200 - :max 200 - :step 0.1 - :value (attr->string letter-spacing) - :placeholder (tr "settings.multiple") - :on-change #(handle-change % :letter-spacing) - :on-blur on-blur}]]]))) + [:div {:class (stl/css :letter-spacing)} + [:span + {:class (stl/css :icon) + :alt (tr "workspace.options.text-options.letter-spacing")} + i/text-letterspacing-refactor] + [:> numeric-input* + {:min -200 + :max 200 + :step 0.1 + :class (stl/css :letter-spacing-input) + :value (attr->string letter-spacing) + :placeholder (tr "settings.multiple") + :on-change #(handle-change % :letter-spacing) + :on-blur on-blur}]]])) (mf/defc text-transform-options {::mf/wrap-props false} [{:keys [values on-change on-blur]}] (let [text-transform (or (:text-transform values) "none") - new-css-system (mf/use-ctx ctx/new-css-system) handle-change (fn [type] (if (= text-transform type) (on-change {:text-transform "unset"}) (on-change {:text-transform type})) (when (some? on-blur) (on-blur)))] - (if new-css-system - [:div {:class (stl/css :text-transform)} - [:& radio-buttons {:selected text-transform - :on-change handle-change - :name "text-transform"} - [:& radio-button {:icon i/text-uppercase-refactor - :type "checkbox" - :value "uppercase" - :id "text-transform-uppercase"}] - [:& radio-button {:icon i/text-lowercase-refactor - :type "checkbox" - :value "lowercase" - :id "text-transform-lowercase"}] - [:& radio-button {:icon i/text-mixed-refactor - :type "checkbox" - :value "capitalize" - :id "text-transform-capitalize"}]]] - [:div.align-icons - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.none") - :class (dom/classnames :current (= "none" text-transform)) - :on-focus #(dom/prevent-default %) - :on-click #(handle-change "none")} - i/minus] - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.uppercase") - :class (dom/classnames :current (= "uppercase" text-transform)) - :on-click #(handle-change "uppercase")} - i/uppercase] - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.lowercase") - :class (dom/classnames :current (= "lowercase" text-transform)) - :on-click #(handle-change "lowercase")} - i/lowercase] - [:span.tooltip.tooltip-bottom - {:alt (tr "workspace.options.text-options.titlecase") - :class (dom/classnames :current (= "capitalize" text-transform)) - :on-click #(handle-change "capitalize")} - i/titlecase]]))) + [:div {:class (stl/css :text-transform)} + [:& radio-buttons {:selected text-transform + :on-change handle-change + :name "text-transform"} + [:& radio-button {:icon i/text-uppercase-refactor + :type "checkbox" + :value "uppercase" + :id "text-transform-uppercase"}] + [:& radio-button {:icon i/text-lowercase-refactor + :type "checkbox" + :value "lowercase" + :id "text-transform-lowercase"}] + [:& radio-button {:icon i/text-mixed-refactor + :type "checkbox" + :value "capitalize" + :id "text-transform-capitalize"}]]])) (mf/defc text-options {::mf/wrap-props false} [{:keys [ids editor values on-change on-blur show-recent]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - opts #js {:editor editor + (let [opts #js {:editor editor :ids ids :values values :on-change on-change :on-blur on-blur :show-recent show-recent}] - (if new-css-system - [:div {:class (stl/css :text-options)} - [:> font-options opts] - [:div {:class (stl/css :typography-variations)} - [:> spacing-options opts] - [:> text-transform-options opts]]] - - [:div.element-set-content - [:> font-options opts] - [:div.row-flex - [:> spacing-options opts]] - [:div.row-flex - [:> text-transform-options opts]]]))) - + [:div {:class (stl/css :text-options)} + [:> font-options opts] + [:div {:class (stl/css :typography-variations)} + [:> spacing-options opts] + [:> text-transform-options opts]]])) (mf/defc typography-advanced-options {::mf/wrap [mf/memo]} @@ -684,16 +514,11 @@ :on-click navigate-to-library} (tr "workspace.assets.typography.go-to-edit")])])]))) - (mf/defc typography-entry {::mf/wrap-props false} [{:keys [file-id typography local? selected? on-click on-change on-detach on-context-menu editing? renaming? focus-name? external-open*]}] - (let [hover-detach* (mf/use-state false) - hover-detach? (deref hover-detach*) - - name-input-ref (mf/use-ref) + (let [name-input-ref (mf/use-ref) read-only? (mf/use-ctx ctx/workspace-read-only?) - new-css-system (mf/use-ctx ctx/new-css-system) editable? (and local? (not read-only?)) open* (mf/use-state editing?) @@ -710,12 +535,6 @@ (on-change {:name name}) (st/emit! #(update % :workspace-global dissoc :rename-typography)))))) - on-pointer-enter - (mf/use-fn #(reset! hover-detach* true)) - - on-pointer-leave - (mf/use-fn #(reset! hover-detach* false)) - on-open (mf/use-fn #(reset! open* true)) @@ -755,147 +574,62 @@ (dom/focus! node) (dom/select-text! node))))) - (if new-css-system - [:* - [:div {:class (stl/css-case :typography-entry true - :selected ^boolean selected?) - :style {:display (when ^boolean open? "none")}} - (if renaming? - [:div {:class (stl/css :font-name-wrapper)} - [:div - {:class (stl/css :typography-sample-input) - :style {:font-family (:font-family typography) - :font-weight (:font-weight typography) - :font-style (:font-style typography)}} - (tr "workspace.assets.typography.sample")] - - [:input - {:class (stl/css :adv-typography-name) - :type "text" - :ref name-input-ref - :default-value (:name typography) - :on-key-down on-key-down - :on-blur on-name-blur}]] - [:div - {:class (stl/css-case :typography-selection-wrapper true - :is-selectable ^boolean on-click) - :on-click on-click - :on-context-menu on-context-menu} - [:div - {:class (stl/css :typography-sample) - :style {:font-family (:font-family typography) - :font-weight (:font-weight typography) - :font-style (:font-style typography)}} - (tr "workspace.assets.typography.sample")] - - [:div {:class (stl/css :typography-name) - :title (:name typography)} (:name typography)] - - (when-not name-only? - [:div {:class (stl/css :typography-font) - :title (:name font-data)} - (:name font-data)])]) - [:div {:class (stl/css :element-set-actions)} - (when ^boolean on-detach - [:button {:class (stl/css :element-set-actions-button) - :on-click on-detach} - i/detach-refactor]) - [:button {:class (stl/css :menu-btn) - :on-click on-open} - i/menu-refactor]]] - - [:& typography-advanced-options - {:visible? open? - :on-close on-close - :typography typography - :editable? editable? - :name-input-ref name-input-ref - :on-change on-change - :on-name-blur on-name-blur - :on-key-down on-key-down - :local? local? - :navigate-to-library navigate-to-library}]] - - - [:* - [:div.element-set-options-group.typography-entry - {:class (when ^boolean selected? "selected") - :style {:display (when ^boolean open? "none")}} - [:div.typography-selection-wrapper - {:class (when ^boolean on-click "is-selectable") - :on-click on-click - :on-context-menu on-context-menu} - [:div.typography-sample - {:style {:font-family (:font-family typography) + [:* + [:div {:class (stl/css-case :typography-entry true + :selected ^boolean selected?) + :style {:display (when ^boolean open? "none")}} + (if renaming? + [:div {:class (stl/css :font-name-wrapper)} + [:div + {:class (stl/css :typography-sample-input) + :style {:font-family (:font-family typography) :font-weight (:font-weight typography) :font-style (:font-style typography)}} (tr "workspace.assets.typography.sample")] - [:div.typography-name {:title (:name typography)} (:name typography)]] - [:div.element-set-actions - (when ^boolean on-detach - [:div.element-set-actions-button - {:on-pointer-enter on-pointer-enter - :on-pointer-leave on-pointer-leave - :on-click on-detach} - (if ^boolean hover-detach? i/unchain i/chain)]) - [:div.element-set-actions-button - {:on-click on-open} - i/actions]]] + [:input + {:class (stl/css :adv-typography-name) + :type "text" + :ref name-input-ref + :default-value (:name typography) + :on-key-down on-key-down + :on-blur on-name-blur}]] + [:div + {:class (stl/css-case :typography-selection-wrapper true + :is-selectable ^boolean on-click) + :on-click on-click + :on-context-menu on-context-menu} + [:div + {:class (stl/css :typography-sample) + :style {:font-family (:font-family typography) + :font-weight (:font-weight typography) + :font-style (:font-style typography)}} + (tr "workspace.assets.typography.sample")] - [:& advanced-options {:visible? open? :on-close on-close} - (if ^boolean editable? - [:* - [:div.element-set-content - [:div.row-flex - [:input.element-name.adv-typography-name - {:type "text" - :ref name-input-ref - :default-value (:name typography) - :on-blur on-name-blur}] + [:div {:class (stl/css :typography-name) + :title (:name typography)} (:name typography)] - [:div.element-set-actions-button - {:on-click on-close} - i/actions]]] + (when-not name-only? + [:div {:class (stl/css :typography-font) + :title (:name font-data)} + (:name font-data)])]) + [:div {:class (stl/css :element-set-actions)} + (when ^boolean on-detach + [:button {:class (stl/css :element-set-actions-button) + :on-click on-detach} + i/detach-refactor]) + [:button {:class (stl/css :menu-btn) + :on-click on-open} + i/menu-refactor]]] - [:& text-options {:values typography - :on-change on-change - :show-recent false}]] - - [:div.element-set-content.typography-read-only-data - [:div.row-flex.typography-name - [:span {:title (:name typography)} (:name typography)]] - - [:div.row-flex - [:span.label (tr "workspace.assets.typography.font-id")] - [:span (:font-id typography)]] - - [:div.element-set-actions-button.actions-inside - {:on-click on-close} - i/actions] - - [:div.row-flex - [:span.label (tr "workspace.assets.typography.font-variant-id")] - [:span (:font-variant-id typography)]] - - [:div.row-flex - [:span.label (tr "workspace.assets.typography.font-size")] - [:span (:font-size typography)]] - - [:div.row-flex - [:span.label (tr "workspace.assets.typography.line-height")] - [:span (:line-height typography)]] - - [:div.row-flex - [:span.label (tr "workspace.assets.typography.letter-spacing")] - [:span (:letter-spacing typography)]] - - [:div.row-flex - [:span.label (tr "workspace.assets.typography.text-transform")] - [:span (:text-transform typography)]] - - (when-not local? - [:div.row-flex - [:a.go-to-lib-button - {:on-click navigate-to-library} - (tr "workspace.assets.typography.go-to-edit")]])])]]))) + [:& typography-advanced-options + {:visible? open? + :on-close on-close + :typography typography + :editable? editable? + :name-input-ref name-input-ref + :on-change on-change + :on-name-blur on-name-blur + :on-key-down on-key-down + :local? local? + :navigate-to-library navigate-to-library}]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss index 305fc6e83e..6f7aab290d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss @@ -142,103 +142,104 @@ height: 100%; width: 100%; background-color: var(--assets-title-background-color); - .typography-info-wrapper { - @include flexColumn; - margin-bottom: $s-12; - .typography-name-wrapper { - @extend .asset-element; - display: grid; - grid-template-columns: $s-24 auto 1fr $s-28; - flex: 1; +} + +.typography-info-wrapper { + @include flexColumn; + margin-bottom: $s-12; + .typography-name-wrapper { + @extend .asset-element; + display: grid; + grid-template-columns: $s-24 auto 1fr $s-28; + flex: 1; + height: $s-32; + width: 100%; + padding: 0 0 0 $s-12; + background-color: var(--assets-item-background-color-hover); + margin-bottom: $s-4; + .typography-sample { + @include flexCenter; + min-width: $s-24; + font-size: $fs-16; height: $s-32; - width: 100%; - padding: 0 0 0 $s-12; - background-color: var(--assets-item-background-color-hover); - margin-bottom: $s-4; - .typography-sample { - @include flexCenter; - min-width: $s-24; - font-size: $fs-16; - height: $s-32; - padding: 0; - color: var(--assets-item-name-foreground-color-hover); + padding: 0; + color: var(--assets-item-name-foreground-color-hover); + } + .typography-name { + @include titleTipography; + @include textEllipsis; + display: flex; + align-items: center; + justify-content: flex-start; + margin-left: $s-6; + color: var(--assets-item-name-foreground-color-hover); + } + .typography-font { + @include titleTipography; + @include textEllipsis; + margin-left: $s-6; + display: flex; + align-items: center; + justify-content: flex-start; + min-width: 0; + color: var(--assets-item-name-foreground-color); + } + .action-btn { + @extend .button-tertiary; + width: $s-28; + height: $s-32; + svg { + @extend .button-icon; } - .typography-name { - @include titleTipography; - @include textEllipsis; - display: flex; - align-items: center; - justify-content: flex-start; - margin-left: $s-6; - color: var(--assets-item-name-foreground-color-hover); - } - .typography-font { - @include titleTipography; - @include textEllipsis; - margin-left: $s-6; - display: flex; - align-items: center; - justify-content: flex-start; - min-width: 0; - color: var(--assets-item-name-foreground-color); - } - .action-btn { - @extend .button-tertiary; - width: $s-28; - height: $s-32; - svg { - @extend .button-icon; - } - &:active { - background-color: transparent; - } + &:active { + background-color: transparent; } } + } - .info-row { - display: grid; - grid-template-columns: 50% 50%; - height: $s-32; - --calcualted-width: calc(var(--width) - $s-48); - padding-left: $s-2; - .info-label { - @include titleTipography; - @include textEllipsis; - width: calc(var(--calcualted-width) / 2); - padding-top: $s-8; - color: var(--assets-item-name-foreground-color); - } - .info-content { - @include titleTipography; - @include textEllipsis; - padding-top: $s-8; - width: calc(var(--calcualted-width) / 2); - color: var(--assets-item-name-foreground-color-hover); + .info-row { + display: grid; + grid-template-columns: 50% 50%; + height: $s-32; + --calcualted-width: calc(var(--width) - $s-48); + padding-left: $s-2; + .info-label { + @include titleTipography; + @include textEllipsis; + width: calc(var(--calcualted-width) / 2); + padding-top: $s-8; + color: var(--assets-item-name-foreground-color); + } + .info-content { + @include titleTipography; + @include textEllipsis; + padding-top: $s-8; + width: calc(var(--calcualted-width) / 2); + color: var(--assets-item-name-foreground-color-hover); + } + } + + .link-btn { + @include tabTitleTipography; + @extend .button-secondary; + width: 100%; + height: $s-32; + border-radius: $br-8; + &:hover { + background-color: var(--button-secondary-background-color-hover); + color: var(--button-secondary-foreground-color-hover); + border: $s-1 solid var(--button-secondary-border-color-hover); + text-decoration: none; + svg { + stroke: var(--button-secondary-foreground-color-hover); } } - - .link-btn { - @include tabTitleTipography; - @extend .button-secondary; - width: 100%; - height: $s-32; - border-radius: $br-8; - &:hover { - background-color: var(--button-secondary-background-color-hover); - color: var(--button-secondary-foreground-color-hover); - border: $s-1 solid var(--button-secondary-border-color-hover); - text-decoration: none; - svg { - stroke: var(--button-secondary-foreground-color-hover); - } - } - &:focus { - background-color: var(--button-secondary-background-color-focus); - color: var(--button-secondary-foreground-color-focus); - border: $s-1 solid var(--button-secondary-border-color-focus); - svg { - stroke: var(--button-secondary-foreground-color-focus); - } + &:focus { + background-color: var(--button-secondary-background-color-focus); + color: var(--button-secondary-foreground-color-focus); + border: $s-1 solid var(--button-secondary-border-color-focus); + svg { + stroke: var(--button-secondary-foreground-color-focus); } } } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/page.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/page.cljs index dbdd285075..9a99b7dbfd 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/page.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/page.cljs @@ -14,7 +14,6 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.title-bar :refer [title-bar]] - [app.main.ui.context :as ctx] [app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]] [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) @@ -23,40 +22,24 @@ {::mf/wrap [mf/memo] ::mf/wrap-props false} [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - options (mf/deref refs/workspace-page-options) + (let [options (mf/deref refs/workspace-page-options) on-change (mf/use-fn #(st/emit! (dw/change-canvas-color %))) on-open (mf/use-fn #(st/emit! (dwu/start-undo-transaction :options))) on-close (mf/use-fn #(st/emit! (dwu/commit-undo-transaction :options)))] - (if new-css-system - [:div {:class (stl/css :element-set)} - [:div {:class (stl/css :element-title)} - [:& title-bar {:collapsable? false - :title (tr "workspace.options.canvas-background") - :class (stl/css :title-spacing-page)}]] - [:div {:class (stl/css :element-content)} - [:& color-row - {:disable-gradient true - :disable-opacity true - :disable-image true - :title (tr "workspace.options.canvas-background") - :color {:color (get options :background clr/canvas) - :opacity 1} - :on-change on-change - :on-open on-open - :on-close on-close}]]] - - [:div.element-set - [:div.element-set-title (tr "workspace.options.canvas-background")] - [:div.element-set-content - [:& color-row - {:disable-gradient true - :disable-opacity true - :disable-image true - :title (tr "workspace.options.canvas-background") - :color {:color (get options :background clr/canvas) - :opacity 1} - :on-change on-change - :on-open on-open - :on-close on-close}]]]))) + [:div {:class (stl/css :element-set)} + [:div {:class (stl/css :element-title)} + [:& title-bar {:collapsable? false + :title (tr "workspace.options.canvas-background") + :class (stl/css :title-spacing-page)}]] + [:div {:class (stl/css :element-content)} + [:& color-row + {:disable-gradient true + :disable-opacity true + :disable-image true + :title (tr "workspace.options.canvas-background") + :color {:color (get options :background clr/canvas) + :opacity 1} + :on-change on-change + :on-open on-open + :on-close on-close}]]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs index cad09c56ac..15be74946d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs @@ -15,7 +15,6 @@ [app.main.data.workspace.libraries :as dwl] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.components.color-bullet :as cb] [app.main.ui.components.color-bullet-new :as cbn] [app.main.ui.components.color-input :refer [color-input*]] [app.main.ui.components.numeric-input :refer [numeric-input*]] @@ -43,10 +42,9 @@ (mf/defc color-row [{:keys [index color disable-gradient disable-opacity disable-image on-change - on-reorder on-detach on-open on-close title on-remove + on-reorder on-detach on-open on-close on-remove disable-drag on-focus on-blur select-only select-on-focus]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - current-file-id (mf/use-ctx ctx/current-file-id) + (let [current-file-id (mf/use-ctx ctx/current-file-id) file-colors (mf/deref refs/workspace-file-colors) shared-libs (mf/deref refs/workspace-libraries) hover-detach (mf/use-state false) @@ -186,152 +184,87 @@ (when (not= prev-color color) (modal/update-props! :colorpicker {:data (parse-color color)}))) - (if new-css-system - [:div {:class (stl/css-case - :color-data true - :dnd-over-top (= (:over dprops) :top) - :dnd-over-bot (= (:over dprops) :bot)) - :ref dref} - [:span {:class (stl/css :color-info)} - [:span {:class (stl/css-case :color-name-wrapper true - :no-opacity (or disable-opacity - (not opacity?)) - :library-name-wrapper library-color? - :editing editing-text? - :gradient-name-wrapper gradient-color?)} - [:span {:class (stl/css :color-bullet-wrapper)} - [:& cbn/color-bullet {:color (cond-> color - (nil? color-name) (assoc - :id nil - :file-id nil)) - :mini? true - :on-click handle-click-color}]] - (cond - ;; Rendering a color with ID - library-color? - [:* - [:div {:class (stl/css :color-name) - :title (str color-name)} - - (str color-name)] - (when on-detach - [:button - {:class (stl/css :detach-btn) - :title (tr "settings.detach") - :on-pointer-enter #(reset! hover-detach true) - :on-pointer-leave #(reset! hover-detach false) - :on-click detach-value} - i/detach-refactor])] - - ;; Rendering a gradient - gradient-color? - [:* - [:div {:class (stl/css :color-name)} - (uc/gradient-type->string (get-in color [:gradient :type]))]] - - ;; Rendering an image - image-color? - [:* - [:div {:class (stl/css :color-name)} - (tr "media.image")]] - - ;; Rendering a plain color - :else - [:span {:class (stl/css :color-input-wrapper)} - [:> color-input* {:value (if multiple-colors? - "" - (-> color :color cc/remove-hash)) - :placeholder (tr "settings.multiple") - :className (stl/css :color-input) - :on-focus on-focus - :on-blur on-blur - :on-change handle-value-change}]])] - - (when opacity? - [:div {:class (stl/css :opacity-element-wrapper)} - [:span {:class (stl/css :icon-text)} - "%"] - [:> numeric-input* {:value (-> color :opacity opacity->string) - :className (stl/css :opacity-input) - :placeholder "--" - :select-on-focus select-on-focus - :on-focus on-focus - :on-blur on-blur - :on-change handle-opacity-change - :min 0 - :max 100}]])] - - (when (some? on-remove) - [:button {:class (stl/css :remove-btn) - :on-click on-remove} i/remove-refactor]) - (when select-only - [:button {:class (stl/css :select-btn) - :on-click handle-select} - i/move-refactor])] - - ;; OLD CSS - [:div.row-flex.color-data {:title title - :class (dom/classnames - :dnd-over-top (= (:over dprops) :top) - :dnd-over-bot (= (:over dprops) :bot)) - :ref dref} - [:& cb/color-bullet {:color (cond-> color - (nil? color-name) (assoc - :id nil - :file-id nil)) - :on-click handle-click-color}] - + [:div {:class (stl/css-case + :color-data true + :dnd-over-top (= (:over dprops) :top) + :dnd-over-bot (= (:over dprops) :bot)) + :ref dref} + [:span {:class (stl/css :color-info)} + [:span {:class (stl/css-case :color-name-wrapper true + :no-opacity (or disable-opacity + (not opacity?)) + :library-name-wrapper library-color? + :editing editing-text? + :gradient-name-wrapper gradient-color?)} + [:span {:class (stl/css :color-bullet-wrapper)} + [:& cbn/color-bullet {:color (cond-> color + (nil? color-name) (assoc + :id nil + :file-id nil)) + :mini? true + :on-click handle-click-color}]] (cond - ;; Rendering a color with ID + ;; Rendering a color with ID library-color? [:* - [:div.color-info - [:div.color-name (str color-name)]] + [:div {:class (stl/css :color-name) + :title (str color-name)} + + (str color-name)] (when on-detach - [:div.element-set-actions-button - {:on-pointer-enter #(reset! hover-detach true) + [:button + {:class (stl/css :detach-btn) + :title (tr "settings.detach") + :on-pointer-enter #(reset! hover-detach true) :on-pointer-leave #(reset! hover-detach false) :on-click detach-value} - (if @hover-detach i/unchain i/chain)])] + i/detach-refactor])] - ;; Rendering a gradient + ;; Rendering a gradient gradient-color? [:* - [:div.color-info - [:div.color-name (uc/gradient-type->string (get-in color [:gradient :type]))]] - (when select-only - [:div.element-set-actions-button {:on-click handle-select} - i/pointer-inner])] + [:div {:class (stl/css :color-name)} + (uc/gradient-type->string (get-in color [:gradient :type]))]] - ;; Rendering a plain color/opacity - :else + ;; Rendering an image + image-color? [:* - [:div.color-info - [:> color-input* {:value (if multiple-colors? - "" - (-> color :color cc/remove-hash)) - :placeholder (tr "settings.multiple") + [:div {:class (stl/css :color-name)} + (tr "media.image")]] + + ;; Rendering a plain color + :else + [:span {:class (stl/css :color-input-wrapper)} + [:> color-input* {:value (if multiple-colors? + "" + (-> color :color cc/remove-hash)) + :placeholder (tr "settings.multiple") + :className (stl/css :color-input) + :on-focus on-focus + :on-blur on-blur + :on-change handle-value-change}]])] + + (when opacity? + [:div {:class (stl/css :opacity-element-wrapper)} + [:span {:class (stl/css :icon-text)} + "%"] + [:> numeric-input* {:value (-> color :opacity opacity->string) + :className (stl/css :opacity-input) + :placeholder "--" + :select-on-focus select-on-focus :on-focus on-focus :on-blur on-blur - :on-change handle-value-change}]] + :on-change handle-opacity-change + :min 0 + :max 100}]])] - (when (and (not disable-opacity) - (not (:gradient color))) - [:div.input-element - {:class (dom/classnames :percentail (not= (:opacity color) :multiple))} - [:> numeric-input* {:value (-> color :opacity opacity->string) - :placeholder (tr "settings.multiple") - :select-on-focus select-on-focus - :on-focus on-focus - :on-blur on-blur - :on-change handle-opacity-change - :min 0 - :max 100}]]) - (when select-only - [:div.element-set-actions-button {:on-click handle-select} - i/pointer-inner])]) - (when (some? on-remove) - [:div.element-set-actions-button.remove {:on-click on-remove} i/minus])]) + (when (some? on-remove) + [:button {:class (stl/css :remove-btn) + :on-click on-remove} i/remove-refactor]) + (when select-only + [:button {:class (stl/css :select-btn) + :on-click handle-select} + i/move-refactor])] )) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.scss b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.scss index 83f32b627a..5285303c5f 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.scss @@ -8,155 +8,6 @@ .color-data { @include flexRow; - .color-info { - display: flex; - align-items: center; - gap: $s-2; - border-radius: $s-8; - background-color: var(--input-details-color); - height: $s-32; - width: 100%; - flex-grow: 1; - .color-name-wrapper { - @extend .input-element; - flex-grow: 1; - width: 100%; - border-radius: $br-8 0 0 $br-8; - padding: 0; - margin-right: 0; - gap: $s-4; - input { - padding: 0; - } - .color-bullet-wrapper { - height: $s-28; - padding: 0 $s-2 0 $s-8; - border-radius: $br-8 0 0 $br-8; - background-color: transparent; - &:hover { - background-color: transparent; - } - } - .color-name { - @include titleTipography; - display: flex; - align-items: center; - height: $s-28; - padding-left: $s-6; - border-radius: $br-8; - width: 100%; - flex-grow: 1; - color: var(--input-foreground-color-active); - } - .detach-btn { - @extend .button-tertiary; - height: $s-28; - width: $s-28; - border-radius: 0 $br-8 $br-8 0; - display: none; - svg { - @extend .button-icon; - } - } - .color-input-wrapper { - @include titleTipography; - display: flex; - align-items: center; - height: $s-28; - padding: 0 $s-0; - width: 100%; - margin: 0; - flex-grow: 1; - background-color: var(--input-background-color); - color: var(--input-foreground-color); - border-radius: $br-0; - } - &.no-opacity { - border-radius: $br-8; - .color-input-wrapper { - border-radius: $br-8; - } - } - &:hover { - background-color: var(--input-background-color-hover); - border: $s-1 solid var(--input-background-color-hover); - .color-bullet-wrapper, - .color-name, - .detach-btn, - .color-input-wrapper { - background-color: var(--input-background-color-hover); - } - .detach-btn { - display: flex; - svg { - stroke: var(--input-foreground-color-active); - } - } - &.editing { - background-color: var(--input-background-color-active); - .color-bullet-wrapper, - .color-name, - .detach-btn, - .color-input-wrapper { - background-color: var(--input-background-color-active); - } - } - } - &:focus, - &:focus-within { - background-color: var(--input-background-color-active); - } - - &.editing { - background-color: var(--input-background-color-active); - } - } - .gradient-name-wrapper { - border-radius: 0 $br-8 $br-8 0; - .color-name { - @include flexRow; - border-radius: 0 $br-8 $br-8 0; - } - } - .library-name-wrapper { - border-radius: $br-8; - } - .opacity-element-wrapper { - @extend .input-element; - width: $s-60; - border-radius: 0 $br-8 $br-8 0; - .opacity-input { - padding: 0; - border-radius: 0 $br-8 $br-8 0; - min-width: $s-28; - } - .icon-text { - @include flexCenter; - height: $s-32; - margin-right: $s-4; - padding-top: $s-2; - } - } - - &:hover { - .detach-btn, - .select-btn { - background-color: transparent; - svg { - stroke: var(--input-foreground-color-active); - } - } - } - } - .remove-btn, - .select-btn { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - } &.dnd-over-top { border-top: $s-1 solid var(--layer-row-foreground-color-drag); @@ -165,3 +16,155 @@ border-bottom: $s-1 solid var(--layer-row-foreground-color-drag); } } + +.color-info { + display: flex; + align-items: center; + gap: $s-2; + border-radius: $s-8; + background-color: var(--input-details-color); + height: $s-32; + width: 100%; + flex-grow: 1; + + .color-name-wrapper { + @extend .input-element; + flex-grow: 1; + width: 100%; + border-radius: $br-8 0 0 $br-8; + padding: 0; + margin-right: 0; + gap: $s-4; + input { + padding: 0; + } + .color-bullet-wrapper { + height: $s-28; + padding: 0 $s-2 0 $s-8; + border-radius: $br-8 0 0 $br-8; + background-color: transparent; + &:hover { + background-color: transparent; + } + } + .color-name { + @include titleTipography; + display: flex; + align-items: center; + height: $s-28; + padding-left: $s-6; + border-radius: $br-8; + width: 100%; + flex-grow: 1; + color: var(--input-foreground-color-active); + } + .detach-btn { + @extend .button-tertiary; + height: $s-28; + width: $s-28; + border-radius: 0 $br-8 $br-8 0; + display: none; + svg { + @extend .button-icon; + } + } + .color-input-wrapper { + @include titleTipography; + display: flex; + align-items: center; + height: $s-28; + padding: 0 $s-0; + width: 100%; + margin: 0; + flex-grow: 1; + background-color: var(--input-background-color); + color: var(--input-foreground-color); + border-radius: $br-0; + } + &.no-opacity { + border-radius: $br-8; + .color-input-wrapper { + border-radius: $br-8; + } + } + &:hover { + background-color: var(--input-background-color-hover); + border: $s-1 solid var(--input-background-color-hover); + .color-bullet-wrapper, + .color-name, + .detach-btn, + .color-input-wrapper { + background-color: var(--input-background-color-hover); + } + .detach-btn { + display: flex; + svg { + stroke: var(--input-foreground-color-active); + } + } + &.editing { + background-color: var(--input-background-color-active); + .color-bullet-wrapper, + .color-name, + .detach-btn, + .color-input-wrapper { + background-color: var(--input-background-color-active); + } + } + } + &:focus, + &:focus-within { + background-color: var(--input-background-color-active); + } + + &.editing { + background-color: var(--input-background-color-active); + } + } + .gradient-name-wrapper { + border-radius: 0 $br-8 $br-8 0; + .color-name { + @include flexRow; + border-radius: 0 $br-8 $br-8 0; + } + } + .library-name-wrapper { + border-radius: $br-8; + } + .opacity-element-wrapper { + @extend .input-element; + width: $s-60; + border-radius: 0 $br-8 $br-8 0; + .opacity-input { + padding: 0; + border-radius: 0 $br-8 $br-8 0; + min-width: $s-28; + } + .icon-text { + @include flexCenter; + height: $s-32; + margin-right: $s-4; + padding-top: $s-2; + } + } + + &:hover { + .detach-btn, + .select-btn { + background-color: transparent; + svg { + stroke: var(--input-foreground-color-active); + } + } + } +} + +.remove-btn, +.select-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/input_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/input_row.cljs deleted file mode 100644 index f0aca9d255..0000000000 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/input_row.cljs +++ /dev/null @@ -1,72 +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 - -(ns app.main.ui.workspace.sidebar.options.rows.input-row - (:require - [app.main.ui.components.editable-select :refer [editable-select]] - [app.main.ui.components.numeric-input :refer [numeric-input*]] - [app.main.ui.components.select :refer [select]] - [app.util.object :as obj] - [rumext.v2 :as mf])) - -(mf/defc input-row - [{:keys [label options value class min max on-change type placeholder default nillable on-focus select-on-focus]}] - [:div.row-flex.input-row - [:span.element-set-subtitle label] - [:div.input-element {:class class} - - (case type - :select - [:& select {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :default-value value - :class "input-option" - :options options - :on-change on-change}] - - :editable-select - [:& editable-select {:value value - :class "input-option" - :options options - :type "number" - :min min - :max max - :placeholder placeholder - :on-change on-change}] - - :text - [:input {:value value - :class "input-text" - :on-change on-change} ] - - [:> numeric-input* - {:placeholder placeholder - :min min - :max max - :default default - :nillable nillable - :on-change on-change - :on-focus on-focus - :select-on-focus select-on-focus - :value (or value "")}])]]) - - -;; NOTE: (by niwinz) this is a new version of input-row, I didn't -;; touched the original one because it is used in many sites and I -;; don't have intention to refactor all the code right now. We should -;; consider to use the new one and once we have migrated all to the -;; new component, we can proceed to rename it and delete the old one. - -(mf/defc input-row-v2 - {::mf/wrap-props false} - [props] - (let [label (obj/get props "label") - class (obj/get props "class") - children (obj/get props "children")] - [:div.row-flex.input-row - [:span.element-set-subtitle label] - [:div.input-element {:class class} - children]])) - diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs index 2b661b1096..558e86b416 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs @@ -10,7 +10,6 @@ [app.common.data :as d] [app.common.types.shape.layout :as ctl] [app.main.refs :as refs] - [app.main.ui.context :as ctx] [app.main.ui.hooks :as hooks] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]] @@ -33,8 +32,7 @@ {::mf/wrap [mf/memo] ::mf/wrap-props false} [props] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - shape (unchecked-get props "shape") + (let [shape (unchecked-get props "shape") shape-with-children (unchecked-get props "shape-with-children") shared-libs (unchecked-get props "shared-libs") objects (->> shape-with-children (group-by :id) (d/mapm (fn [_ v] (first v)))) @@ -69,8 +67,7 @@ [layout-item-ids layout-item-values] (get-attrs [shape] objects :layout-item)] - [:div {:class (stl/css-case new-css-system - :options true)} + [:div {:class (stl/css :options)} [:& layer-menu {:type type :ids layer-ids :values layer-values}] [:& measures-menu {:type type :ids measure-ids :values measure-values :shape shape}] [:& component-menu {:shapes [shape]}] ;;remove this in components-v2 diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs index 8309844a76..a562269aec 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs @@ -16,7 +16,6 @@ [app.common.types.shape.layout :as ctl] [app.main.data.workspace.texts :as dwt] [app.main.refs :as refs] - [app.main.ui.context :as ctx] [app.main.ui.hooks :as hooks] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-attrs blur-menu]] [app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]] @@ -268,8 +267,7 @@ {::mf/wrap [#(mf/memo' % (mf/check-props ["shapes" "shapes-with-children" "page-id" "file-id"]))] ::mf/wrap-props false} [props] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - shapes (unchecked-get props "shapes") + (let [shapes (unchecked-get props "shapes") shapes-with-children (unchecked-get props "shapes-with-children") ;; remove children from bool shapes @@ -348,8 +346,7 @@ components (filter ctk/instance-head? shapes)] - [:div {:class (stl/css-case new-css-system - :options true)} + [:div {:class (stl/css :options)} (when-not (empty? layer-ids) [:& layer-menu {:type type :ids layer-ids :values layer-values}]) diff --git a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs index 65b37b1e10..04748ef406 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs @@ -5,13 +5,12 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.workspace.sidebar.shortcuts - (:require-macros [app.main.style :refer [css]]) + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] [app.config :as cf] [app.main.data.dashboard.shortcuts] - [app.main.data.events :as ev] [app.main.data.shortcuts :as ds] [app.main.data.viewer.shortcuts] [app.main.data.workspace :as dw] @@ -19,21 +18,17 @@ [app.main.data.workspace.shortcuts] [app.main.store :as st] [app.main.ui.components.search-bar :refer [search-bar]] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :refer [tr]] - [app.util.keyboard :as kbd] [app.util.strings :refer [matches-search]] [clojure.set :as set] [clojure.string] - [cuerdas.core :as str] [rumext.v2 :as mf])) (mf/defc converted-chars [{:keys [char command] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - modified-keys {:up ds/up-arrow + (let [modified-keys {:up ds/up-arrow :down ds/down-arrow :left ds/left-arrow :right ds/right-arrow @@ -51,10 +46,8 @@ char (if (contains? modified-keys (keyword char)) ((keyword char) modified-keys) char) char (if (and is-macos? (contains? macos-keys (keyword char))) ((keyword char) macos-keys) char) unique-key (str (d/name command) "-" char)] - (if new-css-system - [:span {:class (css :key) - :key unique-key} char] - [:span.char-box {:key unique-key} char]))) + [:span {:class (stl/css :key) + :key unique-key} char])) (defn translation-keyname [type keyname] @@ -222,8 +215,7 @@ (mf/defc shortcuts-keys [{:keys [content command] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - managed-list (if (coll? content) + (let [managed-list (if (coll? content) content (conj () content)) chars-list (map ds/split-sc managed-list) @@ -232,43 +224,25 @@ chars-list (drop-last chars-list)) penultimate (last short-char-list)] - (if new-css-system - [:span {:class (css :keys)} - (for [chars short-char-list] - [:* - (for [char chars] - [:& converted-chars {:key (dm/str char "-" (name command)) - :char char - :command command}]) - (when (not= chars penultimate) [:span {:class (css :space)} ","])]) - (when (not= last-element penultimate) - [:* - [:span {:class (css :space)} (tr "shortcuts.or")] - (for [char last-element] - [:& converted-chars {:key (dm/str char "-" (name command)) - :char char - :command command}])])] - - [:span.keys - (for [chars short-char-list] - [:* - (for [char chars] - [:& converted-chars {:key (dm/str char "-" (name command)) - :char char - :command command}]) - (when (not= chars penultimate) [:span.space ","])]) - (when (not= last-element penultimate) - [:* - [:span.space (tr "shortcuts.or")] - (for [char last-element] - [:& converted-chars {:key (dm/str char "-" (name command)) - :char char - :command command}])])]))) + [:span {:class (stl/css :keys)} + (for [chars short-char-list] + [:* + (for [char chars] + [:& converted-chars {:key (dm/str char "-" (name command)) + :char char + :command command}]) + (when (not= chars penultimate) [:span {:class (stl/css :space)} ","])]) + (when (not= last-element penultimate) + [:* + [:span {:class (stl/css :space)} (tr "shortcuts.or")] + (for [char last-element] + [:& converted-chars {:key (dm/str char "-" (name command)) + :char char + :command command}])])])) (mf/defc shortcut-row [{:keys [elements filter-term match-section? match-subsection?] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - shortcut-name (keys elements) + (let [shortcut-name (keys elements) shortcut-translations (map #(translation-keyname :sc %) shortcut-name) match-shortcut? (some #(matches-search % @filter-term) shortcut-translations) filtered (if (and (or match-section? match-subsection?) (not match-shortcut?)) @@ -276,46 +250,32 @@ (filter #(matches-search % @filter-term) shortcut-translations)) sorted-filtered (sort filtered)] - (if new-css-system - [:ul {:class (css :sub-menu)} - (for [command-translate sorted-filtered] - (let [sc-by-translate (first (filter #(= (:translation (second %)) command-translate) elements)) - [command comand-info] sc-by-translate - content (or (:show-command comand-info) (:command comand-info))] - [:li {:class (css :shortcuts-name) - :key command-translate} - [:span {:class (css :command-name)} - command-translate] - [:& shortcuts-keys {:content content - :command command}]]))] - - [:ul.sub-menu - (for [command-translate sorted-filtered] - (let [sc-by-translate (first (filter #(= (:translation (second %)) command-translate) elements)) - [command comand-info] sc-by-translate - content (or (:show-command comand-info) (:command comand-info))] - [:li.shortcut-name {:key command-translate} - [:span.command-name command-translate] - [:& shortcuts-keys {:content content - :command command}]]))]))) + [:ul {:class (stl/css :sub-menu)} + (for [command-translate sorted-filtered] + (let [sc-by-translate (first (filter #(= (:translation (second %)) command-translate) elements)) + [command comand-info] sc-by-translate + content (or (:show-command comand-info) (:command comand-info))] + [:li {:class (stl/css :shortcuts-name) + :key command-translate} + [:span {:class (stl/css :command-name)} + command-translate] + [:& shortcuts-keys {:content content + :command command}]]))])) (mf/defc section-title [{:keys [is-visible? name is-sub?] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:div {:class (if is-sub? (css :subsection-title) (css :section-title))} - [:span {:class (dom/classnames (css :open) is-visible? - (css :collapsed-shortcuts) true)} i/arrow-refactor] - [:span {:class (if is-sub? (css :subsection-name) (css :section-name))} name]] - - [:div {:class (if is-sub? "subsection-title" "section-title")} - [:span.collapesed-shortcuts {:class (when is-visible? "open")} i/arrow-slide] - [:span {:class (if is-sub? "subsection-name" "section-name")} name]]))) + [:div {:class (if is-sub? + (stl/css :subsection-title) + (stl/css :section-title))} + [:span {:class (stl/css-case :open is-visible? + :collapsed-shortcuts true)} i/arrow-refactor] + [:span {:class (if is-sub? + (stl/css :subsection-name) + (stl/css :section-name))} name]]) (mf/defc shortcut-subsection [{:keys [subsections manage-sections filter-term match-section? open-sections] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - subsections-names (keys subsections) + (let [subsections-names (keys subsections) subsection-translations (if (= :none (first subsections-names)) (map #(translation-keyname :sc %) subsections-names) (map #(translation-keyname :sub-sec %) subsections-names)) @@ -328,8 +288,7 @@ :match-section? match-section? :match-subsection? true}]) - [:ul {:class (dom/classnames (css :subsection-menu) new-css-system - :subsection-menu (not new-css-system))} + [:ul {:class (stl/css :subsection-menu)} (for [sub-translated sorted-translations] (let [sub-by-translate (first (filter #(= (:translation (second %)) sub-translated) subsections)) [sub-name sub-info] sub-by-translate @@ -368,7 +327,7 @@ visible? (some #(= % section-id) @open-sections)] (when (or match-section? match-subsection? match-shortcut?) - [:div {:class (css :section) + [:div {:class (stl/css :section) :on-click (manage-sections section-id)} [:& section-title {:is-visible? visible? :is-sub? false @@ -383,8 +342,7 @@ (mf/defc shortcuts-container [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - workspace-shortcuts app.main.data.workspace.shortcuts/shortcuts + (let [workspace-shortcuts app.main.data.workspace.shortcuts/shortcuts path-shortcuts app.main.data.workspace.path.shortcuts/shortcuts all-workspace-shortcuts (->> (d/deep-merge path-shortcuts workspace-shortcuts) (add-translation :sc) @@ -496,13 +454,6 @@ (manage-section-on-search :viewer term))] (reset! open-sections ids)))) - on-search-term-change - (mf/use-callback - (fn [event] - (let [value (dom/get-target-val event)] - (manage-sections-on-search value) - (reset! filter-term value)))) - on-search-term-change-2 (mf/use-callback (fn [value] @@ -512,80 +463,31 @@ (mf/use-callback (fn [_] (reset! open-sections [[1]]) - (reset! filter-term ""))) - - manage-key-down - (mf/use-callback - (fn [event] - (when (kbd/esc? event) - (st/emit! (-> (dw/toggle-layout-flag :shortcuts) - (vary-meta assoc ::ev/origin "shortcuts-panel")))))) - - on-key-down - (mf/use-callback - (fn [event] - (when (kbd/enter? event) - (on-search-clear-click) - (dom/focus! (dom/get-element "shortcut-search")))))] + (reset! filter-term "")))] (mf/with-effect [] (dom/focus! (dom/get-element "shortcut-search"))) - (if new-css-system - [:div {:class (css :shortcuts)} - [:div {:class (css :shortcuts-header)} - [:div {:class (css :shortcuts-title)} (tr "shortcuts.title")] - [:div {:class (css :shortcuts-close-button) - :on-click close-fn} - i/close-refactor]] - [:div {:class (css :search-field)} + [:div {:class (stl/css :shortcuts)} + [:div {:class (stl/css :shortcuts-header)} + [:div {:class (stl/css :shortcuts-title)} (tr "shortcuts.title")] + [:div {:class (stl/css :shortcuts-close-button) + :on-click close-fn} + i/close-refactor]] + [:div {:class (stl/css :search-field)} - [:& search-bar {:on-change on-search-term-change-2 - :clear-action on-search-clear-click - :value @filter-term - :placeholder (tr "shortcuts.title") - :icon (mf/html [:span {:class (css :search-icon)} i/search-refactor])}]] + [:& search-bar {:on-change on-search-term-change-2 + :clear-action on-search-clear-click + :value @filter-term + :placeholder (tr "shortcuts.title") + :icon (mf/html [:span {:class (stl/css :search-icon)} i/search-refactor])}]] - (if match-any? - [:div {:class (dom/classnames (css :shortcuts-list) true)} - (for [section all-shortcuts] - [:& shortcut-section - {:section section - :manage-sections manage-sections - :open-sections open-sections - :filter-term filter-term}])] - [:div {:class (css :not-found)} (tr "shortcuts.not-found")])] - - [:div.shortcuts - [:div.shortcuts-header - [:div.shortcuts-close-button - {:on-click close-fn} i/close] - [:div.shortcuts-title (tr "shortcuts.title")]] - [:div.search-field - [:div.search-box - [:input.input-text - {:id "shortcut-search" - :placeholder (tr "shortcuts.search-placeholder") - :type "text" - :value @filter-term - :on-change on-search-term-change - :auto-complete "off" - :on-key-down manage-key-down}] - (if (str/empty? @filter-term) - [:span.icon-wrapper - i/search] - [:button.icon-wrapper - {:on-click on-search-clear-click - :on-key-down on-key-down} - [:span.icon.close - i/close]])]] - (if match-any? - [:div.shortcut-list - (for [section all-shortcuts] - [:& shortcut-section - {:section section - :manage-sections manage-sections - :open-sections open-sections - :filter-term filter-term}])] - - [:div.not-found (tr "shortcuts.not-found")])]))) + (if match-any? + [:div {:class (stl/css :shortcuts-list)} + (for [section all-shortcuts] + [:& shortcut-section + {:section section + :manage-sections manage-sections + :open-sections open-sections + :filter-term filter-term}])] + [:div {:class (stl/css :not-found)} (tr "shortcuts.not-found")])])) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 6933fb1c85..8374386cf4 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -229,7 +229,7 @@ show-rules? (and (contains? layout :rules) (not hide-ui?)) - disabled-guides? (or drawing-tool transform) + disabled-guides? (or drawing-tool transform drawing-path? node-editing?) one-selected-shape? (= (count selected-shapes) 1) diff --git a/frontend/src/app/main/ui/workspace/viewport/comments.cljs b/frontend/src/app/main/ui/workspace/viewport/comments.cljs index f036cbde96..aa59d9e91f 100644 --- a/frontend/src/app/main/ui/workspace/viewport/comments.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/comments.cljs @@ -12,15 +12,13 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.comments :as cmt] - [app.main.ui.context :as ctx] [cuerdas.core :as str] [okulary.core :as l] [rumext.v2 :as mf])) (mf/defc comments-layer [{:keys [vbox vport zoom file-id page-id drawing] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - pos-x (* (- (:x vbox)) zoom) + (let [pos-x (* (- (:x vbox)) zoom) pos-y (* (- (:y vbox)) zoom) profile (mf/deref refs/profile) @@ -57,54 +55,29 @@ (st/emit! (dwcm/initialize-comments file-id)) (fn [] (st/emit! ::dwcm/finalize)))) - (if new-css-system - [:div {:class (stl/css :comments-section)} - [:div - {:class (stl/css :workspace-comments-container) - :style {:width (str (:width vport) "px") - :height (str (:height vport) "px")}} - [:div {:class (stl/css :threads) - :style {:transform (str/format "translate(%spx, %spx)" pos-x pos-y)}} - (for [item threads] - [:& cmt/thread-bubble {:thread item - :zoom zoom - :open? (= (:id item) (:open local)) - :key (:seqn item)}]) + [:div {:class (stl/css :comments-section)} + [:div + {:class (stl/css :workspace-comments-container) + :style {:width (str (:width vport) "px") + :height (str (:height vport) "px")}} + [:div {:class (stl/css :threads) + :style {:transform (str/format "translate(%spx, %spx)" pos-x pos-y)}} + (for [item threads] + [:& cmt/thread-bubble {:thread item + :zoom zoom + :open? (= (:id item) (:open local)) + :key (:seqn item)}]) - (when-let [id (:open local)] - (when-let [thread (get threads-map id)] - [:& cmt/thread-comments {:thread (update-thread-position thread) - :users users - :zoom zoom}])) + (when-let [id (:open local)] + (when-let [thread (get threads-map id)] + [:& cmt/thread-comments {:thread (update-thread-position thread) + :users users + :zoom zoom}])) - (when-let [draft (:comment drawing)] - [:& cmt/draft-thread {:draft draft - :on-cancel on-draft-cancel - :on-submit on-draft-submit - :zoom zoom}])]]] - - ;; OLD - [:div.comments-section - [:div.workspace-comments-container - {:style {:width (str (:width vport) "px") - :height (str (:height vport) "px")}} - [:div.threads {:style {:transform (str/format "translate(%spx, %spx)" pos-x pos-y)}} - (for [item threads] - [:& cmt/thread-bubble {:thread item - :zoom zoom - :open? (= (:id item) (:open local)) - :key (:seqn item)}]) - - (when-let [id (:open local)] - (when-let [thread (get threads-map id)] - [:& cmt/thread-comments {:thread (update-thread-position thread) - :users users - :zoom zoom}])) - - (when-let [draft (:comment drawing)] - [:& cmt/draft-thread {:draft draft - :on-cancel on-draft-cancel - :on-submit on-draft-submit - :zoom zoom}])]]]) + (when-let [draft (:comment drawing)] + [:& cmt/draft-thread {:draft draft + :on-cancel on-draft-cancel + :on-submit on-draft-submit + :zoom zoom}])]]] )) diff --git a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs index 8066696b00..fcac73c07b 100644 --- a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs @@ -10,6 +10,7 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.files.helpers :as cfh] + [app.common.geom.line :as gl] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] @@ -24,12 +25,12 @@ [app.main.data.workspace.shape-layout :as dwsl] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.css-cursors :as cur] [app.main.ui.formats :as fmt] [app.main.ui.hooks :as hooks] [app.main.ui.icons :as i] [app.main.ui.workspace.viewport.viewport-ref :as uwvv] + [app.util.debug :as dbg] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] @@ -53,28 +54,16 @@ (mf/defc grid-edition-actions {::mf/wrap-props false} [{:keys [shape]}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] - (if new-css-system - [:div {:class (stl/css :grid-actions)} - [:div {:class (stl/css :grid-actions-container)} - [:div {:class (stl/css :grid-actions-title)} - (tr "workspace.layout_grid.editor.title") " " [:span {:stl/css :board-name} (:name shape)]] - [:button {:class (stl/css :locate-btn) - :on-click #(st/emit! (dwge/locate-board (:id shape)))} - (tr "workspace.layout_grid.editor.top-bar.locate")] - [:button {:class (stl/css :done-btn) - :on-click #(st/emit! dw/clear-edition-mode)} - (tr "workspace.layout_grid.editor.top-bar.done")]]] - - [:div.viewport-actions - [:div.viewport-actions-container - [:div.viewport-actions-title - (tr "workspace.layout_grid.editor.title") " " [:span.grid-edit-board-name (:name shape)]] - [:button.btn-secondary {:on-click #(st/emit! (dwge/locate-board (:id shape)))} - (tr "workspace.layout_grid.editor.top-bar.locate")] - [:button.btn-primary {:on-click #(st/emit! dw/clear-edition-mode)} - (tr "workspace.layout_grid.editor.top-bar.done")] - [:button.btn-icon-basic {:on-click #(st/emit! dw/clear-edition-mode)} i/close]]]))) + [:div {:class (stl/css :grid-actions)} + [:div {:class (stl/css :grid-actions-container)} + [:div {:class (stl/css :grid-actions-title)} + (tr "workspace.layout_grid.editor.title") " " [:span {:stl/css :board-name} (:name shape)]] + [:button {:class (stl/css :locate-btn) + :on-click #(st/emit! (dwge/locate-board (:id shape)))} + (tr "workspace.layout_grid.editor.top-bar.locate")] + [:button {:class (stl/css :done-btn) + :on-click #(st/emit! dw/clear-edition-mode)} + (tr "workspace.layout_grid.editor.top-bar.done")]]]) (mf/defc grid-editor-frame {::mf/wrap-props false} @@ -163,7 +152,7 @@ (mf/set-ref-val! dragging-ref true) (mf/set-ref-val! start-pos-ref raw-pt) (mf/set-ref-val! current-pos-ref raw-pt) - (when on-drag-start (on-drag-start position))))) + (when on-drag-start (on-drag-start event position))))) handle-lost-pointer-capture (mf/use-callback @@ -174,7 +163,7 @@ (dom/release-pointer event) (mf/set-ref-val! dragging-ref false) (mf/set-ref-val! start-pos-ref nil) - (when on-drag-end (on-drag-end position))))) + (when on-drag-end (on-drag-end event position))))) handle-pointer-move (mf/use-callback @@ -185,8 +174,8 @@ pos (dom/get-client-position event) pt (uwvv/point->viewport pos)] (mf/set-ref-val! current-pos-ref pos) - (when on-drag-delta (on-drag-delta (gpt/to-vec start pos))) - (when on-drag-position (on-drag-position pt))))))] + (when on-drag-delta (on-drag-delta event (gpt/to-vec start pos))) + (when on-drag-position (on-drag-position event pt))))))] {:handle-pointer-down handle-pointer-down :handle-lost-pointer-capture handle-lost-pointer-capture @@ -212,7 +201,7 @@ handle-drag-position (mf/use-callback (mf/deps shape row column row-span column-span) - (fn [position] + (fn [_ position] (let [[drag-row drag-column] (gsg/get-position-grid-coord layout-data position) [new-row new-column new-row-span new-column-span] @@ -336,11 +325,52 @@ (mf/use-callback (mf/deps (:id shape) (:id cell) selected?) (fn [event] - (if (and (kbd/shift? event) selected?) - (st/emit! (dwge/remove-selection (:id shape) (:id cell))) - (st/emit! (dwge/select-grid-cell (:id shape) (:id cell) (kbd/shift? event)) ))))] + (when (dom/left-mouse? event) + (cond + (and selected? (or (kbd/mod? event) (kbd/shift? event))) + (st/emit! (dwge/remove-selection (:id shape) (:id cell))) + + (and (not selected?) (kbd/mod? event)) + (st/emit! (dwge/add-to-selection (:id shape) (:id cell))) + + (and (not selected?) (kbd/shift? event)) + (st/emit! (dwge/add-to-selection (:id shape) (:id cell) true)) + + :else + (st/emit! (dwge/set-selection (:id shape) (:id cell))))))) + + handle-context-menu + (mf/use-callback + (fn [event] + (dom/prevent-default event) + (dom/stop-propagation event) + (let [position (dom/get-client-position event)] + (st/emit! (dw/show-grid-cell-context-menu {:position position :grid-id (:id shape)})))))] [:g.cell-editor + ;; DEBUG OVERLAY + (when (dbg/enabled? :grid-cells) + [:g.debug-cell {:pointer-events "none" + :transform (dm/str (gmt/transform-in cell-center (:transform shape)))} + + [:rect + {:x (:x cell-origin) + :y (:y cell-origin) + :width cell-width + :height cell-height + :fill (cond + (= (:position cell) :auto) "green" + (= (:position cell) :manual) "red" + (= (:position cell) :area) "yellow" + :else "black") + :fill-opacity 0.2}] + + (when (seq (:shapes cell)) + [:circle + {:cx (+ (:x cell-origin) cell-width (- (/ 7 zoom))) + :cy (+ (:y cell-origin) (/ 7 zoom)) + :r (/ 5 zoom) + :fill "red"}])]) [:rect {:transform (dm/str (gmt/transform-in cell-center (:transform shape))) :class (dom/classnames (stl/css :grid-cell-outline) true @@ -351,6 +381,7 @@ :width cell-width :height cell-height + :on-context-menu handle-context-menu :on-pointer-enter handle-pointer-enter :on-pointer-leave handle-pointer-leave :on-pointer-down handle-pointer-down}] @@ -410,7 +441,7 @@ handle-drag-position (mf/use-callback (mf/deps shape track-before track-after) - (fn [position] + (fn [_ position] (let [[tracks-prop axis] (if (= :column type) [:layout-grid-columns :x] [:layout-grid-rows :y]) @@ -451,11 +482,14 @@ (let [shape (unchecked-get props "shape") index (unchecked-get props "index") last? (unchecked-get props "last?") + drop? (unchecked-get props "drop?") track-before (unchecked-get props "track-before") track-after (unchecked-get props "track-after") snap-pixel? (unchecked-get props "snap-pixel?") - {:keys [column-total-size column-total-gap row-total-size row-total-gap]} (unchecked-get props "layout-data") + {:keys [column-total-size column-total-gap row-total-size row-total-gap] :as layout-data} + (unchecked-get props "layout-data") + start-p (unchecked-get props "start-p") type (unchecked-get props "type") zoom (unchecked-get props "zoom") @@ -477,7 +511,7 @@ [(+ column-total-size column-total-gap) (max 0 (- layout-gap-row (/ 10 zoom)) (/ 8 zoom))]) - start-p + start-p-resize (cond-> start-p (and (= type :column) (= index 0)) (gpt/subtract (hv (/ width 2))) @@ -491,22 +525,52 @@ (and (= type :row) (not= index 0) (not last?)) (-> (gpt/subtract (vv (/ layout-gap-row 2))) - (gpt/subtract (vv (/ height 2)))))] + (gpt/subtract (vv (/ height 2))))) - [:rect.resize-track-handler - {:x (:x start-p) - :y (:y start-p) - :height height - :width width - :on-pointer-down handle-pointer-down - :on-lost-pointer-capture handle-lost-pointer-capture - :on-pointer-move handle-pointer-move - :transform (dm/str (gmt/transform-in start-p (:transform shape))) - :class (if (= type :column) - (cur/get-dynamic "resize-ew" (:rotation shape)) - (cur/get-dynamic "resize-ns" (:rotation shape))) - :style {:fill "transparent" - :stroke-width 0}}])) + start-p-drop + (cond-> start-p + (and (= type :column) (= index 0)) + (gpt/subtract (hv (/ width 2))) + + (and (= type :row) (= index 0)) + (gpt/subtract (vv (/ height 2))) + + (and (= type :column) last?) + (gpt/add (hv (/ width 2))) + + (and (= type :row) last?) + (gpt/add (vv (/ height 2))) + + (and (= type :column) (not= index 0) (not last?)) + (-> (gpt/subtract (hv (/ layout-gap-col 2))) + (gpt/subtract (hv (/ 5 zoom)))) + + (and (= type :row) (not= index 0) (not last?)) + (-> (gpt/subtract (vv (/ layout-gap-row 2))) + (gpt/subtract (vv (/ 5 zoom)))))] + [:* + (when drop? + [:rect.drop + {:x (:x start-p-drop) + :y (:y start-p-drop) + :width (if (= type :column)(/ 10 zoom) width) + :height (if (= type :row) (/ 10 zoom) height) + :fill "var(--grid-editor-area-background)"}]) + + [:rect.resize-track-handler + {:x (:x start-p-resize) + :y (:y start-p-resize) + :height height + :width width + :on-pointer-down handle-pointer-down + :on-lost-pointer-capture handle-lost-pointer-capture + :on-pointer-move handle-pointer-move + :transform (dm/str (gmt/transform-in start-p (:transform shape))) + :class (if (= type :column) + (cur/get-dynamic "resize-ew" (:rotation shape)) + (cur/get-dynamic "resize-ns" (:rotation shape))) + :style {:fill "transparent" + :stroke-width 0}}]])) (def marker-width 24) (def marker-h1 20) @@ -620,7 +684,7 @@ (dm/str value)]])) (mf/defc track - {::mf/wrap [#(mf/memo' % (mf/check-props ["shape" "zoom" "index" "type" "track-data" "layout-data" "hovering?"]))] + {::mf/wrap [mf/memo] ::mf/wrap-props false} [props] (let [shape (unchecked-get props "shape") @@ -631,6 +695,11 @@ track-data (unchecked-get props "track-data") layout-data (unchecked-get props "layout-data") hovering? (unchecked-get props "hovering?") + drop? (unchecked-get props "drop?") + + on-start-reorder-track (unchecked-get props "on-start-reorder-track") + on-move-reorder-track (unchecked-get props "on-move-reorder-track") + on-end-reorder-track (unchecked-get props "on-end-reorder-track") track-input-ref (mf/use-ref) [layout-gap-row layout-gap-col] (ctl/gaps shape) @@ -707,29 +776,60 @@ (fn [] (st/emit! (dwsl/hover-layout-track [(:id shape)] type index false)))) - handle-remove-track - (mf/use-callback - (mf/deps (:id shape) type index) - (fn [] - (st/emit! (dwsl/remove-layout-track [(:id shape)] type index)))) - track-list-prop (if (= type :column) :column-tracks :row-tracks) [text-x text-y text-width text-height] (if (= type :column) [(:x text-p) (- (:y text-p) (/ 36 zoom)) (max 0 (:size track-data)) (/ 36 zoom)] [(- (:x text-p) (max 0 (:size track-data))) (- (:y text-p) (/ 36 zoom)) (max 0 (:size track-data)) (/ 36 zoom)]) + handle-drag-start + (mf/use-callback + (mf/deps on-start-reorder-track type index) + (fn [] + (on-start-reorder-track type index))) + + handle-drag-end + (mf/use-callback + (mf/deps on-end-reorder-track type index) + (fn [event position] + (on-end-reorder-track type index position (not (kbd/mod? event))))) + + handle-drag-position + (mf/use-callback + (mf/deps on-move-reorder-track type index) + (fn [_ position] + (on-move-reorder-track type index position))) + + handle-show-track-menu + (mf/use-callback + (fn [event] + (dom/stop-propagation event) + (dom/prevent-default event) + (let [position (cond-> (dom/get-client-position event) + (= type :column) (update :y + 40) + (= type :row) (update :x + 30))] + (st/emit! (dw/show-track-context-menu {:position position + :grid-id (:id shape) + :type type + :index index}))))) + trackwidth (* text-width zoom) medium? (and (>= trackwidth small-size-limit) (< trackwidth medium-size-limit)) small? (< trackwidth small-size-limit) - track-before (get-in layout-data [track-list-prop (dec index)])] + track-before (get-in layout-data [track-list-prop (dec index)]) + + {:keys [handle-pointer-down handle-lost-pointer-capture handle-pointer-move]} + (use-drag {:on-drag-start handle-drag-start + :on-drag-end handle-drag-end + :on-drag-position handle-drag-position})] (mf/use-effect (mf/deps track-data) (fn [] (dom/set-value! (mf/ref-val track-input-ref) (format-size track-data)))) + [:g.track [:g {:on-pointer-enter handle-pointer-enter :on-pointer-leave handle-pointer-leave @@ -737,17 +837,21 @@ (dm/str (gmt/transform-in text-p (:transform shape))) (dm/str (gmt/transform-in text-p (gmt/rotate (:transform shape) -90))))} - (when (and hovering? (not small?)) - [:rect {:x (+ text-x (/ 18 zoom)) - :y text-y - :width (- text-width (/ 36 zoom)) - :height (- text-height (/ 5 zoom)) - :rx (/ 3 zoom) - :fill "var(--grid-editor-marker-color)" - :opacity 0.2}]) + [:rect {:class (stl/css :grid-editor-header-hover) + :x (+ text-x (/ 18 zoom)) + :y text-y + :width (- text-width (/ 36 zoom)) + :height (- text-height (/ 5 zoom)) + :rx (/ 3 zoom) + :style {:cursor "pointer"} + :opacity (if (and hovering? (not small?)) 0.2 0)}] (when (not small?) [:foreignObject {:x text-x :y text-y :width text-width :height text-height} - [:div {:class (stl/css :grid-editor-wrapper)} + [:div {:class (stl/css :grid-editor-wrapper) + :on-context-menu handle-show-track-menu + :on-pointer-down handle-pointer-down + :on-lost-pointer-capture handle-lost-pointer-capture + :on-pointer-move handle-pointer-move} [:input {:ref track-input-ref :style {} @@ -759,7 +863,7 @@ :on-blur handle-blur-track-input}] (when (and hovering? (not medium?) (not small?)) [:button {:class (stl/css :grid-editor-button) - :on-click handle-remove-track} i/delete-refactor])]])] + :on-click handle-show-track-menu} i/menu-refactor])]])] [:g {:transform (when (= type :row) (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p)))} [:& track-marker @@ -778,6 +882,7 @@ :layout-data layout-data :shape shape :snap-pixel? snap-pixel? + :drop? drop? :start-p start-p :track-after track-data :track-before track-before @@ -855,8 +960,8 @@ (mf/deps shape children) #(gsg/calc-layout-data shape bounds children all-bounds objects)) - width (max (gpo/width-points bounds) (+ column-total-size column-total-gap)) - height (max (gpo/height-points bounds) (+ row-total-size row-total-gap)) + width (max (gpo/width-points bounds) (+ column-total-size column-total-gap (ctl/h-padding shape))) + height (max (gpo/height-points bounds) (+ row-total-size row-total-gap (ctl/v-padding shape))) handle-pointer-down (mf/use-callback @@ -875,7 +980,68 @@ (mf/use-callback (mf/deps (:id shape)) (fn [] - (st/emit! (st/emit! (dwsl/add-layout-track [(:id shape)] :row ctl/default-track-value)))))] + (st/emit! (st/emit! (dwsl/add-layout-track [(:id shape)] :row ctl/default-track-value))))) + + + target-tracks* (mf/use-ref nil) + drop-track-type* (mf/use-state nil) + drop-track-target* (mf/use-state nil) + + handle-start-reorder-track + (mf/use-callback + (mf/deps layout-data) + (fn [type _from-idx] + ;; Initialize target-tracks + (let [line-vec (if (= type :column) (vv 1) (hv 1)) + + first-point origin + last-point (if (= type :column) (nth bounds 1) (nth bounds 3)) + mid-points + (if (= type :column) + (->> (:column-tracks layout-data) + (mapv #(gpt/add (:start-p %) (hv (/ (:size %) 2))))) + + (->> (:row-tracks layout-data) + (mapv #(gpt/add (:start-p %) (vv (/ (:size %) 2)))))) + + tracks + (->> (d/with-prev (d/concat-vec [first-point] mid-points [last-point])) + (d/enumerate) + (keep + (fn [[index [current prev]]] + (when (some? prev) + [[prev current line-vec] (dec index)]))))] + + (mf/set-ref-val! target-tracks* tracks) + (reset! drop-track-type* type)))) + + handle-move-reorder-track + (mf/use-callback + (fn [_type _from-idx position] + (let [index + (->> (mf/ref-val target-tracks*) + (d/seek (fn [[[p1 p2 v] _]] + (gl/is-inside-lines? [p1 v] [p2 v] position))) + (second))] + (when (some? index) + (reset! drop-track-target* index))))) + + handle-end-reorder-track + (mf/use-callback + (mf/deps base-shape @drop-track-target*) + (fn [type from-index _position move-content?] + (when-let [to-index @drop-track-target*] + (let [ids [(:id base-shape)]] + (cond + (< from-index to-index) + (st/emit! (dwsl/reorder-layout-track ids type from-index (dec to-index) move-content?)) + + (> from-index to-index) + (st/emit! (dwsl/reorder-layout-track ids type from-index (dec to-index) move-content?))))) + + (mf/set-ref-val! target-tracks* nil) + (reset! drop-track-type* nil) + (reset! drop-track-target* nil)))] (mf/use-effect (fn [] @@ -914,15 +1080,21 @@ :on-click handle-add-row}]]) (for [[idx column-data] (d/enumerate column-tracks)] - [:& track {:key (dm/str "column-track-" idx) - :shape shape - :zoom zoom - :type :column - :index idx - :layout-data layout-data - :snap-pixel? snap-pixel? - :track-data column-data - :hovering? (contains? hover-columns idx)}]) + (let [drop? (and (= :column @drop-track-type*) + (= idx @drop-track-target*))] + [:& track {:key (dm/str "column-track-" idx) + :shape shape + :zoom zoom + :type :column + :index idx + :layout-data layout-data + :snap-pixel? snap-pixel? + :drop? drop? + :track-data column-data + :hovering? (contains? hover-columns idx) + :on-start-reorder-track handle-start-reorder-track + :on-move-reorder-track handle-move-reorder-track + :on-end-reorder-track handle-end-reorder-track}])) ;; Last track resize handler (when-not (empty? column-tracks) @@ -940,27 +1112,36 @@ :type :column :value (dm/str (inc (count column-tracks))) :zoom zoom}] - [:& resize-track-handler - {:index (count column-tracks) - :last? true - :shape shape - :layout-data layout-data - :snap-pixel? snap-pixel? - :start-p end-p - :type :column - :track-before (last column-tracks) - :zoom zoom}]])) + (let [drop? (and (= :column @drop-track-type*) + (= (count column-tracks) @drop-track-target*))] + [:& resize-track-handler + {:index (count column-tracks) + :last? true + :drop? drop? + :shape shape + :layout-data layout-data + :snap-pixel? snap-pixel? + :start-p end-p + :type :column + :track-before (last column-tracks) + :zoom zoom}])])) (for [[idx row-data] (d/enumerate row-tracks)] - [:& track {:index idx - :key (dm/str "row-track-" idx) - :layout-data layout-data - :shape shape - :snap-pixel? snap-pixel? - :track-data row-data - :type :row - :zoom zoom - :hovering? (contains? hover-rows idx)}]) + (let [drop? (and (= :row @drop-track-type*) + (= idx @drop-track-target*))] + [:& track {:index idx + :key (dm/str "row-track-" idx) + :layout-data layout-data + :shape shape + :snap-pixel? snap-pixel? + :drop? drop? + :track-data row-data + :type :row + :zoom zoom + :hovering? (contains? hover-rows idx) + :on-start-reorder-track handle-start-reorder-track + :on-move-reorder-track handle-move-reorder-track + :on-end-reorder-track handle-end-reorder-track}])) (when-not (empty? row-tracks) (let [last-track (last row-tracks) start-p (:start-p last-track) @@ -977,13 +1158,16 @@ :type :row :value (dm/str (inc (count row-tracks))) :zoom zoom}]] - [:& resize-track-handler - {:index (count row-tracks) - :last? true - :shape shape - :layout-data layout-data - :start-p end-p - :type :row - :track-before (last row-tracks) - :snap-pixel? snap-pixel? - :zoom zoom}]]))])]))) + (let [drop? (and (= :row @drop-track-type*) + (= (count row-tracks) @drop-track-target*))] + [:& resize-track-handler + {:index (count row-tracks) + :last? true + :drop? drop? + :shape shape + :layout-data layout-data + :start-p end-p + :type :row + :track-before (last row-tracks) + :snap-pixel? snap-pixel? + :zoom zoom}])]))])]))) diff --git a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.scss b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.scss index c519a0356d..497bc2e879 100644 --- a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.scss +++ b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.scss @@ -18,30 +18,35 @@ } .grid-editor-wrapper { + cursor: grab; width: 100%; height: 80%; display: flex; + flex-direction: column; justify-content: center; align-items: center; } +.grid-editor-header-hover { + fill: var(--grid-editor-marker-color); +} + .grid-editor-label { + flex: 1; background: none; - border-bottom: calc($s-1 / var(--zoom)) solid transparent; border: 0; color: var(--grid-editor-marker-text); font-family: worksans; font-size: calc($fs-12 / var(--zoom)); font-weight: 400; margin: 0; - max-width: calc($s-80 / var(--zoom)); + max-width: calc($s-60 / var(--zoom)); padding: 0; - padding: $s-4; + padding: calc($s-4 / var(--zoom)); text-align: center; &:focus { outline: none; - border-bottom: calc($s-1 / var(--zoom)) solid var(--grid-editor-marker-text); } } diff --git a/frontend/src/app/main/ui/workspace/viewport/path_actions.cljs b/frontend/src/app/main/ui/workspace/viewport/path_actions.cljs index 5dcb1b6d39..3f2c7558b3 100644 --- a/frontend/src/app/main/ui/workspace/viewport/path_actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/path_actions.cljs @@ -10,7 +10,6 @@ [app.main.data.workspace.path :as drp] [app.main.data.workspace.path.shortcuts :as sc] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.ui.workspace.shapes.path.common :as pc] [app.util.i18n :as i18n :refer [tr]] @@ -40,7 +39,6 @@ (mf/defc path-actions [{:keys [shape]}] (let [{:keys [edit-mode selected-points snap-toggled] :as all} (mf/deref pc/current-edit-path-ref) content (:content shape) - new-css-system (mf/use-ctx ctx/new-css-system) enabled-buttons (mf/use-memo @@ -111,160 +109,80 @@ (fn [_] (st/emit! (drp/toggle-snap))))] - (if new-css-system - [:div {:class (stl/css :sub-actions)} - [:div {:class (stl/css :sub-actions-group)} + [:div {:class (stl/css :sub-actions)} + [:div {:class (stl/css :sub-actions-group)} - ;; Draw Mode - [:button - {:class (stl/css-case :is-toggled (= edit-mode :draw)) - :title (tr "workspace.path.actions.draw-nodes" (sc/get-tooltip :draw-nodes)) - :on-click on-select-draw-mode} - i/pentool-refactor] + ;; Draw Mode + [:button + {:class (stl/css-case :is-toggled (= edit-mode :draw)) + :title (tr "workspace.path.actions.draw-nodes" (sc/get-tooltip :draw-nodes)) + :on-click on-select-draw-mode} + i/pentool-refactor] - ;; Edit mode - [:button - {:class (stl/css-case :is-toggled (= edit-mode :move)) - :title (tr "workspace.path.actions.move-nodes" (sc/get-tooltip :move-nodes)) - :on-click on-select-edit-mode} - i/move-refactor]] + ;; Edit mode + [:button + {:class (stl/css-case :is-toggled (= edit-mode :move)) + :title (tr "workspace.path.actions.move-nodes" (sc/get-tooltip :move-nodes)) + :on-click on-select-edit-mode} + i/move-refactor]] - [:div {:class (stl/css :sub-actions-group)} - ;; Add Node - [:button - {:disabled (not (:add-node enabled-buttons)) - :title (tr "workspace.path.actions.add-node" (sc/get-tooltip :add-node)) - :on-click on-add-node} - i/add-refactor] + [:div {:class (stl/css :sub-actions-group)} + ;; Add Node + [:button + {:disabled (not (:add-node enabled-buttons)) + :title (tr "workspace.path.actions.add-node" (sc/get-tooltip :add-node)) + :on-click on-add-node} + i/add-refactor] - ;; Remove node - [:button - {:disabled (not (:remove-node enabled-buttons)) - :title (tr "workspace.path.actions.delete-node" (sc/get-tooltip :delete-node)) - :on-click on-remove-node} - i/remove-refactor]] + ;; Remove node + [:button + {:disabled (not (:remove-node enabled-buttons)) + :title (tr "workspace.path.actions.delete-node" (sc/get-tooltip :delete-node)) + :on-click on-remove-node} + i/remove-refactor]] - [:div {:class (stl/css :sub-actions-group)} - ;; Merge Nodes - [:button - {:disabled (not (:merge-nodes enabled-buttons)) - :title (tr "workspace.path.actions.merge-nodes" (sc/get-tooltip :merge-nodes)) - :on-click on-merge-nodes} - i/merge-nodes-refactor] + [:div {:class (stl/css :sub-actions-group)} + ;; Merge Nodes + [:button + {:disabled (not (:merge-nodes enabled-buttons)) + :title (tr "workspace.path.actions.merge-nodes" (sc/get-tooltip :merge-nodes)) + :on-click on-merge-nodes} + i/merge-nodes-refactor] - ;; Join Nodes - [:button - {:disabled (not (:join-nodes enabled-buttons)) - :title (tr "workspace.path.actions.join-nodes" (sc/get-tooltip :join-nodes)) - :on-click on-join-nodes} - i/join-nodes-refactor] + ;; Join Nodes + [:button + {:disabled (not (:join-nodes enabled-buttons)) + :title (tr "workspace.path.actions.join-nodes" (sc/get-tooltip :join-nodes)) + :on-click on-join-nodes} + i/join-nodes-refactor] - ;; Separate Nodes - [:button - {:disabled (not (:separate-nodes enabled-buttons)) - :title (tr "workspace.path.actions.separate-nodes" (sc/get-tooltip :separate-nodes)) - :on-click on-separate-nodes} - i/separate-nodes-refactor]] + ;; Separate Nodes + [:button + {:disabled (not (:separate-nodes enabled-buttons)) + :title (tr "workspace.path.actions.separate-nodes" (sc/get-tooltip :separate-nodes)) + :on-click on-separate-nodes} + i/separate-nodes-refactor]] - ;; Make Corner - [:div {:class (stl/css :sub-actions-group)} - [:button - {:disabled (not (:make-corner enabled-buttons)) - :title (tr "workspace.path.actions.make-corner" (sc/get-tooltip :make-corner)) - :on-click on-make-corner} - i/to-corner-refactor] + ;; Make Corner + [:div {:class (stl/css :sub-actions-group)} + [:button + {:disabled (not (:make-corner enabled-buttons)) + :title (tr "workspace.path.actions.make-corner" (sc/get-tooltip :make-corner)) + :on-click on-make-corner} + i/to-corner-refactor] - ;; Make Curve - [:button - {:disabled (not (:make-curve enabled-buttons)) - :title (tr "workspace.path.actions.make-curve" (sc/get-tooltip :make-curve)) - :on-click on-make-curve} - i/to-curve-refactor]] + ;; Make Curve + [:button + {:disabled (not (:make-curve enabled-buttons)) + :title (tr "workspace.path.actions.make-curve" (sc/get-tooltip :make-curve)) + :on-click on-make-curve} + i/to-curve-refactor]] - ;; Toggle snap - [:div {:class (stl/css :sub-actions-group)} - [:button - {:class (stl/css-case :is-toggled snap-toggled) - :title (tr "workspace.path.actions.snap-nodes" (sc/get-tooltip :snap-nodes)) - :on-click on-toggle-snap} - i/snap-nodes-refactor]]] - - - - [:div.path-actions - [:div.viewport-actions-group - - ;; Draw Mode - [:div.viewport-actions-entry.tooltip.tooltip-bottom - {:class (when (= edit-mode :draw) "is-toggled") - :alt (tr "workspace.path.actions.draw-nodes" (sc/get-tooltip :draw-nodes)) - :on-click on-select-draw-mode} - i/pen] - - ;; Edit mode - [:div.viewport-actions-entry.tooltip.tooltip-bottom - {:class (when (= edit-mode :move) "is-toggled") - :alt (tr "workspace.path.actions.move-nodes" (sc/get-tooltip :move-nodes)) - :on-click on-select-edit-mode} - i/pointer-inner]] - - [:div.viewport-actions-group - ;; Add Node - [:div.viewport-actions-entry.tooltip.tooltip-bottom - {:class (when-not (:add-node enabled-buttons) "is-disabled") - :alt (tr "workspace.path.actions.add-node" (sc/get-tooltip :add-node)) - :on-click on-add-node} - i/nodes-add] - - ;; Remove node - [:div.viewport-actions-entry.tooltip.tooltip-bottom - {:class (when-not (:remove-node enabled-buttons) "is-disabled") - :alt (tr "workspace.path.actions.delete-node" (sc/get-tooltip :delete-node)) - :on-click on-remove-node} - i/nodes-remove]] - - [:div.viewport-actions-group - ;; Merge Nodes - [:div.viewport-actions-entry.tooltip.tooltip-bottom - {:class (when-not (:merge-nodes enabled-buttons) "is-disabled") - :alt (tr "workspace.path.actions.merge-nodes" (sc/get-tooltip :merge-nodes)) - :on-click on-merge-nodes} - i/nodes-merge] - - ;; Join Nodes - [:div.viewport-actions-entry.tooltip.tooltip-bottom - {:class (when-not (:join-nodes enabled-buttons) "is-disabled") - :alt (tr "workspace.path.actions.join-nodes" (sc/get-tooltip :join-nodes)) - :on-click on-join-nodes} - i/nodes-join] - - ;; Separate Nodes - [:div.viewport-actions-entry.tooltip.tooltip-bottom - {:class (when-not (:separate-nodes enabled-buttons) "is-disabled") - :alt (tr "workspace.path.actions.separate-nodes" (sc/get-tooltip :separate-nodes)) - :on-click on-separate-nodes} - i/nodes-separate]] - - ;; Make Corner - [:div.viewport-actions-group - [:div.viewport-actions-entry.tooltip.tooltip-bottom - {:class (when-not (:make-corner enabled-buttons) "is-disabled") - :alt (tr "workspace.path.actions.make-corner" (sc/get-tooltip :make-corner)) - :on-click on-make-corner} - i/nodes-corner] - - ;; Make Curve - [:div.viewport-actions-entry.tooltip.tooltip-bottom - {:class (when-not (:make-curve enabled-buttons) "is-disabled") - :alt (tr "workspace.path.actions.make-curve" (sc/get-tooltip :make-curve)) - :on-click on-make-curve} - i/nodes-curve]] - - ;; Toggle snap - [:div.viewport-actions-group - [:div.viewport-actions-entry.tooltip.tooltip-bottom - {:class (when snap-toggled "is-toggled") - :alt (tr "workspace.path.actions.snap-nodes" (sc/get-tooltip :snap-nodes)) - :on-click on-toggle-snap} - i/nodes-snap]]]))) + ;; Toggle snap + [:div {:class (stl/css :sub-actions-group)} + [:button + {:class (stl/css-case :is-toggled snap-toggled) + :title (tr "workspace.path.actions.snap-nodes" (sc/get-tooltip :snap-nodes)) + :on-click on-toggle-snap} + i/snap-nodes-refactor]]])) diff --git a/frontend/src/app/main/ui/workspace/viewport/pixel_overlay.cljs b/frontend/src/app/main/ui/workspace/viewport/pixel_overlay.cljs index fb295276f0..babee74956 100644 --- a/frontend/src/app/main/ui/workspace/viewport/pixel_overlay.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/pixel_overlay.cljs @@ -13,7 +13,6 @@ [app.main.fonts :as fonts] [app.main.rasterizer :as thr] [app.main.store :as st] - [app.main.ui.context :as ctx] [app.main.ui.css-cursors :as cur] [app.util.dom :as dom] [app.util.keyboard :as kbd] @@ -51,8 +50,7 @@ (mf/defc pixel-overlay {::mf/wrap-props false} [props] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - vport (unchecked-get props "vport") + (let [vport (unchecked-get props "vport") viewport-ref (unchecked-get props "viewport-ref") viewport-node (mf/ref-val viewport-ref) @@ -81,8 +79,8 @@ (when-let [zoom-view-node (dom/get-element "picker-detail")] (when-not (mf/ref-val zoom-view-context) (mf/set-ref-val! zoom-view-context (.getContext zoom-view-node "2d"))) - (let [canvas-width (if new-css-system 260 200) - canvas-height (if new-css-system 140 160) + (let [canvas-width 260 + canvas-height 140 {brx :left bry :top} (dom/get-bounding-rect viewport-node) x (mth/floor (- (.-clientX event) brx)) @@ -100,10 +98,10 @@ ;; I don't know why, but the zoom view is offset by 24px ;; instead of 25. - sx (- x (if new-css-system 32 24)) - sy (- y (if new-css-system 17 20)) - sw (if new-css-system 65 50) - sh (if new-css-system 35 40) + sx (- x 32) + sy (- y 17) + sw 65 + sh 35 dx 0 dy 0 dw canvas-width diff --git a/frontend/src/app/main/ui/workspace/viewport/rules.cljs b/frontend/src/app/main/ui/workspace/viewport/rules.cljs index 87ec78766f..af978fef0f 100644 --- a/frontend/src/app/main/ui/workspace/viewport/rules.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/rules.cljs @@ -6,12 +6,10 @@ (ns app.main.ui.workspace.viewport.rules (:require - [app.common.colors :as colors] [app.common.data :as d] [app.common.data.macros :as dm] [app.common.geom.shapes :as gsh] [app.common.math :as mth] - [app.main.ui.context :as ctx] [app.main.ui.formats :as fmt] [app.main.ui.hooks :as hooks] [app.util.object :as obj] @@ -22,8 +20,7 @@ (def rules-width 1) (def rule-area-size 22) (def rule-area-half-size (/ rule-area-size 2)) -(def rules-background "var(--color-gray-50)") -(def new-css-rules-background "var(--panel-background-color)") +(def rules-background "var(--panel-background-color)") (def selection-area-color "var(--color-primary)") (def selection-area-opacity 0.3) (def over-number-size 50) @@ -31,9 +28,8 @@ (def font-size 12) (def font-family "worksans") -(def font-color colors/gray-30) -(def new-css-font-color "var(--layer-row-foreground-color)") -(def new-css-canvas-border-radius 12) +(def font-color "var(--layer-row-foreground-color)") +(def canvas-border-radius 12) ;; ---------------- ;; RULES @@ -156,9 +152,8 @@ (let [rules-width (* rules-width zoom-inverse) step (calculate-step-size zoom) clip-id (str "clip-rule-" (d/name axis)) - new-css-system (mf/use-ctx ctx/new-css-system) - font-color (if new-css-system new-css-font-color font-color) - rules-background (if new-css-system new-css-rules-background rules-background)] + font-color font-color + rules-background rules-background] [:* (let [{:keys [x y width height]} (get-background-area vbox zoom-inverse axis)] @@ -207,8 +202,7 @@ (mf/defc selection-area [{:keys [vbox zoom-inverse selection-rect offset-x offset-y]}] ;; When using the format-number callls we consider if the guide is associated to a frame and we show the position relative to it with the offset - (let [new-css-system (mf/use-ctx ctx/new-css-system) - rules-background (if new-css-system new-css-rules-background rules-background)] + (let [rules-background rules-background] [:g.selection-area [:g [:rect {:x (:x selection-rect) @@ -306,9 +300,8 @@ (hooks/use-equal-memo)) show-rules? (obj/get props "show-rules?") - new-css-system (mf/use-ctx ctx/new-css-system) - rules-background (if new-css-system new-css-rules-background rules-background) - border-radius (/ new-css-canvas-border-radius zoom) + rules-background rules-background + border-radius (/ canvas-border-radius zoom) selection-rect (mf/use-memo @@ -324,21 +317,20 @@ [:& rules-axis {:zoom zoom :zoom-inverse zoom-inverse :vbox vbox :axis :y :offset offset-y}]]) ;; Draw the rules' rounded corners in the viewport corners - (when new-css-system - (let [{:keys [x y width height]} vbox - rule-area-size (if show-rules? (/ rule-area-size zoom) 0)] - [:* - [:path {:d (round-corner-path-tl (+ x rule-area-size) (+ y rule-area-size) border-radius) - :style {:fill rules-background}}] + (let [{:keys [x y width height]} vbox + rule-area-size (if show-rules? (/ rule-area-size zoom) 0)] + [:* + [:path {:d (round-corner-path-tl (+ x rule-area-size) (+ y rule-area-size) border-radius) + :style {:fill rules-background}}] - [:path {:d (round-corner-path-tr (+ x width (- border-radius)) (+ y rule-area-size) border-radius) - :style {:fill rules-background}}] + [:path {:d (round-corner-path-tr (+ x width (- border-radius)) (+ y rule-area-size) border-radius) + :style {:fill rules-background}}] - [:path {:d (round-corner-path-bl (+ x rule-area-size) (+ y height (- border-radius)) border-radius) - :style {:fill rules-background}}] + [:path {:d (round-corner-path-bl (+ x rule-area-size) (+ y height (- border-radius)) border-radius) + :style {:fill rules-background}}] - [:path {:d (round-corner-path-br (+ x (:width vbox) (- border-radius)) (+ y height (- border-radius)) border-radius) - :style {:fill rules-background}}]])) + [:path {:d (round-corner-path-br (+ x (:width vbox) (- border-radius)) (+ y height (- border-radius)) border-radius) + :style {:fill rules-background}}]]) (when (and show-rules? (some? selection-rect)) [:& selection-area {:zoom zoom diff --git a/frontend/src/app/main/ui/workspace/viewport/top_bar.cljs b/frontend/src/app/main/ui/workspace/viewport/top_bar.cljs index c3efb32784..04eeb79d1f 100644 --- a/frontend/src/app/main/ui/workspace/viewport/top_bar.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/top_bar.cljs @@ -13,7 +13,6 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.context :as ctx] - [app.main.ui.icons :as i] [app.main.ui.workspace.viewport.grid-layout-editor :refer [grid-edition-actions]] [app.main.ui.workspace.viewport.path-actions :refer [path-actions]] [app.util.i18n :as i18n :refer [tr]] @@ -21,30 +20,19 @@ (mf/defc view-only-actions [] - (let [new-css-system (mf/use-ctx ctx/new-css-system) - handle-close-view-mode + (let [handle-close-view-mode (mf/use-callback (fn [] (st/emit! :interrupt (dw/set-options-mode :design) (dw/set-workspace-read-only false))))] - (if new-css-system - [:div {:class (stl/css :viewport-actions)} - [:div {:class (stl/css :viewport-actions-container)} - [:div {:class (stl/css :viewport-actions-title)} - [:& i18n/tr-html {:tag-name "span" - :label "workspace.top-bar.read-only"}]] - [:button {:class (stl/css :done-btn) - :on-click handle-close-view-mode} (tr "workspace.top-bar.read-only.done")]]] - - ;; OLD - [:div.viewport-actions - [:div.viewport-actions-container - [:div.viewport-actions-title - [:& i18n/tr-html {:tag-name "span" - :label "workspace.top-bar.read-only"}]] - [:button.btn-primary {:on-click handle-close-view-mode} (tr "workspace.top-bar.read-only.done")] - [:button.btn-icon-basic {:on-click handle-close-view-mode} i/close]]]))) + [:div {:class (stl/css :viewport-actions)} + [:div {:class (stl/css :viewport-actions-container)} + [:div {:class (stl/css :viewport-actions-title)} + [:& i18n/tr-html {:tag-name "span" + :label "workspace.top-bar.read-only"}]] + [:button {:class (stl/css :done-btn) + :on-click handle-close-view-mode} (tr "workspace.top-bar.read-only.done")]]])) (mf/defc top-bar {::mf/wrap [mf/memo]} diff --git a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs index 58fde157b7..ec646a9665 100644 --- a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs @@ -5,6 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.workspace.viewport.widgets + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] @@ -251,12 +252,14 @@ :width 100000 :height 24 :transform (vwu/text-transform flow-pos zoom)} - [:div.flow-badge {:class (dom/classnames :selected selected?)} - [:div.content {:on-pointer-down on-pointer-down + [:div {:class (stl/css-case :flow-badge true + :selected selected?)} + [:div {:class (stl/css :content) + :on-pointer-down on-pointer-down :on-double-click on-double-click :on-pointer-enter on-pointer-enter :on-pointer-leave on-pointer-leave} - i/play + i/play-refactor [:span (:name flow)]]]])) (mf/defc frame-flows diff --git a/frontend/src/app/main/ui/workspace/viewport/widgets.scss b/frontend/src/app/main/ui/workspace/viewport/widgets.scss new file mode 100644 index 0000000000..2fc2a81daf --- /dev/null +++ b/frontend/src/app/main/ui/workspace/viewport/widgets.scss @@ -0,0 +1,64 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.flow-element { + display: flex; + align-items: center; + + .element-label { + } + + .flow-name { + cursor: pointer; + } + + & input.element-name { + background: transparent; + } +} + +.flow-badge { + cursor: pointer; + display: flex; + .content { + @include titleTipography; + display: flex; + align-items: center; + height: $s-24; + border-radius: $br-6; + background-color: var(--flow-tag-background-color); + svg { + @extend .button-icon; + height: $s-24; + width: $s-12; + stroke: var(--icon-foreground); + margin: 0 $s-8; + } + + span { + height: 100%; + display: flex; + align-items: center; + justify-content: center; + margin-right: $s-8; + color: var(--flow-tag-foreground-color); + } + } + + &.selected .content, + &:hover .content { + background-color: var(--flow-tag-background-color-hover); + svg { + stroke: var(--flow-tag-foreground-color-hover); + } + + span { + color: var(--flow-tag-foreground-color-hover); + } + } +} diff --git a/frontend/src/app/rasterizer.cljs b/frontend/src/app/rasterizer.cljs index d6eeb474ca..157b57a653 100644 --- a/frontend/src/app/rasterizer.cljs +++ b/frontend/src/app/rasterizer.cljs @@ -96,10 +96,16 @@ :method :get :mode :cors :omit-default-headers true}) - (rx/map :body) - (rx/mapcat wapi/read-file-as-data-url) - (rx/tap (fn [data-uri] - (.set data-uri-cache uri (wapi/create-blob data-uri "text/plain"))))))) + (rx/catch (fn [cause] + (log/error :hint "fetching data uri" + :cause cause) + (rx/of nil))) + (rx/mapcat (fn [response] + (if (nil? response) + (rx/of uri) + (->> (rx/of (:body response)) + (rx/mapcat wapi/read-file-as-data-url) + (rx/tap (fn [data-uri] (.set data-uri-cache uri (wapi/create-blob data-uri "text/plain"))))))))))) (defn- svg-update-image! "Updates an image in an SVG to a Data URI." @@ -128,27 +134,53 @@ (dom/append-child! style (dom/create-text svg styles)) (dom/append-child! doc style))) -(defn- svg-resolve-styles! - "Resolves all fonts in an SVG to Data URIs." - [svg styles] +(defn- svg-resolve-external-resources + "Resolves all external resources in an SVG to Data URIs." + [styles] (->> (rx/from (re-seq #"url\((https?://[^)]+)\)" styles)) (rx/map second) (rx/mapcat (fn [url] - (->> (fetch-as-data-uri url) - (rx/map (fn [uri] [url uri]))))) - + (->> (fetch-as-data-uri url) + (rx/map (fn [uri] [url uri]))))) (rx/reduce (fn [styles [url uri]] (str/replace styles url uri)) - styles) + styles))) + +(defn- svg-resolve-styles! + "Resolves all fonts in an SVG to Data URIs." + [svg styles] + (->> (svg-resolve-external-resources styles) (rx/tap (partial svg-add-style! svg)) (rx/ignore))) +(defn- svg-resolve-inline-styles! + "Resolves all inline styles in an SVG to Data URIs." + [svg] + (->> (rx/from (dom/query-all svg "[style]")) + (rx/mapcat (fn [node] + (let [styles (dom/get-attribute node "style")] + (->> (svg-resolve-external-resources styles) + (rx/tap (fn [styles] (dom/set-attribute! node "style" styles))))))) + (rx/ignore))) + +(defn- svg-resolve-style-elements! + "Resolves all style elements in an SVG to Data URIs." + [svg] + (->> (rx/from (dom/query-all svg "style")) + (rx/mapcat (fn [node] + (let [styles (dom/get-text node)] + (->> (svg-resolve-external-resources styles) + (rx/tap (fn [styles] (dom/set-text! node styles))))))) + (rx/ignore))) + (defn- svg-resolve-all! "Resolves all images and fonts in an SVG to Data URIs." [svg styles] (rx/concat (svg-resolve-images! svg) (svg-resolve-styles! svg styles) + (svg-resolve-inline-styles! svg) + (svg-resolve-style-elements! svg) (rx/of svg))) (defn- svg-parse diff --git a/frontend/src/app/util/debug.cljs b/frontend/src/app/util/debug.cljs index 7fecae2192..7bf6304bde 100644 --- a/frontend/src/app/util/debug.cljs +++ b/frontend/src/app/util/debug.cljs @@ -75,6 +75,9 @@ ;; :grid-layout + + ;; Show an overlay to the grid cells to know its properties + :grid-cells }) (defn enable! diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs index 8b04784789..998a581ebc 100644 --- a/frontend/src/app/util/dom.cljs +++ b/frontend/src/app/util/dom.cljs @@ -504,6 +504,10 @@ (.setAttribute node property value)) node) +(defn get-text [^js node] + (when (some? node) + (.-textContent node))) + (defn set-text! [^js node text] (when (some? node) (set! (.-textContent node) text)) diff --git a/frontend/src/debug.cljs b/frontend/src/debug.cljs index 4b1058f618..02964dcabe 100644 --- a/frontend/src/debug.cljs +++ b/frontend/src/debug.cljs @@ -21,7 +21,7 @@ [app.main.data.preview :as dp] [app.main.data.viewer.shortcuts] [app.main.data.workspace :as dw] - [app.main.data.workspace.changes :as dwc] + [app.main.data.workspace.changes :as dch] [app.main.data.workspace.path.shortcuts] [app.main.data.workspace.selection :as dws] [app.main.data.workspace.shortcuts] @@ -297,7 +297,7 @@ (let [file-id (:current-file-id @st/state) changes (t/decode-str changes*)] - (st/emit! (dwc/commit-changes {:redo-changes changes + (st/emit! (dch/commit-changes {:redo-changes changes :undo-changes [] :save-undo? true :file-id file-id})))) diff --git a/frontend/translations/en.po b/frontend/translations/en.po index f1206c084c..1d1addfc4a 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -5057,3 +5057,43 @@ msgstr "Choose image" msgid "workspace.options.guides.title" msgstr "Guides" + +msgid "workspace.context-menu.grid-track.column.duplicate" +msgstr "Duplicate column" + +msgid "workspace.context-menu.grid-track.column.add-before" +msgstr "Add 1 column to the left" + +msgid "workspace.context-menu.grid-track.column.add-after" +msgstr "Add 1 column to the right" + +msgid "workspace.context-menu.grid-track.column.delete" +msgstr "Delete column" + +msgid "workspace.context-menu.grid-track.column.delete-shapes" +msgstr "Delete column and shapes" + +msgid "workspace.context-menu.grid-track.row.duplicate" +msgstr "Duplicate row" + +msgid "workspace.context-menu.grid-track.row.add-before" +msgstr "Add 1 row above" + +msgid "workspace.context-menu.grid-track.row.add-after" +msgstr "Add 1 row below" + +msgid "workspace.context-menu.grid-track.row.delete" +msgstr "Delete row" + +msgid "workspace.context-menu.grid-track.row.delete-shapes" +msgstr "Delete row and shapes" + +msgid "workspace.context-menu.grid-cells.merge" +msgstr "Merge cells" + +msgid "workspace.context-menu.grid-cells.area" +msgstr "Create area" + +msgid "workspace.context-menu.grid-cells.create-board" +msgstr "Create board" + diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 7b4e5b85ed..8845dc4848 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -5146,3 +5146,43 @@ msgstr "Elegir imagen" msgid "workspace.options.guides.title" msgstr "Guías" + +msgid "workspace.context-menu.grid-track.column.duplicate" +msgstr "Duplicar columna" + +msgid "workspace.context-menu.grid-track.column.add-before" +msgstr "Añadir 1 columna a la izquierda" + +msgid "workspace.context-menu.grid-track.column.add-after" +msgstr "Añadir 1 columna a la derecha" + +msgid "workspace.context-menu.grid-track.column.delete" +msgstr "Borrar columna" + +msgid "workspace.context-menu.grid-track.column.delete-shapes" +msgstr "Borrar columna con el contenido" + +msgid "workspace.context-menu.grid-track.row.duplicate" +msgstr "Duplicar fila" + +msgid "workspace.context-menu.grid-track.row.add-before" +msgstr "Añadir 1 fila encima" + +msgid "workspace.context-menu.grid-track.row.add-after" +msgstr "Añadir 1 fila debajo" + +msgid "workspace.context-menu.grid-track.row.delete" +msgstr "Borrar fila" + +msgid "workspace.context-menu.grid-track.row.delete-shapes" +msgstr "Borrar fila con el contenido" + +msgid "workspace.context-menu.grid-cells.merge" +msgstr "Fusionar celdas" + +msgid "workspace.context-menu.grid-cells.area" +msgstr "Crear area" + +msgid "workspace.context-menu.grid-cells.create-board" +msgstr "Crear tablero" +