diff --git a/.gitignore b/.gitignore index e7dfcc7462..701cbe1e15 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ !.yarn/releases !.yarn/sdks !.yarn/versions +.pnpm-store *-init.clj *.css.json *.jar diff --git a/CHANGES.md b/CHANGES.md index f1fd70b2e5..0e7b48fa22 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -35,6 +35,7 @@ - Fix problem when pasting elements in reverse flex layout [Taiga #12460](https://tree.taiga.io/project/penpot/issue/12460) - Fix wrong board size presets in Android [Taiga #12339](https://tree.taiga.io/project/penpot/issue/12339) - Fix problem with grid layout components and auto sizing [Github #7797](https://github.com/penpot/penpot/issues/7797) +- Fix some alignments on inspect tab [Taiga #12915](https://tree.taiga.io/project/penpot/issue/12915) ## 2.12.1 diff --git a/backend/src/app/auth/oidc.clj b/backend/src/app/auth/oidc.clj index 93330b4e4d..7668a49b99 100644 --- a/backend/src/app/auth/oidc.clj +++ b/backend/src/app/auth/oidc.clj @@ -36,17 +36,6 @@ [integrant.core :as ig] [yetti.response :as-alias yres])) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; HELPERS -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn obfuscate-string - [s] - (if (< (count s) 10) - (apply str (take (count s) (repeat "*"))) - (str (subs s 0 5) - (apply str (take (- (count s) 5) (repeat "*")))))) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; OIDC PROVIDER (GENERIC) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -177,7 +166,7 @@ (l/inf :hint "provider initialized" :provider (:id provider) :client-id (:client-id provider) - :client-secret (obfuscate-string (:client-secret provider))) + :client-secret (d/obfuscate-string (:client-secret provider))) provider) (catch Throwable cause @@ -222,7 +211,7 @@ (l/inf :hint "provider initialized" :provider (:id provider) :client-id (:client-id provider) - :client-secret (obfuscate-string (:client-secret provider))) + :client-secret (d/obfuscate-string (:client-secret provider))) provider) (catch Throwable cause @@ -299,7 +288,7 @@ (l/inf :hint "provider initialized" :provider (:id provider) :client-id (:client-id provider) - :client-secret (obfuscate-string (:client-secret provider))) + :client-secret (d/obfuscate-string (:client-secret provider))) provider) (catch Throwable cause @@ -341,7 +330,7 @@ :provider "gitlab" :base-uri (:base-uri provider) :client-id (:client-id provider) - :client-secret (obfuscate-string (:client-secret provider))) + :client-secret (d/obfuscate-string (:client-secret provider))) provider) (catch Throwable cause (ex/raise :type ::internal @@ -361,7 +350,7 @@ (l/inf :hint "provider initialized" :provider (:id provider) :client-id (:client-id provider) - :client-secret (obfuscate-string (:client-secret provider))) + :client-secret (d/obfuscate-string (:client-secret provider))) provider) (catch Throwable cause @@ -459,7 +448,7 @@ (l/trc :hint "fetch access token" :provider (:id provider) :client-id (:client-id provider) - :client-secret (obfuscate-string (:client-secret provider)) + :client-secret (d/obfuscate-string (:client-secret provider)) :grant-type (:grant_type params) :redirect-uri (:redirect_uri params)) @@ -512,7 +501,7 @@ [cfg provider tdata] (l/trc :hint "fetch user info" :uri (:user-uri provider) - :token (obfuscate-string (:token/access tdata))) + :token (d/obfuscate-string (:token/access tdata))) (let [params {:uri (:user-uri provider) :headers {"Authorization" (str (:token/type tdata) " " (:token/access tdata))} diff --git a/common/src/app/common/data.cljc b/common/src/app/common/data.cljc index 015246c005..6f05c439da 100644 --- a/common/src/app/common/data.cljc +++ b/common/src/app/common/data.cljc @@ -1024,6 +1024,26 @@ :clj (sort comp-fn items)))) +(defn obfuscate-string + "Obfuscates potentially sensitive values. + + - One-arg arity: + * For strings shorter than 10 characters, all characters are replaced by `*`. + * For longer strings, the first 5 characters are preserved and the rest obfuscated. + - Two-arg arity accepts a boolean `full?` that, when true, replaces the whole value + by `*`, preserving only the length." + ([v] + (obfuscate-string v false)) + ([v full?] + (let [s (str v) + n (count s)] + (cond + (zero? n) s + full? (apply str (repeat n "*")) + (< n 10) (apply str (repeat n "*")) + :else (str (subs s 0 5) + (apply str (repeat (- n 5) "*"))))))) + (defn reorder "Reorder a vector by moving one of their items from some position to some space between positions. It clamps the position numbers to a valid range." diff --git a/common/src/app/common/exceptions.cljc b/common/src/app/common/exceptions.cljc index 6def4ef91c..8a0a9cf834 100644 --- a/common/src/app/common/exceptions.cljc +++ b/common/src/app/common/exceptions.cljc @@ -10,6 +10,7 @@ (:refer-clojure :exclude [instance?]) (:require #?(:clj [clojure.stacktrace :as strace]) + [app.common.data :refer [obfuscate-string]] [app.common.pprint :as pp] [app.common.schema :as sm] [clojure.core :as c] @@ -19,6 +20,10 @@ (:import clojure.lang.IPersistentMap))) +(def ^:private sensitive-fields + "Keys whose values must be obfuscated in validation explains." + #{:password :old-password :token :invitation-token}) + #?(:clj (set! *warn-on-reflection* true)) (def ^:dynamic *data-length* 8) @@ -110,7 +115,25 @@ (explain (:explain data) opts) (contains? data ::sm/explain) - (sm/humanize-explain (::sm/explain data) opts))) + (let [exp (::sm/explain data) + sanitize-map (fn sanitize-map [m] + (reduce-kv + (fn [acc k v] + (let [k* (if (string? k) (keyword k) k)] + (cond + (contains? sensitive-fields k*) + (assoc acc k (if (map? v) + (sanitize-map v) + (obfuscate-string v true))) + + (map? v) (assoc acc k (sanitize-map v)) + :else (assoc acc k v)))) + {} + m)) + sanitize-explain (fn [exp] + (cond-> exp + (:value exp) (update :value sanitize-map)))] + (sm/humanize-explain (sanitize-explain exp) opts)))) #?(:clj (defn format-throwable diff --git a/common/src/app/common/path_names.cljc b/common/src/app/common/path_names.cljc index 74774c0044..6fdf1d1525 100644 --- a/common/src/app/common/path_names.cljc +++ b/common/src/app/common/path_names.cljc @@ -132,94 +132,3 @@ Some naming conventions: (if-let [last-period (str/last-index-of s ".")] [(subs s 0 (inc last-period)) (subs s (inc last-period))] [s ""])) - -;; Tree building functions -------------------------------------------------- - -"Build tree structure from flat list of paths" - -"`build-tree-root` is the main function to build the tree." - -"Receives a list of segments with 'name' properties representing paths, - and a separator string." -"E.g segments = [{... :name 'one/two/three'} {... :name 'one/two/four'} {... :name 'one/five'}]" - -"Transforms into a tree structure like: - [{:name 'one' - :path 'one' - :depth 0 - :leaf nil - :children-fn (fn [] [{:name 'two' - :path 'one.two' - :depth 1 - :leaf nil - :children-fn (fn [] [{... :name 'three'} {... :name 'four'}])} - {:name 'five' - :path 'one.five' - :depth 1 - :leaf {... :name 'five'} - ...}])}]" - -(defn- sort-by-children - "Sorts segments so that those with children come first." - [segments separator] - (sort-by (fn [segment] - (let [path (split-path (:name segment) :separator separator) - path-length (count path)] - (if (= path-length 1) - 1 - 0))) - segments)) - -(defn- group-by-first-segment - "Groups segments by their first path segment and update segment name." - [segments separator] - (reduce (fn [acc segment] - (let [[first-segment & remaining-segments] (split-path (:name segment) :separator separator) - rest-path (when (seq remaining-segments) (join-path remaining-segments :separator separator :with-spaces? false))] - (update acc first-segment (fnil conj []) - (if rest-path - (assoc segment :name rest-path) - segment)))) - {} - segments)) - -(defn- sort-and-group-segments - "Sorts elements and groups them by their first path segment." - [segments separator] - (let [sorted (sort-by-children segments separator) - grouped (group-by-first-segment sorted separator)] - grouped)) - -(defn- build-tree-node - "Builds a single tree node with lazy children." - [segment-name remaining-segments separator parent-path depth] - (let [current-path (if parent-path - (str parent-path "." segment-name) - segment-name) - - is-leaf? (and (seq remaining-segments) - (every? (fn [segment] - (let [remaining-segment-name (first (split-path (:name segment) :separator separator))] - (= segment-name remaining-segment-name))) - remaining-segments)) - - leaf-segment (when is-leaf? (first remaining-segments)) - node {:name segment-name - :path current-path - :depth depth - :leaf leaf-segment - :children-fn (when-not is-leaf? - (fn [] - (let [grouped-elements (sort-and-group-segments remaining-segments separator)] - (mapv (fn [[child-segment-name remaining-child-segments]] - (build-tree-node child-segment-name remaining-child-segments separator current-path (inc depth))) - grouped-elements))))}] - node)) - -(defn build-tree-root - "Builds the root level of the tree." - [segments separator] - (let [grouped-elements (sort-and-group-segments segments separator)] - (mapv (fn [[segment-name remaining-segments]] - (build-tree-node segment-name remaining-segments separator nil 0)) - grouped-elements))) diff --git a/common/src/app/common/types/path.cljc b/common/src/app/common/types/path.cljc index b47eca63c0..be0e15b1aa 100644 --- a/common/src/app/common/types/path.cljc +++ b/common/src/app/common/types/path.cljc @@ -112,8 +112,10 @@ (:c2y params) (update-in [index :params :c2y] + (:c2y params))) content))] - (impl/path-data - (reduce apply-to-index (vec content) modifiers)))) + (if (some? modifiers) + (impl/path-data + (reduce apply-to-index (vec content) modifiers)) + content))) (defn transform-content "Applies a transformation matrix over content and returns a new diff --git a/docker/devenv/files/start-tmux.sh b/docker/devenv/files/start-tmux.sh index 6f1716bed9..6418e0a86b 100755 --- a/docker/devenv/files/start-tmux.sh +++ b/docker/devenv/files/start-tmux.sh @@ -8,14 +8,10 @@ source ~/.bashrc echo "[start-tmux.sh] Installing node dependencies" pushd ~/penpot/frontend/ -corepack install; -yarn install; -yarn playwright install chromium +./scripts/setup; popd pushd ~/penpot/exporter/ -corepack install; -yarn install -yarn playwright install chromium +./scripts/setup; popd tmux -2 new-session -d -s penpot diff --git a/exporter/scripts/setup b/exporter/scripts/setup new file mode 100755 index 0000000000..691c8c8d48 --- /dev/null +++ b/exporter/scripts/setup @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -e; + +corepack enable; +corepack install; +yarn install; +yarn playwright install chromium diff --git a/frontend/playwright/ui/specs/inspect-tab.spec.js b/frontend/playwright/ui/specs/inspect-tab.spec.js index 12f56f1343..00f481cdab 100644 --- a/frontend/playwright/ui/specs/inspect-tab.spec.js +++ b/frontend/playwright/ui/specs/inspect-tab.spec.js @@ -305,7 +305,7 @@ test.describe("Inspect tab - Styles", () => { ); await openInspectTab(workspacePage); - const panel = await getPanelByTitle(workspacePage, "Size & position"); + const panel = await getPanelByTitle(workspacePage, "Size and position"); await expect(panel).toBeVisible(); const propertyRow = panel.getByTestId("property-row"); @@ -335,7 +335,7 @@ test.describe("Inspect tab - Styles", () => { ); await openInspectTab(workspacePage); - const panel = await getPanelByTitle(workspacePage, "Size & position"); + const panel = await getPanelByTitle(workspacePage, "Size and position"); await expect(panel).toBeVisible(); const propertyRow = panel.getByTestId("property-row"); @@ -375,7 +375,7 @@ test.describe("Inspect tab - Styles", () => { ); await openInspectTab(workspacePage); - const panel = await getPanelByTitle(workspacePage, "Size & position"); + const panel = await getPanelByTitle(workspacePage, "Size and position"); await expect(panel).toBeVisible(); const propertyRow = panel.getByTestId("property-row"); diff --git a/frontend/playwright/ui/specs/tokens.spec.js b/frontend/playwright/ui/specs/tokens.spec.js index 3ad2dd2b67..f8502b5ecb 100644 --- a/frontend/playwright/ui/specs/tokens.spec.js +++ b/frontend/playwright/ui/specs/tokens.spec.js @@ -40,7 +40,6 @@ const setupEmptyTokensFile = async (page, options = {}) => { tokensUpdateCreateModal: workspacePage.tokensUpdateCreateModal, tokenThemesSetsSidebar: workspacePage.tokenThemesSetsSidebar, tokenSetItems: workspacePage.tokenSetItems, - tokensSidebar: workspacePage.tokensSidebar, tokenSetGroupItems: workspacePage.tokenSetGroupItems, tokenContextMenuForSet: workspacePage.tokenContextMenuForSet, }; @@ -111,12 +110,15 @@ const checkInputFieldWithError = async ( ).toBeVisible(); }; -const checkInputFieldWithoutError = async (inputLocator) => { +const checkInputFieldWithoutError = async ( + tokenThemeUpdateCreateModal, + inputLocator, +) => { expect(await inputLocator.getAttribute("aria-invalid")).toBeNull(); expect(await inputLocator.getAttribute("aria-describedby")).toBeNull(); }; -const testTokenCreationFlow = async ( +async function testTokenCreationFlow( page, { tokenLabel, @@ -130,7 +132,7 @@ const testTokenCreationFlow = async ( resolvedValueText, secondResolvedValueText, }, -) => { +) { const invalidValueError = "Invalid token value"; const emptyNameError = "Name should be at least 1 character"; const selfReferenceError = "Token has self reference"; @@ -240,45 +242,7 @@ const testTokenCreationFlow = async ( await expect( tokensTabPanel.getByRole("button", { name: "my-token-2" }), ).toBeEnabled(); -}; - -const unfoldTokenTree = async (tokensTabPanel, type, tokenName) => { - const tokenSegments = tokenName.split("."); - const tokenFolderTree = tokenSegments.slice(0, -1); - const tokenLeafName = tokenSegments.pop(); - - const typeParentWrapper = tokensTabPanel.getByTestId(`section-${type}`); - const typeSectionButton = typeParentWrapper - .getByRole("button", { - name: type, - }) - .first(); - - const isSectionExpanded = - await typeSectionButton.getAttribute("aria-expanded"); - - if (isSectionExpanded === "false") { - await typeSectionButton.click(); - } - - for (const segment of tokenFolderTree) { - const segmentButton = typeParentWrapper - .getByRole("listitem") - .getByRole("button", { name: segment }) - .first(); - - const isExpanded = await segmentButton.getAttribute("aria-expanded"); - if (isExpanded === "false") { - await segmentButton.click(); - } - } - - await expect( - typeParentWrapper.getByRole("button", { - name: tokenLeafName, - }), - ).toBeEnabled(); -}; +} test.describe("Tokens: Tokens Tab", () => { test("Clicking tokens tab button opens tokens sidebar tab", async ({ @@ -434,12 +398,15 @@ test.describe("Tokens: Tokens Tab", () => { const emptyNameError = "Name should be at least 1 character"; const selfReferenceError = "Token has self reference"; const missingReferenceError = "Missing token references"; - const { tokensUpdateCreateModal, tokenThemesSetsSidebar, tokensSidebar } = + const { tokensUpdateCreateModal, tokenThemesSetsSidebar } = await setupEmptyTokensFile(page); - await tokensSidebar - .getByRole("button", { name: "Add Token: Color" }) - .click(); + const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); + const addTokenButton = tokensTabPanel.getByRole("button", { + name: `Add Token: Color`, + }); + + await addTokenButton.click(); await expect(tokensUpdateCreateModal).toBeVisible(); // Placeholder checks @@ -504,34 +471,38 @@ test.describe("Tokens: Tokens Tab", () => { await expect(submitButton).toBeEnabled(); await submitButton.click(); - await unfoldTokenTree(tokensSidebar, "color", "color.primary"); + await expect( + tokensTabPanel.getByRole("button", { + name: "color.primary", + }), + ).toBeEnabled(); // Create token referencing the previous one with keyboard - await tokensSidebar + await tokensTabPanel .getByRole("button", { name: "Add Token: Color" }) .click(); await expect(tokensUpdateCreateModal).toBeVisible(); await nameField.click(); - await nameField.fill("secondary"); + await nameField.fill("color.secondary"); await nameField.press("Tab"); await valueField.click(); await valueField.fill("{color.primary}"); await expect(submitButton).toBeEnabled(); - await submitButton.press("Enter"); + await nameField.press("Enter"); await expect( - tokensSidebar.getByRole("button", { - name: "secondary", + tokensTabPanel.getByRole("button", { + name: "color.secondary", }), ).toBeEnabled(); // Tokens tab panel should have two tokens with the color red / #ff0000 await expect( - tokensSidebar.getByRole("button", { name: "#ff0000" }), + tokensTabPanel.getByRole("button", { name: "#ff0000" }), ).toHaveCount(2); // Global set has been auto created and is active @@ -547,7 +518,7 @@ test.describe("Tokens: Tokens Tab", () => { ).toHaveAttribute("aria-checked", "true"); // Check color picker - await tokensSidebar + await tokensTabPanel .getByRole("button", { name: "Add Token: Color" }) .click(); await expect(tokensUpdateCreateModal).toBeVisible(); @@ -1108,7 +1079,7 @@ test.describe("Tokens: Tokens Tab", () => { const emptyNameError = "Name should be at least 1 character"; const { tokensUpdateCreateModal, tokenThemesSetsSidebar } = - await setupEmptyTokensFile(page, { flags: ["enable-token-shadow"] }); + await setupEmptyTokensFile(page, {flags: ["enable-token-shadow"]}); // Open modal const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); @@ -1536,15 +1507,24 @@ test.describe("Tokens: Tokens Tab", () => { test("User edits token and auto created set show up in the sidebar", async ({ page, }) => { - const { tokensUpdateCreateModal, tokensSidebar, tokenContextMenuForToken } = - await setupTokensFile(page); + const { + workspacePage, + tokensUpdateCreateModal, + tokenThemesSetsSidebar, + tokensSidebar, + tokenContextMenuForToken, + } = await setupTokensFile(page); await expect(tokensSidebar).toBeVisible(); - await unfoldTokenTree(tokensSidebar, "color", "colors.blue.100"); + const tokensColorGroup = tokensSidebar.getByRole("button", { + name: "Color 92", + }); + await expect(tokensColorGroup).toBeVisible(); + await tokensColorGroup.click(); const colorToken = tokensSidebar.getByRole("button", { - name: "100", + name: "colors.blue.100", }); await expect(colorToken).toBeVisible(); await colorToken.click({ button: "right" }); @@ -1561,10 +1541,8 @@ test.describe("Tokens: Tokens Tab", () => { await expect(tokensUpdateCreateModal).not.toBeVisible(); - await unfoldTokenTree(tokensSidebar, "color", "colors.blue.100.changed"); - const colorTokenChanged = tokensSidebar.getByRole("button", { - name: "changed", + name: "colors.blue.100.changed", }); await expect(colorTokenChanged).toBeVisible(); }); @@ -1655,10 +1633,11 @@ test.describe("Tokens: Tokens Tab", () => { }); test("User creates grouped color token", async ({ page }) => { - const { workspacePage, tokensUpdateCreateModal, tokensSidebar } = + const { workspacePage, tokensUpdateCreateModal, tokenThemesSetsSidebar } = await setupEmptyTokensFile(page); - await tokensSidebar + const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); + await tokensTabPanel .getByRole("button", { name: "Add Token: Color" }) .click(); @@ -1670,7 +1649,7 @@ test.describe("Tokens: Tokens Tab", () => { const valueField = tokensUpdateCreateModal.getByLabel("Value"); await nameField.click(); - await nameField.fill("dark.primary"); + await nameField.fill("color.dark.primary"); await valueField.click(); await valueField.fill("red"); @@ -1681,9 +1660,7 @@ test.describe("Tokens: Tokens Tab", () => { await expect(submitButton).toBeEnabled(); await submitButton.click(); - await unfoldTokenTree(tokensSidebar, "color", "dark.primary"); - - await expect(tokensSidebar.getByLabel("primary")).toBeEnabled(); + await expect(tokensTabPanel.getByLabel("color.dark.primary")).toBeEnabled(); }); test("User cant create regular token with value missing", async ({ @@ -1699,6 +1676,7 @@ test.describe("Tokens: Tokens Tab", () => { await expect(tokensUpdateCreateModal).toBeVisible(); const nameField = tokensUpdateCreateModal.getByLabel("Name"); + const valueField = tokensUpdateCreateModal.getByLabel("Value"); const submitButton = tokensUpdateCreateModal.getByRole("button", { name: "Save", }); @@ -1708,7 +1686,7 @@ test.describe("Tokens: Tokens Tab", () => { // Fill in name but leave value empty await nameField.click(); - await nameField.fill("primary"); + await nameField.fill("color.primary"); // Submit button should remain disabled when value is empty await expect(submitButton).toBeDisabled(); @@ -1726,6 +1704,7 @@ test.describe("Tokens: Tokens Tab", () => { .click(); await expect(tokensUpdateCreateModal).toBeVisible(); + const nameField = tokensUpdateCreateModal.getByLabel("Name"); const valueField = tokensUpdateCreateModal.getByLabel("Value"); await valueField.click(); @@ -1775,10 +1754,15 @@ test.describe("Tokens: Tokens Tab", () => { await expect(tokensSidebar).toBeVisible(); - unfoldTokenTree(tokensSidebar, "color", "colors.blue.100"); + const tokensColorGroup = tokensSidebar.getByRole("button", { + name: "Color 92", + }); + + await expect(tokensColorGroup).toBeVisible(); + await tokensColorGroup.click(); const colorToken = tokensSidebar.getByRole("button", { - name: "100", + name: "colors.blue.100", }); await colorToken.click({ button: "right" }); @@ -1798,10 +1782,15 @@ test.describe("Tokens: Tokens Tab", () => { await expect(tokensSidebar).toBeVisible(); - unfoldTokenTree(tokensSidebar, "color", "colors.blue.100"); + const tokensColorGroup = tokensSidebar.getByRole("button", { + name: "Color 92", + }); + await expect(tokensColorGroup).toBeVisible(); + + await tokensColorGroup.click(); const colorToken = tokensSidebar.getByRole("button", { - name: "100", + name: "colors.blue.100", }); await expect(colorToken).toBeVisible(); await colorToken.click({ button: "right" }); @@ -1814,7 +1803,8 @@ test.describe("Tokens: Tokens Tab", () => { }); test("User fold/unfold color tokens", async ({ page }) => { - const { tokensSidebar } = await setupTokensFile(page); + const { tokensSidebar, tokenContextMenuForToken } = + await setupTokensFile(page); await expect(tokensSidebar).toBeVisible(); @@ -1824,10 +1814,8 @@ test.describe("Tokens: Tokens Tab", () => { await expect(tokensColorGroup).toBeVisible(); await tokensColorGroup.click(); - unfoldTokenTree(tokensSidebar, "color", "colors.blue.100"); - const colorToken = tokensSidebar.getByRole("button", { - name: "100", + name: "colors.blue.100", }); await expect(colorToken).toBeVisible(); await tokensColorGroup.click(); @@ -2230,10 +2218,13 @@ test.describe("Tokens: Apply token", () => { const tokensTabButton = page.getByRole("tab", { name: "Tokens" }); await tokensTabButton.click(); - unfoldTokenTree(tokensSidebar, "color", "colors.black"); + await tokensSidebar + .getByRole("button") + .filter({ hasText: "Color" }) + .click(); await tokensSidebar - .getByRole("button", { name: "black" }) + .getByRole("button", { name: "colors.black" }) .click({ button: "right" }); await tokenContextMenuForToken.getByText("Fill").click(); @@ -2471,7 +2462,7 @@ test.describe("Tokens: Apply token", () => { await expect(tokensUpdateCreateModal).toBeVisible(); const nameField = tokensUpdateCreateModal.getByLabel("Name"); - await nameField.fill("primary"); + await nameField.fill("shadow.primary"); // User adds first shadow with a color from the color ramp const firstShadowFields = tokensUpdateCreateModal.getByTestId( @@ -2718,11 +2709,9 @@ test.describe("Tokens: Apply token", () => { await submitButton.click(); await expect(tokensUpdateCreateModal).not.toBeVisible(); - unfoldTokenTree(tokensSidebar, "shadow", "primary"); - // Verify token appears in sidebar const shadowToken = tokensSidebar.getByRole("button", { - name: "primary", + name: "shadow.primary", }); await expect(shadowToken).toBeEnabled(); diff --git a/frontend/scripts/setup b/frontend/scripts/setup new file mode 100755 index 0000000000..7c7014e54d --- /dev/null +++ b/frontend/scripts/setup @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +corepack enable; +corepack install; +yarn install; +yarn playwright install chromium; diff --git a/frontend/scripts/test-components b/frontend/scripts/test-components index 5e7676c1e7..747a417780 100755 --- a/frontend/scripts/test-components +++ b/frontend/scripts/test-components @@ -1,10 +1,10 @@ #!/usr/bin/env bash -set -ex -corepack enable; -corepack install; -yarn install; +SCRIPT_DIR=$(dirname $0); + +set -ex + +$SCRIPT_DIR/setup; -yarn run playwright install chromium --with-deps; yarn run build:storybook yarn run test:storybook diff --git a/frontend/scripts/test-e2e b/frontend/scripts/test-e2e index 8accfcced5..1903b4ff18 100755 --- a/frontend/scripts/test-e2e +++ b/frontend/scripts/test-e2e @@ -1,8 +1,9 @@ #!/usr/bin/env bash +SCRIPT_DIR=$(dirname $0); + set -ex -corepack enable; -corepack install; -yarn install; -yarn run playwright install chromium --with-deps; + +$SCRIPT_DIR/setup; + yarn run test:e2e -x --workers=2 --reporter=list "$@"; diff --git a/frontend/src/app/main/data/workspace/path/edition.cljs b/frontend/src/app/main/data/workspace/path/edition.cljs index 8839947701..1210111f71 100644 --- a/frontend/src/app/main/data/workspace/path/edition.cljs +++ b/frontend/src/app/main/data/workspace/path/edition.cljs @@ -47,32 +47,31 @@ (ptk/reify ::apply-content-modifiers ptk/WatchEvent (watch [it state _] - (let [page-id (get state :current-page-id state) - objects (dsh/lookup-page-objects state) - - id (st/get-path-id state) - - shape - (st/get-path state) + (let [id (st/get-path-id state) + shape (st/get-path state) content-modifiers - (dm/get-in state [:workspace-local :edit-path id :content-modifiers]) + (dm/get-in state [:workspace-local :edit-path id :content-modifiers])] + (if (or (nil? shape) (nil? content-modifiers)) + (rx/of (dwe/clear-edition-mode)) + (let [page-id (get state :current-page-id state) + objects (dsh/lookup-page-objects state) - content (get shape :content) - new-content (path/apply-content-modifiers content content-modifiers) + content (get shape :content) + new-content (path/apply-content-modifiers content content-modifiers) - old-points (path.segment/get-points content) - new-points (path.segment/get-points new-content) - point-change (->> (map hash-map old-points new-points) (reduce merge))] + old-points (path.segment/get-points content) + new-points (path.segment/get-points new-content) + point-change (->> (map hash-map old-points new-points) (reduce merge))] - (when (and (some? new-content) (some? shape)) - (let [changes (changes/generate-path-changes it objects page-id shape (:content shape) new-content)] - (if (empty? new-content) - (rx/of (dch/commit-changes changes) - (dwe/clear-edition-mode)) - (rx/of (dch/commit-changes changes) - (selection/update-selection point-change) - (fn [state] (update-in state [:workspace-local :edit-path id] dissoc :content-modifiers :moving-nodes :moving-handler)))))))))) + (when (and (some? new-content) (some? shape)) + (let [changes (changes/generate-path-changes it objects page-id shape (:content shape) new-content)] + (if (empty? new-content) + (rx/of (dch/commit-changes changes) + (dwe/clear-edition-mode)) + (rx/of (dch/commit-changes changes) + (selection/update-selection point-change) + (fn [state] (update-in state [:workspace-local :edit-path id] dissoc :content-modifiers :moving-nodes :moving-handler)))))))))))) (defn modify-content-point [content {dx :x dy :y} modifiers point] diff --git a/frontend/src/app/main/ui/components/title_bar.cljs b/frontend/src/app/main/ui/components/title_bar.cljs index 9c63312590..432936b0b3 100644 --- a/frontend/src/app/main/ui/components/title_bar.cljs +++ b/frontend/src/app/main/ui/components/title_bar.cljs @@ -53,6 +53,6 @@ (mf/defc inspect-title-bar* - [{:keys [class title]}] + [{:keys [class title title-class]}] [:div {:class [(stl/css :title-bar) class]} - [:div {:class (stl/css :title-only :inspect-title)} title]]) + [:div {:class [title-class (stl/css :title-only :inspect-title)]} title]]) diff --git a/frontend/src/app/main/ui/ds/layers/layer_button.cljs b/frontend/src/app/main/ui/ds/layers/layer_button.cljs deleted file mode 100644 index 759952c30a..0000000000 --- a/frontend/src/app/main/ui/ds/layers/layer_button.cljs +++ /dev/null @@ -1,49 +0,0 @@ -;; 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.ds.layers.layer-button - (:require-macros - [app.main.style :as stl]) - (:require - [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] - [rumext.v2 :as mf])) - -(def ^:private schema:layer-button - [:map - [:label :string] - [:description {:optional true} [:maybe :string]] - [:class {:optional true} :string] - [:expandable {:optional true} :boolean] - [:expanded {:optional true} :boolean] - [:icon {:optional true} :string] - [:on-toggle-expand fn?]]) - -(mf/defc layer-button* - {::mf/schema schema:layer-button} - [{:keys [label description class is-expandable expanded icon on-toggle-expand children] :rest props}] - (let [button-props (mf/spread-props props - {:class [class (stl/css-case :layer-button true - :layer-button--expandable is-expandable - :layer-button--expanded expanded)] - :type "button" - :on-click on-toggle-expand})] - [:div {:class (stl/css :layer-button-wrapper)} - [:> "button" button-props - [:div {:class (stl/css :layer-button-content)} - (when is-expandable - (if expanded - [:> icon* {:icon-id i/arrow-down :class (stl/css :folder-node-icon)}] - [:> icon* {:icon-id i/arrow-right :class (stl/css :folder-node-icon)}])) - (when icon - [:> icon* {:icon-id icon :class (stl/css :layer-button-icon)}]) - [:span {:class (stl/css :layer-button-name)} - label] - (when description - [:span {:class (stl/css :layer-button-description)} - description]) - [:span {:class (stl/css :layer-button-quantity)}]]] - [:div {:class (stl/css :layer-button-actions)} - children]])) diff --git a/frontend/src/app/main/ui/ds/layers/layer_button.scss b/frontend/src/app/main/ui/ds/layers/layer_button.scss deleted file mode 100644 index 56e59e8acf..0000000000 --- a/frontend/src/app/main/ui/ds/layers/layer_button.scss +++ /dev/null @@ -1,56 +0,0 @@ -// 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 - -@use "ds/_borders.scss" as *; -@use "ds/_sizes.scss" as *; -@use "ds/typography.scss" as *; -@use "ds/colors.scss" as *; - -.layer-button-wrapper { - --layer-button-block-size: #{$sz-32}; - --layer-button-background: var(--color-background-primary); - --layer-button-text: var(--color-foreground-secondary); - - display: flex; - justify-content: space-between; - - block-size: var(--layer-button-block-size); - - background: var(--layer-button-background); - color: var(--layer-button-text); -} - -.layer-button { - @include use-typography("body-small"); - - appearance: none; - - flex: 1; - display: flex; - align-items: center; - - border: none; - background: none; - color: inherit; -} - -.layer-button--expanded { - & .layer-button-name { - color: var(--color-foreground-primary); - } -} - -.layer-button-content { - display: flex; - align-items: center; - gap: var(--sp-xs); -} - -.layer-button-description { - padding: var(--sp-xs); - background-color: var(--color-background-tertiary); - border-radius: $br-6; -} diff --git a/frontend/src/app/main/ui/inspect/attributes.scss b/frontend/src/app/main/ui/inspect/attributes.scss index 7735b10010..4eafa389eb 100644 --- a/frontend/src/app/main/ui/inspect/attributes.scss +++ b/frontend/src/app/main/ui/inspect/attributes.scss @@ -12,14 +12,17 @@ flex-direction: column; gap: var(--sp-l); width: 100%; - height: calc(100vh - px2rem(128)); // TODO: Fix this hardcoded value + max-height: calc(100vh - px2rem(128)); // TODO: Fix this hardcoded value padding-top: var(--sp-s); + padding-inline: var(--sp-m); overflow-y: auto; overflow-x: hidden; scrollbar-gutter: stable; + background-color: var(--low-emphasis-background); } .workspace-element-options { - height: calc(100vh - px2rem(200)); // TODO: Fix this hardcoded value + max-height: calc(100vh - px2rem(180)); // TODO: Fix this hardcoded value padding-inline: var(--sp-m); + background-color: var(--low-emphasis-background); } diff --git a/frontend/src/app/main/ui/inspect/attributes/blur.cljs b/frontend/src/app/main/ui/inspect/attributes/blur.cljs index 979eeaf0fb..21a21a6b4b 100644 --- a/frontend/src/app/main/ui/inspect/attributes/blur.cljs +++ b/frontend/src/app/main/ui/inspect/attributes/blur.cljs @@ -24,7 +24,8 @@ [:div {:class (stl/css :attributes-block)} [:> inspect-title-bar* {:title (tr "inspect.attributes.blur") - :class (stl/css :title-spacing-blur)} + :class (stl/css :title-wrapper) + :title-class (stl/css :blur-attr-title)} (when (= (count shapes) 1) [:> copy-button* {:data (css/get-css-property objects (first shapes) :filter) :class (stl/css :copy-btn-title)}])] diff --git a/frontend/src/app/main/ui/inspect/attributes/blur.scss b/frontend/src/app/main/ui/inspect/attributes/blur.scss index 736ab135c9..9ae8c464eb 100644 --- a/frontend/src/app/main/ui/inspect/attributes/blur.scss +++ b/frontend/src/app/main/ui/inspect/attributes/blur.scss @@ -5,17 +5,28 @@ // Copyright (c) KALEIDOS INC @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; .attributes-block { - @include deprecated.flexColumn; + --box-border-color: var(--color-background-primary); + display: flex; + flex-direction: column; + border-block-end: $b-2 solid var(--box-border-color); } -.title-spacing-blur { - @extend .attr-title; +.title-wrapper { + margin-inline-start: 0; +} + +.blur-attr-title { + color: var(--entry-foreground-color-hover); + padding-block: var(--sp-s); } .blur-row { @extend .attr-row; + block-size: $sz-36; } .button-children { @@ -23,5 +34,5 @@ } .copy-btn-title { - max-width: deprecated.$s-28; + max-inline-size: $sz-28; } diff --git a/frontend/src/app/main/ui/inspect/attributes/fill.cljs b/frontend/src/app/main/ui/inspect/attributes/fill.cljs index e408859c4f..7c3ea85640 100644 --- a/frontend/src/app/main/ui/inspect/attributes/fill.cljs +++ b/frontend/src/app/main/ui/inspect/attributes/fill.cljs @@ -68,7 +68,8 @@ [:div {:class (stl/css :attributes-block)} [:> inspect-title-bar* {:title (tr "inspect.attributes.fill") - :class (stl/css :title-spacing-fill)}] + :class (stl/css :title-wrapper) + :class-title (stl/css :fill-attr-title)}] [:div {:class (stl/css :attributes-content)} (for [shape shapes] diff --git a/frontend/src/app/main/ui/inspect/attributes/fill.scss b/frontend/src/app/main/ui/inspect/attributes/fill.scss index c55c401b98..3cede83d81 100644 --- a/frontend/src/app/main/ui/inspect/attributes/fill.scss +++ b/frontend/src/app/main/ui/inspect/attributes/fill.scss @@ -5,16 +5,30 @@ // Copyright (c) KALEIDOS INC @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; .attributes-block { - @include deprecated.flexColumn; + --box-border-color: var(--color-background-primary); + display: flex; + flex-direction: column; + border-block-end: $b-2 solid var(--box-border-color); } -.title-spacing-fill { - @extend .attr-title; +.title-wrapper { + margin-inline-start: 0; +} + +.fill-attr-title { + color: var(--entry-foreground-color-hover); + padding-block: var(--sp-s); } .attributes-content { display: grid; gap: deprecated.$s-4; } + +.attributes-fill-block { + block-size: $sz-36; +} diff --git a/frontend/src/app/main/ui/inspect/attributes/geometry.cljs b/frontend/src/app/main/ui/inspect/attributes/geometry.cljs index 78828abdba..52d765fb86 100644 --- a/frontend/src/app/main/ui/inspect/attributes/geometry.cljs +++ b/frontend/src/app/main/ui/inspect/attributes/geometry.cljs @@ -44,7 +44,8 @@ [:div {:class (stl/css :attributes-block)} [:> inspect-title-bar* {:title (tr "inspect.attributes.size") - :class (stl/css :title-spacing-geometry)} + :class (stl/css :title-wrapper) + :title-class (stl/css :geometry-attr-title)} (when (= (count shapes) 1) [:> copy-button* {:data (css/get-shape-properties-css objects (first shapes) properties) diff --git a/frontend/src/app/main/ui/inspect/attributes/geometry.scss b/frontend/src/app/main/ui/inspect/attributes/geometry.scss index dd002ce5e6..f1a90db1e3 100644 --- a/frontend/src/app/main/ui/inspect/attributes/geometry.scss +++ b/frontend/src/app/main/ui/inspect/attributes/geometry.scss @@ -5,17 +5,28 @@ // Copyright (c) KALEIDOS INC @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; .attributes-block { - @include deprecated.flexColumn; + --box-border-color: var(--color-background-primary); + display: flex; + flex-direction: column; + border-block-end: $b-2 solid var(--box-border-color); } -.title-spacing-geometry { - @extend .attr-title; +.title-wrapper { + margin-inline-start: 0; +} + +.geometry-attr-title { + color: var(--entry-foreground-color-hover); + padding-block: var(--sp-s); } .geometry-row { @extend .attr-row; + block-size: $sz-36; } .button-children { @@ -23,5 +34,5 @@ } .copy-btn-title { - max-width: deprecated.$s-28; + max-inline-size: $sz-28; } diff --git a/frontend/src/app/main/ui/inspect/attributes/layout.cljs b/frontend/src/app/main/ui/inspect/attributes/layout.cljs index 545efdc9fa..e8e4c597ca 100644 --- a/frontend/src/app/main/ui/inspect/attributes/layout.cljs +++ b/frontend/src/app/main/ui/inspect/attributes/layout.cljs @@ -57,7 +57,8 @@ [:div {:class (stl/css :attributes-block)} [:> inspect-title-bar* {:title "Layout" - :class (stl/css :title-spacing-layout)} + :class (stl/css :title-wrapper) + :title-class (stl/css :layout-attr-title)} (when (= (count shapes) 1) [:> copy-button* {:data (css/get-shape-properties-css objects (first shapes) properties) diff --git a/frontend/src/app/main/ui/inspect/attributes/layout.scss b/frontend/src/app/main/ui/inspect/attributes/layout.scss index 0eb1b64168..2164e152fc 100644 --- a/frontend/src/app/main/ui/inspect/attributes/layout.scss +++ b/frontend/src/app/main/ui/inspect/attributes/layout.scss @@ -5,17 +5,28 @@ // Copyright (c) KALEIDOS INC @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; .attributes-block { - @include deprecated.flexColumn; + --box-border-color: var(--color-background-primary); + display: flex; + flex-direction: column; + border-block-end: $b-2 solid var(--box-border-color); } -.title-spacing-layout { - @extend .attr-title; +.title-wrapper { + margin-inline-start: 0; +} + +.layout-attr-title { + color: var(--entry-foreground-color-hover); + padding-block: var(--sp-s); } .layout-row { @extend .attr-row; + block-size: $sz-36; } .button-children { @@ -23,5 +34,5 @@ } .copy-btn-title { - max-width: deprecated.$s-28; + max-inline-size: $sz-28; } diff --git a/frontend/src/app/main/ui/inspect/attributes/layout_element.cljs b/frontend/src/app/main/ui/inspect/attributes/layout_element.cljs index 599b8b5cbf..8269329eac 100644 --- a/frontend/src/app/main/ui/inspect/attributes/layout_element.cljs +++ b/frontend/src/app/main/ui/inspect/attributes/layout_element.cljs @@ -69,7 +69,8 @@ [:div {:class (stl/css :attributes-block)} [:> title-bar* {:collapsable false :title menu-title - :class (stl/css :title-spacing-layout-element)} + :class (stl/css :title-wrapper) + :title-class (stl/css :layout-element-attr-title)} (when (= (count shapes) 1) [:> copy-button* {:data (css/get-shape-properties-css objects (first shapes) properties) :class (stl/css :copy-btn-title)}])] diff --git a/frontend/src/app/main/ui/inspect/attributes/layout_element.scss b/frontend/src/app/main/ui/inspect/attributes/layout_element.scss index 7f32cca1a1..a51009ab53 100644 --- a/frontend/src/app/main/ui/inspect/attributes/layout_element.scss +++ b/frontend/src/app/main/ui/inspect/attributes/layout_element.scss @@ -5,17 +5,28 @@ // Copyright (c) KALEIDOS INC @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; .attributes-block { - @include deprecated.flexColumn; + --box-border-color: var(--color-background-primary); + display: flex; + flex-direction: column; + border-block-end: $b-2 solid var(--box-border-color); } -.title-spacing-layout-element { - @extend .attr-title; +.title-wrapper { + margin-inline-start: 0; +} + +.layout-element-attr-title { + color: var(--entry-foreground-color-hover); + padding-block: var(--sp-s); } .layout-element-row { @extend .attr-row; + block-size: $sz-36; } .button-children { @@ -23,5 +34,6 @@ } .copy-btn-title { - max-width: deprecated.$s-28; + max-inline-size: $sz-28; + max-inline-size: $sz-28; } diff --git a/frontend/src/app/main/ui/inspect/attributes/shadow.cljs b/frontend/src/app/main/ui/inspect/attributes/shadow.cljs index 55da3e552f..d64e5be956 100644 --- a/frontend/src/app/main/ui/inspect/attributes/shadow.cljs +++ b/frontend/src/app/main/ui/inspect/attributes/shadow.cljs @@ -63,7 +63,8 @@ [:div {:class (stl/css :attributes-block)} [:> inspect-title-bar* {:title (tr "inspect.attributes.shadow") - :class (stl/css :title-spacing-shadow)}] + :class (stl/css :title-wrapper) + :title-class (stl/css :shadow-attr-title)}] [:div {:class (stl/css :attributes-content)} (for [shape shapes] diff --git a/frontend/src/app/main/ui/inspect/attributes/shadow.scss b/frontend/src/app/main/ui/inspect/attributes/shadow.scss index 106cb03bca..8cfb86f0c3 100644 --- a/frontend/src/app/main/ui/inspect/attributes/shadow.scss +++ b/frontend/src/app/main/ui/inspect/attributes/shadow.scss @@ -5,17 +5,28 @@ // Copyright (c) KALEIDOS INC @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; .attributes-block { - @include deprecated.flexColumn; + --box-border-color: var(--color-background-primary); + display: flex; + flex-direction: column; + border-block-end: $b-2 solid var(--box-border-color); } -.title-spacing-shadow { - @extend .attr-title; +.title-wrapper { + margin-inline-start: 0; +} + +.shadow-attr-title { + color: var(--entry-foreground-color-hover); + padding-block: var(--sp-s); } .shadow-row { @extend .attr-row; + block-size: $sz-36; } .button-children { diff --git a/frontend/src/app/main/ui/inspect/attributes/stroke.cljs b/frontend/src/app/main/ui/inspect/attributes/stroke.cljs index 11bdfc1655..1592cc5002 100644 --- a/frontend/src/app/main/ui/inspect/attributes/stroke.cljs +++ b/frontend/src/app/main/ui/inspect/attributes/stroke.cljs @@ -88,7 +88,8 @@ [:div {:class (stl/css :attributes-block)} [:> inspect-title-bar* {:title (tr "inspect.attributes.stroke") - :class (stl/css :title-spacing-stroke)}] + :class (stl/css :title-wrapper) + :title-class (stl/css :stroke-attr-title)}] [:div {:class (stl/css :attributes-content)} (for [shape shapes] diff --git a/frontend/src/app/main/ui/inspect/attributes/stroke.scss b/frontend/src/app/main/ui/inspect/attributes/stroke.scss index a83ad6cfea..dd5bf8d4b4 100644 --- a/frontend/src/app/main/ui/inspect/attributes/stroke.scss +++ b/frontend/src/app/main/ui/inspect/attributes/stroke.scss @@ -5,21 +5,34 @@ // Copyright (c) KALEIDOS INC @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; .attributes-block { - @include deprecated.flexColumn; + --box-border-color: var(--color-background-primary); + display: flex; + flex-direction: column; + border-block-end: $b-2 solid var(--box-border-color); } -.title-spacing-stroke { - @extend .attr-title; +.title-wrapper { + margin-inline-start: 0; +} + +.stroke-attr-title { + color: var(--entry-foreground-color-hover); + padding-block: var(--sp-s); } .attributes-stroke-block { - @include deprecated.flexColumn; + display: flex; + flex-direction: column; + gap: var(--sp-xs); } .stroke-row { @extend .attr-row; + block-size: $sz-36; } .button-children { @@ -28,5 +41,5 @@ .attributes-content { display: grid; - gap: deprecated.$s-4; + gap: var(--sp-xs); } diff --git a/frontend/src/app/main/ui/inspect/attributes/svg.cljs b/frontend/src/app/main/ui/inspect/attributes/svg.cljs index 798a1b0849..27305b4901 100644 --- a/frontend/src/app/main/ui/inspect/attributes/svg.cljs +++ b/frontend/src/app/main/ui/inspect/attributes/svg.cljs @@ -54,5 +54,6 @@ [:div {:class (stl/css :attributes-block)} [:> inspect-title-bar* {:title (tr "workspace.sidebar.options.svg-attrs.title") - :class (stl/css :title-spacing-svg)}] + :class (stl/css :title-wrapper) + :title-class (stl/css :svg-attr-title)}] [:& svg-block {:shape shape}]]))) diff --git a/frontend/src/app/main/ui/inspect/attributes/svg.scss b/frontend/src/app/main/ui/inspect/attributes/svg.scss index 41846547dc..1b7495e61d 100644 --- a/frontend/src/app/main/ui/inspect/attributes/svg.scss +++ b/frontend/src/app/main/ui/inspect/attributes/svg.scss @@ -5,17 +5,29 @@ // Copyright (c) KALEIDOS INC @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/typography.scss" as *; .attributes-block { - @include deprecated.flexColumn; + --box-border-color: var(--color-background-primary); + display: flex; + flex-direction: column; + border-block-end: $b-2 solid var(--box-border-color); } -.title-spacing-svg { - @extend .attr-title; +.title-wrapper { + margin-inline-start: 0; +} + +.svg-attr-title { + color: var(--entry-foreground-color-hover); + padding-block: var(--sp-s); } .svg-row { @extend .attr-row; + block-size: $sz-36; } .button-children { @@ -23,12 +35,12 @@ } .attributes-subtitle { - @include deprecated.uppercaseTitleTipography; + @include use-typography("headline-small"); display: flex; justify-content: space-between; - height: deprecated.$s-32; + block-size: $sz-32; span { - height: deprecated.$s-32; + block-size: $sz-32; display: flex; align-items: center; } diff --git a/frontend/src/app/main/ui/inspect/attributes/text.cljs b/frontend/src/app/main/ui/inspect/attributes/text.cljs index 0d293bf0ef..33feff476c 100644 --- a/frontend/src/app/main/ui/inspect/attributes/text.cljs +++ b/frontend/src/app/main/ui/inspect/attributes/text.cljs @@ -157,7 +157,8 @@ [:div {:class (stl/css :attributes-block)} [:> inspect-title-bar* {:title (tr "inspect.attributes.typography") - :class (stl/css :title-spacing-text)}] + :class (stl/css :title-wrapper) + :title-class (stl/css :text-atrr-title)}] (for [shape shapes] [:& text-block {:shape shape diff --git a/frontend/src/app/main/ui/inspect/attributes/text.scss b/frontend/src/app/main/ui/inspect/attributes/text.scss index 54bc1e8095..9f3ecf1808 100644 --- a/frontend/src/app/main/ui/inspect/attributes/text.scss +++ b/frontend/src/app/main/ui/inspect/attributes/text.scss @@ -5,23 +5,37 @@ // Copyright (c) KALEIDOS INC @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; .attributes-block { - @include deprecated.flexColumn; + --box-border-color: var(--color-background-primary); + display: flex; + flex-direction: column; + border-block-end: $b-2 solid var(--box-border-color); } -.title-spacing-text { - @extend .attr-title; +.title-wrapper { + margin-inline-start: 0; +} + +.text-attr-title { + color: var(--entry-foreground-color-hover); + padding-block: var(--sp-s); } .attributes-content { - @include deprecated.flexColumn; + display: flex; + flex-direction: column; + gap: var(--sp-xs); } .text-row { @extend .attr-row; - height: unset; - min-height: deprecated.$s-32; + block-size: unset; + min-block-size: $sz-36; :global(.attr-value) { align-items: center; } @@ -32,20 +46,20 @@ } .attributes-content-row { - max-width: deprecated.$s-240; - min-height: calc(deprecated.$s-2 + deprecated.$s-32); - border-radius: deprecated.$br-8; - border: deprecated.$s-1 solid var(--menu-border-color-disabled); - margin-top: deprecated.$s-4; + max-inline-size: px2rem(240); + min-block-size: px2rem(34); + border-radius: $br-8; + border: $b-1 solid var(--menu-border-color-disabled); + margin-block-start: var(--sp-xs); .content { - @include deprecated.bodySmallTypography; + @include use-typography("body-small"); width: 100%; - padding: deprecated.$s-4 0; + padding: var(--sp-xs) 0; color: var(--color-foreground-secondary); } &:hover { - border: deprecated.$s-1 solid var(--color-background-tertiary); + border: $b-1 solid var(--color-background-tertiary); background-color: var(--menu-background-color); .content { color: var(--menu-foreground-color-hover); diff --git a/frontend/src/app/main/ui/inspect/attributes/variant.cljs b/frontend/src/app/main/ui/inspect/attributes/variant.cljs index e9b74dad0f..82b764a606 100644 --- a/frontend/src/app/main/ui/inspect/attributes/variant.cljs +++ b/frontend/src/app/main/ui/inspect/attributes/variant.cljs @@ -42,7 +42,8 @@ [:div {:class (stl/css :attributes-block)} [:> inspect-title-bar* {:title (if is-container? (tr "inspect.attributes.variants") (tr "inspect.attributes.variant")) - :class (stl/css :title-spacing-variant)}] + :class (stl/css :title-wrapper) + :title-class (stl/css :variant-attr-title)}] (for [[pos property] (map-indexed vector properties)] [:> variant-block* {:key (dm/str "variant-property-" pos) :name (:name property) :value (:value property)}])])) diff --git a/frontend/src/app/main/ui/inspect/attributes/variant.scss b/frontend/src/app/main/ui/inspect/attributes/variant.scss index 7478fae373..3d0df70402 100644 --- a/frontend/src/app/main/ui/inspect/attributes/variant.scss +++ b/frontend/src/app/main/ui/inspect/attributes/variant.scss @@ -5,18 +5,29 @@ // Copyright (c) KALEIDOS INC @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; .attributes-block { - @include deprecated.flexColumn; + --box-border-color: var(--color-background-primary); + display: flex; + flex-direction: column; + border-block-end: $b-2 solid var(--box-border-color); } -.title-spacing-variant { - @extend .attr-title; +.title-wrapper { + margin-inline-start: 0; +} + +.variant-attr-title { + color: var(--entry-foreground-color-hover); + padding-block: var(--sp-s); } .variant-row { @extend .attr-row; - height: fit-content; + block-size: fit-content; + min-block-size: $sz-36; } .button-children { diff --git a/frontend/src/app/main/ui/inspect/attributes/visibility.cljs b/frontend/src/app/main/ui/inspect/attributes/visibility.cljs index b89e165501..af1fc789b9 100644 --- a/frontend/src/app/main/ui/inspect/attributes/visibility.cljs +++ b/frontend/src/app/main/ui/inspect/attributes/visibility.cljs @@ -51,7 +51,8 @@ [:div {:class (stl/css :attributes-block)} [:> inspect-title-bar* {:title "Visibility" - :class (stl/css :title-spacing-visibility)} + :class (stl/css :title-wrapper) + :title-class (stl/css :visibility-attr-title)} (when (= (count shapes) 1) [:> copy-button* {:data (css/get-shape-properties-css objects (first shapes) properties) diff --git a/frontend/src/app/main/ui/inspect/attributes/visibility.scss b/frontend/src/app/main/ui/inspect/attributes/visibility.scss index d76c906b18..c888735ff1 100644 --- a/frontend/src/app/main/ui/inspect/attributes/visibility.scss +++ b/frontend/src/app/main/ui/inspect/attributes/visibility.scss @@ -5,17 +5,28 @@ // Copyright (c) KALEIDOS INC @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; .attributes-block { - @include deprecated.flexColumn; + --box-border-color: var(--color-background-primary); + display: flex; + flex-direction: column; + border-block-end: $b-2 solid var(--box-border-color); } -.title-spacing-visibility { - @extend .attr-title; +.title-wrapper { + margin-inline-start: 0; +} + +.visibility-attr-title { + color: var(--entry-foreground-color-hover); + padding-block: var(--sp-s); } .visibility-row { @extend .attr-row; + block-size: $sz-36; } .button-children { @@ -23,5 +34,5 @@ } .copy-btn-title { - max-width: deprecated.$s-28; + max-inline-size: $sz-28; } diff --git a/frontend/src/app/main/ui/inspect/right_sidebar.cljs b/frontend/src/app/main/ui/inspect/right_sidebar.cljs index bcee180956..5e205b502a 100644 --- a/frontend/src/app/main/ui/inspect/right_sidebar.cljs +++ b/frontend/src/app/main/ui/inspect/right_sidebar.cljs @@ -186,6 +186,7 @@ [:> styles-tab* {:color-space color-space :objects objects :shapes shapes + :from from :libraries libraries :file-id file-id}] :computed diff --git a/frontend/src/app/main/ui/inspect/right_sidebar.scss b/frontend/src/app/main/ui/inspect/right_sidebar.scss index e0393254cd..ca57b53f1c 100644 --- a/frontend/src/app/main/ui/inspect/right_sidebar.scss +++ b/frontend/src/app/main/ui/inspect/right_sidebar.scss @@ -24,10 +24,6 @@ } } -.viewer-code { - padding-inline-start: var(--sp-s); -} - .tool-windows { block-size: 100%; display: grid; diff --git a/frontend/src/app/main/ui/inspect/styles.cljs b/frontend/src/app/main/ui/inspect/styles.cljs index 4c46bf393a..72df40bdbe 100644 --- a/frontend/src/app/main/ui/inspect/styles.cljs +++ b/frontend/src/app/main/ui/inspect/styles.cljs @@ -90,7 +90,7 @@ :multiple)) (mf/defc styles-tab* - [{:keys [color-space shapes libraries objects file-id]}] + [{:keys [color-space shapes libraries objects file-id from]}] (let [data (dm/get-in libraries [file-id :data]) first-shape (first shapes) first-component (ctkl/get-component data (:component-id first-shape)) @@ -131,7 +131,8 @@ (mf/deps shorthands*) (fn [shorthand] (swap! shorthands* assoc (:panel shorthand) (:property shorthand))))] - [:ol {:class (stl/css :styles-tab) :aria-label (tr "labels.styles")} + [:ol {:class (stl/css-case :styles-tab true + :styles-tab-workspace (= from :workspace)) :aria-label (tr "labels.styles")} ;; TOKENS PANEL (when (or (seq active-themes) (seq active-sets)) [:li diff --git a/frontend/src/app/main/ui/inspect/styles.scss b/frontend/src/app/main/ui/inspect/styles.scss index 1c1c4e6871..0680351132 100644 --- a/frontend/src/app/main/ui/inspect/styles.scss +++ b/frontend/src/app/main/ui/inspect/styles.scss @@ -7,5 +7,9 @@ @use "ds/_utils.scss" as *; .styles-tab { - block-size: calc(100vh - px2rem(200)); // TODO: Fix this hardcoded value + block-size: calc(100vh - px2rem(140)); // TODO: Fix this hardcoded value +} + +.styles-tab-workspace { + block-size: calc(100vh - px2rem(180)); // TODO: Fix this hardcoded value } diff --git a/frontend/src/app/main/ui/inspect/styles/property_detail_copiable.scss b/frontend/src/app/main/ui/inspect/styles/property_detail_copiable.scss index 413f1437f4..c1ddecdf4e 100644 --- a/frontend/src/app/main/ui/inspect/styles/property_detail_copiable.scss +++ b/frontend/src/app/main/ui/inspect/styles/property_detail_copiable.scss @@ -60,6 +60,7 @@ } .property-detail-text { + @include use-typography("body-small"); color: var(--detail-color); } diff --git a/frontend/src/app/main/ui/inspect/styles/style_box.cljs b/frontend/src/app/main/ui/inspect/styles/style_box.cljs index 432639ac26..9d63b2a052 100644 --- a/frontend/src/app/main/ui/inspect/styles/style_box.cljs +++ b/frontend/src/app/main/ui/inspect/styles/style_box.cljs @@ -19,7 +19,7 @@ (case type :variant (tr "inspect.tabs.styles.variants-panel") :token (tr "inspect.tabs.styles.token-panel") - :geometry (tr "inspect.tabs.styles.geometry-panel") + :geometry (tr "inspect.attributes.size") :fill (tr "labels.fill") :stroke (tr "labels.stroke") :text (tr "labels.text") diff --git a/frontend/src/app/main/ui/inspect/styles/style_box.scss b/frontend/src/app/main/ui/inspect/styles/style_box.scss index fcb9ac73b7..a55a6b5fc4 100644 --- a/frontend/src/app/main/ui/inspect/styles/style_box.scss +++ b/frontend/src/app/main/ui/inspect/styles/style_box.scss @@ -33,7 +33,6 @@ display: flex; align-items: center; gap: var(--title-gap); - padding-block: var(--title-padding); } .disclosure-button { @@ -52,4 +51,5 @@ @include use-typography("headline-small"); flex: 1; color: var(--title-color); + padding-block: var(--title-padding); } diff --git a/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs b/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs index e93327313d..24996169ac 100644 --- a/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs @@ -148,11 +148,12 @@ (mf/use-fn (mf/deps index prefix is-move) (fn [event] - (dom/stop-propagation event) - (dom/prevent-default event) + (when (dom/left-mouse? event) + (dom/stop-propagation event) + (dom/prevent-default event) - (when ^boolean is-move - (st/emit! (drp/start-move-handler index prefix)))))] + (when ^boolean is-move + (st/emit! (drp/start-move-handler index prefix))))))] [:g.handler {:pointer-events (if ^boolean is-draw "none" "visible")} [:line diff --git a/frontend/src/app/main/ui/workspace/tokens/management.cljs b/frontend/src/app/main/ui/workspace/tokens/management.cljs index 846c112bdb..98bbb080b5 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management.cljs @@ -44,39 +44,6 @@ [(seq (array/sort! empty)) (seq (array/sort! filled))])))) -(mf/defc selected-set-info* - {::mf/private true} - [{:keys [tokens-lib selected-token-set-id]}] - (let [selected-token-set - (mf/with-memo [tokens-lib] - (when selected-token-set-id - (some-> tokens-lib (ctob/get-set selected-token-set-id)))) - - active-token-sets-names - (mf/with-memo [tokens-lib] - (some-> tokens-lib (ctob/get-active-themes-set-names))) - - token-set-active? - (mf/use-fn - (mf/deps active-token-sets-names) - (fn [name] - (contains? active-token-sets-names name)))] - [:div {:class (stl/css :sets-header-container)} - [:> text* {:as "span" - :typography "headline-small" - :class (stl/css :sets-header)} - (tr "workspace.tokens.tokens-section-title" (ctob/get-name selected-token-set))] - [:div {:class (stl/css :sets-header-status) :title (tr "workspace.tokens.inactive-set-description")} - ;; NOTE: when no set in tokens-lib, the selected-token-set-id - ;; will be `nil`, so for properly hide the inactive message we - ;; check that at least `selected-token-set-id` has a value - (when (and (some? selected-token-set-id) - (not (token-set-active? (ctob/get-name selected-token-set)))) - [:* - [:> icon* {:class (stl/css :sets-header-status-icon) :icon-id i/eye-off}] - [:> text* {:as "span" :typography "body-small" :class (stl/css :sets-header-status-text)} - (tr "workspace.tokens.inactive-set")]])]])) - (mf/defc tokens-section* {::mf/private true} [{:keys [tokens-lib active-tokens resolved-active-tokens]}] @@ -98,7 +65,9 @@ selected-token-set-id (mf/deref refs/selected-token-set-id) - + selected-token-set + (when selected-token-set-id + (some-> tokens-lib (ctob/get-set selected-token-set-id))) ;; If we have not selected any set explicitly we just ;; select the first one from the list of sets @@ -123,9 +92,15 @@ tokens)] (ctob/group-by-type tokens))) + active-token-sets-names + (mf/with-memo [tokens-lib] + (some-> tokens-lib (ctob/get-active-themes-set-names))) - - + token-set-active? + (mf/use-fn + (mf/deps active-token-sets-names) + (fn [name] + (contains? active-token-sets-names name))) [empty-group filled-group] (mf/with-memo [tokens-by-type] @@ -143,27 +118,34 @@ [:* [:& token-context-menu] - - [:& selected-set-info* {:tokens-lib tokens-lib - :selected-token-set-id selected-token-set-id}] + [:div {:class (stl/css :sets-header-container)} + [:> text* {:as "span" :typography "headline-small" :class (stl/css :sets-header)} (tr "workspace.tokens.tokens-section-title" (ctob/get-name selected-token-set))] + [:div {:class (stl/css :sets-header-status) :title (tr "workspace.tokens.inactive-set-description")} + ;; NOTE: when no set in tokens-lib, the selected-token-set-id + ;; will be `nil`, so for properly hide the inactive message we + ;; check that at least `selected-token-set-id` has a value + (when (and (some? selected-token-set-id) + (not (token-set-active? (ctob/get-name selected-token-set)))) + [:* + [:> icon* {:class (stl/css :sets-header-status-icon) :icon-id i/eye-off}] + [:> text* {:as "span" :typography "body-small" :class (stl/css :sets-header-status-text)} + (tr "workspace.tokens.inactive-set")]])]] (for [type filled-group] (let [tokens (get tokens-by-type type)] [:> token-group* {:key (name type) - :tokens tokens - :is-expanded (get open-status type false) + :is-open (get open-status type false) :type type :selected-ids selected :selected-shapes selected-shapes :is-selected-inside-layout is-selected-inside-layout :active-theme-tokens resolved-active-tokens - :tokens-lib tokens-lib - :selected-token-set-id selected-token-set-id}])) + :tokens tokens}])) (for [type empty-group] [:> token-group* {:key (name type) - :tokens [] :type type :selected-shapes selected-shapes - :is-selected-inside-layout is-selected-inside-layout - :active-theme-tokens resolved-active-tokens}])])) + :is-selected-inside-layout :is-selected-inside-layout + :active-theme-tokens resolved-active-tokens + :tokens []}])])) diff --git a/frontend/src/app/main/ui/workspace/tokens/management/group.cljs b/frontend/src/app/main/ui/workspace/tokens/management/group.cljs index 0d038a2324..8dd73d5fce 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/group.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/group.cljs @@ -8,9 +8,6 @@ (ns app.main.ui.workspace.tokens.management.group (:require-macros [app.main.style :as stl]) (:require - [app.common.data :as d] - [app.common.data.macros :as dm] - [app.common.types.tokens-lib :as ctob] [app.main.data.modal :as modal] [app.main.data.workspace.tokens.application :as dwta] [app.main.data.workspace.tokens.library-edit :as dwtl] @@ -19,70 +16,51 @@ [app.main.ui.context :as ctx] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :as i] - [app.main.ui.ds.layers.layer-button :refer [layer-button*]] - [app.main.ui.workspace.tokens.management.token-tree :refer [token-tree*]] + [app.main.ui.workspace.sidebar.assets.common :as cmm] + [app.main.ui.workspace.tokens.management.token-pill :refer [token-pill*]] [app.util.dom :as dom] [app.util.i18n :refer [tr]] [rumext.v2 :as mf])) - (defn token-section-icon [type] (case type - :border-radius i/corner-radius - :color i/drop - :boolean i/boolean-difference - :font-family i/text-font-family - :font-size i/text-font-size - :letter-spacing i/text-letterspacing - :text-case i/text-mixed - :text-decoration i/text-underlined - :font-weight i/text-font-weight - :typography i/text-typography - :opacity i/percentage - :number i/number - :rotation i/rotation - :spacing i/padding-extended - :string i/text-mixed - :stroke-width i/stroke-size - :dimensions i/expand - :sizing i/expand - :shadow i/drop-shadow + :border-radius "corner-radius" + :color "drop" + :boolean "boolean-difference" + :font-family "text-font-family" + :font-size "text-font-size" + :letter-spacing "text-letterspacing" + :text-case "text-mixed" + :text-decoration "text-underlined" + :font-weight "text-font-weight" + :typography "text-typography" + :opacity "percentage" + :number "number" + :rotation "rotation" + :spacing "padding-extended" + :string "text-mixed" + :stroke-width "stroke-size" + :dimensions "expand" + :sizing "expand" + :shadow "drop-shadow" "add")) -(def ^:private schema:token-group - [:map - [:type :keyword] - [:tokens :any] - [:selected-shapes :any] - [:is-selected-inside-layout {:optional true} [:maybe :boolean]] - [:active-theme-tokens {:optional true} :any] - [:selected-token-set-id {:optional true} :any] - [:tokens-lib {:optional true} :any] - [:on-token-pill-click {:optional true} fn?] - [:on-context-menu {:optional true} fn?]]) - (mf/defc token-group* - {::mf/schema schema:token-group} - [{:keys [type tokens selected-shapes is-selected-inside-layout active-theme-tokens selected-token-set-id tokens-lib is-expanded selected-ids]}] + {::mf/private true} + [{:keys [type tokens selected-shapes is-selected-inside-layout active-theme-tokens is-open selected-ids]}] (let [{:keys [modal title]} (get dwta/token-properties type) editing-ref (mf/deref refs/workspace-editor-state) not-editing? (empty? editing-ref) - is-expanded (d/nilv is-expanded false) - can-edit? (mf/use-ctx ctx/can-edit?) - is-selected-inside-layout (d/nilv is-selected-inside-layout false) - tokens (mf/with-memo [tokens] (vec (sort-by :name tokens))) - expandable? (d/nilv (seq tokens) false) - on-context-menu (mf/use-fn (fn [event token] @@ -95,8 +73,8 @@ on-toggle-open-click (mf/use-fn - (mf/deps is-expanded type) - #(st/emit! (dwtl/set-token-type-section-open type (not is-expanded)))) + (mf/deps is-open type) + #(st/emit! (dwtl/set-token-type-section-open type (not is-open)))) on-popover-open-click (mf/use-fn @@ -118,36 +96,33 @@ (mf/use-fn (mf/deps not-editing? selected-ids) (fn [event token] - (let [token (ctob/get-token tokens-lib selected-token-set-id (:id token))] - (dom/stop-propagation event) - (when (and not-editing? (seq selected-shapes) (not= (:type token) :number)) - (st/emit! (dwta/toggle-token {:token token - :shape-ids selected-ids}))))))] + (dom/stop-propagation event) + (when (and not-editing? (seq selected-shapes) (not= (:type token) :number)) + (st/emit! (dwta/toggle-token {:token token + :shape-ids selected-ids})))))] - [:div {:class (stl/css :token-section-wrapper) - :data-testid (dm/str "section-" (name type))} - [:> layer-button* {:label title - :expanded is-expanded - :description (when expandable? (dm/str (count tokens))) - :is-expandable expandable? - :aria-expanded is-expanded - :aria-controls (dm/str "token-tree-" (name type)) - :on-toggle-expand on-toggle-open-click - :icon (token-section-icon type)} - (when can-edit? - [:> icon-button* {:id (str "add-token-button-" title) - :icon "add" - :aria-label (tr "workspace.tokens.add-token" title) - :variant "ghost" - :on-click on-popover-open-click - :class (stl/css :token-section-icon)}])] - (when is-expanded - [:> token-tree* {:tokens tokens - :id (dm/str "token-tree-" (name type)) - :tokens-lib tokens-lib - :selected-shapes selected-shapes - :active-theme-tokens active-theme-tokens - :selected-token-set-id selected-token-set-id - :is-selected-inside-layout is-selected-inside-layout - :on-token-pill-click on-token-pill-click - :on-context-menu on-context-menu}])])) + [:div {:on-click on-toggle-open-click :class (stl/css :token-section-wrapper)} + [:> cmm/asset-section* {:icon (token-section-icon type) + :title title + :section :tokens + :assets-count (count tokens) + :is-open is-open} + [:> cmm/asset-section-block* {:role :title-button} + (when can-edit? + [:> icon-button* {:on-click on-popover-open-click + :variant "ghost" + :icon i/add + :id (str "add-token-button-" title) + :aria-label (tr "workspace.tokens.add-token" title)}])] + (when is-open + [:> cmm/asset-section-block* {:role :content} + [:div {:class (stl/css :token-pills-wrapper)} + (for [token tokens] + [:> token-pill* + {:key (:name token) + :token token + :selected-shapes selected-shapes + :is-selected-inside-layout is-selected-inside-layout + :active-theme-tokens active-theme-tokens + :on-click on-token-pill-click + :on-context-menu on-context-menu}])]])]])) diff --git a/frontend/src/app/main/ui/workspace/tokens/management/group.scss b/frontend/src/app/main/ui/workspace/tokens/management/group.scss new file mode 100644 index 0000000000..e46bfb846f --- /dev/null +++ b/frontend/src/app/main/ui/workspace/tokens/management/group.scss @@ -0,0 +1,11 @@ +// 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 + +.token-pills-wrapper { + display: flex; + gap: var(--sp-xs); + flex-wrap: wrap; +} diff --git a/frontend/src/app/main/ui/workspace/tokens/management/token_pill.cljs b/frontend/src/app/main/ui/workspace/tokens/management/token_pill.cljs index bfb1a1f0a3..dd001e187c 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/token_pill.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/token_pill.cljs @@ -307,9 +307,10 @@ :class (stl/css :token-pill-icon)}]) (if contains-path? - (let [[_ last-part] (cpn/split-by-last-period name)] + (let [[first-part last-part] (cpn/split-by-last-period name)] [:span {:class (stl/css :divided-name-wrapper) :aria-label name} + [:span {:class (stl/css :first-name-wrapper)} first-part] [:span {:class (stl/css :last-name-wrapper)} last-part]]) [:span {:class (stl/css :name-wrapper) :aria-label name} diff --git a/frontend/src/app/main/ui/workspace/tokens/management/token_tree.cljs b/frontend/src/app/main/ui/workspace/tokens/management/token_tree.cljs deleted file mode 100644 index 5a31dbcd54..0000000000 --- a/frontend/src/app/main/ui/workspace/tokens/management/token_tree.cljs +++ /dev/null @@ -1,110 +0,0 @@ -;; 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.tokens.management.token-tree - (:require-macros [app.main.style :as stl]) - (:require - [app.common.path-names :as cpn] - [app.common.types.tokens-lib :as ctob] - [app.main.ui.ds.layers.layer-button :refer [layer-button*]] - [app.main.ui.workspace.tokens.management.token-pill :refer [token-pill*]] - [rumext.v2 :as mf])) - -(def ^:private schema:folder-node - [:map - [:node :any] - [:selected-shapes :any] - [:is-selected-inside-layout {:optional true} :boolean] - [:active-theme-tokens {:optional true} :any] - [:selected-token-set-id {:optional true} :any] - [:tokens-lib {:optional true} :any] - [:on-token-pill-click {:optional true} fn?] - [:on-context-menu {:optional true} fn?]]) - -(mf/defc folder-node* - {::mf/schema schema:folder-node} - [{:keys [node selected-shapes is-selected-inside-layout active-theme-tokens selected-token-set-id tokens-lib on-token-pill-click on-context-menu]}] - (let [expanded* (mf/use-state false) - expanded (deref expanded*) - swap-folder-expanded #(swap! expanded* not)] - [:li {:class (stl/css :folder-node)} - [:> layer-button* {:label (:name node) - :expanded expanded - :aria-expanded expanded - :aria-controls (str "folder-children-" (:path node)) - :is-expandable (not (:leaf node)) - :on-toggle-expand swap-folder-expanded}] - (when expanded - (let [children-fn (:children-fn node)] - [:div {:class (stl/css :folder-children-wrapper) - :id (str "folder-children-" (:path node))} - (when children-fn - (let [children (children-fn)] - (for [child children] - (if (not (:leaf child)) - [:ul {:class (stl/css :node-parent)} - [:> folder-node* {:key (:path child) - :node child - :selected-shapes selected-shapes - :is-selected-inside-layout is-selected-inside-layout - :active-theme-tokens active-theme-tokens - :on-token-pill-click on-token-pill-click - :on-context-menu on-context-menu - :tokens-lib tokens-lib - :selected-token-set-id selected-token-set-id}]] - (let [id (:id (:leaf child)) - token (ctob/get-token tokens-lib selected-token-set-id id)] - [:> token-pill* - {:key id - :token token - :selected-shapes selected-shapes - :is-selected-inside-layout is-selected-inside-layout - :active-theme-tokens active-theme-tokens - :on-click on-token-pill-click - :on-context-menu on-context-menu}])))))]))])) - -(def ^:private schema:token-tree - [:map - [:tokens :any] - [:selected-shapes :any] - [:is-selected-inside-layout {:optional true} :boolean] - [:active-theme-tokens {:optional true} :any] - [:selected-token-set-id {:optional true} :any] - [:tokens-lib {:optional true} :any] - [:on-token-pill-click {:optional true} fn?] - [:on-context-menu {:optional true} fn?]]) - -(mf/defc token-tree* - {::mf/schema schema:token-tree} - [{:keys [tokens selected-shapes is-selected-inside-layout active-theme-tokens tokens-lib selected-token-set-id on-token-pill-click on-context-menu]}] - (let [separator "." - tree (mf/use-memo - (mf/deps tokens) - (fn [] - (cpn/build-tree-root tokens separator)))] - [:div {:class (stl/css :token-tree-wrapper)} - (for [node tree] - [:ul {:class (stl/css :node-parent) - :key (:path node) - :style {:--node-depth (inc (:depth node))}} - (if (:leaf node) - (let [token (ctob/get-token tokens-lib selected-token-set-id (get-in node [:leaf :id]))] - [:> token-pill* - {:token token - :selected-shapes selected-shapes - :is-selected-inside-layout is-selected-inside-layout - :active-theme-tokens active-theme-tokens - :on-click on-token-pill-click - :on-context-menu on-context-menu}]) - ;; Render segment folder - [:> folder-node* {:node node - :selected-shapes selected-shapes - :is-selected-inside-layout is-selected-inside-layout - :active-theme-tokens active-theme-tokens - :on-token-pill-click on-token-pill-click - :on-context-menu on-context-menu - :tokens-lib tokens-lib - :selected-token-set-id selected-token-set-id}])])])) diff --git a/frontend/src/app/main/ui/workspace/tokens/management/token_tree.scss b/frontend/src/app/main/ui/workspace/tokens/management/token_tree.scss deleted file mode 100644 index 3320379d04..0000000000 --- a/frontend/src/app/main/ui/workspace/tokens/management/token_tree.scss +++ /dev/null @@ -1,39 +0,0 @@ -// 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 - -@use "ds/_borders.scss" as *; - -.token-tree-wrapper { - padding-block-end: var(--sp-s); -} - -.node-parent { - --node-spacing: var(--sp-l); - --node-depth: 0; - - margin-block-end: 0; - padding-inline-start: calc(var(--node-spacing) * var(--node-depth)); -} - -.folder-children-wrapper:has(> button) { - margin-inline-start: var(--sp-s); - padding-inline-start: var(--sp-s); - border-inline-start: $b-2 solid var(--color-background-quaternary); - display: flex; - flex-wrap: wrap; - column-gap: var(--sp-xs); - - & .node-parent { - flex: 1 0 100%; - - &:last-of-type { - margin-block-end: var(--sp-s); - } - } - & .token-pill { - flex: 0 0 auto; - } -} diff --git a/frontend/text-editor/README.md b/frontend/text-editor/README.md index 763f4424a9..705c2277bd 100644 --- a/frontend/text-editor/README.md +++ b/frontend/text-editor/README.md @@ -37,7 +37,7 @@ This command is going to search for the file located in `frontend/src/app/main/u ## How it works? -The text editor divides the content in three elements: `root`, `paragraph` and `inline`. An `inline` in terms of content is a styled element that it is displayed in a line inside a block and an `inline` only can have one child (a Text node). A `paragraph` is a **block** element that can contain multiple `inline`s (**inline** elements). +The text editor divides the content in three elements: `root`, `paragraph` and `textSpan`. In terms of content, a `textSpan` is a styled element displayed on a line within a block. A `textSpan` can only have one child (a Text node). A `paragraph` is a **block** element that can contain multiple `textSpan`s (**textSpan** elements). ```html