mirror of
https://github.com/penpot/penpot.git
synced 2025-05-13 22:36:39 +02:00
✨ Improve layers performance.
This commit is contained in:
parent
45bf06733f
commit
d6d38283e7
8 changed files with 122 additions and 60 deletions
|
@ -93,7 +93,7 @@ function readLocales() {
|
||||||
return JSON.stringify(result);
|
return JSON.stringify(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
function readConfig() {
|
function readConfig(data) {
|
||||||
const publicURL = process.env.UXBOX_PUBLIC_URL;
|
const publicURL = process.env.UXBOX_PUBLIC_URL;
|
||||||
const demoWarn = process.env.UXBOX_DEMO_WARNING;
|
const demoWarn = process.env.UXBOX_DEMO_WARNING;
|
||||||
const deployDate = process.env.UXBOX_DEPLOY_DATE;
|
const deployDate = process.env.UXBOX_DEPLOY_DATE;
|
||||||
|
@ -115,6 +115,8 @@ function readConfig() {
|
||||||
cfg.deployCommit = deployCommit;
|
cfg.deployCommit = deployCommit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.assign(cfg, data);
|
||||||
|
|
||||||
return JSON.stringify(cfg);
|
return JSON.stringify(cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +130,7 @@ function templatePipeline(options) {
|
||||||
const themes = ["default"];
|
const themes = ["default"];
|
||||||
|
|
||||||
const locales = readLocales();
|
const locales = readLocales();
|
||||||
const config = readConfig();
|
const config = readConfig({themes});
|
||||||
|
|
||||||
const tmpl = mustache({
|
const tmpl = mustache({
|
||||||
ts: ts,
|
ts: ts,
|
||||||
|
@ -174,22 +176,21 @@ gulp.task("templates", gulp.series("template:main"));
|
||||||
* Development
|
* Development
|
||||||
***********************************************/
|
***********************************************/
|
||||||
|
|
||||||
gulp.task("dev:clean", function(next) {
|
gulp.task("clean", function(next) {
|
||||||
rimraf(paths.output, next);
|
rimraf(paths.output, next);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("dev:copy:images", function() {
|
gulp.task("copy:assets:images", function() {
|
||||||
return gulp.src(paths.resources + "images/**/*")
|
return gulp.src(paths.resources + "images/**/*")
|
||||||
.pipe(gulp.dest(paths.output + "images/"));
|
.pipe(gulp.dest(paths.output + "images/"));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("dev:copy:fonts", function() {
|
gulp.task("copy:assets:fonts", function() {
|
||||||
return gulp.src(paths.resources + "fonts/**/*")
|
return gulp.src(paths.resources + "fonts/**/*")
|
||||||
.pipe(gulp.dest(paths.output + "fonts/"));
|
.pipe(gulp.dest(paths.output + "fonts/"));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("dev:copy", gulp.parallel("dev:copy:images",
|
gulp.task("copy:assets", gulp.parallel("copy:assets:images", "copy:assets:fonts"));
|
||||||
"dev:copy:fonts"));
|
|
||||||
|
|
||||||
gulp.task("dev:dirs", function(next) {
|
gulp.task("dev:dirs", function(next) {
|
||||||
mkdirp("./resources/public/css/").then(() => next())
|
mkdirp("./resources/public/css/").then(() => next())
|
||||||
|
@ -198,18 +199,18 @@ gulp.task("dev:dirs", function(next) {
|
||||||
gulp.task("watch:main", function() {
|
gulp.task("watch:main", function() {
|
||||||
gulp.watch(paths.scss, gulp.series("scss"));
|
gulp.watch(paths.scss, gulp.series("scss"));
|
||||||
gulp.watch(paths.resources + "images/**/*",
|
gulp.watch(paths.resources + "images/**/*",
|
||||||
gulp.series("svg:sprite",
|
gulp.series("svg:sprite", "copy:assets:images"));
|
||||||
"dev:copy:images"));
|
|
||||||
|
|
||||||
gulp.watch([paths.resources + "templates/*.mustache",
|
gulp.watch([paths.resources + "templates/*.mustache",
|
||||||
paths.resources + "locales.json"],
|
paths.resources + "locales.json"],
|
||||||
gulp.series("templates"));
|
gulp.series("templates"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gulp.task("build", gulp.parallel("scss", "svg:sprite", "templates", "copy:assets"));
|
||||||
|
|
||||||
gulp.task("watch", gulp.series(
|
gulp.task("watch", gulp.series(
|
||||||
"dev:dirs",
|
"dev:dirs",
|
||||||
gulp.parallel("scss", "templates", "svg:sprite"),
|
"build",
|
||||||
"dev:copy",
|
|
||||||
"watch:main"
|
"watch:main"
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -231,10 +232,3 @@ gulp.task("dist:gzip", function() {
|
||||||
.pipe(gzip({gzipOptions: {level: 9}}))
|
.pipe(gzip({gzipOptions: {level: 9}}))
|
||||||
.pipe(gulp.dest(paths.dist));
|
.pipe(gulp.dest(paths.dist));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("dist", gulp.series(
|
|
||||||
"dev:clean",
|
|
||||||
"dist:clean",
|
|
||||||
gulp.parallel("scss", "templates", "svg:sprite", "dev:copy"),
|
|
||||||
"dist:copy"
|
|
||||||
));
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
|
||||||
"dashboard.grid.delete" : {
|
"dashboard.grid.delete" : {
|
||||||
"used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:61", "src/uxbox/main/ui/dashboard/grid.cljs:92" ],
|
"used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:61", "src/uxbox/main/ui/dashboard/grid.cljs:92" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
|
|
|
@ -18,8 +18,7 @@
|
||||||
window.uxboxTranslations = JSON.parse({{& translations }});
|
window.uxboxTranslations = JSON.parse({{& translations }});
|
||||||
window.uxboxThemes = {{& themes }};
|
window.uxboxThemes = {{& themes }};
|
||||||
</script>
|
</script>
|
||||||
<script src="/js/shared.js?ts={{& ts}}"></script>
|
<script src="/js/shared.js?ts={{& ts }}"></script>
|
||||||
<script src="/js/main.js?ts={{& ts}}"></script>
|
<script src="/js/main.js?ts={{& ts }}"></script>
|
||||||
<script>uxbox.main.init()</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -7,9 +7,11 @@ npm ci
|
||||||
|
|
||||||
export NODE_ENV=production;
|
export NODE_ENV=production;
|
||||||
|
|
||||||
npx gulp dist:clean || exit 1;
|
# Clean the output directory
|
||||||
npx gulp dist || exit 1;
|
npx gulp clean || exit 1;
|
||||||
|
|
||||||
shadow-cljs release main
|
shadow-cljs release main
|
||||||
|
npx gulp build || exit 1;
|
||||||
|
npx gulp dist:clean || exit 1;
|
||||||
|
npx gulp dist:copy || exit 1;
|
||||||
npx gulp dist:gzip || exit 1;
|
npx gulp dist:gzip || exit 1;
|
||||||
|
|
|
@ -13,16 +13,17 @@
|
||||||
:modules
|
:modules
|
||||||
{:shared {:entries []}
|
{:shared {:entries []}
|
||||||
:main {:entries [uxbox.main]
|
:main {:entries [uxbox.main]
|
||||||
:depends-on #{:shared}}
|
:depends-on #{:shared}
|
||||||
|
:init-fn uxbox.main/init}
|
||||||
:worker {:entries [uxbox.worker]
|
:worker {:entries [uxbox.worker]
|
||||||
:web-worker true
|
:web-worker true
|
||||||
:depends-on #{:shared}}}
|
:depends-on #{:shared}}}
|
||||||
:compiler-options
|
:compiler-options
|
||||||
{:output-feature-set :es8
|
{:output-feature-set :es8
|
||||||
|
;; :optimizations :simple
|
||||||
:output-wrapper false}
|
:output-wrapper false}
|
||||||
:release
|
:release
|
||||||
{:output-dir "target/dist/js"
|
{:compiler-options
|
||||||
:compiler-options
|
|
||||||
{:fn-invoke-direct true
|
{:fn-invoke-direct true
|
||||||
:source-map true
|
:source-map true
|
||||||
:anon-fn-naming-policy :off
|
:anon-fn-naming-policy :off
|
||||||
|
|
|
@ -1907,6 +1907,20 @@
|
||||||
(let [page-id (::page-id state)]
|
(let [page-id (::page-id state)]
|
||||||
(assoc-in state [:workspace-data page-id :objects id :hidden] hidden?)))
|
(assoc-in state [:workspace-data page-id :objects id :hidden] hidden?)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn toggle-collapse
|
||||||
|
[id]
|
||||||
|
(ptk/reify ::toggle-collapse
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(update-in state [:workspace-local :expanded id] not))))
|
||||||
|
|
||||||
|
(def collapse-all
|
||||||
|
(ptk/reify ::collapse-all
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(update state :workspace-local dissoc :expanded))))
|
||||||
|
|
||||||
;; --- Shape Blocking
|
;; --- Shape Blocking
|
||||||
|
|
||||||
(declare impl-update-shape-blocked)
|
(declare impl-update-shape-blocked)
|
||||||
|
|
|
@ -11,9 +11,11 @@
|
||||||
(ns uxbox.main.ui.workspace.sidebar.layers
|
(ns uxbox.main.ui.workspace.sidebar.layers
|
||||||
(:require
|
(:require
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
|
[okulary.core :as l]
|
||||||
[uxbox.common.data :as d]
|
[uxbox.common.data :as d]
|
||||||
[uxbox.builtins.icons :as i]
|
[uxbox.builtins.icons :as i]
|
||||||
[uxbox.main.data.workspace :as dw]
|
[uxbox.main.data.workspace :as dw]
|
||||||
|
[uxbox.main.data.helpers :as dh]
|
||||||
[uxbox.main.refs :as refs]
|
[uxbox.main.refs :as refs]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
[uxbox.main.ui.hooks :as hooks]
|
[uxbox.main.ui.hooks :as hooks]
|
||||||
|
@ -74,33 +76,27 @@
|
||||||
{:on-double-click on-click}
|
{:on-double-click on-click}
|
||||||
(:name shape "")])))
|
(:name shape "")])))
|
||||||
|
|
||||||
(defn- layer-item-memo-equals?
|
(defn- make-collapsed-iref
|
||||||
[nprops oprops]
|
[id]
|
||||||
(let [n-item (unchecked-get nprops "item")
|
#(-> (l/in [:expanded id])
|
||||||
o-item (unchecked-get oprops "item")
|
(l/derived refs/workspace-local)))
|
||||||
n-selc (unchecked-get nprops "selected")
|
|
||||||
o-selc (unchecked-get oprops "selected")
|
|
||||||
n-indx (unchecked-get nprops "index")
|
|
||||||
o-indx (unchecked-get oprops "index")]
|
|
||||||
;; (js/console.log "FOR" (:name n-item)
|
|
||||||
;; "NEW SEL" n-selc
|
|
||||||
;; "OLD SEL" o-selc)6
|
|
||||||
(and (identical? n-item o-item)
|
|
||||||
(identical? n-indx o-indx)
|
|
||||||
(identical? n-selc o-selc))))
|
|
||||||
|
|
||||||
(declare layer-item)
|
|
||||||
|
|
||||||
(mf/defc layer-item
|
(mf/defc layer-item
|
||||||
;; {::mf/wrap [#(mf/memo' % layer-item-memo-equals?)]}
|
|
||||||
[{:keys [index item selected objects] :as props}]
|
[{:keys [index item selected objects] :as props}]
|
||||||
(let [selected? (contains? selected (:id item))
|
(let [selected? (contains? selected (:id item))
|
||||||
collapsed? (mf/use-state false)
|
|
||||||
|
expanded-iref (mf/use-memo
|
||||||
|
(mf/deps (:id item))
|
||||||
|
(make-collapsed-iref (:id item)))
|
||||||
|
|
||||||
|
expanded? (mf/deref expanded-iref)
|
||||||
|
|
||||||
toggle-collapse
|
toggle-collapse
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(swap! collapsed? not))
|
(if (and expanded? (kbd/shift? event))
|
||||||
|
(st/emit! dw/collapse-all)
|
||||||
|
(st/emit! (dw/toggle-collapse (:id item)))))
|
||||||
|
|
||||||
toggle-blocking
|
toggle-blocking
|
||||||
(fn [event]
|
(fn [event]
|
||||||
|
@ -163,7 +159,6 @@
|
||||||
:index index
|
:index index
|
||||||
:name (:name item)})
|
:name (:name item)})
|
||||||
]
|
]
|
||||||
;; (prn "layer-item" (:name item) index)
|
|
||||||
[:li {:on-context-menu on-context-menu
|
[:li {:on-context-menu on-context-menu
|
||||||
:ref dref
|
:ref dref
|
||||||
:data-index index
|
:data-index index
|
||||||
|
@ -190,36 +185,91 @@
|
||||||
(when (:shapes item)
|
(when (:shapes item)
|
||||||
[:span.toggle-content
|
[:span.toggle-content
|
||||||
{:on-click toggle-collapse
|
{:on-click toggle-collapse
|
||||||
:class (when-not @collapsed? "inverse")}
|
:class (when expanded? "inverse")}
|
||||||
i/arrow-slide])]
|
i/arrow-slide])]
|
||||||
(when (and (:shapes item) (not @collapsed?))
|
(when (and (:shapes item) expanded?)
|
||||||
[:ul.element-children
|
[:ul.element-children
|
||||||
(for [[index id] (reverse (d/enumerate (:shapes item)))]
|
(for [[index id] (reverse (d/enumerate (:shapes item)))]
|
||||||
(when-let [item (get objects id)]
|
(when-let [item (get objects id)]
|
||||||
[:& uxbox.main.ui.workspace.sidebar.layers/layer-item
|
[:& layer-item
|
||||||
{:item item
|
{:item item
|
||||||
:selected selected
|
:selected selected
|
||||||
:index index
|
:index index
|
||||||
:objects objects
|
:objects objects
|
||||||
:key (:id item)}]))])]))
|
:key (:id item)}]))])]))
|
||||||
|
|
||||||
|
(defn frame-wrapper-memo-equals?
|
||||||
|
[oprops nprops]
|
||||||
|
(let [new-sel (unchecked-get nprops "selected")
|
||||||
|
old-sel (unchecked-get oprops "selected")
|
||||||
|
new-itm (unchecked-get nprops "item")
|
||||||
|
old-itm (unchecked-get oprops "item")
|
||||||
|
new-idx (unchecked-get nprops "index")
|
||||||
|
old-idx (unchecked-get oprops "index")
|
||||||
|
new-obs (unchecked-get nprops "objects")
|
||||||
|
old-obs (unchecked-get oprops "objects")]
|
||||||
|
(and (= new-itm old-itm)
|
||||||
|
(identical? new-idx old-idx)
|
||||||
|
(let [childs (dh/get-children (:id new-itm) new-obs)
|
||||||
|
childs' (conj childs (:id new-itm))]
|
||||||
|
(and (or (= new-sel old-sel)
|
||||||
|
(not (or (boolean (some new-sel childs'))
|
||||||
|
(boolean (some old-sel childs')))))
|
||||||
|
(loop [ids (rest childs)
|
||||||
|
id (first childs)]
|
||||||
|
(if (nil? id)
|
||||||
|
true
|
||||||
|
(if (= (get new-obs id)
|
||||||
|
(get old-obs id))
|
||||||
|
(recur (rest ids)
|
||||||
|
(first ids))
|
||||||
|
false))))))))
|
||||||
|
|
||||||
|
;; This components is a piece for sharding equality check between top
|
||||||
|
;; level frames and try to avoid rerender frames that are does not
|
||||||
|
;; affected by the selected set.
|
||||||
|
|
||||||
|
(mf/defc frame-wrapper
|
||||||
|
{::mf/wrap-props false
|
||||||
|
::mf/wrap [#(mf/memo' % frame-wrapper-memo-equals?)]}
|
||||||
|
[props]
|
||||||
|
[:> layer-item props])
|
||||||
|
|
||||||
|
(def ^:private layers-objects
|
||||||
|
(letfn [(strip-data [obj]
|
||||||
|
(select-keys obj [:id :name :blocked :hidden :shapes :type]))
|
||||||
|
(selector [{:keys [objects] :as data}]
|
||||||
|
(persistent!
|
||||||
|
(reduce-kv (fn [res id obj]
|
||||||
|
(assoc! res id (strip-data obj)))
|
||||||
|
(transient {})
|
||||||
|
objects)))]
|
||||||
|
(l/derived selector refs/workspace-data =)))
|
||||||
|
|
||||||
(mf/defc layers-tree
|
(mf/defc layers-tree
|
||||||
{::mf/wrap [mf/memo]}
|
{::mf/wrap [mf/memo]}
|
||||||
[props]
|
[]
|
||||||
(let [selected (mf/deref refs/selected-shapes)
|
(let [selected (mf/deref refs/selected-shapes)
|
||||||
data (mf/deref refs/workspace-data)
|
objects (mf/deref layers-objects)
|
||||||
objects (:objects data)
|
|
||||||
root (get objects uuid/zero)]
|
root (get objects uuid/zero)]
|
||||||
|
|
||||||
;; [:& perf/profiler {:label "layers-tree" :enabled false}
|
;; [:& perf/profiler {:label "layers-tree" :enabled false}
|
||||||
[:ul.element-list
|
[:ul.element-list
|
||||||
(for [[index id] (reverse (d/enumerate (:shapes root)))]
|
(for [[index id] (reverse (d/enumerate (:shapes root)))]
|
||||||
[:& layer-item
|
(let [obj (get objects id)]
|
||||||
{:item (get objects id)
|
(if (= :frame (:type obj))
|
||||||
:selected selected
|
[:& frame-wrapper
|
||||||
:index index
|
{:item (get objects id)
|
||||||
:objects objects
|
:selected selected
|
||||||
:key id}])]))
|
:index index
|
||||||
|
:objects objects
|
||||||
|
:key id}]
|
||||||
|
[:& layer-item
|
||||||
|
{:item (get objects id)
|
||||||
|
:selected selected
|
||||||
|
:index index
|
||||||
|
:objects objects
|
||||||
|
:key id}])))]))
|
||||||
|
|
||||||
|
|
||||||
;; --- Layers Toolbox
|
;; --- Layers Toolbox
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
(ns uxbox.main.worker
|
(ns uxbox.main.worker
|
||||||
(:require
|
(:require
|
||||||
[cljs.spec.alpha :as s]
|
[cljs.spec.alpha :as s]
|
||||||
|
[uxbox.config :as cfg]
|
||||||
[uxbox.common.spec :as us]
|
[uxbox.common.spec :as us]
|
||||||
[uxbox.util.worker :as uw]))
|
[uxbox.util.worker :as uw]))
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue