Compare commits

..

1 Commits

Author SHA1 Message Date
David Barragán Merino
32d0fe6463 🔧 Use selfhosted runner 01 to generate the bundle 2026-01-20 18:09:18 +01:00
12 changed files with 177 additions and 491 deletions

View File

@@ -40,7 +40,7 @@ on:
jobs: jobs:
build-bundle: build-bundle:
name: Build and Upload Penpot Bundle name: Build and Upload Penpot Bundle
runs-on: ubuntu-24.04 runs-on: penpot-runner-01
env: env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

View File

@@ -474,8 +474,8 @@
:height #{:sizing :dimensions} :height #{:sizing :dimensions}
:max-width #{:sizing :dimensions} :max-width #{:sizing :dimensions}
:max-height #{:sizing :dimensions} :max-height #{:sizing :dimensions}
:x #{:dimensions} :x #{:spacing :dimensions}
:y #{:dimensions} :y #{:spacing :dimensions}
:rotation #{:number :rotation} :rotation #{:number :rotation}
:border-radius #{:border-radius :dimensions} :border-radius #{:border-radius :dimensions}
:row-gap #{:spacing :dimensions} :row-gap #{:spacing :dimensions}
@@ -488,7 +488,6 @@
:sided-margins #{:spacing :dimensions} :sided-margins #{:spacing :dimensions}
:line-height #{:line-height :number} :line-height #{:line-height :number}
:opacity #{:opacity} :opacity #{:opacity}
:stroke-width #{:stroke-width}
:font-size #{:font-size} :font-size #{:font-size}
:letter-spacing #{:letter-spacing} :letter-spacing #{:letter-spacing}
:fill #{:color} :fill #{:color}

View File

