mirror of
https://github.com/penpot/penpot.git
synced 2025-03-14 16:51:18 -05:00
Merge pull request #4585 from penpot/alotor-plugins-api
Updates to Plugin API and menu
This commit is contained in:
commit
a3d4d62269
16 changed files with 2947 additions and 2079 deletions
|
@ -406,8 +406,12 @@
|
||||||
[shape text]
|
[shape text]
|
||||||
(let [content (:content shape)
|
(let [content (:content shape)
|
||||||
|
|
||||||
paragraph-style (select-keys (->> content (node-seq is-paragraph-node?) first) text-all-attrs)
|
paragraph-style (merge
|
||||||
text-style (select-keys (->> content (node-seq is-text-node?) first) text-all-attrs)
|
default-text-attrs
|
||||||
|
(select-keys (->> content (node-seq is-paragraph-node?) first) text-all-attrs))
|
||||||
|
text-style (merge
|
||||||
|
default-text-attrs
|
||||||
|
(select-keys (->> content (node-seq is-text-node?) first) text-all-attrs))
|
||||||
|
|
||||||
paragraph-texts (str/split text "\n")
|
paragraph-texts (str/split text "\n")
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -29,7 +29,7 @@
|
||||||
<script defer src="{{& polyfills}}"></script>
|
<script defer src="{{& polyfills}}"></script>
|
||||||
{{/manifest}}
|
{{/manifest}}
|
||||||
|
|
||||||
<script type="module" src="{{pluginRuntimeUri}}/index.mjs"></script>
|
<script type="module" src="{{pluginRuntimeUri}}/index.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.penpotTranslations = JSON.parse({{& translations}});
|
window.penpotTranslations = JSON.parse({{& translations}});
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
(:require
|
(:require
|
||||||
[app.main.data.events :as ev]
|
[app.main.data.events :as ev]
|
||||||
[app.main.data.exports :as de]
|
[app.main.data.exports :as de]
|
||||||
|
[app.main.data.modal :as modal]
|
||||||
[app.main.data.preview :as dp]
|
[app.main.data.preview :as dp]
|
||||||
[app.main.data.shortcuts :as ds]
|
[app.main.data.shortcuts :as ds]
|
||||||
[app.main.data.users :as du]
|
[app.main.data.users :as du]
|
||||||
|
@ -23,6 +24,7 @@
|
||||||
[app.main.data.workspace.texts :as dwtxt]
|
[app.main.data.workspace.texts :as dwtxt]
|
||||||
[app.main.data.workspace.transforms :as dwt]
|
[app.main.data.workspace.transforms :as dwt]
|
||||||
[app.main.data.workspace.undo :as dwu]
|
[app.main.data.workspace.undo :as dwu]
|
||||||
|
[app.main.features :as features]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.hooks.resize :as r]
|
[app.main.ui.hooks.resize :as r]
|
||||||
|
@ -552,7 +554,15 @@
|
||||||
:command (ds/a-mod "m")
|
:command (ds/a-mod "m")
|
||||||
:subsections [:basics]
|
:subsections [:basics]
|
||||||
:fn #(st/emit! (with-meta (du/toggle-theme)
|
:fn #(st/emit! (with-meta (du/toggle-theme)
|
||||||
{::ev/origin "workspace:shortcut"}))}})
|
{::ev/origin "workspace:shortcut"}))}
|
||||||
|
|
||||||
|
|
||||||
|
;; PLUGINS
|
||||||
|
:plugins {:tooltip (ds/meta (ds/alt "P"))
|
||||||
|
:command (ds/c-mod "alt+p")
|
||||||
|
:subsections [:basics]
|
||||||
|
:fn #(when (features/active-feature? @st/state "plugins/runtime")
|
||||||
|
(st/emit! (modal/show :plugin-management {})))}})
|
||||||
|
|
||||||
(def debug-shortcuts
|
(def debug-shortcuts
|
||||||
;; PREVIEW
|
;; PREVIEW
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
(ns app.main.ui.components.search-bar
|
(ns app.main.ui.components.search-bar
|
||||||
(:require-macros [app.main.style :as stl])
|
(:require-macros [app.main.style :as stl])
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
|
@ -23,7 +24,7 @@
|
||||||
icon (unchecked-get props "icon")
|
icon (unchecked-get props "icon")
|
||||||
autofocus (unchecked-get props "auto-focus")
|
autofocus (unchecked-get props "auto-focus")
|
||||||
id (unchecked-get props "id")
|
id (unchecked-get props "id")
|
||||||
|
input-class (unchecked-get props "class")
|
||||||
|
|
||||||
handle-change
|
handle-change
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
|
@ -51,7 +52,7 @@
|
||||||
[:span {:class (stl/css-case :search-box true
|
[:span {:class (stl/css-case :search-box true
|
||||||
:has-children (some? children))}
|
:has-children (some? children))}
|
||||||
children
|
children
|
||||||
[:div {:class (stl/css :search-input-wrapper)}
|
[:div {:class (dm/str input-class " " (stl/css :search-input-wrapper))}
|
||||||
icon
|
icon
|
||||||
[:input {:id id
|
[:input {:id id
|
||||||
:on-change handle-change
|
:on-change handle-change
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
[app.main.ui.workspace.libraries]
|
[app.main.ui.workspace.libraries]
|
||||||
[app.main.ui.workspace.nudge]
|
[app.main.ui.workspace.nudge]
|
||||||
[app.main.ui.workspace.palette :refer [palette]]
|
[app.main.ui.workspace.palette :refer [palette]]
|
||||||
|
[app.main.ui.workspace.plugins]
|
||||||
[app.main.ui.workspace.sidebar :refer [left-sidebar right-sidebar]]
|
[app.main.ui.workspace.sidebar :refer [left-sidebar right-sidebar]]
|
||||||
[app.main.ui.workspace.sidebar.collapsable-button :refer [collapsed-button]]
|
[app.main.ui.workspace.sidebar.collapsable-button :refer [collapsed-button]]
|
||||||
[app.main.ui.workspace.sidebar.history :refer [history-toolbox]]
|
[app.main.ui.workspace.sidebar.history :refer [history-toolbox]]
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
(ns app.main.ui.workspace.main-menu
|
(ns app.main.ui.workspace.main-menu
|
||||||
(:require-macros [app.main.style :as stl])
|
(:require-macros [app.main.style :as stl])
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
[app.common.files.helpers :as cfh]
|
[app.common.files.helpers :as cfh]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
|
@ -20,12 +22,14 @@
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.common :as dwc]
|
||||||
[app.main.data.workspace.libraries :as dwl]
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
[app.main.data.workspace.shortcuts :as sc]
|
[app.main.data.workspace.shortcuts :as sc]
|
||||||
|
[app.main.features :as features]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.dropdown-menu :refer [dropdown-menu dropdown-menu-item*]]
|
[app.main.ui.components.dropdown-menu :refer [dropdown-menu dropdown-menu-item*]]
|
||||||
[app.main.ui.context :as ctx]
|
[app.main.ui.context :as ctx]
|
||||||
[app.main.ui.hooks.resize :as r]
|
[app.main.ui.hooks.resize :as r]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
|
[app.main.ui.workspace.plugins :as uwp]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
|
@ -60,6 +64,9 @@
|
||||||
nav-to-feedback
|
nav-to-feedback
|
||||||
(mf/use-fn #(st/emit! (rt/nav-new-window* {:rname :settings-feedback})))
|
(mf/use-fn #(st/emit! (rt/nav-new-window* {:rname :settings-feedback})))
|
||||||
|
|
||||||
|
plugins?
|
||||||
|
(features/active-feature? @st/state "plugins/runtime")
|
||||||
|
|
||||||
show-shortcuts
|
show-shortcuts
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps layout)
|
(mf/deps layout)
|
||||||
|
@ -83,7 +90,8 @@
|
||||||
[:& dropdown-menu {:show true
|
[:& dropdown-menu {:show true
|
||||||
:on-close on-close
|
:on-close on-close
|
||||||
:list-class (stl/css-case :sub-menu true
|
:list-class (stl/css-case :sub-menu true
|
||||||
:help-info true)}
|
:help-info plugins?
|
||||||
|
:help-info-old (not plugins?))}
|
||||||
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
||||||
:on-click nav-to-helpc-center
|
:on-click nav-to-helpc-center
|
||||||
:on-key-down (fn [event]
|
:on-key-down (fn [event]
|
||||||
|
@ -148,7 +156,6 @@
|
||||||
:id "file-menu-shortcuts"}
|
:id "file-menu-shortcuts"}
|
||||||
[:span {:class (stl/css :item-name)} (tr "label.shortcuts")]
|
[:span {:class (stl/css :item-name)} (tr "label.shortcuts")]
|
||||||
[:span {:class (stl/css :shortcut)}
|
[:span {:class (stl/css :shortcut)}
|
||||||
|
|
||||||
(for [sc (scd/split-sc (sc/get-tooltip :show-shortcuts))]
|
(for [sc (scd/split-sc (sc/get-tooltip :show-shortcuts))]
|
||||||
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
|
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
|
||||||
|
|
||||||
|
@ -596,6 +603,41 @@
|
||||||
[:span {:class (stl/css :item-name)}
|
[:span {:class (stl/css :item-name)}
|
||||||
(tr "dashboard.export-frames")]])]))
|
(tr "dashboard.export-frames")]])]))
|
||||||
|
|
||||||
|
(mf/defc plugins-menu
|
||||||
|
{::mf/wrap-props false
|
||||||
|
::mf/wrap [mf/memo]}
|
||||||
|
[{:keys [open-plugins on-close]}]
|
||||||
|
(when (features/active-feature? @st/state "plugins/runtime")
|
||||||
|
(let [plugins (uwp/load-from-store)]
|
||||||
|
[:& dropdown-menu {:show true
|
||||||
|
:list-class (stl/css-case :sub-menu true :plugins true)
|
||||||
|
:on-close on-close}
|
||||||
|
[:> dropdown-menu-item* {:on-click open-plugins
|
||||||
|
:class (stl/css :submenu-item)
|
||||||
|
:on-key-down (fn [event]
|
||||||
|
(when (kbd/enter? event)
|
||||||
|
(open-plugins event)))
|
||||||
|
:data-test "open-plugins"
|
||||||
|
:id "file-menu-open-plugins"}
|
||||||
|
[:span {:class (stl/css :item-name)}
|
||||||
|
(tr "workspace.plugins.menu.plugins-manager")]
|
||||||
|
[:span {:class (stl/css :shortcut)}
|
||||||
|
(for [sc (scd/split-sc (sc/get-tooltip :plugins))]
|
||||||
|
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
|
||||||
|
|
||||||
|
|
||||||
|
(when (d/not-empty? plugins)
|
||||||
|
[:div {:class (stl/css :separator)}])
|
||||||
|
|
||||||
|
(for [[idx {:keys [name url]}] (d/enumerate plugins)]
|
||||||
|
[:> dropdown-menu-item* {:key (dm/str "plugins-menu-" idx)
|
||||||
|
:on-click #(uwp/open-plugin! url)
|
||||||
|
:class (stl/css :submenu-item)
|
||||||
|
:on-key-down (fn [event]
|
||||||
|
(when (kbd/enter? event)
|
||||||
|
#(uwp/open-plugin! url)))}
|
||||||
|
[:span {:class (stl/css :item-name)} name]])])))
|
||||||
|
|
||||||
(mf/defc menu
|
(mf/defc menu
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[{:keys [layout file profile]}]
|
[{:keys [layout file profile]}]
|
||||||
|
@ -644,12 +686,19 @@
|
||||||
(reset! show-menu* false)
|
(reset! show-menu* false)
|
||||||
(reset! sub-menu* nil))))
|
(reset! sub-menu* nil))))
|
||||||
|
|
||||||
|
|
||||||
toggle-theme
|
toggle-theme
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(st/emit! (du/toggle-theme))))]
|
(st/emit! (du/toggle-theme))))
|
||||||
|
|
||||||
|
open-plugins-manager
|
||||||
|
(mf/use-fn
|
||||||
|
(fn [event]
|
||||||
|
(dom/stop-propagation event)
|
||||||
|
(reset! show-menu* false)
|
||||||
|
(reset! sub-menu* nil)
|
||||||
|
(st/emit! (modal/show :plugin-management {}))))]
|
||||||
|
|
||||||
|
|
||||||
[:*
|
[:*
|
||||||
|
@ -703,6 +752,19 @@
|
||||||
:id "file-menu-preferences"}
|
:id "file-menu-preferences"}
|
||||||
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.preferences")]
|
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.preferences")]
|
||||||
[:span {:class (stl/css :open-arrow)} i/arrow]]
|
[:span {:class (stl/css :open-arrow)} i/arrow]]
|
||||||
|
|
||||||
|
(when (features/active-feature? @st/state "plugins/runtime")
|
||||||
|
[:> dropdown-menu-item* {:class (stl/css :menu-item)
|
||||||
|
:on-click on-menu-click
|
||||||
|
:on-key-down (fn [event]
|
||||||
|
(when (kbd/enter? event)
|
||||||
|
(on-menu-click event)))
|
||||||
|
:on-pointer-enter on-menu-click
|
||||||
|
:data-test "plugins"
|
||||||
|
:id "file-menu-plugins"}
|
||||||
|
[:span {:class (stl/css :item-name)} (tr "workspace.plugins.menu.title")]
|
||||||
|
[:span {:class (stl/css :open-arrow)} i/arrow]])
|
||||||
|
|
||||||
[:div {:class (stl/css :separator)}]
|
[:div {:class (stl/css :separator)}]
|
||||||
[:> dropdown-menu-item* {:class (stl/css-case :menu-item true)
|
[:> dropdown-menu-item* {:class (stl/css-case :menu-item true)
|
||||||
:on-click on-menu-click
|
:on-click on-menu-click
|
||||||
|
@ -739,6 +801,11 @@
|
||||||
:toggle-theme toggle-theme
|
:toggle-theme toggle-theme
|
||||||
:on-close close-sub-menu}]
|
:on-close close-sub-menu}]
|
||||||
|
|
||||||
|
:plugins
|
||||||
|
[:& plugins-menu
|
||||||
|
{:open-plugins open-plugins-manager
|
||||||
|
:on-close close-sub-menu}]
|
||||||
|
|
||||||
:help-info
|
:help-info
|
||||||
[:& help-info-menu
|
[:& help-info-menu
|
||||||
{:layout layout
|
{:layout layout
|
||||||
|
|
|
@ -95,7 +95,15 @@
|
||||||
top: $s-148;
|
top: $s-148;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.plugins {
|
||||||
|
top: $s-180;
|
||||||
|
}
|
||||||
|
|
||||||
&.help-info {
|
&.help-info {
|
||||||
top: $s-196;
|
top: $s-232;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.help-info-old {
|
||||||
|
top: $s-192;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
180
frontend/src/app/main/ui/workspace/plugins.cljs
Normal file
180
frontend/src/app/main/ui/workspace/plugins.cljs
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/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
(ns app.main.ui.workspace.plugins
|
||||||
|
(:require-macros [app.main.style :as stl])
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.main.data.modal :as modal]
|
||||||
|
[app.main.ui.components.search-bar :refer [search-bar]]
|
||||||
|
[app.main.ui.components.title-bar :refer [title-bar]]
|
||||||
|
[app.main.ui.icons :as i]
|
||||||
|
[app.util.http :as http]
|
||||||
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[beicon.v2.core :as rx]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(def ^:private close-icon
|
||||||
|
(i/icon-xref :close (stl/css :close-icon)))
|
||||||
|
|
||||||
|
|
||||||
|
(mf/defc plugin-entry
|
||||||
|
[{:keys [index _icon url name description on-open-plugin on-remove-plugin]}]
|
||||||
|
|
||||||
|
(let [handle-open-click
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps index url on-open-plugin)
|
||||||
|
(fn []
|
||||||
|
(when on-open-plugin
|
||||||
|
(on-open-plugin index url))))
|
||||||
|
|
||||||
|
handle-delete-click
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps index url on-remove-plugin)
|
||||||
|
(fn []
|
||||||
|
(when on-remove-plugin
|
||||||
|
(on-remove-plugin index url))))]
|
||||||
|
[:div {:class (stl/css :plugins-list-element)}
|
||||||
|
[:div {:class (stl/css :plugin-icon)} ""]
|
||||||
|
[:div {:class (stl/css :plugin-description)}
|
||||||
|
[:div {:class (stl/css :plugin-title)} name]
|
||||||
|
[:div {:class (stl/css :plugin-summary)} description]]
|
||||||
|
[:button {:class (stl/css :open-button)
|
||||||
|
:on-click handle-open-click} (tr "workspace.plugins.button-open")]
|
||||||
|
[:button {:class (stl/css :trash-button)
|
||||||
|
:on-click handle-delete-click} i/delete]]))
|
||||||
|
|
||||||
|
(defn load-from-store
|
||||||
|
[]
|
||||||
|
(let [ls (.-localStorage js/window)
|
||||||
|
plugins-val (.getItem ls "plugins")]
|
||||||
|
(when plugins-val
|
||||||
|
(let [plugins-js (.parse js/JSON plugins-val)]
|
||||||
|
(js->clj plugins-js {:keywordize-keys true})))))
|
||||||
|
|
||||||
|
(defn save-to-store
|
||||||
|
[plugins]
|
||||||
|
(let [ls (.-localStorage js/window)
|
||||||
|
plugins-js (clj->js plugins)
|
||||||
|
plugins-val (.stringify js/JSON plugins-js)]
|
||||||
|
(.setItem ls "plugins" plugins-val)))
|
||||||
|
|
||||||
|
(defn open-plugin!
|
||||||
|
[url]
|
||||||
|
(.ɵloadPlugin js/window #js {:manifest url}))
|
||||||
|
|
||||||
|
(mf/defc plugin-management-dialog
|
||||||
|
{::mf/register modal/components
|
||||||
|
::mf/register-as :plugin-management}
|
||||||
|
[]
|
||||||
|
|
||||||
|
(let [plugins-state* (mf/use-state [])
|
||||||
|
plugins-state @plugins-state*
|
||||||
|
|
||||||
|
plugin-url* (mf/use-state "")
|
||||||
|
plugin-url @plugin-url*
|
||||||
|
|
||||||
|
input-status* (mf/use-state nil) ;; :error-url :error-manifest :success
|
||||||
|
input-status @input-status*
|
||||||
|
|
||||||
|
error? (contains? #{:error-url :error-manifest} input-status)
|
||||||
|
|
||||||
|
handle-close-dialog
|
||||||
|
(mf/use-callback
|
||||||
|
(fn []
|
||||||
|
(modal/hide!)))
|
||||||
|
|
||||||
|
handle-url-input
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [value]
|
||||||
|
(reset! input-status* nil)
|
||||||
|
(reset! plugin-url* value)))
|
||||||
|
|
||||||
|
handle-install-click
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps plugins-state plugin-url)
|
||||||
|
(fn []
|
||||||
|
(->> (http/send! {:method :get
|
||||||
|
:uri plugin-url
|
||||||
|
:response-type :json})
|
||||||
|
(rx/map :body)
|
||||||
|
(rx/subs!
|
||||||
|
(fn [body]
|
||||||
|
(let [name (obj/get body "name")
|
||||||
|
new-state (conj plugins-state {:name name :url plugin-url})]
|
||||||
|
(reset! input-status* :success)
|
||||||
|
(reset! plugin-url* "")
|
||||||
|
(reset! plugins-state* new-state)
|
||||||
|
(save-to-store new-state)))
|
||||||
|
(fn [_]
|
||||||
|
(reset! input-status* :error-url))))))
|
||||||
|
|
||||||
|
handle-open-plugin
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [_ url]
|
||||||
|
(open-plugin! url)
|
||||||
|
(modal/hide!)))
|
||||||
|
|
||||||
|
handle-remove-plugin
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps plugins-state)
|
||||||
|
(fn [rm-idx _]
|
||||||
|
(let [new-state
|
||||||
|
(into []
|
||||||
|
(keep-indexed (fn [idx item]
|
||||||
|
(when (not= idx rm-idx) item)))
|
||||||
|
plugins-state)]
|
||||||
|
|
||||||
|
(reset! plugins-state* new-state)
|
||||||
|
(save-to-store new-state))))]
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(fn []
|
||||||
|
(reset! plugins-state* (d/nilv (load-from-store) []))))
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-overlay)}
|
||||||
|
[:div {:class (stl/css :modal-dialog)}
|
||||||
|
[:button {:class (stl/css :close-btn) :on-click handle-close-dialog} close-icon]
|
||||||
|
[:div {:class (stl/css :modal-title)} (tr "workspace.plugins.title")]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-content)}
|
||||||
|
[:div {:class (stl/css :top-bar)}
|
||||||
|
[:& search-bar {:on-change handle-url-input
|
||||||
|
:value plugin-url
|
||||||
|
:placeholder (tr "workspace.plugins.search-placeholder")
|
||||||
|
:class (stl/css-case :input-error error?)}]
|
||||||
|
|
||||||
|
[:button {:class (stl/css :primary-button)
|
||||||
|
:disabled (empty? plugin-url)
|
||||||
|
:on-click handle-install-click} (tr "workspace.plugins.install")]]
|
||||||
|
|
||||||
|
(when error?
|
||||||
|
[:div {:class (stl/css-case :info true :error error?)}
|
||||||
|
(tr "workspace.plugins.error.url")])
|
||||||
|
|
||||||
|
[:hr]
|
||||||
|
|
||||||
|
[:& title-bar {:collapsable false
|
||||||
|
:title (tr "workspace.plugins.installed-plugins")}]
|
||||||
|
|
||||||
|
(if (empty? plugins-state)
|
||||||
|
[:div {:class (stl/css :plugins-empty)}
|
||||||
|
[:div {:class (stl/css :plugins-empty-logo)} i/logo-icon]
|
||||||
|
[:div {:class (stl/css :plugins-empty-text)} (tr "workspace.plugins.empty-plugins")]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :plugins-list)}
|
||||||
|
|
||||||
|
(for [[idx {:keys [name url]}] (d/enumerate plugins-state)]
|
||||||
|
[:& plugin-entry {:key (dm/str "plugin-" idx)
|
||||||
|
:name name
|
||||||
|
:url url
|
||||||
|
:index idx
|
||||||
|
:icon nil
|
||||||
|
:description "Nullam ullamcorper ligula ac felis commodo pulvinar."
|
||||||
|
:on-open-plugin handle-open-plugin
|
||||||
|
:on-remove-plugin handle-remove-plugin}])])]]]))
|
167
frontend/src/app/main/ui/workspace/plugins.scss
Normal file
167
frontend/src/app/main/ui/workspace/plugins.scss
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
// 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/.
|
||||||
|
//
|
||||||
|
// Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
@import "refactor/common-refactor.scss";
|
||||||
|
|
||||||
|
.modal-overlay {
|
||||||
|
@extend .modal-overlay-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-dialog {
|
||||||
|
@extend .modal-container-base;
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
height: $s-472;
|
||||||
|
max-height: $s-472;
|
||||||
|
width: $s-472;
|
||||||
|
max-width: $s-472;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
@extend .modal-close-btn-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-icon {
|
||||||
|
@extend .button-icon;
|
||||||
|
stroke: var(--icon-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
@include headlineMediumTypography;
|
||||||
|
margin-block-end: $s-16;
|
||||||
|
color: var(--modal-title-foreground-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: $s-380;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary-button {
|
||||||
|
@extend .button-primary;
|
||||||
|
@include headlineSmallTypography;
|
||||||
|
padding: $s-0 $s-16;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-icon {
|
||||||
|
@include flexCenter;
|
||||||
|
width: $s-20;
|
||||||
|
padding: 0 0 0 $s-8;
|
||||||
|
svg {
|
||||||
|
@extend .button-icon-small;
|
||||||
|
stroke: var(--icon-foreground);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-bar {
|
||||||
|
display: flex;
|
||||||
|
gap: $s-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.open-button {
|
||||||
|
@extend .button-secondary;
|
||||||
|
width: $s-68;
|
||||||
|
min-width: $s-68;
|
||||||
|
height: $s-32;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trash-button {
|
||||||
|
@extend .button-tertiary;
|
||||||
|
width: $s-32;
|
||||||
|
height: $s-32;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
@extend .button-icon;
|
||||||
|
stroke: var(--icon-foreground);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugins-list {
|
||||||
|
padding-top: $s-20;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: scroll;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugins-list-element {
|
||||||
|
display: flex;
|
||||||
|
gap: $s-12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-icon {
|
||||||
|
min-width: $s-32;
|
||||||
|
min-height: $s-32;
|
||||||
|
width: $s-32;
|
||||||
|
height: $s-32;
|
||||||
|
background: #b1b2b5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-description {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $s-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-title {
|
||||||
|
@include bodyMediumTypography;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-summary {
|
||||||
|
@include bodySmallTypography;
|
||||||
|
color: #8f9da3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugins-empty {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
margin-top: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugins-empty-logo {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: #212426;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
fill: #8f9da3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugins-empty-text {
|
||||||
|
@include bodySmallTypography;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.input-error {
|
||||||
|
border: 1px solid var(--input-border-color-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
@include bodySmallTypography;
|
||||||
|
margin-top: $s-4;
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
color: var(--input-border-color-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
color: var(--input-border-color-success);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@
|
||||||
[app.common.files.changes-builder :as cb]
|
[app.common.files.changes-builder :as cb]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.record :as cr]
|
[app.common.record :as cr]
|
||||||
|
[app.common.text :as txt]
|
||||||
[app.common.types.shape :as cts]
|
[app.common.types.shape :as cts]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace.changes :as ch]
|
[app.main.data.workspace.changes :as ch]
|
||||||
|
@ -19,6 +20,7 @@
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.plugins.events :as events]
|
[app.plugins.events :as events]
|
||||||
[app.plugins.file :as file]
|
[app.plugins.file :as file]
|
||||||
|
[app.plugins.library :as library]
|
||||||
[app.plugins.page :as page]
|
[app.plugins.page :as page]
|
||||||
[app.plugins.shape :as shape]
|
[app.plugins.shape :as shape]
|
||||||
[app.plugins.utils :as utils]
|
[app.plugins.utils :as utils]
|
||||||
|
@ -129,13 +131,29 @@
|
||||||
[_]
|
[_]
|
||||||
(create-shape :rect))
|
(create-shape :rect))
|
||||||
|
|
||||||
|
(createText
|
||||||
|
[_ text]
|
||||||
|
(let [page-id (:current-page-id @st/state)
|
||||||
|
page (dm/get-in @st/state [:workspace-data :pages-index page-id])
|
||||||
|
shape (-> (cts/setup-shape {:type :text :x 0 :y 0 :grow-type :auto-width})
|
||||||
|
(txt/change-text text)
|
||||||
|
(assoc :position-data nil))
|
||||||
|
changes
|
||||||
|
(-> (cb/empty-changes)
|
||||||
|
(cb/with-page page)
|
||||||
|
(cb/with-objects (:objects page))
|
||||||
|
(cb/add-object shape))]
|
||||||
|
(st/emit! (ch/commit-changes changes))
|
||||||
|
(shape/data->shape-proxy shape)))
|
||||||
|
|
||||||
(createShapeFromSvg
|
(createShapeFromSvg
|
||||||
[_ svg-string]
|
[_ svg-string]
|
||||||
(let [id (uuid/next)
|
(when (some? svg-string)
|
||||||
page-id (:current-page-id @st/state)]
|
(let [id (uuid/next)
|
||||||
(st/emit! (dwm/create-svg-shape id "svg" svg-string (gpt/point 0 0)))
|
page-id (:current-page-id @st/state)]
|
||||||
(shape/data->shape-proxy
|
(st/emit! (dwm/create-svg-shape id "svg" svg-string (gpt/point 0 0)))
|
||||||
(dm/get-in @st/state [:workspace-data :pages-index page-id :objects id])))))
|
(shape/data->shape-proxy
|
||||||
|
(dm/get-in @st/state [:workspace-data :pages-index page-id :objects id]))))))
|
||||||
|
|
||||||
(defn create-context
|
(defn create-context
|
||||||
[]
|
[]
|
||||||
|
@ -144,4 +162,5 @@
|
||||||
{:name "root" :get #(.getRoot ^js %)}
|
{:name "root" :get #(.getRoot ^js %)}
|
||||||
{:name "currentPage" :get #(.getPage ^js %)}
|
{:name "currentPage" :get #(.getPage ^js %)}
|
||||||
{:name "selection" :get #(.getSelectedShapes ^js %)}
|
{:name "selection" :get #(.getSelectedShapes ^js %)}
|
||||||
{:name "viewport" :get #(.getViewport ^js %)}))
|
{:name "viewport" :get #(.getViewport ^js %)}
|
||||||
|
{:name "library" :get (fn [_] (library/create-library-subcontext))}))
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
(st/emit! (dwsl/add-layout-track #{id} :row {:type type :value value}))))
|
(st/emit! (dwsl/add-layout-track #{id} :row {:type type :value value}))))
|
||||||
|
|
||||||
(addRowAtIndex
|
(addRowAtIndex
|
||||||
[self type value index]
|
[self index type value]
|
||||||
(let [id (get-data self :id)
|
(let [id (get-data self :id)
|
||||||
type (keyword type)]
|
type (keyword type)]
|
||||||
(st/emit! (dwsl/add-layout-track #{id} :row {:type type :value value} index))))
|
(st/emit! (dwsl/add-layout-track #{id} :row {:type type :value value} index))))
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
(st/emit! (dwsl/add-layout-track #{id} :column {:type type :value value}))))
|
(st/emit! (dwsl/add-layout-track #{id} :column {:type type :value value}))))
|
||||||
|
|
||||||
(addColumnAtIndex
|
(addColumnAtIndex
|
||||||
[self type value index]
|
[self index type value]
|
||||||
(let [id (get-data self :id)
|
(let [id (get-data self :id)
|
||||||
type (keyword type)]
|
type (keyword type)]
|
||||||
(st/emit! (dwsl/add-layout-track #{id} :column {:type type :value value} index))))
|
(st/emit! (dwsl/add-layout-track #{id} :column {:type type :value value} index))))
|
||||||
|
@ -170,4 +170,37 @@
|
||||||
(fn [self value]
|
(fn [self value]
|
||||||
(let [id (get-data self :id)]
|
(let [id (get-data self :id)]
|
||||||
(when (us/safe-int? value)
|
(when (us/safe-int? value)
|
||||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 value}})))))})))
|
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 value}})))))}
|
||||||
|
|
||||||
|
|
||||||
|
{:name "topPadding"
|
||||||
|
:get #(:p1 (get-state % :layout-padding))
|
||||||
|
:set
|
||||||
|
(fn [self value]
|
||||||
|
(let [id (get-data self :id)]
|
||||||
|
(when (us/safe-int? value)
|
||||||
|
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value}})))))}
|
||||||
|
|
||||||
|
{:name "rightPadding"
|
||||||
|
:get #(:p2 (get-state % :layout-padding))
|
||||||
|
:set
|
||||||
|
(fn [self value]
|
||||||
|
(let [id (get-data self :id)]
|
||||||
|
(when (us/safe-int? value)
|
||||||
|
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value}})))))}
|
||||||
|
|
||||||
|
{:name "bottomPadding"
|
||||||
|
:get #(:p3 (get-state % :layout-padding))
|
||||||
|
:set
|
||||||
|
(fn [self value]
|
||||||
|
(let [id (get-data self :id)]
|
||||||
|
(when (us/safe-int? value)
|
||||||
|
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p3 value}})))))}
|
||||||
|
|
||||||
|
{:name "leftPadding"
|
||||||
|
:get #(:p4 (get-state % :layout-padding))
|
||||||
|
:set
|
||||||
|
(fn [self value]
|
||||||
|
(let [id (get-data self :id)]
|
||||||
|
(when (us/safe-int? value)
|
||||||
|
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p4 value}})))))})))
|
||||||
|
|
96
frontend/src/app/plugins/library.cljs
Normal file
96
frontend/src/app/plugins/library.cljs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
;; 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/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
(ns app.plugins.library
|
||||||
|
"RPC for plugins runtime."
|
||||||
|
(:require
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.record :as cr]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.plugins.utils :as utils :refer [get-data]]
|
||||||
|
[app.util.object :as obj]))
|
||||||
|
|
||||||
|
(defn get-library-info
|
||||||
|
([self attr]
|
||||||
|
(let [lib-id (get-data self :id)
|
||||||
|
current-file-id (:current-file-id @st/state)]
|
||||||
|
(if (= lib-id current-file-id)
|
||||||
|
(dm/get-in @st/state [:workspace-file attr])
|
||||||
|
(dm/get-in @st/state [:workspace-libraries lib-id attr]))))
|
||||||
|
|
||||||
|
([self attr mapfn]
|
||||||
|
(-> (get-library-info self attr)
|
||||||
|
(mapfn))))
|
||||||
|
|
||||||
|
(defn get-library-data
|
||||||
|
([self attr]
|
||||||
|
(let [lib-id (get-data self :id)
|
||||||
|
current-file-id (:current-file-id @st/state)]
|
||||||
|
(if (= lib-id current-file-id)
|
||||||
|
(dm/get-in @st/state [:workspace-data attr])
|
||||||
|
(dm/get-in @st/state [:workspace-libraries lib-id :data attr]))))
|
||||||
|
|
||||||
|
([self attr mapfn]
|
||||||
|
(-> (get-library-data self attr)
|
||||||
|
(mapfn))))
|
||||||
|
|
||||||
|
(defn- array-to-js
|
||||||
|
[value]
|
||||||
|
(.freeze
|
||||||
|
js/Object
|
||||||
|
(apply array (->> value (map utils/to-js)))))
|
||||||
|
|
||||||
|
(deftype Library [_data]
|
||||||
|
Object)
|
||||||
|
|
||||||
|
(defn create-library
|
||||||
|
[data]
|
||||||
|
(cr/add-properties!
|
||||||
|
(Library. data)
|
||||||
|
{:name "_data"
|
||||||
|
:enumerable false}
|
||||||
|
|
||||||
|
{:name "id"
|
||||||
|
:get (fn [self]
|
||||||
|
(str (:id (obj/get self "_data"))))}
|
||||||
|
|
||||||
|
{:name "name"
|
||||||
|
:get (fn [self]
|
||||||
|
(get-library-info self :name))}
|
||||||
|
|
||||||
|
{:name "colors"
|
||||||
|
:get (fn [self]
|
||||||
|
(array-to-js (get-library-data self :colors vals)))}
|
||||||
|
|
||||||
|
{:name "typographies"
|
||||||
|
:get (fn [self]
|
||||||
|
(array-to-js (get-library-data self :typographies vals)))}
|
||||||
|
|
||||||
|
{:name "components"
|
||||||
|
:get (fn [self]
|
||||||
|
(array-to-js (get-library-data self :components vals)))}))
|
||||||
|
|
||||||
|
(deftype PenpotLibrarySubcontext []
|
||||||
|
Object
|
||||||
|
(find
|
||||||
|
[_ _name])
|
||||||
|
|
||||||
|
(find [_]))
|
||||||
|
|
||||||
|
(defn create-library-subcontext
|
||||||
|
[]
|
||||||
|
(cr/add-properties!
|
||||||
|
(PenpotLibrarySubcontext.)
|
||||||
|
{:name "local" :get
|
||||||
|
(fn [_]
|
||||||
|
(let [file (get @st/state :workspace-file)
|
||||||
|
data (get @st/state :workspace-data)]
|
||||||
|
(create-library (assoc file :data data))))}
|
||||||
|
|
||||||
|
{:name "connected" :get
|
||||||
|
(fn [_]
|
||||||
|
(let [libraries (get @st/state :workspace-libraries)]
|
||||||
|
(apply array (->> libraries vals (map create-library)))))}))
|
|
@ -7,10 +7,13 @@
|
||||||
(ns app.plugins.shape
|
(ns app.plugins.shape
|
||||||
"RPC for plugins runtime."
|
"RPC for plugins runtime."
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.files.helpers :as cfh]
|
[app.common.files.helpers :as cfh]
|
||||||
[app.common.record :as crc]
|
[app.common.record :as crc]
|
||||||
|
[app.common.spec :as us]
|
||||||
[app.common.text :as txt]
|
[app.common.text :as txt]
|
||||||
|
[app.common.types.shape :as cts]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace :as udw]
|
[app.main.data.workspace :as udw]
|
||||||
[app.main.data.workspace.changes :as dwc]
|
[app.main.data.workspace.changes :as dwc]
|
||||||
|
@ -24,17 +27,11 @@
|
||||||
|
|
||||||
(declare data->shape-proxy)
|
(declare data->shape-proxy)
|
||||||
|
|
||||||
(defn- make-fills
|
(defn- array-to-js
|
||||||
[fills]
|
[value]
|
||||||
(.freeze
|
(.freeze
|
||||||
js/Object
|
js/Object
|
||||||
(apply array (->> fills (map utils/to-js)))))
|
(apply array (->> value (map utils/to-js)))))
|
||||||
|
|
||||||
(defn- make-strokes
|
|
||||||
[strokes]
|
|
||||||
(.freeze
|
|
||||||
js/Object
|
|
||||||
(apply array (->> strokes (map utils/to-js)))))
|
|
||||||
|
|
||||||
(defn- locate-shape
|
(defn- locate-shape
|
||||||
[shape-id]
|
[shape-id]
|
||||||
|
@ -86,7 +83,8 @@
|
||||||
|
|
||||||
(addGridLayout [self]
|
(addGridLayout [self]
|
||||||
(let [id (get-data self :id)]
|
(let [id (get-data self :id)]
|
||||||
(st/emit! (dwsl/create-layout-from-id id :grid :from-frame? true :calculate-params? false)))))
|
(st/emit! (dwsl/create-layout-from-id id :grid :from-frame? true :calculate-params? false))
|
||||||
|
(grid/grid-layout-proxy (obj/get self "_data")))))
|
||||||
|
|
||||||
(crc/define-properties!
|
(crc/define-properties!
|
||||||
ShapeProxy
|
ShapeProxy
|
||||||
|
@ -107,6 +105,118 @@
|
||||||
{:name "type"
|
{:name "type"
|
||||||
:get (get-data-fn :type name)}
|
:get (get-data-fn :type name)}
|
||||||
|
|
||||||
|
{:name "name"
|
||||||
|
:get #(get-state % :name)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)]
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :name value)))))}
|
||||||
|
|
||||||
|
{:name "blocked"
|
||||||
|
:get #(get-state % :blocked boolean)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)]
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :blocked value)))))}
|
||||||
|
|
||||||
|
{:name "hidden"
|
||||||
|
:get #(get-state % :hidden boolean)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)]
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :hidden value)))))}
|
||||||
|
|
||||||
|
{:name "proportionLock"
|
||||||
|
:get #(get-state % :proportion-lock boolean)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)]
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :proportion-lock value)))))}
|
||||||
|
|
||||||
|
{:name "constraintsHorizontal"
|
||||||
|
:get #(get-state % :constraints-h d/name)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)
|
||||||
|
value (keyword value)]
|
||||||
|
(when (contains? cts/horizontal-constraint-types value)
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :constraints-h value))))))}
|
||||||
|
|
||||||
|
{:name "constraintsVertical"
|
||||||
|
:get #(get-state % :constraints-v d/name)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)
|
||||||
|
value (keyword value)]
|
||||||
|
(when (contains? cts/vertical-constraint-types value)
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :constraints-v value))))))}
|
||||||
|
|
||||||
|
{:name "borderRadius"
|
||||||
|
:get #(get-state % :rx)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)]
|
||||||
|
(when (us/safe-int? value)
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :rx value :ry value))))))}
|
||||||
|
|
||||||
|
{:name "borderRadiusTopLeft"
|
||||||
|
:get #(get-state % :r1)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)]
|
||||||
|
(when (us/safe-int? value)
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :r1 value))))))}
|
||||||
|
|
||||||
|
{:name "borderRadiusTopRight"
|
||||||
|
:get #(get-state % :r2)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)]
|
||||||
|
(when (us/safe-int? value)
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :r2 value))))))}
|
||||||
|
|
||||||
|
{:name "borderRadiusBottomRight"
|
||||||
|
:get #(get-state % :r3)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)]
|
||||||
|
(when (us/safe-int? value)
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :r3 value))))))}
|
||||||
|
|
||||||
|
{:name "borderRadiusBottomLeft"
|
||||||
|
:get #(get-state % :r4)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)]
|
||||||
|
(when (us/safe-int? value)
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :r4 value))))))}
|
||||||
|
|
||||||
|
{:name "opacity"
|
||||||
|
:get #(get-state % :opacity)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)]
|
||||||
|
(when (and (us/safe-number? value) (>= value 0) (<= value 1))
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :opacity value))))))}
|
||||||
|
|
||||||
|
{:name "blendMode"
|
||||||
|
:get #(get-state % :blend-mode d/name)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)
|
||||||
|
value (keyword value)]
|
||||||
|
(when (contains? cts/blend-modes value)
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :blend-mode value))))))}
|
||||||
|
|
||||||
|
{:name "shadows"
|
||||||
|
:get #(get-state % :shadow array-to-js)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)
|
||||||
|
value (mapv #(utils/from-js %) value)]
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :shadows value)))))}
|
||||||
|
|
||||||
|
{:name "blur"
|
||||||
|
:get #(get-state % :blur utils/to-js)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)
|
||||||
|
value (utils/from-js value)]
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :blur value)))))}
|
||||||
|
|
||||||
|
{:name "exports"
|
||||||
|
:get #(get-state % :exports array-to-js)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)
|
||||||
|
value (mapv #(utils/from-js %) value)]
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :exports value)))))}
|
||||||
|
|
||||||
|
;; Geometry properties
|
||||||
{:name "x"
|
{:name "x"
|
||||||
:get #(get-state % :x)
|
:get #(get-state % :x)
|
||||||
:set
|
:set
|
||||||
|
@ -183,21 +293,22 @@
|
||||||
{:name "height"
|
{:name "height"
|
||||||
:get #(get-state % :height)}
|
:get #(get-state % :height)}
|
||||||
|
|
||||||
{:name "name"
|
{:name "flipX"
|
||||||
:get #(get-state % :name)
|
:get #(get-state % :flip-x)}
|
||||||
:set (fn [self value]
|
|
||||||
(let [id (get-data self :id)]
|
|
||||||
(st/emit! (dwc/update-shapes [id] #(assoc % :name value)))))}
|
|
||||||
|
|
||||||
|
{:name "flipY"
|
||||||
|
:get #(get-state % :flip-y)}
|
||||||
|
|
||||||
|
;; Strokes and fills
|
||||||
{:name "fills"
|
{:name "fills"
|
||||||
:get #(get-state % :fills make-fills)
|
:get #(get-state % :fills array-to-js)
|
||||||
:set (fn [self value]
|
:set (fn [self value]
|
||||||
(let [id (get-data self :id)
|
(let [id (get-data self :id)
|
||||||
value (mapv #(utils/from-js %) value)]
|
value (mapv #(utils/from-js %) value)]
|
||||||
(st/emit! (dwc/update-shapes [id] #(assoc % :fills value)))))}
|
(st/emit! (dwc/update-shapes [id] #(assoc % :fills value)))))}
|
||||||
|
|
||||||
{:name "strokes"
|
{:name "strokes"
|
||||||
:get #(get-state % :strokes make-strokes)
|
:get #(get-state % :strokes array-to-js)
|
||||||
:set (fn [self value]
|
:set (fn [self value]
|
||||||
(let [id (get-data self :id)
|
(let [id (get-data self :id)
|
||||||
value (mapv #(utils/from-js %) value)]
|
value (mapv #(utils/from-js %) value)]
|
||||||
|
@ -220,8 +331,16 @@
|
||||||
(fn [self]
|
(fn [self]
|
||||||
(let [layout (get-state self :layout)]
|
(let [layout (get-state self :layout)]
|
||||||
(when (= :grid layout)
|
(when (= :grid layout)
|
||||||
(grid/grid-layout-proxy data))))})
|
(grid/grid-layout-proxy data))))}
|
||||||
|
|
||||||
|
{:name "guides"
|
||||||
|
:get #(get-state % :grids array-to-js)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)
|
||||||
|
value (mapv #(utils/from-js %) value)]
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :grids value)))))})
|
||||||
|
|
||||||
|
;; TODO: Flex properties
|
||||||
#_(crc/add-properties!
|
#_(crc/add-properties!
|
||||||
{:name "flex"
|
{:name "flex"
|
||||||
:get
|
:get
|
||||||
|
@ -235,9 +354,18 @@
|
||||||
(obj/unset! "addFlexLayout")))
|
(obj/unset! "addFlexLayout")))
|
||||||
|
|
||||||
(cond-> (cfh/text-shape? data)
|
(cond-> (cfh/text-shape? data)
|
||||||
(crc/add-properties!
|
(-> (crc/add-properties!
|
||||||
{:name "characters"
|
{:name "characters"
|
||||||
:get #(get-state % :content txt/content->text)
|
:get #(get-state % :content txt/content->text)
|
||||||
:set (fn [self value]
|
:set (fn [self value]
|
||||||
(let [id (get-data self :id)]
|
(let [id (get-data self :id)]
|
||||||
(st/emit! (dwc/update-shapes [id] #(txt/change-text % value)))))}))))
|
(st/emit! (dwc/update-shapes [id] #(txt/change-text % value)))))})
|
||||||
|
|
||||||
|
(crc/add-properties!
|
||||||
|
{:name "growType"
|
||||||
|
:get #(get-state % :grow-type d/name)
|
||||||
|
:set (fn [self value]
|
||||||
|
(let [id (get-data self :id)
|
||||||
|
value (keyword value)]
|
||||||
|
(when (contains? #{:auto-width :auto-height :fixed} value)
|
||||||
|
(st/emit! (dwc/update-shapes [id] #(assoc % :grow-type value))))))})))))
|
||||||
|
|
|
@ -5175,3 +5175,33 @@ msgstr "Update"
|
||||||
|
|
||||||
msgid "workspace.viewport.click-to-close-path"
|
msgid "workspace.viewport.click-to-close-path"
|
||||||
msgstr "Click to close the path"
|
msgstr "Click to close the path"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.title"
|
||||||
|
msgstr "Plugins"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.search-placeholder"
|
||||||
|
msgstr "Write a plugin URL"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.install"
|
||||||
|
msgstr "Install"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.installed-plugins"
|
||||||
|
msgstr "Installed plugins"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.empty-plugins"
|
||||||
|
msgstr "No plugins installed yet"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.button-open"
|
||||||
|
msgstr "Open"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.error.url"
|
||||||
|
msgstr "The plugin doesn't exist or the URL is not correct."
|
||||||
|
|
||||||
|
msgid "workspace.plugins.success"
|
||||||
|
msgstr "Plugin correctly loaded."
|
||||||
|
|
||||||
|
msgid "workspace.plugins.menu.title"
|
||||||
|
msgstr "Plugins"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.menu.plugins-manager"
|
||||||
|
msgstr "Plugins manager"
|
||||||
|
|
|
@ -5301,3 +5301,34 @@ msgstr "Actualizar"
|
||||||
|
|
||||||
msgid "workspace.viewport.click-to-close-path"
|
msgid "workspace.viewport.click-to-close-path"
|
||||||
msgstr "Pulsar para cerrar la ruta"
|
msgstr "Pulsar para cerrar la ruta"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.title"
|
||||||
|
msgstr "Extensiones"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.search-placeholder"
|
||||||
|
msgstr "Intruduzca URL de la extensión"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.install"
|
||||||
|
msgstr "Instalar"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.installed-plugins"
|
||||||
|
msgstr "Extensiones instaladas"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.empty-plugins"
|
||||||
|
msgstr "No se encuentran extensiones"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.button-open"
|
||||||
|
msgstr "Abrir"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.error.url"
|
||||||
|
msgstr "La extensión no existe o la url no es correcta."
|
||||||
|
|
||||||
|
msgid "workspace.plugins.success"
|
||||||
|
msgstr "Extensión cargada correctamente."
|
||||||
|
|
||||||
|
msgid "workspace.plugins.menu.title"
|
||||||
|
msgstr "Extensiones"
|
||||||
|
|
||||||
|
msgid "workspace.plugins.menu.plugins-manager"
|
||||||
|
msgstr "Gestor de extensiones"
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue