mirror of
https://github.com/penpot/penpot.git
synced 2025-12-12 06:24:17 +01:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
@@ -111,7 +111,7 @@ jobs:
|
|||||||
yarn run build:app:assets
|
yarn run build:app:assets
|
||||||
clojure -M:dev:shadow-cljs release main
|
clojure -M:dev:shadow-cljs release main
|
||||||
yarn playwright install --with-deps chromium
|
yarn playwright install --with-deps chromium
|
||||||
yarn e2e:test
|
yarn test:e2e
|
||||||
|
|
||||||
- run:
|
- run:
|
||||||
name: "backend tests"
|
name: "backend tests"
|
||||||
|
|||||||
18
CHANGES.md
18
CHANGES.md
@@ -18,10 +18,17 @@
|
|||||||
|
|
||||||
### :rocket: Epics and highlights
|
### :rocket: Epics and highlights
|
||||||
|
|
||||||
|
- **New plugin system.**
|
||||||
|
|
||||||
|
Penpot now supports custom plugins. Read everything about developing your plugins [HERE](https://help.penpot.app/plugins/)
|
||||||
|
|
||||||
### :boom: Breaking changes & Deprecations
|
### :boom: Breaking changes & Deprecations
|
||||||
|
|
||||||
### :heart: Community contributions (Thank you!)
|
### :heart: Community contributions (Thank you!)
|
||||||
|
|
||||||
|
- All our plugins beta testers :heart:.
|
||||||
|
- Fix problem when translating multiple path points by @eeropic [#4459](https://github.com/penpot/penpot/issues/4459)
|
||||||
|
|
||||||
### :sparkles: New features
|
### :sparkles: New features
|
||||||
|
|
||||||
- **Replace Draft.js completely with a custom editor** [Taiga #7706](https://tree.taiga.io/project/penpot/us/7706)
|
- **Replace Draft.js completely with a custom editor** [Taiga #7706](https://tree.taiga.io/project/penpot/us/7706)
|
||||||
@@ -32,8 +39,17 @@
|
|||||||
|
|
||||||
You can enable it with the `enable-feature-text-editor-v2` configuration flag.
|
You can enable it with the `enable-feature-text-editor-v2` configuration flag.
|
||||||
|
|
||||||
|
|
||||||
### :bug: Bugs fixed
|
### :bug: Bugs fixed
|
||||||
|
|
||||||
|
- Fix problem with go back button on error page [Taiga #8887](https://tree.taiga.io/project/penpot/issue/8887)
|
||||||
|
- Fix problem with shadows in text for Safari [Taiga #8770](https://tree.taiga.io/project/penpot/issue/8770)
|
||||||
|
- Fix a regression with feedback form subject and content limits [Taiga #8908](https://tree.taiga.io/project/penpot/issue/8908)
|
||||||
|
- Fix problem with stroke and filter ordering in frames [Github #5058](https://github.com/penpot/penpot/issues/5058)
|
||||||
|
- Fix problem with hover layers when hidden/blocked [Github #5074](https://github.com/penpot/penpot/issues/5074)
|
||||||
|
- Fix problem with precision on boolean calculation [Taiga #8482](https://tree.taiga.io/project/penpot/issue/8482)
|
||||||
|
- Fix problem when translating multiple path points [Github #4459](https://github.com/penpot/penpot/issues/4459)
|
||||||
|
|
||||||
## 2.2.1
|
## 2.2.1
|
||||||
|
|
||||||
### :bug: Bugs fixed
|
### :bug: Bugs fixed
|
||||||
@@ -178,7 +194,7 @@ time being.
|
|||||||
|
|
||||||
### :boom: Breaking changes & Deprecations
|
### :boom: Breaking changes & Deprecations
|
||||||
|
|
||||||
### :heart: Community contributions (Thank you!)
|
### :heart: Communityq contributions (Thank you!)
|
||||||
|
|
||||||
### :sparkles: New features
|
### :sparkles: New features
|
||||||
|
|
||||||
|
|||||||
@@ -315,15 +315,13 @@
|
|||||||
(l/dbg :hint "sendmail"
|
(l/dbg :hint "sendmail"
|
||||||
:id (:id params)
|
:id (:id params)
|
||||||
:to (:to params)
|
:to (:to params)
|
||||||
:subject (str/trim (:subject params))
|
:subject (str/trim (:subject params)))
|
||||||
:body (str/join "," (map :type (:body params))))
|
|
||||||
|
|
||||||
(.sendMessage ^Transport transport
|
(.sendMessage ^Transport transport
|
||||||
^MimeMessage message
|
^MimeMessage message
|
||||||
(.getAllRecipients message))))))
|
(.getAllRecipients message))))))
|
||||||
|
|
||||||
(when (or (contains? cf/flags :log-emails)
|
(when (contains? cf/flags :log-emails)
|
||||||
(not (contains? cf/flags :smtp)))
|
|
||||||
(send-to-logger! cfg params))))
|
(send-to-logger! cfg params))))
|
||||||
|
|
||||||
(defmethod ig/pre-init-spec ::handler [_]
|
(defmethod ig/pre-init-spec ::handler [_]
|
||||||
|
|||||||
@@ -21,8 +21,8 @@
|
|||||||
|
|
||||||
(def ^:private schema:send-user-feedback
|
(def ^:private schema:send-user-feedback
|
||||||
[:map {:title "send-user-feedback"}
|
[:map {:title "send-user-feedback"}
|
||||||
[:subject [:string {:max 250}]]
|
[:subject [:string {:max 400}]]
|
||||||
[:content [:string {:max 250}]]])
|
[:content [:string {:max 2500}]]])
|
||||||
|
|
||||||
(sv/defmethod ::send-user-feedback
|
(sv/defmethod ::send-user-feedback
|
||||||
{::doc/added "1.18"
|
{::doc/added "1.18"
|
||||||
|
|||||||
@@ -780,6 +780,7 @@
|
|||||||
|
|
||||||
(def ^:private schema:create-invitation
|
(def ^:private schema:create-invitation
|
||||||
[:map {:title "params:create-invitation"}
|
[:map {:title "params:create-invitation"}
|
||||||
|
[::rpc/profile-id ::sm/uuid]
|
||||||
[:team
|
[:team
|
||||||
[:map
|
[:map
|
||||||
[:id ::sm/uuid]
|
[:id ::sm/uuid]
|
||||||
@@ -936,7 +937,7 @@
|
|||||||
(map :email))
|
(map :email))
|
||||||
|
|
||||||
(defn- create-team-invitations
|
(defn- create-team-invitations
|
||||||
[{:keys [::db/conn] :as cfg} profile team role emails]
|
[{:keys [::db/conn] :as cfg} {:keys [profile team role emails] :as params}]
|
||||||
(let [join-requests (into #{} xf:map-email
|
(let [join-requests (into #{} xf:map-email
|
||||||
(get-valid-requests-email conn (:id team)))
|
(get-valid-requests-email conn (:id team)))
|
||||||
team-members (into #{} xf:map-email
|
team-members (into #{} xf:map-email
|
||||||
@@ -950,11 +951,7 @@
|
|||||||
;; We don't send invitations to
|
;; We don't send invitations to
|
||||||
;; join-requested members
|
;; join-requested members
|
||||||
(remove join-requests)
|
(remove join-requests)
|
||||||
(map (fn [email]
|
(map (fn [email] (assoc params :email email)))
|
||||||
{:email email
|
|
||||||
:team team
|
|
||||||
:profile profile
|
|
||||||
:role role}))
|
|
||||||
(keep (partial create-invitation cfg)))
|
(keep (partial create-invitation cfg)))
|
||||||
emails)]
|
emails)]
|
||||||
|
|
||||||
@@ -980,7 +977,7 @@
|
|||||||
join the team."
|
join the team."
|
||||||
{::doc/added "1.17"
|
{::doc/added "1.17"
|
||||||
::sm/params schema:create-team-invitations}
|
::sm/params schema:create-team-invitations}
|
||||||
[cfg {:keys [::rpc/profile-id team-id emails role] :as params}]
|
[cfg {:keys [::rpc/profile-id team-id emails] :as params}]
|
||||||
(let [perms (get-permissions cfg profile-id team-id)
|
(let [perms (get-permissions cfg profile-id team-id)
|
||||||
profile (db/get-by-id cfg :profile profile-id)
|
profile (db/get-by-id cfg :profile profile-id)
|
||||||
emails (into #{} (map profile/clean-email) emails)]
|
emails (into #{} (map profile/clean-email) emails)]
|
||||||
@@ -1006,7 +1003,16 @@
|
|||||||
(check-profile-muted cfg profile)
|
(check-profile-muted cfg profile)
|
||||||
|
|
||||||
(let [team (db/get-by-id cfg :team team-id)
|
(let [team (db/get-by-id cfg :team team-id)
|
||||||
invitations (db/tx-run! cfg create-team-invitations profile team role emails)]
|
;; NOTE: Is important pass RPC method params down to the
|
||||||
|
;; `create-team-invitations` because it uses the implicit
|
||||||
|
;; RPC properties from params for fill necessary data on
|
||||||
|
;; emiting an entry to the audit-log
|
||||||
|
invitations (db/tx-run! cfg create-team-invitations
|
||||||
|
(-> params
|
||||||
|
(assoc :profile profile)
|
||||||
|
(assoc :team team)
|
||||||
|
(assoc :emails emails)))]
|
||||||
|
|
||||||
(with-meta {:total (count invitations)
|
(with-meta {:total (count invitations)
|
||||||
:invitations invitations}
|
:invitations invitations}
|
||||||
{::audit/props {:invitations (count invitations)}}))))
|
{::audit/props {:invitations (count invitations)}}))))
|
||||||
@@ -1057,17 +1063,16 @@
|
|||||||
(audit/submit! cfg event))
|
(audit/submit! cfg event))
|
||||||
|
|
||||||
;; Create invitations for all provided emails.
|
;; Create invitations for all provided emails.
|
||||||
(let [profile (db/get-by-id conn :profile profile-id)]
|
(let [profile (db/get-by-id conn :profile profile-id)
|
||||||
(->> emails
|
params (-> params
|
||||||
(map (fn [email]
|
(assoc :team team)
|
||||||
(-> params
|
(assoc :profile profile)
|
||||||
(assoc :team team)
|
(assoc :role role))
|
||||||
(assoc :profile profile)
|
invitations (->> emails
|
||||||
(assoc :email email)
|
(map (fn [email] (assoc params :email email)))
|
||||||
(assoc :role role))))
|
(map (partial create-invitation cfg)))]
|
||||||
(run! (partial create-invitation cfg))))
|
|
||||||
|
|
||||||
(vary-meta team assoc ::audit/props {:invitations (count emails)})))
|
(vary-meta team assoc ::audit/props {:invitations (count invitations)}))))
|
||||||
|
|
||||||
;; --- Query: get-team-invitation-token
|
;; --- Query: get-team-invitation-token
|
||||||
|
|
||||||
|
|||||||
@@ -852,8 +852,10 @@
|
|||||||
|
|
||||||
(defn ray-overlaps?
|
(defn ray-overlaps?
|
||||||
[ray-point {selrect :selrect}]
|
[ray-point {selrect :selrect}]
|
||||||
(and (>= (:y ray-point) (:y1 selrect))
|
(and (or (> (:y ray-point) (:y1 selrect))
|
||||||
(<= (:y ray-point) (:y2 selrect))))
|
(mth/almost-zero? (- (:y ray-point) (:y1 selrect))))
|
||||||
|
(or (< (:y ray-point) (:y2 selrect))
|
||||||
|
(mth/almost-zero? (- (:y ray-point) (:y2 selrect))))))
|
||||||
|
|
||||||
(defn content->geom-data
|
(defn content->geom-data
|
||||||
[content]
|
[content]
|
||||||
|
|||||||
@@ -15,8 +15,7 @@
|
|||||||
:output-wrapper false}
|
:output-wrapper false}
|
||||||
|
|
||||||
:release
|
:release
|
||||||
{:closure-defines {goog.debug.LOGGING_ENABLED true}
|
{:compiler-options
|
||||||
:compiler-options
|
|
||||||
{:fn-invoke-direct true
|
{:fn-invoke-direct true
|
||||||
:source-map true
|
:source-map true
|
||||||
:optimizations #shadow/env ["PENPOT_BUILD_OPTIMIZATIONS" :as :keyword :default :simple]
|
:optimizations #shadow/env ["PENPOT_BUILD_OPTIMIZATIONS" :as :keyword :default :simple]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v14.15.0
|
v20.11.1
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
"build:storybook:cljs": "clojure -M:dev:shadow-cljs release storybook",
|
"build:storybook:cljs": "clojure -M:dev:shadow-cljs release storybook",
|
||||||
"build:renderer": "yarn run wasm-pack build ./renderer --target web --out-dir ../resources/public/js/renderer --release",
|
"build:renderer": "yarn run wasm-pack build ./renderer --target web --out-dir ../resources/public/js/renderer --release",
|
||||||
"e2e:server": "node ./scripts/e2e-server.js",
|
"e2e:server": "node ./scripts/e2e-server.js",
|
||||||
"e2e:test": "playwright test --project default",
|
|
||||||
"fmt:clj": "cljfmt fix --parallel=true src/ test/",
|
"fmt:clj": "cljfmt fix --parallel=true src/ test/",
|
||||||
"fmt:clj:check": "cljfmt check --parallel=false src/ test/",
|
"fmt:clj:check": "cljfmt check --parallel=false src/ test/",
|
||||||
"fmt:js": "yarn run prettier -c src/**/*.stories.jsx -c playwright/**/*.js -c scripts/**/*.js -w",
|
"fmt:js": "yarn run prettier -c src/**/*.stories.jsx -c playwright/**/*.js -c scripts/**/*.js -w",
|
||||||
@@ -35,6 +34,7 @@
|
|||||||
"test:compile": "clojure -M:dev:shadow-cljs compile test --config-merge '{:autorun false}'",
|
"test:compile": "clojure -M:dev:shadow-cljs compile test --config-merge '{:autorun false}'",
|
||||||
"test:run": "node target/tests.cjs",
|
"test:run": "node target/tests.cjs",
|
||||||
"test:watch": "clojure -M:dev:shadow-cljs watch test",
|
"test:watch": "clojure -M:dev:shadow-cljs watch test",
|
||||||
|
"test:e2e": "playwright test --project default",
|
||||||
"translations": "node ./scripts/translations.js",
|
"translations": "node ./scripts/translations.js",
|
||||||
"watch": "yarn run watch:app:assets",
|
"watch": "yarn run watch:app:assets",
|
||||||
"watch:app:assets": "node ./scripts/watch.js",
|
"watch:app:assets": "node ./scripts/watch.js",
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export default defineConfig({
|
|||||||
/* Run your local dev server before starting the tests */
|
/* Run your local dev server before starting the tests */
|
||||||
webServer: {
|
webServer: {
|
||||||
timeout: 2 * 60 * 1000,
|
timeout: 2 * 60 * 1000,
|
||||||
command: "yarn e2e:server",
|
command: "yarn run e2e:server",
|
||||||
url: "http://localhost:3000",
|
url: "http://localhost:3000",
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: !process.env.CI,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ export class WorkspacePage extends BaseWebSocketPage {
|
|||||||
async moveSelectionToShape(name) {
|
async moveSelectionToShape(name) {
|
||||||
await this.page.locator("rect.viewport-selrect").hover();
|
await this.page.locator("rect.viewport-selrect").hover();
|
||||||
await this.page.mouse.down();
|
await this.page.mouse.down();
|
||||||
await this.viewport.getByTestId(name).first().hover({ force: true });
|
await this.viewport.getByText(name).first().hover({ force: true });
|
||||||
await this.page.mouse.up();
|
await this.page.mouse.up();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,7 @@
|
|||||||
<script defer src="{{& polyfills}}"></script>
|
<script defer src="{{& polyfills}}"></script>
|
||||||
{{/manifest}}
|
{{/manifest}}
|
||||||
|
|
||||||
<script type="module" src="{{pluginRuntimeUri}}/index.js"></script>
|
<script type="module" src="{{& pluginRuntimeUri}}"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.penpotTranslations = JSON.parse({{& translations}});
|
window.penpotTranslations = JSON.parse({{& translations}});
|
||||||
|
|||||||
@@ -181,14 +181,16 @@ export async function watch(baseDir, predicate, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function readShadowManifest() {
|
async function readShadowManifest() {
|
||||||
|
const ts = Date.now();
|
||||||
try {
|
try {
|
||||||
const manifestPath = "resources/public/js/manifest.json";
|
const manifestPath = "resources/public/js/manifest.json";
|
||||||
let content = await fs.readFile(manifestPath, { encoding: "utf8" });
|
let content = await fs.readFile(manifestPath, { encoding: "utf8" });
|
||||||
content = JSON.parse(content);
|
content = JSON.parse(content);
|
||||||
|
|
||||||
const index = {
|
const index = {
|
||||||
config: "js/config.js?ts=" + Date.now(),
|
ts: ts,
|
||||||
polyfills: "js/polyfills.js?ts=" + Date.now(),
|
config: "js/config.js?ts=" + ts,
|
||||||
|
polyfills: "js/polyfills.js?ts=" + ts,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let item of content) {
|
for (let item of content) {
|
||||||
@@ -198,12 +200,13 @@ async function readShadowManifest() {
|
|||||||
return index;
|
return index;
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
return {
|
return {
|
||||||
config: "js/config.js",
|
ts: ts,
|
||||||
polyfills: "js/polyfills.js",
|
config: "js/config.js?ts=" + ts,
|
||||||
main: "js/main.js",
|
polyfills: "js/polyfills.js?ts=" + ts,
|
||||||
shared: "js/shared.js",
|
main: "js/main.js?ts=" + ts,
|
||||||
worker: "js/worker.js",
|
shared: "js/shared.js?ts=" + ts,
|
||||||
rasterizer: "js/rasterizer.js",
|
worker: "js/worker.js?ts=" + ts,
|
||||||
|
rasterizer: "js/rasterizer.js?ts=" + ts,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -409,8 +412,8 @@ async function generateTemplates() {
|
|||||||
|
|
||||||
const pluginRuntimeUri =
|
const pluginRuntimeUri =
|
||||||
process.env.PENPOT_PLUGIN_DEV === "true"
|
process.env.PENPOT_PLUGIN_DEV === "true"
|
||||||
? "http://localhost:4200"
|
? "http://localhost:4200/index.js?ts=" + manifest.ts
|
||||||
: "./plugins-runtime";
|
: "plugins-runtime/index.js?ts=" + manifest.ts;
|
||||||
|
|
||||||
content = await renderTemplate(
|
content = await renderTemplate(
|
||||||
"resources/templates/index.mustache",
|
"resources/templates/index.mustache",
|
||||||
|
|||||||
@@ -26,6 +26,13 @@
|
|||||||
(catch :default e
|
(catch :default e
|
||||||
(.error js/console "Error" e))))
|
(.error js/console "Error" e))))
|
||||||
|
|
||||||
|
(defn close-plugin!
|
||||||
|
[{:keys [plugin-id]}]
|
||||||
|
(try
|
||||||
|
(.ɵunloadPlugin ^js ug/global plugin-id)
|
||||||
|
(catch :default e
|
||||||
|
(.error js/console "Error" e))))
|
||||||
|
|
||||||
(defn delay-open-plugin
|
(defn delay-open-plugin
|
||||||
[plugin]
|
[plugin]
|
||||||
(ptk/reify ::delay-open-plugin
|
(ptk/reify ::delay-open-plugin
|
||||||
|
|||||||
@@ -483,7 +483,8 @@
|
|||||||
|
|
||||||
;; Empty values means "submit" the form (whent some items have been added
|
;; Empty values means "submit" the form (whent some items have been added
|
||||||
(when (and (kbd/enter? event) (str/empty? @value) (not-empty @items))
|
(when (and (kbd/enter? event) (str/empty? @value) (not-empty @items))
|
||||||
(on-submit form))
|
(when (fn? on-submit)
|
||||||
|
(on-submit form event)))
|
||||||
|
|
||||||
;; If we have a string in the input we add it only if valid
|
;; If we have a string in the input we add it only if valid
|
||||||
(when (and (valid-item-fn val) (not (str/empty? @value)))
|
(when (and (valid-item-fn val) (not (str/empty? @value)))
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
[app.main.ui.context :as muc]
|
[app.main.ui.context :as muc]
|
||||||
[app.main.ui.shapes.attrs :as attrs]
|
[app.main.ui.shapes.attrs :as attrs]
|
||||||
[app.main.ui.shapes.custom-stroke :refer [shape-fills shape-strokes]]
|
[app.main.ui.shapes.custom-stroke :refer [shape-fills shape-strokes]]
|
||||||
|
[app.main.ui.shapes.filters :as filters]
|
||||||
[app.util.debug :as dbg]
|
[app.util.debug :as dbg]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
@@ -65,6 +66,11 @@
|
|||||||
|
|
||||||
render-id (mf/use-ctx muc/render-id)
|
render-id (mf/use-ctx muc/render-id)
|
||||||
|
|
||||||
|
filter-id-blur (dm/fmt "filter-blur-%" render-id)
|
||||||
|
filter-id-shadows (dm/fmt "filter-shadow-%" render-id)
|
||||||
|
filter-str-blur (filters/filter-str filter-id-blur shape)
|
||||||
|
filter-str-shadows (filters/filter-str filter-id-shadows shape)
|
||||||
|
|
||||||
x (dm/get-prop shape :x)
|
x (dm/get-prop shape :x)
|
||||||
y (dm/get-prop shape :y)
|
y (dm/get-prop shape :y)
|
||||||
w (dm/get-prop shape :width)
|
w (dm/get-prop shape :width)
|
||||||
@@ -86,29 +92,37 @@
|
|||||||
:className "frame-background"})))
|
:className "frame-background"})))
|
||||||
path? (some? (.-d props))]
|
path? (some? (.-d props))]
|
||||||
|
|
||||||
[:*
|
;; We need to separate blur from shadows because the blur is applied to the strokes
|
||||||
[:g {:clip-path (when-not ^boolean show-content?
|
;; while the shadows have to be placed *under* the stroke (for example, the inner shadows)
|
||||||
(frame-clip-url shape render-id))
|
;; and the shadows needs to be applied only to the content (without the stroke)
|
||||||
;; A frame sets back normal fill behavior (default
|
[:g {:filter filter-str-blur}
|
||||||
;; transparent). It may have been changed to default black
|
[:defs
|
||||||
;; if a shape coming from an imported SVG file is
|
[:& filters/filters {:shape (dissoc shape :blur) :filter-id filter-id-shadows}]
|
||||||
;; rendered. See main.ui.shapes.attrs/add-style-attrs.
|
[:& filters/filters {:shape (assoc shape :shadow []) :filter-id filter-id-blur}]]
|
||||||
:fill "none"
|
|
||||||
:opacity opacity}
|
|
||||||
|
|
||||||
[:& shape-fills {:shape shape}
|
;; This need to be separated in two layers so the clip doesn't affect the shadow filters
|
||||||
(if ^boolean path?
|
;; otherwise the shadow will be clipped and not visible
|
||||||
[:> :path props]
|
[:g {:filter filter-str-shadows}
|
||||||
[:> :rect props])]
|
[:g {:clip-path (when-not ^boolean show-content? (frame-clip-url shape render-id))
|
||||||
|
;; A frame sets back normal fill behavior (default
|
||||||
|
;; transparent). It may have been changed to default black
|
||||||
|
;; if a shape coming from an imported SVG file is
|
||||||
|
;; rendered. See main.ui.shapes.attrs/add-style-attrs.
|
||||||
|
:fill "none"
|
||||||
|
:opacity opacity}
|
||||||
|
|
||||||
children]
|
[:& shape-fills {:shape shape}
|
||||||
|
(if ^boolean path?
|
||||||
|
[:> :path props]
|
||||||
|
[:> :rect props])]
|
||||||
|
|
||||||
|
children]]
|
||||||
|
|
||||||
[:& shape-strokes {:shape shape}
|
[:& shape-strokes {:shape shape}
|
||||||
(if ^boolean path?
|
(if ^boolean path?
|
||||||
[:> :path props]
|
[:> :path props]
|
||||||
[:> :rect props])]]))
|
[:> :rect props])]]))
|
||||||
|
|
||||||
|
|
||||||
(mf/defc frame-thumbnail-image
|
(mf/defc frame-thumbnail-image
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
|
|||||||
@@ -56,7 +56,6 @@
|
|||||||
(let [shape (unchecked-get props "shape")
|
(let [shape (unchecked-get props "shape")
|
||||||
children (unchecked-get props "children")
|
children (unchecked-get props "children")
|
||||||
pointer-events (unchecked-get props "pointer-events")
|
pointer-events (unchecked-get props "pointer-events")
|
||||||
disable-shadows? (unchecked-get props "disable-shadows?")
|
|
||||||
shape-id (dm/get-prop shape :id)
|
shape-id (dm/get-prop shape :id)
|
||||||
|
|
||||||
preview-blend-mode-ref
|
preview-blend-mode-ref
|
||||||
@@ -67,7 +66,6 @@
|
|||||||
|
|
||||||
type (dm/get-prop shape :type)
|
type (dm/get-prop shape :type)
|
||||||
render-id (h/use-render-id)
|
render-id (h/use-render-id)
|
||||||
filter-id (dm/str "filter-" render-id)
|
|
||||||
styles (-> (obj/create)
|
styles (-> (obj/create)
|
||||||
(obj/set! "pointerEvents" pointer-events)
|
(obj/set! "pointerEvents" pointer-events)
|
||||||
(cond-> (not (cfh/frame-shape? shape))
|
(cond-> (not (cfh/frame-shape? shape))
|
||||||
@@ -82,32 +80,30 @@
|
|||||||
shape-without-blur (dissoc shape :blur)
|
shape-without-blur (dissoc shape :blur)
|
||||||
shape-without-shadows (assoc shape :shadow [])
|
shape-without-shadows (assoc shape :shadow [])
|
||||||
|
|
||||||
|
filter-id (dm/str "filter-" render-id)
|
||||||
filter-str
|
filter-str
|
||||||
(when (and (or (cfh/group-shape? shape)
|
(when (or (cfh/group-shape? shape)
|
||||||
(cfh/frame-shape? shape)
|
(cfh/svg-raw-shape? shape))
|
||||||
(cfh/svg-raw-shape? shape))
|
|
||||||
(not disable-shadows?))
|
|
||||||
(filters/filter-str filter-id shape))
|
(filters/filter-str filter-id shape))
|
||||||
|
|
||||||
wrapper-props
|
wrapper-props
|
||||||
(-> (obj/clone props)
|
(-> (obj/clone props)
|
||||||
(obj/unset! "shape")
|
(obj/unset! "shape")
|
||||||
(obj/unset! "children")
|
(obj/unset! "children")
|
||||||
(obj/unset! "disable-shadows?")
|
|
||||||
(obj/set! "ref" ref)
|
(obj/set! "ref" ref)
|
||||||
(obj/set! "id" (dm/fmt "shape-%" shape-id))
|
(obj/set! "id" (dm/fmt "shape-%" shape-id))
|
||||||
(obj/set! "data-testid" (:name shape))
|
|
||||||
|
|
||||||
;; TODO: This is added for backward compatibility.
|
|
||||||
(cond-> (and (cfh/text-shape? shape) (empty? (:position-data shape)))
|
|
||||||
(-> (obj/set! "x" (:x shape))
|
|
||||||
(obj/set! "y" (:y shape))
|
|
||||||
(obj/set! "width" (:width shape))
|
|
||||||
(obj/set! "height" (:height shape))))
|
|
||||||
(obj/set! "style" styles))
|
(obj/set! "style" styles))
|
||||||
|
|
||||||
wrapper-props
|
wrapper-props
|
||||||
(cond-> wrapper-props
|
(cond-> wrapper-props
|
||||||
|
;; NOTE: This is added for backward compatibility
|
||||||
|
(and (cfh/text-shape? shape)
|
||||||
|
(empty? (:position-data shape)))
|
||||||
|
(-> (obj/set! "x" (:x shape))
|
||||||
|
(obj/set! "y" (:y shape))
|
||||||
|
(obj/set! "width" (:width shape))
|
||||||
|
(obj/set! "height" (:height shape)))
|
||||||
|
|
||||||
(= :group type)
|
(= :group type)
|
||||||
(-> (attrs/add-fill-props! shape render-id)
|
(-> (attrs/add-fill-props! shape render-id)
|
||||||
(attrs/add-border-props! shape))
|
(attrs/add-border-props! shape))
|
||||||
@@ -115,11 +111,13 @@
|
|||||||
(some? filter-str)
|
(some? filter-str)
|
||||||
(obj/set! "filter" filter-str))
|
(obj/set! "filter" filter-str))
|
||||||
|
|
||||||
svg-group? (and (contains? shape :svg-attrs) (= :group type))
|
svg-group?
|
||||||
|
(and (contains? shape :svg-attrs) (= :group type))
|
||||||
|
|
||||||
children (cond-> children
|
children
|
||||||
svg-group?
|
(cond-> children
|
||||||
(propagate-wrapper-styles wrapper-props))]
|
svg-group?
|
||||||
|
(propagate-wrapper-styles wrapper-props))]
|
||||||
|
|
||||||
[:& (mf/provider muc/render-id) {:value render-id}
|
[:& (mf/provider muc/render-id) {:value render-id}
|
||||||
[:> :g wrapper-props
|
[:> :g wrapper-props
|
||||||
@@ -128,9 +126,14 @@
|
|||||||
|
|
||||||
[:defs
|
[:defs
|
||||||
[:& defs/svg-defs {:shape shape :render-id render-id}]
|
[:& defs/svg-defs {:shape shape :render-id render-id}]
|
||||||
[:& filters/filters {:shape shape :filter-id filter-id}]
|
|
||||||
[:& filters/filters {:shape shape-without-blur :filter-id (dm/fmt "filter-shadow-%" render-id)}]
|
;; The filters for frames should be setup inside the container.
|
||||||
[:& filters/filters {:shape shape-without-shadows :filter-id (dm/fmt "filter-blur-%" render-id)}]
|
(when-not (cfh/frame-shape? shape)
|
||||||
|
[:*
|
||||||
|
[:& filters/filters {:shape shape :filter-id filter-id}]
|
||||||
|
[:& filters/filters {:shape shape-without-blur :filter-id (dm/fmt "filter-shadow-%" render-id)}]
|
||||||
|
[:& filters/filters {:shape shape-without-shadows :filter-id (dm/fmt "filter-blur-%" render-id)}]])
|
||||||
|
|
||||||
[:& frame/frame-clip-def {:shape shape :render-id render-id}]
|
[:& frame/frame-clip-def {:shape shape :render-id render-id}]
|
||||||
|
|
||||||
;; Text fills need to be defined afterwards because they are specified per text-block
|
;; Text fills need to be defined afterwards because they are specified per text-block
|
||||||
|
|||||||
@@ -98,7 +98,11 @@
|
|||||||
(obj/set! "fill" (str "url(#fill-" index "-" render-id ")")))}
|
(obj/set! "fill" (str "url(#fill-" index "-" render-id ")")))}
|
||||||
(cond-> browser-props
|
(cond-> browser-props
|
||||||
(obj/merge! browser-props)))
|
(obj/merge! browser-props)))
|
||||||
shape (assoc shape :fills (:fills data))
|
shape (-> shape
|
||||||
|
(assoc :fills (:fills data))
|
||||||
|
;; The text elements have the shadow and blur already applied in the
|
||||||
|
;; group parent.
|
||||||
|
(dissoc :shadow :blur))
|
||||||
|
|
||||||
;; Need to create new render-id per text-block
|
;; Need to create new render-id per text-block
|
||||||
render-id (dm/str render-id "-" index)]
|
render-id (dm/str render-id "-" index)]
|
||||||
|
|||||||
@@ -42,11 +42,10 @@
|
|||||||
[:section {:class (stl/css :exception-layout)}
|
[:section {:class (stl/css :exception-layout)}
|
||||||
[:button
|
[:button
|
||||||
{:class (stl/css :exception-header)
|
{:class (stl/css :exception-header)
|
||||||
:on-click rt/nav-root}
|
:on-click on-nav-root}
|
||||||
i/logo-icon
|
i/logo-icon
|
||||||
(when profile-id
|
(when profile-id
|
||||||
(str "< "
|
(str "< " (tr "not-found.no-permission.go-dashboard")))]
|
||||||
(tr "not-found.no-permission.go-dashboard")))]
|
|
||||||
[:div {:class (stl/css :deco-before)} i/logo-error-screen]
|
[:div {:class (stl/css :deco-before)} i/logo-error-screen]
|
||||||
(when-not profile-id
|
(when-not profile-id
|
||||||
[:button {:class (stl/css :login-header)
|
[:button {:class (stl/css :login-header)
|
||||||
|
|||||||
@@ -141,6 +141,7 @@
|
|||||||
(st/emit! (ptk/event ::ev/event {::ev/name "remove-plugin"
|
(st/emit! (ptk/event ::ev/event {::ev/name "remove-plugin"
|
||||||
:name (:name plugin)
|
:name (:name plugin)
|
||||||
:host (:host plugin)}))
|
:host (:host plugin)}))
|
||||||
|
(dp/close-plugin! plugin)
|
||||||
(preg/remove-plugin! plugin)
|
(preg/remove-plugin! plugin)
|
||||||
(reset! plugins-state* (preg/plugins-list)))))]
|
(reset! plugins-state* (preg/plugins-list)))))]
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.files.helpers :as cfh]
|
|
||||||
[app.common.geom.shapes.bounds :as gsb]
|
[app.common.geom.shapes.bounds :as gsb]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.common.thumbnails :as thc]
|
[app.common.thumbnails :as thc]
|
||||||
@@ -45,7 +44,7 @@
|
|||||||
(refs/children-objects shape-id))
|
(refs/children-objects shape-id))
|
||||||
childs (mf/deref childs-ref)]
|
childs (mf/deref childs-ref)]
|
||||||
|
|
||||||
[:& shape-container {:shape shape :ref ref :disable-shadows? (cfh/is-direct-child-of-root? shape)}
|
[:& shape-container {:shape shape :ref ref}
|
||||||
[:& frame-shape {:shape shape :childs childs}]
|
[:& frame-shape {:shape shape :childs childs}]
|
||||||
(when *assert*
|
(when *assert*
|
||||||
[:& wsd/shape-debug {:shape shape}])]))))
|
[:& wsd/shape-debug {:shape shape}])]))))
|
||||||
@@ -187,7 +186,7 @@
|
|||||||
|
|
||||||
(fdm/use-dynamic-modifiers objects (mf/ref-val content-ref) modifiers)
|
(fdm/use-dynamic-modifiers objects (mf/ref-val content-ref) modifiers)
|
||||||
|
|
||||||
[:& shape-container {:shape shape :disable-shadows? thumbnail?}
|
[:& shape-container {:shape shape}
|
||||||
[:g.frame-container
|
[:g.frame-container
|
||||||
{:id (dm/str "frame-container-" frame-id)
|
{:id (dm/str "frame-container-" frame-id)
|
||||||
:key "frame-container"
|
:key "frame-container"
|
||||||
|
|||||||
@@ -230,6 +230,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layer-row:hover .element-actions.selected & {
|
||||||
|
opacity: $op-10;
|
||||||
|
}
|
||||||
|
|
||||||
.layer-row.highlight &,
|
.layer-row.highlight &,
|
||||||
.layer-row:hover & {
|
.layer-row:hover & {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -218,7 +218,7 @@
|
|||||||
(rx/filter some?))))))
|
(rx/filter some?))))))
|
||||||
|
|
||||||
over-shapes-stream
|
over-shapes-stream
|
||||||
(mf/with-memo [move-stream mod-str]
|
(mf/with-memo [query-point move-stream mod-str]
|
||||||
(->> (rx/merge
|
(->> (rx/merge
|
||||||
;; This stream works to "refresh" the outlines when the control is pressed
|
;; This stream works to "refresh" the outlines when the control is pressed
|
||||||
;; but the mouse has not been moved from its position.
|
;; but the mouse has not been moved from its position.
|
||||||
|
|||||||
Reference in New Issue
Block a user