@@ -149,14 +149,12 @@ test.describe("Tokens: Apply token", () => {
await detachButton.click(); await detachButton.click();
// Open dropdown from input // Open dropdown from input
const dropdownBtn = layerMenuSection.getByLabel("Open token list"); const dropdownBtn = layerMenuSection.getByLabel('Open token list');
await expect(dropdownBtn).toBeVisible(); await expect(dropdownBtn).toBeVisible();
await dropdownBtn.click(); await dropdownBtn.click();
// Change token from dropdown // Change token from dropdown
const opacityLowOption = layerMenuSection.getByRole("option", { const opacityLowOption = layerMenuSection.getByRole('option', { name: 'opacity.low' });
name: "opacity.low",
});
await expect(opacityLowOption).toBeVisible(); await expect(opacityLowOption).toBeVisible();
await opacityLowOption.click(); await opacityLowOption.click();
@@ -484,219 +482,4 @@ test.describe("Tokens: Apply token", () => {
await expect(shadowSection).toHaveCount(2); await expect(shadowSection).toHaveCount(2);
}); });
}); });
test("User applies dimension token to a shape on width and height", async ({
page,
}) => {
const { workspacePage, tokensSidebar, tokenContextMenuForToken } =
await setupTokensFile(page);
// Unfolds dimensions on token panel
await page.getByRole("tab", { name: "Layers" }).click();
await workspacePage.layers.getByTestId("layer-row").nth(1).click();
const tokensTabButton = page.getByRole("tab", { name: "Tokens" });
await tokensTabButton.click();
unfoldTokenTree(tokensSidebar, "dimensions", "dimension.dimension.sm");
// Apply token to width and height token from token panel
await tokensSidebar.getByRole("button", { name: "dimension.sm" }).click();
// Check if measures sections is visible on right sidebar
const measuresSection = page.getByRole("region", {
name: "shape-measures-section",
});
await expect(measuresSection).toBeVisible();
// Check if token pill is visible on design tab on right sidebar
const dimensionSMTokenPill = measuresSection.getByRole("button", {
name: "dimension.sm",
});
await expect(dimensionSMTokenPill).toHaveCount(2);
await dimensionSMTokenPill.nth(1).click();
// Change token from dropdown
const dimensionTokenOptionXl = measuresSection.getByLabel("dimension.xl");
await expect(dimensionTokenOptionXl).toBeVisible();
await dimensionTokenOptionXl.click();
await expect(dimensionSMTokenPill).toHaveCount(1);
const dimensionXLTokenPill = measuresSection.getByRole("button", {
name: "dimension.xl",
});
await expect(dimensionXLTokenPill).toBeVisible();
// Detach token from design tab on right sidebar
const detachButton = measuresSection.getByRole("button", {
name: "Detach token",
});
await detachButton.nth(1).click();
await expect(dimensionXLTokenPill).not.toBeVisible();
});
test("User applies dimension token to a shape on x position", async ({
page,
}) => {
const { workspacePage, tokensSidebar, tokenContextMenuForToken } =
await setupTokensFile(page);
// Unfolds dimensions on token panel
await page.getByRole("tab", { name: "Layers" }).click();
await workspacePage.layers.getByTestId("layer-row").nth(1).click();
const tokensTabButton = page.getByRole("tab", { name: "Tokens" });
await tokensTabButton.click();
unfoldTokenTree(tokensSidebar, "dimensions", "dimension.dimension.sm");
// Apply token to width and height token from token panel
await tokensSidebar
.getByRole("button", { name: "dimension.sm" })
.click({ button: "right" });
await tokenContextMenuForToken.getByText("AxisX").click();
// Check if measures sections is visible on right sidebar
const measuresSection = page.getByRole("region", {
name: "shape-measures-section",
});
await expect(measuresSection).toBeVisible();
// Check if token pill is visible on design tab on right sidebar
const dimensionSMTokenPill = measuresSection.getByRole("button", {
name: "dimension.sm",
});
await expect(dimensionSMTokenPill).toBeVisible();
await dimensionSMTokenPill.click();
// Change token from dropdown
const dimensionTokenOptionXl = measuresSection.getByLabel("dimension.xl");
await expect(dimensionTokenOptionXl).toBeVisible();
await dimensionTokenOptionXl.click();
await expect(dimensionSMTokenPill).not.toBeVisible();
const dimensionXLTokenPill = measuresSection.getByRole("button", {
name: "dimension.xl",
});
await expect(dimensionXLTokenPill).toBeVisible();
// Detach token from design tab on right sidebar
const detachButton = measuresSection.getByRole("button", {
name: "Detach token",
});
await detachButton.nth(0).click();
await expect(dimensionXLTokenPill).not.toBeVisible();
});
test("User applies dimension token to a shape on y position", async ({
page,
}) => {
const { workspacePage, tokensSidebar, tokenContextMenuForToken } =
await setupTokensFile(page);
// Unfolds dimensions on token panel
await page.getByRole("tab", { name: "Layers" }).click();
await workspacePage.layers.getByTestId("layer-row").nth(1).click();
const tokensTabButton = page.getByRole("tab", { name: "Tokens" });
await tokensTabButton.click();
unfoldTokenTree(tokensSidebar, "dimensions", "dimension.dimension.sm");
// Apply token to width and height token from token panel
await tokensSidebar
.getByRole("button", { name: "dimension.sm" })
.click({ button: "right" });
await tokenContextMenuForToken.getByText("Y").click();
// Check if measures sections is visible on right sidebar
const measuresSection = page.getByRole("region", {
name: "shape-measures-section",
});
await expect(measuresSection).toBeVisible();
// Check if token pill is visible on design tab on right sidebar
const dimensionSMTokenPill = measuresSection.getByRole("button", {
name: "dimension.sm",
});
await expect(dimensionSMTokenPill).toBeVisible();
await dimensionSMTokenPill.click();
// Change token from dropdown
const dimensionTokenOptionXl = measuresSection.getByLabel("dimension.xl");
await expect(dimensionTokenOptionXl).toBeVisible();
await dimensionTokenOptionXl.click();
await expect(dimensionSMTokenPill).not.toBeVisible();
const dimensionXLTokenPill = measuresSection.getByRole("button", {
name: "dimension.xl",
});
await expect(dimensionXLTokenPill).toBeVisible();
// Detach token from design tab on right sidebar
const detachButton = measuresSection.getByRole("button", {
name: "Detach token",
});
await detachButton.nth(0).click();
await expect(dimensionXLTokenPill).not.toBeVisible();
});
test("User applies dimension token to a shape border-radius", async ({
page,
}) => {
const { workspacePage, tokensSidebar, tokenContextMenuForToken } =
await setupTokensFile(page);
// Unfolds dimensions on token panel
await page.getByRole("tab", { name: "Layers" }).click();
await workspacePage.layers.getByTestId("layer-row").nth(2).click();
const tokensTabButton = page.getByRole("tab", { name: "Tokens" });
await tokensTabButton.click();
unfoldTokenTree(tokensSidebar, "dimensions", "dimension.dimension.xs");
// Apply token to width and height token from token panel
await tokensSidebar
.getByRole("button", { name: "dimension.xs" })
.click({ button: "right" });
await tokenContextMenuForToken.getByText("Border radius").hover();
await tokenContextMenuForToken.getByText("RadiusAll").click();
// Check if border radius sections is visible on right sidebar
const borderRadiusSection = page.getByRole("region", {
name: "border-radius-section",
});
await expect(borderRadiusSection).toBeVisible();
// Check if token pill is visible on design tab on right sidebar
const dimensionXSTokenPill = borderRadiusSection.getByRole("button", {
name: "dimension.xs",
});
await expect(dimensionXSTokenPill).toBeVisible();
await dimensionXSTokenPill.click();
// Change token from dropdown
const dimensionTokenOptionXl = borderRadiusSection.getByLabel("dimension.xl");
await expect(dimensionTokenOptionXl).toBeVisible();
await dimensionTokenOptionXl.click();
await expect(dimensionXSTokenPill).not.toBeVisible();
const dimensionXLTokenPill = borderRadiusSection.getByRole("button", {
name: "dimension.xl",
});
await expect(dimensionXLTokenPill).toBeVisible();
// Detach token from design tab on right sidebar
const detachButton = borderRadiusSection.getByRole("button", {
name: "Detach token",
});
await detachButton.nth(0).click();
await expect(dimensionXLTokenPill).not.toBeVisible();
});
}); });

View File

@@ -191,6 +191,16 @@
(when (:fill attributes) (update-fill value shape-ids attributes page-id)) (when (:fill attributes) (update-fill value shape-ids attributes page-id))
(when (:stroke-color attributes) (update-stroke-color value shape-ids attributes page-id))))))) (when (:stroke-color attributes) (update-stroke-color value shape-ids attributes page-id)))))))
(defn update-shape-dimensions
([value shape-ids attributes] (update-shape-dimensions value shape-ids attributes nil))
([value shape-ids attributes page-id]
(ptk/reify ::update-shape-dimensions
ptk/WatchEvent
(watch [_ _ _]
(when (number? value)
(rx/of
(when (:width attributes) (dwtr/update-dimensions shape-ids :width value {:ignore-touched true :page-id page-id}))
(when (:height attributes) (dwtr/update-dimensions shape-ids :height value {:ignore-touched true :page-id page-id}))))))))
(defn- attributes->layout-gap [attributes value] (defn- attributes->layout-gap [attributes value]
(let [layout-gap (-> (set/intersection attributes #{:column-gap :row-gap}) (let [layout-gap (-> (set/intersection attributes #{:column-gap :row-gap})
@@ -238,6 +248,21 @@
{:ignore-touched true {:ignore-touched true
:page-id page-id})))))))) :page-id page-id}))))))))
(defn update-layout-spacing
([value shape-ids attributes] (update-layout-spacing value shape-ids attributes nil))
([value shape-ids attributes page-id]
(ptk/reify ::update-layout-spacing
ptk/WatchEvent
(watch [_ state _]
(when (number? value)
(let [ids-with-layout (shape-ids-with-layout state (or page-id (:current-page-id state)) shape-ids)
layout-attributes (attributes->layout-gap attributes value)]
(rx/of
(dwsl/update-layout ids-with-layout
layout-attributes
{:ignore-touched true
:page-id page-id}))))))))
(defn update-shape-position (defn update-shape-position
([value shape-ids attributes] (update-shape-position value shape-ids attributes nil)) ([value shape-ids attributes] (update-shape-position value shape-ids attributes nil))
([value shape-ids attributes page-id] ([value shape-ids attributes page-id]
@@ -251,20 +276,6 @@
{:ignore-touched true {:ignore-touched true
:page-id page-id}))))))))) :page-id page-id})))))))))
(defn update-layout-gap
[value shape-ids attributes page-id]
(ptk/reify ::update-layout-gao
ptk/WatchEvent
(watch [_ state _]
(when (number? value)
(let [ids-with-layout (shape-ids-with-layout state (or page-id (:current-page-id state)) shape-ids)
layout-attributes (attributes->layout-gap attributes value)]
(rx/of
(dwsl/update-layout ids-with-layout
layout-attributes
{:ignore-touched true
:page-id page-id})))))))
(defn update-layout-sizing-limits (defn update-layout-sizing-limits
([value shape-ids attributes] (update-layout-sizing-limits value shape-ids attributes nil)) ([value shape-ids attributes] (update-layout-sizing-limits value shape-ids attributes nil))
([value shape-ids attributes page-id] ([value shape-ids attributes page-id]
@@ -459,126 +470,20 @@
value value
[shape-ids attributes page-id]))))) [shape-ids attributes page-id])))))
(defn update-shape-dimensions (defn update-typography-interactive
([value shape-ids attributes] (update-shape-dimensions value shape-ids attributes nil)) ([value shape-ids attributes] (update-typography value shape-ids attributes nil))
([value shape-ids attributes page-id] ([value shape-ids attributes page-id]
(ptk/reify ::update-shape-dimensions (when (map? value)
ptk/WatchEvent (rx/merge
(watch [_ _ _] (apply-functions-map
(when (number? value) {:font-size update-font-size
(rx/of :font-family update-font-family-interactive
(when (:width attributes) (dwtr/update-dimensions shape-ids :width value {:ignore-touched true :page-id page-id})) :font-weight update-font-weight-interactive
(when (:height attributes) (dwtr/update-dimensions shape-ids :height value {:ignore-touched true :page-id page-id})))))))) :letter-spacing update-letter-spacing
:text-case update-text-case
(defn- attributes->actions :text-decoration update-text-decoration-interactive}
[{:keys [value shape-ids attributes page-id]}] value
(cond-> [] [shape-ids attributes page-id])))))
(some attributes #{:width :height})
(conj #(update-shape-dimensions
value shape-ids
(set (filter attributes #{:width :height}))
page-id))
(some attributes #{:x :y})
(conj #(update-shape-position
value shape-ids
(set (filter attributes #{:x :y}))
page-id))
(some attributes #{:p1 :p2 :p3 :p4})
(conj #(update-layout-padding
value shape-ids
(set (filter attributes #{:p1 :p2 :p3 :p4}))
page-id))
(some attributes #{:m1 :m2 :m3 :m4})
(conj #(update-layout-item-margin
value shape-ids
(set (filter attributes #{:m1 :m2 :m3 :m4}))
page-id))
(some attributes #{:row-gap :column-gap})
(conj #(update-layout-gap
value shape-ids
(set (filter attributes #{:row-gap :column-gap}))
page-id))
(some attributes #{:r1 :r2 :r3 :r4})
(conj #(if (= attributes #{:r1 :r2 :r3 :r4})
(update-shape-radius-all value shape-ids attributes page-id)
(update-shape-radius-for-corners
value shape-ids
(set (filter attributes #{:r1 :r2 :r3 :r4}))
page-id)))
(some attributes #{:strole-width})
(conj #(update-stroke-width
value shape-ids
#{:strole-width}
page-id))
(some attributes #{:max-width :max-height})
(conj #(update-layout-sizing-limits
value shape-ids
(set (filter attributes #{:max-width :max-height}))
page-id))))
(defn use-dimensions-token
([value shape-ids attributes] (use-dimensions-token value shape-ids attributes nil))
([value shape-ids attributes page-id]
(ptk/reify ::use-dimensions-token
ptk/WatchEvent
(watch [_ state _]
(when (number? value)
(let [actions (attributes->actions
{:value value
:shape-ids shape-ids
:attributes attributes
:page-id page-id
:state state})]
(apply rx/of (map #(%) actions))))))))
(defn use-spacing-token
([value shape-ids attributes] (use-spacing-token value shape-ids attributes nil))
([value shape-ids attributes page-id]
(ptk/reify ::use-spacing-token
ptk/WatchEvent
(watch [_ state _]
(let [spacing-attrs
#{:row-gap :column-gap
:m1 :m2 :m3 :m4
:p1 :p2 :p3 :p4}]
(when (and (number? value)
(set? attributes)
(set/subset? attributes spacing-attrs))
(let [actions (attributes->actions
{:value value
:shape-ids shape-ids
:attributes attributes
:page-id page-id
:state state})]
(apply rx/of (map #(%) actions)))))))))
(defn use-sizing-token
([value shape-ids attributes] (use-sizing-token value shape-ids attributes nil))
([value shape-ids attributes page-id]
(ptk/reify ::use-sizing-token
ptk/WatchEvent
(watch [_ state _]
(let [sizing-attrs
#{:width :height
:max-width :max-height}]
(when (and (number? value)
(set? attributes)
(set/subset? attributes sizing-attrs))
(let [actions (attributes->actions
{:value value
:shape-ids shape-ids
:attributes attributes
:page-id page-id
:state state})]
(apply rx/of (map #(%) actions)))))))))
;; Events to apply / unapply tokens to shapes ------------------------------------------------------------ ;; Events to apply / unapply tokens to shapes ------------------------------------------------------------
@@ -718,19 +623,54 @@
:token token :token token
:shape-ids shape-ids})) :shape-ids shape-ids}))
(rx/of (rx/of
(cond (case (:type token)
(and (= (:type token) :spacing) :spacing
(nil? attrs))
(apply-spacing-token {:token token (apply-spacing-token {:token token
:attr attrs :attr attrs
:shapes shapes}) :shapes shapes})
:else
(apply-token {:attributes (if (empty? attrs) attributes attrs) (apply-token {:attributes (if (empty? attrs) attributes attrs)
:token token :token token
:shape-ids shape-ids :shape-ids shape-ids
:on-update-shape on-update-shape})))))))) :on-update-shape on-update-shape}))))))))
(defn toggle-border-radius-token
[{:keys [token attrs shape-ids expand-with-children]}]
(ptk/reify ::on-toggle-border-radius-token
ptk/WatchEvent
(watch [_ state _]
(let [objects (dsh/lookup-page-objects state)
shapes (into [] (keep (d/getf objects)) shape-ids)
shapes
(if expand-with-children
(into []
(mapcat (fn [shape]
(if (= (:type shape) :group)
(keep objects (:shapes shape))
[shape])))
shapes)
shapes)
{:keys [attributes all-attributes]}
(get token-properties (:type token))
unapply-tokens?
(cft/shapes-token-applied? token shapes (or attrs all-attributes attributes))
shape-ids (map :id shapes)]
(if unapply-tokens?
(rx/of
(unapply-token {:attributes (or attrs all-attributes attributes)
:token token
:shape-ids shape-ids}))
(rx/of
(apply-token {:attributes attrs
:token token
:shape-ids shape-ids
:on-update-shape update-shape-radius-for-corners})))))))
(defn apply-token-on-selected (defn apply-token-on-selected
[color-operations token] [color-operations token]
(ptk/reify ::apply-token-on-selected (ptk/reify ::apply-token-on-selected
@@ -860,7 +800,7 @@
{:title "Sizing" {:title "Sizing"
:attributes #{:width :height} :attributes #{:width :height}
:all-attributes ctt/sizing-keys :all-attributes ctt/sizing-keys
:on-update-shape use-sizing-token :on-update-shape update-shape-dimensions
:modal {:key :tokens/sizing :modal {:key :tokens/sizing
:fields [{:label "Sizing" :fields [{:label "Sizing"
:key :sizing}]}} :key :sizing}]}}
@@ -873,7 +813,7 @@
ctt/border-radius-keys ctt/border-radius-keys
ctt/axis-keys ctt/axis-keys
ctt/stroke-width-keys) ctt/stroke-width-keys)
:on-update-shape use-dimensions-token :on-update-shape update-shape-dimensions
:modal {:key :tokens/dimensions :modal {:key :tokens/dimensions
:fields [{:label "Dimensions" :fields [{:label "Dimensions"
:key :dimensions}]}} :key :dimensions}]}}
@@ -906,7 +846,7 @@
{:title "Spacing" {:title "Spacing"
:attributes #{:column-gap :row-gap} :attributes #{:column-gap :row-gap}
:all-attributes ctt/spacing-keys :all-attributes ctt/spacing-keys
:on-update-shape use-spacing-token :on-update-shape update-layout-spacing
:modal {:key :tokens/spacing :modal {:key :tokens/spacing
:fields [{:label "Spacing" :fields [{:label "Spacing"
:key :spacing}]}})) :key :spacing}]}}))

View File

@@ -54,7 +54,7 @@
{ctt/border-radius-keys dwta/update-shape-radius-for-corners {ctt/border-radius-keys dwta/update-shape-radius-for-corners
ctt/color-keys dwta/update-fill-stroke ctt/color-keys dwta/update-fill-stroke
ctt/stroke-width-keys dwta/update-stroke-width ctt/stroke-width-keys dwta/update-stroke-width
ctt/sizing-keys dwta/use-dimensions-token ctt/sizing-keys dwta/update-shape-dimensions
ctt/opacity-keys dwta/update-opacity ctt/opacity-keys dwta/update-opacity
ctt/rotation-keys dwta/update-rotation ctt/rotation-keys dwta/update-rotation
@@ -73,8 +73,8 @@
#{:x :y} dwta/update-shape-position #{:x :y} dwta/update-shape-position
#{:p1 :p2 :p3 :p4} dwta/update-layout-padding #{:p1 :p2 :p3 :p4} dwta/update-layout-padding
#{:m1 :m2 :m3 :m4} dwta/update-layout-item-margin #{:m1 :m2 :m3 :m4} dwta/update-layout-item-margin
#{:column-gap :row-gap} dwta/update-layout-gap #{:column-gap :row-gap} dwta/update-layout-spacing
#{:width :height} dwta/use-dimensions-token #{:width :height} dwta/update-shape-dimensions
#{:layout-item-min-w :layout-item-min-h :layout-item-max-w :layout-item-max-h} dwta/update-layout-sizing-limits}) #{:layout-item-min-w :layout-item-min-h :layout-item-max-w :layout-item-max-h} dwta/update-layout-sizing-limits})
(def ^:private attribute-actions-map (def ^:private attribute-actions-map

View File

@@ -192,10 +192,11 @@
(st/emit! (st/emit!
(change-radius (fn [shape] (change-radius (fn [shape]
(ctsr/set-radius-to-all-corners shape value)))) (ctsr/set-radius-to-all-corners shape value))))
(st/emit! (doseq [attr [:r1 :r2 :r3 :r4]]
(dwta/toggle-token {:token (first value) (st/emit!
:attrs #{:r1 :r2 :r3 :r4} (dwta/toggle-token {:token (first value)
:shape-ids ids}))))) :attrs #{attr}
:shape-ids ids}))))))
on-single-radius-change on-single-radius-change
@@ -204,10 +205,9 @@
(fn [value attr] (fn [value attr]
(if (or (string? value) (number? value)) (if (or (string? value) (number? value))
(st/emit! (change-one-radius #(ctsr/set-radius-to-single-corner % attr value) attr)) (st/emit! (change-one-radius #(ctsr/set-radius-to-single-corner % attr value) attr))
(st/emit! (st/emit! (st/emit! (dwta/toggle-border-radius-token {:token (first value)
(dwta/toggle-token {:token (first value) :attrs #{attr}
:attrs #{attr} :shape-ids ids})))))
:shape-ids ids}))))))
on-radius-r1-change #(on-single-radius-change % :r1) on-radius-r1-change #(on-single-radius-change % :r1)
on-radius-r2-change #(on-single-radius-change % :r2) on-radius-r2-change #(on-single-radius-change % :r2)

View File

@@ -369,12 +369,12 @@
(if (or (string? value) (int? value)) (if (or (string? value) (int? value))
(on-change :simple attr value event) (on-change :simple attr value event)
(do (do
(st/emit! (let [resolved-value (:resolved-value (first value))
(dwta/toggle-token {:token (first value) updated-attr (if (= :p1 attr) #{:p1 :p3} #{:p2 :p4})]
:attrs (if (= :p1 attr) (st/emit! (dwta/toggle-token {:token (first value)
#{:p1 :p3} :attrs updated-attr
#{:p2 :p4}) :shape-ids ids}))
:shape-ids ids})))))) (on-change :simple attr resolved-value event))))))
on-detach-token on-detach-token
(mf/use-fn (mf/use-fn
@@ -483,9 +483,11 @@
(if (or (string? value) (int? value)) (if (or (string? value) (int? value))
(on-change :multiple attr value event) (on-change :multiple attr value event)
(do (do
(st/emit! (dwta/toggle-token {:token (first value) (let [resolved-value (:resolved-value (first value))]
:attrs #{attr} (st/emit! (dwta/toggle-token {:token (first value)
:shape-ids ids})))))) :attrs #{attr}
:shape-ids ids}))
(on-change :multiple attr resolved-value event))))))
on-focus on-focus
(mf/use-fn (mf/use-fn
@@ -714,12 +716,11 @@
(if (or (string? value) (int? value)) (if (or (string? value) (int? value))
(on-change (= "nowrap" wrap-type) attr value event) (on-change (= "nowrap" wrap-type) attr value event)
(do (do
(st/emit! (let [resolved-value (:resolved-value (first value))]
(dwta/toggle-token {:token (first value) (st/emit! (dwta/toggle-token {:token (first value)
:attrs (if (= "nowrap" wrap-type) :attrs #{attr}
#{:row-gap :colum-gap} :shape-ids ids}))
#{attr}) (on-change (= "nowrap" wrap-type) attr resolved-value event))))))
:shape-ids ids}))))))
on-detach-token on-detach-token
(mf/use-fn (mf/use-fn

View File

@@ -284,17 +284,28 @@
(st/emit! (udw/change-orientation ids (keyword orientation))))) (st/emit! (udw/change-orientation ids (keyword orientation)))))
;; SIZE AND PROPORTION LOCK ;; SIZE AND PROPORTION LOCK
do-size-change
(mf/use-fn
(mf/deps ids)
(fn [value attr]
(st/emit! (udw/trigger-bounding-box-cloaking ids)
(udw/update-dimensions ids attr value))))
on-size-change on-size-change
(mf/use-fn (mf/use-fn
(mf/deps ids shapes) (mf/deps ids shapes)
(fn [value attr] (fn [value attr]
(if (or (string? value) (number? value)) (if (or (string? value) (number? value))
(st/emit! (udw/trigger-bounding-box-cloaking ids) (do
(udw/update-dimensions ids attr value)) (st/emit! (udw/trigger-bounding-box-cloaking ids))
(st/emit! (udw/trigger-bounding-box-cloaking ids) (run! #(do-size-change value attr) shapes))
(dwta/toggle-token {:token (first value) (do
:attrs #{attr} (let [resolved-value (:resolved-value (first value))]
:shape-ids ids}))))) (st/emit! (udw/trigger-bounding-box-cloaking ids)
(dwta/toggle-token {:token (first value)
:attrs #{attr}
:shape-ids ids}))
(run! #(do-size-change resolved-value attr) shapes))))))
on-proportion-lock-change on-proportion-lock-change
(mf/use-fn (mf/use-fn
@@ -304,6 +315,11 @@
(run! #(st/emit! (udw/set-shape-proportion-lock % new-lock)) ids)))) (run! #(st/emit! (udw/set-shape-proportion-lock % new-lock)) ids))))
;; POSITION ;; POSITION
do-position-change
(mf/use-fn
(fn [shape' value attr]
(st/emit! (udw/update-position (:id shape') {attr value}))))
on-position-change on-position-change
(mf/use-fn (mf/use-fn
(mf/deps ids) (mf/deps ids)
@@ -311,11 +327,21 @@
(if (or (string? value) (number? value)) (if (or (string? value) (number? value))
(do (do
(st/emit! (udw/trigger-bounding-box-cloaking ids)) (st/emit! (udw/trigger-bounding-box-cloaking ids))
(st/emit! (udw/update-position ids {attr value}))) (run! #(do-position-change %1 value attr) shapes))
(st/emit! (udw/trigger-bounding-box-cloaking ids) (do
(dwta/toggle-token {:token (first value) (let [resolved-value (:resolved-value (first value))]
:attrs #{attr} (st/emit! (udw/trigger-bounding-box-cloaking ids)
:shape-ids ids}))))) (dwta/toggle-token {:token (first value)
:attrs #{attr}
:shape-ids ids}))
(run! #(do-position-change %1 resolved-value attr) shapes))))))
;; ROTATION
do-rotation-change
(mf/use-fn
(mf/deps ids)
(fn [value]
(st/emit! (udw/increase-rotation ids value))))
on-rotation-change on-rotation-change
(mf/use-fn (mf/use-fn
@@ -324,11 +350,14 @@
(if (or (string? value) (number? value)) (if (or (string? value) (number? value))
(do (do
(st/emit! (udw/trigger-bounding-box-cloaking ids)) (st/emit! (udw/trigger-bounding-box-cloaking ids))
(st/emit! (udw/increase-rotation ids value))) (run! #(do-rotation-change value) shapes))
(st/emit! (udw/trigger-bounding-box-cloaking ids) (do
(dwta/toggle-token {:token (first value) (let [resolved-value (:resolved-value (first value))]
:attrs #{:rotation} (st/emit! (udw/trigger-bounding-box-cloaking ids)
:shape-ids ids}))))) (dwta/toggle-token {:token (first value)
:attrs #{:rotation}
:shape-ids ids}))
(run! #(do-rotation-change resolved-value) shapes))))))
on-width-change on-width-change
(mf/use-fn (mf/deps on-size-change) #(on-size-change % :width)) (mf/use-fn (mf/deps on-size-change) #(on-size-change % :width))
@@ -381,8 +410,7 @@
(fn [] (fn []
(st/emit! (dwt/selected-fit-content))))] (st/emit! (dwt/selected-fit-content))))]
[:section {:class (stl/css :element-set) [:div {:class (stl/css :element-set)}
:aria-label "shape-measures-section"}
(when (and (options :presets) (when (and (options :presets)
(or (nil? all-types) (= (count all-types) 1))) (or (nil? all-types) (= (count all-types) 1)))
[:div {:class (stl/css :presets)} [:div {:class (stl/css :presets)}

View File

@@ -9,50 +9,18 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.types.color :as ctc] [app.common.types.color :as ctc]
[app.common.types.token :as tk]
[app.main.data.workspace.tokens.application :as dwta] [app.main.data.workspace.tokens.application :as dwta]
[app.main.features :as features]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.numeric-input :as deprecated-input] [app.main.ui.components.numeric-input :refer [numeric-input*]]
[app.main.ui.components.reorder-handler :refer [reorder-handler*]] [app.main.ui.components.reorder-handler :refer [reorder-handler*]]
[app.main.ui.components.select :refer [select]] [app.main.ui.components.select :refer [select]]
[app.main.ui.context :as muc]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.controls.numeric-input :refer [numeric-input*]]
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
[app.main.ui.hooks :as h] [app.main.ui.hooks :as h]
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row*]] [app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row*]]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(mf/defc numeric-input-wrapper*
{::mf/private true}
[{:keys [values name applied-tokens align on-detach] :rest props}]
(let [tokens (mf/use-ctx muc/active-tokens-by-type)
tokens (mf/with-memo [tokens name]
(delay
(-> (deref tokens)
(select-keys (get tk/tokens-by-input name))
(not-empty))))
on-detach-attr (mf/use-fn
(mf/deps on-detach name)
#(on-detach % name))
applied-token (get applied-tokens name)
props (mf/spread-props props
{:placeholder (if (= :multiple values)
(tr "settings.multiple")
"--")
:applied-token applied-token
:tokens (if (delay? tokens) @tokens tokens)
:align align
:on-detach on-detach-attr
:name name
:value values})]
[:> numeric-input* props]))
(mf/defc stroke-row* (mf/defc stroke-row*
[{:keys [index [{:keys [index
stroke stroke
@@ -77,10 +45,7 @@
select-on-focus select-on-focus
ids]}] ids]}]
(let [token-numeric-inputs (let [on-drop
(features/use-feature "tokens/numeric-input")
on-drop
(mf/use-fn (mf/use-fn
(mf/deps on-reorder index) (mf/deps on-reorder index)
(fn [relative-pos data] (fn [relative-pos data]
@@ -123,13 +88,7 @@
on-width-change on-width-change
(mf/use-fn (mf/use-fn
(mf/deps index on-stroke-width-change) (mf/deps index on-stroke-width-change)
(fn [value] #(on-stroke-width-change index %))
(if (or (string? value) (int? value))
(on-stroke-width-change index value)
(do
(st/emit! (dwta/toggle-token {:token (first value)
:attrs #{:stroke-width}
:shape-ids ids}))))))
stroke-alignment (or (:stroke-alignment stroke) :center) stroke-alignment (or (:stroke-alignment stroke) :center)
@@ -190,12 +149,6 @@
(fn [token] (fn [token]
(on-detach-token token #{:stroke-color}))) (on-detach-token token #{:stroke-color})))
on-detach-token-width
(mf/use-fn
(mf/deps on-detach-token)
(fn [token]
(on-detach-token (first token) #{:stroke-width})))
stroke-caps-options stroke-caps-options
[{:value nil :label (tr "workspace.options.stroke-cap.none")} [{:value nil :label (tr "workspace.options.stroke-cap.none")}
:separator :separator
@@ -242,30 +195,17 @@
;; Stroke Width, Alignment & Style ;; Stroke Width, Alignment & Style
[:div {:class (stl/css :stroke-options)} [:div {:class (stl/css :stroke-options)}
(if token-numeric-inputs [:div {:class (stl/css :stroke-width-input)
[:> numeric-input-wrapper* {:on-change on-width-change :title (tr "workspace.options.stroke-width")}
:on-detach on-detach-token-width [:> icon* {:icon-id i/stroke-size
:icon i/stroke-size :size "s"}]
:min 0 [:> numeric-input* {:value stroke-width
:on-focus on-focus :min 0
:on-blur on-blur :placeholder (tr "settings.multiple")
:name :stroke-width :on-change on-width-change
:class (stl/css :numeric-input-wrapper) :on-focus on-focus
:property (tr "workspace.options.stroke-width") :select-on-focus select-on-focus
:applied-tokens applied-tokens :on-blur on-blur}]]
:values stroke-width}]
[:div {:class (stl/css :stroke-width-input)
:title (tr "workspace.options.stroke-width")}
[:> icon* {:icon-id i/stroke-size
:size "s"}]
[:> deprecated-input/numeric-input* {:value stroke-width
:min 0
:placeholder (tr "settings.multiple")
:on-change on-width-change
:on-focus on-focus
:select-on-focus select-on-focus
:on-blur on-blur}]])
[:div {:class (stl/css :stroke-alignment-select) [:div {:class (stl/css :stroke-alignment-select)
:data-testid "stroke.alignment"} :data-testid "stroke.alignment"}

View File

@@ -45,11 +45,6 @@
padding-inline-start: var(--sp-xs); padding-inline-start: var(--sp-xs);
} }
.numeric-input-wrapper {
grid-column: span 2;
--dropdown-width: var(--7-columns-dropdown-width);
}
.stroke-alignment-select { .stroke-alignment-select {
grid-column: span 3; grid-column: span 3;
} }

View File

@@ -223,7 +223,7 @@
gap-items (all-or-separate-actions {:attribute-labels {:column-gap "Column Gap" gap-items (all-or-separate-actions {:attribute-labels {:column-gap "Column Gap"
:row-gap "Row Gap"} :row-gap "Row Gap"}
:hint (tr "workspace.tokens.gaps") :hint (tr "workspace.tokens.gaps")
:on-update-shape dwta/update-layout-gap} :on-update-shape dwta/update-layout-spacing}
context-data)] context-data)]
(->> (concat (->> (concat
gap-items gap-items
@@ -239,7 +239,7 @@
(all-or-separate-actions {:attribute-labels {:width "Width" (all-or-separate-actions {:attribute-labels {:width "Width"
:height "Height"} :height "Height"}
:hint (tr "workspace.tokens.size") :hint (tr "workspace.tokens.size")
:on-update-shape dwta/use-dimensions-token} :on-update-shape dwta/update-shape-dimensions}
context-data) context-data)
[:separator] [:separator]
(all-or-separate-actions {:attribute-labels {:layout-item-min-w "Min Width" (all-or-separate-actions {:attribute-labels {:layout-item-min-w "Min Width"

View File

@@ -260,7 +260,7 @@
events [(dwta/apply-token {:shape-ids [(:id rect-1)] events [(dwta/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:width :height} :attributes #{:width :height}
:token (toht/get-token file "dimensions.sm") :token (toht/get-token file "dimensions.sm")
:on-update-shape dwta/use-dimensions-token})]] :on-update-shape dwta/update-shape-dimensions})]]
(tohs/run-store-async (tohs/run-store-async
store done events store done events
(fn [new-state] (fn [new-state]
@@ -333,7 +333,7 @@
events [(dwta/apply-token {:shape-ids [(:id rect-1)] events [(dwta/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:width :height} :attributes #{:width :height}
:token (toht/get-token file "sizing.sm") :token (toht/get-token file "sizing.sm")
:on-update-shape dwta/use-dimensions-token})]] :on-update-shape dwta/update-shape-dimensions})]]
(tohs/run-store-async (tohs/run-store-async
store done events store done events
(fn [new-state] (fn [new-state]