mirror of
https://github.com/penpot/penpot.git
synced 2025-05-14 22:36:37 +02:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
b90a308d66
11 changed files with 244 additions and 239 deletions
|
@ -22,6 +22,7 @@
|
||||||
### :sparkles: New features
|
### :sparkles: New features
|
||||||
|
|
||||||
- Group assets by drag and drop [Taiga #2831](https://tree.taiga.io/project/penpot/us/2831)
|
- Group assets by drag and drop [Taiga #2831](https://tree.taiga.io/project/penpot/us/2831)
|
||||||
|
- Search and filter layers [Taiga #2564](https://tree.taiga.io/project/penpot/us/2564)
|
||||||
- Constraints are not well assigned when default and multiselection [Taiga #3069](https://tree.taiga.io/project/penpot/issue/3069)
|
- Constraints are not well assigned when default and multiselection [Taiga #3069](https://tree.taiga.io/project/penpot/issue/3069)
|
||||||
- Exporting big files flow [Taiga #2218](https://tree.taiga.io/project/penpot/us/2218)
|
- Exporting big files flow [Taiga #2218](https://tree.taiga.io/project/penpot/us/2218)
|
||||||
- Multiexport from main menu [Taiga #520](https://tree.taiga.io/project/penpot/us/28541)
|
- Multiexport from main menu [Taiga #520](https://tree.taiga.io/project/penpot/us/28541)
|
||||||
|
@ -51,6 +52,7 @@
|
||||||
### :bug: Bugs fixed
|
### :bug: Bugs fixed
|
||||||
|
|
||||||
- Round the size values on handoff to two decimals [Taiga #3227](https://tree.taiga.io/project/penpot/issue/3227)
|
- Round the size values on handoff to two decimals [Taiga #3227](https://tree.taiga.io/project/penpot/issue/3227)
|
||||||
|
- Fix blend modes ignored in component updates [Taiga #2626](https://tree.taiga.io/project/penpot/issue/2626)
|
||||||
- Fix internal error when hoverin over shape [Taiga #3237](https://tree.taiga.io/project/penpot/issue/3237)
|
- Fix internal error when hoverin over shape [Taiga #3237](https://tree.taiga.io/project/penpot/issue/3237)
|
||||||
- Fix mouse leave in handoff close overlay animation breaks [Taiga #3173](https://tree.taiga.io/project/penpot/issue/3173)
|
- Fix mouse leave in handoff close overlay animation breaks [Taiga #3173](https://tree.taiga.io/project/penpot/issue/3173)
|
||||||
- Fix different behaviour during image drag [Taiga #2279](https://tree.taiga.io/project/penpot/issue/2279)
|
- Fix different behaviour during image drag [Taiga #2279](https://tree.taiga.io/project/penpot/issue/2279)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
export PENPOT_HOST=devenv
|
export PENPOT_HOST=devenv
|
||||||
export PENPOT_TENANT=dev
|
export PENPOT_TENANT=dev
|
||||||
export PENPOT_FLAGS="$PENPOT_FLAGS enable-backend-asserts enable-audit-log enable-cors enable-transit-readable-response enable-demo-users"
|
export PENPOT_FLAGS="$PENPOT_FLAGS enable-backend-asserts enable-audit-log enable-transit-readable-response enable-demo-users disable-secure-session-cookies"
|
||||||
|
|
||||||
# export PENPOT_DATABASE_URI="postgresql://172.17.0.1:5432/penpot"
|
# export PENPOT_DATABASE_URI="postgresql://172.17.0.1:5432/penpot"
|
||||||
# export PENPOT_DATABASE_USERNAME="penpot"
|
# export PENPOT_DATABASE_USERNAME="penpot"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
export PENPOT_HOST=devenv
|
export PENPOT_HOST=devenv
|
||||||
export PENPOT_TENANT=dev
|
export PENPOT_TENANT=dev
|
||||||
export PENPOT_FLAGS="$PENPOT_FLAGS enable-backend-asserts enable-audit-log enable-cors enable-transit-readable-response enable-demo-users disable-secure-session-cookies"
|
export PENPOT_FLAGS="$PENPOT_FLAGS enable-backend-asserts enable-audit-log enable-transit-readable-response enable-demo-users disable-secure-session-cookies"
|
||||||
|
|
||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,8 @@
|
||||||
:height :geometry-group
|
:height :geometry-group
|
||||||
:transform :geometry-group
|
:transform :geometry-group
|
||||||
:transform-inverse :geometry-group
|
:transform-inverse :geometry-group
|
||||||
|
:opacity :layer-effects-group
|
||||||
|
:blend-mode :layer-effects-group
|
||||||
:shadow :shadow-group
|
:shadow :shadow-group
|
||||||
:blur :blur-group
|
:blur :blur-group
|
||||||
:masked-group? :mask-group
|
:masked-group? :mask-group
|
||||||
|
|
|
@ -14,7 +14,6 @@ volumes:
|
||||||
|
|
||||||
services:
|
services:
|
||||||
main:
|
main:
|
||||||
profiles: ["full"]
|
|
||||||
privileged: true
|
privileged: true
|
||||||
image: "penpotapp/devenv:latest"
|
image: "penpotapp/devenv:latest"
|
||||||
build:
|
build:
|
||||||
|
@ -67,7 +66,6 @@ services:
|
||||||
- PENPOT_LDAP_ATTRS_PHOTO=jpegPhoto
|
- PENPOT_LDAP_ATTRS_PHOTO=jpegPhoto
|
||||||
|
|
||||||
minio:
|
minio:
|
||||||
profiles: ["full"]
|
|
||||||
image: "minio/minio:latest"
|
image: "minio/minio:latest"
|
||||||
command: minio server /mnt/data --console-address ":9001"
|
command: minio server /mnt/data --console-address ":9001"
|
||||||
|
|
||||||
|
@ -82,56 +80,6 @@ services:
|
||||||
- 9000:9000
|
- 9000:9000
|
||||||
- 9001:9001
|
- 9001:9001
|
||||||
|
|
||||||
backend:
|
|
||||||
profiles: ["backend"]
|
|
||||||
privileged: true
|
|
||||||
image: "penpotapp/devenv:latest"
|
|
||||||
build:
|
|
||||||
context: "."
|
|
||||||
container_name: "penpot-backend"
|
|
||||||
stop_signal: SIGINT
|
|
||||||
|
|
||||||
depends_on:
|
|
||||||
- postgres
|
|
||||||
- redis
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- "user_data:/home/penpot/"
|
|
||||||
- "${PWD}:/home/penpot/penpot:z"
|
|
||||||
|
|
||||||
ports:
|
|
||||||
- 6060:6060
|
|
||||||
- 6061:6061
|
|
||||||
- 9090:9090
|
|
||||||
|
|
||||||
environment:
|
|
||||||
- EXTERNAL_UID=${CURRENT_USER_ID}
|
|
||||||
- PENPOT_SECRET_KEY=super-secret-devenv-key
|
|
||||||
|
|
||||||
# SMTP setup
|
|
||||||
- PENPOT_SMTP_ENABLED=true
|
|
||||||
- PENPOT_SMTP_DEFAULT_FROM=no-reply@example.com
|
|
||||||
- PENPOT_SMTP_DEFAULT_REPLY_TO=no-reply@example.com
|
|
||||||
- PENPOT_SMTP_HOST=mailer
|
|
||||||
- PENPOT_SMTP_PORT=1025
|
|
||||||
- PENPOT_SMTP_USERNAME=
|
|
||||||
- PENPOT_SMTP_PASSWORD=
|
|
||||||
- PENPOT_SMTP_SSL=false
|
|
||||||
- PENPOT_SMTP_TLS=false
|
|
||||||
|
|
||||||
# LDAP setup
|
|
||||||
- PENPOT_LDAP_HOST=ldap
|
|
||||||
- PENPOT_LDAP_PORT=10389
|
|
||||||
- PENPOT_LDAP_SSL=false
|
|
||||||
- PENPOT_LDAP_STARTTLS=false
|
|
||||||
- PENPOT_LDAP_BASE_DN=ou=people,dc=planetexpress,dc=com
|
|
||||||
- PENPOT_LDAP_BIND_DN=cn=admin,dc=planetexpress,dc=com
|
|
||||||
- PENPOT_LDAP_BIND_PASSWORD=GoodNewsEveryone
|
|
||||||
- PENPOT_LDAP_ATTRS_USERNAME=uid
|
|
||||||
- PENPOT_LDAP_ATTRS_EMAIL=mail
|
|
||||||
- PENPOT_LDAP_ATTRS_FULLNAME=cn
|
|
||||||
- PENPOT_LDAP_ATTRS_PHOTO=jpegPhoto
|
|
||||||
|
|
||||||
# keycloak:
|
# keycloak:
|
||||||
# image: "quay.io/keycloak/keycloak:15.0.2"
|
# image: "quay.io/keycloak/keycloak:15.0.2"
|
||||||
# environment:
|
# environment:
|
||||||
|
|
|
@ -45,12 +45,12 @@ RUN set -eux; \
|
||||||
esac; \
|
esac; \
|
||||||
curl -LfsSo /tmp/openjdk.tar.gz ${BINARY_URL}; \
|
curl -LfsSo /tmp/openjdk.tar.gz ${BINARY_URL}; \
|
||||||
echo "${ESUM} */tmp/openjdk.tar.gz" | sha256sum -c -; \
|
echo "${ESUM} */tmp/openjdk.tar.gz" | sha256sum -c -; \
|
||||||
mkdir -p /usr/lib/jvm/openjdk16; \
|
mkdir -p /usr/lib/jvm/openjdk; \
|
||||||
cd /usr/lib/jvm/openjdk16; \
|
cd /usr/lib/jvm/openjdk; \
|
||||||
tar -xf /tmp/openjdk.tar.gz --strip-components=1; \
|
tar -xf /tmp/openjdk.tar.gz --strip-components=1; \
|
||||||
rm -rf /tmp/openjdk.tar.gz;
|
rm -rf /tmp/openjdk.tar.gz;
|
||||||
|
|
||||||
ENV JAVA_HOME=/usr/lib/jvm/openjdk16 PATH="/usr/lib/jvm/openjdk16/bin:$PATH"
|
ENV JAVA_HOME=/usr/lib/jvm/openjdk PATH="/usr/lib/jvm/openjdk/bin:$PATH"
|
||||||
ADD ./bundle-backend/ /opt/penpot/backend/
|
ADD ./bundle-backend/ /opt/penpot/backend/
|
||||||
WORKDIR /opt/penpot/backend
|
WORKDIR /opt/penpot/backend
|
||||||
CMD ["/bin/bash", "run.sh"]
|
CMD ["/bin/bash", "run.sh"]
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
FROM debian:bullseye
|
FROM ubuntu:20.04
|
||||||
LABEL maintainer="Andrey Antukh <niwi@niwi.nz>"
|
LABEL maintainer="Andrey Antukh <niwi@niwi.nz>"
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
ENV LANG=en_US.UTF-8 \
|
ENV LANG=en_US.UTF-8 \
|
||||||
LC_ALL=en_US.UTF-8 \
|
LC_ALL=en_US.UTF-8 \
|
||||||
NODE_VERSION=v16.13.1 \
|
NODE_VERSION=v16.14.2
|
||||||
PENPOT_BROWSER_EXECUTABLE_PATH=/usr/bin/chromium
|
|
||||||
|
|
||||||
RUN set -ex; \
|
RUN set -ex; \
|
||||||
mkdir -p /etc/resolvconf/resolv.conf.d; \
|
mkdir -p /etc/resolvconf/resolv.conf.d; \
|
||||||
|
@ -60,7 +59,6 @@ RUN set -ex; \
|
||||||
fonts-liberation \
|
fonts-liberation \
|
||||||
libnss3 \
|
libnss3 \
|
||||||
libgbm1 \
|
libgbm1 \
|
||||||
chromium \
|
|
||||||
; \
|
; \
|
||||||
rm -rf /var/lib/apt/lists/*;
|
rm -rf /var/lib/apt/lists/*;
|
||||||
|
|
||||||
|
@ -95,6 +93,8 @@ WORKDIR /opt/app
|
||||||
|
|
||||||
ADD ./bundle-exporter/ /opt/app/
|
ADD ./bundle-exporter/ /opt/app/
|
||||||
|
|
||||||
RUN set -ex; yarn install;
|
RUN set -ex; \
|
||||||
|
yarn install; \
|
||||||
|
npx playwright install chromium;
|
||||||
|
|
||||||
CMD ["/usr/local/nodejs/bin/node", "app.js"]
|
CMD ["/usr/local/nodejs/bin/node", "app.js"]
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
[app.common.spec.file :as spec.file]
|
[app.common.spec.file :as spec.file]
|
||||||
[app.common.spec.typography :as spec.typography]
|
[app.common.spec.typography :as spec.typography]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
|
[app.main.data.dashboard :as dd]
|
||||||
[app.main.data.events :as ev]
|
[app.main.data.events :as ev]
|
||||||
[app.main.data.messages :as dm]
|
[app.main.data.messages :as dm]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
|
@ -38,6 +39,8 @@
|
||||||
;; Change this to :info :debug or :trace to debug this module, or :warn to reset to default
|
;; Change this to :info :debug or :trace to debug this module, or :warn to reset to default
|
||||||
(log/set-level! :warn)
|
(log/set-level! :warn)
|
||||||
|
|
||||||
|
(s/def ::file ::dd/file)
|
||||||
|
|
||||||
(defn- log-changes
|
(defn- log-changes
|
||||||
[changes file]
|
[changes file]
|
||||||
(let [extract-change
|
(let [extract-change
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.exceptions :as ex]
|
[app.common.exceptions :as ex]
|
||||||
[app.common.pprint :as pp]
|
[app.common.pprint :as pp]
|
||||||
|
[app.common.spec :as us]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
[app.main.data.messages :as msg]
|
[app.main.data.messages :as msg]
|
||||||
[app.main.data.users :as du]
|
[app.main.data.users :as du]
|
||||||
|
@ -131,6 +132,7 @@
|
||||||
(:name error)
|
(:name error)
|
||||||
(dm/str cf/public-uri "js/cljs-runtime/" (:file error))
|
(dm/str cf/public-uri "js/cljs-runtime/" (:file error))
|
||||||
(:line error))]
|
(:line error))]
|
||||||
|
|
||||||
(ts/schedule
|
(ts/schedule
|
||||||
#(st/emit! (msg/show {:content "Internal error: assertion."
|
#(st/emit! (msg/show {:content "Internal error: assertion."
|
||||||
:type :error
|
:type :error
|
||||||
|
@ -139,6 +141,7 @@
|
||||||
;; Print to the console some debugging info
|
;; Print to the console some debugging info
|
||||||
(js/console.group message)
|
(js/console.group message)
|
||||||
(js/console.info context)
|
(js/console.info context)
|
||||||
|
(js/console.log (us/pretty-explain error))
|
||||||
(js/console.groupEnd message)))
|
(js/console.groupEnd message)))
|
||||||
|
|
||||||
;; That are special case server-errors that should be treated
|
;; That are special case server-errors that should be treated
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
[app.util.object :as obj]
|
|
||||||
[app.util.timers :as ts]
|
[app.util.timers :as ts]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
|
@ -251,7 +250,8 @@
|
||||||
[:> layer-item props])
|
[:> layer-item props])
|
||||||
|
|
||||||
(mf/defc layers-tree
|
(mf/defc layers-tree
|
||||||
{::mf/wrap [#(mf/memo % =)]}
|
{::mf/wrap [#(mf/memo % =)
|
||||||
|
#(mf/throttle % 200)]}
|
||||||
[{:keys [objects] :as props}]
|
[{:keys [objects] :as props}]
|
||||||
(let [selected (mf/deref refs/selected-shapes)
|
(let [selected (mf/deref refs/selected-shapes)
|
||||||
selected (hooks/use-equal-memo selected)
|
selected (hooks/use-equal-memo selected)
|
||||||
|
@ -274,33 +274,101 @@
|
||||||
:objects objects
|
:objects objects
|
||||||
:key id}])))]]))
|
:key id}])))]]))
|
||||||
|
|
||||||
(mf/defc layers-tree-wrapper
|
(mf/defc filters-tree
|
||||||
{::mf/wrap-props false
|
{::mf/wrap [#(mf/memo % =)
|
||||||
::mf/wrap [mf/memo #(mf/throttle % 200)]}
|
#(mf/throttle % 200)]}
|
||||||
[props]
|
[{:keys [objects] :as props}]
|
||||||
(let [search (obj/get props "search")
|
(let [selected (mf/deref refs/selected-shapes)
|
||||||
filters (obj/get props "filters")
|
selected (hooks/use-equal-memo selected)
|
||||||
filters (if (some #{:shape} filters)
|
root (get objects uuid/zero)]
|
||||||
(conj filters :rect :circle :path :bool)
|
[:ul.element-list
|
||||||
filters)
|
(for [[index id] (d/enumerate (:shapes root))]
|
||||||
objects (-> (obj/get props "objects")
|
(when-let [obj (get objects id)]
|
||||||
(hooks/use-equal-memo))
|
[:& layer-item
|
||||||
|
{:item obj
|
||||||
|
:selected selected
|
||||||
|
:index index
|
||||||
|
:objects objects
|
||||||
|
:key id}]))]))
|
||||||
|
|
||||||
;; TODO: Fix performance
|
|
||||||
reparented-objects (d/mapm (fn [_ val]
|
(defn calc-reparented-objects
|
||||||
|
[objects]
|
||||||
|
|
||||||
|
(let [reparented-objects
|
||||||
|
(d/mapm (fn [_ val]
|
||||||
(assoc val :parent-id uuid/zero :shapes nil))
|
(assoc val :parent-id uuid/zero :shapes nil))
|
||||||
objects)
|
objects)
|
||||||
|
|
||||||
reparented-shapes (->> reparented-objects
|
reparented-shapes
|
||||||
|
(->> reparented-objects
|
||||||
keys
|
keys
|
||||||
(filter #(not= uuid/zero %))
|
(filter #(not= uuid/zero %))
|
||||||
vec)
|
vec)]
|
||||||
|
(update reparented-objects uuid/zero assoc :shapes reparented-shapes)))
|
||||||
|
|
||||||
reparented-objects (update reparented-objects uuid/zero assoc :shapes reparented-shapes)
|
;; --- Layers Toolbox
|
||||||
|
|
||||||
search-and-filters (mf/use-callback
|
(defn use-search
|
||||||
(mf/deps search filters)
|
[page objects]
|
||||||
|
(let [filter-state (mf/use-state {:show-search-box false
|
||||||
|
:show-filters-menu false
|
||||||
|
:search-text ""
|
||||||
|
:active-filters #{}
|
||||||
|
:num-items 100})
|
||||||
|
|
||||||
|
clear-search-text
|
||||||
|
(mf/use-callback
|
||||||
|
(fn []
|
||||||
|
(swap! filter-state assoc :search-text "" :num-items 100)))
|
||||||
|
|
||||||
|
update-search-text
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [event]
|
||||||
|
(let [value (-> event dom/get-target dom/get-value)]
|
||||||
|
(swap! filter-state assoc :search-text value :num-items 100))))
|
||||||
|
|
||||||
|
toggle-search
|
||||||
|
(mf/use-callback
|
||||||
|
(fn []
|
||||||
|
(swap! filter-state assoc :search-text "")
|
||||||
|
(swap! filter-state assoc :active-filters #{})
|
||||||
|
(swap! filter-state assoc :show-filters-menu false)
|
||||||
|
(swap! filter-state assoc :num-items 100)
|
||||||
|
(swap! filter-state update :show-search-box not)))
|
||||||
|
|
||||||
|
toggle-filters
|
||||||
|
(mf/use-callback
|
||||||
|
(fn []
|
||||||
|
(swap! filter-state update :show-filters-menu not)))
|
||||||
|
|
||||||
|
remove-filter
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps @filter-state)
|
||||||
|
(fn [key]
|
||||||
|
(fn [_]
|
||||||
|
(swap! filter-state update :active-filters disj key)
|
||||||
|
(swap! filter-state assoc :num-items 100))))
|
||||||
|
|
||||||
|
add-filter
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps @filter-state (:show-filters-menu @filter-state))
|
||||||
|
(fn [key]
|
||||||
|
(fn [_]
|
||||||
|
(swap! filter-state update :active-filters conj key)
|
||||||
|
(swap! filter-state assoc :num-items 100)
|
||||||
|
(toggle-filters))))
|
||||||
|
|
||||||
|
active?
|
||||||
|
(and
|
||||||
|
(:show-search-box @filter-state)
|
||||||
|
(or (d/not-empty? (:search-text @filter-state))
|
||||||
|
(d/not-empty? (:active-filters @filter-state))))
|
||||||
|
|
||||||
|
search-and-filters
|
||||||
(fn [[id shape]]
|
(fn [[id shape]]
|
||||||
|
(let [search (:search-text @filter-state)
|
||||||
|
filters (:active-filters @filter-state)]
|
||||||
(or
|
(or
|
||||||
(= uuid/zero id)
|
(= uuid/zero id)
|
||||||
(and
|
(and
|
||||||
|
@ -321,15 +389,64 @@
|
||||||
(some #{:mask} filters)
|
(some #{:mask} filters)
|
||||||
(true? (:masked-group? shape))))))))
|
(true? (:masked-group? shape))))))))
|
||||||
|
|
||||||
objects (if (and (= "" search) (empty? filters))
|
filtered-objects-total
|
||||||
objects
|
(mf/use-memo
|
||||||
(into {} (filter search-and-filters
|
(mf/deps objects active? @filter-state)
|
||||||
reparented-objects)))]
|
#(when active?
|
||||||
|
;; filterv so count is constant time
|
||||||
|
(filterv search-and-filters objects)))
|
||||||
|
|
||||||
[:& layers-tree {:objects objects}]))
|
filtered-objects
|
||||||
|
(mf/use-memo
|
||||||
|
(mf/deps filtered-objects-total)
|
||||||
|
#(when active?
|
||||||
|
(calc-reparented-objects
|
||||||
|
(into {}
|
||||||
|
(take (:num-items @filter-state))
|
||||||
|
filtered-objects-total))))
|
||||||
|
|
||||||
|
|
||||||
;; --- Layers Toolbox
|
handle-show-more
|
||||||
|
(fn []
|
||||||
|
(when (<= (:num-items @filter-state) (count filtered-objects-total))
|
||||||
|
(swap! filter-state update :num-items + 100)))]
|
||||||
|
|
||||||
|
[filtered-objects
|
||||||
|
handle-show-more
|
||||||
|
|
||||||
|
(mf/html
|
||||||
|
(if (:show-search-box @filter-state)
|
||||||
|
[:*
|
||||||
|
[:div.tool-window-bar.search
|
||||||
|
[:span.search-box
|
||||||
|
[:span.filter {:on-click toggle-filters :class (dom/classnames :active active?)} i/icon-filter]
|
||||||
|
[:span
|
||||||
|
[:input {:on-change update-search-text
|
||||||
|
:value (:search-text @filter-state)
|
||||||
|
:auto-focus (:show-search-box @filter-state)
|
||||||
|
:placeholder (tr "workspace.sidebar.layers.search")}]]
|
||||||
|
(when (not (= "" (:search-text @filter-state)))
|
||||||
|
[:span.clear {:on-click clear-search-text} i/exclude])]
|
||||||
|
[:span {:on-click toggle-search} i/cross]]
|
||||||
|
|
||||||
|
[:div.active-filters
|
||||||
|
(for [f (:active-filters @filter-state)]
|
||||||
|
[:span {:on-click (remove-filter f)}
|
||||||
|
(tr f) i/cross])]
|
||||||
|
|
||||||
|
(when (:show-filters-menu @filter-state)
|
||||||
|
[:div.filters-container
|
||||||
|
[:span{:on-click (add-filter :frame)} i/artboard (tr "workspace.sidebar.layers.frames")]
|
||||||
|
[:span{:on-click (add-filter :group)} i/folder (tr "workspace.sidebar.layers.groups")]
|
||||||
|
[:span{:on-click (add-filter :mask)} i/mask (tr "workspace.sidebar.layers.masks")]
|
||||||
|
[:span{:on-click (add-filter :component)} i/component (tr "workspace.sidebar.layers.components")]
|
||||||
|
[:span{:on-click (add-filter :text)} i/text (tr "workspace.sidebar.layers.texts")]
|
||||||
|
[:span{:on-click (add-filter :image)} i/image (tr "workspace.sidebar.layers.images")]
|
||||||
|
[:span{:on-click (add-filter :shape)} i/curve (tr "workspace.sidebar.layers.shapes")]])]
|
||||||
|
|
||||||
|
[:div.tool-window-bar
|
||||||
|
[:span (:name page)]
|
||||||
|
[:span {:on-click toggle-search} i/search]]))]))
|
||||||
|
|
||||||
(mf/defc layers-toolbox
|
(mf/defc layers-toolbox
|
||||||
{:wrap [mf/memo]}
|
{:wrap [mf/memo]}
|
||||||
|
@ -338,10 +455,30 @@
|
||||||
focus (mf/deref refs/workspace-focus-selected)
|
focus (mf/deref refs/workspace-focus-selected)
|
||||||
objects (hooks/with-focus-objects (:objects page) focus)
|
objects (hooks/with-focus-objects (:objects page) focus)
|
||||||
title (when (= 1 (count focus)) (get-in objects [(first focus) :name]))
|
title (when (= 1 (count focus)) (get-in objects [(first focus) :name]))
|
||||||
filter-state (mf/use-state {:show-search-box false
|
|
||||||
:show-filters-menu false
|
observer-var (mf/use-var nil)
|
||||||
:search-text ""
|
lazy-load-ref (mf/use-ref nil)
|
||||||
:active-filters {}})
|
|
||||||
|
[filtered-objects show-more filter-component] (use-search page objects)
|
||||||
|
|
||||||
|
intersection-callback
|
||||||
|
(fn [entries]
|
||||||
|
(when (and (.-isIntersecting (first entries)) (some? show-more))
|
||||||
|
(show-more)))
|
||||||
|
|
||||||
|
on-render-container
|
||||||
|
(fn [element]
|
||||||
|
(let [options #js {:root element}
|
||||||
|
lazy-el (mf/ref-val lazy-load-ref)]
|
||||||
|
(cond
|
||||||
|
(and (some? element) (not (some? @observer-var)))
|
||||||
|
(let [observer (js/IntersectionObserver. intersection-callback options)]
|
||||||
|
(.observe observer lazy-el)
|
||||||
|
(reset! observer-var observer))
|
||||||
|
|
||||||
|
(and (nil? element) (some? @observer-var))
|
||||||
|
(do (.disconnect @observer-var)
|
||||||
|
(reset! observer-var nil)))))
|
||||||
|
|
||||||
on-scroll
|
on-scroll
|
||||||
(fn [event]
|
(fn [event]
|
||||||
|
@ -355,33 +492,7 @@
|
||||||
(dom/remove-class! frame "sticky"))
|
(dom/remove-class! frame "sticky"))
|
||||||
|
|
||||||
(when last-hidden-frame
|
(when last-hidden-frame
|
||||||
(dom/add-class! last-hidden-frame "sticky"))))
|
(dom/add-class! last-hidden-frame "sticky"))))]
|
||||||
clear-search-text #(swap! filter-state assoc :search-text "")
|
|
||||||
update-search-text (fn [event]
|
|
||||||
(let [value (-> event dom/get-target dom/get-value)]
|
|
||||||
(swap! filter-state assoc :search-text value)))
|
|
||||||
toggle-search (fn []
|
|
||||||
(swap! filter-state assoc :search-text "")
|
|
||||||
(swap! filter-state assoc :active-filters {})
|
|
||||||
(swap! filter-state assoc :show-filters-menu false)
|
|
||||||
(swap! filter-state update :show-search-box not))
|
|
||||||
toggle-filters #(swap! filter-state update :show-filters-menu not)
|
|
||||||
|
|
||||||
|
|
||||||
remove-filter
|
|
||||||
(mf/use-callback
|
|
||||||
(mf/deps @filter-state)
|
|
||||||
(fn [key]
|
|
||||||
(fn [_]
|
|
||||||
(swap! filter-state update :active-filters dissoc key))))
|
|
||||||
|
|
||||||
add-filter
|
|
||||||
(mf/use-callback
|
|
||||||
(mf/deps @filter-state (:show-filters-menu @filter-state))
|
|
||||||
(fn [key value]
|
|
||||||
(fn [_]
|
|
||||||
(swap! filter-state update :active-filters assoc key value)
|
|
||||||
(toggle-filters))))]
|
|
||||||
|
|
||||||
|
|
||||||
[:div#layers.tool-window
|
[:div#layers.tool-window
|
||||||
|
@ -394,48 +505,17 @@
|
||||||
[:span (or title (tr "workspace.focus.selection"))]
|
[:span (or title (tr "workspace.focus.selection"))]
|
||||||
[:div.focus-mode (tr "workspace.focus.focus-mode")]]]
|
[:div.focus-mode (tr "workspace.focus.focus-mode")]]]
|
||||||
|
|
||||||
|
filter-component)
|
||||||
|
|
||||||
(if (:show-search-box @filter-state)
|
(when (some? filtered-objects)
|
||||||
[:*
|
[:div.tool-window-content {:ref on-render-container :key "filters"}
|
||||||
[:div.tool-window-bar.search
|
[:& filters-tree {:objects filtered-objects
|
||||||
[:span.search-box
|
:key (dm/str (:id page))}]
|
||||||
[:span.filter {:on-click toggle-filters
|
[:div.lazy {:ref lazy-load-ref
|
||||||
:class (dom/classnames :active (or
|
:key "lazy-load"
|
||||||
(:show-filters-menu @filter-state)
|
:style {:min-height 16}}]])
|
||||||
(not-empty (:active-filters @filter-state))))}
|
|
||||||
i/icon-filter]
|
|
||||||
[:span
|
|
||||||
[:input {:on-change update-search-text
|
|
||||||
:value (:search-text @filter-state)
|
|
||||||
:auto-focus (:show-search-box @filter-state)
|
|
||||||
:placeholder (tr "workspace.sidebar.layers.search")}]]
|
|
||||||
(when (not (= "" (:search-text @filter-state)))
|
|
||||||
[:span.clear {:on-click clear-search-text} i/exclude])]
|
|
||||||
[:span {:on-click toggle-search} i/cross]
|
|
||||||
]
|
|
||||||
[:div.active-filters
|
|
||||||
(for [f (:active-filters @filter-state)]
|
|
||||||
[:span {:on-click (remove-filter (key f))}
|
|
||||||
(tr (val f)) i/cross])
|
|
||||||
]
|
|
||||||
|
|
||||||
|
[:div.tool-window-content {:on-scroll on-scroll
|
||||||
(when (:show-filters-menu @filter-state)
|
:style {:display (when (some? filtered-objects) "none")}}
|
||||||
[:div.filters-container
|
[:& layers-tree {:objects objects
|
||||||
[:span{:on-click (add-filter :frame "workspace.sidebar.layers.frames")} i/artboard (tr "workspace.sidebar.layers.frames")]
|
:key (dm/str (:id page))}]]]))
|
||||||
[:span{:on-click (add-filter :group "workspace.sidebar.layers.groups")} i/folder (tr "workspace.sidebar.layers.groups")]
|
|
||||||
[:span{:on-click (add-filter :mask "workspace.sidebar.layers.masks")} i/mask (tr "workspace.sidebar.layers.masks")]
|
|
||||||
[:span{:on-click (add-filter :component "workspace.sidebar.layers.components")} i/component (tr "workspace.sidebar.layers.components")]
|
|
||||||
[:span{:on-click (add-filter :text "workspace.sidebar.layers.texts")} i/text (tr "workspace.sidebar.layers.texts")]
|
|
||||||
[:span{:on-click (add-filter :image "workspace.sidebar.layers.images")} i/image (tr "workspace.sidebar.layers.images")]
|
|
||||||
[:span{:on-click (add-filter :shape "workspace.sidebar.layers.shapes")} i/curve (tr "workspace.sidebar.layers.shapes")]])]
|
|
||||||
|
|
||||||
[:div.tool-window-bar
|
|
||||||
[:span (:name page)]
|
|
||||||
[:span {:on-click toggle-search} i/search]]))
|
|
||||||
|
|
||||||
[:div.tool-window-content {:on-scroll on-scroll}
|
|
||||||
[:& layers-tree-wrapper {:objects objects
|
|
||||||
:key (dm/str (:id page))
|
|
||||||
:search (:search-text @filter-state)
|
|
||||||
:filters (keys (:active-filters @filter-state))}]]]))
|
|
||||||
|
|
39
manage.sh
39
manage.sh
|
@ -45,31 +45,15 @@ function pull-devenv-if-not-exists {
|
||||||
function start-devenv {
|
function start-devenv {
|
||||||
pull-devenv-if-not-exists $@;
|
pull-devenv-if-not-exists $@;
|
||||||
|
|
||||||
# Check if the "backend-only" container is running. If it is, we need tot stop it first
|
docker compose -p $DEVENV_PNAME -f docker/devenv/docker-compose.yaml up -d;
|
||||||
if [[ ! $(docker ps -f "name=penpot-backend" -q) ]]; then
|
|
||||||
docker compose -p $DEVENV_PNAME --profile backend -f docker/devenv/docker-compose.yaml stop -t 2 backend;
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker compose -p $DEVENV_PNAME --profile full -f docker/devenv/docker-compose.yaml up -d;
|
|
||||||
}
|
|
||||||
|
|
||||||
function start-backend {
|
|
||||||
pull-devenv-if-not-exists $@;
|
|
||||||
|
|
||||||
# Check if the "devenv" container is running. If it is, we need tot stop it first because conflicts with the backend
|
|
||||||
if [[ ! $(docker ps -f "name=penpot-devenv-main" -q) ]]; then
|
|
||||||
docker compose -p $DEVENV_PNAME --profile full -f docker/devenv/docker-compose.yaml stop -t 2 main;
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker compose -p $DEVENV_PNAME --profile backend -f docker/devenv/docker-compose.yaml up -d;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function stop-devenv {
|
function stop-devenv {
|
||||||
docker compose -p $DEVENV_PNAME --profile full --profile backend -f docker/devenv/docker-compose.yaml stop -t 2;
|
docker compose -p $DEVENV_PNAME -f docker/devenv/docker-compose.yaml stop -t 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
function drop-devenv {
|
function drop-devenv {
|
||||||
docker compose -p $DEVENV_PNAME --profile full --profile backend -f docker/devenv/docker-compose.yaml down -t 2 -v;
|
docker compose -p $DEVENV_PNAME -f docker/devenv/docker-compose.yaml down -t 2 -v;
|
||||||
|
|
||||||
echo "Clean old development image $DEVENV_IMGNAME..."
|
echo "Clean old development image $DEVENV_IMGNAME..."
|
||||||
docker images $DEVENV_IMGNAME -q | awk '{print $3}' | xargs --no-run-if-empty docker rmi
|
docker images $DEVENV_IMGNAME -q | awk '{print $3}' | xargs --no-run-if-empty docker rmi
|
||||||
|
@ -87,14 +71,6 @@ function run-devenv {
|
||||||
docker exec -ti penpot-devenv-main sudo -EH -u penpot /home/start-tmux.sh
|
docker exec -ti penpot-devenv-main sudo -EH -u penpot /home/start-tmux.sh
|
||||||
}
|
}
|
||||||
|
|
||||||
function run-backend {
|
|
||||||
if [[ ! $(docker ps -f "name=penpot-backend" -q) ]]; then
|
|
||||||
start-backend
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker exec -ti penpot-backend sudo -EH -u penpot /home/start-tmux-back.sh
|
|
||||||
}
|
|
||||||
|
|
||||||
function build {
|
function build {
|
||||||
echo ">> build start: $1"
|
echo ">> build start: $1"
|
||||||
local version=$(print-current-version);
|
local version=$(print-current-version);
|
||||||
|
@ -200,9 +176,6 @@ function usage {
|
||||||
echo "- stop-devenv Stops the development oriented docker compose service."
|
echo "- stop-devenv Stops the development oriented docker compose service."
|
||||||
echo "- drop-devenv Remove the development oriented docker compose containers, volumes and clean images."
|
echo "- drop-devenv Remove the development oriented docker compose containers, volumes and clean images."
|
||||||
echo "- run-devenv Attaches to the running devenv container and starts development environment"
|
echo "- run-devenv Attaches to the running devenv container and starts development environment"
|
||||||
echo "- start-backend Start the backend only service."
|
|
||||||
echo "- run-backend Starts a backend-only instance and attach tmux to it"
|
|
||||||
echo " based on tmux (frontend at localhost:3449, backend at localhost:6060)."
|
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,15 +200,9 @@ case $1 in
|
||||||
start-devenv)
|
start-devenv)
|
||||||
start-devenv ${@:2}
|
start-devenv ${@:2}
|
||||||
;;
|
;;
|
||||||
start-backend)
|
|
||||||
start-backend ${@:2}
|
|
||||||
;;
|
|
||||||
run-devenv)
|
run-devenv)
|
||||||
run-devenv ${@:2}
|
run-devenv ${@:2}
|
||||||
;;
|
;;
|
||||||
run-backend)
|
|
||||||
run-backend ${@:2}
|
|
||||||
;;
|
|
||||||
stop-devenv)
|
stop-devenv)
|
||||||
stop-devenv ${@:2}
|
stop-devenv ${@:2}
|
||||||
;;
|
;;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue