0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-19 11:11:21 -05:00

Handsoff mode basic structure.

This commit is contained in:
alonso.torres 2020-10-21 09:31:48 +02:00 committed by Hirunatan
parent aaaf099a3f
commit 04f620ec00
24 changed files with 720 additions and 25 deletions

View file

@ -0,0 +1,3 @@
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.91 21.82c.95-1 .95-2.63 0-3.64l-6.35-6.73a2.11 2.11 0 010-2.86 1.82 1.82 0 012.68 0l9.05 9.6c.95 1 .95 2.62 0 3.63l-9.05 9.6c-.74.78-1.94.78-2.68 0a2.11 2.11 0 010-2.87l6.35-6.73zM6.09 21.82c-.95-1-.95-2.63 0-3.64l6.35-6.73c.75-.79.75-2.07 0-2.86a1.82 1.82 0 00-2.68 0L.7 18.19c-.95 1-.95 2.62 0 3.63l9.05 9.6c.74.78 1.94.78 2.68 0 .75-.8.75-2.08 0-2.87L6.1 21.82zM23.37 4.06c1.05.28 1.63 1.52 1.3 2.77l-5.26 27.6c-.33 1.25-1.45 2.03-2.5 1.75-1.06-.28-1.64-1.52-1.3-2.77l5.25-27.6c.33-1.25 1.46-2.03 2.5-1.75z"/>
</svg>

After

(image error) Size: 639 B

View file

@ -1 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 500 500.00001"><path d="M358.943 28.602c-.308 0-.604.037-.89.09-49.585.04-99.173.093-148.752-.014-27.727-.028-55.458.074-83.177.85-4.752-1.018-10.443 1.406-11.455 6.533-.466 103.07-.64 206.2.08 309.292-1.238 5.19 3.24 9.595 8.193 9.9 125.59 1.67 251.218.43 376.822.83-.053-62.194.004-124.387.004-186.58 0-.025.006-.048.006-.072v-33.244c.01-1.015-.075-1.874-.633-2.63 0 0-100.553-100.47-103.737-103.525-1.173-1.204-1.968-1.41-3.215-1.428zm-3.77 41.04V170.24c0 .766.378 1.454.98 1.98.526.603 1.214.98 1.98.98H459.01c.024 47.2-.018 94.397-.512 141.595-100.834.884-201.672.388-302.508.517l.46-245.412c66.24-.125 132.482-.207 198.724-.257zm-311.92 74.215c-14.417.007-28.835.03-43.253.063v327.385c122.697-.3 245.398.592 368.092-.62 5.462-.334 13.184 1.326 16.04-4.894 2.218-19.765 1.22-39.8 1.23-59.675-.866-8.903 1.344-18.108-1.346-26.87-2.656-6.83-11.41-6.93-17.608-6.776-6.942.5-16.216-1.82-20.775 5.065-3.71 17.107-1.46 34.96-2.182 52.37-9.804.426-20.454.09-30.583.226-90.7-.124-181.406.822-272.103-.165l.46-244.926 56.083-.954.754-40.133c-3.85-.03-7.7-.064-11.55-.074-14.418-.022-28.838-.03-43.256-.023z"/></svg>
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 39.46l1.5-2.6c.15.1.31.14.5.14h2v3h-2c-.73 0-1.41-.2-2-.54zM34 40v-3h2c.19 0 .35-.05.5-.13l1.5 2.6c-.59.34-1.27.53-2 .53h-2zm6-26h-3v-2a.98.98 0 00-.13-.5l2.6-1.5c.34.59.53 1.27.53 2v2zM8.54 38l2.6-1.5A.98.98 0 0111 36v-2H8v2c0 .73.2 1.41.54 2zM8 32h3v-2H8v2zM30 8v3h2V8h-2zm4 0v3h2c.19 0 .35.05.5.13l1.5-2.6A3.98 3.98 0 0036 8h-2zm6 8h-3v4h3v-4zm0 6h-3v4h3v-4zm0 6h-3v4h3v-4zm0 6h-3v2c0 .19-.05.35-.13.5l2.6 1.5c.34-.59.53-1.27.53-2v-2zm-8 6v-3h-4v3h4zm-6 0v-3h-4v3h4zm-6 0v-3h-4v3h4z"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M28 3H4a1 1 0 00-1 1v24a1 1 0 001 1h24a1 1 0 001-1V4a1 1 0 00-1-1zM4 0a4 4 0 00-4 4v24a4 4 0 004 4h24a4 4 0 004-4V4a4 4 0 00-4-4H4z"/>
</svg>

Before

(image error) Size: 1.2 KiB

After

(image error) Size: 801 B

View file

@ -1 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 500 500"><defs/><rect width="484.4" height="484.4" x="-492" y="-492" stroke="#64666a" stroke-width="15.6" rx="242.2" transform="scale(-1)"/><path fill="#64666a" fill-rule="evenodd" d="M343 250c0 12-9 21-20 21H145c-11 0-20-9-20-21s9-21 20-21h178c11 0 20 9 20 21z" clip-rule="evenodd"/><path fill="#64666a" fill-rule="evenodd" d="M309 269c10-11 10-27 0-38l-68-70c-8-8-8-22 0-30s21-8 29 0l97 100c11 11 11 27 0 38l-97 100c-8 8-21 8-29 0s-8-22 0-30z" clip-rule="evenodd"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" fill="none">
<rect width="484.4" height="484.4" x="-492" y="-492" stroke="#64666a" stroke-width="15.6" rx="242.2" transform="scale(-1)"/>
<path fill="#64666a" fill-rule="evenodd" d="M343 250c0 12-9 21-20 21H145c-11 0-20-9-20-21s9-21 20-21h178c11 0 20 9 20 21z" clip-rule="evenodd"/>
<path fill="#64666a" fill-rule="evenodd" d="M309 269c10-11 10-27 0-38l-68-70c-8-8-8-22 0-30s21-8 29 0l97 100c11 11 11 27 0 38l-97 100c-8 8-21 8-29 0s-8-22 0-30z" clip-rule="evenodd"/>
</svg>

Before

(image error) Size: 539 B

After

(image error) Size: 542 B

View file

@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16">
<path fill="#000" d="M.343 15.974a.514.514 0 01-.317-.321c-.023-.07-.026-.23-.026-1.43 0-1.468-.001-1.445.09-1.586.02-.032 1.703-1.724 3.74-3.759a596.805 596.805 0 003.7-3.716c0-.009-.367-.384-.816-.833a29.9 29.9 0 01-.817-.833c0-.01.474-.49 1.054-1.07l1.053-1.053.948.946.947.947 1.417-1.413C12.366.806 12.765.418 12.856.357c.238-.161.52-.28.792-.334.17-.034.586-.03.76.008.801.173 1.41.794 1.57 1.603.03.15.03.569 0 .718a2.227 2.227 0 01-.334.793c-.061.09-.45.49-1.496 1.54L12.734 6.1l.947.948.947.947-1.053 1.054c-.58.58-1.061 1.054-1.07 1.054-.01 0-.384-.368-.833-.817-.45-.45-.824-.817-.834-.817-.009 0-1.68 1.666-3.716 3.701a493.093 493.093 0 01-3.759 3.74c-.14.091-.117.09-1.59.089-1.187 0-1.366-.004-1.43-.027zm6.024-4.633a592.723 592.723 0 003.663-3.68c0-.02-1.67-1.69-1.69-1.69-.01 0-1.666 1.648-3.68 3.663L.996 13.297v.834c0 .627.005.839.02.854.015.014.227.02.854.02h.833l3.664-3.664z"/>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<path d="M.343 15.974a.514.514 0 01-.317-.321c-.023-.07-.026-.23-.026-1.43 0-1.468-.001-1.445.09-1.586.02-.032 1.703-1.724 3.74-3.759a596.805 596.805 0 003.7-3.716c0-.009-.367-.384-.816-.833a29.9 29.9 0 01-.817-.833c0-.01.474-.49 1.054-1.07l1.053-1.053.948.946.947.947 1.417-1.413C12.366.806 12.765.418 12.856.357c.238-.161.52-.28.792-.334.17-.034.586-.03.76.008.801.173 1.41.794 1.57 1.603.03.15.03.569 0 .718a2.227 2.227 0 01-.334.793c-.061.09-.45.49-1.496 1.54L12.734 6.1l.947.948.947.947-1.053 1.054c-.58.58-1.061 1.054-1.07 1.054-.01 0-.384-.368-.833-.817-.45-.45-.824-.817-.834-.817-.009 0-1.68 1.666-3.716 3.701a493.093 493.093 0 01-3.759 3.74c-.14.091-.117.09-1.59.089-1.187 0-1.366-.004-1.43-.027zm6.024-4.633a592.723 592.723 0 003.663-3.68c0-.02-1.67-1.69-1.69-1.69-.01 0-1.666 1.648-3.68 3.663L.996 13.297v.834c0 .627.005.839.02.854.015.014.227.02.854.02h.833l3.664-3.664z"/>
</svg>

Before

(image error) Size: 981 B

After

(image error) Size: 957 B

View file

@ -1,3 +1,3 @@
<svg width="500" height="500" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.5566 0.802143C5.73484 0.832201 3.03915 2.69963 2.02385 5.31247C1.85137 5.73396 1.71601 6.17029 1.61706 6.61447C1.14241 6.64869 0.619077 6.53608 0.164899 6.73219C-0.0777895 6.90104 -0.0358054 7.27633 0.181443 7.44833C0.780549 8.19904 1.36737 8.96764 2.04494 9.64955C2.36308 9.83712 2.77377 9.62369 2.92385 9.31767C3.48353 8.60401 4.10411 7.92266 4.56258 7.14015C4.6219 6.81493 4.25454 6.5743 3.95701 6.62813C3.72094 6.61113 3.48372 6.64427 3.24811 6.6178C3.79083 4.35481 5.91004 2.56008 8.25024 2.3836C10.2173 2.20058 12.271 3.13167 13.395 4.7633C14.5033 6.33202 14.7495 8.4588 13.9626 10.2177C13.2423 11.9016 11.6562 13.1891 9.84502 13.5267C8.12195 13.8686 6.25055 13.3618 4.94453 12.1845C4.72991 11.996 4.51061 11.7519 4.19717 11.7614C3.74737 11.7292 3.35166 12.1464 3.38782 12.5871C3.38929 12.8543 3.57566 13.0629 3.77045 13.2236C5.77493 15.1876 9.00192 15.7487 11.5753 14.6313C13.6464 13.7664 15.277 11.9044 15.7901 9.72495C16.3003 7.68095 15.8731 5.41472 14.5896 3.72888C13.2349 1.90511 10.9825 0.742088 8.68867 0.802015C8.6446 0.801698 8.60061 0.801698 8.5566 0.802111V0.802143ZM8.71661 3.18016C8.24963 3.18124 7.887 3.67092 7.98284 4.11561C7.9743 5.49196 7.92351 6.68183 8.00108 8.05586C8.00108 8.56496 8.40611 8.76793 9.00003 8.76793C10.1984 8.7713 11.4021 8.82853 12.5972 8.72777C13.1187 8.5769 13.2746 7.83426 12.8979 7.46167C12.6991 7.22791 12.3702 7.18867 12.0829 7.22552L9.54781 7.20217C9.52157 6.02587 9.5815 4.84522 9.47376 3.67232C9.36598 3.36623 9.03677 3.17047 8.71657 3.18013L8.71661 3.18016Z" fill="#1F1F1F"/>
<svg width="500" height="500" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="M8.5566 0.802143C5.73484 0.832201 3.03915 2.69963 2.02385 5.31247C1.85137 5.73396 1.71601 6.17029 1.61706 6.61447C1.14241 6.64869 0.619077 6.53608 0.164899 6.73219C-0.0777895 6.90104 -0.0358054 7.27633 0.181443 7.44833C0.780549 8.19904 1.36737 8.96764 2.04494 9.64955C2.36308 9.83712 2.77377 9.62369 2.92385 9.31767C3.48353 8.60401 4.10411 7.92266 4.56258 7.14015C4.6219 6.81493 4.25454 6.5743 3.95701 6.62813C3.72094 6.61113 3.48372 6.64427 3.24811 6.6178C3.79083 4.35481 5.91004 2.56008 8.25024 2.3836C10.2173 2.20058 12.271 3.13167 13.395 4.7633C14.5033 6.33202 14.7495 8.4588 13.9626 10.2177C13.2423 11.9016 11.6562 13.1891 9.84502 13.5267C8.12195 13.8686 6.25055 13.3618 4.94453 12.1845C4.72991 11.996 4.51061 11.7519 4.19717 11.7614C3.74737 11.7292 3.35166 12.1464 3.38782 12.5871C3.38929 12.8543 3.57566 13.0629 3.77045 13.2236C5.77493 15.1876 9.00192 15.7487 11.5753 14.6313C13.6464 13.7664 15.277 11.9044 15.7901 9.72495C16.3003 7.68095 15.8731 5.41472 14.5896 3.72888C13.2349 1.90511 10.9825 0.742088 8.68867 0.802015C8.6446 0.801698 8.60061 0.801698 8.5566 0.802111V0.802143ZM8.71661 3.18016C8.24963 3.18124 7.887 3.67092 7.98284 4.11561C7.9743 5.49196 7.92351 6.68183 8.00108 8.05586C8.00108 8.56496 8.40611 8.76793 9.00003 8.76793C10.1984 8.7713 11.4021 8.82853 12.5972 8.72777C13.1187 8.5769 13.2746 7.83426 12.8979 7.46167C12.6991 7.22791 12.3702 7.18867 12.0829 7.22552L9.54781 7.20217C9.52157 6.02587 9.5815 4.84522 9.47376 3.67232C9.36598 3.36623 9.03677 3.17047 8.71657 3.18013L8.71661 3.18016Z"/>
</svg>

Before

(image error) Size: 1.6 KiB

After

(image error) Size: 1.6 KiB

View file

@ -1 +1,3 @@
<svg height="500" viewBox="0 0 500 500.00001" width="500" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="15.5" y="78" width="469" height="344" rx="50"/></svg>
<svg height="500" viewBox="0 0 500 500.00001" width="500" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="15.5" y="78" width="469" height="344" rx="50"/>
</svg>

Before

(image error) Size: 168 B

After

(image error) Size: 172 B

View file

@ -1 +1,3 @@
<svg height="500" viewBox="0 0 500 500.00001" width="500" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="78" y="15.5" width="344" height="469" rx="50"/></svg>
<svg height="500" viewBox="0 0 500 500.00001" width="500" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="78" y="15.5" width="344" height="469" rx="50"/>
</svg>

Before

(image error) Size: 168 B

After

(image error) Size: 172 B

View file

@ -29,6 +29,7 @@
@import 'main/layouts/main-layout';
@import "main/layouts/not-found";
@import "main/layouts/viewer";
@import "main/layouts/handoff";
//#################################################
// Commons
@ -80,3 +81,4 @@
@import 'main/partials/workspace-header';
@import 'main/partials/workspace-comments';
@import 'main/partials/color-bullet';
@import "main/partials/handoff";

View file

@ -0,0 +1,33 @@
.handoff-layout {
display: grid;
grid-template-rows: 40px auto;
grid-template-columns: 1fr;
user-select: none;
&.fullscreen {
.viewer-header {
opacity: 0;
&:hover {
opacity: 1;
}
}
.viewer-content {
grid-row: 1 / span 2;
}
}
.viewer-header {
grid-column: 1 / span 1;
grid-row: 1 / span 1;
}
.viewer-content {
grid-column: 1 / span 1;
grid-row: 2 / span 1;
}
}
.handoff-layout .settings-bar.settings-bar-left {
left: 0;
}

View file

@ -0,0 +1,16 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible With Secondary Licenses", as
// defined by the Mozilla Public License, v. 2.0.
//
// Copyright (c) 2020 UXBOX Labs SL
.handoff-svg-wrapper {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}

View file

@ -115,6 +115,25 @@ $width-settings-bar: 16rem;
}
}
.tool-window-bar-icon {
height: 15px;
svg {
width: 15px;
height: 15px;
}
}
&.big {
height: 3rem;
padding-bottom: 1rem;
}
.tool-window-bar-title {
font-size: $fs14;
margin-left: 0.5rem;
}
.tool-window-icon {
margin-right: $small;
display: none;

View file

@ -4,7 +4,7 @@
border-bottom: 1px solid $color-gray-60;
display: flex;
height: 40px;
padding: $x-small $medium $x-small 55px;
padding: 0 $medium 0 55px;
position: relative;
z-index: 12;
justify-content: space-between;
@ -109,6 +109,36 @@
}
}
.mode-zone {
display: flex;
height: 100%;
.mode-zone-button {
background: inherit;
border: none;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 40px;
svg {
fill: $color-gray-20;
width: 24px;
height: 24px;
}
&.active {
background: $color-gray-60;
svg {
fill: $color-primary;
}
}
}
}
.options-zone {
align-items: center;
display: flex;

View file

@ -51,7 +51,11 @@
:page-id page-id
:file-id file-id
:interactions-mode :hide
:show-interactions? false}))
:show-interactions? false
:selected #{}
:collapsed #{}
:hover #{}}))
ptk/WatchEvent
(watch [_ state stream]
@ -170,24 +174,25 @@
ptk/WatchEvent
(watch [_ state stream]
(let [route (:route state)
screen (-> route :data :name keyword)
qparams (get-in route [:params :query])
pparams (get-in route [:params :path])
index (d/parse-integer (:index qparams))]
(when (pos? index)
(rx/of (rt/nav :viewer pparams (assoc qparams :index (dec index)))))))))
(rx/of (rt/nav screen pparams (assoc qparams :index (dec index)))))))))
(def select-next-frame
(ptk/reify ::select-prev-frame
ptk/WatchEvent
(watch [_ state stream]
(let [route (:route state)
screen (-> route :data :name keyword)
qparams (get-in route [:params :query])
pparams (get-in route [:params :path])
index (d/parse-integer (:index qparams))
total (count (get-in state [:viewer-data :frames]))]
(when (< index (dec total))
(rx/of (rt/nav :viewer pparams (assoc qparams :index (inc index)))))))))
(rx/of (rt/nav screen pparams (assoc qparams :index (inc index)))))))))
(defn set-interactions-mode
[mode]
@ -249,3 +254,36 @@
"shift+2" #(st/emit! zoom-to-200)
"left" #(st/emit! select-prev-frame)
"right" #(st/emit! select-next-frame)})
(defn deselect-all []
(ptk/reify ::deselect-all
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:viewer-local :selected] #{}))))
(defn select-shape
([id] (select-shape id false))
([id toggle?]
(ptk/reify ::select-shape
ptk/UpdateEvent
(update [_ state]
(-> state
(assoc-in [:viewer-local :selected] #{id}))))))
;; TODO
(defn collapse-all []
(ptk/reify ::collapse-all))
(defn toggle-collapse [id]
(ptk/reify ::toggle-collapse
ptk/UpdateEvent
(update [_ state]
(let [toggled? (contains? (get-in state [:viewer-local :collapsed]) id)]
(update-in state [:viewer-local :collapsed] (if toggled? disj conj) id)))))
(defn hover-shape [id hover?]
(ptk/reify ::hover-shape
ptk/UpdateEvent
(update [_ state]
(update-in state [:viewer-local :hover] (if hover? conj disj) id))))

View file

@ -27,6 +27,7 @@
[app.main.ui.settings :as settings]
[app.main.ui.static :refer [not-found-page not-authorized-page]]
[app.main.ui.viewer :refer [viewer-page]]
[app.main.ui.viewer.handoff :refer [handoff]]
[app.main.ui.workspace :as workspace]
[app.util.i18n :as i18n :refer [tr t]]
[app.util.timers :as ts]
@ -53,6 +54,7 @@
["/options" :settings-options]]
["/view/:file-id/:page-id" :viewer]
["/handoff/:file-id/:page-id" :handoff]
["/not-found" :not-found]
["/not-authorized" :not-authorized]
@ -127,6 +129,14 @@
:index index
:token token}])
:handoff
(let [index (d/parse-integer (get-in route [:params :query :index]))
file-id (uuid (get-in route [:params :path :file-id]))
page-id (uuid (get-in route [:params :path :page-id]))]
[:& handoff {:page-id page-id
:file-id file-id
:index index}])
:render-object
(do
(let [file-id (uuid (get-in route [:params :path :file-id]))

View file

@ -17,7 +17,7 @@
(def align-middle (icon-xref :align-middle))
(def align-top (icon-xref :align-top))
(def alignment (icon-xref :alignment))
(def arrow (icon-xref :arrow))
#_(def arrow (icon-xref :arrow))
(def arrow-down (icon-xref :arrow-down))
(def arrow-end (icon-xref :arrow-end))
(def arrow-slide (icon-xref :arrow-slide))
@ -91,7 +91,7 @@
(def rotate (icon-xref :rotate))
(def ruler (icon-xref :ruler))
(def ruler-tool (icon-xref :ruler-tool))
(def save (icon-xref :save))
#_(def save (icon-xref :save))
(def search (icon-xref :search))
(def shape-halign-center (icon-xref :shape-halign-center))
(def shape-halign-left (icon-xref :shape-halign-left))
@ -128,6 +128,7 @@
(def picker-ramp (icon-xref :picker-ramp))
(def checkbox-checked (icon-xref :checkbox-checked))
(def checkbox-unchecked (icon-xref :checkbox-unchecked))
(def code (icon-xref :code))
(def loader-pencil
(mf/html
@ -149,9 +150,10 @@
(mf/defc debug-icons-preview
{::mf/wrap-props false}
[props]
[:section.debug-icons-preview
[:section.debug-icons-preview {:style {:background-color "black"}}
(for [[key val] (sort-by first (ns-publics 'app.main.ui.icons))]
(when (not= key 'debug-icons-preview)
[:div.icon-item {:key key}
[:div.icon-item {:key key
:style {:fill "white"}}
(deref val)
[:span (pr-str key)]]))])

View file

@ -92,10 +92,12 @@
:toggle-fullscreen toggle-fullscreen
:fullscreen? fullscreen?
:local local
:index index}]
:index index
:screen :viewer}]
[:div.viewer-content {:on-click on-click}
(when (:show-thumbnails local)
[:& thumbnails-panel {:index index
[:& thumbnails-panel {:screen :viewer
:index index
:data data}])
[:& main-panel {:data data
:local local

View file

@ -0,0 +1,121 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns app.main.ui.viewer.handoff
(:require
[rumext.alpha :as mf]
[beicon.core :as rx]
[goog.events :as events]
[okulary.core :as l]
[app.common.exceptions :as ex]
[app.util.data :refer [classnames]]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [t tr]]
[app.main.data.viewer :as dv]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.dropdown :refer [dropdown]]
[app.main.ui.hooks :as hooks]
[app.main.ui.icons :as i]
[app.main.ui.keyboard :as kbd]
[app.main.ui.viewer.header :refer [header]]
[app.main.ui.viewer.thumbnails :refer [thumbnails-panel]]
[app.main.ui.viewer.handoff.render :refer [render-frame-svg]]
[app.main.ui.viewer.handoff.layers-sidebar :refer [layers-sidebar]]
[app.main.ui.viewer.handoff.attributes-sidebar :refer [attributes-sidebar]])
(:import goog.events.EventType))
(defn handle-select-frame [frame]
#(do (dom/prevent-default %)
(dom/stop-propagation %)
(st/emit! (dv/select-shape (:id frame)))))
(mf/defc render-panel
[{:keys [data local index]}]
(let [locale (mf/deref i18n/locale)
frames (:frames data [])
objects (:objects data)
frame (get frames index)]
[:section.viewer-preview
(cond
(empty? frames)
[:section.empty-state
[:span (t locale "viewer.empty-state")]]
(nil? frame)
[:section.empty-state
[:span (t locale "viewer.frame-not-found")]]
:else
[:*
[:& layers-sidebar {:frame frame}]
[:div.handoff-svg-wrapper {:on-click (handle-select-frame frame)}
[:& render-frame-svg {:frame-id (:id frame)
:zoom (:zoom local)
:objects objects}]]
[:& attributes-sidebar]])]))
(mf/defc handoff-content
[{:keys [data local index] :as props}]
(let [container (mf/use-ref)
[toggle-fullscreen fullscreen?] (hooks/use-fullscreen container)
on-mouse-wheel
(fn [event]
(when (kbd/ctrl? event)
(dom/prevent-default event)
(let [event (.getBrowserEvent ^js event)]
(if (pos? (.-deltaY ^js event))
(st/emit! dv/decrease-zoom)
(st/emit! dv/increase-zoom)))))
on-mount
(fn []
;; bind with passive=false to allow the event to be cancelled
;; https://stackoverflow.com/a/57582286/3219895
(let [key1 (events/listen goog/global EventType.WHEEL
on-mouse-wheel #js {"passive" false})]
(fn []
(events/unlistenByKey key1))))]
(mf/use-effect on-mount)
(hooks/use-shortcuts dv/shortcuts)
[:div.handoff-layout {:class (classnames :fullscreen fullscreen?)
:ref container}
[:& header {:data data
:toggle-fullscreen toggle-fullscreen
:fullscreen? fullscreen?
:local local
:index index
:screen :handoff}]
[:div.viewer-content
(when (:show-thumbnails local)
[:& thumbnails-panel {:index index
:data data
:screen :handoff}])
[:& render-panel {:data data
:local local
:index index}]]]))
(mf/defc handoff
[{:keys [file-id page-id index] :as props}]
(mf/use-effect
(mf/deps file-id page-id)
(fn []
(st/emit! (dv/initialize props))))
(let [data (mf/deref refs/viewer-data)
local (mf/deref refs/viewer-local)]
(when data
[:& handoff-content {:index index
:local local
:data data}])))

View file

@ -0,0 +1,38 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns app.main.ui.viewer.handoff.attributes-sidebar
(:require
[rumext.alpha :as mf]
[app.main.ui.icons :as i]
[app.main.ui.components.tab-container :refer [tab-container tab-element]]))
(mf/defc info-panel []
[:div.element-options])
(mf/defc code-panel []
[:div.element-options])
(mf/defc attributes-sidebar []
(let [section (mf/use-state :info #_:code)]
[:aside.settings-bar.settings-bar-right
[:div.settings-bar-inside
[:div.tool-window
[:div.tool-window-bar.big
[:span.tool-window-bar-icon i/text]
[:span.tool-window-bar-title "Text"]]
[:div.tool-window-content
[:& tab-container {:on-change-tab #(reset! section %)
:selected @section}
[:& tab-element {:id :info :title "Info"}
[:& info-panel]]
[:& tab-element {:id :code :title "Code"}
[:& code-panel]]]]]]]))

View file

@ -0,0 +1,121 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns app.main.ui.viewer.handoff.layers-sidebar
(:require
[rumext.alpha :as mf]
[okulary.core :as l]
[app.common.data :as d]
[app.common.uuid :as uuid]
[app.main.store :as st]
[app.util.dom :as dom]
[app.main.data.viewer :as dv]
[app.main.ui.icons :as i]
[app.main.ui.keyboard :as kbd]
[app.main.ui.workspace.sidebar.layers :refer [element-icon layer-name frame-wrapper]]))
(def selected-shapes
(l/derived (comp :selected :viewer-local) st/state))
(def page-ref
(l/derived (comp :page :viewer-data) st/state))
(defn- make-collapsed-iref
[id]
#(-> (l/in [:viewer-local :collapsed id])
(l/derived st/state) ))
(mf/defc layer-item
[{:keys [index item selected objects disable-collapse?] :as props}]
(let [id (:id item)
selected? (contains? selected id)
item-ref (mf/use-ref nil)
collapsed-iref (mf/use-memo
(mf/deps id)
(make-collapsed-iref id))
expanded? (not (mf/deref collapsed-iref))
toggle-collapse
(fn [event]
(dom/stop-propagation event)
(if (and expanded? (kbd/shift? event))
(st/emit! (dv/collapse-all))
(st/emit! (dv/toggle-collapse id))))
select-shape
(fn [event]
(dom/prevent-default event)
(let [id (:id item)]
(st/emit! (dv/select-shape id))
#_(cond
(or (:blocked item)
(:hidden item))
nil
(.-shiftKey event)
(st/emit! (dv/select-shape id true))
(> (count selected) 1)
(st/emit! (dv/deselect-all)
(dv/select-shape id))
:else
(st/emit! (dv/deselect-all)
(dv/select-shape id)))))
]
(mf/use-effect
(mf/deps selected)
(fn []
(when (and (= (count selected) 1) selected?)
(.scrollIntoView (mf/ref-val item-ref) false))))
[:li {:ref item-ref
:class (dom/classnames
:component (not (nil? (:component-id item)))
:masked (:masked-group? item)
:selected selected?)}
[:div.element-list-body {:class (dom/classnames :selected selected?
:icon-layer (= (:type item) :icon))
:on-click select-shape}
[:& element-icon {:shape item}]
[:& layer-name {:shape item}]
(when (and (not disable-collapse?) (:shapes item))
[:span.toggle-content
{:on-click toggle-collapse
:class (when expanded? "inverse")}
i/arrow-slide])]
(when (and (:shapes item) expanded?)
[:ul.element-children
(for [[index id] (reverse (d/enumerate (:shapes item)))]
(when-let [item (get objects id)]
[:& layer-item
{:item item
:selected selected
:index index
:objects objects
:key (:id item)}]))])]))
(mf/defc layers-sidebar [{:keys [frame]}]
(let [page (mf/deref page-ref)
selected (mf/deref selected-shapes)
objects (:objects page)]
[:aside.settings-bar.settings-bar-left
[:div.settings-bar-inside
[:ul.element-list
[:& layer-item
{:item frame
:selected selected
:index 0
:objects objects
:disable-collapse? true}]]]]))

View file

@ -0,0 +1,170 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns app.main.ui.viewer.handoff.render
"The main container for a frame in handoff mode"
(:require
[rumext.alpha :as mf]
[app.util.object :as obj]
[app.util.dom :as dom]
[app.common.data :as d]
[app.common.pages :as cp]
[app.common.pages-helpers :as cph]
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as geom]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.data.viewer :as dv]
[app.main.ui.shapes.filters :as filters]
[app.main.ui.shapes.circle :as circle]
[app.main.ui.shapes.frame :as frame]
[app.main.ui.shapes.group :as group]
[app.main.ui.shapes.icon :as icon]
[app.main.ui.shapes.image :as image]
[app.main.ui.shapes.path :as path]
[app.main.ui.shapes.rect :as rect]
[app.main.ui.shapes.text :as text]
[app.main.ui.viewer.handoff.selection-feedback :refer [selection-feedback]]
[app.main.ui.shapes.shape :refer [shape-container]]))
(declare shape-container-factory)
(defn handle-hover-shape [{:keys [type id]} hover?]
#(when-not (#{:group :frame} type)
(do
(dom/prevent-default %)
(dom/stop-propagation %)
(st/emit! (dv/hover-shape id hover?)))))
(defn select-shape [{:keys [type id]}]
#(when-not (#{:group :frame} type)
(dom/prevent-default %)
(dom/stop-propagation %)
(st/emit! (dv/select-shape id))))
(defn shape-wrapper-factory
[component]
(mf/fnc shape-wrapper
{::mf/wrap-props false}
[props]
(let [shape (unchecked-get props "shape")
childs (unchecked-get props "childs")
frame (unchecked-get props "frame")]
[:> shape-container {:shape shape
:on-mouse-enter (handle-hover-shape shape true)
:on-mouse-leave (handle-hover-shape shape false)
:on-click (select-shape shape)}
[:& component {:shape shape
:frame frame
:childs childs
:is-child-selected? true}]])))
(defn frame-container-factory
[objects]
(let [shape-container (shape-container-factory objects)
frame-shape (frame/frame-shape shape-container)
frame-wrapper (shape-wrapper-factory frame-shape)]
(mf/fnc frame-container
{::mf/wrap-props false}
[props]
(let [shape (unchecked-get props "shape")
childs (mapv #(get objects %) (:shapes shape))
shape (geom/transform-shape shape)
props (-> (obj/new)
(obj/merge! props)
(obj/merge! #js {:shape shape
:childs childs}))]
[:> frame-wrapper props]))))
(defn group-container-factory
[objects]
(let [shape-container (shape-container-factory objects)
group-shape (group/group-shape shape-container)
group-wrapper (shape-wrapper-factory group-shape)]
(mf/fnc group-container
{::mf/wrap-props false}
[props]
(let [shape (unchecked-get props "shape")
childs (mapv #(get objects %) (:shapes shape))
props (-> (obj/new)
(obj/merge! props)
(obj/merge! #js {:childs childs}))]
[:> group-wrapper props]))))
(defn shape-container-factory
[objects show-interactions?]
(let [path-wrapper (shape-wrapper-factory path/path-shape)
text-wrapper (shape-wrapper-factory text/text-shape)
icon-wrapper (shape-wrapper-factory icon/icon-shape)
rect-wrapper (shape-wrapper-factory rect/rect-shape)
image-wrapper (shape-wrapper-factory image/image-shape)
circle-wrapper (shape-wrapper-factory circle/circle-shape)]
(mf/fnc shape-container
{::mf/wrap-props false}
[props]
(let [shape (unchecked-get props "shape")
frame (unchecked-get props "frame")
group-container (mf/use-memo
(mf/deps objects)
#(group-container-factory objects))]
(when (and shape (not (:hidden shape)))
(let [shape (geom/transform-shape frame shape)
opts #js {:shape shape
:frame frame}]
(case (:type shape)
:curve [:> path-wrapper opts]
:text [:> text-wrapper opts]
:icon [:> icon-wrapper opts]
:rect [:> rect-wrapper opts]
:path [:> path-wrapper opts]
:image [:> image-wrapper opts]
:circle [:> circle-wrapper opts]
:group [:> group-container opts])))))))
(defn adjust-frame-position [frame-id objects]
(let [frame (get objects frame-id)
modifier (-> (gpt/point (:x frame) (:y frame))
(gpt/negate)
(gmt/translate-matrix))
update-fn #(assoc-in %1 [%2 :modifiers :displacement] modifier)
modifier-ids (d/concat [frame-id] (cph/get-children frame-id objects))]
(reduce update-fn objects modifier-ids)))
(defn make-vbox [frame]
(str "0 0 " (:width frame 0) " " (:height frame 0)))
(mf/defc render-frame-svg
{::mf/wrap [mf/memo]}
[{:keys [objects frame-id zoom] :or {zoom 1} :as props}]
(let [objects (adjust-frame-position frame-id objects)
frame (get objects frame-id)
width (* (:width frame) zoom)
height (* (:height frame) zoom)
vbox (make-vbox frame)
render-frame (mf/use-memo
(mf/deps objects)
#(frame-container-factory objects))]
[:svg {:view-box vbox
:width width
:height height
:version "1.1"
:xmlnsXlink "http://www.w3.org/1999/xlink"
:xmlns "http://www.w3.org/2000/svg"}
[:& render-frame {:shape frame
:view-box vbox}]
[:& selection-feedback {:frame frame}]]))

View file

@ -0,0 +1,65 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns app.main.ui.viewer.handoff.selection-feedback
(:require
[rumext.alpha :as mf]
[okulary.core :as l]
#_[app.util.object :as obj]
#_[app.common.data :as d]
#_[app.common.pages :as cp]
#_[app.common.pages-helpers :as cph]
#_[app.common.geom.matrix :as gmt]
#_[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
#_[app.main.refs :as refs]
[app.main.store :as st]
#_[app.main.data.viewer :as dv]
#_[app.main.ui.shapes.filters :as filters]
#_[app.main.ui.shapes.circle :as circle]
#_[app.main.ui.shapes.frame :as frame]
#_[app.main.ui.shapes.group :as group]
#_[app.main.ui.shapes.icon :as icon]
#_[app.main.ui.shapes.image :as image]
#_[app.main.ui.shapes.path :as path]
#_[app.main.ui.shapes.rect :as rect]
#_[app.main.ui.shapes.text :as text]
#_[app.main.ui.shapes.shape :refer [shape-container]]))
(def selection-rect-color-normal "#1FDEA7")
(def selection-rect-color-component "#00E0FF")
(def selection-rect-width 1)
#_(def hover-ref
(l/derived (l/in [:viewer-local :hover]) st/state))
(defn make-hover-shapes-iref
[]
(let [hover->shapes
(fn [state]
(let [hover (get-in state [:viewer-local :hover])
objects (get-in state [:viewer-data :page :objects])
resolve-shape #(get objects %)]
(map resolve-shape hover)))]
#(l/derived hover->shapes st/state)))
(mf/defc selection-feedback [{:keys [frame]}]
(let [hover-shapes-ref (mf/use-memo (make-hover-shapes-iref))
hover-shapes (->> (mf/deref hover-shapes-ref)
(map #(gsh/translate-to-frame % frame)))]
(for [shape hover-shapes]
(let [{:keys [x y width height]} (:selrect shape)]
[:rect {:x x
:y y
:width width
:height height
:fill "transparent"
:stroke selection-rect-color-normal
:stroke-width selection-rect-width
:pointer-events "none"}]))))

View file

@ -122,7 +122,7 @@
(t locale "viewer.header.share.create-link")])]]]]))
(mf/defc header
[{:keys [data index local fullscreen? toggle-fullscreen] :as props}]
[{:keys [data index local fullscreen? toggle-fullscreen screen] :as props}]
(let [{:keys [project file page frames]} data
total (count frames)
on-click #(st/emit! dv/toggle-thumbnails-panel)
@ -141,7 +141,14 @@
on-edit #(st/emit! (rt/nav :workspace
{:project-id project-id
:file-id file-id}
{:page-id page-id}))]
{:page-id page-id}))
change-screen
(fn [screen]
(st/emit!
(rt/nav screen
{:file-id file-id :page-id page-id}
{:index index})))]
[:header.viewer-header
[:div.main-icon
[:a {:on-click on-edit} i/logo-icon]]
@ -156,6 +163,14 @@
[:span.dropdown-button i/arrow-down]
[:span.counters (str (inc index) " / " total)]]
[:div.mode-zone
[:button.mode-zone-button {:on-click #(when (not= screen :viewer)
(change-screen :viewer))
:class (when (= screen :viewer) "active")} i/play]
[:button.mode-zone-button {:on-click #(when (not= screen :handoff)
(change-screen :handoff))
:class (when (= screen :handoff) "active")} i/code]]
[:div.options-zone
[:& interactions-menu {:interactions-mode interactions-mode}]

View file

@ -94,7 +94,7 @@
[:span.name (:name frame)]]])
(mf/defc thumbnails-panel
[{:keys [data index] :as props}]
[{:keys [data index screen] :as props}]
(let [expanded? (mf/use-state false)
container (mf/use-ref)
page-id (get-in data [:page :id])
@ -111,7 +111,7 @@
on-item-click
(fn [event index]
(compare-and-set! selected false true)
(st/emit! (rt/nav :viewer {:file-id file-id
(st/emit! (rt/nav screen {:file-id file-id
:page-id page-id} {:index index}))
(when @expanded?
(on-close)))]

View file

@ -331,8 +331,7 @@
(mf/defc layers-toolbox
{:wrap [mf/memo]}
[]
(let [locale (mf/deref i18n/locale)
page (mf/deref refs/workspace-page)]
(let [page (mf/deref refs/workspace-page)]
[:div#layers.tool-window
[:div.tool-window-bar
[:div.tool-window-icon i/layers]