diff --git a/common/src/app/common/files/shapes_helpers.cljc b/common/src/app/common/files/shapes_helpers.cljc index a36fadf916..04e1d70911 100644 --- a/common/src/app/common/files/shapes_helpers.cljc +++ b/common/src/app/common/files/shapes_helpers.cljc @@ -16,7 +16,7 @@ [app.common.uuid :as uuid])) (defn prepare-add-shape - [changes shape objects _selected] + [changes shape objects] (let [index (:index (meta shape)) id (:id shape) @@ -55,53 +55,115 @@ changes))) (defn prepare-create-artboard-from-selection - [changes id parent-id objects selected index frame-name without-fill?] - (let [selected-objs (map #(get objects %) selected) - new-index (or index - (cfh/get-index-replacement selected objects))] - (when (d/not-empty? selected) - (let [srect (gsh/shapes->rect selected-objs) - selected-id (first selected) + ([changes id parent-id objects selected index frame-name without-fill?] + (prepare-create-artboard-from-selection + changes id parent-id objects selected index frame-name without-fill? nil)) - frame-id (dm/get-in objects [selected-id :frame-id]) - parent-id (or parent-id (dm/get-in objects [selected-id :parent-id])) + ([changes id parent-id objects selected index frame-name without-fill? target-cell-id] + (let [selected-objs (map #(get objects %) selected) + new-index (or index + (cfh/get-index-replacement selected objects))] + (when (d/not-empty? selected) + (let [srect (gsh/shapes->rect selected-objs) + selected-id (first selected) - attrs {:type :frame - :x (:x srect) - :y (:y srect) - :width (:width srect) - :height (:height srect)} + frame-id (dm/get-in objects [selected-id :frame-id]) + parent-id (or parent-id (dm/get-in objects [selected-id :parent-id])) + base-parent (get objects parent-id) - shape (cts/setup-shape - (cond-> attrs - (some? id) - (assoc :id id) + attrs {:type :frame + :x (:x srect) + :y (:y srect) + :width (:width srect) + :height (:height srect)} - (some? frame-name) - (assoc :name frame-name) + shape (cts/setup-shape + (cond-> attrs + (some? id) + (assoc :id id) - :always - (assoc :frame-id frame-id - :parent-id parent-id - :shapes (into [] selected)) + (some? frame-name) + (assoc :name frame-name) - :always - (with-meta {:index new-index}) + :always + (assoc :frame-id frame-id + :parent-id parent-id + :shapes (into [] selected)) - (or (not= frame-id uuid/zero) without-fill?) - (assoc :fills [] :hide-in-viewer true))) + :always + (with-meta {:index new-index}) - [shape changes] - (prepare-add-shape changes shape objects selected) + (or (not= frame-id uuid/zero) without-fill?) + (assoc :fills [] :hide-in-viewer true))) - changes - (prepare-move-shapes-into-frame changes (:id shape) selected objects) + [shape changes] + (prepare-add-shape changes shape objects) - changes - (cond-> changes - (ctl/grid-layout? objects (:parent-id shape)) - (-> (pcb/update-shapes [(:parent-id shape)] ctl/assign-cells) - (pcb/reorder-grid-children [(:parent-id shape)])))] + changes + (prepare-move-shapes-into-frame changes (:id shape) selected objects) - [shape changes])))) + changes + (cond-> changes + (ctl/grid-layout? objects (:parent-id shape)) + (-> (cond-> (some? target-cell-id) + (pcb/update-shapes + [(:parent-id shape)] + (fn [parent] + (-> parent + (assoc :layout-grid-cells (:layout-grid-cells base-parent)) + (assoc-in [:layout-grid-cells target-cell-id :shapes] [id]) + (assoc :position :auto))))) + (pcb/update-shapes [(:parent-id shape)] ctl/assign-cells) + (pcb/reorder-grid-children [(:parent-id shape)])))] + [shape changes]))))) + + +(defn prepare-create-empty-artboard + [changes frame-id parent-id objects index frame-name without-fill? target-cell-id] + + (let [base-parent (get objects parent-id) + + attrs {:type :frame + :x 0 + :y 0 + :width 0.01 + :height 0.01} + + shape (cts/setup-shape + (cond-> attrs + (some? frame-id) + (assoc :id frame-id) + + (some? frame-name) + (assoc :name frame-name) + + :always + (assoc :frame-id frame-id + :parent-id parent-id + :shapes []) + + :always + (with-meta {:index index}) + + (or (not= frame-id uuid/zero) without-fill?) + (assoc :fills [] :hide-in-viewer true))) + + [shape changes] + (prepare-add-shape changes shape objects) + + changes + (cond-> changes + (ctl/grid-layout? objects (:parent-id shape)) + (-> (cond-> (some? target-cell-id) + (pcb/update-shapes + [(:parent-id shape)] + (fn [parent] + (-> parent + (assoc :layout-grid-cells (:layout-grid-cells base-parent)) + (assoc-in [:layout-grid-cells target-cell-id :shapes] [frame-id]) + (assoc :position :auto))))) + (pcb/update-shapes [(:parent-id shape)] ctl/assign-cells) + (pcb/reorder-grid-children [(:parent-id shape)])))] + + [shape changes])) diff --git a/common/src/app/common/types/shape/layout.cljc b/common/src/app/common/types/shape/layout.cljc index c6aa7cb55b..183d16c8a2 100644 --- a/common/src/app/common/types/shape/layout.cljc +++ b/common/src/app/common/types/shape/layout.cljc @@ -1227,6 +1227,7 @@ target-cell (-> prev-cell (assoc + :position :manual :row new-row :column new-column :row-span new-row-span @@ -1459,7 +1460,6 @@ (defn valid-area-cells? [cells] - (let [{:keys [first-row last-row first-column last-column cell-coords]} (cells-coordinates cells)] (every? #(contains? cell-coords %) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 0eee768b7b..055d65e57d 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1506,10 +1506,13 @@ (ptk/reify ::show-grid-cell-context-menu ptk/WatchEvent (watch [_ state _] - (let [cells (get-in state [:workspace-grid-edition grid-id :selected])] + (let [objects (wsh/lookup-page-objects state) + grid (get objects grid-id) + cells (->> (get-in state [:workspace-grid-edition grid-id :selected]) + (map #(get-in grid [:layout-grid-cells %])))] (rx/of (show-context-menu (-> params (assoc :kind :grid-cells - :grid-id grid-id + :grid grid :cells cells)))))))) (def hide-context-menu (ptk/reify ::hide-context-menu diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index 98624d1286..dbb65351ec 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -11,6 +11,7 @@ [app.common.data.macros :as dm] [app.common.files.changes-builder :as pcb] [app.common.files.helpers :as cfh] + [app.common.files.shapes-helpers :as cfsh] [app.common.geom.point :as gpt] [app.common.geom.shapes.flex-layout :as flex] [app.common.geom.shapes.grid-layout :as grid] @@ -583,6 +584,37 @@ (ptk/data-event :layout/update [layout-id]) (dwu/commit-undo-transaction undo-id)))))) +(defn merge-cells + [layout-id ids] + + (ptk/reify ::merge-cells + ptk/WatchEvent + (watch [_ _ _] + (let [undo-id (js/Symbol)] + (rx/of + (dwu/start-undo-transaction undo-id) + (dwc/update-shapes + [layout-id] + (fn [shape objects] + (let [cells (->> ids (map #(get-in shape [:layout-grid-cells %]))) + + {:keys [first-row first-column last-row last-column]} + (ctl/cells-coordinates cells) + + target-cell + (ctl/get-cell-by-position shape first-row first-column)] + (-> shape + (ctl/resize-cell-area + (:row target-cell) (:column target-cell) + first-row + first-column + (inc (- last-row first-row)) + (inc (- last-column first-column))) + (ctl/assign-cells objects))))) + (dwge/clean-selection layout-id) + (ptk/data-event :layout/update [layout-id]) + (dwu/commit-undo-transaction undo-id)))))) + (defn update-grid-cell-position [layout-id cell-id props] @@ -607,3 +639,58 @@ (ctl/assign-cells objects))))) (ptk/data-event :layout/update [layout-id]) (dwu/commit-undo-transaction undo-id)))))) + + +(defn create-cell-board + [layout-id cell-ids] + (ptk/reify ::create-cell-board + ptk/WatchEvent + (watch [it state _] + (let [page-id (:current-page-id state) + objects (wsh/lookup-page-objects state) + frame-id (uuid/next) + + undo-id (js/Symbol) + + shape (get objects layout-id) + cells (->> cell-ids (map #(get-in shape [:layout-grid-cells %]))) + selected (into #{} (mapcat :shapes) cells) + + {:keys [first-row first-column last-row last-column]} (ctl/cells-coordinates cells) + + target-cell (ctl/get-cell-by-position shape first-row first-column) + + [_ changes] + (-> (pcb/empty-changes it page-id) + (pcb/with-objects objects) + (cond-> (d/not-empty? selected) + (cfsh/prepare-create-artboard-from-selection + frame-id layout-id objects selected 0 nil true (:id target-cell))) + + (cond-> (empty? (seq selected)) + (cfsh/prepare-create-empty-artboard + frame-id layout-id objects 0 nil true (:id target-cell)))) + + changes + (-> changes + (pcb/update-shapes + [frame-id] + (fn [shape] + (-> shape + (assoc :layout-item-h-sizing :fill) + (assoc :layout-item-v-sizing :fill)))) + (pcb/update-shapes + [layout-id] + (fn [shape] + (let [new-row-span (inc (- last-row first-row)) + new-col-span (inc (- last-column first-column))] + (-> shape + (ctl/resize-cell-area + (:row target-cell) (:column target-cell) + first-row first-column new-row-span new-col-span))))))] + + (rx/of + (dwu/start-undo-transaction undo-id) + (dwc/commit-changes changes) + (ptk/data-event :layout/update [layout-id]) + (dwu/commit-undo-transaction undo-id)))))) diff --git a/frontend/src/app/main/data/workspace/shapes.cljs b/frontend/src/app/main/data/workspace/shapes.cljs index 42422fa521..9a95a69791 100644 --- a/frontend/src/app/main/data/workspace/shapes.cljs +++ b/frontend/src/app/main/data/workspace/shapes.cljs @@ -41,12 +41,11 @@ (watch [it state _] (let [page-id (:current-page-id state) objects (wsh/lookup-page-objects state page-id) - selected (wsh/lookup-selected state) [shape changes] (-> (pcb/empty-changes it page-id) (pcb/with-objects objects) - (cfsh/prepare-add-shape shape objects selected)) + (cfsh/prepare-add-shape shape objects)) changes (cond-> changes (cfh/text-shape? shape) diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index 7107b5a47f..3312782eac 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -49,7 +49,7 @@ (dom/stop-propagation event)) (mf/defc menu-entry - [{:keys [title shortcut on-click on-pointer-enter on-pointer-leave on-unmount children selected? icon] :as props}] + [{:keys [title shortcut on-click on-pointer-enter on-pointer-leave on-unmount children selected? icon disabled] :as props}] (let [submenu-ref (mf/use-ref nil) hovering? (mf/use-ref false) new-css-system (mf/use-ctx ctx/new-css-system) @@ -89,6 +89,7 @@ [:li {:class (if new-css-system (dom/classnames (css :icon-menu-item) true) (dom/classnames :icon-menu-item true)) + :disabled disabled :ref set-dom-node :on-click on-click :on-pointer-enter on-pointer-enter @@ -113,6 +114,7 @@ (dom/classnames (css :title) true) (dom/classnames :title true))} title]] [:li {:class (dom/classnames (css :context-menu-item) new-css-system) + :disabled disabled :ref set-dom-node :on-click on-click :on-pointer-enter on-pointer-enter @@ -579,33 +581,50 @@ (if (= type :column) [:* - [:& menu-entry {:title "Duplicate column" :on-click do-duplicate-track}] - [:& menu-entry {:title "Add 1 column to the left" :on-click do-add-track-before}] - [:& menu-entry {:title "Add 1 column to the right" :on-click do-add-track-after}] - [:& menu-entry {:title "Delete column" :on-click do-delete-track}]] + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.column.duplicate") :on-click do-duplicate-track}] + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.column.add-before") :on-click do-add-track-before}] + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.column.add-after") :on-click do-add-track-after}] + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.column.delete") :on-click do-delete-track}]] [:* - [:& menu-entry {:title "Duplicate row" :on-click do-duplicate-track}] - [:& menu-entry {:title "Add 1 row above" :on-click do-add-track-before}] - [:& menu-entry {:title "Add 1 row bellow" :on-click do-add-track-after}] - [:& menu-entry {:title "Delete row" :on-click do-delete-track}]]))) + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.row.duplicate") :on-click do-duplicate-track}] + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.row.add-before") :on-click do-add-track-before}] + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.row.add-after") :on-click do-add-track-after}] + [:& menu-entry {:title (tr "workspace.context-menu.grid-track.row.delete") :on-click do-delete-track}]]))) (mf/defc grid-cells-context-menu [{:keys [mdata] :as props}] - (let [{:keys [grid-id cells]} mdata + (let [{:keys [grid cells]} mdata + + single? (= (count cells) 1) + can-merge? + (mf/use-memo + (mf/deps cells) + #(ctl/valid-area-cells? cells)) do-merge-cells (mf/use-callback - (mf/deps grid-id cells) - (fn [])) + (mf/deps grid cells) + (fn [] + (st/emit! (dwsl/merge-cells (:id grid) (map :id cells))))) do-create-board (mf/use-callback - (mf/deps grid-id cells) - (fn []))] + (mf/deps grid cells) + (fn [] + (st/emit! (dwsl/create-cell-board (:id grid) (map :id cells)))))] [:* - [:& menu-entry {:title "Merge cells" :on-click do-merge-cells}] - [:& menu-entry {:title "Create board" :on-click do-create-board}]])) + (when (not single?) + [:& menu-entry {:title (tr "workspace.context-menu.grid-cells.merge") + :on-click do-merge-cells + :disabled (not can-merge?)}]) + + (when single? + [:& menu-entry {:title (tr "workspace.context-menu.grid-cells.area") + :on-click do-merge-cells}]) + + [:& menu-entry {:title (tr "workspace.context-menu.grid-cells.create-board") + :on-click do-create-board}]])) (mf/defc context-menu diff --git a/frontend/src/app/main/ui/workspace/context_menu.scss b/frontend/src/app/main/ui/workspace/context_menu.scss index 94f73018ef..f8f8573a13 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.scss +++ b/frontend/src/app/main/ui/workspace/context_menu.scss @@ -110,3 +110,10 @@ } } } + +.icon-menu-item[disabled], +.context-menu-item[disabled] +{ + pointer-events: none; + opacity: 0.6; +} diff --git a/frontend/translations/en.po b/frontend/translations/en.po index f1206c084c..3d32f6632e 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -5057,3 +5057,37 @@ msgstr "Choose image" msgid "workspace.options.guides.title" msgstr "Guides" + +msgid "workspace.context-menu.grid-track.column.duplicate" +msgstr "Duplicate column" + +msgid "workspace.context-menu.grid-track.column.add-before" +msgstr "Add 1 column to the left" + +msgid "workspace.context-menu.grid-track.column.add-after" +msgstr "Add 1 column to the right" + +msgid "workspace.context-menu.grid-track.column.delete" +msgstr "Delete column" + +msgid "workspace.context-menu.grid-track.row.duplicate" +msgstr "Duplicate row" + +msgid "workspace.context-menu.grid-track.row.add-before" +msgstr "Add 1 row above" + +msgid "workspace.context-menu.grid-track.row.add-after" +msgstr "Add 1 row bellow" + +msgid "workspace.context-menu.grid-track.row.delete" +msgstr "Delete row" + +msgid "workspace.context-menu.grid-cells.merge" +msgstr "Merge cells" + +msgid "workspace.context-menu.grid-cells.area" +msgstr "Create area" + +msgid "workspace.context-menu.grid-cells.create-board" +msgstr "Create board" + diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 7b4e5b85ed..9fcdbdbcf0 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -5146,3 +5146,37 @@ msgstr "Elegir imagen" msgid "workspace.options.guides.title" msgstr "Guías" + +msgid "workspace.context-menu.grid-track.column.duplicate" +msgstr "Duplicar columna" + +msgid "workspace.context-menu.grid-track.column.add-before" +msgstr "Añadir 1 columna a la izquierda" + +msgid "workspace.context-menu.grid-track.column.add-after" +msgstr "Añadir 1 columna a la derecha" + +msgid "workspace.context-menu.grid-track.column.delete" +msgstr "Borrar columna" + +msgid "workspace.context-menu.grid-track.row.duplicate" +msgstr "Duplicar fila" + +msgid "workspace.context-menu.grid-track.row.add-before" +msgstr "Añadir 1 fila encima" + +msgid "workspace.context-menu.grid-track.row.add-after" +msgstr "Añadir 1 fila debajo" + +msgid "workspace.context-menu.grid-track.row.delete" +msgstr "Borrar fila" + +msgid "workspace.context-menu.grid-cells.merge" +msgstr "Fusionar celdas" + +msgid "workspace.context-menu.grid-cells.area" +msgstr "Crear area" + +msgid "workspace.context-menu.grid-cells.create-board" +msgstr "Crear tablero" +