diff --git a/package-lock.json b/package-lock.json index e84f86b1..6eb39c00 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@next2d/player", - "version": "3.0.3", + "version": "3.0.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@next2d/player", - "version": "3.0.3", + "version": "3.0.4", "license": "MIT", "workspaces": [ "packages/*" @@ -24,14 +24,14 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^12.3.0", "@types/node": "^25.2.3", - "@typescript-eslint/eslint-plugin": "^8.55.0", - "@typescript-eslint/parser": "^8.55.0", + "@typescript-eslint/eslint-plugin": "^8.56.0", + "@typescript-eslint/parser": "^8.56.0", "@vitest/web-worker": "^4.0.18", "@webgpu/types": "^0.1.69", "eslint": "^9.39.2", "eslint-plugin-unused-imports": "^4.4.1", "globals": "^17.3.0", - "jsdom": "^28.0.0", + "jsdom": "^28.1.0", "rollup": "^4.57.1", "tslib": "^2.8.1", "typescript": "^5.9.3", @@ -84,9 +84,9 @@ } }, "node_modules/@asamuzakjp/dom-selector": { - "version": "6.7.8", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.8.tgz", - "integrity": "sha512-stisC1nULNc9oH5lakAj8MH88ZxeGxzyWNDfbdCxvJSJIvDsHNZqYvscGTgy/ysgXWLJPt6K/4t0/GjvtKcFJQ==", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz", + "integrity": "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==", "dev": true, "license": "MIT", "dependencies": { @@ -94,7 +94,7 @@ "bidi-js": "^1.0.3", "css-tree": "^3.1.0", "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.2.5" + "lru-cache": "^11.2.6" } }, "node_modules/@asamuzakjp/nwsapi": { @@ -104,6 +104,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, "node_modules/@csstools/color-helpers": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.1.tgz", @@ -125,9 +138,9 @@ } }, "node_modules/@csstools/css-calc": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.0.1.tgz", - "integrity": "sha512-bsDKIP6f4ta2DO9t+rAbSSwv4EMESXy5ZIvzQl1afmD6Z1XHkVu9ijcG9QR/qSgQS1dVa+RaQ/MfQ7FIB/Dn1Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz", + "integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==", "dev": true, "funding": [ { @@ -823,9 +836,9 @@ } }, "node_modules/@exodus/bytes": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.12.0.tgz", - "integrity": "sha512-BuCOHA/EJdPN0qQ5MdgAiJSt9fYDHbghlgrj33gRdy/Yp1/FMCDhU6vJfcKrLC0TPWGSrfH3vYXBQWmFHxlddw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.14.1.tgz", + "integrity": "sha512-OhkBFWI6GcRMUroChZiopRiSp2iAMvEBK47NhJooDqz1RERO4QuZIZnjP63TXX8GAiLABkYmX+fuQsdJ1dd2QQ==", "dev": true, "license": "MIT", "engines": { @@ -1550,17 +1563,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.55.0.tgz", - "integrity": "sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.0.tgz", + "integrity": "sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.55.0", - "@typescript-eslint/type-utils": "8.55.0", - "@typescript-eslint/utils": "8.55.0", - "@typescript-eslint/visitor-keys": "8.55.0", + "@typescript-eslint/scope-manager": "8.56.0", + "@typescript-eslint/type-utils": "8.56.0", + "@typescript-eslint/utils": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" @@ -1573,8 +1586,8 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.55.0", - "eslint": "^8.57.0 || ^9.0.0", + "@typescript-eslint/parser": "^8.56.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, @@ -1589,16 +1602,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.55.0.tgz", - "integrity": "sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.0.tgz", + "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.55.0", - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/typescript-estree": "8.55.0", - "@typescript-eslint/visitor-keys": "8.55.0", + "@typescript-eslint/scope-manager": "8.56.0", + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0", "debug": "^4.4.3" }, "engines": { @@ -1609,19 +1622,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.55.0.tgz", - "integrity": "sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.0.tgz", + "integrity": "sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.55.0", - "@typescript-eslint/types": "^8.55.0", + "@typescript-eslint/tsconfig-utils": "^8.56.0", + "@typescript-eslint/types": "^8.56.0", "debug": "^4.4.3" }, "engines": { @@ -1636,14 +1649,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.55.0.tgz", - "integrity": "sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.0.tgz", + "integrity": "sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/visitor-keys": "8.55.0" + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1654,9 +1667,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.55.0.tgz", - "integrity": "sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.0.tgz", + "integrity": "sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg==", "dev": true, "license": "MIT", "engines": { @@ -1671,15 +1684,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.55.0.tgz", - "integrity": "sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.0.tgz", + "integrity": "sha512-qX2L3HWOU2nuDs6GzglBeuFXviDODreS58tLY/BALPC7iu3Fa+J7EOTwnX9PdNBxUI7Uh0ntP0YWGnxCkXzmfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/typescript-estree": "8.55.0", - "@typescript-eslint/utils": "8.55.0", + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0", + "@typescript-eslint/utils": "8.56.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, @@ -1691,14 +1704,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.55.0.tgz", - "integrity": "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.0.tgz", + "integrity": "sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ==", "dev": true, "license": "MIT", "engines": { @@ -1710,16 +1723,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.55.0.tgz", - "integrity": "sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.0.tgz", + "integrity": "sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.55.0", - "@typescript-eslint/tsconfig-utils": "8.55.0", - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/visitor-keys": "8.55.0", + "@typescript-eslint/project-service": "8.56.0", + "@typescript-eslint/tsconfig-utils": "8.56.0", + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", @@ -1764,16 +1777,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.55.0.tgz", - "integrity": "sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.0.tgz", + "integrity": "sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.55.0", - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/typescript-estree": "8.55.0" + "@typescript-eslint/scope-manager": "8.56.0", + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1783,19 +1796,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.55.0.tgz", - "integrity": "sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.0.tgz", + "integrity": "sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.55.0", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.56.0", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1806,13 +1819,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.0.tgz", + "integrity": "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" @@ -2195,16 +2208,16 @@ "license": "MIT" }, "node_modules/cssstyle": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.7.tgz", - "integrity": "sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-6.0.1.tgz", + "integrity": "sha512-IoJs7La+oFp/AB033wBStxNOJt4+9hHMxsXUPANcoXL2b3W4DZKghlJ2cI/eyeRZIQ9ysvYEorVhjrcYctWbog==", "dev": true, "license": "MIT", "dependencies": { - "@asamuzakjp/css-color": "^4.1.1", - "@csstools/css-syntax-patches-for-csstree": "^1.0.21", + "@asamuzakjp/css-color": "^4.1.2", + "@csstools/css-syntax-patches-for-csstree": "^1.0.26", "css-tree": "^3.1.0", - "lru-cache": "^11.2.4" + "lru-cache": "^11.2.5" }, "engines": { "node": ">=20" @@ -2971,16 +2984,17 @@ } }, "node_modules/jsdom": { - "version": "28.0.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-28.0.0.tgz", - "integrity": "sha512-KDYJgZ6T2TKdU8yBfYueq5EPG/EylMsBvCaenWMJb2OXmjgczzwveRCoJ+Hgj1lXPDyasvrgneSn4GBuR1hYyA==", + "version": "28.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-28.1.0.tgz", + "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", "dependencies": { "@acemir/cssom": "^0.9.31", - "@asamuzakjp/dom-selector": "^6.7.6", + "@asamuzakjp/dom-selector": "^6.8.1", + "@bramus/specificity": "^2.4.2", "@exodus/bytes": "^1.11.0", - "cssstyle": "^5.3.7", + "cssstyle": "^6.0.1", "data-urls": "^7.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^6.0.0", @@ -2991,7 +3005,7 @@ "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.0", - "undici": "^7.20.0", + "undici": "^7.21.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", @@ -3596,11 +3610,14 @@ "license": "ISC" }, "node_modules/smob": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", - "integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.6.1.tgz", + "integrity": "sha512-KAkBqZl3c2GvNgNhcoyJae1aKldDW0LO279wF9bk1PnluRTETKBq0WyzRXxEhoQLk56yHaOY4JCBEKDuJIET5g==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } }, "node_modules/source-map": { "version": "0.6.1", @@ -3850,9 +3867,9 @@ } }, "node_modules/undici": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.21.0.tgz", - "integrity": "sha512-Hn2tCQpoDt1wv23a68Ctc8Cr/BHpUSfaPYrkajTXOS9IKpxVRx/X5m1K2YkbK2ipgZgxXSgsUinl3x+2YdSSfg==", + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz", + "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 2dc2cadc..183f7694 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@next2d/player", - "version": "3.0.3", + "version": "3.0.4", "description": "Experience the fast and beautiful anti-aliased rendering of WebGL. You can create rich, interactive graphics, cross-platform applications and games without worrying about browser or device compatibility.", "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", "license": "MIT", @@ -60,14 +60,14 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^12.3.0", "@types/node": "^25.2.3", - "@typescript-eslint/eslint-plugin": "^8.55.0", - "@typescript-eslint/parser": "^8.55.0", + "@typescript-eslint/eslint-plugin": "^8.56.0", + "@typescript-eslint/parser": "^8.56.0", "@vitest/web-worker": "^4.0.18", "@webgpu/types": "^0.1.69", "eslint": "^9.39.2", "eslint-plugin-unused-imports": "^4.4.1", "globals": "^17.3.0", - "jsdom": "^28.0.0", + "jsdom": "^28.1.0", "rollup": "^4.57.1", "tslib": "^2.8.1", "typescript": "^5.9.3", diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerMouseHitUseCase.test.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerMouseHitUseCase.test.ts new file mode 100644 index 00000000..2f832ebe --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerMouseHitUseCase.test.ts @@ -0,0 +1,908 @@ +import { execute } from "./DisplayObjectContainerMouseHitUseCase"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { Shape } from "../../Shape"; +import { Sprite } from "../../Sprite"; +import { MovieClip } from "../../MovieClip"; +import { TextField } from "@next2d/text"; +import { Video } from "@next2d/media"; +import type { IPlayerHitObject } from "../../interface/IPlayerHitObject"; +import { describe, expect, it, beforeEach, vi } from "vitest"; + +// ShapeHitTestUseCase をモック +vi.mock("../../Shape/usecase/ShapeHitTestUseCase", () => ({ + execute: vi.fn(() => false) +})); + +// TextFieldHitTestUseCase をモック +vi.mock("../../TextField/usecase/TextFieldHitTestUseCase", () => ({ + execute: vi.fn(() => false) +})); + +// VideoHitTestUseCase をモック +vi.mock("../../Video/usecase/VideoHitTestUseCase", () => ({ + execute: vi.fn(() => false) +})); + +import { execute as shapeHitTestMock } from "../../Shape/usecase/ShapeHitTestUseCase"; +import { execute as textFieldHitTestMock } from "../../TextField/usecase/TextFieldHitTestUseCase"; +import { execute as videoHitTestMock } from "../../Video/usecase/VideoHitTestUseCase"; + +describe("DisplayObjectContainerMouseHitUseCase.js test", () => +{ + let canvas: HTMLCanvasElement; + let context: CanvasRenderingContext2D; + let matrix: Float32Array; + + const createHitObject = (x: number = 50, y: number = 50): IPlayerHitObject => { + return { x, y, pointer: "auto", hit: null }; + }; + + beforeEach(() => + { + canvas = document.createElement("canvas"); + canvas.width = 200; + canvas.height = 200; + context = canvas.getContext("2d") as CanvasRenderingContext2D; + matrix = new Float32Array([1, 0, 0, 1, 0, 0]); + + vi.mocked(shapeHitTestMock).mockReset().mockReturnValue(false); + vi.mocked(textFieldHitTestMock).mockReset().mockReturnValue(false); + vi.mocked(videoHitTestMock).mockReset().mockReturnValue(false); + }); + + it("子要素がない場合はfalseを返す", () => + { + const container = new DisplayObjectContainer(); + const hitObject = createHitObject(); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(false); + }); + + it("Shape子要素でヒットする場合はtrueを返す", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + container.addChild(shape); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + }); + + it("Shape子要素でヒットしない場合はfalseを返す", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(false); + + const container = new DisplayObjectContainer(); + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + container.addChild(shape); + + const hitObject = createHitObject(150, 150); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(false); + }); + + it("非表示の子要素はヒット判定から除外される", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + shape.visible = false; + container.addChild(shape); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(false); + // 非表示なのでshapeHitTestUseCaseは呼ばれない + expect(shapeHitTestMock).not.toHaveBeenCalled(); + }); + + it("isMaskがtrueの子要素はスキップされる", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + shape.isMask = true; + container.addChild(shape); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(false); + // isMaskなのでshapeHitTestUseCaseは呼ばれない + expect(shapeHitTestMock).not.toHaveBeenCalled(); + }); + + it("ネストされたDisplayObjectContainerでヒット判定を行う", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const parent = new DisplayObjectContainer(); + const child = new DisplayObjectContainer(); + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + child.addChild(shape); + parent.addChild(child); + + const hitObject = createHitObject(50, 50); + + const result = execute(parent, context, matrix, hitObject); + + expect(result).toBe(true); + }); + + it("mouseChildren=falseの場合、ヒットしてもbreakして終了する", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + container.addChild(shape); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject, false); + + expect(result).toBe(true); + // mouseChildren=falseの場合、hit判定後すぐbreakする + expect(hitObject.hit).toBe(null); + }); + + it("mouseChildrenがfalseのコンテナでは子のhit情報を設定しない", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + container.mouseChildren = false; + + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + container.addChild(shape); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + // mouseChildren=falseなのでhitは設定されない + expect(hitObject.hit).toBe(null); + }); + + it("Sprite子要素でbuttonMode+useHandCursorの場合pointerがpointerになる", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const sprite = new Sprite(); + sprite.buttonMode = true; + sprite.useHandCursor = true; + sprite.mouseEnabled = true; + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + sprite.addChild(shape); + container.addChild(sprite); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + expect(hitObject.pointer).toBe("pointer"); + }); + + it("mouseEnabled=falseの子要素ではhit_objectが更新されない", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const sprite = new Sprite(); + sprite.mouseEnabled = false; + sprite.buttonMode = true; + sprite.useHandCursor = true; + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + sprite.addChild(shape); + container.addChild(sprite); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + // mouseEnabledがfalseなのでpointerは更新されない + expect(hitObject.pointer).toBe("auto"); + }); + + it("Shapeはinteractiveではないのでhit_objectのhitに設定されない", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + container.addChild(shape); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + // Shapeはinteractive=falseなのでhitには設定されない + expect(hitObject.hit).toBe(null); + }); + + it("hitAreaが設定されたSpriteでヒット判定を行う", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const sprite = new Sprite(); + const hitAreaSprite = new Sprite(); + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + hitAreaSprite.addChild(shape); + sprite.hitArea = hitAreaSprite; + + // children.lengthが0だとfalseになるので子要素を追加 + const dummyShape = new Shape(); + dummyShape.graphics.xMin = 0; + dummyShape.graphics.yMin = 0; + dummyShape.graphics.xMax = 1; + dummyShape.graphics.yMax = 1; + sprite.addChild(dummyShape); + + const hitObject = createHitObject(50, 50); + + const result = execute(sprite, context, matrix, hitObject); + + expect(result).toBe(true); + }); + + it("hitAreaが設定されたSpriteでbuttonMode+useHandCursorの場合pointerがpointerになる", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const sprite = new Sprite(); + sprite.buttonMode = true; + sprite.useHandCursor = true; + + const hitAreaSprite = new Sprite(); + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + hitAreaSprite.addChild(shape); + sprite.hitArea = hitAreaSprite; + + const dummyShape = new Shape(); + dummyShape.graphics.xMin = 0; + dummyShape.graphics.yMin = 0; + dummyShape.graphics.xMax = 1; + dummyShape.graphics.yMax = 1; + sprite.addChild(dummyShape); + + const hitObject = createHitObject(50, 50); + + const result = execute(sprite, context, matrix, hitObject); + + expect(result).toBe(true); + expect(hitObject.pointer).toBe("pointer"); + expect(hitObject.hit).toBe(sprite); + }); + + it("hitAreaが設定されたSpriteでヒットしない場合はfalseを返す", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(false); + + const sprite = new Sprite(); + const hitAreaSprite = new Sprite(); + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 10; + shape.graphics.yMax = 10; + hitAreaSprite.addChild(shape); + sprite.hitArea = hitAreaSprite; + + const dummyShape = new Shape(); + dummyShape.graphics.xMin = 0; + dummyShape.graphics.yMin = 0; + dummyShape.graphics.xMax = 1; + dummyShape.graphics.yMax = 1; + sprite.addChild(dummyShape); + + const hitObject = createHitObject(150, 150); + + const result = execute(sprite, context, matrix, hitObject); + + expect(result).toBe(false); + }); + + it("maskがShapeの場合、マスク領域内ならヒットする", () => + { + // maskのヒットテストとtargetのヒットテスト両方true + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const maskShape = new Shape(); + maskShape.graphics.xMin = 0; + maskShape.graphics.yMin = 0; + maskShape.graphics.xMax = 100; + maskShape.graphics.yMax = 100; + container.mask = maskShape; + + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + container.addChild(shape); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + }); + + it("maskが設定されたコンテナでマスク領域外ならfalseを返す", () => + { + // maskのヒットテストはfalse + vi.mocked(shapeHitTestMock).mockReturnValue(false); + + const container = new DisplayObjectContainer(); + const maskShape = new Shape(); + maskShape.graphics.xMin = 0; + maskShape.graphics.yMin = 0; + maskShape.graphics.xMax = 10; + maskShape.graphics.yMax = 10; + container.mask = maskShape; + + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 200; + shape.graphics.yMax = 200; + container.addChild(shape); + + const hitObject = createHitObject(150, 150); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(false); + }); + + it("複数の子要素がある場合、最後に追加された要素が優先される", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + + const shape1 = new Shape(); + shape1.graphics.xMin = 0; + shape1.graphics.yMin = 0; + shape1.graphics.xMax = 50; + shape1.graphics.yMax = 50; + container.addChild(shape1); + + const sprite = new Sprite(); + sprite.buttonMode = true; + sprite.useHandCursor = true; + const shape2 = new Shape(); + shape2.graphics.xMin = 0; + shape2.graphics.yMin = 0; + shape2.graphics.xMax = 100; + shape2.graphics.yMax = 100; + sprite.addChild(shape2); + container.addChild(sprite); + + const hitObject = createHitObject(25, 25); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + // targets.pop()で後ろから取得されるのでspriteが先にチェックされる + expect(hitObject.pointer).toBe("pointer"); + expect(hitObject.hit).toBe(sprite); + }); + + it("TextField子要素でヒットする場合はtrueを返す", () => + { + vi.mocked(textFieldHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const textField = new TextField(); + container.addChild(textField); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + }); + + it("input型TextFieldの場合pointerがtextになる", () => + { + vi.mocked(textFieldHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const textField = new TextField(); + textField.type = "input"; + container.addChild(textField); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + expect(hitObject.pointer).toBe("text"); + expect(hitObject.hit).toBe(textField); + }); + + it("dynamic型TextFieldの場合pointerはautoのまま", () => + { + vi.mocked(textFieldHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const textField = new TextField(); + textField.type = "dynamic"; + container.addChild(textField); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + // isTextでinputでない場合はpointer変更なし + expect(hitObject.pointer).toBe("auto"); + }); + + it("Video子要素でヒットする場合はtrueを返す", () => + { + vi.mocked(videoHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const video = new Video(100, 100); + container.addChild(video); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + }); + + it("MovieClip子要素でヒットした場合hitにMovieClipが設定される", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const movieClip = new MovieClip(); + movieClip.buttonMode = true; + movieClip.useHandCursor = true; + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + movieClip.addChild(shape); + container.addChild(movieClip); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + // MovieClipはSpriteを継承、isSprite=trueでbuttonMode+useHandCursor=true + expect(hitObject.pointer).toBe("pointer"); + expect(hitObject.hit).toBe(movieClip); + }); + + it("MovieClip子要素でbuttonMode=falseの場合pointerはautoのまま", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const movieClip = new MovieClip(); + // buttonModeはデフォルトfalse + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + movieClip.addChild(shape); + container.addChild(movieClip); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + // isSprite=trueだがbuttonMode=falseなのでpointerは変更されない + expect(hitObject.pointer).toBe("auto"); + expect(hitObject.hit).toBe(movieClip); + }); + + it("clipDepthを持つ子要素はクリップとして扱われる", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + + // clip + const clipShape = new Shape(); + clipShape.graphics.xMin = 0; + clipShape.graphics.yMin = 0; + clipShape.graphics.xMax = 100; + clipShape.graphics.yMax = 100; + clipShape.clipDepth = 10; + container.addChild(clipShape); + + // target (clip内) + const targetShape = new Shape(); + targetShape.graphics.xMin = 0; + targetShape.graphics.yMin = 0; + targetShape.graphics.xMax = 100; + targetShape.graphics.yMax = 100; + targetShape.placeId = 5; + container.addChild(targetShape); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + }); + + it("clipDepthの範囲外の子要素はクリップされない", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + + // clip (depth:5まで) + const clipShape = new Shape(); + clipShape.graphics.xMin = 0; + clipShape.graphics.yMin = 0; + clipShape.graphics.xMax = 10; + clipShape.graphics.yMax = 10; + clipShape.clipDepth = 5; + container.addChild(clipShape); + + // clipDepthの範囲外 (placeId > clipDepth) + const targetShape = new Shape(); + targetShape.graphics.xMin = 0; + targetShape.graphics.yMin = 0; + targetShape.graphics.xMax = 100; + targetShape.graphics.yMax = 100; + targetShape.placeId = 10; + container.addChild(targetShape); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + // placeId > clipDepthなのでクリップから外れ、通常のヒット判定となる + expect(result).toBe(true); + }); + + it("maskがDisplayObjectContainerの場合も正しくヒット判定される", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const maskContainer = new DisplayObjectContainer(); + const maskShape = new Shape(); + maskShape.graphics.xMin = 0; + maskShape.graphics.yMin = 0; + maskShape.graphics.xMax = 100; + maskShape.graphics.yMax = 100; + maskContainer.addChild(maskShape); + container.mask = maskContainer; + + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + container.addChild(shape); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + }); + + it("maskがTextFieldの場合も正しくヒット判定される", () => + { + vi.mocked(textFieldHitTestMock).mockReturnValue(true); + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const maskTextField = new TextField(); + container.mask = maskTextField; + + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + container.addChild(shape); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + }); + + it("maskがVideoの場合も正しくヒット判定される", () => + { + vi.mocked(videoHitTestMock).mockReturnValue(true); + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const maskVideo = new Video(100, 100); + container.mask = maskVideo; + + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + container.addChild(shape); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + }); + + it("hit_objectのpointerが既にauto以外の場合は上書きされない", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const sprite = new Sprite(); + sprite.buttonMode = true; + sprite.useHandCursor = true; + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + sprite.addChild(shape); + container.addChild(sprite); + + const hitObject = createHitObject(50, 50); + hitObject.pointer = "text"; + + execute(container, context, matrix, hitObject); + + // pointerが既にauto以外なので上書きされない + expect(hitObject.pointer).toBe("text"); + }); + + it("hit_objectのhitが既に設定されている場合は上書きされない", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const sprite = new Sprite(); + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + sprite.addChild(shape); + container.addChild(sprite); + + const existingHit = new Sprite(); + const hitObject = createHitObject(50, 50); + hitObject.hit = existingHit; + + execute(container, context, matrix, hitObject); + + // hitが既に設定されているので上書きされない + expect(hitObject.hit).toBe(existingHit); + }); + + it("mouse_children引数のデフォルト値はtrue", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const sprite = new Sprite(); + sprite.buttonMode = true; + sprite.useHandCursor = true; + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + sprite.addChild(shape); + container.addChild(sprite); + + const hitObject = createHitObject(50, 50); + + // mouse_children引数を省略 + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(true); + expect(hitObject.pointer).toBe("pointer"); + expect(hitObject.hit).toBe(sprite); + }); + + it("全子要素が非表示の場合はfalseを返す", () => + { + const container = new DisplayObjectContainer(); + const shape1 = new Shape(); + shape1.graphics.xMin = 0; + shape1.graphics.yMin = 0; + shape1.graphics.xMax = 100; + shape1.graphics.yMax = 100; + shape1.visible = false; + container.addChild(shape1); + + const shape2 = new Shape(); + shape2.graphics.xMin = 0; + shape2.graphics.yMin = 0; + shape2.graphics.xMax = 100; + shape2.graphics.yMax = 100; + shape2.visible = false; + container.addChild(shape2); + + const hitObject = createHitObject(50, 50); + + const result = execute(container, context, matrix, hitObject); + + expect(result).toBe(false); + }); + + it("hitAreaのヒットでmouseChildren=falseの場合hitが設定されない", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const sprite = new Sprite(); + sprite.buttonMode = true; + sprite.useHandCursor = true; + sprite.mouseChildren = false; + + const hitAreaSprite = new Sprite(); + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + hitAreaSprite.addChild(shape); + sprite.hitArea = hitAreaSprite; + + const dummyShape = new Shape(); + dummyShape.graphics.xMin = 0; + dummyShape.graphics.yMin = 0; + dummyShape.graphics.xMax = 1; + dummyShape.graphics.yMax = 1; + sprite.addChild(dummyShape); + + const hitObject = createHitObject(50, 50); + + const result = execute(sprite, context, matrix, hitObject); + + expect(result).toBe(true); + // mouseChildren=falseなのでhitは設定されない + expect(hitObject.hit).toBe(null); + }); + + it("hitAreaのヒットでmouseEnabled=falseの場合hitが設定されない", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const sprite = new Sprite(); + sprite.buttonMode = true; + sprite.useHandCursor = true; + sprite.mouseEnabled = false; + + const hitAreaSprite = new Sprite(); + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + hitAreaSprite.addChild(shape); + sprite.hitArea = hitAreaSprite; + + const dummyShape = new Shape(); + dummyShape.graphics.xMin = 0; + dummyShape.graphics.yMin = 0; + dummyShape.graphics.xMax = 1; + dummyShape.graphics.yMax = 1; + sprite.addChild(dummyShape); + + const hitObject = createHitObject(50, 50); + + const result = execute(sprite, context, matrix, hitObject); + + expect(result).toBe(true); + // mouseEnabled=falseなのでhitは設定されない + expect(hitObject.hit).toBe(null); + }); + + it("Sprite子要素でuseHandCursor=falseの場合pointerはautoのまま", () => + { + vi.mocked(shapeHitTestMock).mockReturnValue(true); + + const container = new DisplayObjectContainer(); + const sprite = new Sprite(); + sprite.buttonMode = true; + sprite.useHandCursor = false; + const shape = new Shape(); + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = 100; + shape.graphics.yMax = 100; + sprite.addChild(shape); + container.addChild(sprite); + + const hitObject = createHitObject(50, 50); + + execute(container, context, matrix, hitObject); + + // buttonMode=true, useHandCursor=false → isSpriteケースに入るがcondition未達 + // pointerが変更されずautoのまま + expect(hitObject.pointer).toBe("auto"); + expect(hitObject.hit).toBe(sprite); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerMouseHitUseCase.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerMouseHitUseCase.ts index 44280114..cefb420b 100644 --- a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerMouseHitUseCase.ts +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerMouseHitUseCase.ts @@ -98,6 +98,7 @@ export const execute =

| null; + public hitArea: ISprite | null; /** * @type {SoundTransform|null} diff --git a/src/index.ts b/src/index.ts index 37751284..0d8efa32 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,7 @@ import { Next2D } from "@next2d/core"; if (!("next2d" in window)) { - console.log("%c Next2D Player %c 3.0.3 %c https://next2d.app", + console.log("%c Next2D Player %c 3.0.4 %c https://next2d.app", "color: #fff; background: #5f5f5f", "color: #fff; background: #4bc729", "");