mirror of
https://github.com/penpot/penpot.git
synced 2025-07-04 15:27:17 +02:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
8a81bc11e0
19 changed files with 201 additions and 118 deletions
|
@ -14,6 +14,7 @@
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.db.sql :as sql]
|
[app.db.sql :as sql]
|
||||||
|
[app.features.components-v2 :as feat.compv2]
|
||||||
[app.features.fdata :as fdata]
|
[app.features.fdata :as fdata]
|
||||||
[app.rpc :as-alias rpc]
|
[app.rpc :as-alias rpc]
|
||||||
[app.rpc.commands.files :as files]
|
[app.rpc.commands.files :as files]
|
||||||
|
@ -46,22 +47,26 @@
|
||||||
[cfg {:keys [::rpc/profile-id project-id] :as params}]
|
[cfg {:keys [::rpc/profile-id project-id] :as params}]
|
||||||
(db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}]
|
(db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}]
|
||||||
(projects/check-edition-permissions! conn profile-id project-id)
|
(projects/check-edition-permissions! conn profile-id project-id)
|
||||||
(let [team (teams/get-team conn
|
(let [team (teams/get-team conn :profile-id profile-id :project-id project-id)
|
||||||
:profile-id profile-id
|
|
||||||
:project-id project-id)
|
|
||||||
|
|
||||||
;; When we create files, we only need to respect the team
|
;; When we create files, we only need to respect the team
|
||||||
;; features, because some features can be enabled
|
;; features, because some features can be enabled
|
||||||
;; globally, but the team is still not migrated properly.
|
;; globally, but the team is still not migrated properly.
|
||||||
features (-> (cfeat/get-team-enabled-features cf/flags team)
|
input-features (:features params #{})
|
||||||
(cfeat/check-client-features! (:features params)))
|
|
||||||
|
;; If the imported project doesn't contain v2 we need to remove it
|
||||||
|
team-features
|
||||||
|
(cond-> (cfeat/get-team-enabled-features cf/flags team)
|
||||||
|
(not (contains? input-features "components/v2"))
|
||||||
|
(disj "components/v2"))
|
||||||
|
|
||||||
|
|
||||||
;; We also include all no migration features declared by
|
;; We also include all no migration features declared by
|
||||||
;; client; that enables the ability to enable a runtime
|
;; client; that enables the ability to enable a runtime
|
||||||
;; feature on frontend and make it permanent on file
|
;; feature on frontend and make it permanent on file
|
||||||
features (-> (:features params #{})
|
features (-> input-features
|
||||||
(set/intersection cfeat/no-migration-features)
|
(set/intersection cfeat/no-migration-features)
|
||||||
(set/union features))
|
(set/union team-features))
|
||||||
|
|
||||||
params (-> params
|
params (-> params
|
||||||
(assoc :profile-id profile-id)
|
(assoc :profile-id profile-id)
|
||||||
|
@ -100,7 +105,7 @@
|
||||||
;; --- MUTATION COMMAND: persist-temp-file
|
;; --- MUTATION COMMAND: persist-temp-file
|
||||||
|
|
||||||
(defn persist-temp-file
|
(defn persist-temp-file
|
||||||
[{:keys [::db/conn] :as cfg} {:keys [id] :as params}]
|
[{:keys [::db/conn] :as cfg} {:keys [id ::rpc/profile-id] :as params}]
|
||||||
(let [file (files/get-file cfg id
|
(let [file (files/get-file cfg id
|
||||||
:migrate? false
|
:migrate? false
|
||||||
:lock-for-update? true)]
|
:lock-for-update? true)]
|
||||||
|
@ -109,6 +114,7 @@
|
||||||
(ex/raise :type :validation
|
(ex/raise :type :validation
|
||||||
:code :cant-persist-already-persisted-file))
|
:code :cant-persist-already-persisted-file))
|
||||||
|
|
||||||
|
|
||||||
(let [changes (->> (db/cursor conn
|
(let [changes (->> (db/cursor conn
|
||||||
(sql/select :file-change {:file-id id}
|
(sql/select :file-change {:file-id id}
|
||||||
{:order-by [[:revn :asc]]})
|
{:order-by [[:revn :asc]]})
|
||||||
|
@ -133,6 +139,19 @@
|
||||||
:revn 1
|
:revn 1
|
||||||
:data (blob/encode (:data file))}
|
:data (blob/encode (:data file))}
|
||||||
{:id id})
|
{:id id})
|
||||||
|
|
||||||
|
(let [team (teams/get-team conn :profile-id profile-id :project-id (:project-id file))
|
||||||
|
file-features (:features file)
|
||||||
|
team-features (cfeat/get-team-enabled-features cf/flags team)]
|
||||||
|
(when (and (contains? team-features "components/v2")
|
||||||
|
(not (contains? file-features "components/v2")))
|
||||||
|
;; Migrate components v2
|
||||||
|
(feat.compv2/migrate-file! cfg
|
||||||
|
(:id file)
|
||||||
|
:max-procs 2
|
||||||
|
:validate? true
|
||||||
|
:throw-on-validate? true)))
|
||||||
|
|
||||||
nil)))
|
nil)))
|
||||||
|
|
||||||
(def ^:private schema:persist-temp-file
|
(def ^:private schema:persist-temp-file
|
||||||
|
|
|
@ -524,7 +524,10 @@
|
||||||
(dissoc :main-instance-y))
|
(dissoc :main-instance-y))
|
||||||
|
|
||||||
obj (-> (cts/setup-shape attrs)
|
obj (-> (cts/setup-shape attrs)
|
||||||
(check-name file root-type))]
|
(check-name file root-type)
|
||||||
|
;; Components need to have nil values for frame and parent
|
||||||
|
(assoc :frame-id nil)
|
||||||
|
(assoc :parent-id nil))]
|
||||||
|
|
||||||
(-> file
|
(-> file
|
||||||
(commit-change
|
(commit-change
|
||||||
|
@ -537,19 +540,24 @@
|
||||||
:shapes [obj]})
|
:shapes [obj]})
|
||||||
|
|
||||||
(assoc :last-id (:id obj))
|
(assoc :last-id (:id obj))
|
||||||
(update :parent-stack conjv (:id obj))
|
(assoc :parent-stack [(:id obj)])
|
||||||
(assoc :current-component-id (:id obj))
|
(assoc :current-component-id (:id obj))
|
||||||
(assoc :current-frame-id (when (= (:type obj) :frame)
|
(assoc :current-frame-id (if (= (:type obj) :frame) (:id obj) uuid/zero))))))
|
||||||
(:id obj)))))))
|
|
||||||
|
|
||||||
(defn finish-component
|
(defn finish-component
|
||||||
[file]
|
[file]
|
||||||
(let [component-id (:current-component-id file)
|
(let [component-id (:current-component-id file)
|
||||||
|
component-data (ctkl/get-component (:data file) component-id)
|
||||||
|
|
||||||
component (lookup-shape file component-id)
|
component (lookup-shape file component-id)
|
||||||
children (->> component :shapes (mapv #(lookup-shape file %)))
|
children (->> component :shapes (mapv #(lookup-shape file %)))
|
||||||
|
|
||||||
file
|
file
|
||||||
(cond
|
(cond
|
||||||
|
;; Components-v2 component we skip this step
|
||||||
|
(and component-data (:main-instance-id component-data))
|
||||||
|
file
|
||||||
|
|
||||||
(empty? children)
|
(empty? children)
|
||||||
(commit-change
|
(commit-change
|
||||||
file
|
file
|
||||||
|
|
|
@ -406,10 +406,9 @@
|
||||||
height: $s-16;
|
height: $s-16;
|
||||||
min-width: $s-16;
|
min-width: $s-16;
|
||||||
min-height: $s-16;
|
min-height: $s-16;
|
||||||
border-radius: $br-6;
|
background-color: var(--input-checkbox-background-color-rest);
|
||||||
background-color: var(--input-checkbox-inactive-background-color);
|
border: $s-1 solid var(--input-checkbox-border-color-rest);
|
||||||
border-radius: $br-4;
|
border-radius: $br-4;
|
||||||
box-shadow: inset 0 0 0px 2px rgb(255 255 255 / 20%);
|
|
||||||
svg {
|
svg {
|
||||||
width: $s-16;
|
width: $s-16;
|
||||||
height: $s-16;
|
height: $s-16;
|
||||||
|
@ -422,22 +421,25 @@
|
||||||
&:focus {
|
&:focus {
|
||||||
border-color: var(--input-checkbox-border-color-focus);
|
border-color: var(--input-checkbox-border-color-focus);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:global(.checked) {
|
&:global(.checked) {
|
||||||
border-color: var(--input-checkbox-border-color-active);
|
border-color: var(--input-checkbox-border-color-active);
|
||||||
background-color: var(--input-checkbox-active-background-color);
|
background-color: var(--input-checkbox-background-color-active);
|
||||||
svg {
|
svg {
|
||||||
@extend .button-icon-small;
|
@extend .button-icon-small;
|
||||||
stroke: var(--input-checkbox-active-foreground-color);
|
stroke: var(--input-checkbox-foreground-color-active);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:global(.intermediate) {
|
&:global(.intermediate) {
|
||||||
background-color: var(--input-checkbox-background-color-intermediate);
|
background-color: var(--input-checkbox-background-color-intermediate);
|
||||||
border-color: var(--input-checkbox-border-color-active);
|
border-color: var(--input-checkbox-border-color-intermediate);
|
||||||
svg {
|
svg {
|
||||||
@extend .button-icon-small;
|
@extend .button-icon-small;
|
||||||
stroke: var(--input-details-color);
|
stroke: var(--input-checkbox-foreground-color-intermediate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:global(.unchecked) {
|
&:global(.unchecked) {
|
||||||
background-color: var(--input-checkbox-background-color-rest);
|
background-color: var(--input-checkbox-background-color-rest);
|
||||||
border: $s-1 solid var(--input-checkbox-background-color-rest);
|
border: $s-1 solid var(--input-checkbox-background-color-rest);
|
||||||
|
@ -468,6 +470,13 @@
|
||||||
border-color: var(--input-checkbox-border-color-hover);
|
border-color: var(--input-checkbox-border-color-hover);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&:focus-within {
|
||||||
|
span {
|
||||||
|
border-color: var(--input-checkbox-border-color-focus);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -159,18 +159,22 @@
|
||||||
--input-border-color-error: var(--error-color);
|
--input-border-color-error: var(--error-color);
|
||||||
--input-border-color-success: var(--color-accent-primary);
|
--input-border-color-success: var(--color-accent-primary);
|
||||||
--input-details-color: var(--color-background-primary);
|
--input-details-color: var(--color-background-primary);
|
||||||
|
|
||||||
--input-checkbox-background-color-rest: var(--color-background-quaternary);
|
--input-checkbox-background-color-rest: var(--color-background-quaternary);
|
||||||
|
--input-checkbox-border-color-rest: var(--color-foreground-secondary);
|
||||||
--input-checkbox-border-color-active: var(--color-background-quaternary);
|
--input-checkbox-border-color-active: var(--color-background-quaternary);
|
||||||
--input-checkbox-border-color-focus: var(--color-accent-primary);
|
--input-checkbox-border-color-focus: var(--color-accent-primary);
|
||||||
--input-checkbox-border-color: var(--color-background-secondary);
|
--input-checkbox-border-color: var(--color-background-secondary);
|
||||||
--input-checkbox-border-color-hover: var(--color-accent-primary-muted);
|
--input-checkbox-border-color-hover: var(--color-accent-primary-muted);
|
||||||
--input-checkbox-background-color-intermediate: var(--color-foreground-secondary);
|
--input-checkbox-background-color-intermediate: var(--color-foreground-secondary);
|
||||||
|
--input-checkbox-border-color-intermediate: var(--color-foreground-secondary);
|
||||||
|
--input-checkbox-foreground-color-intermediate: var(--color-background-secondary);
|
||||||
|
|
||||||
// Checkbox color
|
// Checkbox color
|
||||||
--input-checkbox-inactive-background-color: var(--color-foreground-secondary);
|
--input-checkbox-inactive-background-color: var(--color-foreground-secondary);
|
||||||
--input-checkbox-inactive-foreground-color: var(--color-background-primary);
|
--input-checkbox-inactive-foreground-color: var(--color-background-primary);
|
||||||
--input-checkbox-active-background-color: var(--color-accent-primary);
|
--input-checkbox-background-color-active: var(--color-accent-primary);
|
||||||
--input-checkbox-active-foreground-color: var(--color-background-primary);
|
--input-checkbox-foreground-color-active: var(--color-background-primary);
|
||||||
--input-checkbox-text-foreground-color: var(--color-foreground-secondary);
|
--input-checkbox-text-foreground-color: var(--color-foreground-secondary);
|
||||||
|
|
||||||
--menu-background-color: var(--color-background-tertiary);
|
--menu-background-color: var(--color-background-tertiary);
|
||||||
|
|
|
@ -336,7 +336,7 @@
|
||||||
;; used to render thumbnails on assets panel.
|
;; used to render thumbnails on assets panel.
|
||||||
(mf/defc component-svg
|
(mf/defc component-svg
|
||||||
{::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]}
|
{::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]}
|
||||||
[{:keys [objects root-shape show-grids? zoom] :or {zoom 1} :as props}]
|
[{:keys [objects root-shape show-grids? zoom class] :or {zoom 1} :as props}]
|
||||||
(when root-shape
|
(when root-shape
|
||||||
(let [root-shape-id (:id root-shape)
|
(let [root-shape-id (:id root-shape)
|
||||||
include-metadata (mf/use-ctx export/include-metadata-ctx)
|
include-metadata (mf/use-ctx export/include-metadata-ctx)
|
||||||
|
@ -373,6 +373,7 @@
|
||||||
:width (ust/format-precision width viewbox-decimal-precision)
|
:width (ust/format-precision width viewbox-decimal-precision)
|
||||||
:height (ust/format-precision height viewbox-decimal-precision)
|
:height (ust/format-precision height viewbox-decimal-precision)
|
||||||
:version "1.1"
|
:version "1.1"
|
||||||
|
:class class
|
||||||
:xmlns "http://www.w3.org/2000/svg"
|
:xmlns "http://www.w3.org/2000/svg"
|
||||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||||
:xmlns:penpot (when include-metadata "https://penpot.app/xmlns")
|
:xmlns:penpot (when include-metadata "https://penpot.app/xmlns")
|
||||||
|
@ -388,7 +389,7 @@
|
||||||
|
|
||||||
(mf/defc component-svg-thumbnail
|
(mf/defc component-svg-thumbnail
|
||||||
{::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]}
|
{::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]}
|
||||||
[{:keys [thumbnail-uri on-error show-grids?
|
[{:keys [thumbnail-uri on-error show-grids? class
|
||||||
objects root-shape zoom] :or {zoom 1} :as props}]
|
objects root-shape zoom] :or {zoom 1} :as props}]
|
||||||
|
|
||||||
(when root-shape
|
(when root-shape
|
||||||
|
@ -421,6 +422,7 @@
|
||||||
:width (ust/format-precision width-zoom viewbox-decimal-precision)
|
:width (ust/format-precision width-zoom viewbox-decimal-precision)
|
||||||
:height (ust/format-precision height-zoom viewbox-decimal-precision)
|
:height (ust/format-precision height-zoom viewbox-decimal-precision)
|
||||||
:version "1.1"
|
:version "1.1"
|
||||||
|
:class class
|
||||||
:xmlns "http://www.w3.org/2000/svg"
|
:xmlns "http://www.w3.org/2000/svg"
|
||||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||||
:fill "none"}
|
:fill "none"}
|
||||||
|
@ -506,7 +508,7 @@
|
||||||
(mf/deps objects)
|
(mf/deps objects)
|
||||||
(fn [] (frame-wrapper-factory objects)))]
|
(fn [] (frame-wrapper-factory objects)))]
|
||||||
|
|
||||||
[:> "symbol" #js {:id (str root-id)
|
[:> "symbol" #js {:id (str (:id component))
|
||||||
:viewBox vbox
|
:viewBox vbox
|
||||||
"penpot:path" path
|
"penpot:path" path
|
||||||
"penpot:main-instance-id" main-instance-id
|
"penpot:main-instance-id" main-instance-id
|
||||||
|
|
|
@ -80,12 +80,13 @@
|
||||||
(assoc :deleted? true))))))
|
(assoc :deleted? true))))))
|
||||||
|
|
||||||
(defn set-analyze-error
|
(defn set-analyze-error
|
||||||
[files uri]
|
[files uri error]
|
||||||
(->> files
|
(->> files
|
||||||
(mapv (fn [file]
|
(mapv (fn [file]
|
||||||
(cond-> file
|
(cond-> file
|
||||||
(= uri (:uri file))
|
(= uri (:uri file))
|
||||||
(assoc :status :analyze-error))))))
|
(-> (assoc :status :analyze-error)
|
||||||
|
(assoc :error error)))))))
|
||||||
|
|
||||||
(defn set-analyze-result [files uri type data]
|
(defn set-analyze-result [files uri type data]
|
||||||
(let [existing-files? (into #{} (->> files (map :file-id) (filter some?)))
|
(let [existing-files? (into #{} (->> files (map :file-id) (filter some?)))
|
||||||
|
@ -150,7 +151,6 @@
|
||||||
|
|
||||||
(mf/defc import-entry
|
(mf/defc import-entry
|
||||||
[{:keys [state file editing? can-be-deleted?]}]
|
[{:keys [state file editing? can-be-deleted?]}]
|
||||||
|
|
||||||
(let [loading? (or (= :analyzing (:status file))
|
(let [loading? (or (= :analyzing (:status file))
|
||||||
(= :importing (:status file)))
|
(= :importing (:status file)))
|
||||||
analyze-error? (= :analyze-error (:status file))
|
analyze-error? (= :analyze-error (:status file))
|
||||||
|
@ -226,7 +226,9 @@
|
||||||
(cond
|
(cond
|
||||||
analyze-error?
|
analyze-error?
|
||||||
[:div {:class (stl/css :error-message)}
|
[:div {:class (stl/css :error-message)}
|
||||||
(tr "dashboard.import.analyze-error")]
|
(if (some? (:error file))
|
||||||
|
(tr (:error file))
|
||||||
|
(tr "dashboard.import.analyze-error"))]
|
||||||
|
|
||||||
import-error?
|
import-error?
|
||||||
[:div {:class (stl/css :error-message)}
|
[:div {:class (stl/css :error-message)}
|
||||||
|
@ -260,13 +262,14 @@
|
||||||
(fn [files]
|
(fn [files]
|
||||||
(->> (uw/ask-many!
|
(->> (uw/ask-many!
|
||||||
{:cmd :analyze-import
|
{:cmd :analyze-import
|
||||||
:files files})
|
:files files
|
||||||
|
:features @features/features-ref})
|
||||||
(rx/mapcat #(rx/delay emit-delay (rx/of %)))
|
(rx/mapcat #(rx/delay emit-delay (rx/of %)))
|
||||||
(rx/filter some?)
|
(rx/filter some?)
|
||||||
(rx/subs!
|
(rx/subs!
|
||||||
(fn [{:keys [uri data error type] :as msg}]
|
(fn [{:keys [uri data error type] :as msg}]
|
||||||
(if (some? error)
|
(if (some? error)
|
||||||
(swap! state update :files set-analyze-error uri)
|
(swap! state update :files set-analyze-error uri error)
|
||||||
(swap! state update :files set-analyze-result uri type data)))))))
|
(swap! state update :files set-analyze-result uri type data)))))))
|
||||||
|
|
||||||
import-files
|
import-files
|
||||||
|
|
|
@ -240,17 +240,21 @@
|
||||||
(tr "common.share-link.permissions-pages")]
|
(tr "common.share-link.permissions-pages")]
|
||||||
[:div {:class (stl/css :items)}
|
[:div {:class (stl/css :items)}
|
||||||
(if (= 1 (count pages))
|
(if (= 1 (count pages))
|
||||||
|
|
||||||
[:div {:class (stl/css :checkbox-wrapper)}
|
[:div {:class (stl/css :checkbox-wrapper)}
|
||||||
|
|
||||||
|
[:label {:for (str "page-" current-page-id)
|
||||||
|
:class (stl/css-case :global/checked true)}
|
||||||
|
|
||||||
|
[:span {:class (stl/css :checked)}
|
||||||
|
i/status-tick-refactor]
|
||||||
|
|
||||||
|
(:name current-page)]
|
||||||
|
|
||||||
[:input {:type "checkbox"
|
[:input {:type "checkbox"
|
||||||
:id (dm/str "page-" current-page-id)
|
:id (dm/str "page-" current-page-id)
|
||||||
:data-page-id (dm/str current-page-id)
|
:data-page-id (dm/str current-page-id)
|
||||||
:on-change on-mark-checked-page
|
:on-change on-mark-checked-page
|
||||||
:checked true}]
|
: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"))]]
|
[:span (str " " (tr "common.share-link.current-tag"))]]
|
||||||
|
|
||||||
[:*
|
[:*
|
||||||
|
|
|
@ -187,4 +187,12 @@
|
||||||
@extend .input-checkbox;
|
@extend .input-checkbox;
|
||||||
height: $s-32;
|
height: $s-32;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
span.checked {
|
||||||
|
background-color: var(--input-checkbox-background-color-active);
|
||||||
|
border: $s-1 solid var(--input-checkbox-background-color-active);
|
||||||
|
svg {
|
||||||
|
@extend .button-icon-small;
|
||||||
|
stroke: var(--input-checkbox-foreground-color-active);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -281,7 +281,7 @@
|
||||||
(mf/defc component-item-thumbnail
|
(mf/defc component-item-thumbnail
|
||||||
"Component that renders the thumbnail image or the original SVG."
|
"Component that renders the thumbnail image or the original SVG."
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[{:keys [file-id root-shape component container]}]
|
[{:keys [file-id root-shape component container class]}]
|
||||||
(let [retry (mf/use-state 0)
|
(let [retry (mf/use-state 0)
|
||||||
thumbnail-uri (get-component-thumbnail-uri file-id component)
|
thumbnail-uri (get-component-thumbnail-uri file-id component)
|
||||||
handle-error
|
handle-error
|
||||||
|
@ -294,6 +294,7 @@
|
||||||
(if (some? thumbnail-uri)
|
(if (some? thumbnail-uri)
|
||||||
[:& component-svg-thumbnail
|
[:& component-svg-thumbnail
|
||||||
{:thumbnail-uri thumbnail-uri
|
{:thumbnail-uri thumbnail-uri
|
||||||
|
:class class
|
||||||
:on-error handle-error
|
:on-error handle-error
|
||||||
:root-shape root-shape
|
:root-shape root-shape
|
||||||
:objects (:objects container)
|
:objects (:objects container)
|
||||||
|
@ -301,6 +302,7 @@
|
||||||
|
|
||||||
[:& component-svg
|
[:& component-svg
|
||||||
{:root-shape root-shape
|
{:root-shape root-shape
|
||||||
|
:class class
|
||||||
:objects (:objects container)
|
:objects (:objects container)
|
||||||
:show-grids? true}])))
|
:show-grids? true}])))
|
||||||
|
|
||||||
|
|
|
@ -179,7 +179,8 @@
|
||||||
|
|
||||||
(when visible?
|
(when visible?
|
||||||
[:& cmm/component-item-thumbnail {:file-id file-id
|
[:& cmm/component-item-thumbnail {:file-id file-id
|
||||||
:class (stl/css :thumbnail)
|
:class (stl/css-case :thumbnail true
|
||||||
|
:asset-list-thumbnail (not listing-thumbs?))
|
||||||
:root-shape root-shape
|
:root-shape root-shape
|
||||||
:component component
|
:component component
|
||||||
:container container}])])]))
|
:container container}])])]))
|
||||||
|
|
|
@ -26,14 +26,6 @@
|
||||||
background-color: var(--assets-component-background-color);
|
background-color: var(--assets-component-background-color);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
img {
|
|
||||||
height: auto;
|
|
||||||
width: auto;
|
|
||||||
max-height: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
pointer-events: none;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cell-name {
|
.cell-name {
|
||||||
@include titleTipography;
|
@include titleTipography;
|
||||||
|
@ -84,6 +76,8 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
|
pointer-events: none;
|
||||||
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-placeholder {
|
.grid-placeholder {
|
||||||
|
@ -96,10 +90,13 @@
|
||||||
.asset-enum {
|
.asset-enum {
|
||||||
margin: 0 $s-12;
|
margin: 0 $s-12;
|
||||||
}
|
}
|
||||||
|
|
||||||
.enum-item {
|
.enum-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
column-gap: $s-8;
|
||||||
height: $s-36;
|
height: $s-36;
|
||||||
margin-bottom: $s-4;
|
margin-bottom: $s-4;
|
||||||
padding: $s-2;
|
padding: $s-2;
|
||||||
|
@ -107,55 +104,6 @@
|
||||||
background-color: var(--assets-item-background-color);
|
background-color: var(--assets-item-background-color);
|
||||||
cursor: pointer;
|
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 {
|
&:hover {
|
||||||
background-color: var(--assets-item-background-color-hover);
|
background-color: var(--assets-item-background-color-hover);
|
||||||
.item-name {
|
.item-name {
|
||||||
|
@ -176,6 +124,45 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.item-name {
|
||||||
|
@include titleTipography;
|
||||||
|
@include textEllipsis;
|
||||||
|
order: 2;
|
||||||
|
color: var(--assets-item-name-foreground-color);
|
||||||
|
input {
|
||||||
|
@include textEllipsis;
|
||||||
|
@include titleTipography;
|
||||||
|
@include removeInputStyle;
|
||||||
|
height: $s-32;
|
||||||
|
padding: $s-4;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
display: flex;
|
||||||
|
place-items: center;
|
||||||
|
padding-inline-end: $s-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.editing {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
align-items: center;
|
||||||
|
column-gap: $s-8;
|
||||||
|
border: $s-1 solid var(--input-border-color-focus);
|
||||||
|
border-radius: $br-8;
|
||||||
|
background-color: var(--input-background-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-list-thumbnail {
|
||||||
|
@include flexCenter;
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: $s-2;
|
||||||
|
height: $s-32;
|
||||||
|
width: $s-32;
|
||||||
|
border-radius: $br-6;
|
||||||
|
background-color: var(--assets-component-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
.grid-placeholder {
|
.grid-placeholder {
|
||||||
height: $s-2;
|
height: $s-2;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.path {
|
.path {
|
||||||
|
@include textEllipsis;
|
||||||
|
max-width: 90%;
|
||||||
margin-left: $s-2;
|
margin-left: $s-2;
|
||||||
text-transform: initial;
|
text-transform: initial;
|
||||||
color: var(--title-foreground-color-hover);
|
color: var(--title-foreground-color-hover);
|
||||||
|
|
|
@ -47,6 +47,8 @@
|
||||||
.component-name {
|
.component-name {
|
||||||
@include titleTipography;
|
@include titleTipography;
|
||||||
@include textEllipsis;
|
@include textEllipsis;
|
||||||
|
direction: rtl;
|
||||||
|
text-align: left;
|
||||||
width: 70%;
|
width: 70%;
|
||||||
flex-grow: 2;
|
flex-grow: 2;
|
||||||
margin-left: $s-8;
|
margin-left: $s-8;
|
||||||
|
@ -55,6 +57,9 @@
|
||||||
.component-parent-name {
|
.component-parent-name {
|
||||||
@include titleTipography;
|
@include titleTipography;
|
||||||
@include textEllipsis;
|
@include textEllipsis;
|
||||||
|
direction: rtl;
|
||||||
|
text-align: left;
|
||||||
|
max-width: 95%;
|
||||||
padding-left: $s-36;
|
padding-left: $s-36;
|
||||||
color: var(--title-foreground-color);
|
color: var(--title-foreground-color);
|
||||||
}
|
}
|
||||||
|
@ -224,6 +229,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: $s-4;
|
margin-bottom: $s-4;
|
||||||
|
padding-right: $s-8;
|
||||||
font-size: $s-12;
|
font-size: $s-12;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -147,7 +147,7 @@
|
||||||
border-radius: $br-6;
|
border-radius: $br-6;
|
||||||
background-color: var(--input-checkbox-inactive-background-color);
|
background-color: var(--input-checkbox-inactive-background-color);
|
||||||
&.checked {
|
&.checked {
|
||||||
background-color: var(--input-checkbox-active-background-color);
|
background-color: var(--input-checkbox-background-color-active);
|
||||||
svg {
|
svg {
|
||||||
@extend .button-icon-small;
|
@extend .button-icon-small;
|
||||||
stroke: var(--input-details-color);
|
stroke: var(--input-details-color);
|
||||||
|
|
|
@ -654,7 +654,8 @@
|
||||||
[:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-duration")]
|
[:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-duration")]
|
||||||
[:div {:class (stl/css :input-element-wrapper)
|
[:div {:class (stl/css :input-element-wrapper)
|
||||||
:title (tr "workspace.options.interaction-ms")}
|
:title (tr "workspace.options.interaction-ms")}
|
||||||
[:span.after (tr "workspace.options.interaction-ms")]
|
[:span {:class (stl/css :after)}
|
||||||
|
(tr "workspace.options.interaction-ms")]
|
||||||
[:> numeric-input* {:ref ext-duration-ref
|
[:> numeric-input* {:ref ext-duration-ref
|
||||||
:on-change change-duration
|
:on-change change-duration
|
||||||
:value (-> interaction :animation :duration)
|
:value (-> interaction :animation :duration)
|
||||||
|
@ -719,9 +720,9 @@
|
||||||
[:button {:class (stl/css :add-interaction-btn)
|
[:button {:class (stl/css :add-interaction-btn)
|
||||||
:on-click add-interaction}
|
:on-click add-interaction}
|
||||||
i/add-refactor]]])
|
i/add-refactor]]])
|
||||||
[:div {:class (stl/css :help-content)}
|
|
||||||
(when (= (count interactions) 0)
|
(when (= (count interactions) 0)
|
||||||
[:*
|
[:div {:class (stl/css :help-content)}
|
||||||
(when (and shape (not (cfh/unframed-shape? shape)))
|
(when (and shape (not (cfh/unframed-shape? shape)))
|
||||||
[:div {:class (stl/css :help-group)}
|
[:div {:class (stl/css :help-group)}
|
||||||
[:div {:class (stl/css :interactions-help-icon)} i/add-refactor]
|
[:div {:class (stl/css :interactions-help-icon)} i/add-refactor]
|
||||||
|
@ -734,7 +735,7 @@
|
||||||
[:div {:class (stl/css :help-group)}
|
[:div {:class (stl/css :help-group)}
|
||||||
[:div {:class (stl/css :interactions-help-icon)} i/play-refactor]
|
[:div {:class (stl/css :interactions-help-icon)} i/play-refactor]
|
||||||
[:div {:class (stl/css :interactions-help)}
|
[:div {:class (stl/css :interactions-help)}
|
||||||
(tr "workspace.options.use-play-button")]]])]
|
(tr "workspace.options.use-play-button")]]])
|
||||||
[:div {:class (stl/css :groups)}
|
[:div {:class (stl/css :groups)}
|
||||||
(for [[index interaction] (d/enumerate interactions)]
|
(for [[index interaction] (d/enumerate interactions)]
|
||||||
[:& interaction-entry {:key (dm/str (:id shape) "-" index)
|
[:& interaction-entry {:key (dm/str (:id shape) "-" index)
|
||||||
|
|
|
@ -54,6 +54,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.after {
|
||||||
|
@include titleTipography;
|
||||||
|
margin-top: $s-1;
|
||||||
|
}
|
||||||
|
|
||||||
.interactions-help {
|
.interactions-help {
|
||||||
@include titleTipography;
|
@include titleTipography;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
@ -137,7 +137,11 @@
|
||||||
:is-shared (:shared context)
|
:is-shared (:shared context)
|
||||||
:project-id (:project-id context)
|
:project-id (:project-id context)
|
||||||
:create-page false
|
:create-page false
|
||||||
:features features})))
|
|
||||||
|
;; If the features object exists send that. Otherwise we remove the components/v2 because
|
||||||
|
;; if the features attribute doesn't exist is a version < 2.0. The other features will
|
||||||
|
;; be kept so the shapes are created full featured
|
||||||
|
:features (d/nilv (:features context) (disj features "components/v2"))})))
|
||||||
|
|
||||||
(defn link-file-libraries
|
(defn link-file-libraries
|
||||||
"Create a new file on the back-end"
|
"Create a new file on the back-end"
|
||||||
|
@ -154,7 +158,8 @@
|
||||||
[context file]
|
[context file]
|
||||||
(let [file-id (:id file)
|
(let [file-id (:id file)
|
||||||
session-id (uuid/next)
|
session-id (uuid/next)
|
||||||
batches (->> (fb/generate-changes file)
|
changes (fb/generate-changes file)
|
||||||
|
batches (->> changes
|
||||||
(partition change-batch-size change-batch-size nil)
|
(partition change-batch-size change-batch-size nil)
|
||||||
(mapv vec))
|
(mapv vec))
|
||||||
|
|
||||||
|
@ -538,7 +543,9 @@
|
||||||
(rx/merge-map (comp d/kebab-keys parser/string->uuid))
|
(rx/merge-map (comp d/kebab-keys parser/string->uuid))
|
||||||
(rx/mapcat
|
(rx/mapcat
|
||||||
(fn [[id media]]
|
(fn [[id media]]
|
||||||
(let [media (assoc media :id (resolve id))]
|
(let [media (-> media
|
||||||
|
(assoc :id (resolve id))
|
||||||
|
(update :name str))]
|
||||||
(->> (get-file context :media id media)
|
(->> (get-file context :media id media)
|
||||||
(rx/map (fn [blob]
|
(rx/map (fn [blob]
|
||||||
(let [content (.slice blob 0 (.-size blob) (:mtype media))]
|
(let [content (.slice blob 0 (.-size blob) (:mtype media))]
|
||||||
|
@ -625,7 +632,7 @@
|
||||||
"other")))
|
"other")))
|
||||||
|
|
||||||
(defmethod impl/handler :analyze-import
|
(defmethod impl/handler :analyze-import
|
||||||
[{:keys [files]}]
|
[{:keys [files features]}]
|
||||||
|
|
||||||
(->> (rx/from files)
|
(->> (rx/from files)
|
||||||
(rx/merge-map
|
(rx/merge-map
|
||||||
|
@ -646,7 +653,16 @@
|
||||||
(rx/merge-map #(zip/loadAsync (:body %)))
|
(rx/merge-map #(zip/loadAsync (:body %)))
|
||||||
(rx/merge-map #(get-file {:zip %} :manifest))
|
(rx/merge-map #(get-file {:zip %} :manifest))
|
||||||
(rx/map (comp d/kebab-keys parser/string->uuid))
|
(rx/map (comp d/kebab-keys parser/string->uuid))
|
||||||
(rx/map #(hash-map :uri (:uri file) :data % :type "application/zip")))
|
(rx/map
|
||||||
|
(fn [data]
|
||||||
|
;; Checks if the file is exported with components v2 and the current team only
|
||||||
|
;; supports components v1
|
||||||
|
(let [has-file-v2?
|
||||||
|
(->> (:files data)
|
||||||
|
(d/seek (fn [[_ file]] (contains? (set (:features file)) "components/v2"))))]
|
||||||
|
(if (and has-file-v2? (not (contains? features "components/v2")))
|
||||||
|
{:uri (:uri file) :error "dashboard.import.analyze-error.components-v2"}
|
||||||
|
(hash-map :uri (:uri file) :data data :type "application/zip"))))))
|
||||||
(->> st
|
(->> st
|
||||||
(rx/filter (fn [data] (= "application/octet-stream" (:type data))))
|
(rx/filter (fn [data] (= "application/octet-stream" (:type data))))
|
||||||
(rx/map (fn [_]
|
(rx/map (fn [_]
|
||||||
|
|
|
@ -534,6 +534,9 @@ msgstr "Import Penpot files"
|
||||||
msgid "dashboard.import.analyze-error"
|
msgid "dashboard.import.analyze-error"
|
||||||
msgstr "Oops! We couldn't import this file"
|
msgstr "Oops! We couldn't import this file"
|
||||||
|
|
||||||
|
msgid "dashboard.import.analyze-error.components-v2"
|
||||||
|
msgstr "File with components v2 activated but this team doesn't support it yet."
|
||||||
|
|
||||||
msgid "dashboard.import.import-error"
|
msgid "dashboard.import.import-error"
|
||||||
msgstr "There was a problem importing the file. The file wasn't imported."
|
msgstr "There was a problem importing the file. The file wasn't imported."
|
||||||
|
|
||||||
|
|
|
@ -542,6 +542,9 @@ msgstr "Importar archivos Penpot"
|
||||||
msgid "dashboard.import.analyze-error"
|
msgid "dashboard.import.analyze-error"
|
||||||
msgstr "¡Vaya! No hemos podido importar el fichero"
|
msgstr "¡Vaya! No hemos podido importar el fichero"
|
||||||
|
|
||||||
|
msgid "dashboard.import.analyze-error.components-v2"
|
||||||
|
msgstr "Fichero exportado con componentes-v2 pero el equipo actual no lo soporta aún."
|
||||||
|
|
||||||
msgid "dashboard.import.import-error"
|
msgid "dashboard.import.import-error"
|
||||||
msgstr "Hubo un problema importando el fichero. No ha podido ser importado."
|
msgstr "Hubo un problema importando el fichero. No ha podido ser importado."
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue