mirror of
https://github.com/penpot/penpot.git
synced 2025-06-07 13:41:38 +02:00
commit
567e177699
60 changed files with 2310 additions and 1377 deletions
|
@ -55,6 +55,36 @@
|
||||||
(>= % min-safe-int)
|
(>= % min-safe-int)
|
||||||
(<= % max-safe-int)))
|
(<= % max-safe-int)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(s/def :internal.gradient.stop/color ::string)
|
||||||
|
(s/def :internal.gradient.stop/opacity ::safe-number)
|
||||||
|
(s/def :internal.gradient.stop/offset ::safe-number)
|
||||||
|
|
||||||
|
(s/def :internal.gradient/type #{:linear :radial})
|
||||||
|
(s/def :internal.gradient/start-x ::safe-number)
|
||||||
|
(s/def :internal.gradient/start-y ::safe-number)
|
||||||
|
(s/def :internal.gradient/end-x ::safe-number)
|
||||||
|
(s/def :internal.gradient/end-y ::safe-number)
|
||||||
|
(s/def :internal.gradient/width ::safe-number)
|
||||||
|
|
||||||
|
(s/def :internal.gradient/stop
|
||||||
|
(s/keys :req-un [:internal.gradient.stop/color
|
||||||
|
:internal.gradient.stop/opacity
|
||||||
|
:internal.gradient.stop/offset]))
|
||||||
|
|
||||||
|
(s/def :internal.gradient/stops
|
||||||
|
(s/coll-of :internal.gradient/stop :kind vector?))
|
||||||
|
|
||||||
|
(s/def ::gradient
|
||||||
|
(s/keys :req-un [:internal.gradient/type
|
||||||
|
:internal.gradient/start-x
|
||||||
|
:internal.gradient/start-y
|
||||||
|
:internal.gradient/end-x
|
||||||
|
:internal.gradient/end-y
|
||||||
|
:internal.gradient/width
|
||||||
|
:internal.gradient/stops]))
|
||||||
|
|
||||||
;; Page Options
|
;; Page Options
|
||||||
(s/def :internal.page.grid.color/value string?)
|
(s/def :internal.page.grid.color/value string?)
|
||||||
(s/def :internal.page.grid.color/opacity ::safe-number)
|
(s/def :internal.page.grid.color/opacity ::safe-number)
|
||||||
|
@ -110,10 +140,13 @@
|
||||||
(s/def :internal.shape/blocked boolean?)
|
(s/def :internal.shape/blocked boolean?)
|
||||||
(s/def :internal.shape/collapsed boolean?)
|
(s/def :internal.shape/collapsed boolean?)
|
||||||
(s/def :internal.shape/content any?)
|
(s/def :internal.shape/content any?)
|
||||||
|
|
||||||
(s/def :internal.shape/fill-color string?)
|
(s/def :internal.shape/fill-color string?)
|
||||||
|
(s/def :internal.shape/fill-opacity ::safe-number)
|
||||||
|
(s/def :internal.shape/fill-gradient (s/nilable ::gradient))
|
||||||
(s/def :internal.shape/fill-color-ref-file (s/nilable uuid?))
|
(s/def :internal.shape/fill-color-ref-file (s/nilable uuid?))
|
||||||
(s/def :internal.shape/fill-color-ref-id (s/nilable uuid?))
|
(s/def :internal.shape/fill-color-ref-id (s/nilable uuid?))
|
||||||
(s/def :internal.shape/fill-opacity ::safe-number)
|
|
||||||
(s/def :internal.shape/font-family string?)
|
(s/def :internal.shape/font-family string?)
|
||||||
(s/def :internal.shape/font-size ::safe-integer)
|
(s/def :internal.shape/font-size ::safe-integer)
|
||||||
(s/def :internal.shape/font-style string?)
|
(s/def :internal.shape/font-style string?)
|
||||||
|
@ -262,13 +295,26 @@
|
||||||
:internal.page/options
|
:internal.page/options
|
||||||
:internal.page/objects]))
|
:internal.page/objects]))
|
||||||
|
|
||||||
|
|
||||||
(s/def :internal.color/name ::string)
|
(s/def :internal.color/name ::string)
|
||||||
(s/def :internal.color/value ::string)
|
(s/def :internal.color/value (s/nilable ::string))
|
||||||
|
(s/def :internal.color/color (s/nilable ::string))
|
||||||
|
(s/def :internal.color/opacity (s/nilable ::safe-number))
|
||||||
|
(s/def :internal.color/gradient (s/nilable ::gradient))
|
||||||
|
|
||||||
(s/def ::color
|
(s/def ::color
|
||||||
(s/keys :req-un [::id
|
(s/keys :req-un [::id
|
||||||
:internal.color/name
|
:internal.color/name]
|
||||||
:internal.color/value]))
|
:opt-un [:internal.color/value
|
||||||
|
:internal.color/color
|
||||||
|
:internal.color/opacity
|
||||||
|
:internal.color/gradient]))
|
||||||
|
|
||||||
|
(s/def ::recent-color
|
||||||
|
(s/keys :opt-un [:internal.color/value
|
||||||
|
:internal.color/color
|
||||||
|
:internal.color/opacity
|
||||||
|
:internal.color/gradient]))
|
||||||
|
|
||||||
(s/def :internal.media-object/name ::string)
|
(s/def :internal.media-object/name ::string)
|
||||||
(s/def :internal.media-object/path ::string)
|
(s/def :internal.media-object/path ::string)
|
||||||
|
@ -294,7 +340,7 @@
|
||||||
(s/map-of ::uuid ::color))
|
(s/map-of ::uuid ::color))
|
||||||
|
|
||||||
(s/def :internal.file/recent-colors
|
(s/def :internal.file/recent-colors
|
||||||
(s/coll-of ::string :kind vector?))
|
(s/coll-of ::recent-color :kind vector?))
|
||||||
|
|
||||||
(s/def :internal.typography/id ::id)
|
(s/def :internal.typography/id ::id)
|
||||||
(s/def :internal.typography/name ::string)
|
(s/def :internal.typography/name ::string)
|
||||||
|
@ -408,8 +454,10 @@
|
||||||
(defmethod change-spec :del-color [_]
|
(defmethod change-spec :del-color [_]
|
||||||
(s/keys :req-un [::id]))
|
(s/keys :req-un [::id]))
|
||||||
|
|
||||||
|
(s/def :internal.changes.add-recent-color/color ::recent-color)
|
||||||
|
|
||||||
(defmethod change-spec :add-recent-color [_]
|
(defmethod change-spec :add-recent-color [_]
|
||||||
(s/keys :req-un [:recent-color/color]))
|
(s/keys :req-un [:internal.changes.add-recent-color/color]))
|
||||||
|
|
||||||
(s/def :internal.changes.media/object ::media-object)
|
(s/def :internal.changes.media/object ::media-object)
|
||||||
|
|
||||||
|
@ -821,7 +869,7 @@
|
||||||
|
|
||||||
(defmethod process-change :mod-color
|
(defmethod process-change :mod-color
|
||||||
[data {:keys [color]}]
|
[data {:keys [color]}]
|
||||||
(d/update-in-when data [:colors (:id color)] merge color))
|
(d/assoc-in-when data [:colors (:id color)] color))
|
||||||
|
|
||||||
(defmethod process-change :del-color
|
(defmethod process-change :del-color
|
||||||
[data {:keys [id]}]
|
[data {:keys [id]}]
|
||||||
|
@ -917,3 +965,4 @@
|
||||||
(ex/raise :type :not-implemented
|
(ex/raise :type :not-implemented
|
||||||
:code :operation-not-implemented
|
:code :operation-not-implemented
|
||||||
:context {:type (:type op)}))
|
:context {:type (:type op)}))
|
||||||
|
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" height="500" width="500"><path d="M24.7-.203C11.421-.203.001 11.217.001 24.494v451.012c0 13.277 11.417 24.695 24.691 24.697h450.608c13.28 0 24.697-11.42 24.697-24.697V24.494c0-13.276-11.42-24.697-24.697-24.697H24.699zm4.142 31.35h442.316v437.707H28.842V31.146z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
|
||||||
|
<path d="M24.7-.203C11.421-.203.001 11.217.001 24.494v451.012c0 13.277 11.417 24.695 24.691 24.697H475.3c13.28 0 24.697-11.42 24.697-24.697V24.494c0-13.276-11.42-24.697-24.697-24.697H24.699zm12.142 39.35h426.316v421.707H36.842V39.146z"/>
|
||||||
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 332 B After Width: | Height: | Size: 310 B |
|
@ -1,4 +1,3 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16">
|
||||||
<path fill="#fff" d="M.607 13.076v2.401l2.224.025 7.86-7.405s-.05-.885-.253-1.087c-.202-.202-2.25-1.744-2.25-1.744z"/>
|
|
||||||
<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"/>
|
<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>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 981 B |
|
@ -18,7 +18,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"auth.create-demo-profile" : {
|
"auth.create-demo-profile" : {
|
||||||
"used-in" : [ "src/app/main/ui/auth/register.cljs:133", "src/app/main/ui/auth/register.cljs:136", "src/app/main/ui/auth/login.cljs:144", "src/app/main/ui/auth/login.cljs:147" ],
|
"used-in" : [ "src/app/main/ui/auth/login.cljs:144", "src/app/main/ui/auth/login.cljs:147", "src/app/main/ui/auth/register.cljs:133", "src/app/main/ui/auth/register.cljs:136" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Just wanna try it?",
|
"en" : "Just wanna try it?",
|
||||||
"fr" : "Vous voulez juste essayer?",
|
"fr" : "Vous voulez juste essayer?",
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"auth.email" : {
|
"auth.email" : {
|
||||||
"used-in" : [ "src/app/main/ui/auth/register.cljs:101", "src/app/main/ui/auth/recovery_request.cljs:47", "src/app/main/ui/auth/login.cljs:92" ],
|
"used-in" : [ "src/app/main/ui/auth/login.cljs:92", "src/app/main/ui/auth/register.cljs:101", "src/app/main/ui/auth/recovery_request.cljs:47" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Email",
|
"en" : "Email",
|
||||||
"fr" : "Adresse email",
|
"fr" : "Adresse email",
|
||||||
|
@ -177,7 +177,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"auth.password" : {
|
"auth.password" : {
|
||||||
"used-in" : [ "src/app/main/ui/auth/register.cljs:106", "src/app/main/ui/auth/login.cljs:99" ],
|
"used-in" : [ "src/app/main/ui/auth/login.cljs:99", "src/app/main/ui/auth/register.cljs:106" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Password",
|
"en" : "Password",
|
||||||
"fr" : "Mot de passe",
|
"fr" : "Mot de passe",
|
||||||
|
@ -276,7 +276,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dashboard.add-shared" : {
|
"dashboard.add-shared" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:221", "src/app/main/ui/dashboard/grid.cljs:177" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:224", "src/app/main/ui/dashboard/grid.cljs:180" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Add as Shared Library",
|
"en" : "Add as Shared Library",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -322,7 +322,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dashboard.empty-files" : {
|
"dashboard.empty-files" : {
|
||||||
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:184" ],
|
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:187" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "You still have no files here",
|
"en" : "You still have no files here",
|
||||||
"fr" : "Vous n'avez encore aucun fichier ici",
|
"fr" : "Vous n'avez encore aucun fichier ici",
|
||||||
|
@ -533,7 +533,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dashboard.remove-shared" : {
|
"dashboard.remove-shared" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:219", "src/app/main/ui/dashboard/grid.cljs:176" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:222", "src/app/main/ui/dashboard/grid.cljs:179" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Remove as Shared Library",
|
"en" : "Remove as Shared Library",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -578,7 +578,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dashboard.show-all-files" : {
|
"dashboard.show-all-files" : {
|
||||||
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:246" ],
|
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:249" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Show all files",
|
"en" : "Show all files",
|
||||||
"es" : "Ver todos los ficheros"
|
"es" : "Ver todos los ficheros"
|
||||||
|
@ -636,7 +636,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dashboard.update-settings" : {
|
"dashboard.update-settings" : {
|
||||||
"used-in" : [ "src/app/main/ui/settings/profile.cljs:80", "src/app/main/ui/settings/password.cljs:96", "src/app/main/ui/settings/options.cljs:72" ],
|
"used-in" : [ "src/app/main/ui/settings/options.cljs:72", "src/app/main/ui/settings/profile.cljs:80", "src/app/main/ui/settings/password.cljs:96" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Update settings",
|
"en" : "Update settings",
|
||||||
"fr" : "Mettre à jour les paramètres",
|
"fr" : "Mettre à jour les paramètres",
|
||||||
|
@ -645,7 +645,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dashboard.your-account-title" : {
|
"dashboard.your-account-title" : {
|
||||||
"used-in" : [ "src/app/main/ui/settings.cljs:28" ],
|
"used-in" : [ "src/app/main/ui/settings.cljs:29" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Your account",
|
"en" : "Your account",
|
||||||
"es" : "Su cuenta"
|
"es" : "Su cuenta"
|
||||||
|
@ -712,7 +712,7 @@
|
||||||
"unused" : true
|
"unused" : true
|
||||||
},
|
},
|
||||||
"ds.confirm-cancel" : {
|
"ds.confirm-cancel" : {
|
||||||
"used-in" : [ "src/app/main/ui/confirm.cljs:28" ],
|
"used-in" : [ "src/app/main/ui/confirm.cljs:36" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Cancel",
|
"en" : "Cancel",
|
||||||
"fr" : "Annuler",
|
"fr" : "Annuler",
|
||||||
|
@ -721,7 +721,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ds.confirm-ok" : {
|
"ds.confirm-ok" : {
|
||||||
"used-in" : [ "src/app/main/ui/confirm.cljs:29" ],
|
"used-in" : [ "src/app/main/ui/confirm.cljs:37" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Ok",
|
"en" : "Ok",
|
||||||
"fr" : "Ok",
|
"fr" : "Ok",
|
||||||
|
@ -730,7 +730,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ds.confirm-title" : {
|
"ds.confirm-title" : {
|
||||||
"used-in" : [ "src/app/main/ui/confirm.cljs:27", "src/app/main/ui/confirm.cljs:30" ],
|
"used-in" : [ "src/app/main/ui/confirm.cljs:35", "src/app/main/ui/confirm.cljs:39" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Are you sure?",
|
"en" : "Are you sure?",
|
||||||
"fr" : "Êtes-vous sûr?",
|
"fr" : "Êtes-vous sûr?",
|
||||||
|
@ -757,7 +757,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"errors.email-already-exists" : {
|
"errors.email-already-exists" : {
|
||||||
"used-in" : [ "src/app/main/ui/auth/verify_token.cljs:80", "src/app/main/ui/settings/change_email.cljs:47" ],
|
"used-in" : [ "src/app/main/ui/settings/change_email.cljs:47", "src/app/main/ui/auth/verify_token.cljs:80" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Email already used",
|
"en" : "Email already used",
|
||||||
"fr" : "Adresse e-mail déjà utilisée",
|
"fr" : "Adresse e-mail déjà utilisée",
|
||||||
|
@ -784,7 +784,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"errors.generic" : {
|
"errors.generic" : {
|
||||||
"used-in" : [ "src/app/main/ui/auth/verify_token.cljs:89", "src/app/main/ui/settings/profile.cljs:40", "src/app/main/ui/settings/options.cljs:32" ],
|
"used-in" : [ "src/app/main/ui/settings/options.cljs:32", "src/app/main/ui/settings/profile.cljs:40", "src/app/main/ui/auth/verify_token.cljs:89" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Something wrong has happened.",
|
"en" : "Something wrong has happened.",
|
||||||
"fr" : "Quelque chose c'est mal passé.",
|
"fr" : "Quelque chose c'est mal passé.",
|
||||||
|
@ -811,7 +811,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"errors.media-type-mismatch" : {
|
"errors.media-type-mismatch" : {
|
||||||
"used-in" : [ "src/app/main/data/workspace/persistence.cljs:413", "src/app/main/data/media.cljs:61" ],
|
"used-in" : [ "src/app/main/data/media.cljs:61", "src/app/main/data/workspace/persistence.cljs:413" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Seems that the contents of the image does not match the file extension.",
|
"en" : "Seems that the contents of the image does not match the file extension.",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -820,7 +820,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"errors.media-type-not-allowed" : {
|
"errors.media-type-not-allowed" : {
|
||||||
"used-in" : [ "src/app/main/data/workspace/persistence.cljs:410", "src/app/main/data/media.cljs:58" ],
|
"used-in" : [ "src/app/main/data/media.cljs:58", "src/app/main/data/workspace/persistence.cljs:410" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Seems that this is not a valid image.",
|
"en" : "Seems that this is not a valid image.",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -865,7 +865,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"errors.unexpected-error" : {
|
"errors.unexpected-error" : {
|
||||||
"used-in" : [ "src/app/main/data/media.cljs:64", "src/app/main/ui/workspace/sidebar/options/exports.cljs:66", "src/app/main/ui/auth/register.cljs:45" ],
|
"used-in" : [ "src/app/main/data/media.cljs:64", "src/app/main/ui/auth/register.cljs:45", "src/app/main/ui/workspace/sidebar/options/exports.cljs:66" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "An unexpected error occurred.",
|
"en" : "An unexpected error occurred.",
|
||||||
"fr" : "Une erreur inattendue c'est produite",
|
"fr" : "Une erreur inattendue c'est produite",
|
||||||
|
@ -931,7 +931,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"labels.delete" : {
|
"labels.delete" : {
|
||||||
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:174", "src/app/main/ui/dashboard/files.cljs:85" ],
|
"used-in" : [ "src/app/main/ui/dashboard/files.cljs:85", "src/app/main/ui/dashboard/grid.cljs:177" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Delete",
|
"en" : "Delete",
|
||||||
"fr" : "Supprimer",
|
"fr" : "Supprimer",
|
||||||
|
@ -973,7 +973,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"labels.logout" : {
|
"labels.logout" : {
|
||||||
"used-in" : [ "src/app/main/ui/settings.cljs:30", "src/app/main/ui/dashboard/sidebar.cljs:459" ],
|
"used-in" : [ "src/app/main/ui/settings.cljs:31", "src/app/main/ui/dashboard/sidebar.cljs:459" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Logout",
|
"en" : "Logout",
|
||||||
"fr" : "Quitter",
|
"fr" : "Quitter",
|
||||||
|
@ -982,7 +982,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"labels.members" : {
|
"labels.members" : {
|
||||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:59", "src/app/main/ui/dashboard/team.cljs:63", "src/app/main/ui/dashboard/sidebar.cljs:295" ],
|
"used-in" : [ "src/app/main/ui/dashboard/sidebar.cljs:295", "src/app/main/ui/dashboard/team.cljs:59", "src/app/main/ui/dashboard/team.cljs:63" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Members"
|
"en" : "Members"
|
||||||
}
|
}
|
||||||
|
@ -1064,7 +1064,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"labels.rename" : {
|
"labels.rename" : {
|
||||||
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:173", "src/app/main/ui/dashboard/sidebar.cljs:298", "src/app/main/ui/dashboard/files.cljs:84" ],
|
"used-in" : [ "src/app/main/ui/dashboard/sidebar.cljs:298", "src/app/main/ui/dashboard/files.cljs:84", "src/app/main/ui/dashboard/grid.cljs:176" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Rename",
|
"en" : "Rename",
|
||||||
"es" : "Renombrar"
|
"es" : "Renombrar"
|
||||||
|
@ -1077,7 +1077,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"labels.settings" : {
|
"labels.settings" : {
|
||||||
"used-in" : [ "src/app/main/ui/settings/sidebar.cljs:80", "src/app/main/ui/dashboard/team.cljs:65", "src/app/main/ui/dashboard/sidebar.cljs:296" ],
|
"used-in" : [ "src/app/main/ui/settings/sidebar.cljs:80", "src/app/main/ui/dashboard/sidebar.cljs:296", "src/app/main/ui/dashboard/team.cljs:65" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Settings",
|
"en" : "Settings",
|
||||||
"fr" : "Settings",
|
"fr" : "Settings",
|
||||||
|
@ -1111,7 +1111,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"media.loading" : {
|
"media.loading" : {
|
||||||
"used-in" : [ "src/app/main/data/workspace/persistence.cljs:394", "src/app/main/data/media.cljs:43" ],
|
"used-in" : [ "src/app/main/data/media.cljs:43", "src/app/main/data/workspace/persistence.cljs:394" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Loading image...",
|
"en" : "Loading image...",
|
||||||
"fr" : "Chargement de l'image...",
|
"fr" : "Chargement de l'image...",
|
||||||
|
@ -1129,7 +1129,7 @@
|
||||||
"unused" : true
|
"unused" : true
|
||||||
},
|
},
|
||||||
"modals.add-shared-confirm.accept" : {
|
"modals.add-shared-confirm.accept" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:113", "src/app/main/ui/dashboard/grid.cljs:115" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:114", "src/app/main/ui/dashboard/grid.cljs:116" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Add as Shared Library",
|
"en" : "Add as Shared Library",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1147,7 +1147,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"modals.add-shared-confirm.message" : {
|
"modals.add-shared-confirm.message" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:110", "src/app/main/ui/dashboard/grid.cljs:112" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:111", "src/app/main/ui/dashboard/grid.cljs:113" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Add “%s” as Shared Library",
|
"en" : "Add “%s” as Shared Library",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1381,7 +1381,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"modals.remove-shared-confirm.accept" : {
|
"modals.remove-shared-confirm.accept" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:124", "src/app/main/ui/dashboard/grid.cljs:129" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:127", "src/app/main/ui/dashboard/grid.cljs:132" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Remove as Shared Library",
|
"en" : "Remove as Shared Library",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1390,7 +1390,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"modals.remove-shared-confirm.hint" : {
|
"modals.remove-shared-confirm.hint" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:123", "src/app/main/ui/dashboard/grid.cljs:128" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:125", "src/app/main/ui/dashboard/grid.cljs:130" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Once removed as Shared Library, the File Library of this file will stop being available to be used among the rest of your files.",
|
"en" : "Once removed as Shared Library, the File Library of this file will stop being available to be used among the rest of your files.",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1399,7 +1399,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"modals.remove-shared-confirm.message" : {
|
"modals.remove-shared-confirm.message" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:122", "src/app/main/ui/dashboard/grid.cljs:127" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:124", "src/app/main/ui/dashboard/grid.cljs:129" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Remove “%s” as Shared Library",
|
"en" : "Remove “%s” as Shared Library",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1417,7 +1417,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notifications.profile-saved" : {
|
"notifications.profile-saved" : {
|
||||||
"used-in" : [ "src/app/main/ui/settings/profile.cljs:36", "src/app/main/ui/settings/options.cljs:36" ],
|
"used-in" : [ "src/app/main/ui/settings/options.cljs:36", "src/app/main/ui/settings/profile.cljs:36" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Profile saved successfully!",
|
"en" : "Profile saved successfully!",
|
||||||
"fr" : "Profil enregistré avec succès!",
|
"fr" : "Profil enregistré avec succès!",
|
||||||
|
@ -1426,7 +1426,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notifications.validation-email-sent" : {
|
"notifications.validation-email-sent" : {
|
||||||
"used-in" : [ "src/app/main/ui/auth/register.cljs:54", "src/app/main/ui/settings/change_email.cljs:56" ],
|
"used-in" : [ "src/app/main/ui/settings/change_email.cljs:56", "src/app/main/ui/auth/register.cljs:54" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Verification email sent to %s; check your email!"
|
"en" : "Verification email sent to %s; check your email!"
|
||||||
}
|
}
|
||||||
|
@ -1441,7 +1441,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"settings.multiple" : {
|
"settings.multiple" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/stroke.cljs:156", "src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136", "src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:145", "src/app/main/ui/workspace/sidebar/options/typography.cljs:99", "src/app/main/ui/workspace/sidebar/options/typography.cljs:149", "src/app/main/ui/workspace/sidebar/options/typography.cljs:162" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:153", "src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:163", "src/app/main/ui/workspace/sidebar/options/typography.cljs:99", "src/app/main/ui/workspace/sidebar/options/typography.cljs:149", "src/app/main/ui/workspace/sidebar/options/typography.cljs:162", "src/app/main/ui/workspace/sidebar/options/stroke.cljs:156" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Mixed",
|
"en" : "Mixed",
|
||||||
"fr" : null,
|
"fr" : null,
|
||||||
|
@ -1666,7 +1666,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.assets" : {
|
"workspace.assets.assets" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:615" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:629" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Assets",
|
"en" : "Assets",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1675,7 +1675,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.box-filter-all" : {
|
"workspace.assets.box-filter-all" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:635" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:649" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "All assets",
|
"en" : "All assets",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1702,7 +1702,7 @@
|
||||||
"unused" : true
|
"unused" : true
|
||||||
},
|
},
|
||||||
"workspace.assets.colors" : {
|
"workspace.assets.colors" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:320", "src/app/main/ui/workspace/sidebar/assets.cljs:638" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:329", "src/app/main/ui/workspace/sidebar/assets.cljs:652" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Colors",
|
"en" : "Colors",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1711,7 +1711,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.components" : {
|
"workspace.assets.components" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:82", "src/app/main/ui/workspace/sidebar/assets.cljs:636" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:83", "src/app/main/ui/workspace/sidebar/assets.cljs:650" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Components",
|
"en" : "Components",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1720,7 +1720,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.delete" : {
|
"workspace.assets.delete" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:102", "src/app/main/ui/workspace/sidebar/assets.cljs:190", "src/app/main/ui/workspace/sidebar/assets.cljs:296", "src/app/main/ui/workspace/sidebar/assets.cljs:419" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:103", "src/app/main/ui/workspace/sidebar/assets.cljs:191", "src/app/main/ui/workspace/sidebar/assets.cljs:305", "src/app/main/ui/workspace/sidebar/assets.cljs:433" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Delete",
|
"en" : "Delete",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1729,7 +1729,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.edit" : {
|
"workspace.assets.edit" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:295", "src/app/main/ui/workspace/sidebar/assets.cljs:418" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:304", "src/app/main/ui/workspace/sidebar/assets.cljs:432" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Edit",
|
"en" : "Edit",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1738,7 +1738,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.file-library" : {
|
"workspace.assets.file-library" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:518" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:532" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "File library",
|
"en" : "File library",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1747,7 +1747,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.graphics" : {
|
"workspace.assets.graphics" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:163", "src/app/main/ui/workspace/sidebar/assets.cljs:637" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:164", "src/app/main/ui/workspace/sidebar/assets.cljs:651" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Graphics",
|
"en" : "Graphics",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1756,7 +1756,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.libraries" : {
|
"workspace.assets.libraries" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:618" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:632" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Libraries",
|
"en" : "Libraries",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1765,7 +1765,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.not-found" : {
|
"workspace.assets.not-found" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:579" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:593" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "No assets found",
|
"en" : "No assets found",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1774,7 +1774,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.rename" : {
|
"workspace.assets.rename" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:294", "src/app/main/ui/workspace/sidebar/assets.cljs:417" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:303", "src/app/main/ui/workspace/sidebar/assets.cljs:431" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Rename",
|
"en" : "Rename",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1783,7 +1783,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.search" : {
|
"workspace.assets.search" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:622" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:636" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Search assets",
|
"en" : "Search assets",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1792,7 +1792,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.shared" : {
|
"workspace.assets.shared" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:520" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:534" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "SHARED",
|
"en" : "SHARED",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1801,7 +1801,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.assets.typography" : {
|
"workspace.assets.typography" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:406", "src/app/main/ui/workspace/sidebar/assets.cljs:639" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:420", "src/app/main/ui/workspace/sidebar/assets.cljs:653" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Typographies"
|
"en" : "Typographies"
|
||||||
}
|
}
|
||||||
|
@ -1854,8 +1854,20 @@
|
||||||
"en" : "Text Transform"
|
"en" : "Text Transform"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"workspace.gradients.linear" : {
|
||||||
|
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:39", "src/app/main/ui/components/color_bullet.cljs:30" ],
|
||||||
|
"translations" : {
|
||||||
|
"en" : "Linear gradient"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"workspace.gradients.radial" : {
|
||||||
|
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:40", "src/app/main/ui/components/color_bullet.cljs:31" ],
|
||||||
|
"translations" : {
|
||||||
|
"en" : "Radial gradient"
|
||||||
|
}
|
||||||
|
},
|
||||||
"workspace.header.menu.disable-dynamic-alignment" : {
|
"workspace.header.menu.disable-dynamic-alignment" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:213" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:216" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Disable dynamic alignment",
|
"en" : "Disable dynamic alignment",
|
||||||
"fr" : "Désactiver l'alignement dynamique",
|
"fr" : "Désactiver l'alignement dynamique",
|
||||||
|
@ -1864,7 +1876,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.header.menu.disable-snap-grid" : {
|
"workspace.header.menu.disable-snap-grid" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:185" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:188" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Disable snap to grid",
|
"en" : "Disable snap to grid",
|
||||||
"fr" : "Désactiver l'alignement sur la grille",
|
"fr" : "Désactiver l'alignement sur la grille",
|
||||||
|
@ -1873,7 +1885,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.header.menu.enable-dynamic-alignment" : {
|
"workspace.header.menu.enable-dynamic-alignment" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:214" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:217" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Enable dynamic aligment",
|
"en" : "Enable dynamic aligment",
|
||||||
"fr" : "Activer l'alignement dynamique",
|
"fr" : "Activer l'alignement dynamique",
|
||||||
|
@ -1882,7 +1894,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.header.menu.enable-snap-grid" : {
|
"workspace.header.menu.enable-snap-grid" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:186" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:189" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Snap to grid",
|
"en" : "Snap to grid",
|
||||||
"fr" : "Aligner sur la grille",
|
"fr" : "Aligner sur la grille",
|
||||||
|
@ -1891,7 +1903,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.header.menu.hide-assets" : {
|
"workspace.header.menu.hide-assets" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:206" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:209" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Hide assets",
|
"en" : "Hide assets",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1900,7 +1912,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.header.menu.hide-grid" : {
|
"workspace.header.menu.hide-grid" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:178" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:181" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Hide grids",
|
"en" : "Hide grids",
|
||||||
"fr" : "Masquer la grille",
|
"fr" : "Masquer la grille",
|
||||||
|
@ -1909,7 +1921,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.header.menu.hide-layers" : {
|
"workspace.header.menu.hide-layers" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:192" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:195" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Hide layers",
|
"en" : "Hide layers",
|
||||||
"fr" : "Masquer les couches",
|
"fr" : "Masquer les couches",
|
||||||
|
@ -1918,7 +1930,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.header.menu.hide-palette" : {
|
"workspace.header.menu.hide-palette" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:199" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:202" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Hide color palette",
|
"en" : "Hide color palette",
|
||||||
"fr" : "Masquer la palette de couleurs",
|
"fr" : "Masquer la palette de couleurs",
|
||||||
|
@ -1927,7 +1939,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.header.menu.hide-rules" : {
|
"workspace.header.menu.hide-rules" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:171" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:174" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Hide rules",
|
"en" : "Hide rules",
|
||||||
"fr" : "Masquer les règles",
|
"fr" : "Masquer les règles",
|
||||||
|
@ -1936,7 +1948,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.header.menu.show-assets" : {
|
"workspace.header.menu.show-assets" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:207" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:210" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Show assets",
|
"en" : "Show assets",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -1945,7 +1957,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.header.menu.show-grid" : {
|
"workspace.header.menu.show-grid" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:179" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:182" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Show grid",
|
"en" : "Show grid",
|
||||||
"fr" : "Montrer la grille",
|
"fr" : "Montrer la grille",
|
||||||
|
@ -1954,7 +1966,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.header.menu.show-layers" : {
|
"workspace.header.menu.show-layers" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:193" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:196" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Show layers",
|
"en" : "Show layers",
|
||||||
"fr" : "Montrer les couches",
|
"fr" : "Montrer les couches",
|
||||||
|
@ -1963,7 +1975,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.header.menu.show-palette" : {
|
"workspace.header.menu.show-palette" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:200" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:203" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Show color palette",
|
"en" : "Show color palette",
|
||||||
"fr" : "Montrer la palette de couleurs",
|
"fr" : "Montrer la palette de couleurs",
|
||||||
|
@ -1972,7 +1984,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.header.menu.show-rules" : {
|
"workspace.header.menu.show-rules" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:172" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:175" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Show rules",
|
"en" : "Show rules",
|
||||||
"fr" : "Montrer les règles",
|
"fr" : "Montrer les règles",
|
||||||
|
@ -2005,7 +2017,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.header.viewer" : {
|
"workspace.header.viewer" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:260" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:263" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "View mode (Ctrl + P)",
|
"en" : "View mode (Ctrl + P)",
|
||||||
"fr" : "Mode visualisation (Ctrl + P)",
|
"fr" : "Mode visualisation (Ctrl + P)",
|
||||||
|
@ -2038,19 +2050,19 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.libraries.colors.file-library" : {
|
"workspace.libraries.colors.file-library" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/colorpicker.cljs:340", "src/app/main/ui/workspace/colorpalette.cljs:149" ],
|
"used-in" : [ "src/app/main/ui/workspace/colorpicker/libraries.cljs:87", "src/app/main/ui/workspace/colorpalette.cljs:149" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "File library"
|
"en" : "File library"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.libraries.colors.recent-colors" : {
|
"workspace.libraries.colors.recent-colors" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/colorpicker.cljs:339", "src/app/main/ui/workspace/colorpalette.cljs:159" ],
|
"used-in" : [ "src/app/main/ui/workspace/colorpicker/libraries.cljs:86", "src/app/main/ui/workspace/colorpalette.cljs:159" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Recent colors"
|
"en" : "Recent colors"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.libraries.colors.save-color" : {
|
"workspace.libraries.colors.save-color" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/colorpicker.cljs:375" ],
|
"used-in" : [ "src/app/main/ui/workspace/colorpicker.cljs:338" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Save color"
|
"en" : "Save color"
|
||||||
}
|
}
|
||||||
|
@ -2290,7 +2302,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.fill" : {
|
"workspace.options.fill" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/fill.cljs:51" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/fill.cljs:54" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Fill",
|
"en" : "Fill",
|
||||||
"fr" : "Remplissage",
|
"fr" : "Remplissage",
|
||||||
|
@ -2308,7 +2320,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.column" : {
|
"workspace.options.grid.column" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:129" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:132" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Columns",
|
"en" : "Columns",
|
||||||
"fr" : "Colonnes",
|
"fr" : "Colonnes",
|
||||||
|
@ -2317,7 +2329,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.params.columns" : {
|
"workspace.options.grid.params.columns" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:170" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:173" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Columns",
|
"en" : "Columns",
|
||||||
"fr" : "Colonnes",
|
"fr" : "Colonnes",
|
||||||
|
@ -2326,7 +2338,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.params.gutter" : {
|
"workspace.options.grid.params.gutter" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:203" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:206" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Gutter",
|
"en" : "Gutter",
|
||||||
"fr" : "Gouttière",
|
"fr" : "Gouttière",
|
||||||
|
@ -2335,7 +2347,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.params.height" : {
|
"workspace.options.grid.params.height" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:194" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:197" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Height",
|
"en" : "Height",
|
||||||
"fr" : "Hauteur",
|
"fr" : "Hauteur",
|
||||||
|
@ -2344,7 +2356,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.params.margin" : {
|
"workspace.options.grid.params.margin" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:209" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:212" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Margin",
|
"en" : "Margin",
|
||||||
"fr" : "Marge",
|
"fr" : "Marge",
|
||||||
|
@ -2353,7 +2365,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.params.rows" : {
|
"workspace.options.grid.params.rows" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:161" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:164" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Rows",
|
"en" : "Rows",
|
||||||
"fr" : "Lignes",
|
"fr" : "Lignes",
|
||||||
|
@ -2362,7 +2374,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.params.set-default" : {
|
"workspace.options.grid.params.set-default" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:222" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:226" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Set as default",
|
"en" : "Set as default",
|
||||||
"fr" : "Définir par défaut",
|
"fr" : "Définir par défaut",
|
||||||
|
@ -2371,7 +2383,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.params.size" : {
|
"workspace.options.grid.params.size" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:154" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:157" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Size",
|
"en" : "Size",
|
||||||
"fr" : "Taille",
|
"fr" : "Taille",
|
||||||
|
@ -2380,7 +2392,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.params.type" : {
|
"workspace.options.grid.params.type" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:179" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:182" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Type",
|
"en" : "Type",
|
||||||
"fr" : "Type",
|
"fr" : "Type",
|
||||||
|
@ -2389,7 +2401,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.params.type.bottom" : {
|
"workspace.options.grid.params.type.bottom" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:187" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:190" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Bottom",
|
"en" : "Bottom",
|
||||||
"fr" : "Bas",
|
"fr" : "Bas",
|
||||||
|
@ -2398,7 +2410,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.params.type.center" : {
|
"workspace.options.grid.params.type.center" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:185" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:188" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Center",
|
"en" : "Center",
|
||||||
"fr" : "Centre",
|
"fr" : "Centre",
|
||||||
|
@ -2407,7 +2419,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.params.type.left" : {
|
"workspace.options.grid.params.type.left" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:184" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:187" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Left",
|
"en" : "Left",
|
||||||
"fr" : "Gauche",
|
"fr" : "Gauche",
|
||||||
|
@ -2416,7 +2428,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.params.type.right" : {
|
"workspace.options.grid.params.type.right" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:188" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:191" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Right",
|
"en" : "Right",
|
||||||
"fr" : "Droite",
|
"fr" : "Droite",
|
||||||
|
@ -2425,7 +2437,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.params.type.stretch" : {
|
"workspace.options.grid.params.type.stretch" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:181" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:184" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Stretch",
|
"en" : "Stretch",
|
||||||
"fr" : "Étirer",
|
"fr" : "Étirer",
|
||||||
|
@ -2434,7 +2446,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.params.type.top" : {
|
"workspace.options.grid.params.type.top" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:183" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:186" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Top",
|
"en" : "Top",
|
||||||
"fr" : "Haut",
|
"fr" : "Haut",
|
||||||
|
@ -2443,7 +2455,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.params.use-default" : {
|
"workspace.options.grid.params.use-default" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:220" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:224" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Use default",
|
"en" : "Use default",
|
||||||
"fr" : "Utiliser la valeur par défaut",
|
"fr" : "Utiliser la valeur par défaut",
|
||||||
|
@ -2452,7 +2464,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.params.width" : {
|
"workspace.options.grid.params.width" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:195" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:198" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Width",
|
"en" : "Width",
|
||||||
"fr" : "Largeur",
|
"fr" : "Largeur",
|
||||||
|
@ -2461,7 +2473,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.row" : {
|
"workspace.options.grid.row" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:130" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:133" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Rows",
|
"en" : "Rows",
|
||||||
"fr" : "Lignes",
|
"fr" : "Lignes",
|
||||||
|
@ -2470,7 +2482,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.square" : {
|
"workspace.options.grid.square" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:128" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:131" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Square",
|
"en" : "Square",
|
||||||
"fr" : "Carré",
|
"fr" : "Carré",
|
||||||
|
@ -2479,7 +2491,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.grid.title" : {
|
"workspace.options.grid.title" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:234" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:238" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Grid & Layouts",
|
"en" : "Grid & Layouts",
|
||||||
"fr" : "Grille & couches",
|
"fr" : "Grille & couches",
|
||||||
|
@ -2488,7 +2500,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.group-fill" : {
|
"workspace.options.group-fill" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/fill.cljs:50" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/fill.cljs:53" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Group fill",
|
"en" : "Group fill",
|
||||||
"fr" : null,
|
"fr" : null,
|
||||||
|
@ -2497,7 +2509,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.group-stroke" : {
|
"workspace.options.group-stroke" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/stroke.cljs:70" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/stroke.cljs:72" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Group stroke",
|
"en" : "Group stroke",
|
||||||
"fr" : null,
|
"fr" : null,
|
||||||
|
@ -2524,7 +2536,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.position" : {
|
"workspace.options.position" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/measures.cljs:146", "src/app/main/ui/workspace/sidebar/options/frame.cljs:126" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame.cljs:126", "src/app/main/ui/workspace/sidebar/options/measures.cljs:146" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Position",
|
"en" : "Position",
|
||||||
"fr" : "Position",
|
"fr" : "Position",
|
||||||
|
@ -2578,7 +2590,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.selection-fill" : {
|
"workspace.options.selection-fill" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/fill.cljs:49" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/fill.cljs:52" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Selection fill",
|
"en" : "Selection fill",
|
||||||
"fr" : null,
|
"fr" : null,
|
||||||
|
@ -2587,7 +2599,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.selection-stroke" : {
|
"workspace.options.selection-stroke" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/stroke.cljs:69" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/stroke.cljs:71" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Selection stroke",
|
"en" : "Selection stroke",
|
||||||
"fr" : null,
|
"fr" : null,
|
||||||
|
@ -2632,13 +2644,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.shadow-options.title" : {
|
"workspace.options.shadow-options.title" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/shadow.cljs:190" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/shadow.cljs:194" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Shadow"
|
"en" : "Shadow"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.size" : {
|
"workspace.options.size" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/measures.cljs:116", "src/app/main/ui/workspace/sidebar/options/frame.cljs:99" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame.cljs:99", "src/app/main/ui/workspace/sidebar/options/measures.cljs:116" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Size",
|
"en" : "Size",
|
||||||
"fr" : "Taille",
|
"fr" : "Taille",
|
||||||
|
@ -2656,7 +2668,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.stroke" : {
|
"workspace.options.stroke" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/stroke.cljs:71" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/stroke.cljs:73" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Stroke",
|
"en" : "Stroke",
|
||||||
"fr" : "Bordure",
|
"fr" : "Bordure",
|
||||||
|
@ -2845,7 +2857,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.options.text-options.none" : {
|
"workspace.options.text-options.none" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/text.cljs:153", "src/app/main/ui/workspace/sidebar/options/typography.cljs:178" ],
|
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/typography.cljs:178", "src/app/main/ui/workspace/sidebar/options/text.cljs:153" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "None",
|
"en" : "None",
|
||||||
"fr" : "Aucune",
|
"fr" : "Aucune",
|
||||||
|
@ -2960,7 +2972,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.sitemap" : {
|
"workspace.sitemap" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:146" ],
|
"used-in" : [ "src/app/main/ui/workspace/header.cljs:149" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Sitemap",
|
"en" : "Sitemap",
|
||||||
"fr" : null,
|
"fr" : null,
|
||||||
|
@ -3059,7 +3071,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.updates.dismiss" : {
|
"workspace.updates.dismiss" : {
|
||||||
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:521" ],
|
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:538" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Dismiss",
|
"en" : "Dismiss",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -3068,7 +3080,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.updates.there-are-updates" : {
|
"workspace.updates.there-are-updates" : {
|
||||||
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:517" ],
|
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:534" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "There are updates in shared libraries",
|
"en" : "There are updates in shared libraries",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
@ -3077,7 +3089,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.updates.update" : {
|
"workspace.updates.update" : {
|
||||||
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:519" ],
|
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:536" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Update",
|
"en" : "Update",
|
||||||
"fr" : "",
|
"fr" : "",
|
||||||
|
|
|
@ -78,3 +78,4 @@
|
||||||
@import 'main/partials/user-settings';
|
@import 'main/partials/user-settings';
|
||||||
@import 'main/partials/workspace';
|
@import 'main/partials/workspace';
|
||||||
@import 'main/partials/workspace-header';
|
@import 'main/partials/workspace-header';
|
||||||
|
@import 'main/partials/color-bullet';
|
||||||
|
|
180
frontend/resources/styles/main/partials/color-bullet.scss
Normal file
180
frontend/resources/styles/main/partials/color-bullet.scss
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
.color-cell {
|
||||||
|
.color-bullet {
|
||||||
|
background-color: $color-white;
|
||||||
|
// Creates strange artifacts
|
||||||
|
border: 2px solid $color-gray-60;
|
||||||
|
// box-shadow: 0 0 0 2px $color-gray-60;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.cell-big .color-bullet {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.cell-small .color-bullet {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-bullet.color-big {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-cell.current {
|
||||||
|
.color-bullet {
|
||||||
|
border-color: $color-gray-50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.palette-menu .color-bullet {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid $color-gray-10;
|
||||||
|
margin-right: 5px;
|
||||||
|
background-size: 8px;
|
||||||
|
}
|
||||||
|
.color-cell.add-color .color-bullet {
|
||||||
|
align-items: center;
|
||||||
|
background-color: $color-gray-50;
|
||||||
|
border: 3px dashed $color-gray-10;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding: .6rem;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: $color-gray-10;
|
||||||
|
height: 30px;
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.colorpicker-content .color-bullet {
|
||||||
|
grid-area: color;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid $color-gray-10;
|
||||||
|
background-size: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-group .group-list-item .color-bullet {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-right: $x-small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-cell.add-color:hover .color-bullet {
|
||||||
|
border-color: $color-gray-30;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: $color-gray-30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-bullet {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=") left center;
|
||||||
|
background-color: $color-white;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-data .color-bullet.multiple {
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "?"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-data .color-bullet {
|
||||||
|
background-color: $color-gray-30;
|
||||||
|
border: 1px solid $color-gray-30;
|
||||||
|
border-radius: $br-small;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: $color-gray-10;
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 20px;
|
||||||
|
margin: 5px 4px 0 0;
|
||||||
|
width: 20px;
|
||||||
|
|
||||||
|
&.color-name {
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.palette-th {
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid $color-gray-30;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: $color-gray-30;
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: $color-primary;
|
||||||
|
svg {
|
||||||
|
fill: $color-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.colorpicker-content .libraries .selected-colors .color-bullet {
|
||||||
|
grid-area: auto;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: $color-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.button {
|
||||||
|
background: $color-white;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.button svg {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
fill: $color-gray-30;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.plus-button svg {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
fill: $color-black;
|
||||||
|
}
|
||||||
|
}
|
|
@ -123,7 +123,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 4.8rem;
|
height: 5rem;
|
||||||
padding: 0.25rem;
|
padding: 0.25rem;
|
||||||
|
|
||||||
&.size-small {
|
&.size-small {
|
||||||
|
@ -156,28 +156,6 @@
|
||||||
flex-basis: 52px;
|
flex-basis: 52px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color {
|
|
||||||
background-color: $color-gray-10;
|
|
||||||
border: 2px solid $color-gray-60;
|
|
||||||
border-radius: 50%;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.cell-big .color {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.cell-small .color {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color.color-big {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-text {
|
.color-text {
|
||||||
color: $color-gray-20;
|
color: $color-gray-20;
|
||||||
font-size: $fs12;
|
font-size: $fs12;
|
||||||
|
@ -186,11 +164,9 @@
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
width: 66px;
|
width: 66px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
margin-top: 0.25rem;
|
||||||
}
|
}
|
||||||
&.current {
|
&.current {
|
||||||
.color {
|
|
||||||
border-color: $color-gray-50;
|
|
||||||
}
|
|
||||||
.color-text {
|
.color-text {
|
||||||
color: $color-gray-50;
|
color: $color-gray-50;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
@ -217,31 +193,11 @@
|
||||||
}
|
}
|
||||||
&.add-color {
|
&.add-color {
|
||||||
margin-left: 1.5rem;
|
margin-left: 1.5rem;
|
||||||
.color {
|
|
||||||
align-items: center;
|
|
||||||
background-color: $color-gray-50;
|
|
||||||
border: 3px dashed $color-gray-10;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
padding: .6rem;
|
|
||||||
svg {
|
|
||||||
fill: $color-gray-10;
|
|
||||||
height: 30px;
|
|
||||||
width: 30px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.color-text {
|
.color-text {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
.color {
|
|
||||||
border-color: $color-gray-30;
|
|
||||||
svg {
|
|
||||||
fill: $color-gray-30;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.color-text {
|
.color-text {
|
||||||
color: $color-gray-40;
|
color: $color-gray-40;
|
||||||
}
|
}
|
||||||
|
@ -336,12 +292,5 @@ ul.palette-menu {
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-bullet {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
border-radius: 12px;
|
|
||||||
border: 1px solid $color-gray-10;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&.active,
|
&.active svg,
|
||||||
&:hover svg {
|
&:hover svg {
|
||||||
fill: $color-primary;
|
fill: $color-primary;
|
||||||
}
|
}
|
||||||
|
@ -72,10 +72,16 @@
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
.gradient-background {
|
.gradient-background-wrapper {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 1px solid $color-gray-10;
|
border: 1px solid $color-gray-10;
|
||||||
|
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=") left center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradient-background {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gradient-stop-wrapper {
|
.gradient-stop-wrapper {
|
||||||
|
@ -85,16 +91,21 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.gradient-stop {
|
.gradient-stop {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 50% 50%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 14px;
|
width: 15px;
|
||||||
height: 14px;
|
height: 15px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
border: 1px solid $color-gray-20;
|
border: 1px solid $color-gray-20;
|
||||||
margin-top: -2px;
|
margin-top: -2px;
|
||||||
margin-left: -7px;
|
margin-left: -7px;
|
||||||
box-shadow: 0 2px 2px rgb(0 0 0 / 15%);
|
box-shadow: 0 2px 2px rgb(0 0 0 / 15%);
|
||||||
|
|
||||||
.selected {
|
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=") left center;
|
||||||
|
background-color: $color-white;
|
||||||
|
|
||||||
|
&.active {
|
||||||
border-color: $color-primary;
|
border-color: $color-primary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,15 +241,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-bullet {
|
|
||||||
grid-area: color;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
background-color: rgba(var(--color));
|
|
||||||
border-radius: 12px;
|
|
||||||
border: 1px solid $color-gray-10;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shade-selector {
|
.shade-selector {
|
||||||
display: grid;
|
display: grid;
|
||||||
justify-items: center;
|
justify-items: center;
|
||||||
|
@ -269,6 +271,10 @@
|
||||||
justify-items: center;
|
justify-items: center;
|
||||||
grid-column-gap: 0.25rem;
|
grid-column-gap: 0.25rem;
|
||||||
|
|
||||||
|
&.disable-opacity {
|
||||||
|
grid-template-columns: 3.5rem repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -325,34 +331,6 @@
|
||||||
content: "";
|
content: "";
|
||||||
flex: auto;
|
flex: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selected-colors .color-bullet {
|
|
||||||
grid-area: auto;
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: $color-primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.button {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.button svg {
|
|
||||||
width: 12px;
|
|
||||||
height: 12px;
|
|
||||||
fill: $color-gray-30;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.plus-button svg {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
fill: $color-black;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
|
@ -465,13 +443,10 @@
|
||||||
|
|
||||||
.colorpicker-tabs {
|
.colorpicker-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-top: 0.25rem;
|
margin: 0.5rem 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 1px solid $color-gray-10;
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
background-color: $color-gray-10;
|
|
||||||
|
|
||||||
.active {
|
|
||||||
background-color: $color-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.colorpicker-tab {
|
.colorpicker-tab {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -483,9 +458,21 @@
|
||||||
svg {
|
svg {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
fill: $color-gray-30;
|
fill: $color-gray-20;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
background-color: $color-gray-10;
|
||||||
|
svg {
|
||||||
|
fill: $color-gray-60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:hover svg {
|
||||||
|
fill: $color-primary;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -241,13 +241,6 @@
|
||||||
color: $color-white;
|
color: $color-white;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
& .color-block {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin-right: $x-small;
|
|
||||||
}
|
|
||||||
|
|
||||||
& span {
|
& span {
|
||||||
margin-left: $x-small;
|
margin-left: $x-small;
|
||||||
color: $color-gray-30;
|
color: $color-gray-30;
|
||||||
|
|
|
@ -467,47 +467,6 @@
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-th {
|
|
||||||
background-color: $color-gray-30;
|
|
||||||
border: 1px solid $color-gray-30;
|
|
||||||
border-radius: $br-small;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: $color-gray-10;
|
|
||||||
flex-shrink: 0;
|
|
||||||
height: 20px;
|
|
||||||
margin: 5px 4px 0 0;
|
|
||||||
width: 20px;
|
|
||||||
|
|
||||||
&.color-name {
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.palette-th {
|
|
||||||
align-items: center;
|
|
||||||
border: 1px solid $color-gray-30;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
fill: $color-gray-30;
|
|
||||||
height: 16px;
|
|
||||||
width: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: $color-primary;
|
|
||||||
svg {
|
|
||||||
fill: $color-primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.presets {
|
.presets {
|
||||||
.custom-select-dropdown {
|
.custom-select-dropdown {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
|
|
|
@ -104,27 +104,26 @@
|
||||||
(assoc-in [:workspace-local :picked-color-select] value)
|
(assoc-in [:workspace-local :picked-color-select] value)
|
||||||
(assoc-in [:workspace-local :picked-shift?] shift?)))))
|
(assoc-in [:workspace-local :picked-shift?] shift?)))))
|
||||||
|
|
||||||
|
|
||||||
(defn change-fill
|
(defn change-fill
|
||||||
([ids color id file-id]
|
([ids color]
|
||||||
(change-fill ids color 1 id file-id))
|
|
||||||
([ids color opacity id file-id]
|
|
||||||
(ptk/reify ::change-fill
|
(ptk/reify ::change-fill
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state s]
|
(watch [_ state s]
|
||||||
(let [pid (:current-page-id state)
|
(let [pid (:current-page-id state)
|
||||||
objects (get-in state [:workspace-data :pages-index pid :objects])
|
objects (get-in state [:workspace-data :pages-index pid :objects])
|
||||||
children (mapcat #(cph/get-children % objects) ids)
|
not-frame (fn [shape-id] (not= (get-in objects [shape-id :type]) :frame))
|
||||||
|
children (->> ids (filter not-frame) (mapcat #(cph/get-children % objects)))
|
||||||
ids (into ids children)
|
ids (into ids children)
|
||||||
|
|
||||||
is-text? #(= :text (:type (get objects %)))
|
is-text? #(= :text (:type (get objects %)))
|
||||||
text-ids (filter is-text? ids)
|
text-ids (filter is-text? ids)
|
||||||
shape-ids (filter (comp not is-text?) ids)
|
shape-ids (filter (comp not is-text?) ids)
|
||||||
|
|
||||||
attrs (cond-> {:fill-color color
|
attrs (cond-> {:fill-color (:color color)
|
||||||
:fill-color-ref-id id
|
:fill-color-ref-id (:id color)
|
||||||
:fill-color-ref-file file-id}
|
:fill-color-ref-file (:file-id color)
|
||||||
(and opacity (not= opacity :multiple)) (assoc :fill-opacity opacity))
|
:fill-color-gradient (:gradient color)
|
||||||
|
:fill-opacity (:opacity color)})
|
||||||
|
|
||||||
update-fn (fn [shape] (merge shape attrs))
|
update-fn (fn [shape] (merge shape attrs))
|
||||||
editors (get-in state [:workspace-local :editors])
|
editors (get-in state [:workspace-local :editors])
|
||||||
|
@ -135,20 +134,22 @@
|
||||||
(map #(dwt/update-text-attrs {:id % :editor (get editors %) :attrs attrs}) text-ids)
|
(map #(dwt/update-text-attrs {:id % :editor (get editors %) :attrs attrs}) text-ids)
|
||||||
(dwc/update-shapes shape-ids update-fn))))))))
|
(dwc/update-shapes shape-ids update-fn))))))))
|
||||||
|
|
||||||
(defn change-stroke [ids color id file-id]
|
(defn change-stroke [ids color]
|
||||||
(ptk/reify ::change-stroke
|
(ptk/reify ::change-stroke
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state s]
|
(watch [_ state s]
|
||||||
(let [objects (get-in state [:workspace-data :pages-index (:current-page-id state) :objects])
|
(let [objects (get-in state [:workspace-data :pages-index (:current-page-id state) :objects])
|
||||||
children (mapcat #(cph/get-children % objects) ids)
|
not-frame (fn [shape-id] (not= (get-in objects [shape-id :type]) :frame))
|
||||||
|
children (->> ids (filter not-frame) (mapcat #(cph/get-children % objects)))
|
||||||
ids (into ids children)
|
ids (into ids children)
|
||||||
|
|
||||||
update-fn (fn [s]
|
update-fn (fn [s]
|
||||||
(cond-> s
|
(cond-> s
|
||||||
true
|
true
|
||||||
(assoc :stroke-color color
|
(assoc :stroke-color (:color color)
|
||||||
:stroke-color-ref-id id
|
:stroke-color-gradient (:gradient color)
|
||||||
:stroke-color-ref-file file-id)
|
:stroke-color-ref-id (:id color)
|
||||||
|
:stroke-color-ref-file (:file-id color))
|
||||||
|
|
||||||
(= (:stroke-style s) :none)
|
(= (:stroke-style s) :none)
|
||||||
(assoc :stroke-style :solid
|
(assoc :stroke-style :solid
|
||||||
|
@ -157,20 +158,67 @@
|
||||||
(rx/of (dwc/update-shapes ids update-fn))))))
|
(rx/of (dwc/update-shapes ids update-fn))))))
|
||||||
|
|
||||||
(defn picker-for-selected-shape []
|
(defn picker-for-selected-shape []
|
||||||
;; TODO: replace st/emit! by a subject push and set that in the WatchEvent
|
(let [sub (rx/subject)]
|
||||||
(let [handle-change-color (fn [color opacity id file-id shift?]
|
(ptk/reify ::picker-for-selected-shape
|
||||||
(let [ids (get-in @st/state [:workspace-local :selected])]
|
ptk/WatchEvent
|
||||||
(st/emit!
|
(watch [_ state stream]
|
||||||
(if shift?
|
(let [ids (get-in state [:workspace-local :selected])
|
||||||
(change-stroke ids color nil nil)
|
stop? (->> stream
|
||||||
(change-fill ids color nil nil))
|
(rx/filter (ptk/type? ::stop-picker)))
|
||||||
(md/hide))))]
|
|
||||||
(ptk/reify ::start-picker
|
update-events (fn [[color shift?]]
|
||||||
|
(rx/of (if shift?
|
||||||
|
(change-stroke ids color)
|
||||||
|
(change-fill ids color))
|
||||||
|
(stop-picker)))]
|
||||||
|
(rx/merge
|
||||||
|
;; Stream that updates the stroke/width and stops if `esc` pressed
|
||||||
|
(->> sub
|
||||||
|
(rx/take-until stop?)
|
||||||
|
(rx/flat-map update-events))
|
||||||
|
|
||||||
|
;; Hide the modal if the stop event is emitted
|
||||||
|
(->> stop?
|
||||||
|
(rx/first)
|
||||||
|
(rx/map #(md/hide))))))
|
||||||
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
|
(let [handle-change-color (fn [color shift?] (rx/push! sub [color shift?]))]
|
||||||
|
(-> state
|
||||||
|
(assoc-in [:workspace-local :picking-color?] true)
|
||||||
|
(assoc ::md/modal {:id (random-uuid)
|
||||||
|
:data {:color "#000000" :opacity 1}
|
||||||
|
:type :colorpicker
|
||||||
|
:props {:on-change handle-change-color}
|
||||||
|
:allow-click-outside true})))))))
|
||||||
|
|
||||||
|
(defn start-gradient [gradient]
|
||||||
|
(ptk/reify ::start-gradient
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(let [id (first (get-in state [:workspace-local :selected]))]
|
||||||
(-> state
|
(-> state
|
||||||
(assoc-in [:workspace-local :picking-color?] true)
|
(assoc-in [:workspace-local :current-gradient] gradient)
|
||||||
(assoc ::md/modal {:id (random-uuid)
|
(assoc-in [:workspace-local :current-gradient :shape-id] id))))))
|
||||||
:type :colorpicker
|
|
||||||
:props {:on-change handle-change-color}
|
(defn stop-gradient []
|
||||||
:allow-click-outside true}))))))
|
(ptk/reify ::stop-gradient
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(-> state
|
||||||
|
(update :workspace-local dissoc :current-gradient)))))
|
||||||
|
|
||||||
|
(defn update-gradient [changes]
|
||||||
|
(ptk/reify ::update-gradient
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(-> state
|
||||||
|
(update-in [:workspace-local :current-gradient] merge changes)))))
|
||||||
|
|
||||||
|
(defn select-gradient-stop [spot]
|
||||||
|
(ptk/reify ::select-gradient-stop
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(-> state
|
||||||
|
(assoc-in [:workspace-local :editing-stop] spot)))))
|
||||||
|
|
|
@ -29,6 +29,14 @@
|
||||||
:type type
|
:type type
|
||||||
:props props
|
:props props
|
||||||
:allow-click-outside false})))))
|
:allow-click-outside false})))))
|
||||||
|
(defn update-props
|
||||||
|
([type props]
|
||||||
|
(ptk/reify ::show-modal
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(cond-> state
|
||||||
|
(::modal state)
|
||||||
|
(update-in [::modal :props] merge props))))))
|
||||||
|
|
||||||
(defn hide
|
(defn hide
|
||||||
[]
|
[]
|
||||||
|
@ -42,12 +50,18 @@
|
||||||
(ptk/reify ::update-modal
|
(ptk/reify ::update-modal
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(c/update state ::modal merge options))))
|
(cond-> state
|
||||||
|
(::modal state)
|
||||||
|
(c/update ::modal merge options)))))
|
||||||
|
|
||||||
(defn show!
|
(defn show!
|
||||||
[type props]
|
[type props]
|
||||||
(st/emit! (show type props)))
|
(st/emit! (show type props)))
|
||||||
|
|
||||||
|
(defn update-props!
|
||||||
|
[type props]
|
||||||
|
(st/emit! (update-props type props)))
|
||||||
|
|
||||||
(defn allow-click-outside!
|
(defn allow-click-outside!
|
||||||
[]
|
[]
|
||||||
(st/emit! (update {:allow-click-outside true})))
|
(st/emit! (update {:allow-click-outside true})))
|
||||||
|
|
|
@ -741,7 +741,7 @@
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(let [selected (get-in state [:workspace-local :selected])]
|
(let [selected (get-in state [:workspace-local :selected])]
|
||||||
(rx/of (delete-shapes selected)
|
(rx/of (delete-shapes selected)
|
||||||
dws/deselect-all)))))
|
(dws/deselect-all))))))
|
||||||
|
|
||||||
;; --- Shape Vertical Ordering
|
;; --- Shape Vertical Ordering
|
||||||
|
|
||||||
|
@ -1318,7 +1318,7 @@
|
||||||
:grow-type (if (> (count text) 100) :auto-height :auto-width)
|
:grow-type (if (> (count text) 100) :auto-height :auto-width)
|
||||||
:content (as-content text)})]
|
:content (as-content text)})]
|
||||||
(rx/of dwc/start-undo-transaction
|
(rx/of dwc/start-undo-transaction
|
||||||
dws/deselect-all
|
(dws/deselect-all)
|
||||||
(add-shape shape)
|
(add-shape shape)
|
||||||
(dwc/rehash-shape-frame-relationship [id])
|
(dwc/rehash-shape-frame-relationship [id])
|
||||||
dwc/commit-undo-transaction)))))
|
dwc/commit-undo-transaction)))))
|
||||||
|
@ -1444,22 +1444,23 @@
|
||||||
|
|
||||||
(defn change-canvas-color
|
(defn change-canvas-color
|
||||||
[color]
|
[color]
|
||||||
(s/assert string? color)
|
;; TODO: Create a color spec
|
||||||
|
#_(s/assert string? color)
|
||||||
(ptk/reify ::change-canvas-color
|
(ptk/reify ::change-canvas-color
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(let [page-id (get state :current-page-id)
|
(let [page-id (get state :current-page-id)
|
||||||
options (dwc/lookup-page-options state page-id)
|
options (dwc/lookup-page-options state page-id)
|
||||||
ccolor (:background options)]
|
previus-color (:background options)]
|
||||||
(rx/of (dwc/commit-changes
|
(rx/of (dwc/commit-changes
|
||||||
[{:type :set-option
|
[{:type :set-option
|
||||||
:page-id page-id
|
:page-id page-id
|
||||||
:option :background
|
:option :background
|
||||||
:value color}]
|
:value (:color color)}]
|
||||||
[{:type :set-option
|
[{:type :set-option
|
||||||
:page-id page-id
|
:page-id page-id
|
||||||
:option :background
|
:option :background
|
||||||
:value ccolor}]
|
:value previus-color}]
|
||||||
{:commit-local? true}))))))
|
{:commit-local? true}))))))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
@ -1530,7 +1531,7 @@
|
||||||
(select-for-drawing :text))
|
(select-for-drawing :text))
|
||||||
"ctrl+c" #(st/emit! copy-selected)
|
"ctrl+c" #(st/emit! copy-selected)
|
||||||
"ctrl+v" #(st/emit! paste)
|
"ctrl+v" #(st/emit! paste)
|
||||||
"escape" #(st/emit! :interrupt deselect-all)
|
"escape" #(st/emit! :interrupt (deselect-all true))
|
||||||
"del" #(st/emit! delete-selected)
|
"del" #(st/emit! delete-selected)
|
||||||
"backspace" #(st/emit! delete-selected)
|
"backspace" #(st/emit! delete-selected)
|
||||||
"ctrl+up" #(st/emit! (vertical-order-selected :up))
|
"ctrl+up" #(st/emit! (vertical-order-selected :up))
|
||||||
|
|
|
@ -299,7 +299,7 @@
|
||||||
(rx/of dwc/start-undo-transaction)
|
(rx/of dwc/start-undo-transaction)
|
||||||
(rx/empty))
|
(rx/empty))
|
||||||
|
|
||||||
(rx/of dw/deselect-all
|
(rx/of (dw/deselect-all)
|
||||||
(dw/add-shape shape))))))))))
|
(dw/add-shape shape))))))))))
|
||||||
|
|
||||||
(def close-drawing-path
|
(def close-drawing-path
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
(defonce ^:private default-square-params
|
(defonce ^:private default-square-params
|
||||||
{:size 16
|
{:size 16
|
||||||
:color {:value "#59B9E2"
|
:color {:color "#59B9E2"
|
||||||
:opacity 0.2}})
|
:opacity 0.2}})
|
||||||
|
|
||||||
(defonce ^:private default-layout-params
|
(defonce ^:private default-layout-params
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
:item-length nil
|
:item-length nil
|
||||||
:gutter 8
|
:gutter 8
|
||||||
:margin 0
|
:margin 0
|
||||||
:color {:value "#DE4762"
|
:color {:color "#DE4762"
|
||||||
:opacity 0.1}})
|
:opacity 0.1}})
|
||||||
|
|
||||||
(defonce default-grid-params
|
(defonce default-grid-params
|
||||||
|
|
|
@ -33,25 +33,32 @@
|
||||||
|
|
||||||
(declare sync-file)
|
(declare sync-file)
|
||||||
|
|
||||||
|
(defn default-color-name [color]
|
||||||
|
(or (:color color)
|
||||||
|
(case (get-in color [:gradient :type])
|
||||||
|
:linear (tr "workspace.gradients.linear")
|
||||||
|
:radial (tr "workspace.gradients.radial"))))
|
||||||
|
|
||||||
(defn add-color
|
(defn add-color
|
||||||
[color]
|
[color]
|
||||||
(us/assert ::us/string color)
|
(let [id (uuid/next)
|
||||||
(ptk/reify ::add-color
|
color (assoc color
|
||||||
ptk/WatchEvent
|
:id id
|
||||||
(watch [_ state s]
|
:name (default-color-name color))]
|
||||||
(let [id (uuid/next)
|
(us/assert ::cp/color color)
|
||||||
rchg {:type :add-color
|
(ptk/reify ::add-color
|
||||||
:color {:id id
|
ptk/WatchEvent
|
||||||
:name color
|
(watch [_ state s]
|
||||||
:value color}}
|
(let [rchg {:type :add-color
|
||||||
uchg {:type :del-color
|
:color color}
|
||||||
:id id}]
|
uchg {:type :del-color
|
||||||
(rx/of #(assoc-in % [:workspace-local :color-for-rename] id)
|
:id id}]
|
||||||
(dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))
|
(rx/of #(assoc-in % [:workspace-local :color-for-rename] id)
|
||||||
|
(dwc/commit-changes [rchg] [uchg] {:commit-local? true})))))))
|
||||||
|
|
||||||
(defn add-recent-color
|
(defn add-recent-color
|
||||||
[color]
|
[color]
|
||||||
(us/assert ::us/string color)
|
(us/assert ::cp/recent-color color)
|
||||||
(ptk/reify ::add-recent-color
|
(ptk/reify ::add-recent-color
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state s]
|
(watch [_ state s]
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.common :as dwc]
|
||||||
|
[app.main.data.modal :as md]
|
||||||
[app.main.streams :as ms]
|
[app.main.streams :as ms]
|
||||||
[app.main.worker :as uw]))
|
[app.main.worker :as uw]))
|
||||||
|
|
||||||
|
@ -66,7 +67,7 @@
|
||||||
(ms/mouse-up? %))
|
(ms/mouse-up? %))
|
||||||
stream)]
|
stream)]
|
||||||
(rx/concat
|
(rx/concat
|
||||||
(rx/of deselect-all)
|
(rx/of (deselect-all))
|
||||||
(->> ms/mouse-position
|
(->> ms/mouse-position
|
||||||
(rx/scan (fn [data pos]
|
(rx/scan (fn [data pos]
|
||||||
(if data
|
(if data
|
||||||
|
@ -117,15 +118,26 @@
|
||||||
objects (dwc/lookup-page-objects state page-id)]
|
objects (dwc/lookup-page-objects state page-id)]
|
||||||
(rx/of (dwc/expand-all-parents ids objects))))))
|
(rx/of (dwc/expand-all-parents ids objects))))))
|
||||||
|
|
||||||
(def deselect-all
|
(defn deselect-all
|
||||||
"Clear all possible state of drawing, edition
|
"Clear all possible state of drawing, edition
|
||||||
or any similar action taken by the user."
|
or any similar action taken by the user.
|
||||||
(ptk/reify ::deselect-all
|
When `check-modal` the method will check if a modal is opened
|
||||||
ptk/UpdateEvent
|
and not deselect if it's true"
|
||||||
(update [_ state]
|
([] (deselect-all false))
|
||||||
(update state :workspace-local #(-> %
|
|
||||||
(assoc :selected (d/ordered-set))
|
([check-modal]
|
||||||
(dissoc :selected-frame))))))
|
(ptk/reify ::deselect-all
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
|
||||||
|
;; Only deselect if there is no modal openned
|
||||||
|
(cond-> state
|
||||||
|
(or (not check-modal)
|
||||||
|
(not (::md/modal state)))
|
||||||
|
(update :workspace-local
|
||||||
|
#(-> %
|
||||||
|
(assoc :selected (d/ordered-set))
|
||||||
|
(dissoc :selected-frame))))))))
|
||||||
|
|
||||||
;; --- Select Shapes (By selrect)
|
;; --- Select Shapes (By selrect)
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,8 @@
|
||||||
[app.main.ui.shapes.path :as path]
|
[app.main.ui.shapes.path :as path]
|
||||||
[app.main.ui.shapes.rect :as rect]
|
[app.main.ui.shapes.rect :as rect]
|
||||||
[app.main.ui.shapes.text :as text]
|
[app.main.ui.shapes.text :as text]
|
||||||
[app.main.ui.shapes.group :as group]))
|
[app.main.ui.shapes.group :as group]
|
||||||
|
[app.main.ui.shapes.shape :refer [shape-container]]))
|
||||||
|
|
||||||
(def ^:private default-color "#E8E9EA") ;; $color-canvas
|
(def ^:private default-color "#E8E9EA") ;; $color-canvas
|
||||||
|
|
||||||
|
@ -56,7 +57,8 @@
|
||||||
[{:keys [shape] :as props}]
|
[{:keys [shape] :as props}]
|
||||||
(let [childs (mapv #(get objects %) (:shapes shape))
|
(let [childs (mapv #(get objects %) (:shapes shape))
|
||||||
shape (geom/transform-shape shape)]
|
shape (geom/transform-shape shape)]
|
||||||
[:& frame-shape {:shape shape :childs childs}]))))
|
[:> shape-container {:shape shape}
|
||||||
|
[:& frame-shape {:shape shape :childs childs}]]))))
|
||||||
|
|
||||||
(defn group-wrapper-factory
|
(defn group-wrapper-factory
|
||||||
[objects]
|
[objects]
|
||||||
|
@ -78,10 +80,8 @@
|
||||||
frame-wrapper (mf/use-memo (mf/deps objects) #(frame-wrapper-factory objects))]
|
frame-wrapper (mf/use-memo (mf/deps objects) #(frame-wrapper-factory objects))]
|
||||||
(when (and shape (not (:hidden shape)))
|
(when (and shape (not (:hidden shape)))
|
||||||
(let [shape (geom/transform-shape frame shape)
|
(let [shape (geom/transform-shape frame shape)
|
||||||
opts #js {:shape shape}
|
opts #js {:shape shape}]
|
||||||
filter-id (filters/get-filter-id)]
|
[:> shape-container {:shape shape}
|
||||||
[:g {:filter (filters/filter-str filter-id shape)}
|
|
||||||
[:& filters/filters {:filter-id filter-id :shape shape}]
|
|
||||||
(case (:type shape)
|
(case (:type shape)
|
||||||
:curve [:> path/path-shape opts]
|
:curve [:> path/path-shape opts]
|
||||||
:text [:> text/text-shape opts]
|
:text [:> text/text-shape opts]
|
||||||
|
|
41
frontend/src/app/main/ui/components/color_bullet.cljs
Normal file
41
frontend/src/app/main/ui/components/color_bullet.cljs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
;; 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.components.color-bullet
|
||||||
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
|
[app.util.color :as uc]))
|
||||||
|
|
||||||
|
(mf/defc color-bullet [{:keys [color on-click]}]
|
||||||
|
(if (uc/multiple? color)
|
||||||
|
[:div.color-bullet.multiple {:on-click #(when on-click (on-click %))}]
|
||||||
|
|
||||||
|
;; No multiple selection
|
||||||
|
(let [color (if (string? color) {:color color :opacity 1} color)]
|
||||||
|
[:div.color-bullet {:on-click #(when on-click (on-click %))}
|
||||||
|
(when (not (:gradient color))
|
||||||
|
[:div.color-bullet-left {:style {:background (uc/color->background (assoc color :opacity 1))}}])
|
||||||
|
|
||||||
|
[:div.color-bullet-right {:style {:background (uc/color->background color)}}]])))
|
||||||
|
|
||||||
|
(defn gradient-type->string [type]
|
||||||
|
(case type
|
||||||
|
:linear (tr "workspace.gradients.linear")
|
||||||
|
:radial (tr "workspace.gradients.radial")
|
||||||
|
(str "???" type)))
|
||||||
|
|
||||||
|
(mf/defc color-name [{:keys [color size on-click on-double-click]}]
|
||||||
|
(let [color (if (string? color) {:color color :opacity 1} color)
|
||||||
|
{:keys [name color opacity gradient]} color
|
||||||
|
color-str (or name color (gradient-type->string (:type gradient)))]
|
||||||
|
(when (= size :big)
|
||||||
|
[:span.color-text {:on-click #(when on-click (on-click %))
|
||||||
|
:on-double-click #(when on-double-click (on-double-click %))
|
||||||
|
:title name } color-str])))
|
|
@ -12,3 +12,5 @@
|
||||||
[rumext.alpha :as mf]))
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
(def embed-ctx (mf/create-context false))
|
(def embed-ctx (mf/create-context false))
|
||||||
|
|
||||||
|
(def render-ctx (mf/create-context nil))
|
||||||
|
|
|
@ -20,10 +20,10 @@
|
||||||
(:import goog.events.EventType))
|
(:import goog.events.EventType))
|
||||||
|
|
||||||
(defn- on-esc-clicked
|
(defn- on-esc-clicked
|
||||||
[event]
|
[event allow-click-outside]
|
||||||
(when (k/esc? event)
|
(when (and (k/esc? event) (not allow-click-outside))
|
||||||
(st/emit! (dm/hide))
|
(do (dom/stop-propagation event)
|
||||||
(dom/stop-propagation event)))
|
(st/emit! (dm/hide)))))
|
||||||
|
|
||||||
(defn- on-pop-state
|
(defn- on-pop-state
|
||||||
[event]
|
[event]
|
||||||
|
@ -43,11 +43,14 @@
|
||||||
(st/emit! (dm/hide)))))
|
(st/emit! (dm/hide)))))
|
||||||
|
|
||||||
(defn- on-click-outside
|
(defn- on-click-outside
|
||||||
[event wrapper-ref allow-click-outside]
|
[event wrapper-ref type allow-click-outside]
|
||||||
(let [wrapper (mf/ref-val wrapper-ref)
|
(let [wrapper (mf/ref-val wrapper-ref)
|
||||||
current (dom/get-target event)]
|
current (dom/get-target event)]
|
||||||
|
|
||||||
(when (and wrapper (not allow-click-outside) (not (.contains wrapper current)))
|
(when (and wrapper
|
||||||
|
(not allow-click-outside)
|
||||||
|
(not (.contains wrapper current))
|
||||||
|
(not (= type (keyword (.getAttribute current "data-allow-click-modal")))))
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(st/emit! (dm/hide)))))
|
(st/emit! (dm/hide)))))
|
||||||
|
@ -59,16 +62,23 @@
|
||||||
(let [data (unchecked-get props "data")
|
(let [data (unchecked-get props "data")
|
||||||
wrapper-ref (mf/use-ref nil)
|
wrapper-ref (mf/use-ref nil)
|
||||||
|
|
||||||
|
allow-click-outside (:allow-click-outside data)
|
||||||
|
|
||||||
handle-click-outside
|
handle-click-outside
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(on-click-outside event wrapper-ref (:allow-click-outside data)))]
|
(on-click-outside event wrapper-ref (:type data) allow-click-outside))
|
||||||
|
|
||||||
|
handle-keydown
|
||||||
|
(fn [event]
|
||||||
|
(on-esc-clicked event allow-click-outside))]
|
||||||
|
|
||||||
(mf/use-layout-effect
|
(mf/use-layout-effect
|
||||||
|
(mf/deps allow-click-outside)
|
||||||
(fn []
|
(fn []
|
||||||
(let [keys [(events/listen js/document EventType.KEYDOWN on-esc-clicked)
|
(let [keys [(events/listen js/document EventType.KEYDOWN handle-keydown)
|
||||||
(events/listen js/window EventType.POPSTATE on-pop-state)
|
(events/listen js/window EventType.POPSTATE on-pop-state)
|
||||||
(events/listen js/document EventType.CLICK handle-click-outside)]]
|
(events/listen js/document EventType.CLICK handle-click-outside)]]
|
||||||
#(for [key keys]
|
#(doseq [key keys]
|
||||||
(events/unlistenByKey key)))))
|
(events/unlistenByKey key)))))
|
||||||
|
|
||||||
[:div.modal-wrapper {:ref wrapper-ref}
|
[:div.modal-wrapper {:ref wrapper-ref}
|
||||||
|
|
|
@ -9,8 +9,10 @@
|
||||||
|
|
||||||
(ns app.main.ui.shapes.attrs
|
(ns app.main.ui.shapes.attrs
|
||||||
(:require
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[app.util.object :as obj]))
|
[app.util.object :as obj]
|
||||||
|
[app.main.ui.context :as muc]))
|
||||||
|
|
||||||
(defn- stroke-type->dasharray
|
(defn- stroke-type->dasharray
|
||||||
[style]
|
[style]
|
||||||
|
@ -20,21 +22,37 @@
|
||||||
:dashed "10,10"
|
:dashed "10,10"
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
|
(defn add-border-radius [attrs shape]
|
||||||
|
(obj/merge! attrs #js {:rx (:rx shape)
|
||||||
|
:ry (:ry shape)}))
|
||||||
|
|
||||||
|
(defn add-fill [attrs shape render-id]
|
||||||
|
(let [fill-color-gradient-id (str "fill-color-gradient_" render-id)]
|
||||||
|
(if (:fill-color-gradient shape)
|
||||||
|
(obj/merge! attrs #js {:fill (str/format "url(#%s)" fill-color-gradient-id)})
|
||||||
|
(obj/merge! attrs #js {:fill (or (:fill-color shape) "transparent")
|
||||||
|
:fillOpacity (:fill-opacity shape nil)}))))
|
||||||
|
|
||||||
|
(defn add-stroke [attrs shape render-id]
|
||||||
|
(let [stroke-style (:stroke-style shape :none)
|
||||||
|
stroke-color-gradient-id (str "stroke-color-gradient_" render-id)]
|
||||||
|
(if (not= stroke-style :none)
|
||||||
|
(if (:stroke-color-gradient shape)
|
||||||
|
(obj/merge! attrs
|
||||||
|
#js {:stroke (str/format "url(#%s)" stroke-color-gradient-id)
|
||||||
|
:strokeWidth (:stroke-width shape 1)
|
||||||
|
:strokeDasharray (stroke-type->dasharray stroke-style)})
|
||||||
|
(obj/merge! attrs
|
||||||
|
#js {:stroke (:stroke-color shape nil)
|
||||||
|
:strokeWidth (:stroke-width shape 1)
|
||||||
|
:strokeOpacity (:stroke-opacity shape nil)
|
||||||
|
:strokeDasharray (stroke-type->dasharray stroke-style)}))))
|
||||||
|
attrs)
|
||||||
|
|
||||||
(defn extract-style-attrs
|
(defn extract-style-attrs
|
||||||
([shape] (extract-style-attrs shape nil))
|
([shape]
|
||||||
([shape gradient-id]
|
(let [render-id (mf/use-ctx muc/render-ctx)]
|
||||||
(let [stroke-style (:stroke-style shape :none)
|
(-> (obj/new)
|
||||||
attrs #js {:rx (:rx shape nil)
|
(add-border-radius shape)
|
||||||
:ry (:ry shape nil)}
|
(add-fill shape render-id)
|
||||||
attrs (obj/merge! attrs
|
(add-stroke shape render-id)))))
|
||||||
(if gradient-id
|
|
||||||
#js {:fill (str/format "url(#%s)" gradient-id)}
|
|
||||||
#js {:fill (or (:fill-color shape) "transparent")
|
|
||||||
:fillOpacity (:fill-opacity shape nil)}))]
|
|
||||||
(when (not= stroke-style :none)
|
|
||||||
(obj/merge! attrs
|
|
||||||
#js {:stroke (:stroke-color shape nil)
|
|
||||||
:strokeWidth (:stroke-width shape 1)
|
|
||||||
:strokeOpacity (:stroke-opacity shape nil)
|
|
||||||
:strokeDasharray (stroke-type->dasharray stroke-style)}))
|
|
||||||
attrs)))
|
|
||||||
|
|
|
@ -25,8 +25,9 @@
|
||||||
(str/fmt "url(#$0)" [filter-id])))
|
(str/fmt "url(#$0)" [filter-id])))
|
||||||
|
|
||||||
(mf/defc color-matrix
|
(mf/defc color-matrix
|
||||||
[{:keys [color opacity]}]
|
[{:keys [color]}]
|
||||||
(let [[r g b a] (color/hex->rgba color opacity)
|
(let [{:keys [color opacity]} color
|
||||||
|
[r g b a] (color/hex->rgba color opacity)
|
||||||
[r g b] [(/ r 255) (/ g 255) (/ b 255)]]
|
[r g b] [(/ r 255) (/ g 255) (/ b 255)]]
|
||||||
[:feColorMatrix
|
[:feColorMatrix
|
||||||
{:type "matrix"
|
{:type "matrix"
|
||||||
|
@ -36,7 +37,7 @@
|
||||||
[{:keys [filter-id filter shape]}]
|
[{:keys [filter-id filter shape]}]
|
||||||
|
|
||||||
(let [{:keys [x y width height]} (:selrect shape)
|
(let [{:keys [x y width height]} (:selrect shape)
|
||||||
{:keys [id in-filter color opacity offset-x offset-y blur spread]} filter]
|
{:keys [id in-filter color offset-x offset-y blur spread]} filter]
|
||||||
[:*
|
[:*
|
||||||
[:feColorMatrix {:in "SourceAlpha" :type "matrix"
|
[:feColorMatrix {:in "SourceAlpha" :type "matrix"
|
||||||
:values "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"}]
|
:values "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"}]
|
||||||
|
@ -48,7 +49,7 @@
|
||||||
|
|
||||||
[:feOffset {:dx offset-x :dy offset-y}]
|
[:feOffset {:dx offset-x :dy offset-y}]
|
||||||
[:feGaussianBlur {:stdDeviation (/ blur 2)}]
|
[:feGaussianBlur {:stdDeviation (/ blur 2)}]
|
||||||
[:& color-matrix {:color color :opacity opacity}]
|
[:& color-matrix {:color color}]
|
||||||
|
|
||||||
[:feBlend {:mode "normal"
|
[:feBlend {:mode "normal"
|
||||||
:in2 in-filter
|
:in2 in-filter
|
||||||
|
@ -58,7 +59,7 @@
|
||||||
[{:keys [filter-id filter shape]}]
|
[{:keys [filter-id filter shape]}]
|
||||||
|
|
||||||
(let [{:keys [x y width height]} (:selrect shape)
|
(let [{:keys [x y width height]} (:selrect shape)
|
||||||
{:keys [id in-filter color opacity offset-x offset-y blur spread]} filter]
|
{:keys [id in-filter color offset-x offset-y blur spread]} filter]
|
||||||
[:*
|
[:*
|
||||||
[:feColorMatrix {:in "SourceAlpha" :type "matrix"
|
[:feColorMatrix {:in "SourceAlpha" :type "matrix"
|
||||||
:values "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
:values "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
||||||
|
@ -78,7 +79,7 @@
|
||||||
:k2 "-1"
|
:k2 "-1"
|
||||||
:k3 "1"}]
|
:k3 "1"}]
|
||||||
|
|
||||||
[:& color-matrix {:color color :opacity opacity}]
|
[:& color-matrix {:color color}]
|
||||||
|
|
||||||
[:feBlend {:mode "normal"
|
[:feBlend {:mode "normal"
|
||||||
:in2 in-filter
|
:in2 in-filter
|
||||||
|
@ -122,45 +123,42 @@
|
||||||
|
|
||||||
[filter-x filter-y filter-width filter-height] (get-filters-bounds shape filters)]
|
[filter-x filter-y filter-width filter-height] (get-filters-bounds shape filters)]
|
||||||
(when (seq filters)
|
(when (seq filters)
|
||||||
[:defs
|
[:filter {:id filter-id
|
||||||
[:filter {:id filter-id
|
:x filter-x :y filter-y
|
||||||
:x filter-x :y filter-y
|
:width filter-width :height filter-height
|
||||||
:width filter-width :height filter-height
|
:filterUnits "userSpaceOnUse"
|
||||||
:filterUnits "userSpaceOnUse"
|
:color-interpolation-filters "sRGB"}
|
||||||
:color-interpolation-filters "sRGB"}
|
|
||||||
|
|
||||||
(let [;; Add as a paramter the input filter
|
(let [;; Add as a paramter the input filter
|
||||||
drop-shadow-filters (->> filters (filter #(= :drop-shadow (:style %))))
|
drop-shadow-filters (->> filters (filter #(= :drop-shadow (:style %))))
|
||||||
drop-shadow-filters (->> drop-shadow-filters
|
drop-shadow-filters (->> drop-shadow-filters
|
||||||
(map #(str "filter" (:id %)))
|
(map #(str "filter" (:id %)))
|
||||||
(cons "BackgroundImageFix")
|
(cons "BackgroundImageFix")
|
||||||
(map add-in-filter drop-shadow-filters))
|
(map add-in-filter drop-shadow-filters))
|
||||||
|
|
||||||
inner-shadow-filters (->> filters (filter #(= :inner-shadow (:style %))))
|
inner-shadow-filters (->> filters (filter #(= :inner-shadow (:style %))))
|
||||||
inner-shadow-filters (->> inner-shadow-filters
|
inner-shadow-filters (->> inner-shadow-filters
|
||||||
(map #(str "filter" (:id %)))
|
(map #(str "filter" (:id %)))
|
||||||
(cons "shape")
|
(cons "shape")
|
||||||
(map add-in-filter inner-shadow-filters))]
|
(map add-in-filter inner-shadow-filters))]
|
||||||
|
|
||||||
[:*
|
[:*
|
||||||
[:feFlood {:flood-opacity 0 :result "BackgroundImageFix"}]
|
[:feFlood {:flood-opacity 0 :result "BackgroundImageFix"}]
|
||||||
(for [{:keys [id type] :as filter} drop-shadow-filters]
|
(for [{:keys [id type] :as filter} drop-shadow-filters]
|
||||||
[:& drop-shadow-filter {:key id
|
[:& drop-shadow-filter {:key id
|
||||||
|
:filter-id filter-id
|
||||||
|
:filter filter
|
||||||
|
:shape shape}])
|
||||||
|
|
||||||
|
[:feBlend {:mode "normal"
|
||||||
|
:in "SourceGraphic"
|
||||||
|
:in2 (if (seq drop-shadow-filters)
|
||||||
|
(str "filter" (:id (last drop-shadow-filters)))
|
||||||
|
"BackgroundImageFix")
|
||||||
|
:result "shape"}]
|
||||||
|
|
||||||
|
(for [{:keys [id type] :as filter} inner-shadow-filters]
|
||||||
|
[:& inner-shadow-filter {:key id
|
||||||
:filter-id filter-id
|
:filter-id filter-id
|
||||||
:filter filter
|
:filter filter
|
||||||
:shape shape}])
|
:shape shape}])])])))
|
||||||
|
|
||||||
[:feBlend {:mode "normal"
|
|
||||||
:in "SourceGraphic"
|
|
||||||
:in2 (if (seq drop-shadow-filters)
|
|
||||||
(str "filter" (:id (last drop-shadow-filters)))
|
|
||||||
"BackgroundImageFix")
|
|
||||||
:result "shape"}]
|
|
||||||
|
|
||||||
(for [{:keys [id type] :as filter} inner-shadow-filters]
|
|
||||||
[:& inner-shadow-filter {:key id
|
|
||||||
:filter-id filter-id
|
|
||||||
:filter filter
|
|
||||||
:shape shape}])
|
|
||||||
])
|
|
||||||
]])))
|
|
||||||
|
|
|
@ -11,11 +11,12 @@
|
||||||
(:require
|
(:require
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[goog.object :as gobj]
|
[app.util.object :as obj]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
|
[app.main.ui.context :as muc]
|
||||||
[app.common.geom.point :as gpt]))
|
[app.common.geom.point :as gpt]))
|
||||||
|
|
||||||
(mf/defc linear-gradient [{:keys [id shape gradient]}]
|
(mf/defc linear-gradient [{:keys [id gradient shape]}]
|
||||||
(let [{:keys [x y width height]} shape]
|
(let [{:keys [x y width height]} shape]
|
||||||
[:defs
|
[:defs
|
||||||
[:linearGradient {:id id
|
[:linearGradient {:id id
|
||||||
|
@ -29,12 +30,12 @@
|
||||||
:stop-color color
|
:stop-color color
|
||||||
:stop-opacity opacity}])]]))
|
:stop-opacity opacity}])]]))
|
||||||
|
|
||||||
(mf/defc radial-gradient [{:keys [id shape gradient]}]
|
(mf/defc radial-gradient [{:keys [id gradient shape]}]
|
||||||
(let [{:keys [x y width height]} shape]
|
(let [{:keys [x y width height]} shape]
|
||||||
[:defs
|
[:defs
|
||||||
(let [translate-vec (gpt/point (+ x (* width (:start-x gradient)))
|
(let [[x y] (if (= (:type shape) :frame) [0 0] [x y])
|
||||||
|
translate-vec (gpt/point (+ x (* width (:start-x gradient)))
|
||||||
(+ y (* height (:start-y gradient))))
|
(+ y (* height (:start-y gradient))))
|
||||||
|
|
||||||
|
|
||||||
gradient-vec (gpt/to-vec (gpt/point (* width (:start-x gradient))
|
gradient-vec (gpt/to-vec (gpt/point (* width (:start-x gradient))
|
||||||
(* height (:start-y gradient)))
|
(* height (:start-y gradient)))
|
||||||
|
@ -50,8 +51,8 @@
|
||||||
scale-factor-x (* scale-factor-y (:width gradient))
|
scale-factor-x (* scale-factor-y (:width gradient))
|
||||||
|
|
||||||
scale-vec (gpt/point (* scale-factor-y (/ height 2))
|
scale-vec (gpt/point (* scale-factor-y (/ height 2))
|
||||||
(* scale-factor-x (/ width 2))
|
(* scale-factor-x (/ width 2)))
|
||||||
)
|
|
||||||
tr-translate (str/fmt "translate(%s, %s)" (:x translate-vec) (:y translate-vec))
|
tr-translate (str/fmt "translate(%s, %s)" (:x translate-vec) (:y translate-vec))
|
||||||
tr-rotate (str/fmt "rotate(%s)" angle)
|
tr-rotate (str/fmt "rotate(%s)" angle)
|
||||||
tr-scale (str/fmt "scale(%s, %s)" (:x scale-vec) (:y scale-vec))
|
tr-scale (str/fmt "scale(%s, %s)" (:x scale-vec) (:y scale-vec))
|
||||||
|
@ -71,8 +72,15 @@
|
||||||
(mf/defc gradient
|
(mf/defc gradient
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
(let [gradient (gobj/get props "gradient")]
|
(let [attr (obj/get props "attr")
|
||||||
(case (:type gradient)
|
shape (obj/get props "shape")
|
||||||
:linear [:> linear-gradient props]
|
render-id (mf/use-ctx muc/render-ctx)
|
||||||
:radial [:> radial-gradient props]
|
id (str (name attr) "_" render-id)
|
||||||
nil)))
|
gradient (get shape attr)
|
||||||
|
gradient-props #js {:id id
|
||||||
|
:gradient gradient
|
||||||
|
:shape shape}]
|
||||||
|
(when gradient
|
||||||
|
(case (:type gradient)
|
||||||
|
:linear [:> linear-gradient gradient-props]
|
||||||
|
:radial [:> radial-gradient gradient-props]))))
|
||||||
|
|
|
@ -27,9 +27,7 @@
|
||||||
{:keys [id x y width height]} shape
|
{:keys [id x y width height]} shape
|
||||||
transform (geom/transform-matrix shape)
|
transform (geom/transform-matrix shape)
|
||||||
|
|
||||||
gradient-id (when (:fill-color-gradient shape) (str (uuid/next)))
|
props (-> (attrs/extract-style-attrs shape)
|
||||||
|
|
||||||
props (-> (attrs/extract-style-attrs shape gradient-id)
|
|
||||||
(obj/merge!
|
(obj/merge!
|
||||||
#js {:x x
|
#js {:x x
|
||||||
:y y
|
:y y
|
||||||
|
@ -38,12 +36,6 @@
|
||||||
:width width
|
:width width
|
||||||
:height height}))]
|
:height height}))]
|
||||||
|
|
||||||
|
[:& shape-custom-stroke {:shape shape
|
||||||
[:*
|
:base-props props
|
||||||
(when gradient-id
|
:elem-name "rect"}]))
|
||||||
[:& gradient {:id gradient-id
|
|
||||||
:shape shape
|
|
||||||
:gradient (:fill-color-gradient shape)}])
|
|
||||||
[:& shape-custom-stroke {:shape shape
|
|
||||||
:base-props props
|
|
||||||
:elem-name "rect"}]]))
|
|
||||||
|
|
|
@ -7,5 +7,33 @@
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2020 UXBOX Labs SL
|
;; Copyright (c) 2020 UXBOX Labs SL
|
||||||
|
|
||||||
(ns app.main.ui.shapes.shape)
|
(ns app.main.ui.shapes.shape
|
||||||
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.main.ui.shapes.filters :as filters]
|
||||||
|
[app.main.ui.shapes.gradients :as grad]
|
||||||
|
[app.main.ui.context :as muc]))
|
||||||
|
|
||||||
|
(mf/defc shape-container
|
||||||
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
|
||||||
|
(let [shape (unchecked-get props "shape")
|
||||||
|
children (unchecked-get props "children")
|
||||||
|
render-id (mf/use-memo #(str (uuid/next)))
|
||||||
|
filter-id (str "filter_" render-id)
|
||||||
|
group-props (-> props
|
||||||
|
(obj/clone)
|
||||||
|
(obj/without ["shape" "children"])
|
||||||
|
(obj/set! "className" "shape")
|
||||||
|
(obj/set! "filter" (filters/filter-str filter-id shape)))]
|
||||||
|
[:& (mf/provider muc/render-ctx) {:value render-id}
|
||||||
|
[:> :g group-props
|
||||||
|
[:defs
|
||||||
|
[:& filters/filters {:shape shape :filter-id filter-id}]
|
||||||
|
[:& grad/gradient {:shape shape :attr :fill-color-gradient}]
|
||||||
|
[:& grad/gradient {:shape shape :attr :stroke-color-gradient}]]
|
||||||
|
|
||||||
|
children]]))
|
||||||
|
|
|
@ -68,17 +68,25 @@
|
||||||
|
|
||||||
fill-color (obj/get data "fill-color" fill)
|
fill-color (obj/get data "fill-color" fill)
|
||||||
fill-opacity (obj/get data "fill-opacity" opacity)
|
fill-opacity (obj/get data "fill-opacity" opacity)
|
||||||
|
fill-color-gradient (obj/get data "fill-color-gradient" nil)
|
||||||
|
fill-color-gradient (when fill-color-gradient
|
||||||
|
(-> (js->clj fill-color-gradient :keywordize-keys true)
|
||||||
|
(update :type keyword)))
|
||||||
|
|
||||||
fill-color-ref-id (obj/get data "fill-color-ref-id")
|
fill-color-ref-id (obj/get data "fill-color-ref-id")
|
||||||
fill-color-ref-file (obj/get data "fill-color-ref-file")
|
fill-color-ref-file (obj/get data "fill-color-ref-file")
|
||||||
|
|
||||||
[r g b a] (uc/hex->rgba fill-color fill-opacity)
|
[r g b a] (uc/hex->rgba fill-color fill-opacity)
|
||||||
|
background (if fill-color-gradient
|
||||||
|
(uc/gradient->css (js->clj fill-color-gradient))
|
||||||
|
(str/format "rgba(%s, %s, %s, %s)" r g b a))
|
||||||
|
|
||||||
fontsdb (deref fonts/fontsdb)
|
fontsdb (deref fonts/fontsdb)
|
||||||
|
|
||||||
base #js {:textDecoration text-decoration
|
base #js {:textDecoration text-decoration
|
||||||
:color (str/format "rgba(%s, %s, %s, %s)" r g b a)
|
|
||||||
:textTransform text-transform
|
:textTransform text-transform
|
||||||
:lineHeight (or line-height "inherit")}]
|
:lineHeight (or line-height "inherit")
|
||||||
|
"--text-color" background}]
|
||||||
|
|
||||||
(when (and (string? letter-spacing)
|
(when (and (string? letter-spacing)
|
||||||
(pos? (alength letter-spacing)))
|
(pos? (alength letter-spacing)))
|
||||||
|
@ -167,7 +175,7 @@
|
||||||
|
|
||||||
(if (string? text)
|
(if (string? text)
|
||||||
(let [style (generate-text-styles (clj->js node))]
|
(let [style (generate-text-styles (clj->js node))]
|
||||||
[:span {:style style :key index} (if (= text "") "\u00A0" text)])
|
[:span.text-node {:style style} (if (= text "") "\u00A0" text)])
|
||||||
(let [children (map-indexed (fn [index node]
|
(let [children (map-indexed (fn [index node]
|
||||||
(mf/element text-node {:index index :node node :key index}))
|
(mf/element text-node {:index index :node node :key index}))
|
||||||
children)]
|
children)]
|
||||||
|
@ -179,13 +187,15 @@
|
||||||
{:key index
|
{:key index
|
||||||
:style style
|
:style style
|
||||||
:xmlns "http://www.w3.org/1999/xhtml"}
|
:xmlns "http://www.w3.org/1999/xhtml"}
|
||||||
(when (not (nil? @embeded-fonts))
|
[:*
|
||||||
[:style @embeded-fonts])
|
[:style ".text-node { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"]
|
||||||
|
(when (not (nil? @embeded-fonts))
|
||||||
|
[:style @embeded-fonts])]
|
||||||
children])
|
children])
|
||||||
|
|
||||||
"paragraph-set"
|
"paragraph-set"
|
||||||
(let [style #js {:display "inline-block"}]
|
(let [style #js {:display "inline-block"}]
|
||||||
[:div.paragraphs {:key index :style style} children])
|
[:div.paragraphs {:key index :style style} children])
|
||||||
|
|
||||||
"paragraph"
|
"paragraph"
|
||||||
(let [style (generate-paragraph-styles (clj->js node))]
|
(let [style (generate-paragraph-styles (clj->js node))]
|
||||||
|
|
|
@ -29,7 +29,8 @@
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as geom]))
|
[app.common.geom.shapes :as geom]
|
||||||
|
[app.main.ui.shapes.shape :refer [shape-container]]))
|
||||||
|
|
||||||
(defn on-mouse-down
|
(defn on-mouse-down
|
||||||
[event {:keys [interactions] :as shape}]
|
[event {:keys [interactions] :as shape}]
|
||||||
|
@ -54,14 +55,11 @@
|
||||||
|
|
||||||
on-mouse-down (mf/use-callback
|
on-mouse-down (mf/use-callback
|
||||||
(mf/deps shape)
|
(mf/deps shape)
|
||||||
#(on-mouse-down % shape))
|
#(on-mouse-down % shape))]
|
||||||
|
|
||||||
filter-id (filters/get-filter-id)]
|
[:> shape-container {:shape shape
|
||||||
|
:on-mouse-down on-mouse-down
|
||||||
[:g.shape {:on-mouse-down on-mouse-down
|
:cursor (when (:interactions shape) "pointer")}
|
||||||
:cursor (when (:interactions shape) "pointer")
|
|
||||||
:filter (filters/filter-str filter-id shape)}
|
|
||||||
[:& filters/filters {:filter-id filter-id :shape shape}]
|
|
||||||
[:& component {:shape shape
|
[:& component {:shape shape
|
||||||
:frame frame
|
:frame frame
|
||||||
:childs childs
|
:childs childs
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
[app.main.data.workspace :as udw]
|
[app.main.data.workspace :as udw]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||||
|
[app.main.ui.components.color-bullet :as cb]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.main.ui.keyboard :as kbd]
|
[app.main.ui.keyboard :as kbd]
|
||||||
[app.util.color :refer [hex->rgb]]
|
[app.util.color :refer [hex->rgb]]
|
||||||
|
@ -55,14 +56,13 @@
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(let [ids (get-in @st/state [:workspace-local :selected])]
|
(let [ids (get-in @st/state [:workspace-local :selected])]
|
||||||
(if (kbd/shift? event)
|
(if (kbd/shift? event)
|
||||||
(st/emit! (mdc/change-stroke ids (:value color) id file-id))
|
(st/emit! (mdc/change-stroke ids color))
|
||||||
(st/emit! (mdc/change-fill ids (:value color) id file-id)))))]
|
(st/emit! (mdc/change-fill ids color)))))]
|
||||||
|
|
||||||
[:div.color-cell {:class (str "cell-"(name size))
|
[:div.color-cell {:class (str "cell-"(name size))
|
||||||
:key (or (str (:id color)) (:value color))
|
|
||||||
:on-click select-color}
|
:on-click select-color}
|
||||||
[:span.color {:style {:background (:value color)}}]
|
[:& cb/color-bullet {:color color}]
|
||||||
(when (= size :big) [:span.color-text {:title (:name color) } (or (:name color) (:value color))])]))
|
[:& cb/color-name {:color color :size size}]]))
|
||||||
|
|
||||||
(mf/defc palette
|
(mf/defc palette
|
||||||
[{:keys [left-sidebar? current-colors recent-colors file-colors shared-libs selected size]}]
|
[{:keys [left-sidebar? current-colors recent-colors file-colors shared-libs selected size]}]
|
||||||
|
@ -138,9 +138,9 @@
|
||||||
(when (= selected (:id cur-library)) i/tick)
|
(when (= selected (:id cur-library)) i/tick)
|
||||||
[:div.library-name (str (:name cur-library) " " (str/format "(%s)" (count colors)))]
|
[:div.library-name (str (:name cur-library) " " (str/format "(%s)" (count colors)))]
|
||||||
[:div.color-sample
|
[:div.color-sample
|
||||||
(for [[idx {:keys [id value]}] (map-indexed vector (take 7 colors))]
|
(for [[idx {:keys [id color]}] (map-indexed vector (take 7 colors))]
|
||||||
[:div.color-bullet {:key (str "color-" idx)
|
[:& cb/color-bullet {:key (str "color-" idx)
|
||||||
:style {:background-color value}}])]]))
|
:color color}])]]))
|
||||||
|
|
||||||
|
|
||||||
[:li.palette-library
|
[:li.palette-library
|
||||||
|
@ -149,9 +149,9 @@
|
||||||
[:div.library-name (str (t locale "workspace.libraries.colors.file-library")
|
[:div.library-name (str (t locale "workspace.libraries.colors.file-library")
|
||||||
(str/format " (%s)" (count file-colors)))]
|
(str/format " (%s)" (count file-colors)))]
|
||||||
[:div.color-sample
|
[:div.color-sample
|
||||||
(for [[idx {:keys [value]}] (map-indexed vector (take 7 (vals file-colors))) ]
|
(for [[idx color] (map-indexed vector (take 7 (vals file-colors))) ]
|
||||||
[:div.color-bullet {:key (str "color-" idx)
|
[:& cb/color-bullet {:key (str "color-" idx)
|
||||||
:style {:background-color value}}])]]
|
:color color}])]]
|
||||||
|
|
||||||
[:li.palette-library
|
[:li.palette-library
|
||||||
{:on-click #(st/emit! (mdc/change-palette-selected :recent))}
|
{:on-click #(st/emit! (mdc/change-palette-selected :recent))}
|
||||||
|
@ -159,9 +159,9 @@
|
||||||
[:div.library-name (str (t locale "workspace.libraries.colors.recent-colors")
|
[:div.library-name (str (t locale "workspace.libraries.colors.recent-colors")
|
||||||
(str/format " (%s)" (count recent-colors)))]
|
(str/format " (%s)" (count recent-colors)))]
|
||||||
[:div.color-sample
|
[:div.color-sample
|
||||||
(for [[idx value] (map-indexed vector (take 7 (reverse recent-colors))) ]
|
(for [[idx color] (map-indexed vector (take 7 (reverse recent-colors))) ]
|
||||||
[:div.color-bullet {:key (str "color-" idx)
|
[:& cb/color-bullet {:key (str "color-" idx)
|
||||||
:style {:background-color value}}])]]
|
:color color}])]]
|
||||||
|
|
||||||
[:hr.dropdown-separator]
|
[:hr.dropdown-separator]
|
||||||
|
|
||||||
|
@ -191,14 +191,8 @@
|
||||||
|
|
||||||
[:span.right-arrow {:on-click on-right-arrow-click} i/arrow-slide]]))
|
[:span.right-arrow {:on-click on-right-arrow-click} i/arrow-slide]]))
|
||||||
|
|
||||||
(defn recent->colors [recent-colors]
|
|
||||||
(map #(hash-map :value %) (reverse (or recent-colors []))))
|
|
||||||
|
|
||||||
(defn file->colors [file-colors]
|
|
||||||
(map #(select-keys % [:id :value :name]) (vals file-colors)))
|
|
||||||
|
|
||||||
(defn library->colors [shared-libs selected]
|
(defn library->colors [shared-libs selected]
|
||||||
(map #(merge {:file-id selected} (select-keys % [:id :value :name]))
|
(map #(merge {:file-id selected} %)
|
||||||
(vals (get-in shared-libs [selected :data :colors]))))
|
(vals (get-in shared-libs [selected :data :colors]))))
|
||||||
|
|
||||||
(mf/defc colorpalette
|
(mf/defc colorpalette
|
||||||
|
@ -217,21 +211,21 @@
|
||||||
(reset! current-library-colors
|
(reset! current-library-colors
|
||||||
(into []
|
(into []
|
||||||
(cond
|
(cond
|
||||||
(= selected :recent) (recent->colors recent-colors)
|
(= selected :recent) (reverse recent-colors)
|
||||||
(= selected :file) (file->colors file-colors)
|
(= selected :file) (vals file-colors)
|
||||||
:else (library->colors shared-libs selected))))))
|
:else (library->colors shared-libs selected))))))
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(mf/deps recent-colors)
|
(mf/deps recent-colors)
|
||||||
(fn []
|
(fn []
|
||||||
(when (= selected :recent)
|
(when (= selected :recent)
|
||||||
(reset! current-library-colors (into [] (recent->colors recent-colors))))))
|
(reset! current-library-colors (reverse recent-colors)))))
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(mf/deps file-colors)
|
(mf/deps file-colors)
|
||||||
(fn []
|
(fn []
|
||||||
(when (= selected :file)
|
(when (= selected :file)
|
||||||
(reset! current-library-colors (into [] (file->colors file-colors))))))
|
(reset! current-library-colors (into [] (vals file-colors))))))
|
||||||
|
|
||||||
[:& palette {:left-sidebar? left-sidebar?
|
[:& palette {:left-sidebar? left-sidebar?
|
||||||
:current-colors @current-library-colors
|
:current-colors @current-library-colors
|
||||||
|
|
|
@ -21,10 +21,16 @@
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.data.workspace.libraries :as dwl]
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
[app.main.data.colors :as dwc]
|
[app.main.data.colors :as dc]
|
||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.util.i18n :as i18n :refer [t]]))
|
[app.util.i18n :as i18n :refer [t]]
|
||||||
|
[app.main.ui.workspace.colorpicker.gradients :refer [gradients]]
|
||||||
|
[app.main.ui.workspace.colorpicker.harmony :refer [harmony-selector]]
|
||||||
|
[app.main.ui.workspace.colorpicker.hsva :refer [hsva-selector]]
|
||||||
|
[app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector]]
|
||||||
|
[app.main.ui.workspace.colorpicker.color-inputs :refer [color-inputs]]
|
||||||
|
[app.main.ui.workspace.colorpicker.libraries :refer [libraries]]))
|
||||||
|
|
||||||
;; --- Refs
|
;; --- Refs
|
||||||
|
|
||||||
|
@ -43,414 +49,15 @@
|
||||||
(def viewport
|
(def viewport
|
||||||
(l/derived (l/in [:workspace-local :vport]) st/state))
|
(l/derived (l/in [:workspace-local :vport]) st/state))
|
||||||
|
|
||||||
|
(def editing-spot-state-ref
|
||||||
|
(l/derived (l/in [:workspace-local :editing-stop]) st/state))
|
||||||
|
|
||||||
|
(def current-gradient-ref
|
||||||
|
(l/derived (l/in [:workspace-local :current-gradient]) st/state))
|
||||||
|
|
||||||
;; --- Color Picker Modal
|
;; --- Color Picker Modal
|
||||||
|
|
||||||
(mf/defc value-saturation-selector [{:keys [hue saturation value on-change]}]
|
(defn color->components [value opacity]
|
||||||
(let [dragging? (mf/use-state false)
|
|
||||||
calculate-pos
|
|
||||||
(fn [ev]
|
|
||||||
(let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect)
|
|
||||||
{:keys [x y]} (-> ev dom/get-client-position)
|
|
||||||
px (math/clamp (/ (- x left) (- right left)) 0 1)
|
|
||||||
py (* 255 (- 1 (math/clamp (/ (- y top) (- bottom top)) 0 1)))]
|
|
||||||
(on-change px py)))]
|
|
||||||
[:div.value-saturation-selector
|
|
||||||
{:on-mouse-down #(reset! dragging? true)
|
|
||||||
:on-mouse-up #(reset! dragging? false)
|
|
||||||
:on-pointer-down (partial dom/capture-pointer)
|
|
||||||
:on-pointer-up (partial dom/release-pointer)
|
|
||||||
:on-click calculate-pos
|
|
||||||
:on-mouse-move #(when @dragging? (calculate-pos %))}
|
|
||||||
[:div.handler {:style {:pointer-events "none"
|
|
||||||
:left (str (* 100 saturation) "%")
|
|
||||||
:top (str (* 100 (- 1 (/ value 255))) "%")}}]]))
|
|
||||||
|
|
||||||
|
|
||||||
(mf/defc slider-selector [{:keys [value class min-value max-value vertical? reverse? on-change]}]
|
|
||||||
(let [min-value (or min-value 0)
|
|
||||||
max-value (or max-value 1)
|
|
||||||
dragging? (mf/use-state false)
|
|
||||||
calculate-pos
|
|
||||||
(fn [ev]
|
|
||||||
(when on-change
|
|
||||||
(let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect)
|
|
||||||
{:keys [x y]} (-> ev dom/get-client-position)
|
|
||||||
unit-value (if vertical?
|
|
||||||
(math/clamp (/ (- bottom y) (- bottom top)) 0 1)
|
|
||||||
(math/clamp (/ (- x left) (- right left)) 0 1))
|
|
||||||
unit-value (if reverse?
|
|
||||||
(math/abs (- unit-value 1.0))
|
|
||||||
unit-value)
|
|
||||||
value (+ min-value (* unit-value (- max-value min-value)))]
|
|
||||||
(on-change value))))]
|
|
||||||
|
|
||||||
[:div.slider-selector
|
|
||||||
{:class (str (if vertical? "vertical " "") class)
|
|
||||||
:on-mouse-down #(reset! dragging? true)
|
|
||||||
:on-mouse-up #(reset! dragging? false)
|
|
||||||
:on-pointer-down (partial dom/capture-pointer)
|
|
||||||
:on-pointer-up (partial dom/release-pointer)
|
|
||||||
:on-click calculate-pos
|
|
||||||
:on-mouse-move #(when @dragging? (calculate-pos %))}
|
|
||||||
|
|
||||||
(let [value-percent (* (/ (- value min-value)
|
|
||||||
(- max-value min-value)) 100)
|
|
||||||
|
|
||||||
value-percent (if reverse?
|
|
||||||
(math/abs (- value-percent 100))
|
|
||||||
value-percent)
|
|
||||||
value-percent-str (str value-percent "%")
|
|
||||||
|
|
||||||
style-common #js {:pointerEvents "none"}
|
|
||||||
style-horizontal (obj/merge! #js {:left value-percent-str} style-common)
|
|
||||||
style-vertical (obj/merge! #js {:bottom value-percent-str} style-common)]
|
|
||||||
[:div.handler {:style (if vertical? style-vertical style-horizontal)}])]))
|
|
||||||
|
|
||||||
|
|
||||||
(defn create-color-wheel
|
|
||||||
[canvas-node]
|
|
||||||
(let [ctx (.getContext canvas-node "2d")
|
|
||||||
width (obj/get canvas-node "width")
|
|
||||||
height (obj/get canvas-node "height")
|
|
||||||
radius (/ width 2)
|
|
||||||
cx (/ width 2)
|
|
||||||
cy (/ width 2)
|
|
||||||
step 0.2]
|
|
||||||
|
|
||||||
(.clearRect ctx 0 0 width height)
|
|
||||||
|
|
||||||
(doseq [degrees (range 0 360 step)]
|
|
||||||
(let [degrees-rad (math/radians degrees)
|
|
||||||
x (* radius (math/cos (- degrees-rad)))
|
|
||||||
y (* radius (math/sin (- degrees-rad)))]
|
|
||||||
(obj/set! ctx "strokeStyle" (str/format "hsl(%s, 100%, 50%)" degrees))
|
|
||||||
(.beginPath ctx)
|
|
||||||
(.moveTo ctx cx cy)
|
|
||||||
(.lineTo ctx (+ cx x) (+ cy y))
|
|
||||||
(.stroke ctx)))
|
|
||||||
|
|
||||||
(let [grd (.createRadialGradient ctx cx cy 0 cx cx radius)]
|
|
||||||
(.addColorStop grd 0 "white")
|
|
||||||
(.addColorStop grd 1 "rgba(255, 255, 255, 0")
|
|
||||||
(obj/set! ctx "fillStyle" grd)
|
|
||||||
|
|
||||||
(.beginPath ctx)
|
|
||||||
(.arc ctx cx cy radius 0 (* 2 math/PI) true)
|
|
||||||
(.closePath ctx)
|
|
||||||
(.fill ctx))))
|
|
||||||
|
|
||||||
(mf/defc ramp-selector [{:keys [color on-change]}]
|
|
||||||
(let [{hue :h saturation :s value :v alpha :alpha} color
|
|
||||||
|
|
||||||
on-change-value-saturation
|
|
||||||
(fn [new-saturation new-value]
|
|
||||||
(let [hex (uc/hsv->hex [hue new-saturation new-value])
|
|
||||||
[r g b] (uc/hex->rgb hex)]
|
|
||||||
(on-change {:hex hex
|
|
||||||
:r r :g g :b b
|
|
||||||
:s new-saturation
|
|
||||||
:v new-value})))
|
|
||||||
|
|
||||||
on-change-hue
|
|
||||||
(fn [new-hue]
|
|
||||||
(let [hex (uc/hsv->hex [new-hue saturation value])
|
|
||||||
[r g b] (uc/hex->rgb hex)]
|
|
||||||
(on-change {:hex hex
|
|
||||||
:r r :g g :b b
|
|
||||||
:h new-hue} )))
|
|
||||||
|
|
||||||
on-change-opacity
|
|
||||||
(fn [new-opacity]
|
|
||||||
(on-change {:alpha new-opacity} ))]
|
|
||||||
[:*
|
|
||||||
[:& value-saturation-selector
|
|
||||||
{:hue hue
|
|
||||||
:saturation saturation
|
|
||||||
:value value
|
|
||||||
:on-change on-change-value-saturation}]
|
|
||||||
|
|
||||||
[:div.shade-selector
|
|
||||||
[:div.color-bullet]
|
|
||||||
[:& slider-selector {:class "hue"
|
|
||||||
:max-value 360
|
|
||||||
:value hue
|
|
||||||
:on-change on-change-hue}]
|
|
||||||
|
|
||||||
[:& slider-selector {:class "opacity"
|
|
||||||
:max-value 1
|
|
||||||
:value alpha
|
|
||||||
:on-change on-change-opacity}]]]))
|
|
||||||
|
|
||||||
(defn color->point
|
|
||||||
[canvas-side hue saturation]
|
|
||||||
(let [hue-rad (math/radians (- hue))
|
|
||||||
comp-x (* saturation (math/cos hue-rad))
|
|
||||||
comp-y (* saturation (math/sin hue-rad))
|
|
||||||
x (+ (/ canvas-side 2) (* comp-x (/ canvas-side 2)))
|
|
||||||
y (+ (/ canvas-side 2) (* comp-y (/ canvas-side 2)))]
|
|
||||||
(gpt/point x y)))
|
|
||||||
|
|
||||||
(mf/defc harmony-selector [{:keys [color on-change]}]
|
|
||||||
(let [canvas-ref (mf/use-ref nil)
|
|
||||||
{hue :h saturation :s value :v alpha :alpha} color
|
|
||||||
|
|
||||||
canvas-side 152
|
|
||||||
pos-current (color->point canvas-side hue saturation)
|
|
||||||
pos-complement (color->point canvas-side (mod (+ hue 180) 360) saturation)
|
|
||||||
dragging? (mf/use-state false)
|
|
||||||
|
|
||||||
calculate-pos (fn [ev]
|
|
||||||
(let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect)
|
|
||||||
{:keys [x y]} (-> ev dom/get-client-position)
|
|
||||||
px (math/clamp (/ (- x left) (- right left)) 0 1)
|
|
||||||
py (math/clamp (/ (- y top) (- bottom top)) 0 1)
|
|
||||||
|
|
||||||
px (- (* 2 px) 1)
|
|
||||||
py (- (* 2 py) 1)
|
|
||||||
|
|
||||||
angle (math/degrees (math/atan2 px py))
|
|
||||||
new-hue (math/precision (mod (- angle 90 ) 360) 2)
|
|
||||||
new-saturation (math/clamp (math/distance [px py] [0 0]) 0 1)
|
|
||||||
hex (uc/hsv->hex [new-hue new-saturation value])
|
|
||||||
[r g b] (uc/hex->rgb hex)]
|
|
||||||
(on-change {:hex hex
|
|
||||||
:r r :g g :b b
|
|
||||||
:h new-hue
|
|
||||||
:s new-saturation})))
|
|
||||||
|
|
||||||
on-change-value (fn [new-value]
|
|
||||||
(let [hex (uc/hsv->hex [hue saturation new-value])
|
|
||||||
[r g b] (uc/hex->rgb hex)]
|
|
||||||
(on-change {:hex hex
|
|
||||||
:r r :g g :b b
|
|
||||||
:v new-value})))
|
|
||||||
on-complement-click (fn [ev]
|
|
||||||
(let [new-hue (mod (+ hue 180) 360)
|
|
||||||
hex (uc/hsv->hex [new-hue saturation value])
|
|
||||||
[r g b] (uc/hex->rgb hex)]
|
|
||||||
(on-change {:hex hex
|
|
||||||
:r r :g g :b b
|
|
||||||
:h new-hue
|
|
||||||
:s saturation})))
|
|
||||||
|
|
||||||
on-change-opacity (fn [new-alpha] (on-change {:alpha new-alpha}))]
|
|
||||||
|
|
||||||
(mf/use-effect
|
|
||||||
(mf/deps canvas-ref)
|
|
||||||
(fn [] (when canvas-ref
|
|
||||||
(create-color-wheel (mf/ref-val canvas-ref)))))
|
|
||||||
|
|
||||||
[:div.harmony-selector
|
|
||||||
[:div.hue-wheel-wrapper
|
|
||||||
[:canvas.hue-wheel
|
|
||||||
{:ref canvas-ref
|
|
||||||
:width canvas-side
|
|
||||||
:height canvas-side
|
|
||||||
:on-mouse-down #(reset! dragging? true)
|
|
||||||
:on-mouse-up #(reset! dragging? false)
|
|
||||||
:on-pointer-down (partial dom/capture-pointer)
|
|
||||||
:on-pointer-up (partial dom/release-pointer)
|
|
||||||
:on-click calculate-pos
|
|
||||||
:on-mouse-move #(when @dragging? (calculate-pos %))}]
|
|
||||||
[:div.handler {:style {:pointer-events "none"
|
|
||||||
:left (:x pos-current)
|
|
||||||
:top (:y pos-current)}}]
|
|
||||||
[:div.handler.complement {:style {:left (:x pos-complement)
|
|
||||||
:top (:y pos-complement)
|
|
||||||
:cursor "pointer"}
|
|
||||||
:on-click on-complement-click}]]
|
|
||||||
[:div.handlers-wrapper
|
|
||||||
[:& slider-selector {:class "value"
|
|
||||||
:vertical? true
|
|
||||||
:reverse? true
|
|
||||||
:value value
|
|
||||||
:max-value 255
|
|
||||||
:vertical true
|
|
||||||
:on-change on-change-value}]
|
|
||||||
[:& slider-selector {:class "opacity"
|
|
||||||
:vertical? true
|
|
||||||
:value alpha
|
|
||||||
:max-value 1
|
|
||||||
:vertical true
|
|
||||||
:on-change on-change-opacity}]]]))
|
|
||||||
|
|
||||||
(mf/defc hsva-selector [{:keys [color on-change]}]
|
|
||||||
(let [{hue :h saturation :s value :v alpha :alpha} color
|
|
||||||
handle-change-slider (fn [key]
|
|
||||||
(fn [new-value]
|
|
||||||
(let [change (hash-map key new-value)
|
|
||||||
{:keys [h s v]} (merge color change)
|
|
||||||
hex (uc/hsv->hex [h s v])
|
|
||||||
[r g b] (uc/hex->rgb hex)]
|
|
||||||
(on-change (merge change
|
|
||||||
{:hex hex
|
|
||||||
:r r :g g :b b})))))
|
|
||||||
on-change-opacity (fn [new-alpha] (on-change {:alpha new-alpha}))]
|
|
||||||
[:div.hsva-selector
|
|
||||||
[:span.hsva-selector-label "H"]
|
|
||||||
[:& slider-selector
|
|
||||||
{:class "hue" :max-value 360 :value hue :on-change (handle-change-slider :h)}]
|
|
||||||
|
|
||||||
[:span.hsva-selector-label "S"]
|
|
||||||
[:& slider-selector
|
|
||||||
{:class "saturation" :max-value 1 :value saturation :on-change (handle-change-slider :s)}]
|
|
||||||
|
|
||||||
[:span.hsva-selector-label "V"]
|
|
||||||
[:& slider-selector
|
|
||||||
{:class "value" :reverse? true :max-value 255 :value value :on-change (handle-change-slider :v)}]
|
|
||||||
|
|
||||||
[:span.hsva-selector-label "A"]
|
|
||||||
[:& slider-selector
|
|
||||||
{:class "opacity" :max-value 1 :value alpha :on-change on-change-opacity}]]))
|
|
||||||
|
|
||||||
(mf/defc color-inputs [{:keys [type color on-change]}]
|
|
||||||
(let [{red :r green :g blue :b
|
|
||||||
hue :h saturation :s value :v
|
|
||||||
hex :hex alpha :alpha} color
|
|
||||||
|
|
||||||
parse-hex (fn [val] (if (= (first val) \#) val (str \# val)))
|
|
||||||
|
|
||||||
refs {:hex (mf/use-ref nil)
|
|
||||||
:r (mf/use-ref nil)
|
|
||||||
:g (mf/use-ref nil)
|
|
||||||
:b (mf/use-ref nil)
|
|
||||||
:h (mf/use-ref nil)
|
|
||||||
:s (mf/use-ref nil)
|
|
||||||
:v (mf/use-ref nil)
|
|
||||||
:alpha (mf/use-ref nil)}
|
|
||||||
|
|
||||||
on-change-hex
|
|
||||||
(fn [e]
|
|
||||||
(let [val (-> e dom/get-target-val parse-hex)]
|
|
||||||
(when (uc/hex? val)
|
|
||||||
(let [[r g b] (uc/hex->rgb val)
|
|
||||||
[h s v] (uc/hex->hsv hex)]
|
|
||||||
(on-change {:hex val
|
|
||||||
:h h :s s :v v
|
|
||||||
:r r :g g :b b})))))
|
|
||||||
|
|
||||||
on-change-property
|
|
||||||
(fn [property max-value]
|
|
||||||
(fn [e]
|
|
||||||
(let [val (-> e dom/get-target-val (math/clamp 0 max-value))
|
|
||||||
val (if (#{:s} property) (/ val 100) val)]
|
|
||||||
(when (not (nil? val))
|
|
||||||
(if (#{:r :g :b} property)
|
|
||||||
(let [{:keys [r g b]} (merge color (hash-map property val))
|
|
||||||
hex (uc/rgb->hex [r g b])
|
|
||||||
[h s v] (uc/hex->hsv hex)]
|
|
||||||
(on-change {:hex hex
|
|
||||||
:h h :s s :v v
|
|
||||||
:r r :g g :b b}))
|
|
||||||
|
|
||||||
(let [{:keys [h s v]} (merge color (hash-map property val))
|
|
||||||
hex (uc/hsv->hex [h s v])
|
|
||||||
[r g b] (uc/hex->rgb hex)]
|
|
||||||
(on-change {:hex hex
|
|
||||||
:h h :s s :v v
|
|
||||||
:r r :g g :b b})))))))
|
|
||||||
|
|
||||||
on-change-opacity
|
|
||||||
(fn [e]
|
|
||||||
(when-let [new-alpha (-> e dom/get-target-val (math/clamp 0 100) (/ 100))]
|
|
||||||
(on-change {:alpha new-alpha})))]
|
|
||||||
|
|
||||||
|
|
||||||
;; Updates the inputs values when a property is changed in the parent
|
|
||||||
(mf/use-effect
|
|
||||||
(mf/deps color type)
|
|
||||||
(fn []
|
|
||||||
(doseq [ref-key (keys refs)]
|
|
||||||
(let [property-val (get color ref-key)
|
|
||||||
property-ref (get refs ref-key)]
|
|
||||||
(when (and property-val property-ref)
|
|
||||||
(when-let [node (mf/ref-val property-ref)]
|
|
||||||
(case ref-key
|
|
||||||
(:s :alpha) (dom/set-value! node (math/round (* property-val 100)))
|
|
||||||
:hex (dom/set-value! node property-val)
|
|
||||||
(dom/set-value! node (math/round property-val)))))))))
|
|
||||||
|
|
||||||
[:div.color-values
|
|
||||||
[:input {:id "hex-value"
|
|
||||||
:ref (:hex refs)
|
|
||||||
:default-value hex
|
|
||||||
:on-change on-change-hex}]
|
|
||||||
|
|
||||||
(if (= type :rgb)
|
|
||||||
[:*
|
|
||||||
[:input {:id "red-value"
|
|
||||||
:ref (:r refs)
|
|
||||||
:type "number"
|
|
||||||
:min 0
|
|
||||||
:max 255
|
|
||||||
:default-value red
|
|
||||||
:on-change (on-change-property :r 255)}]
|
|
||||||
|
|
||||||
[:input {:id "green-value"
|
|
||||||
:ref (:g refs)
|
|
||||||
:type "number"
|
|
||||||
:min 0
|
|
||||||
:max 255
|
|
||||||
:default-value green
|
|
||||||
:on-change (on-change-property :g 255)}]
|
|
||||||
|
|
||||||
[:input {:id "blue-value"
|
|
||||||
:ref (:b refs)
|
|
||||||
:type "number"
|
|
||||||
:min 0
|
|
||||||
:max 255
|
|
||||||
:default-value blue
|
|
||||||
:on-change (on-change-property :b 255)}]]
|
|
||||||
[:*
|
|
||||||
[:input {:id "hue-value"
|
|
||||||
:ref (:h refs)
|
|
||||||
:type "number"
|
|
||||||
:min 0
|
|
||||||
:max 360
|
|
||||||
:default-value hue
|
|
||||||
:on-change (on-change-property :h 360)}]
|
|
||||||
|
|
||||||
[:input {:id "saturation-value"
|
|
||||||
:ref (:s refs)
|
|
||||||
:type "number"
|
|
||||||
:min 0
|
|
||||||
:max 100
|
|
||||||
:step 1
|
|
||||||
:default-value saturation
|
|
||||||
:on-change (on-change-property :s 100)}]
|
|
||||||
|
|
||||||
[:input {:id "value-value"
|
|
||||||
:ref (:v refs)
|
|
||||||
:type "number"
|
|
||||||
:min 0
|
|
||||||
:max 255
|
|
||||||
:default-value value
|
|
||||||
:on-change (on-change-property :v 255)}]])
|
|
||||||
|
|
||||||
[:input.alpha-value {:id "alpha-value"
|
|
||||||
:ref (:alpha refs)
|
|
||||||
:type "number"
|
|
||||||
:min 0
|
|
||||||
:step 1
|
|
||||||
:max 100
|
|
||||||
:default-value (if (= alpha :multiple) "" (math/precision alpha 2))
|
|
||||||
:on-change on-change-opacity}]
|
|
||||||
|
|
||||||
[:label.hex-label {:for "hex-value"} "HEX"]
|
|
||||||
(if (= type :rgb)
|
|
||||||
[:*
|
|
||||||
[:label.red-label {:for "red-value"} "R"]
|
|
||||||
[:label.green-label {:for "green-value"} "G"]
|
|
||||||
[:label.blue-label {:for "blue-value"} "B"]]
|
|
||||||
[:*
|
|
||||||
[:label.red-label {:for "hue-value"} "H"]
|
|
||||||
[:label.green-label {:for "saturation-value"} "S"]
|
|
||||||
[:label.blue-label {:for "value-value"} "V"]])
|
|
||||||
[:label.alpha-label {:for "alpha-value"} "A"]]))
|
|
||||||
|
|
||||||
|
|
||||||
(defn as-color-components [value opacity]
|
|
||||||
(let [value (if (uc/hex? value) value "#000000")
|
(let [value (if (uc/hex? value) value "#000000")
|
||||||
[r g b] (uc/hex->rgb value)
|
[r g b] (uc/hex->rgb value)
|
||||||
[h s v] (uc/hex->hsv value)]
|
[h s v] (uc/hex->hsv value)]
|
||||||
|
@ -460,58 +67,139 @@
|
||||||
:r r :g g :b b
|
:r r :g g :b b
|
||||||
:h h :s s :v v}))
|
:h h :s s :v v}))
|
||||||
|
|
||||||
(mf/defc colorpicker
|
(defn data->state [{:keys [color opacity gradient]}]
|
||||||
[{:keys [value opacity on-change on-accept]}]
|
(let [type (cond
|
||||||
(let [current-color (mf/use-state (as-color-components value opacity))
|
(nil? gradient) :color
|
||||||
|
(= :linear (:type gradient)) :linear-gradient
|
||||||
|
(= :radial (:type gradient)) :radial-gradient)
|
||||||
|
|
||||||
|
parse-stop (fn [{:keys [offset color opacity]}]
|
||||||
|
(vector offset (color->components color opacity)))
|
||||||
|
|
||||||
|
stops (when gradient
|
||||||
|
(map parse-stop (:stops gradient)))
|
||||||
|
|
||||||
|
current-color (if (nil? gradient)
|
||||||
|
(color->components color opacity)
|
||||||
|
(-> stops first second))
|
||||||
|
|
||||||
|
gradient-data (select-keys gradient [:start-x :start-y
|
||||||
|
:end-x :end-y
|
||||||
|
:width])]
|
||||||
|
|
||||||
|
(cond-> {:type type
|
||||||
|
:current-color current-color}
|
||||||
|
gradient (assoc :gradient-data gradient-data)
|
||||||
|
stops (assoc :stops (into {} stops))
|
||||||
|
stops (assoc :editing-stop (-> stops first first)))))
|
||||||
|
|
||||||
|
(defn state->data [{:keys [type current-color stops gradient-data]}]
|
||||||
|
(if (= type :color)
|
||||||
|
{:color (:hex current-color)
|
||||||
|
:opacity (:alpha current-color)}
|
||||||
|
|
||||||
|
(let [gradient-type (case type
|
||||||
|
:linear-gradient :linear
|
||||||
|
:radial-gradient :radial)
|
||||||
|
parse-stop (fn [[offset {:keys [hex alpha]}]]
|
||||||
|
(hash-map :offset offset
|
||||||
|
:color hex
|
||||||
|
:opacity alpha))]
|
||||||
|
{:gradient (-> {:type gradient-type
|
||||||
|
:stops (mapv parse-stop stops)}
|
||||||
|
(merge gradient-data))})))
|
||||||
|
|
||||||
|
(defn create-gradient-data [type]
|
||||||
|
{:start-x 0.5
|
||||||
|
:start-y (if (= type :linear-gradient) 0.0 0.5)
|
||||||
|
:end-x 0.5
|
||||||
|
:end-y 1
|
||||||
|
:width 1.0})
|
||||||
|
|
||||||
|
(mf/defc colorpicker
|
||||||
|
[{:keys [data disable-gradient disable-opacity on-change on-accept]}]
|
||||||
|
(let [state (mf/use-state (data->state data))
|
||||||
active-tab (mf/use-state :ramp #_:harmony #_:hsva)
|
active-tab (mf/use-state :ramp #_:harmony #_:hsva)
|
||||||
selected-library (mf/use-state "recent")
|
locale (mf/deref i18n/locale)
|
||||||
current-library-colors (mf/use-state [])
|
|
||||||
ref-picker (mf/use-ref)
|
ref-picker (mf/use-ref)
|
||||||
|
|
||||||
file-colors (mf/deref refs/workspace-file-colors)
|
dirty? (mf/use-var false)
|
||||||
shared-libs (mf/deref refs/workspace-libraries)
|
last-color (mf/use-var data)
|
||||||
recent-colors (mf/deref refs/workspace-recent-colors)
|
|
||||||
|
|
||||||
picking-color? (mf/deref picking-color?)
|
picking-color? (mf/deref picking-color?)
|
||||||
picked-color (mf/deref picked-color)
|
picked-color (mf/deref picked-color)
|
||||||
picked-color-select (mf/deref picked-color-select)
|
picked-color-select (mf/deref picked-color-select)
|
||||||
picked-shift? (mf/deref picked-shift?)
|
picked-shift? (mf/deref picked-shift?)
|
||||||
|
|
||||||
locale (mf/deref i18n/locale)
|
editing-spot-state (mf/deref editing-spot-state-ref)
|
||||||
|
current-gradient (mf/deref current-gradient-ref)
|
||||||
|
|
||||||
value-ref (mf/use-var value)
|
current-color (:current-color @state)
|
||||||
|
|
||||||
on-change (or on-change identity)
|
change-tab
|
||||||
|
(fn [tab]
|
||||||
|
#(reset! active-tab tab))
|
||||||
|
|
||||||
parse-selected (fn [selected]
|
handle-change-color
|
||||||
(if (#{"recent" "file"} selected)
|
(fn [changes]
|
||||||
(keyword selected)
|
(let [editing-stop (:editing-stop @state)]
|
||||||
(uuid selected)) )
|
(swap! state #(cond-> %
|
||||||
|
true (update :current-color merge changes)
|
||||||
|
editing-stop (update-in [:stops editing-stop] merge changes)))
|
||||||
|
(reset! dirty? true)))
|
||||||
|
|
||||||
change-tab (fn [tab] #(reset! active-tab tab))
|
handle-click-picker (fn []
|
||||||
|
(if picking-color?
|
||||||
|
(do (modal/disallow-click-outside!)
|
||||||
|
(st/emit! (dc/stop-picker)))
|
||||||
|
(do (modal/allow-click-outside!)
|
||||||
|
(st/emit! (dc/start-picker)))))
|
||||||
|
|
||||||
handle-change-color (fn [changes]
|
handle-change-stop
|
||||||
(swap! current-color merge changes)
|
(fn [offset]
|
||||||
(when (:hex changes)
|
(when-let [offset-color (get-in @state [:stops offset])]
|
||||||
(reset! value-ref (:hex changes)))
|
(do (swap! state assoc
|
||||||
(on-change (:hex changes (:hex @current-color))
|
:current-color offset-color
|
||||||
(:alpha changes (:alpha @current-color))))]
|
:editing-stop offset)
|
||||||
|
|
||||||
;; Update state when there is a change in the props upstream
|
(st/emit! (dc/select-gradient-stop offset)))))
|
||||||
(mf/use-effect
|
|
||||||
(mf/deps value opacity)
|
on-select-library-color
|
||||||
(fn []
|
(fn [color]
|
||||||
(reset! current-color (as-color-components value opacity))))
|
(reset! dirty? true)
|
||||||
|
(reset! state (data->state color)))
|
||||||
|
|
||||||
|
on-add-library-color
|
||||||
|
(fn [color] (st/emit! (dwl/add-color (state->data @state))))
|
||||||
|
|
||||||
|
on-activate-gradient
|
||||||
|
(fn [type]
|
||||||
|
(fn []
|
||||||
|
(reset! dirty? true)
|
||||||
|
(if (= type (:type @state))
|
||||||
|
(do
|
||||||
|
(swap! state assoc :type :color)
|
||||||
|
(swap! state dissoc :editing-stop :stops :gradient-data)
|
||||||
|
(st/emit! (dc/stop-gradient)))
|
||||||
|
(let [gradient-data (create-gradient-data type)]
|
||||||
|
(swap! state assoc :type type :gradient-data gradient-data)
|
||||||
|
(when (not (:stops @state))
|
||||||
|
(swap! state assoc
|
||||||
|
:editing-stop 0
|
||||||
|
:stops {0 (:current-color @state)
|
||||||
|
1 (-> (:current-color @state)
|
||||||
|
(assoc :alpha 0))}))))))]
|
||||||
|
|
||||||
;; Updates the CSS color variable when there is a change in the color
|
;; Updates the CSS color variable when there is a change in the color
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(mf/deps @current-color)
|
(mf/deps current-color)
|
||||||
(fn [] (let [node (mf/ref-val ref-picker)
|
(fn [] (let [node (mf/ref-val ref-picker)
|
||||||
rgb [(:r @current-color) (:g @current-color) (:b @current-color)]
|
{:keys [r g b h s v]} current-color
|
||||||
hue-rgb (uc/hsv->rgb [(:h @current-color) 1.0 255])
|
rgb [r g b]
|
||||||
hsl-from (uc/hsv->hsl [(:h @current-color) 0 (:v @current-color)])
|
hue-rgb (uc/hsv->rgb [h 1.0 255])
|
||||||
hsl-to (uc/hsv->hsl [(:h @current-color) 1 (:v @current-color)])
|
hsl-from (uc/hsv->hsl [h 0.0 v])
|
||||||
|
hsl-to (uc/hsv->hsl [h 1.0 v])
|
||||||
|
|
||||||
format-hsl (fn [[h s l]]
|
format-hsl (fn [[h s l]]
|
||||||
(str/fmt "hsl(%s, %s, %s)"
|
(str/fmt "hsl(%s, %s, %s)"
|
||||||
|
@ -523,137 +211,131 @@
|
||||||
(dom/set-css-property node "--saturation-grad-from" (format-hsl hsl-from))
|
(dom/set-css-property node "--saturation-grad-from" (format-hsl hsl-from))
|
||||||
(dom/set-css-property node "--saturation-grad-to" (format-hsl hsl-to)))))
|
(dom/set-css-property node "--saturation-grad-to" (format-hsl hsl-to)))))
|
||||||
|
|
||||||
;; Load library colors when the select is changed
|
|
||||||
(mf/use-effect
|
|
||||||
(mf/deps @selected-library)
|
|
||||||
(fn []
|
|
||||||
(let [mapped-colors
|
|
||||||
(cond
|
|
||||||
(= @selected-library "recent")
|
|
||||||
(map #(hash-map :value %) (reverse (or recent-colors [])))
|
|
||||||
|
|
||||||
(= @selected-library "file")
|
|
||||||
(map #(select-keys % [:id :value]) (vals file-colors))
|
|
||||||
|
|
||||||
:else ;; Library UUID
|
|
||||||
(map #(merge {:file-id (uuid @selected-library)} (select-keys % [:id :value]))
|
|
||||||
(vals (get-in shared-libs [(uuid @selected-library) :data :colors]))))]
|
|
||||||
(reset! current-library-colors (into [] mapped-colors)))))
|
|
||||||
|
|
||||||
;; If the file colors change and the file option is selected updates the state
|
|
||||||
(mf/use-effect
|
|
||||||
(mf/deps file-colors)
|
|
||||||
(fn [] (when (= @selected-library "file")
|
|
||||||
(let [colors (map #(select-keys % [:id :value]) (vals file-colors))]
|
|
||||||
(reset! current-library-colors (into [] colors))))))
|
|
||||||
|
|
||||||
;; When closing the modal we update the recent-color list
|
;; When closing the modal we update the recent-color list
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(fn [] (fn []
|
#(fn []
|
||||||
(st/emit! (dwc/stop-picker))
|
(st/emit! (dc/stop-picker))
|
||||||
(when @value-ref
|
(when @last-color
|
||||||
(st/emit! (dwl/add-recent-color @value-ref))))))
|
(st/emit! (dwl/add-recent-color @last-color)))))
|
||||||
|
|
||||||
(mf/use-effect
|
|
||||||
(mf/deps picking-color? picked-color)
|
|
||||||
(fn [] (when picking-color?
|
|
||||||
(let [[r g b] (or picked-color [0 0 0])
|
|
||||||
hex (uc/rgb->hex [r g b])
|
|
||||||
[h s v] (uc/hex->hsv hex)]
|
|
||||||
(swap! current-color assoc
|
|
||||||
:r r :g g :b b
|
|
||||||
:h h :s s :v v
|
|
||||||
:hex hex)
|
|
||||||
(reset! value-ref hex)
|
|
||||||
(when picked-color-select
|
|
||||||
(on-change hex (:alpha @current-color) nil nil picked-shift?))))))
|
|
||||||
|
|
||||||
|
;; Updates color when used el pixel picker
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(mf/deps picking-color? picked-color-select)
|
(mf/deps picking-color? picked-color-select)
|
||||||
(fn [] (when (and picking-color? picked-color-select)
|
(fn []
|
||||||
(on-change (:hex @current-color) (:alpha @current-color) nil nil picked-shift?))))
|
(when (and picking-color? picked-color-select)
|
||||||
|
(let [[r g b alpha] picked-color
|
||||||
|
hex (uc/rgb->hex [r g b])
|
||||||
|
[h s v] (uc/hex->hsv hex)]
|
||||||
|
(handle-change-color {:hex hex
|
||||||
|
:r r :g g :b b
|
||||||
|
:h h :s s :v v
|
||||||
|
:alpha (/ alpha 255)})))))
|
||||||
|
|
||||||
|
;; Changes when another gradient handler is selected
|
||||||
|
(mf/use-effect
|
||||||
|
(mf/deps editing-spot-state)
|
||||||
|
#(when (not= editing-spot-state (:editing-stop @state))
|
||||||
|
(handle-change-stop (or editing-spot-state 0))))
|
||||||
|
|
||||||
|
;; Changes on the viewport when moving a gradient handler
|
||||||
|
(mf/use-effect
|
||||||
|
(mf/deps current-gradient)
|
||||||
|
(fn []
|
||||||
|
(when current-gradient
|
||||||
|
(let [gradient-data (select-keys current-gradient [:start-x :start-y
|
||||||
|
:end-x :end-y
|
||||||
|
:width])]
|
||||||
|
(when (not= (:gradient-data @state) gradient-data)
|
||||||
|
(do
|
||||||
|
(reset! dirty? true)
|
||||||
|
(swap! state assoc :gradient-data gradient-data)))))))
|
||||||
|
|
||||||
|
;; Check if we've opened a color with gradient
|
||||||
|
(mf/use-effect
|
||||||
|
(fn []
|
||||||
|
(when (:gradient data)
|
||||||
|
(st/emit! (dc/start-gradient (:gradient data))))
|
||||||
|
|
||||||
|
;; on-unmount we stop the handlers
|
||||||
|
#(st/emit! (dc/stop-gradient))))
|
||||||
|
|
||||||
|
;; Send the properties to the store
|
||||||
|
(mf/use-effect
|
||||||
|
(mf/deps @state)
|
||||||
|
(fn []
|
||||||
|
(if @dirty?
|
||||||
|
(let [color (state->data @state)]
|
||||||
|
(reset! dirty? false)
|
||||||
|
(reset! last-color color)
|
||||||
|
(when (:gradient color)
|
||||||
|
(st/emit! (dc/start-gradient (:gradient color))))
|
||||||
|
(on-change color)))))
|
||||||
|
|
||||||
[:div.colorpicker {:ref ref-picker}
|
[:div.colorpicker {:ref ref-picker}
|
||||||
[:div.colorpicker-content
|
[:div.colorpicker-content
|
||||||
[:div.top-actions
|
[:div.top-actions
|
||||||
[:button.picker-btn
|
[:button.picker-btn
|
||||||
{:class (when picking-color? "active")
|
{:class (when picking-color? "active")
|
||||||
:on-click (fn []
|
:on-click handle-click-picker}
|
||||||
(modal/allow-click-outside!)
|
|
||||||
(st/emit! (dwc/start-picker)))}
|
|
||||||
i/picker]
|
i/picker]
|
||||||
|
|
||||||
[:div.gradients-buttons
|
(when (not disable-gradient)
|
||||||
[:button.gradient.linear-gradient #_{:class "active"}]
|
[:div.gradients-buttons
|
||||||
[:button.gradient.radial-gradient]]]
|
[:button.gradient.linear-gradient
|
||||||
|
{:on-click (on-activate-gradient :linear-gradient)
|
||||||
|
:class (when (= :linear-gradient (:type @state)) "active")}]
|
||||||
|
|
||||||
#_[:div.gradient-stops
|
[:button.gradient.radial-gradient
|
||||||
[:div.gradient-background {:style {:background "linear-gradient(90deg, #EC0BE5, #CDCDCD)" }}]
|
{:on-click (on-activate-gradient :radial-gradient)
|
||||||
[:div.gradient-stop-wrapper
|
:class (when (= :radial-gradient (:type @state)) "active")}]])]
|
||||||
[:div.gradient-stop.start {:style {:background-color "#EC0BE5"}}]
|
|
||||||
[:div.gradient-stop.end {:style {:background-color "#CDCDCD"
|
[:& gradients {:type (:type @state)
|
||||||
:left "100%"}}]]]
|
:stops (:stops @state)
|
||||||
|
:editing-stop (:editing-stop @state)
|
||||||
|
:on-select-stop handle-change-stop}]
|
||||||
|
|
||||||
|
[:div.colorpicker-tabs
|
||||||
|
[:div.colorpicker-tab {:class (when (= @active-tab :ramp) "active")
|
||||||
|
:on-click (change-tab :ramp)} i/picker-ramp]
|
||||||
|
[:div.colorpicker-tab {:class (when (= @active-tab :harmony) "active")
|
||||||
|
:on-click (change-tab :harmony)} i/picker-harmony]
|
||||||
|
[:div.colorpicker-tab {:class (when (= @active-tab :hsva) "active")
|
||||||
|
:on-click (change-tab :hsva)} i/picker-hsv]]
|
||||||
|
|
||||||
(if picking-color?
|
(if picking-color?
|
||||||
[:div.picker-detail-wrapper
|
[:div.picker-detail-wrapper
|
||||||
[:div.center-circle]
|
[:div.center-circle]
|
||||||
[:canvas#picker-detail {:width 200 :height 160}]]
|
[:canvas#picker-detail {:width 200 :height 160}]]
|
||||||
(case @active-tab
|
(case @active-tab
|
||||||
:ramp [:& ramp-selector {:color @current-color :on-change handle-change-color}]
|
:ramp [:& ramp-selector {:color current-color
|
||||||
:harmony [:& harmony-selector {:color @current-color :on-change handle-change-color}]
|
:disable-opacity disable-opacity
|
||||||
:hsva [:& hsva-selector {:color @current-color :on-change handle-change-color}]
|
:on-change handle-change-color}]
|
||||||
|
:harmony [:& harmony-selector {:color current-color
|
||||||
|
:disable-opacity disable-opacity
|
||||||
|
:on-change handle-change-color}]
|
||||||
|
:hsva [:& hsva-selector {:color current-color
|
||||||
|
:disable-opacity disable-opacity
|
||||||
|
:on-change handle-change-color}]
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
[:& color-inputs {:type (if (= @active-tab :hsva) :hsv :rgb) :color @current-color :on-change handle-change-color}]
|
[:& color-inputs {:type (if (= @active-tab :hsva) :hsv :rgb)
|
||||||
|
:disable-opacity disable-opacity
|
||||||
|
:color current-color
|
||||||
|
:on-change handle-change-color}]
|
||||||
|
|
||||||
[:div.libraries
|
[:& libraries {:current-color current-color
|
||||||
[:select {:on-change (fn [e]
|
:disable-gradient disable-gradient
|
||||||
(let [val (-> e dom/get-target dom/get-value)]
|
:disable-opacity disable-opacity
|
||||||
(reset! selected-library val)))
|
:on-select-color on-select-library-color
|
||||||
:value @selected-library}
|
:on-add-library-color on-add-library-color}]
|
||||||
[:option {:value "recent"} (t locale "workspace.libraries.colors.recent-colors")]
|
|
||||||
[:option {:value "file"} (t locale "workspace.libraries.colors.file-library")]
|
|
||||||
(for [[_ {:keys [name id]}] shared-libs]
|
|
||||||
[:option {:key id
|
|
||||||
:value id} name])]
|
|
||||||
|
|
||||||
[:div.selected-colors
|
(when on-accept
|
||||||
(when (= "file" @selected-library)
|
[:div.actions
|
||||||
[:div.color-bullet.button.plus-button {:style {:background-color "white"}
|
[:button.btn-primary.btn-large
|
||||||
:on-click #(st/emit! (dwl/add-color (:hex @current-color)))}
|
{:on-click (fn []
|
||||||
i/plus])
|
(on-accept (state->data @state))
|
||||||
|
(modal/hide!))}
|
||||||
[:div.color-bullet.button {:style {:background-color "white"}
|
(t locale "workspace.libraries.colors.save-color")]])]]))
|
||||||
:on-click #(st/emit! (dwc/show-palette (parse-selected @selected-library)))}
|
|
||||||
i/palette]
|
|
||||||
|
|
||||||
(for [[idx {:keys [id file-id value]}] (map-indexed vector @current-library-colors)]
|
|
||||||
[:div.color-bullet {:key (str "color-" idx)
|
|
||||||
:on-click (fn []
|
|
||||||
(swap! current-color assoc :hex value)
|
|
||||||
(reset! value-ref value)
|
|
||||||
(let [[r g b] (uc/hex->rgb value)
|
|
||||||
[h s v] (uc/hex->hsv value)]
|
|
||||||
(swap! current-color assoc
|
|
||||||
:r r :g g :b b
|
|
||||||
:h h :s s :v v)
|
|
||||||
(on-change value (:alpha @current-color) id file-id)))
|
|
||||||
:style {:background-color value}}])]]]
|
|
||||||
[:div.colorpicker-tabs
|
|
||||||
[:div.colorpicker-tab {:class (when (= @active-tab :ramp) "active")
|
|
||||||
:on-click (change-tab :ramp)} i/picker-ramp]
|
|
||||||
[:div.colorpicker-tab {:class (when (= @active-tab :harmony) "active")
|
|
||||||
:on-click (change-tab :harmony)} i/picker-harmony]
|
|
||||||
[:div.colorpicker-tab {:class (when (= @active-tab :hsva) "active")
|
|
||||||
:on-click (change-tab :hsva)} i/picker-hsv]]
|
|
||||||
(when on-accept
|
|
||||||
[:div.actions
|
|
||||||
[:button.btn-primary.btn-large
|
|
||||||
{:on-click (fn []
|
|
||||||
(on-accept @value-ref)
|
|
||||||
(modal/hide!))}
|
|
||||||
(t locale "workspace.libraries.colors.save-color")]])])
|
|
||||||
)
|
|
||||||
|
|
||||||
(defn calculate-position
|
(defn calculate-position
|
||||||
"Calculates the style properties for the given coordinates and position"
|
"Calculates the style properties for the given coordinates and position"
|
||||||
|
@ -673,31 +355,32 @@
|
||||||
(mf/defc colorpicker-modal
|
(mf/defc colorpicker-modal
|
||||||
{::mf/register modal/components
|
{::mf/register modal/components
|
||||||
::mf/register-as :colorpicker}
|
::mf/register-as :colorpicker}
|
||||||
[{:keys [x y default value opacity page on-change on-close disable-opacity position on-accept] :as props}]
|
[{:keys [x y default data page position
|
||||||
|
disable-gradient
|
||||||
|
disable-opacity
|
||||||
|
on-change on-close on-accept] :as props}]
|
||||||
(let [vport (mf/deref viewport)
|
(let [vport (mf/deref viewport)
|
||||||
dirty? (mf/use-var false)
|
dirty? (mf/use-var false)
|
||||||
last-change (mf/use-var nil)
|
last-change (mf/use-var nil)
|
||||||
position (or position :left)
|
position (or position :left)
|
||||||
style (calculate-position vport position x y)
|
style (calculate-position vport position x y)
|
||||||
|
|
||||||
handle-change (fn [new-value new-opacity id file-id shift-clicked?]
|
handle-change (fn [new-data shift-clicked?]
|
||||||
(when (or (not= new-value value) (not= new-opacity opacity))
|
(reset! dirty? (not= data new-data))
|
||||||
(reset! dirty? true))
|
(reset! last-change new-data)
|
||||||
(reset! last-change [new-value new-opacity id file-id])
|
|
||||||
(when on-change
|
(when on-change
|
||||||
(on-change new-value new-opacity id file-id shift-clicked?)))]
|
(on-change new-data)))]
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(fn []
|
(fn []
|
||||||
#(when (and @dirty? on-close)
|
#(when (and @dirty? @last-change on-close)
|
||||||
(when-let [[value opacity id file-id] @last-change]
|
(on-close @last-change))))
|
||||||
(on-close value opacity id file-id)))))
|
|
||||||
|
|
||||||
[:div.colorpicker-tooltip
|
[:div.colorpicker-tooltip
|
||||||
{:style (clj->js style)}
|
{:style (clj->js style)}
|
||||||
[:& colorpicker {:value (or value default)
|
[:& colorpicker {:data data
|
||||||
:opacity (or opacity 1)
|
:disable-gradient disable-gradient
|
||||||
|
:disable-opacity disable-opacity
|
||||||
:on-change handle-change
|
:on-change handle-change
|
||||||
:on-accept on-accept
|
:on-accept on-accept}]]))
|
||||||
:disable-opacity disable-opacity}]]))
|
|
||||||
|
|
||||||
|
|
175
frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs
Normal file
175
frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
;; 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.workspace.colorpicker.color-inputs
|
||||||
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
|
[okulary.core :as l]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.math :as math]
|
||||||
|
[app.common.uuid :refer [uuid]]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.util.color :as uc]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
|
[app.main.data.colors :as dc]
|
||||||
|
[app.main.data.modal :as modal]
|
||||||
|
[app.main.ui.icons :as i]
|
||||||
|
[app.util.i18n :as i18n :refer [t]]))
|
||||||
|
|
||||||
|
(mf/defc color-inputs [{:keys [type color disable-opacity on-change]}]
|
||||||
|
(let [{red :r green :g blue :b
|
||||||
|
hue :h saturation :s value :v
|
||||||
|
hex :hex alpha :alpha} color
|
||||||
|
|
||||||
|
parse-hex (fn [val] (if (= (first val) \#) val (str \# val)))
|
||||||
|
|
||||||
|
refs {:hex (mf/use-ref nil)
|
||||||
|
:r (mf/use-ref nil)
|
||||||
|
:g (mf/use-ref nil)
|
||||||
|
:b (mf/use-ref nil)
|
||||||
|
:h (mf/use-ref nil)
|
||||||
|
:s (mf/use-ref nil)
|
||||||
|
:v (mf/use-ref nil)
|
||||||
|
:alpha (mf/use-ref nil)}
|
||||||
|
|
||||||
|
on-change-hex
|
||||||
|
(fn [e]
|
||||||
|
(let [val (-> e dom/get-target-val parse-hex)]
|
||||||
|
(when (uc/hex? val)
|
||||||
|
(let [[r g b] (uc/hex->rgb val)
|
||||||
|
[h s v] (uc/hex->hsv hex)]
|
||||||
|
(on-change {:hex val
|
||||||
|
:h h :s s :v v
|
||||||
|
:r r :g g :b b})))))
|
||||||
|
|
||||||
|
on-change-property
|
||||||
|
(fn [property max-value]
|
||||||
|
(fn [e]
|
||||||
|
(let [val (-> e dom/get-target-val (math/clamp 0 max-value))
|
||||||
|
val (if (#{:s} property) (/ val 100) val)]
|
||||||
|
(when (not (nil? val))
|
||||||
|
(if (#{:r :g :b} property)
|
||||||
|
(let [{:keys [r g b]} (merge color (hash-map property val))
|
||||||
|
hex (uc/rgb->hex [r g b])
|
||||||
|
[h s v] (uc/hex->hsv hex)]
|
||||||
|
(on-change {:hex hex
|
||||||
|
:h h :s s :v v
|
||||||
|
:r r :g g :b b}))
|
||||||
|
|
||||||
|
(let [{:keys [h s v]} (merge color (hash-map property val))
|
||||||
|
hex (uc/hsv->hex [h s v])
|
||||||
|
[r g b] (uc/hex->rgb hex)]
|
||||||
|
(on-change {:hex hex
|
||||||
|
:h h :s s :v v
|
||||||
|
:r r :g g :b b})))))))
|
||||||
|
|
||||||
|
on-change-opacity
|
||||||
|
(fn [e]
|
||||||
|
(when-let [new-alpha (-> e dom/get-target-val (math/clamp 0 100) (/ 100))]
|
||||||
|
(on-change {:alpha new-alpha})))]
|
||||||
|
|
||||||
|
|
||||||
|
;; Updates the inputs values when a property is changed in the parent
|
||||||
|
(mf/use-effect
|
||||||
|
(mf/deps color type)
|
||||||
|
(fn []
|
||||||
|
(doseq [ref-key (keys refs)]
|
||||||
|
(let [property-val (get color ref-key)
|
||||||
|
property-ref (get refs ref-key)]
|
||||||
|
(when (and property-val property-ref)
|
||||||
|
(when-let [node (mf/ref-val property-ref)]
|
||||||
|
(case ref-key
|
||||||
|
(:s :alpha) (dom/set-value! node (math/round (* property-val 100)))
|
||||||
|
:hex (dom/set-value! node property-val)
|
||||||
|
(dom/set-value! node (math/round property-val)))))))))
|
||||||
|
|
||||||
|
[:div.color-values
|
||||||
|
{:class (when disable-opacity "disable-opacity")}
|
||||||
|
[:input {:id "hex-value"
|
||||||
|
:ref (:hex refs)
|
||||||
|
:default-value hex
|
||||||
|
:on-change on-change-hex}]
|
||||||
|
|
||||||
|
(if (= type :rgb)
|
||||||
|
[:*
|
||||||
|
[:input {:id "red-value"
|
||||||
|
:ref (:r refs)
|
||||||
|
:type "number"
|
||||||
|
:min 0
|
||||||
|
:max 255
|
||||||
|
:default-value red
|
||||||
|
:on-change (on-change-property :r 255)}]
|
||||||
|
|
||||||
|
[:input {:id "green-value"
|
||||||
|
:ref (:g refs)
|
||||||
|
:type "number"
|
||||||
|
:min 0
|
||||||
|
:max 255
|
||||||
|
:default-value green
|
||||||
|
:on-change (on-change-property :g 255)}]
|
||||||
|
|
||||||
|
[:input {:id "blue-value"
|
||||||
|
:ref (:b refs)
|
||||||
|
:type "number"
|
||||||
|
:min 0
|
||||||
|
:max 255
|
||||||
|
:default-value blue
|
||||||
|
:on-change (on-change-property :b 255)}]]
|
||||||
|
[:*
|
||||||
|
[:input {:id "hue-value"
|
||||||
|
:ref (:h refs)
|
||||||
|
:type "number"
|
||||||
|
:min 0
|
||||||
|
:max 360
|
||||||
|
:default-value hue
|
||||||
|
:on-change (on-change-property :h 360)}]
|
||||||
|
|
||||||
|
[:input {:id "saturation-value"
|
||||||
|
:ref (:s refs)
|
||||||
|
:type "number"
|
||||||
|
:min 0
|
||||||
|
:max 100
|
||||||
|
:step 1
|
||||||
|
:default-value saturation
|
||||||
|
:on-change (on-change-property :s 100)}]
|
||||||
|
|
||||||
|
[:input {:id "value-value"
|
||||||
|
:ref (:v refs)
|
||||||
|
:type "number"
|
||||||
|
:min 0
|
||||||
|
:max 255
|
||||||
|
:default-value value
|
||||||
|
:on-change (on-change-property :v 255)}]])
|
||||||
|
|
||||||
|
(when (not disable-opacity)
|
||||||
|
[:input.alpha-value {:id "alpha-value"
|
||||||
|
:ref (:alpha refs)
|
||||||
|
:type "number"
|
||||||
|
:min 0
|
||||||
|
:step 1
|
||||||
|
:max 100
|
||||||
|
:default-value (if (= alpha :multiple) "" (math/precision alpha 2))
|
||||||
|
:on-change on-change-opacity}])
|
||||||
|
|
||||||
|
[:label.hex-label {:for "hex-value"} "HEX"]
|
||||||
|
(if (= type :rgb)
|
||||||
|
[:*
|
||||||
|
[:label.red-label {:for "red-value"} "R"]
|
||||||
|
[:label.green-label {:for "green-value"} "G"]
|
||||||
|
[:label.blue-label {:for "blue-value"} "B"]]
|
||||||
|
[:*
|
||||||
|
[:label.red-label {:for "hue-value"} "H"]
|
||||||
|
[:label.green-label {:for "saturation-value"} "S"]
|
||||||
|
[:label.blue-label {:for "value-value"} "V"]])
|
||||||
|
(when (not disable-opacity)
|
||||||
|
[:label.alpha-label {:for "alpha-value"} "A"])]))
|
|
@ -0,0 +1,54 @@
|
||||||
|
;; 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.workspace.colorpicker.gradients
|
||||||
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
|
[okulary.core :as l]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.math :as math]
|
||||||
|
[app.common.uuid :refer [uuid]]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.util.color :as uc]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
|
[app.main.data.colors :as dc]
|
||||||
|
[app.main.data.modal :as modal]
|
||||||
|
[app.main.ui.icons :as i]
|
||||||
|
[app.util.i18n :as i18n :refer [t]]))
|
||||||
|
|
||||||
|
(defn gradient->string [stops]
|
||||||
|
(let [format-stop
|
||||||
|
(fn [[offset {:keys [r g b alpha]}]]
|
||||||
|
(str/fmt "rgba(%s, %s, %s, %s) %s"
|
||||||
|
r g b alpha (str (* offset 100) "%")))
|
||||||
|
|
||||||
|
gradient-css (str/join "," (map format-stop stops))]
|
||||||
|
(str/fmt "linear-gradient(90deg, %s)" gradient-css)))
|
||||||
|
|
||||||
|
(mf/defc gradients [{:keys [type stops editing-stop on-select-stop]}]
|
||||||
|
(when (#{:linear-gradient :radial-gradient} type)
|
||||||
|
[:div.gradient-stops
|
||||||
|
[:div.gradient-background-wrapper
|
||||||
|
[:div.gradient-background {:style {:background (gradient->string stops)}}]]
|
||||||
|
|
||||||
|
[:div.gradient-stop-wrapper
|
||||||
|
(for [[offset value] stops]
|
||||||
|
[:div.gradient-stop
|
||||||
|
{:class (when (= editing-stop offset) "active")
|
||||||
|
:on-click (partial on-select-stop offset)
|
||||||
|
:style {:left (str (* offset 100) "%")}}
|
||||||
|
|
||||||
|
(let [{:keys [hex r g b alpha]} value]
|
||||||
|
[:*
|
||||||
|
[:div.gradient-stop-color {:style {:background-color hex}}]
|
||||||
|
[:div.gradient-stop-alpha {:style {:background-color (str/format "rgba(%s, %s, %s, %s)" r g b alpha)}}]])])]]))
|
155
frontend/src/app/main/ui/workspace/colorpicker/harmony.cljs
Normal file
155
frontend/src/app/main/ui/workspace/colorpicker/harmony.cljs
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
;; 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.workspace.colorpicker.harmony
|
||||||
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
|
[okulary.core :as l]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.math :as math]
|
||||||
|
[app.common.uuid :refer [uuid]]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.util.color :as uc]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
|
[app.main.data.colors :as dc]
|
||||||
|
[app.main.data.modal :as modal]
|
||||||
|
[app.main.ui.icons :as i]
|
||||||
|
[app.util.i18n :as i18n :refer [t]]
|
||||||
|
[app.main.ui.workspace.colorpicker.slider-selector :refer [slider-selector]]))
|
||||||
|
|
||||||
|
|
||||||
|
(defn create-color-wheel
|
||||||
|
[canvas-node]
|
||||||
|
(let [ctx (.getContext canvas-node "2d")
|
||||||
|
width (obj/get canvas-node "width")
|
||||||
|
height (obj/get canvas-node "height")
|
||||||
|
radius (/ width 2)
|
||||||
|
cx (/ width 2)
|
||||||
|
cy (/ width 2)
|
||||||
|
step 0.2]
|
||||||
|
|
||||||
|
(.clearRect ctx 0 0 width height)
|
||||||
|
|
||||||
|
(doseq [degrees (range 0 360 step)]
|
||||||
|
(let [degrees-rad (math/radians degrees)
|
||||||
|
x (* radius (math/cos (- degrees-rad)))
|
||||||
|
y (* radius (math/sin (- degrees-rad)))]
|
||||||
|
(obj/set! ctx "strokeStyle" (str/format "hsl(%s, 100%, 50%)" degrees))
|
||||||
|
(.beginPath ctx)
|
||||||
|
(.moveTo ctx cx cy)
|
||||||
|
(.lineTo ctx (+ cx x) (+ cy y))
|
||||||
|
(.stroke ctx)))
|
||||||
|
|
||||||
|
(let [grd (.createRadialGradient ctx cx cy 0 cx cx radius)]
|
||||||
|
(.addColorStop grd 0 "white")
|
||||||
|
(.addColorStop grd 1 "rgba(255, 255, 255, 0")
|
||||||
|
(obj/set! ctx "fillStyle" grd)
|
||||||
|
|
||||||
|
(.beginPath ctx)
|
||||||
|
(.arc ctx cx cy radius 0 (* 2 math/PI) true)
|
||||||
|
(.closePath ctx)
|
||||||
|
(.fill ctx))))
|
||||||
|
|
||||||
|
(defn color->point
|
||||||
|
[canvas-side hue saturation]
|
||||||
|
(let [hue-rad (math/radians (- hue))
|
||||||
|
comp-x (* saturation (math/cos hue-rad))
|
||||||
|
comp-y (* saturation (math/sin hue-rad))
|
||||||
|
x (+ (/ canvas-side 2) (* comp-x (/ canvas-side 2)))
|
||||||
|
y (+ (/ canvas-side 2) (* comp-y (/ canvas-side 2)))]
|
||||||
|
(gpt/point x y)))
|
||||||
|
|
||||||
|
(mf/defc harmony-selector [{:keys [color disable-opacity on-change]}]
|
||||||
|
(let [canvas-ref (mf/use-ref nil)
|
||||||
|
{hue :h saturation :s value :v alpha :alpha} color
|
||||||
|
|
||||||
|
canvas-side 152
|
||||||
|
pos-current (color->point canvas-side hue saturation)
|
||||||
|
pos-complement (color->point canvas-side (mod (+ hue 180) 360) saturation)
|
||||||
|
dragging? (mf/use-state false)
|
||||||
|
|
||||||
|
calculate-pos (fn [ev]
|
||||||
|
(let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect)
|
||||||
|
{:keys [x y]} (-> ev dom/get-client-position)
|
||||||
|
px (math/clamp (/ (- x left) (- right left)) 0 1)
|
||||||
|
py (math/clamp (/ (- y top) (- bottom top)) 0 1)
|
||||||
|
|
||||||
|
px (- (* 2 px) 1)
|
||||||
|
py (- (* 2 py) 1)
|
||||||
|
|
||||||
|
angle (math/degrees (math/atan2 px py))
|
||||||
|
new-hue (math/precision (mod (- angle 90 ) 360) 2)
|
||||||
|
new-saturation (math/clamp (math/distance [px py] [0 0]) 0 1)
|
||||||
|
hex (uc/hsv->hex [new-hue new-saturation value])
|
||||||
|
[r g b] (uc/hex->rgb hex)]
|
||||||
|
(on-change {:hex hex
|
||||||
|
:r r :g g :b b
|
||||||
|
:h new-hue
|
||||||
|
:s new-saturation})))
|
||||||
|
|
||||||
|
on-change-value (fn [new-value]
|
||||||
|
(let [hex (uc/hsv->hex [hue saturation new-value])
|
||||||
|
[r g b] (uc/hex->rgb hex)]
|
||||||
|
(on-change {:hex hex
|
||||||
|
:r r :g g :b b
|
||||||
|
:v new-value})))
|
||||||
|
on-complement-click (fn [ev]
|
||||||
|
(let [new-hue (mod (+ hue 180) 360)
|
||||||
|
hex (uc/hsv->hex [new-hue saturation value])
|
||||||
|
[r g b] (uc/hex->rgb hex)]
|
||||||
|
(on-change {:hex hex
|
||||||
|
:r r :g g :b b
|
||||||
|
:h new-hue
|
||||||
|
:s saturation})))
|
||||||
|
|
||||||
|
on-change-opacity (fn [new-alpha] (on-change {:alpha new-alpha}))]
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(mf/deps canvas-ref)
|
||||||
|
(fn [] (when canvas-ref
|
||||||
|
(create-color-wheel (mf/ref-val canvas-ref)))))
|
||||||
|
|
||||||
|
[:div.harmony-selector
|
||||||
|
[:div.hue-wheel-wrapper
|
||||||
|
[:canvas.hue-wheel
|
||||||
|
{:ref canvas-ref
|
||||||
|
:width canvas-side
|
||||||
|
:height canvas-side
|
||||||
|
:on-mouse-down #(reset! dragging? true)
|
||||||
|
:on-mouse-up #(reset! dragging? false)
|
||||||
|
:on-pointer-down (partial dom/capture-pointer)
|
||||||
|
:on-pointer-up (partial dom/release-pointer)
|
||||||
|
:on-click calculate-pos
|
||||||
|
:on-mouse-move #(when @dragging? (calculate-pos %))}]
|
||||||
|
[:div.handler {:style {:pointer-events "none"
|
||||||
|
:left (:x pos-current)
|
||||||
|
:top (:y pos-current)}}]
|
||||||
|
[:div.handler.complement {:style {:left (:x pos-complement)
|
||||||
|
:top (:y pos-complement)
|
||||||
|
:cursor "pointer"}
|
||||||
|
:on-click on-complement-click}]]
|
||||||
|
[:div.handlers-wrapper
|
||||||
|
[:& slider-selector {:class "value"
|
||||||
|
:vertical? true
|
||||||
|
:reverse? true
|
||||||
|
:value value
|
||||||
|
:max-value 255
|
||||||
|
:vertical true
|
||||||
|
:on-change on-change-value}]
|
||||||
|
(when (not disable-opacity)
|
||||||
|
[:& slider-selector {:class "opacity"
|
||||||
|
:vertical? true
|
||||||
|
:value alpha
|
||||||
|
:max-value 1
|
||||||
|
:vertical true
|
||||||
|
:on-change on-change-opacity}])]]))
|
59
frontend/src/app/main/ui/workspace/colorpicker/hsva.cljs
Normal file
59
frontend/src/app/main/ui/workspace/colorpicker/hsva.cljs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
;; 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.workspace.colorpicker.hsva
|
||||||
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
|
[okulary.core :as l]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.math :as math]
|
||||||
|
[app.common.uuid :refer [uuid]]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.util.color :as uc]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
|
[app.main.data.colors :as dc]
|
||||||
|
[app.main.data.modal :as modal]
|
||||||
|
[app.main.ui.icons :as i]
|
||||||
|
[app.util.i18n :as i18n :refer [t]]
|
||||||
|
[app.main.ui.workspace.colorpicker.slider-selector :refer [slider-selector]]))
|
||||||
|
|
||||||
|
(mf/defc hsva-selector [{:keys [color disable-opacity on-change]}]
|
||||||
|
(let [{hue :h saturation :s value :v alpha :alpha} color
|
||||||
|
handle-change-slider (fn [key]
|
||||||
|
(fn [new-value]
|
||||||
|
(let [change (hash-map key new-value)
|
||||||
|
{:keys [h s v]} (merge color change)
|
||||||
|
hex (uc/hsv->hex [h s v])
|
||||||
|
[r g b] (uc/hex->rgb hex)]
|
||||||
|
(on-change (merge change
|
||||||
|
{:hex hex
|
||||||
|
:r r :g g :b b})))))
|
||||||
|
on-change-opacity (fn [new-alpha] (on-change {:alpha new-alpha}))]
|
||||||
|
[:div.hsva-selector
|
||||||
|
[:span.hsva-selector-label "H"]
|
||||||
|
[:& slider-selector
|
||||||
|
{:class "hue" :max-value 360 :value hue :on-change (handle-change-slider :h)}]
|
||||||
|
|
||||||
|
[:span.hsva-selector-label "S"]
|
||||||
|
[:& slider-selector
|
||||||
|
{:class "saturation" :max-value 1 :value saturation :on-change (handle-change-slider :s)}]
|
||||||
|
|
||||||
|
[:span.hsva-selector-label "V"]
|
||||||
|
[:& slider-selector
|
||||||
|
{:class "value" :reverse? true :max-value 255 :value value :on-change (handle-change-slider :v)}]
|
||||||
|
|
||||||
|
(when (not disable-opacity)
|
||||||
|
[:*
|
||||||
|
[:span.hsva-selector-label "A"]
|
||||||
|
[:& slider-selector
|
||||||
|
{:class "opacity" :max-value 1 :value alpha :on-change on-change-opacity}]])]))
|
106
frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs
Normal file
106
frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
;; 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.workspace.colorpicker.libraries
|
||||||
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
|
[okulary.core :as l]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.math :as math]
|
||||||
|
[app.common.uuid :refer [uuid]]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.util.color :as uc]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
|
[app.main.data.colors :as dc]
|
||||||
|
[app.main.data.modal :as modal]
|
||||||
|
[app.main.ui.icons :as i]
|
||||||
|
[app.util.i18n :as i18n :refer [t]]
|
||||||
|
[app.main.ui.components.color-bullet :refer [color-bullet]]
|
||||||
|
[app.main.ui.workspace.colorpicker.gradients :refer [gradients]]
|
||||||
|
[app.main.ui.workspace.colorpicker.harmony :refer [harmony-selector]]
|
||||||
|
[app.main.ui.workspace.colorpicker.hsva :refer [hsva-selector]]
|
||||||
|
[app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector]]
|
||||||
|
[app.main.ui.workspace.colorpicker.color-inputs :refer [color-inputs]]))
|
||||||
|
|
||||||
|
(mf/defc libraries [{:keys [current-color on-select-color on-add-library-color
|
||||||
|
disable-gradient disable-opacity]}]
|
||||||
|
(let [selected-library (mf/use-state "recent")
|
||||||
|
current-library-colors (mf/use-state [])
|
||||||
|
|
||||||
|
shared-libs (mf/deref refs/workspace-libraries)
|
||||||
|
file-colors (mf/deref refs/workspace-file-colors)
|
||||||
|
recent-colors (mf/deref refs/workspace-recent-colors)
|
||||||
|
locale (mf/deref i18n/locale)
|
||||||
|
|
||||||
|
parse-selected
|
||||||
|
(fn [selected]
|
||||||
|
(if (#{"recent" "file"} selected)
|
||||||
|
(keyword selected)
|
||||||
|
(uuid selected)) )
|
||||||
|
|
||||||
|
check-valid-color? (fn [color]
|
||||||
|
(and (or (not disable-gradient) (not (:gradient color)))
|
||||||
|
(or (not disable-opacity) (= 1 (:opacity color)))))]
|
||||||
|
|
||||||
|
;; Load library colors when the select is changed
|
||||||
|
(mf/use-effect
|
||||||
|
(mf/deps @selected-library)
|
||||||
|
(fn []
|
||||||
|
(let [mapped-colors
|
||||||
|
(cond
|
||||||
|
(= @selected-library "recent")
|
||||||
|
;; The `map?` check is to keep backwards compatibility. We transform from string to map
|
||||||
|
(map #(if (map? %) % (hash-map :color %)) (reverse (or recent-colors [])))
|
||||||
|
|
||||||
|
(= @selected-library "file")
|
||||||
|
(vals file-colors)
|
||||||
|
|
||||||
|
:else ;; Library UUID
|
||||||
|
(map #(merge {:file-id (uuid @selected-library)})
|
||||||
|
(vals (get-in shared-libs [(uuid @selected-library) :data :colors]))))]
|
||||||
|
(reset! current-library-colors (into [] (filter check-valid-color?) mapped-colors)))))
|
||||||
|
|
||||||
|
;; If the file colors change and the file option is selected updates the state
|
||||||
|
(mf/use-effect
|
||||||
|
(mf/deps file-colors)
|
||||||
|
(fn [] (when (= @selected-library "file")
|
||||||
|
(let [colors (vals file-colors)]
|
||||||
|
(reset! current-library-colors (into [] (filter check-valid-color?) colors))))))
|
||||||
|
|
||||||
|
|
||||||
|
[:div.libraries
|
||||||
|
[:select {:on-change (fn [e]
|
||||||
|
(when-let [val (dom/get-target-val e)]
|
||||||
|
(reset! selected-library val)))
|
||||||
|
:value @selected-library}
|
||||||
|
[:option {:value "recent"} (t locale "workspace.libraries.colors.recent-colors")]
|
||||||
|
[:option {:value "file"} (t locale "workspace.libraries.colors.file-library")]
|
||||||
|
|
||||||
|
(for [[_ {:keys [name id]}] shared-libs]
|
||||||
|
[:option {:key id
|
||||||
|
:value id} name])]
|
||||||
|
|
||||||
|
[:div.selected-colors
|
||||||
|
(when (= "file" @selected-library)
|
||||||
|
[:div.color-bullet.button.plus-button {:style {:background-color "white"}
|
||||||
|
:on-click on-add-library-color}
|
||||||
|
i/plus])
|
||||||
|
|
||||||
|
[:div.color-bullet.button {:style {:background-color "white"}
|
||||||
|
:on-click #(st/emit! (dc/show-palette (parse-selected @selected-library)))}
|
||||||
|
i/palette]
|
||||||
|
|
||||||
|
(for [[idx color] (map-indexed vector @current-library-colors)]
|
||||||
|
[:& color-bullet {:key (str "color-" idx)
|
||||||
|
:color color
|
||||||
|
:on-click #(on-select-color color)}])]]))
|
|
@ -0,0 +1,187 @@
|
||||||
|
;; 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.workspace.colorpicker.pixel-overlay
|
||||||
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[okulary.core :as l]
|
||||||
|
[promesa.core :as p]
|
||||||
|
[beicon.core :as rx]
|
||||||
|
[goog.events :as events]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.util.timers :as timers]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[app.main.data.colors :as dwc]
|
||||||
|
[app.main.data.fetch :as mdf]
|
||||||
|
[app.main.data.modal :as modal]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.ui.context :as muc]
|
||||||
|
[app.main.ui.cursors :as cur]
|
||||||
|
[app.main.ui.keyboard :as kbd]
|
||||||
|
[app.main.ui.workspace.shapes :refer [shape-wrapper frame-wrapper]])
|
||||||
|
(:import goog.events.EventType))
|
||||||
|
|
||||||
|
(defn format-viewbox [vbox]
|
||||||
|
(str/join " " [(+ (:x vbox 0) (:left-offset vbox 0))
|
||||||
|
(:y vbox 0)
|
||||||
|
(:width vbox 0)
|
||||||
|
(:height vbox 0)]))
|
||||||
|
|
||||||
|
(mf/defc overlay-frames
|
||||||
|
{::mf/wrap [mf/memo]
|
||||||
|
::mf/wrap-props false}
|
||||||
|
[]
|
||||||
|
(let [data (mf/deref refs/workspace-page)
|
||||||
|
objects (:objects data)
|
||||||
|
root (get objects uuid/zero)
|
||||||
|
shapes (->> (:shapes root) (map #(get objects %)))]
|
||||||
|
[:*
|
||||||
|
[:g.shapes
|
||||||
|
(for [item shapes]
|
||||||
|
(if (= (:type item) :frame)
|
||||||
|
[:& frame-wrapper {:shape item
|
||||||
|
:key (:id item)
|
||||||
|
:objects objects}]
|
||||||
|
[:& shape-wrapper {:shape item
|
||||||
|
:key (:id item)}]))]]))
|
||||||
|
|
||||||
|
(defn draw-picker-canvas [svg-node canvas-node]
|
||||||
|
(let [canvas-context (.getContext canvas-node "2d")
|
||||||
|
xml (.serializeToString (js/XMLSerializer.) svg-node)
|
||||||
|
img-src (str "data:image/svg+xml;base64,"
|
||||||
|
(-> xml js/encodeURIComponent js/unescape js/btoa))
|
||||||
|
img (js/Image.)
|
||||||
|
|
||||||
|
on-error (fn [err] (.error js/console "ERROR" err))
|
||||||
|
on-load (fn [] (.drawImage canvas-context img 0 0))]
|
||||||
|
(.addEventListener img "error" on-error)
|
||||||
|
(.addEventListener img "load" on-load)
|
||||||
|
(obj/set! img "src" img-src)))
|
||||||
|
|
||||||
|
(mf/defc pixel-overlay
|
||||||
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
(let [vport (unchecked-get props "vport")
|
||||||
|
vbox (unchecked-get props "vbox")
|
||||||
|
viewport-ref (unchecked-get props "viewport-ref")
|
||||||
|
options (unchecked-get props "options")
|
||||||
|
svg-ref (mf/use-ref nil)
|
||||||
|
canvas-ref (mf/use-ref nil)
|
||||||
|
fetch-pending (mf/deref (mdf/pending-ref))
|
||||||
|
|
||||||
|
update-canvas-stream (rx/subject)
|
||||||
|
|
||||||
|
handle-keydown
|
||||||
|
(fn [event]
|
||||||
|
(when (and (kbd/esc? event))
|
||||||
|
(do (dom/stop-propagation event)
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(st/emit! (dwc/stop-picker))
|
||||||
|
(modal/disallow-click-outside!))))
|
||||||
|
|
||||||
|
on-mouse-move-picker
|
||||||
|
(fn [event]
|
||||||
|
(when-let [zoom-view-node (.getElementById js/document "picker-detail")]
|
||||||
|
(let [{brx :left bry :top} (dom/get-bounding-rect (mf/ref-val viewport-ref))
|
||||||
|
x (- (.-clientX event) brx)
|
||||||
|
y (- (.-clientY event) bry)
|
||||||
|
|
||||||
|
zoom-context (.getContext zoom-view-node "2d")
|
||||||
|
canvas-node (mf/ref-val canvas-ref)
|
||||||
|
canvas-context (.getContext canvas-node "2d")
|
||||||
|
pixel-data (.getImageData canvas-context x y 1 1)
|
||||||
|
rgba (.-data pixel-data)
|
||||||
|
r (obj/get rgba 0)
|
||||||
|
g (obj/get rgba 1)
|
||||||
|
b (obj/get rgba 2)
|
||||||
|
a (obj/get rgba 3)
|
||||||
|
|
||||||
|
area-data (.getImageData canvas-context (- x 25) (- y 20) 50 40)]
|
||||||
|
|
||||||
|
(-> (js/createImageBitmap area-data)
|
||||||
|
(p/then (fn [image]
|
||||||
|
;; Draw area
|
||||||
|
(obj/set! zoom-context "imageSmoothingEnabled" false)
|
||||||
|
(.drawImage zoom-context image 0 0 200 160))))
|
||||||
|
(st/emit! (dwc/pick-color [r g b a])))))
|
||||||
|
|
||||||
|
on-mouse-down-picker
|
||||||
|
(fn [event]
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(dom/stop-propagation event)
|
||||||
|
(st/emit! (dwc/pick-color-select true (kbd/shift? event))))
|
||||||
|
|
||||||
|
on-mouse-up-picker
|
||||||
|
(fn [event]
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(dom/stop-propagation event)
|
||||||
|
(st/emit! (dwc/stop-picker))
|
||||||
|
(modal/disallow-click-outside!))]
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(fn []
|
||||||
|
(let [listener (events/listen js/document EventType.KEYDOWN handle-keydown)]
|
||||||
|
#(events/unlistenByKey listener))))
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(fn []
|
||||||
|
(let [sub (->> update-canvas-stream
|
||||||
|
(rx/debounce 10)
|
||||||
|
(rx/subs #(draw-picker-canvas (mf/ref-val svg-ref)
|
||||||
|
(mf/ref-val canvas-ref))))]
|
||||||
|
|
||||||
|
#(rx/dispose! sub))))
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(mf/deps svg-ref canvas-ref)
|
||||||
|
(fn []
|
||||||
|
(when (and svg-ref canvas-ref)
|
||||||
|
|
||||||
|
(let [config (clj->js {:attributes true
|
||||||
|
:childList true
|
||||||
|
:subtree true
|
||||||
|
:characterData true})
|
||||||
|
on-svg-change (fn [mutation-list] (rx/push! update-canvas-stream :update))
|
||||||
|
observer (js/MutationObserver. on-svg-change)]
|
||||||
|
|
||||||
|
(.observe observer (mf/ref-val svg-ref) config)
|
||||||
|
|
||||||
|
;; Disconnect on unmount
|
||||||
|
#(.disconnect observer)))))
|
||||||
|
|
||||||
|
[:*
|
||||||
|
[:div.overlay
|
||||||
|
{:tab-index 0
|
||||||
|
:style {:position "absolute"
|
||||||
|
:top 0
|
||||||
|
:left 0
|
||||||
|
:width "100%"
|
||||||
|
:height "100%"
|
||||||
|
:cursor cur/picker}
|
||||||
|
:on-mouse-down on-mouse-down-picker
|
||||||
|
:on-mouse-up on-mouse-up-picker
|
||||||
|
:on-mouse-move on-mouse-move-picker}]
|
||||||
|
[:canvas {:ref canvas-ref
|
||||||
|
:width (:width vport 0)
|
||||||
|
:height (:height vport 0)
|
||||||
|
:style {:display "none"}}]
|
||||||
|
|
||||||
|
[:& (mf/provider muc/embed-ctx) {:value true}
|
||||||
|
[:svg.viewport
|
||||||
|
{:ref svg-ref
|
||||||
|
:preserveAspectRatio "xMidYMid meet"
|
||||||
|
:width (:width vport 0)
|
||||||
|
:height (:height vport 0)
|
||||||
|
:view-box (format-viewbox vbox)
|
||||||
|
:style {:display "none"
|
||||||
|
:background-color (get options :background "#E8E9EA")}}
|
||||||
|
[:& overlay-frames]]]]))
|
|
@ -0,0 +1,29 @@
|
||||||
|
;; 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.workspace.colorpicker.pixel-picker
|
||||||
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
|
[okulary.core :as l]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.math :as math]
|
||||||
|
[app.common.uuid :refer [uuid]]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.util.color :as uc]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
|
[app.main.data.colors :as dc]
|
||||||
|
[app.main.data.modal :as modal]
|
||||||
|
[app.main.ui.icons :as i]
|
||||||
|
[app.util.i18n :as i18n :refer [t]]))
|
||||||
|
|
||||||
|
|
95
frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs
Normal file
95
frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
;; 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.workspace.colorpicker.ramp
|
||||||
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
|
[okulary.core :as l]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.math :as math]
|
||||||
|
[app.common.uuid :refer [uuid]]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.util.color :as uc]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
|
[app.main.data.colors :as dc]
|
||||||
|
[app.main.data.modal :as modal]
|
||||||
|
[app.main.ui.icons :as i]
|
||||||
|
[app.util.i18n :as i18n :refer [t]]
|
||||||
|
[app.main.ui.components.color-bullet :refer [color-bullet]]
|
||||||
|
[app.main.ui.workspace.colorpicker.slider-selector :refer [slider-selector]]))
|
||||||
|
|
||||||
|
(mf/defc value-saturation-selector [{:keys [hue saturation value on-change]}]
|
||||||
|
(let [dragging? (mf/use-state false)
|
||||||
|
calculate-pos
|
||||||
|
(fn [ev]
|
||||||
|
(let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect)
|
||||||
|
{:keys [x y]} (-> ev dom/get-client-position)
|
||||||
|
px (math/clamp (/ (- x left) (- right left)) 0 1)
|
||||||
|
py (* 255 (- 1 (math/clamp (/ (- y top) (- bottom top)) 0 1)))]
|
||||||
|
(on-change px py)))]
|
||||||
|
[:div.value-saturation-selector
|
||||||
|
{:on-mouse-down #(reset! dragging? true)
|
||||||
|
:on-mouse-up #(reset! dragging? false)
|
||||||
|
:on-pointer-down (partial dom/capture-pointer)
|
||||||
|
:on-pointer-up (partial dom/release-pointer)
|
||||||
|
:on-click calculate-pos
|
||||||
|
:on-mouse-move #(when @dragging? (calculate-pos %))}
|
||||||
|
[:div.handler {:style {:pointer-events "none"
|
||||||
|
:left (str (* 100 saturation) "%")
|
||||||
|
:top (str (* 100 (- 1 (/ value 255))) "%")}}]]))
|
||||||
|
|
||||||
|
|
||||||
|
(mf/defc ramp-selector [{:keys [color disable-opacity on-change]}]
|
||||||
|
(let [{hex :hex
|
||||||
|
hue :h saturation :s value :v alpha :alpha} color
|
||||||
|
|
||||||
|
on-change-value-saturation
|
||||||
|
(fn [new-saturation new-value]
|
||||||
|
(let [hex (uc/hsv->hex [hue new-saturation new-value])
|
||||||
|
[r g b] (uc/hex->rgb hex)]
|
||||||
|
(on-change {:hex hex
|
||||||
|
:r r :g g :b b
|
||||||
|
:s new-saturation
|
||||||
|
:v new-value})))
|
||||||
|
|
||||||
|
on-change-hue
|
||||||
|
(fn [new-hue]
|
||||||
|
(let [hex (uc/hsv->hex [new-hue saturation value])
|
||||||
|
[r g b] (uc/hex->rgb hex)]
|
||||||
|
(on-change {:hex hex
|
||||||
|
:r r :g g :b b
|
||||||
|
:h new-hue} )))
|
||||||
|
|
||||||
|
on-change-opacity
|
||||||
|
(fn [new-opacity]
|
||||||
|
(on-change {:alpha new-opacity} ))]
|
||||||
|
[:*
|
||||||
|
[:& value-saturation-selector
|
||||||
|
{:hue hue
|
||||||
|
:saturation saturation
|
||||||
|
:value value
|
||||||
|
:on-change on-change-value-saturation}]
|
||||||
|
|
||||||
|
[:div.shade-selector
|
||||||
|
[:& color-bullet {:color {:color hex
|
||||||
|
:opacity alpha}}]
|
||||||
|
[:& slider-selector {:class "hue"
|
||||||
|
:max-value 360
|
||||||
|
:value hue
|
||||||
|
:on-change on-change-hue}]
|
||||||
|
|
||||||
|
(when (not disable-opacity)
|
||||||
|
[:& slider-selector {:class "opacity"
|
||||||
|
:max-value 1
|
||||||
|
:value alpha
|
||||||
|
:on-change on-change-opacity}])]]))
|
|
@ -0,0 +1,68 @@
|
||||||
|
;; 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.workspace.colorpicker.slider-selector
|
||||||
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
|
[okulary.core :as l]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.math :as math]
|
||||||
|
[app.common.uuid :refer [uuid]]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.util.color :as uc]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
|
[app.main.data.colors :as dc]
|
||||||
|
[app.main.data.modal :as modal]
|
||||||
|
[app.main.ui.icons :as i]
|
||||||
|
[app.util.i18n :as i18n :refer [t]]))
|
||||||
|
|
||||||
|
(mf/defc slider-selector
|
||||||
|
[{:keys [value class min-value max-value vertical? reverse? on-change]}]
|
||||||
|
(let [min-value (or min-value 0)
|
||||||
|
max-value (or max-value 1)
|
||||||
|
dragging? (mf/use-state false)
|
||||||
|
calculate-pos
|
||||||
|
(fn [ev]
|
||||||
|
(when on-change
|
||||||
|
(let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect)
|
||||||
|
{:keys [x y]} (-> ev dom/get-client-position)
|
||||||
|
unit-value (if vertical?
|
||||||
|
(math/clamp (/ (- bottom y) (- bottom top)) 0 1)
|
||||||
|
(math/clamp (/ (- x left) (- right left)) 0 1))
|
||||||
|
unit-value (if reverse?
|
||||||
|
(math/abs (- unit-value 1.0))
|
||||||
|
unit-value)
|
||||||
|
value (+ min-value (* unit-value (- max-value min-value)))]
|
||||||
|
(on-change (math/precision value 2)))))]
|
||||||
|
|
||||||
|
[:div.slider-selector
|
||||||
|
{:class (str (if vertical? "vertical " "") class)
|
||||||
|
:on-mouse-down #(reset! dragging? true)
|
||||||
|
:on-mouse-up #(reset! dragging? false)
|
||||||
|
:on-pointer-down (partial dom/capture-pointer)
|
||||||
|
:on-pointer-up (partial dom/release-pointer)
|
||||||
|
:on-click calculate-pos
|
||||||
|
:on-mouse-move #(when @dragging? (calculate-pos %))}
|
||||||
|
|
||||||
|
(let [value-percent (* (/ (- value min-value)
|
||||||
|
(- max-value min-value)) 100)
|
||||||
|
|
||||||
|
value-percent (if reverse?
|
||||||
|
(math/abs (- value-percent 100))
|
||||||
|
value-percent)
|
||||||
|
value-percent-str (str value-percent "%")
|
||||||
|
|
||||||
|
style-common #js {:pointerEvents "none"}
|
||||||
|
style-horizontal (obj/merge! #js {:left value-percent-str} style-common)
|
||||||
|
style-vertical (obj/merge! #js {:bottom value-percent-str} style-common)]
|
||||||
|
[:div.handler {:style (if vertical? style-vertical style-horizontal)}])]))
|
|
@ -18,7 +18,9 @@
|
||||||
|
|
||||||
(mf/defc square-grid [{:keys [frame zoom grid] :as props}]
|
(mf/defc square-grid [{:keys [frame zoom grid] :as props}]
|
||||||
(let [{:keys [color size] :as params} (-> grid :params)
|
(let [{:keys [color size] :as params} (-> grid :params)
|
||||||
{color-value :value color-opacity :opacity} (-> grid :params :color)
|
{color-value :color color-opacity :opacity} (-> grid :params :color)
|
||||||
|
;; Support for old color format
|
||||||
|
color-value (or color-value (:value (get-in grid [:params :color :value])))
|
||||||
{frame-width :width frame-height :height :keys [x y]} frame]
|
{frame-width :width frame-height :height :keys [x y]} frame]
|
||||||
(when (> size 0)
|
(when (> size 0)
|
||||||
[:g.grid
|
[:g.grid
|
||||||
|
@ -43,7 +45,9 @@
|
||||||
:stroke-width (str (/ 1 zoom))}}])]])))
|
:stroke-width (str (/ 1 zoom))}}])]])))
|
||||||
|
|
||||||
(mf/defc layout-grid [{:keys [key frame zoom grid]}]
|
(mf/defc layout-grid [{:keys [key frame zoom grid]}]
|
||||||
(let [{color-value :value color-opacity :opacity} (-> grid :params :color)
|
(let [{color-value :color color-opacity :opacity} (-> grid :params :color)
|
||||||
|
;; Support for old color format
|
||||||
|
color-value (or color-value (:value (get-in grid [:params :color :value])))
|
||||||
gutter (-> grid :params :gutter)
|
gutter (-> grid :params :gutter)
|
||||||
gutter? (and (not (nil? gutter)) (not= gutter 0))
|
gutter? (and (not (nil? gutter)) (not= gutter 0))
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,17 @@
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[okulary.core :as l]
|
||||||
[app.main.store :as st]
|
|
||||||
[app.main.streams :as ms]
|
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.util.dom :as dom]
|
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.matrix :as gmt]))
|
[app.common.geom.matrix :as gmt]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.main.streams :as ms]
|
||||||
|
[app.main.data.modal :as modal]
|
||||||
|
[app.main.data.workspace.common :as dwc]
|
||||||
|
[app.main.data.colors :as dc]))
|
||||||
|
|
||||||
(def gradient-line-stroke-width 2)
|
(def gradient-line-stroke-width 2)
|
||||||
(def gradient-line-stroke-color "white")
|
(def gradient-line-stroke-color "white")
|
||||||
|
@ -31,6 +35,12 @@
|
||||||
(def gradient-square-stroke-color "white")
|
(def gradient-square-stroke-color "white")
|
||||||
(def gradient-square-stroke-color-selected "#1FDEA7")
|
(def gradient-square-stroke-color-selected "#1FDEA7")
|
||||||
|
|
||||||
|
(def editing-spot-ref
|
||||||
|
(l/derived (l/in [:workspace-local :editing-stop]) st/state))
|
||||||
|
|
||||||
|
(def current-gradient-ref
|
||||||
|
(l/derived (l/in [:workspace-local :current-gradient]) st/state))
|
||||||
|
|
||||||
(mf/defc shadow [{:keys [id x y width height offset]}]
|
(mf/defc shadow [{:keys [id x y width height offset]}]
|
||||||
[:filter {:id id
|
[:filter {:id id
|
||||||
:x x
|
:x x
|
||||||
|
@ -77,24 +87,13 @@
|
||||||
:height (+ (/ (* 2 gradient-width-handler-radius) zoom) (/ 2 zoom) 4)
|
:height (+ (/ (* 2 gradient-width-handler-radius) zoom) (/ 2 zoom) 4)
|
||||||
:offset (/ 2 zoom)}])
|
:offset (/ 2 zoom)}])
|
||||||
|
|
||||||
(def default-gradient
|
|
||||||
{:type :linear
|
|
||||||
:start-x 0.5 :start-y 0.5
|
|
||||||
:end-x 0.5 :end-y 1
|
|
||||||
:width 1.0
|
|
||||||
:stops [{:offset 0
|
|
||||||
:color "#FF0000"
|
|
||||||
:opacity 1}
|
|
||||||
{:offset 1
|
|
||||||
:color "#FF0000"
|
|
||||||
:opacity 0.2}]})
|
|
||||||
|
|
||||||
(def checkboard "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAIAAAC0tAIdAAACvUlEQVQoFQGyAk39AeLi4gAAAAAAAB0dHQAAAAAAAOPj4wAAAAAAAB0dHQAAAAAAAOPj4wAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB////AAAAAAAA4+PjAAAAAAAAHR0dAAAAAAAA4+PjAAAAAAAAHR0dAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATj4+MAAAAAAAAdHR0AAAAAAADj4+MAAAAAAAAdHR0AAAAAAADj4+MAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjScaa0cU7nIAAAAASUVORK5CYII=")
|
(def checkboard "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAIAAAC0tAIdAAACvUlEQVQoFQGyAk39AeLi4gAAAAAAAB0dHQAAAAAAAOPj4wAAAAAAAB0dHQAAAAAAAOPj4wAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB////AAAAAAAA4+PjAAAAAAAAHR0dAAAAAAAA4+PjAAAAAAAAHR0dAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATj4+MAAAAAAAAdHR0AAAAAAADj4+MAAAAAAAAdHR0AAAAAAADj4+MAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjScaa0cU7nIAAAAASUVORK5CYII=")
|
||||||
|
|
||||||
#_(def checkboard "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=")
|
#_(def checkboard "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=")
|
||||||
|
|
||||||
(mf/defc gradient-color-handler
|
(mf/defc gradient-color-handler
|
||||||
[{:keys [filter-id zoom point color angle on-click on-mouse-down on-mouse-up]}]
|
[{:keys [filter-id zoom point color angle selected
|
||||||
|
on-click on-mouse-down on-mouse-up]}]
|
||||||
[:g {:filter (str/fmt "url(#%s)" filter-id)
|
[:g {:filter (str/fmt "url(#%s)" filter-id)
|
||||||
:transform (gmt/rotate-matrix angle point)}
|
:transform (gmt/rotate-matrix angle point)}
|
||||||
|
|
||||||
|
@ -114,12 +113,13 @@
|
||||||
:on-mouse-down (partial on-mouse-down :to-p)
|
:on-mouse-down (partial on-mouse-down :to-p)
|
||||||
:on-mouse-up (partial on-mouse-up :to-p)}]
|
:on-mouse-up (partial on-mouse-up :to-p)}]
|
||||||
|
|
||||||
[:rect {:x (- (:x point) (/ gradient-square-width 2 zoom))
|
[:rect {:data-allow-click-modal "colorpicker"
|
||||||
|
:x (- (:x point) (/ gradient-square-width 2 zoom))
|
||||||
:y (- (:y point) (/ gradient-square-width 2 zoom))
|
:y (- (:y point) (/ gradient-square-width 2 zoom))
|
||||||
:rx (/ gradient-square-radius zoom)
|
:rx (/ gradient-square-radius zoom)
|
||||||
:width (/ gradient-square-width zoom)
|
:width (/ gradient-square-width zoom)
|
||||||
:height (/ gradient-square-width zoom)
|
:height (/ gradient-square-width zoom)
|
||||||
:stroke "white"
|
:stroke (if selected "#31EFB8" "white")
|
||||||
:stroke-width (/ gradient-square-stroke-width zoom)
|
:stroke-width (/ gradient-square-stroke-width zoom)
|
||||||
:fill (:value color)
|
:fill (:value color)
|
||||||
:fill-opacity (:opacity color)
|
:fill-opacity (:opacity color)
|
||||||
|
@ -128,18 +128,27 @@
|
||||||
:on-mouse-up on-mouse-up}]])
|
:on-mouse-up on-mouse-up}]])
|
||||||
|
|
||||||
(mf/defc gradient-handler-transformed
|
(mf/defc gradient-handler-transformed
|
||||||
[{:keys [from-p to-p width-p from-color to-color zoom on-change-start on-change-finish on-change-width on-change-stop-color]}]
|
[{:keys [from-p to-p width-p from-color to-color zoom editing
|
||||||
|
on-change-start on-change-finish on-change-width on-change-stop-color]}]
|
||||||
(let [moving-point (mf/use-var nil)
|
(let [moving-point (mf/use-var nil)
|
||||||
angle (+ 90 (gpt/angle from-p to-p))
|
angle (+ 90 (gpt/angle from-p to-p))
|
||||||
|
|
||||||
on-click (fn [position event]
|
on-click (fn [position event]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(dom/prevent-default event))
|
(dom/prevent-default event)
|
||||||
|
(when (#{:from-p :to-p} position)
|
||||||
|
(st/emit! (dc/select-gradient-stop (case position
|
||||||
|
:from-p 0
|
||||||
|
:to-p 1)))))
|
||||||
|
|
||||||
on-mouse-down (fn [position event]
|
on-mouse-down (fn [position event]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(reset! moving-point position))
|
(reset! moving-point position)
|
||||||
|
(when (#{:from-p :to-p} position)
|
||||||
|
(st/emit! (dc/select-gradient-stop (case position
|
||||||
|
:from-p 0
|
||||||
|
:to-p 1)))))
|
||||||
|
|
||||||
on-mouse-up (fn [position event]
|
on-mouse-up (fn [position event]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
|
@ -192,7 +201,8 @@
|
||||||
|
|
||||||
(when width-p
|
(when width-p
|
||||||
[:g {:filter "url(#gradient_width_handler_drop_shadow)"}
|
[:g {:filter "url(#gradient_width_handler_drop_shadow)"}
|
||||||
[:circle {:cx (:x width-p)
|
[:circle {:data-allow-click-modal "colorpicker"
|
||||||
|
:cx (:x width-p)
|
||||||
:cy (:y width-p)
|
:cy (:y width-p)
|
||||||
:r (/ gradient-width-handler-radius zoom)
|
:r (/ gradient-width-handler-radius zoom)
|
||||||
:fill gradient-width-handler-color
|
:fill gradient-width-handler-color
|
||||||
|
@ -200,7 +210,8 @@
|
||||||
:on-mouse-up (partial on-mouse-up :width-p)}]])
|
:on-mouse-up (partial on-mouse-up :width-p)}]])
|
||||||
|
|
||||||
[:& gradient-color-handler
|
[:& gradient-color-handler
|
||||||
{:filter-id "gradient_square_from_drop_shadow"
|
{:selected (or (not editing) (= editing 0))
|
||||||
|
:filter-id "gradient_square_from_drop_shadow"
|
||||||
:zoom zoom
|
:zoom zoom
|
||||||
:point from-p
|
:point from-p
|
||||||
:color from-color
|
:color from-color
|
||||||
|
@ -210,7 +221,8 @@
|
||||||
:on-mouse-up (partial on-mouse-up :from-p)}]
|
:on-mouse-up (partial on-mouse-up :from-p)}]
|
||||||
|
|
||||||
[:& gradient-color-handler
|
[:& gradient-color-handler
|
||||||
{:filter-id "gradient_square_to_drop_shadow"
|
{:selected (= editing 1)
|
||||||
|
:filter-id "gradient_square_to_drop_shadow"
|
||||||
:zoom zoom
|
:zoom zoom
|
||||||
:point to-p
|
:point to-p
|
||||||
:color to-color
|
:color to-color
|
||||||
|
@ -219,74 +231,70 @@
|
||||||
:on-mouse-down (partial on-mouse-down :to-p)
|
:on-mouse-down (partial on-mouse-down :to-p)
|
||||||
:on-mouse-up (partial on-mouse-up :to-p)}]]))
|
:on-mouse-up (partial on-mouse-up :to-p)}]]))
|
||||||
|
|
||||||
(mf/defc gradient-handlers
|
|
||||||
[{:keys [shape zoom]}]
|
|
||||||
(let [{:keys [x y width height] :as sr} (:selrect shape)
|
|
||||||
|
|
||||||
state (mf/use-state (:fill-color-gradient shape default-gradient))
|
(mf/defc gradient-handlers
|
||||||
|
[{:keys [id zoom]}]
|
||||||
|
(let [shape (mf/deref (refs/object-by-id id))
|
||||||
|
gradient (mf/deref current-gradient-ref)
|
||||||
|
editing-spot (mf/deref editing-spot-ref)
|
||||||
|
|
||||||
|
{:keys [x y width height] :as sr} (:selrect shape)
|
||||||
|
|
||||||
[{start-color :color start-opacity :opacity}
|
[{start-color :color start-opacity :opacity}
|
||||||
{end-color :color end-opacity :opacity}] (:stops @state)
|
{end-color :color end-opacity :opacity}] (:stops gradient)
|
||||||
|
|
||||||
from-p (gpt/point (+ x (* width (:start-x @state)))
|
from-p (gpt/point (+ x (* width (:start-x gradient)))
|
||||||
(+ y (* height (:start-y @state))))
|
(+ y (* height (:start-y gradient))))
|
||||||
|
|
||||||
to-p (gpt/point (+ x (* width (:end-x @state)))
|
to-p (gpt/point (+ x (* width (:end-x gradient)))
|
||||||
(+ y (* height (:end-y @state))))
|
(+ y (* height (:end-y gradient))))
|
||||||
|
|
||||||
gradient-vec (gpt/to-vec from-p to-p)
|
gradient-vec (gpt/to-vec from-p to-p)
|
||||||
gradient-length (gpt/length gradient-vec)
|
gradient-length (gpt/length gradient-vec)
|
||||||
|
|
||||||
width-v (-> gradient-vec
|
width-v (-> gradient-vec
|
||||||
(gpt/normal-left)
|
(gpt/normal-left)
|
||||||
(gpt/multiply (gpt/point (* (:width @state) (/ gradient-length (/ height 2) ))))
|
(gpt/multiply (gpt/point (* (:width gradient) (/ gradient-length (/ height 2) ))))
|
||||||
(gpt/multiply (gpt/point (/ width 2))))
|
(gpt/multiply (gpt/point (/ width 2))))
|
||||||
|
|
||||||
width-p (gpt/add from-p width-v)
|
width-p (gpt/add from-p width-v)
|
||||||
|
|
||||||
|
change! (fn [changes]
|
||||||
|
(st/emit! (dc/update-gradient changes)))
|
||||||
|
|
||||||
on-change-start (fn [point]
|
on-change-start (fn [point]
|
||||||
(let [start-x (/ (- (:x point) x) width)
|
(let [start-x (/ (- (:x point) x) width)
|
||||||
start-y (/ (- (:y point) y) height)]
|
start-y (/ (- (:y point) y) height)
|
||||||
(swap! state assoc
|
start-x (mth/precision start-x 2)
|
||||||
:start-x start-x
|
start-y (mth/precision start-y 2)]
|
||||||
:start-y start-y )))
|
(change! {:start-x start-x :start-y start-y})))
|
||||||
|
|
||||||
on-change-finish (fn [point]
|
on-change-finish (fn [point]
|
||||||
(let [end-x (/ (- (:x point) x) width)
|
(let [end-x (/ (- (:x point) x) width)
|
||||||
end-y (/ (- (:y point) y) height)]
|
end-y (/ (- (:y point) y) height)
|
||||||
(swap! state assoc
|
|
||||||
:end-x end-x
|
end-x (mth/precision end-x 2)
|
||||||
:end-y end-y)))
|
end-y (mth/precision end-y 2)]
|
||||||
|
(change! {:end-x end-x :end-y end-y})))
|
||||||
|
|
||||||
on-change-width (fn [point]
|
on-change-width (fn [point]
|
||||||
(let [scale-factor-y (/ gradient-length (/ height 2))
|
(let [scale-factor-y (/ gradient-length (/ height 2))
|
||||||
norm-dist (/ (gpt/distance point from-p)
|
norm-dist (/ (gpt/distance point from-p)
|
||||||
(* (/ width 2) scale-factor-y))]
|
(* (/ width 2) scale-factor-y))]
|
||||||
(swap! state assoc :width norm-dist)))
|
|
||||||
|
|
||||||
on-change-stop-color (fn [offset color opacity] (println "change-color"))]
|
(change! {:width norm-dist})))]
|
||||||
|
|
||||||
(mf/use-effect
|
(when (and gradient
|
||||||
(mf/deps shape)
|
(= id (:shape-id gradient))
|
||||||
(fn []
|
(not= (:type shape) :text))
|
||||||
(reset! state (:fill-color-gradient shape default-gradient))))
|
[:& gradient-handler-transformed
|
||||||
|
{:editing editing-spot
|
||||||
(mf/use-effect
|
:from-p from-p
|
||||||
(mf/deps @state)
|
:to-p to-p
|
||||||
(fn []
|
:width-p (when (= :radial (:type gradient)) width-p)
|
||||||
(when (not= (:fill-color-gradient shape) @state)
|
:from-color {:value start-color :opacity start-opacity}
|
||||||
(st/emit! (dwc/update-shapes
|
:to-color {:value end-color :opacity end-opacity}
|
||||||
[(:id shape)]
|
:zoom zoom
|
||||||
#(assoc % :fill-color-gradient @state))))))
|
:on-change-start on-change-start
|
||||||
|
:on-change-finish on-change-finish
|
||||||
[:& gradient-handler-transformed
|
:on-change-width on-change-width}])))
|
||||||
{:from-p from-p
|
|
||||||
:to-p to-p
|
|
||||||
:width-p (when (= :radial (:type @state)) width-p)
|
|
||||||
:from-color {:value start-color :opacity start-opacity}
|
|
||||||
:to-color {:value end-color :opacity end-opacity}
|
|
||||||
:zoom zoom
|
|
||||||
:on-change-start on-change-start
|
|
||||||
:on-change-finish on-change-finish
|
|
||||||
:on-change-width on-change-width
|
|
||||||
:on-change-stop-color on-change-stop-color}]))
|
|
||||||
|
|
|
@ -28,8 +28,7 @@
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
[app.util.debug :refer [debug?]]
|
[app.util.debug :refer [debug?]]
|
||||||
[app.main.ui.workspace.shapes.outline :refer [outline]]
|
[app.main.ui.workspace.shapes.outline :refer [outline]]))
|
||||||
[app.main.ui.workspace.gradients :refer [gradient-handlers]]))
|
|
||||||
|
|
||||||
(def rotation-handler-size 25)
|
(def rotation-handler-size 25)
|
||||||
(def resize-point-radius 4)
|
(def resize-point-radius 4)
|
||||||
|
@ -210,11 +209,7 @@
|
||||||
(case type
|
(case type
|
||||||
:rotation (when (not= :frame (:type shape)) [:> rotation-handler props])
|
:rotation (when (not= :frame (:type shape)) [:> rotation-handler props])
|
||||||
:resize-point [:> resize-point-handler props]
|
:resize-point [:> resize-point-handler props]
|
||||||
:resize-side [:> resize-side-handler props])))
|
:resize-side [:> resize-side-handler props])))])))
|
||||||
|
|
||||||
#_(when (= :rect (:type shape))
|
|
||||||
[:& gradient-handlers {:shape tr-shape
|
|
||||||
:zoom zoom}])])))
|
|
||||||
|
|
||||||
;; --- Selection Handlers (Component)
|
;; --- Selection Handlers (Component)
|
||||||
(mf/defc path-edition-selection-handlers
|
(mf/defc path-edition-selection-handlers
|
||||||
|
|
|
@ -14,11 +14,11 @@
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.keyboard :as kbd]
|
[app.main.ui.keyboard :as kbd]
|
||||||
[app.main.ui.shapes.filters :as filters]
|
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as geom]))
|
[app.common.geom.shapes :as geom]
|
||||||
|
[app.main.ui.shapes.shape :refer [shape-container]]))
|
||||||
|
|
||||||
(defn- on-mouse-down
|
(defn- on-mouse-down
|
||||||
[event {:keys [id type] :as shape}]
|
[event {:keys [id type] :as shape}]
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
(st/emit! (dw/select-shape id true)))
|
(st/emit! (dw/select-shape id true)))
|
||||||
(do
|
(do
|
||||||
(when-not (or (empty? selected) (kbd/shift? event))
|
(when-not (or (empty? selected) (kbd/shift? event))
|
||||||
(st/emit! dw/deselect-all))
|
(st/emit! (dw/deselect-all)))
|
||||||
(st/emit! (dw/select-shape id))))
|
(st/emit! (dw/select-shape id))))
|
||||||
|
|
||||||
(st/emit! (dw/start-move-selected)))))))
|
(st/emit! (dw/start-move-selected)))))))
|
||||||
|
@ -70,12 +70,11 @@
|
||||||
#(on-mouse-down % shape))
|
#(on-mouse-down % shape))
|
||||||
on-context-menu (mf/use-callback
|
on-context-menu (mf/use-callback
|
||||||
(mf/deps shape)
|
(mf/deps shape)
|
||||||
#(on-context-menu % shape))
|
#(on-context-menu % shape))]
|
||||||
filter-id (mf/use-memo filters/get-filter-id)]
|
|
||||||
[:g.shape {:on-mouse-down on-mouse-down
|
[:> shape-container {:shape shape
|
||||||
:on-context-menu on-context-menu
|
:on-mouse-down on-mouse-down
|
||||||
:filter (filters/filter-str filter-id shape)}
|
:on-context-menu on-context-menu}
|
||||||
[:& filters/filters {:filter-id filter-id :shape shape}]
|
|
||||||
[:& component {:shape shape}]])))
|
[:& component {:shape shape}]])))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,13 +19,13 @@
|
||||||
[app.main.ui.workspace.shapes.common :as common]
|
[app.main.ui.workspace.shapes.common :as common]
|
||||||
[app.main.data.workspace.selection :as dws]
|
[app.main.data.workspace.selection :as dws]
|
||||||
[app.main.ui.shapes.frame :as frame]
|
[app.main.ui.shapes.frame :as frame]
|
||||||
[app.main.ui.shapes.filters :as filters]
|
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.main.streams :as ms]
|
[app.main.streams :as ms]
|
||||||
[app.util.timers :as ts]))
|
[app.util.timers :as ts]
|
||||||
|
[app.main.ui.shapes.shape :refer [shape-container]]))
|
||||||
|
|
||||||
(defn- frame-wrapper-factory-equals?
|
(defn- frame-wrapper-factory-equals?
|
||||||
[np op]
|
[np op]
|
||||||
|
@ -99,7 +99,7 @@
|
||||||
(mf/deps (:id shape))
|
(mf/deps (:id shape))
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(st/emit! dw/deselect-all
|
(st/emit! (dw/deselect-all)
|
||||||
(dw/select-shape (:id shape)))))
|
(dw/select-shape (:id shape)))))
|
||||||
|
|
||||||
on-mouse-over
|
on-mouse-over
|
||||||
|
@ -112,9 +112,7 @@
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps (:id shape))
|
(mf/deps (:id shape))
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit! (dws/change-hover-state (:id shape) false))))
|
(st/emit! (dws/change-hover-state (:id shape) false))))]
|
||||||
|
|
||||||
filter-id (mf/use-memo filters/get-filter-id)]
|
|
||||||
|
|
||||||
(when-not (:hidden shape)
|
(when-not (:hidden shape)
|
||||||
[:g {:class (when selected? "selected")
|
[:g {:class (when selected? "selected")
|
||||||
|
@ -126,8 +124,8 @@
|
||||||
:on-context-menu on-context-menu
|
:on-context-menu on-context-menu
|
||||||
:on-double-click on-double-click
|
:on-double-click on-double-click
|
||||||
:on-mouse-down on-mouse-down}]
|
:on-mouse-down on-mouse-down}]
|
||||||
[:g.frame {:filter (filters/filter-str filter-id shape)}
|
|
||||||
[:& filters/filters {:filter-id filter-id :shape shape}]
|
[:> shape-container {:shape shape}
|
||||||
[:& frame-shape
|
[:& frame-shape
|
||||||
{:shape shape
|
{:shape shape
|
||||||
:childs children}]]])))))
|
:childs children}]]])))))
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
(do
|
(do
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(when-not (empty? selected)
|
(when-not (empty? selected)
|
||||||
(st/emit! dw/deselect-all))
|
(st/emit! (dw/deselect-all)))
|
||||||
(st/emit! (dw/select-shape id))
|
(st/emit! (dw/select-shape id))
|
||||||
(st/emit! (dw/start-create-interaction))))
|
(st/emit! (dw/start-create-interaction))))
|
||||||
|
|
||||||
|
|
|
@ -11,18 +11,19 @@
|
||||||
(:require
|
(:require
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.util.timers :as ts]
|
||||||
|
[app.main.streams :as ms]
|
||||||
[app.main.constants :as c]
|
[app.main.constants :as c]
|
||||||
[app.main.data.workspace :as dw]
|
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
[app.main.data.workspace :as dw]
|
||||||
|
[app.main.data.workspace.drawing :as dr]
|
||||||
[app.main.ui.keyboard :as kbd]
|
[app.main.ui.keyboard :as kbd]
|
||||||
[app.main.ui.shapes.path :as path]
|
[app.main.ui.shapes.path :as path]
|
||||||
[app.main.ui.shapes.filters :as filters]
|
[app.main.ui.shapes.filters :as filters]
|
||||||
[app.main.ui.workspace.shapes.common :as common]
|
[app.main.ui.shapes.shape :refer [shape-container]]
|
||||||
[app.main.data.workspace.drawing :as dr]
|
[app.main.ui.workspace.shapes.common :as common]))
|
||||||
[app.util.dom :as dom]
|
|
||||||
[app.main.streams :as ms]
|
|
||||||
[app.util.timers :as ts]))
|
|
||||||
|
|
||||||
(mf/defc path-wrapper
|
(mf/defc path-wrapper
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
|
@ -42,13 +43,13 @@
|
||||||
(do
|
(do
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(st/emit! (dw/start-edition-mode (:id shape)))))))
|
(st/emit! (dw/start-edition-mode (:id shape)))))))]
|
||||||
filter-id (mf/use-memo filters/get-filter-id)]
|
|
||||||
|
|
||||||
[:g.shape {:on-double-click on-double-click
|
[:> shape-container {:shape shape
|
||||||
:on-mouse-down on-mouse-down
|
:on-double-click on-double-click
|
||||||
:on-context-menu on-context-menu
|
:on-mouse-down on-mouse-down
|
||||||
:filter (filters/filter-str filter-id shape)}
|
:on-context-menu on-context-menu}
|
||||||
[:& filters/filters {:filter-id filter-id :shape shape}]
|
|
||||||
[:& path/path-shape {:shape shape :background? true}]]))
|
[:& path/path-shape {:shape shape
|
||||||
|
:background? true}]]))
|
||||||
|
|
||||||
|
|
|
@ -9,32 +9,34 @@
|
||||||
|
|
||||||
(ns app.main.ui.workspace.shapes.text
|
(ns app.main.ui.workspace.shapes.text
|
||||||
(:require
|
(:require
|
||||||
[cuerdas.core :as str]
|
["slate" :as slate]
|
||||||
|
["slate-react" :as rslate]
|
||||||
[goog.events :as events]
|
[goog.events :as events]
|
||||||
[goog.object :as gobj]
|
[goog.object :as gobj]
|
||||||
|
[cuerdas.core :as str]
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
|
[beicon.core :as rx]
|
||||||
|
[app.util.color :as color]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.util.text :as ut]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[app.util.color :as uc]
|
||||||
|
[app.util.timers :as timers]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.common.geom.shapes :as geom]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.fonts :as fonts]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.common :as dwc]
|
||||||
[app.main.data.workspace.texts :as dwt]
|
[app.main.data.workspace.texts :as dwt]
|
||||||
[app.main.refs :as refs]
|
|
||||||
[app.main.store :as st]
|
|
||||||
[app.main.ui.cursors :as cur]
|
[app.main.ui.cursors :as cur]
|
||||||
[app.main.ui.workspace.shapes.common :as common]
|
[app.main.ui.workspace.shapes.common :as common]
|
||||||
[app.main.ui.shapes.text :as text]
|
[app.main.ui.shapes.text :as text]
|
||||||
[app.main.ui.keyboard :as kbd]
|
[app.main.ui.keyboard :as kbd]
|
||||||
[app.main.ui.context :as muc]
|
[app.main.ui.context :as muc]
|
||||||
[app.main.ui.shapes.filters :as filters]
|
[app.main.ui.shapes.filters :as filters]
|
||||||
[app.main.fonts :as fonts]
|
[app.main.ui.shapes.shape :refer [shape-container]])
|
||||||
[app.util.color :as color]
|
|
||||||
[app.util.dom :as dom]
|
|
||||||
[app.util.text :as ut]
|
|
||||||
[app.common.geom.shapes :as geom]
|
|
||||||
[app.util.object :as obj]
|
|
||||||
[app.util.color :as uc]
|
|
||||||
[app.util.timers :as timers]
|
|
||||||
["slate" :as slate]
|
|
||||||
["slate-react" :as rslate])
|
|
||||||
(:import
|
(:import
|
||||||
goog.events.EventType
|
goog.events.EventType
|
||||||
goog.events.KeyCodes))
|
goog.events.KeyCodes))
|
||||||
|
@ -78,9 +80,7 @@
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(when selected?
|
(when selected?
|
||||||
(st/emit! (dw/start-edition-mode (:id shape)))))
|
(st/emit! (dw/start-edition-mode (:id shape)))))]
|
||||||
|
|
||||||
filter-id (mf/use-memo filters/get-filter-id)]
|
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(mf/deps shape edition selected? current-transform)
|
(mf/deps shape edition selected? current-transform)
|
||||||
|
@ -88,26 +88,25 @@
|
||||||
selected?
|
selected?
|
||||||
(not edition?)
|
(not edition?)
|
||||||
(not embed-resources?)
|
(not embed-resources?)
|
||||||
(nil? current-transform))]
|
(nil? current-transform))
|
||||||
(timers/schedule #(reset! render-editor check?)))))
|
result (timers/schedule #(reset! render-editor check?))]
|
||||||
|
#(rx/dispose! result))))
|
||||||
|
|
||||||
[:g.shape {:on-double-click on-double-click
|
[:> shape-container {:shape shape
|
||||||
:on-mouse-down on-mouse-down
|
:on-double-click on-double-click
|
||||||
:on-context-menu on-context-menu
|
:on-mouse-down on-mouse-down
|
||||||
:filter (filters/filter-str filter-id shape)}
|
:on-context-menu on-context-menu}
|
||||||
[:& filters/filters {:filter-id filter-id :shape shape}]
|
(when @render-editor
|
||||||
[:*
|
[:g {:opacity 0
|
||||||
(when @render-editor
|
:style {:pointer-events "none"}}
|
||||||
[:g {:opacity 0
|
;; We only render the component for its side-effect
|
||||||
:style {:pointer-events "none"}}
|
[:& text-shape-edit {:shape shape
|
||||||
;; We only render the component for its side-effect
|
:read-only? true}]])
|
||||||
[:& text-shape-edit {:shape shape
|
|
||||||
:read-only? true}]])
|
|
||||||
|
|
||||||
(if edition?
|
(if edition?
|
||||||
[:& text-shape-edit {:shape shape}]
|
[:& text-shape-edit {:shape shape}]
|
||||||
[:& text/text-shape {:shape shape
|
[:& text/text-shape {:shape shape
|
||||||
:selected? selected?}])]]))
|
:selected? selected?}])]))
|
||||||
|
|
||||||
;; --- Text Editor Rendering
|
;; --- Text Editor Rendering
|
||||||
|
|
||||||
|
@ -158,17 +157,25 @@
|
||||||
|
|
||||||
fill-color (obj/get data "fill-color" fill)
|
fill-color (obj/get data "fill-color" fill)
|
||||||
fill-opacity (obj/get data "fill-opacity" opacity)
|
fill-opacity (obj/get data "fill-opacity" opacity)
|
||||||
|
fill-color-gradient (obj/get data "fill-color-gradient" nil)
|
||||||
|
fill-color-gradient (when fill-color-gradient
|
||||||
|
(-> (js->clj fill-color-gradient :keywordize-keys true)
|
||||||
|
(update :type keyword)))
|
||||||
|
|
||||||
fill-color-ref-id (obj/get data "fill-color-ref-id")
|
fill-color-ref-id (obj/get data "fill-color-ref-id")
|
||||||
fill-color-ref-file (obj/get data "fill-color-ref-file")
|
fill-color-ref-file (obj/get data "fill-color-ref-file")
|
||||||
|
|
||||||
[r g b a] (uc/hex->rgba fill-color fill-opacity)
|
[r g b a] (uc/hex->rgba fill-color fill-opacity)
|
||||||
|
background (if fill-color-gradient
|
||||||
|
(uc/gradient->css (js->clj fill-color-gradient))
|
||||||
|
(str/format "rgba(%s, %s, %s, %s)" r g b a))
|
||||||
|
|
||||||
fontsdb (deref fonts/fontsdb)
|
fontsdb (deref fonts/fontsdb)
|
||||||
|
|
||||||
base #js {:textDecoration text-decoration
|
base #js {:textDecoration text-decoration
|
||||||
:color (str/format "rgba(%s, %s, %s, %s)" r g b a)
|
|
||||||
:textTransform text-transform
|
:textTransform text-transform
|
||||||
:lineHeight (or line-height "inherit")}]
|
:lineHeight (or line-height "inherit")
|
||||||
|
"--text-color" background}]
|
||||||
|
|
||||||
(when (and (string? letter-spacing)
|
(when (and (string? letter-spacing)
|
||||||
(pos? (alength letter-spacing)))
|
(pos? (alength letter-spacing)))
|
||||||
|
@ -243,7 +250,9 @@
|
||||||
childs (obj/get props "children")
|
childs (obj/get props "children")
|
||||||
data (obj/get props "leaf")
|
data (obj/get props "leaf")
|
||||||
style (generate-text-styles data)
|
style (generate-text-styles data)
|
||||||
attrs (obj/set! attrs "style" style)]
|
attrs (-> attrs
|
||||||
|
(obj/set! "style" style)
|
||||||
|
(obj/set! "className" "text-node"))]
|
||||||
[:> :span attrs childs]))
|
[:> :span attrs childs]))
|
||||||
|
|
||||||
(defn- render-element
|
(defn- render-element
|
||||||
|
@ -284,6 +293,14 @@
|
||||||
children-count (->> node :children (map content-size) (reduce +))]
|
children-count (->> node :children (map content-size) (reduce +))]
|
||||||
(+ current children-count)))
|
(+ current children-count)))
|
||||||
|
|
||||||
|
(defn fix-gradients
|
||||||
|
"Fix for the gradient types that need to be keywords"
|
||||||
|
[content]
|
||||||
|
(let [fix-node
|
||||||
|
(fn [node]
|
||||||
|
(d/update-in-when node [:fill-color-gradient :type] keyword))]
|
||||||
|
(ut/map-node fix-node content)))
|
||||||
|
|
||||||
(mf/defc text-shape-edit
|
(mf/defc text-shape-edit
|
||||||
{::mf/wrap [mf/memo]}
|
{::mf/wrap [mf/memo]}
|
||||||
[{:keys [shape read-only?] :or {read-only? false} :as props}]
|
[{:keys [shape read-only?] :or {read-only? false} :as props}]
|
||||||
|
@ -364,7 +381,8 @@
|
||||||
(fn [val]
|
(fn [val]
|
||||||
(when (not read-only?)
|
(when (not read-only?)
|
||||||
(let [content (js->clj val :keywordize-keys true)
|
(let [content (js->clj val :keywordize-keys true)
|
||||||
content (first content)]
|
content (first content)
|
||||||
|
content (fix-gradients content)]
|
||||||
;; Append timestamp so we can react to cursor change events
|
;; Append timestamp so we can react to cursor change events
|
||||||
(st/emit! (dw/update-shape id {:content (assoc content :ts (js->clj (.now js/Date)))}))
|
(st/emit! (dw/update-shape id {:content (assoc content :ts (js->clj (.now js/Date)))}))
|
||||||
(reset! state val)
|
(reset! state val)
|
||||||
|
@ -419,7 +437,8 @@
|
||||||
:x x :y y
|
:x x :y y
|
||||||
:width (if (= :auto-width grow-type) 10000 width)
|
:width (if (= :auto-width grow-type) 10000 width)
|
||||||
:height height}
|
:height height}
|
||||||
[:style "span { line-height: inherit; }"]
|
[:style "span { line-height: inherit; }
|
||||||
|
.text-node { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"]
|
||||||
[:> rslate/Slate {:editor editor
|
[:> rslate/Slate {:editor editor
|
||||||
:value @state
|
:value @state
|
||||||
:on-change on-change}
|
:on-change on-change}
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
||||||
[app.main.ui.components.tab-container :refer [tab-container tab-element]]
|
[app.main.ui.components.tab-container :refer [tab-container tab-element]]
|
||||||
[app.main.ui.workspace.sidebar.options.typography :refer [typography-entry]]
|
[app.main.ui.workspace.sidebar.options.typography :refer [typography-entry]]
|
||||||
|
[app.main.ui.components.color-bullet :as bc]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.main.ui.keyboard :as kbd]
|
[app.main.ui.keyboard :as kbd]
|
||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
|
@ -190,7 +191,7 @@
|
||||||
:options [[(tr "workspace.assets.delete") on-delete]]}])])]))
|
:options [[(tr "workspace.assets.delete") on-delete]]}])])]))
|
||||||
|
|
||||||
(mf/defc color-item
|
(mf/defc color-item
|
||||||
[{:keys [color local? locale file-id] :as props}]
|
[{:keys [color local? locale] :as props}]
|
||||||
(let [rename? (= (:color-for-rename @refs/workspace-local) (:id color))
|
(let [rename? (= (:color-for-rename @refs/workspace-local) (:id color))
|
||||||
id (:id color)
|
id (:id color)
|
||||||
input-ref (mf/use-ref)
|
input-ref (mf/use-ref)
|
||||||
|
@ -198,20 +199,27 @@
|
||||||
:top nil
|
:top nil
|
||||||
:left nil
|
:left nil
|
||||||
:editing rename?})
|
:editing rename?})
|
||||||
|
|
||||||
|
default-name (cond
|
||||||
|
(:gradient color) (bc/gradient-type->string (get-in color [:gradient :type]))
|
||||||
|
(:color color) (:color color)
|
||||||
|
:else (:value color))
|
||||||
|
|
||||||
click-color
|
click-color
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(let [ids (get-in @st/state [:workspace-local :selected])]
|
(let [ids (get-in @st/state [:workspace-local :selected])]
|
||||||
(if (kbd/shift? event)
|
(if (kbd/shift? event)
|
||||||
(st/emit! (dc/change-stroke ids (:value color) id (if local? nil file-id)))
|
(st/emit! (dc/change-stroke ids color))
|
||||||
(st/emit! (dc/change-fill ids (:value color) id (if local? nil file-id))))))
|
(st/emit! (dc/change-fill ids color)))))
|
||||||
|
|
||||||
rename-color
|
rename-color
|
||||||
(fn [name]
|
(fn [name]
|
||||||
(st/emit! (dwl/update-color (assoc color :name name))))
|
(st/emit! (dwl/update-color (assoc color :name name))))
|
||||||
|
|
||||||
edit-color
|
edit-color
|
||||||
(fn [value]
|
(fn [new-color]
|
||||||
(st/emit! (dwl/update-color (assoc color :value value))))
|
(let [updated-color (merge new-color (select-keys color [:id :file-id :name]))]
|
||||||
|
(st/emit! (dwl/update-color updated-color))))
|
||||||
|
|
||||||
delete-color
|
delete-color
|
||||||
(fn []
|
(fn []
|
||||||
|
@ -245,8 +253,7 @@
|
||||||
{:x (.-clientX event)
|
{:x (.-clientX event)
|
||||||
:y (.-clientY event)
|
:y (.-clientY event)
|
||||||
:on-accept edit-color
|
:on-accept edit-color
|
||||||
:value (:value color)
|
:data color
|
||||||
:disable-opacity true
|
|
||||||
:position :right}))
|
:position :right}))
|
||||||
|
|
||||||
on-context-menu
|
on-context-menu
|
||||||
|
@ -269,7 +276,8 @@
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
[:div.group-list-item {:on-context-menu on-context-menu}
|
[:div.group-list-item {:on-context-menu on-context-menu}
|
||||||
[:div.color-block {:style {:background-color (:value color)}}]
|
[:& bc/color-bullet {:color color}]
|
||||||
|
|
||||||
(if (:editing @state)
|
(if (:editing @state)
|
||||||
[:input.element-name
|
[:input.element-name
|
||||||
{:type "text"
|
{:type "text"
|
||||||
|
@ -278,12 +286,13 @@
|
||||||
:on-key-down input-key-down
|
:on-key-down input-key-down
|
||||||
:auto-focus true
|
:auto-focus true
|
||||||
:default-value (:name color "")}]
|
:default-value (:name color "")}]
|
||||||
|
|
||||||
[:div.name-block
|
[:div.name-block
|
||||||
{:on-double-click rename-color-clicked
|
{:on-double-click rename-color-clicked
|
||||||
:on-click click-color}
|
:on-click click-color}
|
||||||
(:name color)
|
(:name color)
|
||||||
(when-not (= (:name color) (:value color))
|
(when-not (= (:name color) default-name)
|
||||||
[:span (:value color)])])
|
[:span default-name])])
|
||||||
(when local?
|
(when local?
|
||||||
[:& context-menu
|
[:& context-menu
|
||||||
{:selectable false
|
{:selectable false
|
||||||
|
@ -312,8 +321,8 @@
|
||||||
{:x (.-clientX event)
|
{:x (.-clientX event)
|
||||||
:y (.-clientY event)
|
:y (.-clientY event)
|
||||||
:on-accept add-color
|
:on-accept add-color
|
||||||
:value "#406280"
|
:data {:color "#406280"
|
||||||
:disable-opacity true
|
:opacity 1}
|
||||||
:position :right})))]
|
:position :right})))]
|
||||||
[:div.asset-group
|
[:div.asset-group
|
||||||
[:div.group-title {:class (when (not open?) "closed")}
|
[:div.group-title {:class (when (not open?) "closed")}
|
||||||
|
@ -324,11 +333,14 @@
|
||||||
(when open?
|
(when open?
|
||||||
[:div.group-list
|
[:div.group-list
|
||||||
(for [color colors]
|
(for [color colors]
|
||||||
[:& color-item {:key (:id color)
|
(let [color (cond-> color
|
||||||
:color color
|
(:value color) (assoc :color (:value color) :opacity 1)
|
||||||
:file-id file-id
|
(:value color) (dissoc :value)
|
||||||
:local? local?
|
true (assoc :file-id file-id))]
|
||||||
:locale locale}])])]))
|
[:& color-item {:key (:id color)
|
||||||
|
:color color
|
||||||
|
:local? local?
|
||||||
|
:locale locale}]))])]))
|
||||||
|
|
||||||
(mf/defc typography-box
|
(mf/defc typography-box
|
||||||
[{:keys [file file-id local? typographies locale open? on-open on-close] :as props}]
|
[{:keys [file file-id local? typographies locale open? on-open on-close] :as props}]
|
||||||
|
|
|
@ -143,10 +143,10 @@
|
||||||
(st/emit! (dw/select-shape id true))
|
(st/emit! (dw/select-shape id true))
|
||||||
|
|
||||||
(> (count selected) 1)
|
(> (count selected) 1)
|
||||||
(st/emit! dw/deselect-all
|
(st/emit! (dw/deselect-all)
|
||||||
(dw/select-shape id))
|
(dw/select-shape id))
|
||||||
:else
|
:else
|
||||||
(st/emit! dw/deselect-all
|
(st/emit! (dw/deselect-all)
|
||||||
(dw/select-shape id)))))
|
(dw/select-shape id)))))
|
||||||
|
|
||||||
on-context-menu
|
on-context-menu
|
||||||
|
@ -160,7 +160,7 @@
|
||||||
on-drag
|
on-drag
|
||||||
(fn [{:keys [id]}]
|
(fn [{:keys [id]}]
|
||||||
(when (not (contains? selected id))
|
(when (not (contains? selected id))
|
||||||
(st/emit! dw/deselect-all
|
(st/emit! (dw/deselect-all)
|
||||||
(dw/select-shape id))))
|
(dw/select-shape id))))
|
||||||
|
|
||||||
on-drop
|
on-drop
|
||||||
|
|
|
@ -40,15 +40,15 @@
|
||||||
[{:keys [shape shapes-with-children page-id file-id]}]
|
[{:keys [shape shapes-with-children page-id file-id]}]
|
||||||
[:*
|
[:*
|
||||||
(case (:type shape)
|
(case (:type shape)
|
||||||
:frame [:& frame/options {:shape shape}]
|
:frame [:& frame/options {:shape shape}]
|
||||||
:group [:& group/options {:shape shape :shape-with-children shapes-with-children}]
|
:group [:& group/options {:shape shape :shape-with-children shapes-with-children}]
|
||||||
:text [:& text/options {:shape shape}]
|
:text [:& text/options {:shape shape}]
|
||||||
:rect [:& rect/options {:shape shape}]
|
:rect [:& rect/options {:shape shape}]
|
||||||
:icon [:& icon/options {:shape shape}]
|
:icon [:& icon/options {:shape shape}]
|
||||||
:circle [:& circle/options {:shape shape}]
|
:circle [:& circle/options {:shape shape}]
|
||||||
:path [:& path/options {:shape shape}]
|
:path [:& path/options {:shape shape}]
|
||||||
:curve [:& path/options {:shape shape}]
|
:curve [:& path/options {:shape shape}]
|
||||||
:image [:& image/options {:shape shape}]
|
:image [:& image/options {:shape shape}]
|
||||||
nil)
|
nil)
|
||||||
[:& exports-menu
|
[:& exports-menu
|
||||||
{:shape shape
|
{:shape shape
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
[app.util.i18n :as i18n :refer [tr t]]
|
[app.util.i18n :as i18n :refer [tr t]]
|
||||||
[app.util.object :as obj]))
|
[app.util.object :as obj]))
|
||||||
|
|
||||||
(def fill-attrs [:fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file])
|
(def fill-attrs [:fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill-color-gradient])
|
||||||
|
|
||||||
(defn- fill-menu-props-equals?
|
(defn- fill-menu-props-equals?
|
||||||
[np op]
|
[np op]
|
||||||
|
@ -36,42 +36,47 @@
|
||||||
(= (:fill-color new-values)
|
(= (:fill-color new-values)
|
||||||
(:fill-color old-values))
|
(:fill-color old-values))
|
||||||
(= (:fill-opacity new-values)
|
(= (:fill-opacity new-values)
|
||||||
(:fill-opacity old-values)))))
|
(:fill-opacity old-values))
|
||||||
|
(= (:fill-color-gradient new-values)
|
||||||
|
(:fill-color-gradient old-values)))))
|
||||||
|
|
||||||
|
|
||||||
(mf/defc fill-menu
|
(mf/defc fill-menu
|
||||||
{::mf/wrap [#(mf/memo' % fill-menu-props-equals?)]}
|
{::mf/wrap [#(mf/memo' % fill-menu-props-equals?)]}
|
||||||
[{:keys [ids type values editor] :as props}]
|
[{:keys [ids type values editor] :as props}]
|
||||||
(let [locale (mf/deref i18n/locale)
|
(let [locale (mf/deref i18n/locale)
|
||||||
show? (not (nil? (:fill-color values)))
|
show? (or (not (nil? (:fill-color values)))
|
||||||
|
(not (nil? (:fill-color-gradient values))))
|
||||||
|
|
||||||
label (case type
|
label (case type
|
||||||
:multiple (t locale "workspace.options.selection-fill")
|
:multiple (t locale "workspace.options.selection-fill")
|
||||||
:group (t locale "workspace.options.group-fill")
|
:group (t locale "workspace.options.group-fill")
|
||||||
(t locale "workspace.options.fill"))
|
(t locale "workspace.options.fill"))
|
||||||
|
|
||||||
color {:value (:fill-color values)
|
color {:color (:fill-color values)
|
||||||
:opacity (:fill-opacity values)
|
:opacity (:fill-opacity values)
|
||||||
:id (:fill-color-ref-id values)
|
:id (:fill-color-ref-id values)
|
||||||
:file-id (:fill-color-ref-file values)}
|
:file-id (:fill-color-ref-file values)
|
||||||
|
:gradient (:fill-color-gradient values)}
|
||||||
|
|
||||||
on-add
|
on-add
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps ids)
|
(mf/deps ids)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(st/emit! (dc/change-fill ids cp/default-color nil nil))))
|
(st/emit! (dc/change-fill ids {:color cp/default-color
|
||||||
|
:opacity 1}))))
|
||||||
|
|
||||||
on-delete
|
on-delete
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps ids)
|
(mf/deps ids)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(st/emit! (dc/change-fill ids nil nil nil))))
|
(st/emit! (dc/change-fill ids nil))))
|
||||||
|
|
||||||
on-change
|
on-change
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps ids)
|
(mf/deps ids)
|
||||||
(fn [value opacity id file-id]
|
(fn [color]
|
||||||
(st/emit! (dc/change-fill ids value opacity id file-id))))
|
(st/emit! (dc/change-fill ids color))))
|
||||||
|
|
||||||
on-open-picker
|
on-open-picker
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
|
|
|
@ -101,14 +101,17 @@
|
||||||
(assoc-in [:params :item-length] item-length)))))
|
(assoc-in [:params :item-length] item-length)))))
|
||||||
|
|
||||||
handle-change-color
|
handle-change-color
|
||||||
(fn [value opacity]
|
(fn [color]
|
||||||
(emit-changes! #(-> %
|
(emit-changes! #(-> % (assoc-in [:params :color] color))))
|
||||||
(assoc-in [:params :color :value] value)
|
|
||||||
(assoc-in [:params :color :opacity] opacity))))
|
|
||||||
|
|
||||||
handle-use-default
|
handle-use-default
|
||||||
(fn []
|
(fn []
|
||||||
(emit-changes! #(hash-map :params ((:type grid) default-grid-params))))
|
(let [params ((:type grid) default-grid-params)
|
||||||
|
color (or (get-in params [:color :value]) (get-in params [:color :color]))
|
||||||
|
params (-> params
|
||||||
|
(assoc-in [:color :color] color)
|
||||||
|
(update :color dissoc :value))]
|
||||||
|
(emit-changes! #(hash-map :params params))))
|
||||||
|
|
||||||
handle-set-as-default
|
handle-set-as-default
|
||||||
(fn []
|
(fn []
|
||||||
|
@ -214,6 +217,7 @@
|
||||||
:on-change (handle-change :params :margin)}]])
|
:on-change (handle-change :params :margin)}]])
|
||||||
|
|
||||||
[:& color-row {:color (:color params)
|
[:& color-row {:color (:color params)
|
||||||
|
:disable-gradient true
|
||||||
:on-change handle-change-color}]
|
:on-change handle-change-color}]
|
||||||
[:div.row-flex
|
[:div.row-flex
|
||||||
[:button.btn-options {:disabled is-default
|
[:button.btn-options {:disabled is-default
|
||||||
|
|
|
@ -44,8 +44,9 @@
|
||||||
[:div.element-set
|
[:div.element-set
|
||||||
[:div.element-set-title (t locale "workspace.options.canvas-background")]
|
[:div.element-set-title (t locale "workspace.options.canvas-background")]
|
||||||
[:div.element-set-content
|
[:div.element-set-content
|
||||||
[:& color-row {:disable-opacity true
|
[:& color-row {:disable-gradient true
|
||||||
:color {:value (get options :background "#E8E9EA")
|
:disable-opacity true
|
||||||
|
:color {:color (get options :background "#E8E9EA")
|
||||||
:opacity 1}
|
:opacity 1}
|
||||||
:on-change handle-change-color
|
:on-change handle-change-color
|
||||||
:on-open on-open
|
:on-open on-open
|
||||||
|
|
|
@ -10,31 +10,33 @@
|
||||||
(ns app.main.ui.workspace.sidebar.options.rows.color-row
|
(ns app.main.ui.workspace.sidebar.options.rows.color-row
|
||||||
(:require
|
(:require
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
|
[cuerdas.core :as str]
|
||||||
[app.common.math :as math]
|
[app.common.math :as math]
|
||||||
|
[app.common.pages :as cp]
|
||||||
|
[app.common.data :as d]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.data :refer [classnames]]
|
[app.util.data :refer [classnames]]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
|
[app.util.color :as uc]
|
||||||
|
[app.main.refs :as refs]
|
||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
[app.common.data :as d]
|
[app.main.ui.components.color-bullet :as cb]))
|
||||||
[app.main.refs :as refs]))
|
|
||||||
|
|
||||||
(defn color-picker-callback
|
(defn color-picker-callback
|
||||||
[color handle-change-color handle-open handle-close disable-opacity]
|
[color disable-gradient disable-opacity handle-change-color handle-open handle-close]
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(let [x (.-clientX event)
|
(let [x (.-clientX event)
|
||||||
y (.-clientY event)
|
y (.-clientY event)
|
||||||
props {:x x
|
props {:x x
|
||||||
:y y
|
:y y
|
||||||
|
:disable-gradient disable-gradient
|
||||||
|
:disable-opacity disable-opacity
|
||||||
:on-change handle-change-color
|
:on-change handle-change-color
|
||||||
:on-close handle-close
|
:on-close handle-close
|
||||||
:value (:value color)
|
:data color}]
|
||||||
:opacity (:opacity color)
|
|
||||||
:disable-opacity disable-opacity}]
|
|
||||||
(handle-open)
|
(handle-open)
|
||||||
(modal/show! :colorpicker props))))
|
(modal/show! :colorpicker props))))
|
||||||
|
|
||||||
(defn value-to-background [value]
|
|
||||||
(if (= value :multiple) "transparent" value))
|
|
||||||
|
|
||||||
(defn remove-hash [value]
|
(defn remove-hash [value]
|
||||||
(if (or (nil? value) (= value :multiple)) "" (subs value 1)))
|
(if (or (nil? value) (= value :multiple)) "" (subs value 1)))
|
||||||
|
@ -59,38 +61,28 @@
|
||||||
(if (= v :multiple) nil v))
|
(if (= v :multiple) nil v))
|
||||||
|
|
||||||
(mf/defc color-row
|
(mf/defc color-row
|
||||||
[{:keys [color on-change on-open on-close disable-opacity]}]
|
[{:keys [color disable-gradient disable-opacity on-change on-open on-close]}]
|
||||||
(let [;;
|
(let [file-colors (mf/deref refs/workspace-file-colors)
|
||||||
file-colors (mf/deref refs/workspace-file-colors)
|
|
||||||
shared-libs (mf/deref refs/workspace-libraries)
|
shared-libs (mf/deref refs/workspace-libraries)
|
||||||
|
|
||||||
get-color-name (fn [{:keys [id file-id]}]
|
get-color-name (fn [{:keys [id file-id]}]
|
||||||
(let [src-colors (if file-id (get-in shared-libs [file-id :data :colors]) file-colors)]
|
(let [src-colors (if file-id (get-in shared-libs [file-id :data :colors]) file-colors)]
|
||||||
(get-in src-colors [id :name])))
|
(get-in src-colors [id :name])))
|
||||||
|
|
||||||
default-color {:value "#000000" :opacity 1}
|
|
||||||
|
|
||||||
parse-color (fn [color]
|
parse-color (fn [color]
|
||||||
(-> (merge default-color color)
|
(-> color
|
||||||
(update :value #(or % "#000000"))
|
(update :color #(or % (:value color)))))
|
||||||
(update :opacity #(or % 1))))
|
|
||||||
|
|
||||||
state (mf/use-state (parse-color color))
|
|
||||||
|
|
||||||
value (:value @state)
|
|
||||||
opacity (:opacity @state)
|
|
||||||
|
|
||||||
change-value (fn [new-value]
|
change-value (fn [new-value]
|
||||||
(swap! state assoc :value new-value)
|
(when on-change (on-change (-> color
|
||||||
(when on-change (on-change new-value (remove-multiple opacity))))
|
(assoc :color new-value)
|
||||||
|
(dissoc :gradient)))))
|
||||||
|
|
||||||
change-opacity (fn [new-opacity]
|
change-opacity (fn [new-opacity]
|
||||||
(swap! state assoc :opacity new-opacity)
|
(when on-change (on-change (assoc color :opacity new-opacity))))
|
||||||
(when on-change (on-change (remove-multiple value) new-opacity)))
|
|
||||||
|
|
||||||
handle-pick-color (fn [new-value new-opacity id file-id]
|
handle-pick-color (fn [color]
|
||||||
(reset! state {:value new-value :opacity new-opacity})
|
(when on-change (on-change color)))
|
||||||
(when on-change (on-change new-value new-opacity id file-id)))
|
|
||||||
|
|
||||||
handle-open (fn [] (when on-open (on-open)))
|
handle-open (fn [] (when on-open (on-open)))
|
||||||
|
|
||||||
|
@ -114,37 +106,63 @@
|
||||||
change-opacity))))
|
change-opacity))))
|
||||||
|
|
||||||
select-all (fn [event]
|
select-all (fn [event]
|
||||||
(dom/select-text! (dom/get-target event)))]
|
(dom/select-text! (dom/get-target event)))
|
||||||
|
|
||||||
|
handle-click-color (mf/use-callback
|
||||||
|
(mf/deps color)
|
||||||
|
(let [;; If multiple, we change to default color
|
||||||
|
color (if (uc/multiple? color)
|
||||||
|
{:color cp/default-color :opacity 1}
|
||||||
|
color)]
|
||||||
|
(color-picker-callback color
|
||||||
|
disable-gradient
|
||||||
|
disable-opacity
|
||||||
|
handle-pick-color
|
||||||
|
handle-open
|
||||||
|
handle-close)))]
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(mf/deps color)
|
(mf/deps color)
|
||||||
#(reset! state (parse-color color)))
|
(fn []
|
||||||
|
(modal/update-props! :colorpicker {:data (parse-color color)})))
|
||||||
|
|
||||||
[:div.row-flex.color-data
|
[:div.row-flex.color-data
|
||||||
[:span.color-th
|
[:& cb/color-bullet {:color color
|
||||||
{:class (when (and (:id color) (not= (:id color) :multiple)) "color-name")
|
:on-click handle-click-color}]
|
||||||
:style {:background-color (-> value value-to-background)}
|
|
||||||
:on-click (color-picker-callback @state handle-pick-color handle-open handle-close disable-opacity)}
|
|
||||||
(when (= value :multiple) "?")]
|
|
||||||
|
|
||||||
(if (:id color)
|
(cond
|
||||||
|
;; Rendering a color with ID
|
||||||
|
(:id color)
|
||||||
[:div.color-info
|
[:div.color-info
|
||||||
[:div.color-name (str (get-color-name color))]]
|
[:div.color-name (str (get-color-name color))]]
|
||||||
|
|
||||||
|
;; Rendering a gradient
|
||||||
|
(and (not (uc/multiple? color))
|
||||||
|
(:gradient color) (get-in color [:gradient :type]))
|
||||||
[:div.color-info
|
[:div.color-info
|
||||||
[:input {:value (-> value remove-hash)
|
[:div.color-name (cb/gradient-type->string (get-in color [:gradient :type]))]]
|
||||||
:pattern "^[0-9a-fA-F]{0,6}$"
|
|
||||||
:placeholder (tr "settings.multiple")
|
|
||||||
:on-click select-all
|
|
||||||
:on-change handle-value-change}]])
|
|
||||||
|
|
||||||
(when (not disable-opacity)
|
;; Rendering a plain color/opacity
|
||||||
[:div.input-element
|
:else
|
||||||
{:class (classnames :percentail (not= opacity :multiple))}
|
[:*
|
||||||
[:input.input-text {:type "number"
|
[:div.color-info
|
||||||
:value (-> opacity opacity->string)
|
[:input {:value (if (uc/multiple? color)
|
||||||
:placeholder (tr "settings.multiple")
|
""
|
||||||
:on-click select-all
|
(-> color :color remove-hash))
|
||||||
:on-change handle-opacity-change
|
:pattern "^[0-9a-fA-F]{0,6}$"
|
||||||
:min "0"
|
:placeholder (tr "settings.multiple")
|
||||||
:max "100"}]])]))
|
:on-click select-all
|
||||||
|
:on-change handle-value-change}]]
|
||||||
|
|
||||||
|
(when (and (not disable-opacity)
|
||||||
|
(not (:gradient color)))
|
||||||
|
[:div.input-element
|
||||||
|
{:class (classnames :percentail (not= (:opacity color) :multiple))}
|
||||||
|
[:input.input-text {:type "number"
|
||||||
|
:value (-> color :opacity opacity->string)
|
||||||
|
:placeholder (tr "settings.multiple")
|
||||||
|
:on-click select-all
|
||||||
|
:on-change handle-opacity-change
|
||||||
|
:min "0"
|
||||||
|
:max "100"}]])])]))
|
||||||
|
|
||||||
|
|
|
@ -174,7 +174,11 @@
|
||||||
[:span.after (t locale "workspace.options.shadow-options.spread")]]]
|
[:span.after (t locale "workspace.options.shadow-options.spread")]]]
|
||||||
|
|
||||||
[:div.color-row-wrap
|
[:div.color-row-wrap
|
||||||
[:& color-row {:color {:value (:color value) :opacity (:opacity value)}
|
[:& color-row {:color (if (string? (:color value))
|
||||||
|
;; Support for old format colors
|
||||||
|
{:color (:color value) :opacity (:opacity value)}
|
||||||
|
(:color value))
|
||||||
|
:disable-gradient true
|
||||||
:on-change (update-color index)
|
:on-change (update-color index)
|
||||||
:on-open #(st/emit! dwc/start-undo-transaction)
|
:on-open #(st/emit! dwc/start-undo-transaction)
|
||||||
:on-close #(st/emit! dwc/commit-undo-transaction)}]]]]))
|
:on-close #(st/emit! dwc/commit-undo-transaction)}]]]]))
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.math :as math]
|
[app.common.math :as math]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.common :as dwc]
|
||||||
|
[app.main.data.colors :as dc]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]
|
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]
|
||||||
|
@ -29,7 +30,8 @@
|
||||||
:stroke-color
|
:stroke-color
|
||||||
:stroke-color-ref-id
|
:stroke-color-ref-id
|
||||||
:stroke-color-ref-file
|
:stroke-color-ref-file
|
||||||
:stroke-opacity])
|
:stroke-opacity
|
||||||
|
:stroke-color-gradient])
|
||||||
|
|
||||||
(defn- stroke-menu-props-equals?
|
(defn- stroke-menu-props-equals?
|
||||||
[np op]
|
[np op]
|
||||||
|
@ -72,19 +74,17 @@
|
||||||
|
|
||||||
show-options (not= (:stroke-style values :none) :none)
|
show-options (not= (:stroke-style values :none) :none)
|
||||||
|
|
||||||
current-stroke-color {:value (:stroke-color values)
|
current-stroke-color {:color (:stroke-color values)
|
||||||
:opacity (:stroke-opacity values)
|
:opacity (:stroke-opacity values)
|
||||||
:id (:stroke-color-ref-id values)
|
:id (:stroke-color-ref-id values)
|
||||||
:file-id (:stroke-color-ref-file values)}
|
:file-id (:stroke-color-ref-file values)
|
||||||
|
:gradient (:stroke-color-gradient values)}
|
||||||
|
|
||||||
handle-change-stroke-color
|
handle-change-stroke-color
|
||||||
(fn [value opacity id file-id]
|
(mf/use-callback
|
||||||
(let [change #(cond-> %
|
(mf/deps ids)
|
||||||
value (assoc :stroke-color value
|
(fn [color]
|
||||||
:stroke-color-ref-id id
|
(st/emit! (dc/change-stroke ids color))))
|
||||||
:stroke-color-ref-file file-id)
|
|
||||||
opacity (assoc :stroke-opacity opacity))]
|
|
||||||
(st/emit! (dwc/update-shapes ids change))))
|
|
||||||
|
|
||||||
on-stroke-style-change
|
on-stroke-style-change
|
||||||
(fn [event]
|
(fn [event]
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
["slate" :refer [Transforms]]))
|
["slate" :refer [Transforms]]))
|
||||||
|
|
||||||
(def text-typography-attrs [:typography-ref-id :typography-ref-file])
|
(def text-typography-attrs [:typography-ref-id :typography-ref-file])
|
||||||
(def text-fill-attrs [:fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill :opacity ])
|
(def text-fill-attrs [:fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill-color-gradient :fill :opacity ])
|
||||||
(def text-font-attrs [:font-id :font-family :font-variant-id :font-size :font-weight :font-style])
|
(def text-font-attrs [:font-id :font-family :font-variant-id :font-size :font-weight :font-style])
|
||||||
(def text-align-attrs [:text-align])
|
(def text-align-attrs [:text-align])
|
||||||
(def text-spacing-attrs [:line-height :letter-spacing])
|
(def text-spacing-attrs [:line-height :letter-spacing])
|
||||||
|
@ -291,6 +291,8 @@
|
||||||
:shape shape
|
:shape shape
|
||||||
:attrs text-fill-attrs})
|
:attrs text-fill-attrs})
|
||||||
|
|
||||||
|
fill-values (d/update-in-when fill-values [:fill-color-gradient :type] keyword)
|
||||||
|
|
||||||
fill-values (cond-> fill-values
|
fill-values (cond-> fill-values
|
||||||
;; Keep for backwards compatibility
|
;; Keep for backwards compatibility
|
||||||
(:fill fill-values) (assoc :fill-color (:fill fill-values))
|
(:fill fill-values) (assoc :fill-color (:fill fill-values))
|
||||||
|
|
|
@ -40,6 +40,8 @@
|
||||||
[app.main.ui.workspace.snap-distances :refer [snap-distances]]
|
[app.main.ui.workspace.snap-distances :refer [snap-distances]]
|
||||||
[app.main.ui.workspace.frame-grid :refer [frame-grid]]
|
[app.main.ui.workspace.frame-grid :refer [frame-grid]]
|
||||||
[app.main.ui.workspace.shapes.outline :refer [outline]]
|
[app.main.ui.workspace.shapes.outline :refer [outline]]
|
||||||
|
[app.main.ui.workspace.gradients :refer [gradient-handlers]]
|
||||||
|
[app.main.ui.workspace.colorpicker.pixel-overlay :refer [pixel-overlay]]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.dom.dnd :as dnd]
|
[app.util.dom.dnd :as dnd]
|
||||||
|
@ -184,104 +186,6 @@
|
||||||
(:width vbox 0)
|
(:width vbox 0)
|
||||||
(:height vbox 0)]))
|
(:height vbox 0)]))
|
||||||
|
|
||||||
(mf/defc pixel-picker-overlay
|
|
||||||
{::mf/wrap-props false}
|
|
||||||
[props]
|
|
||||||
(let [vport (unchecked-get props "vport")
|
|
||||||
vbox (unchecked-get props "vbox")
|
|
||||||
viewport-ref (unchecked-get props "viewport-ref")
|
|
||||||
options (unchecked-get props "options")
|
|
||||||
svg-ref (mf/use-ref nil)
|
|
||||||
canvas-ref (mf/use-ref nil)
|
|
||||||
fetch-pending (mf/deref (mdf/pending-ref))
|
|
||||||
|
|
||||||
on-mouse-move-picker
|
|
||||||
(fn [event]
|
|
||||||
(when-let [zoom-view-node (.getElementById js/document "picker-detail")]
|
|
||||||
(let [{brx :left bry :top} (dom/get-bounding-rect (mf/ref-val viewport-ref))
|
|
||||||
x (- (.-clientX event) brx)
|
|
||||||
y (- (.-clientY event) bry)
|
|
||||||
|
|
||||||
zoom-context (.getContext zoom-view-node "2d")
|
|
||||||
canvas-node (mf/ref-val canvas-ref)
|
|
||||||
canvas-context (.getContext canvas-node "2d")
|
|
||||||
pixel-data (.getImageData canvas-context x y 1 1)
|
|
||||||
rgba (.-data pixel-data)
|
|
||||||
r (obj/get rgba 0)
|
|
||||||
g (obj/get rgba 1)
|
|
||||||
b (obj/get rgba 2)
|
|
||||||
a (obj/get rgba 3)
|
|
||||||
|
|
||||||
area-data (.getImageData canvas-context (- x 25) (- y 20) 50 40)]
|
|
||||||
|
|
||||||
(-> (js/createImageBitmap area-data)
|
|
||||||
(p/then (fn [image]
|
|
||||||
;; Draw area
|
|
||||||
(obj/set! zoom-context "imageSmoothingEnabled" false)
|
|
||||||
(.drawImage zoom-context image 0 0 200 160))))
|
|
||||||
(st/emit! (dwc/pick-color [r g b a])))))
|
|
||||||
|
|
||||||
on-mouse-down-picker
|
|
||||||
(fn [event]
|
|
||||||
(dom/prevent-default event)
|
|
||||||
(dom/stop-propagation event)
|
|
||||||
(st/emit! (dwc/pick-color-select true (kbd/shift? event))))
|
|
||||||
|
|
||||||
on-mouse-up-picker
|
|
||||||
(fn [event]
|
|
||||||
(dom/prevent-default event)
|
|
||||||
(dom/stop-propagation event)
|
|
||||||
(st/emit! (dwc/stop-picker))
|
|
||||||
(modal/disallow-click-outside!))]
|
|
||||||
|
|
||||||
(mf/use-effect
|
|
||||||
;; Everytime we finish retrieving a new URL we redraw the canvas
|
|
||||||
;; so even if we're not finished the user can start to pick basic
|
|
||||||
;; shapes
|
|
||||||
(mf/deps fetch-pending)
|
|
||||||
(fn []
|
|
||||||
(try
|
|
||||||
(let [canvas-node (mf/ref-val canvas-ref)
|
|
||||||
canvas-context (.getContext canvas-node "2d")
|
|
||||||
svg-node (mf/ref-val svg-ref)]
|
|
||||||
(timers/schedule 100
|
|
||||||
#(let [xml (.serializeToString (js/XMLSerializer.) svg-node)
|
|
||||||
img-src (str "data:image/svg+xml;base64,"
|
|
||||||
(-> xml js/encodeURIComponent js/unescape js/btoa))
|
|
||||||
img (js/Image.)
|
|
||||||
on-error (fn [err] (.error js/console "ERROR" err))
|
|
||||||
on-load (fn [] (.drawImage canvas-context img 0 0))]
|
|
||||||
(.addEventListener img "error" on-error)
|
|
||||||
(.addEventListener img "load" on-load)
|
|
||||||
(obj/set! img "src" img-src))))
|
|
||||||
(catch :default e (.error js/console e)))))
|
|
||||||
|
|
||||||
[:*
|
|
||||||
[:div.overlay
|
|
||||||
{:style {:position "absolute"
|
|
||||||
:top 0
|
|
||||||
:left 0
|
|
||||||
:width "100%"
|
|
||||||
:height "100%"
|
|
||||||
:cursor cur/picker}
|
|
||||||
:on-mouse-down on-mouse-down-picker
|
|
||||||
:on-mouse-up on-mouse-up-picker
|
|
||||||
:on-mouse-move on-mouse-move-picker}]
|
|
||||||
[:canvas {:ref canvas-ref
|
|
||||||
:width (:width vport 0)
|
|
||||||
:height (:height vport 0)
|
|
||||||
:style {:display "none"}}]
|
|
||||||
[:& (mf/provider muc/embed-ctx) {:value true}
|
|
||||||
[:svg.viewport
|
|
||||||
{:ref svg-ref
|
|
||||||
:preserveAspectRatio "xMidYMid meet"
|
|
||||||
:width (:width vport 0)
|
|
||||||
:height (:height vport 0)
|
|
||||||
:view-box (format-viewbox vbox)
|
|
||||||
:style {:display "none"
|
|
||||||
:background-color (get options :background "#E8E9EA")}}
|
|
||||||
[:& frames]]]]))
|
|
||||||
|
|
||||||
(mf/defc viewport
|
(mf/defc viewport
|
||||||
[{:keys [page-id page local layout] :as props}]
|
[{:keys [page-id page local layout] :as props}]
|
||||||
(let [{:keys [options-mode
|
(let [{:keys [options-mode
|
||||||
|
@ -309,8 +213,6 @@
|
||||||
drawing-tool (:tool drawing)
|
drawing-tool (:tool drawing)
|
||||||
drawing-obj (:object drawing)
|
drawing-obj (:object drawing)
|
||||||
|
|
||||||
pick-color (mf/use-state [255 255 255 255])
|
|
||||||
|
|
||||||
zoom (or zoom 1)
|
zoom (or zoom 1)
|
||||||
|
|
||||||
on-mouse-down
|
on-mouse-down
|
||||||
|
@ -586,11 +488,11 @@
|
||||||
|
|
||||||
[:*
|
[:*
|
||||||
(when picking-color?
|
(when picking-color?
|
||||||
[:& pixel-picker-overlay {:vport vport
|
[:& pixel-overlay {:vport vport
|
||||||
:vbox vbox
|
:vbox vbox
|
||||||
:viewport-ref viewport-ref
|
:viewport-ref viewport-ref
|
||||||
:options options
|
:options options
|
||||||
:layout layout}])
|
:layout layout}])
|
||||||
|
|
||||||
[:svg.viewport
|
[:svg.viewport
|
||||||
{:preserveAspectRatio "xMidYMid meet"
|
{:preserveAspectRatio "xMidYMid meet"
|
||||||
|
@ -642,6 +544,10 @@
|
||||||
:zoom zoom
|
:zoom zoom
|
||||||
:edition edition}])
|
:edition edition}])
|
||||||
|
|
||||||
|
(when (= (count selected) 1)
|
||||||
|
[:& gradient-handlers {:id (first selected)
|
||||||
|
:zoom zoom}])
|
||||||
|
|
||||||
(when drawing-obj
|
(when drawing-obj
|
||||||
[:& draw-area {:shape drawing-obj
|
[:& draw-area {:shape drawing-obj
|
||||||
:zoom zoom
|
:zoom zoom
|
||||||
|
|
|
@ -77,3 +77,35 @@
|
||||||
(defn hsv->hsl
|
(defn hsv->hsl
|
||||||
[hsv]
|
[hsv]
|
||||||
(hex->hsl (hsv->hex hsv)))
|
(hex->hsl (hsv->hex hsv)))
|
||||||
|
|
||||||
|
(defn gradient->css [{:keys [type stops]}]
|
||||||
|
(let [parse-stop
|
||||||
|
(fn [{:keys [offset color opacity]}]
|
||||||
|
(let [[r g b] (hex->rgb color)]
|
||||||
|
(str/fmt "rgba(%s, %s, %s, %s) %s" r g b opacity (str (* offset 100) "%"))))
|
||||||
|
|
||||||
|
stops-css (str/join "," (map parse-stop stops))]
|
||||||
|
|
||||||
|
(if (= type :linear)
|
||||||
|
(str/fmt "linear-gradient(to bottom, %s)" stops-css)
|
||||||
|
(str/fmt "radial-gradient(circle, %s)" stops-css))))
|
||||||
|
|
||||||
|
;; TODO: REMOVE `VALUE` WHEN COLOR IS INTEGRATED
|
||||||
|
(defn color->background [{:keys [color opacity gradient value]}]
|
||||||
|
(let [color (or color value)
|
||||||
|
opacity (or opacity 1)]
|
||||||
|
(cond
|
||||||
|
(and gradient (not= :multiple gradient))
|
||||||
|
(gradient->css gradient)
|
||||||
|
|
||||||
|
(not= color :multiple)
|
||||||
|
(let [[r g b] (hex->rgb (or color value))]
|
||||||
|
(str/fmt "rgba(%s, %s, %s, %s)" r g b opacity))
|
||||||
|
|
||||||
|
:else "transparent")))
|
||||||
|
|
||||||
|
(defn multiple? [{:keys [value color gradient]}]
|
||||||
|
(or (= value :multiple)
|
||||||
|
(= color :multiple)
|
||||||
|
(= gradient :multiple)
|
||||||
|
(and gradient color)))
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
[goog.object :as gobj]
|
[goog.object :as gobj]
|
||||||
["lodash/omit" :as omit]))
|
["lodash/omit" :as omit]))
|
||||||
|
|
||||||
|
(defn new [] #js {})
|
||||||
|
|
||||||
(defn get
|
(defn get
|
||||||
([obj k]
|
([obj k]
|
||||||
(when-not (nil? obj)
|
(when-not (nil? obj)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue