diff --git a/package-lock.json b/package-lock.json index 06cbc09965c..a36308ee2c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -282,20 +282,20 @@ } }, "node_modules/@asamuzakjp/css-color": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-6.0.7.tgz", - "integrity": "sha512-vC/bk1Lz7Tn/EfU9/apOTBk80/8dyGyWMowPoV1tJ52muDGsDqt2HPT2klrFUiY60MQmQv9q8yIht15JnBgDGw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-7.0.0.tgz", + "integrity": "sha512-IxddtGnnoidVSO6O/aiTueptbTThHvi2qGHu9/AWuD1J7i86Ox+jl+Y5SXcDH1RixzQlP2cjH/PjnlImZiAurQ==", "dev": true, "license": "MIT", "dependencies": { "@csstools/css-calc": "^3.3.0", - "@csstools/css-color-parser": "^4.1.10", + "@csstools/css-color-parser": "^4.2.0", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "lru-cache": "^11.5.2" }, "engines": { - "node": "^22.13.0 || >=24.0.0" + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" } }, "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { @@ -309,19 +309,19 @@ } }, "node_modules/@asamuzakjp/dom-selector": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-8.3.2.tgz", - "integrity": "sha512-93Z1N+BQNXysodoicpOIyNh2drHfz/CTf9nnT0FEx72GJcIiwgydD7tGAr78j41LsYn3hlRn+LdGPuBLn1Bl8Q==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-9.2.0.tgz", + "integrity": "sha512-qKvLetaSd/OTWif9JT0eu1SHBg3H7Nvp9SoMwMxqT5tmYyrxea/P1A5pIfGbnIJlOtm3yVeqw+ORxDOkjN1aEw==", "dev": true, "license": "MIT", "dependencies": { - "bidi-js": "^1.0.3", + "bidi-js": "^1.1.0", "css-tree": "^3.2.1", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.5.2" }, "engines": { - "node": "^22.13.0 || >=24.0.0" + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" } }, "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { @@ -2685,9 +2685,9 @@ } }, "node_modules/@csstools/css-calc": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.3.0.tgz", - "integrity": "sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.4.0.tgz", + "integrity": "sha512-XQKj5B7QiZcHiegCOCAzcAOJdhGgWOHbbu62h5e5mkHnn8lWcfiJhllkqWmxu5zWR9jucPHuo1iTB56P033hcg==", "dev": true, "funding": [ { @@ -2709,9 +2709,9 @@ } }, "node_modules/@csstools/css-color-parser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.1.tgz", - "integrity": "sha512-YpAJZhaHplYQkG8ib+/Fx5Y0eF2lVWi3tIvMJA6i39TLyUNp2439cifzW8VMjhlqrBjHzK5hVGugRRm2zTKI/A==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.3.tgz", + "integrity": "sha512-y4LpL+lmpuyKDiEFq2PnZUVFdAjsoB/qQJod79yLNokXyW7jewi+/WJ69EfItj8A2unWtxXnGjw6LYXgXu5ZjA==", "dev": true, "funding": [ { @@ -2726,7 +2726,7 @@ "license": "MIT", "dependencies": { "@csstools/color-helpers": "^6.1.1", - "@csstools/css-calc": "^3.3.0" + "@csstools/css-calc": "^3.4.0" }, "engines": { "node": ">=20.19.0" @@ -2760,9 +2760,9 @@ } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.9.tgz", - "integrity": "sha512-iGGw4OsAYsS6pD29MdJ2bX/nJx65a04ZZiw6x+VwWlP2DdXf6f++Zmuv/OzALpdyfVhjbduIIF2cXM7HWBIe9A==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.14.tgz", + "integrity": "sha512-HpbVXyrofRXpHpgkNIjU/3EWR4WJvOkO3emNK/L6X/mTJU7bGUI3AkkpoTNXznQLp0KRjLHELTGeKI5dIkI9JQ==", "dev": true, "funding": [ { @@ -2785,9 +2785,9 @@ } }, "node_modules/@csstools/css-tokenizer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", - "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.1.tgz", + "integrity": "sha512-bPlN9S9O1A0euCpEWE4qnvB5YDuyYVsUTrxSgmAM1Is0j4tICHoVyOVAXfWMP/kS9ZrjvyIXWV2PmomiAXXqOw==", "dev": true, "funding": [ { @@ -14708,9 +14708,9 @@ "license": "Apache-2.0" }, "node_modules/bidi-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", - "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.1.0.tgz", + "integrity": "sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==", "dev": true, "license": "MIT", "dependencies": { @@ -22691,16 +22691,16 @@ } }, "node_modules/jsdom": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-30.0.1.tgz", - "integrity": "sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA==", + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-30.1.0.tgz", + "integrity": "sha512-h/Q+H+KmlGf49ITw3TrykSVbg04S75S2O7ZtJc9gMGOWBrQRZmx5AJIKLtJ01e6Kszl+/tpFthCVqxNifxhoqg==", "dev": true, "license": "MIT", "dependencies": { - "@asamuzakjp/css-color": "^6.0.5", - "@asamuzakjp/dom-selector": "^8.3.0", + "@asamuzakjp/css-color": "^7.0.0", + "@asamuzakjp/dom-selector": "^9.1.2", "@bramus/specificity": "^2.4.2", - "@csstools/css-syntax-patches-for-csstree": "^1.1.7", + "@csstools/css-syntax-patches-for-csstree": "^1.1.13", "@exodus/bytes": "^1.15.1", "css-tree": "^3.2.1", "data-urls": "^7.0.0", @@ -22710,13 +22710,12 @@ "lru-cache": "^11.5.2", "parse5": "^8.0.1", "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.2", - "undici": "^8.9.0", + "undici": "^8.10.2", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", - "whatwg-url": "^17.1.0", + "whatwg-url": "^17.1.1", "xml-name-validator": "^5.0.0" }, "engines": { @@ -22791,9 +22790,9 @@ } }, "node_modules/jsdom/node_modules/undici": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.0.tgz", - "integrity": "sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==", + "version": "8.10.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.2.tgz", + "integrity": "sha512-/y4/bH9YNU5hi9NIrpOuvGXFcxrj3CMrV+/AYpowAYTpHn8gX/XPFjNy766FPoYY0miQhdW977JFWKGNhBdwyQ==", "dev": true, "license": "MIT", "engines": { @@ -32488,13 +32487,6 @@ "webpack": ">=2" } }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true, - "license": "MIT" - }, "node_modules/synckit": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz", @@ -35418,9 +35410,9 @@ } }, "node_modules/whatwg-url": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-17.1.0.tgz", - "integrity": "sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw==", + "version": "17.1.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-17.1.1.tgz", + "integrity": "sha512-ohjk1mdUebJVadRt3bAhQhx8lSnISq+GDttK79LFl8EHQkAPvzwctoasC4hs8tBt6kLAncBWWyq1N52qEfKvDw==", "dev": true, "license": "MIT", "dependencies": { @@ -36074,7 +36066,7 @@ "gulp-replace": "^1.1.4", "gulp-sourcemaps": "^3.0.0", "http-server": "^14.1.1", - "jsdom": "30.0.1", + "jsdom": "30.1.0", "json5": "^2.2.3", "mocha": "^12.0.0", "puppeteer-core": "^25.8.0", @@ -36088,7 +36080,7 @@ "node": ">=22" }, "peerDependencies": { - "jsdom": ">=27.4.0 <30.0.0" + "jsdom": ">=27.4.0 <31.0.0" } }, "packages/docs": { diff --git a/packages/blockly/core/dropdowndiv.ts b/packages/blockly/core/dropdowndiv.ts index 925bf9fde80..d3666e88b73 100644 --- a/packages/blockly/core/dropdowndiv.ts +++ b/packages/blockly/core/dropdowndiv.ts @@ -806,11 +806,16 @@ function positionInternal( const parentElement = div.parentElement; if (parentElement) { - const bounds = parentElement.getBoundingClientRect(); - initialX -= bounds.left + window.scrollX; - finalX -= bounds.left + window.scrollX; - initialY -= bounds.top + window.scrollY; - finalY -= bounds.top + window.scrollY; + ({x: initialX, y: initialY} = style.pageToContainerOffset( + initialX, + initialY, + parentElement, + )); + ({x: finalX, y: finalY} = style.pageToContainerOffset( + finalX, + finalY, + parentElement, + )); } // First apply initial translation. diff --git a/packages/blockly/core/field_input.ts b/packages/blockly/core/field_input.ts index 657a6f46cc2..8ebdd927058 100644 --- a/packages/blockly/core/field_input.ts +++ b/packages/blockly/core/field_input.ts @@ -30,6 +30,7 @@ import {Verbosity} from './utils/aria.js'; import * as dom from './utils/dom.js'; import {Rect} from './utils/rect.js'; import type {Size} from './utils/size.js'; +import * as style from './utils/style.js'; import {Svg} from './utils/svg.js'; import * as svgMath from './utils/svg_math.js'; import * as userAgent from './utils/useragent.js'; @@ -828,9 +829,7 @@ export abstract class FieldInput extends Field< const parentElement = div?.parentElement; if (parentElement) { - const bounds = parentElement.getBoundingClientRect(); - x -= bounds.left + window.scrollX; - y -= bounds.top + window.scrollY; + ({x, y} = style.pageToContainerOffset(x, y, parentElement)); } div.style.left = `${x}px`; diff --git a/packages/blockly/core/utils/style.ts b/packages/blockly/core/utils/style.ts index 4f8324be5c0..afac2504bed 100644 --- a/packages/blockly/core/utils/style.ts +++ b/packages/blockly/core/utils/style.ts @@ -138,6 +138,32 @@ export function getBorderBox(element: Element): Rect { return new Rect(top, bottom, left, right); } +/** + * Converts a point in page coordinates into the equivalent CSS `left`/`top` + * for an absolutely positioned child of `container`. + * + * Those properties are resolved against the container's padding box, so the + * container's border widths have to be subtracted along with its position. + * + * @param x Horizontal page coordinate. + * @param y Vertical page coordinate. + * @param container The relatively positioned parent element. + * @returns The equivalent point relative to the container's padding box. + * @internal + */ +export function pageToContainerOffset( + x: number, + y: number, + container: Element, +): Coordinate { + const bounds = container.getBoundingClientRect(); + const border = getBorderBox(container); + return new Coordinate( + x - (bounds.left + window.scrollX + border.left), + y - (bounds.top + window.scrollY + border.top), + ); +} + /** * Changes the scroll position of `container` with the minimum amount so * that the content and the borders of the given `element` become visible. diff --git a/packages/blockly/core/widgetdiv.ts b/packages/blockly/core/widgetdiv.ts index 574e8ac4805..ab894c2820f 100644 --- a/packages/blockly/core/widgetdiv.ts +++ b/packages/blockly/core/widgetdiv.ts @@ -16,6 +16,7 @@ import * as dom from './utils/dom.js'; import * as idGenerator from './utils/idgenerator.js'; import type {Rect} from './utils/rect.js'; import type {Size} from './utils/size.js'; +import * as style from './utils/style.js'; import type {WorkspaceSvg} from './workspace_svg.js'; /** The object currently using this container. */ @@ -285,9 +286,7 @@ function positionInternal(x: number, y: number, height: number) { const parentElement = containerDiv.parentElement; if (parentElement) { - const bounds = parentElement.getBoundingClientRect(); - x -= bounds.left + window.scrollX; - y -= bounds.top + window.scrollY; + ({x, y} = style.pageToContainerOffset(x, y, parentElement)); } containerDiv.style.left = x + 'px'; diff --git a/packages/blockly/package.json b/packages/blockly/package.json index fad57d2945e..7fc83930c7f 100644 --- a/packages/blockly/package.json +++ b/packages/blockly/package.json @@ -195,7 +195,7 @@ "gulp-replace": "^1.1.4", "gulp-sourcemaps": "^3.0.0", "http-server": "^14.1.1", - "jsdom": "30.0.1", + "jsdom": "30.1.0", "json5": "^2.2.3", "mocha": "^12.0.0", "puppeteer-core": "^25.8.0", @@ -209,6 +209,6 @@ "node": ">=22" }, "peerDependencies": { - "jsdom": ">=27.4.0 <30.0.0" + "jsdom": ">=27.4.0 <31.0.0" } }