diff options
34 files changed, 753 insertions, 1017 deletions
diff --git a/composer.json b/composer.json index afccd14e1d2f..c9656c5ddcd4 100644 --- a/composer.json +++ b/composer.json @@ -35,12 +35,12 @@ "phpstan/phpstan": "^1.12.4 || ^2.1.14", "phpstan/phpstan-phpunit": "^1.3.16 || ^2.0.6", "phpunit/phpunit": "^10.5.19 || ^11.5.3", - "symfony/browser-kit": "^7.2", - "symfony/css-selector": "^7.2", - "symfony/dom-crawler": "^7.2", - "symfony/error-handler": "^7.2", - "symfony/lock": "^7.2", - "symfony/var-dumper": "^7.2" + "symfony/browser-kit": "^7.3@beta", + "symfony/css-selector": "^7.3@beta", + "symfony/dom-crawler": "^7.3@beta", + "symfony/error-handler": "^7.3@beta", + "symfony/lock": "^7.3@beta", + "symfony/var-dumper": "^7.3@beta" }, "replace": { "symfony/polyfill-php72": "*", diff --git a/composer.lock b/composer.lock index f9484b94396e..4c80fc74acd2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dcd79681afda0e50ae02b69ee396b5d3", + "content-hash": "e359f7a4bb5e2c4520dd885d47b22963", "packages": [ { "name": "asm89/stack-cors", @@ -497,7 +497,7 @@ "dist": { "type": "path", "url": "core", - "reference": "3fe3a026102b1fb6c77111c00ec61a34a42f3645" + "reference": "3806cfdbb344a5c7ed6e8b78534454547ccd882a" }, "require": { "asm89/stack-cors": "^2.3", @@ -539,7 +539,8 @@ "symfony/http-kernel": "^7.3@beta", "symfony/mailer": "^7.3@beta", "symfony/mime": "^7.3@beta", - "symfony/polyfill-iconv": "^1.26", + "symfony/polyfill-iconv": "^1.32", + "symfony/polyfill-php84": "^1.32", "symfony/process": "^7.3@beta", "symfony/psr-http-message-bridge": "^7.3@beta", "symfony/routing": "^7.3@beta", @@ -2321,16 +2322,16 @@ }, { "name": "symfony/error-handler", - "version": "v7.2.5", + "version": "v7.3.0-BETA1", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "102be5e6a8e4f4f3eb3149bcbfa33a80d1ee374b" + "reference": "47a96276149f049ba944cbd470f4d17bf42914e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/102be5e6a8e4f4f3eb3149bcbfa33a80d1ee374b", - "reference": "102be5e6a8e4f4f3eb3149bcbfa33a80d1ee374b", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/47a96276149f049ba944cbd470f4d17bf42914e3", + "reference": "47a96276149f049ba944cbd470f4d17bf42914e3", "shasum": "" }, "require": { @@ -2343,9 +2344,11 @@ "symfony/http-kernel": "<6.4" }, "require-dev": { + "symfony/console": "^6.4|^7.0", "symfony/deprecation-contracts": "^2.5|^3", "symfony/http-kernel": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0" + "symfony/serializer": "^6.4|^7.0", + "symfony/webpack-encore-bundle": "^1.0|^2.0" }, "bin": [ "Resources/bin/patch-type-declarations" @@ -2376,7 +2379,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.2.5" + "source": "https://github.com/symfony/error-handler/tree/v7.3.0-BETA1" }, "funding": [ { @@ -2392,7 +2395,7 @@ "type": "tidelift" } ], - "time": "2025-03-03T07:12:39+00:00" + "time": "2025-03-17T19:44:19+00:00" }, { "name": "symfony/event-dispatcher", @@ -3520,6 +3523,82 @@ "time": "2024-12-23T08:48:59+00:00" }, { + "name": "symfony/polyfill-php84", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "000df7860439609837bbe28670b0be15783b7fbf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/000df7860439609837bbe28670b0be15783b7fbf", + "reference": "000df7860439609837bbe28670b0be15783b7fbf", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-02-20T12:04:08+00:00" + }, + { "name": "symfony/process", "version": "v7.3.0-BETA1", "source": { @@ -3927,16 +4006,16 @@ }, { "name": "symfony/string", - "version": "v7.2.6", + "version": "v7.3.0-BETA1", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "a214fe7d62bd4df2a76447c67c6b26e1d5e74931" + "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/a214fe7d62bd4df2a76447c67c6b26e1d5e74931", - "reference": "a214fe7d62bd4df2a76447c67c6b26e1d5e74931", + "url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125", + "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125", "shasum": "" }, "require": { @@ -3994,7 +4073,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.2.6" + "source": "https://github.com/symfony/string/tree/v7.3.0-BETA1" }, "funding": [ { @@ -4010,7 +4089,7 @@ "type": "tidelift" } ], - "time": "2025-04-20T20:18:16+00:00" + "time": "2025-04-20T20:19:01+00:00" }, { "name": "symfony/translation-contracts", @@ -4189,20 +4268,21 @@ }, { "name": "symfony/var-dumper", - "version": "v7.2.6", + "version": "v7.3.0-BETA1", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "9c46038cd4ed68952166cf7001b54eb539184ccb" + "reference": "5be5bdd07600c270083d821a4b20697a47526311" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9c46038cd4ed68952166cf7001b54eb539184ccb", - "reference": "9c46038cd4ed68952166cf7001b54eb539184ccb", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/5be5bdd07600c270083d821a4b20697a47526311", + "reference": "5be5bdd07600c270083d821a4b20697a47526311", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { @@ -4252,7 +4332,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.2.6" + "source": "https://github.com/symfony/var-dumper/tree/v7.3.0-BETA1" }, "funding": [ { @@ -4268,24 +4348,25 @@ "type": "tidelift" } ], - "time": "2025-04-09T08:14:01+00:00" + "time": "2025-04-09T08:14:14+00:00" }, { "name": "symfony/var-exporter", - "version": "v7.2.6", + "version": "v7.3.0-BETA1", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "422b8de94c738830a1e071f59ad14d67417d7007" + "reference": "6d25a2377310c85f0400797e4f07c303df00bd74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/422b8de94c738830a1e071f59ad14d67417d7007", - "reference": "422b8de94c738830a1e071f59ad14d67417d7007", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/6d25a2377310c85f0400797e4f07c303df00bd74", + "reference": "6d25a2377310c85f0400797e4f07c303df00bd74", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" }, "require-dev": { "symfony/property-access": "^6.4|^7.0", @@ -4328,7 +4409,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v7.2.6" + "source": "https://github.com/symfony/var-exporter/tree/v7.3.0-BETA1" }, "funding": [ { @@ -4344,7 +4425,7 @@ "type": "tidelift" } ], - "time": "2025-05-02T08:36:00+00:00" + "time": "2025-05-02T08:36:13+00:00" }, { "name": "symfony/yaml", @@ -9369,16 +9450,16 @@ }, { "name": "symfony/browser-kit", - "version": "v7.2.4", + "version": "v7.3.0-BETA1", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "8ce0ee23857d87d5be493abba2d52d1f9e49da61" + "reference": "5384291845e74fd7d54f3d925c4a86ce12336593" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/8ce0ee23857d87d5be493abba2d52d1f9e49da61", - "reference": "8ce0ee23857d87d5be493abba2d52d1f9e49da61", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/5384291845e74fd7d54f3d925c4a86ce12336593", + "reference": "5384291845e74fd7d54f3d925c4a86ce12336593", "shasum": "" }, "require": { @@ -9417,7 +9498,7 @@ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v7.2.4" + "source": "https://github.com/symfony/browser-kit/tree/v7.3.0-BETA1" }, "funding": [ { @@ -9433,11 +9514,11 @@ "type": "tidelift" } ], - "time": "2025-02-14T14:27:24+00:00" + "time": "2025-03-05T10:15:41+00:00" }, { "name": "symfony/css-selector", - "version": "v7.2.0", + "version": "v7.3.0-BETA1", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -9482,7 +9563,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.2.0" + "source": "https://github.com/symfony/css-selector/tree/v7.3.0-BETA1" }, "funding": [ { @@ -9502,16 +9583,16 @@ }, { "name": "symfony/dom-crawler", - "version": "v7.2.4", + "version": "v7.3.0-BETA1", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "19cc7b08efe9ad1ab1b56e0948e8d02e15ed3ef7" + "reference": "0fabbc3d6a9c473b716a93fc8e7a537adb396166" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/19cc7b08efe9ad1ab1b56e0948e8d02e15ed3ef7", - "reference": "19cc7b08efe9ad1ab1b56e0948e8d02e15ed3ef7", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/0fabbc3d6a9c473b716a93fc8e7a537adb396166", + "reference": "0fabbc3d6a9c473b716a93fc8e7a537adb396166", "shasum": "" }, "require": { @@ -9549,7 +9630,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v7.2.4" + "source": "https://github.com/symfony/dom-crawler/tree/v7.3.0-BETA1" }, "funding": [ { @@ -9565,20 +9646,20 @@ "type": "tidelift" } ], - "time": "2025-02-17T15:53:07+00:00" + "time": "2025-03-05T10:15:41+00:00" }, { "name": "symfony/lock", - "version": "v7.2.6", + "version": "v7.3.0-BETA1", "source": { "type": "git", "url": "https://github.com/symfony/lock.git", - "reference": "69599a1d602a6c66fc69cdf733839480d01a06be" + "reference": "5bef45fb874b0454a616ac8091447a7982a438cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/lock/zipball/69599a1d602a6c66fc69cdf733839480d01a06be", - "reference": "69599a1d602a6c66fc69cdf733839480d01a06be", + "url": "https://api.github.com/repos/symfony/lock/zipball/5bef45fb874b0454a616ac8091447a7982a438cf", + "reference": "5bef45fb874b0454a616ac8091447a7982a438cf", "shasum": "" }, "require": { @@ -9627,7 +9708,7 @@ "semaphore" ], "support": { - "source": "https://github.com/symfony/lock/tree/v7.2.6" + "source": "https://github.com/symfony/lock/tree/v7.3.0-BETA1" }, "funding": [ { @@ -9643,7 +9724,7 @@ "type": "tidelift" } ], - "time": "2025-04-17T22:02:25+00:00" + "time": "2025-04-20T20:19:01+00:00" }, { "name": "tbachert/spi", @@ -9858,12 +9939,18 @@ "drupal/core": 20, "drupal/core-project-message": 20, "drupal/core-recipe-unpack": 20, - "drupal/core-vendor-hardening": 20 + "drupal/core-vendor-hardening": 20, + "symfony/browser-kit": 10, + "symfony/css-selector": 10, + "symfony/dom-crawler": 10, + "symfony/error-handler": 10, + "symfony/lock": 10, + "symfony/var-dumper": 10 }, "prefer-stable": true, "prefer-lowest": false, - "platform": [], - "platform-dev": [], + "platform": {}, + "platform-dev": {}, "platform-overrides": { "php": "8.3.0" }, diff --git a/composer/Metapackage/CoreRecommended/composer.json b/composer/Metapackage/CoreRecommended/composer.json index bc11c39d9b80..48e38f50d342 100644 --- a/composer/Metapackage/CoreRecommended/composer.json +++ b/composer/Metapackage/CoreRecommended/composer.json @@ -36,7 +36,7 @@ "symfony/console": "~v7.3.0-BETA1", "symfony/dependency-injection": "~v7.3.0-BETA1", "symfony/deprecation-contracts": "~v3.5.1", - "symfony/error-handler": "~v7.2.5", + "symfony/error-handler": "~v7.3.0-BETA1", "symfony/event-dispatcher": "~v7.3.0-BETA1", "symfony/event-dispatcher-contracts": "~v3.5.1", "symfony/filesystem": "~v7.3.0-BETA1", @@ -51,16 +51,17 @@ "symfony/polyfill-intl-idn": "~v1.32.0", "symfony/polyfill-intl-normalizer": "~v1.32.0", "symfony/polyfill-mbstring": "~v1.32.0", + "symfony/polyfill-php84": "~v1.32.0", "symfony/process": "~v7.3.0-BETA1", "symfony/psr-http-message-bridge": "~v7.3.0-BETA1", "symfony/routing": "~v7.3.0-BETA1", "symfony/serializer": "~v7.3.0-BETA1", "symfony/service-contracts": "~v3.5.1", - "symfony/string": "~v7.2.6", + "symfony/string": "~v7.3.0-BETA1", "symfony/translation-contracts": "~v3.5.1", "symfony/validator": "~v7.3.0-BETA1", - "symfony/var-dumper": "~v7.2.6", - "symfony/var-exporter": "~v7.2.6", + "symfony/var-dumper": "~v7.3.0-BETA1", + "symfony/var-exporter": "~v7.3.0-BETA1", "symfony/yaml": "~v7.3.0-BETA1", "twig/twig": "~v3.21.1" } diff --git a/composer/Metapackage/DevDependencies/composer.json b/composer/Metapackage/DevDependencies/composer.json index 674d8e9f7fbe..402af5480da4 100644 --- a/composer/Metapackage/DevDependencies/composer.json +++ b/composer/Metapackage/DevDependencies/composer.json @@ -26,11 +26,11 @@ "phpstan/phpstan": "^1.12.4 || ^2.1.14", "phpstan/phpstan-phpunit": "^1.3.16 || ^2.0.6", "phpunit/phpunit": "^10.5.19 || ^11.5.3", - "symfony/browser-kit": "^7.2", - "symfony/css-selector": "^7.2", - "symfony/dom-crawler": "^7.2", - "symfony/error-handler": "^7.2", - "symfony/lock": "^7.2", - "symfony/var-dumper": "^7.2" + "symfony/browser-kit": "^7.3@beta", + "symfony/css-selector": "^7.3@beta", + "symfony/dom-crawler": "^7.3@beta", + "symfony/error-handler": "^7.3@beta", + "symfony/lock": "^7.3@beta", + "symfony/var-dumper": "^7.3@beta" } } diff --git a/composer/Metapackage/PinnedDevDependencies/composer.json b/composer/Metapackage/PinnedDevDependencies/composer.json index 5518fa7cbafd..992139dea514 100644 --- a/composer/Metapackage/PinnedDevDependencies/composer.json +++ b/composer/Metapackage/PinnedDevDependencies/composer.json @@ -83,10 +83,10 @@ "sirbrillig/phpcs-variable-analysis": "v2.12.0", "slevomat/coding-standard": "8.18.0", "squizlabs/php_codesniffer": "3.12.2", - "symfony/browser-kit": "v7.2.4", - "symfony/css-selector": "v7.2.0", - "symfony/dom-crawler": "v7.2.4", - "symfony/lock": "v7.2.6", + "symfony/browser-kit": "v7.3.0-BETA1", + "symfony/css-selector": "v7.3.0-BETA1", + "symfony/dom-crawler": "v7.3.0-BETA1", + "symfony/lock": "v7.3.0-BETA1", "tbachert/spi": "v1.0.3", "theseer/tokenizer": "1.2.3", "webflo/drupal-finder": "1.3.1", diff --git a/core/.phpstan-baseline.php b/core/.phpstan-baseline.php index 551b1b4c9f18..6d5e0db73d98 100644 --- a/core/.phpstan-baseline.php +++ b/core/.phpstan-baseline.php @@ -15836,12 +15836,6 @@ $ignoreErrors[] = [ 'path' => __DIR__ . '/modules/contextual/tests/src/FunctionalJavascript/ContextualLinksTest.php', ]; $ignoreErrors[] = [ - 'message' => '#^Variable \\$unrestricted_tab_count might not be defined\\.$#', - 'identifier' => 'variable.undefined', - 'count' => 1, - 'path' => __DIR__ . '/modules/contextual/tests/src/FunctionalJavascript/EditModeTest.php', -]; -$ignoreErrors[] = [ 'message' => '#^Method Drupal\\\\datetime\\\\DateTimeComputed\\:\\:setValue\\(\\) has no return type specified\\.$#', 'identifier' => 'missingType.return', 'count' => 1, diff --git a/core/composer.json b/core/composer.json index 59bca714e5b9..af9d9ca3162d 100644 --- a/core/composer.json +++ b/core/composer.json @@ -32,7 +32,8 @@ "symfony/serializer": "^7.3@beta", "symfony/validator": "^7.3@beta", "symfony/process": "^7.3@beta", - "symfony/polyfill-iconv": "^1.26", + "symfony/polyfill-iconv": "^1.32", + "symfony/polyfill-php84": "^1.32", "symfony/yaml": "^7.3@beta", "revolt/event-loop": "^1.0", "twig/twig": "^3.21.0", diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php index 63514b60eff8..9b6dc8765017 100644 --- a/core/lib/Drupal.php +++ b/core/lib/Drupal.php @@ -76,7 +76,7 @@ class Drupal { /** * The current system version. */ - const VERSION = '11.2-dev'; + const VERSION = '11.3-dev'; /** * Core API compatibility. diff --git a/core/modules/contextual/contextual.libraries.yml b/core/modules/contextual/contextual.libraries.yml index bfc1c996c98f..6798d02d9070 100644 --- a/core/modules/contextual/contextual.libraries.yml +++ b/core/modules/contextual/contextual.libraries.yml @@ -1,16 +1,9 @@ drupal.contextual-links: version: VERSION js: + js/contextualModelView.js: {} # Ensure to run before contextual/drupal.context-toolbar. - # Core. js/contextual.js: { weight: -2 } - # Models. - js/models/StateModel.js: { weight: -2 } - # Views. - js/views/AuralView.js: { weight: -2 } - js/views/KeyboardView.js: { weight: -2 } - js/views/RegionView.js: { weight: -2 } - js/views/VisualView.js: { weight: -2 } css: component: css/contextual.module.css: {} @@ -22,28 +15,21 @@ drupal.contextual-links: - core/drupal - core/drupal.ajax - core/drupalSettings - # @todo Remove this in https://www.drupal.org/project/drupal/issues/3203920 - - core/internal.backbone - core/once - core/drupal.touchevents-test drupal.contextual-toolbar: version: VERSION js: + js/toolbar/contextualToolbarModelView.js: {} js/contextual.toolbar.js: {} - # Models. - js/toolbar/models/StateModel.js: {} - # Views. - js/toolbar/views/AuralView.js: {} - js/toolbar/views/VisualView.js: {} css: component: css/contextual.toolbar.css: {} dependencies: - core/jquery + - contextual/drupal.contextual-links - core/drupal - # @todo Remove this in https://www.drupal.org/project/drupal/issues/3203920 - - core/internal.backbone - core/once - core/drupal.tabbingmanager - core/drupal.announce diff --git a/core/modules/contextual/css/contextual.theme.css b/core/modules/contextual/css/contextual.theme.css index 06a6728be396..55a83d5ca12a 100644 --- a/core/modules/contextual/css/contextual.theme.css +++ b/core/modules/contextual/css/contextual.theme.css @@ -17,6 +17,10 @@ left: 0; } +.contextual.open { + z-index: 501; +} + /** * Contextual region. */ diff --git a/core/modules/contextual/js/contextual.js b/core/modules/contextual/js/contextual.js index 87ccaa52dffe..f1008eabe07b 100644 --- a/core/modules/contextual/js/contextual.js +++ b/core/modules/contextual/js/contextual.js @@ -3,7 +3,7 @@ * Attaches behaviors for the Contextual module. */ -(function ($, Drupal, drupalSettings, _, Backbone, JSON, storage) { +(function ($, Drupal, drupalSettings, JSON, storage) { const options = $.extend( drupalSettings.contextual, // Merge strings on top of drupalSettings so that they are not mutable. @@ -14,22 +14,19 @@ }, }, ); - // Clear the cached contextual links whenever the current user's set of // permissions changes. const cachedPermissionsHash = storage.getItem( 'Drupal.contextual.permissionsHash', ); - const permissionsHash = drupalSettings.user.permissionsHash; + const { permissionsHash } = drupalSettings.user; if (cachedPermissionsHash !== permissionsHash) { if (typeof permissionsHash === 'string') { - _.chain(storage) - .keys() - .each((key) => { - if (key.startsWith('Drupal.contextual.')) { - storage.removeItem(key); - } - }); + Object.keys(storage).forEach((key) => { + if (key.startsWith('Drupal.contextual.')) { + storage.removeItem(key); + } + }); } storage.setItem('Drupal.contextual.permissionsHash', permissionsHash); } @@ -87,7 +84,7 @@ */ function initContextual($contextual, html) { const $region = $contextual.closest('.contextual-region'); - const contextual = Drupal.contextual; + const { contextual } = Drupal; $contextual // Update the placeholder to contain its rendered contextual links. @@ -107,46 +104,18 @@ const glue = url.includes('?') ? '&' : '?'; this.setAttribute('href', url + glue + destination); }); - let title = ''; const $regionHeading = $region.find('h2'); if ($regionHeading.length) { title = $regionHeading[0].textContent.trim(); } - // Create a model and the appropriate views. - const model = new contextual.StateModel({ - title, - }); - const viewOptions = $.extend({ el: $contextual, model }, options); - contextual.views.push({ - visual: new contextual.VisualView(viewOptions), - aural: new contextual.AuralView(viewOptions), - keyboard: new contextual.KeyboardView(viewOptions), - }); - contextual.regionViews.push( - new contextual.RegionView($.extend({ el: $region, model }, options)), - ); - - // Add the model to the collection. This must happen after the views have - // been associated with it, otherwise collection change event handlers can't - // trigger the model change event handler in its views. - contextual.collection.add(model); - - // Let other JavaScript react to the adding of a new contextual link. - $(document).trigger( - 'drupalContextualLinkAdded', - Drupal.deprecatedProperty({ - target: { - $el: $contextual, - $region, - model, - }, - deprecatedProperty: 'model', - message: - 'The model property is deprecated in drupal:9.4.0 and is removed from drupal:12.0.0. There is no replacement.', - }), + options.title = title; + const contextualModelView = new Drupal.contextual.ContextualModelView( + $contextual, + $region, + options, ); - + contextual.instances.push(contextualModelView); // Fix visual collisions between contextual link triggers. adjustIfNestedAndOverlapping($contextual); } @@ -192,7 +161,7 @@ // Initialize after the current execution cycle, to make the AJAX // request for retrieving the uncached contextual links as soon as // possible, but also to ensure that other Drupal behaviors have had - // the chance to set up an event listener on the Backbone collection + // the chance to set up an event listener on the collection // Drupal.contextual.collection. window.setTimeout(() => { initContextual( @@ -217,7 +186,7 @@ data: { 'ids[]': uncachedIDs, 'tokens[]': uncachedTokens }, dataType: 'json', success(results) { - _.each(results, (html, contextualID) => { + Object.entries(results).forEach(([contextualID, html]) => { // Store the metadata. storage.setItem(`Drupal.contextual.${contextualID}`, html); // If the rendered contextual links are empty, then the current @@ -274,21 +243,23 @@ * replacement. */ regionViews: [], + instances: new Proxy([], { + set: function set(obj, prop, value) { + obj[prop] = value; + window.dispatchEvent(new Event('contextual-instances-added')); + return true; + }, + deleteProperty(target, prop) { + if (prop in target) { + delete target[prop]; + window.dispatchEvent(new Event('contextual-instances-removed')); + } + }, + }), + ContextualModelView: {}, }; /** - * A Backbone.Collection of {@link Drupal.contextual.StateModel} instances. - * - * @type {Backbone.Collection} - * - * @deprecated in drupal:9.4.0 and is removed from drupal:12.0.0. There is no - * replacement. - */ - Drupal.contextual.collection = new Backbone.Collection([], { - model: Drupal.contextual.StateModel, - }); - - /** * A trigger is an interactive element often bound to a click handler. * * @return {string} @@ -311,12 +282,4 @@ $(document).on('drupalContextualLinkAdded', (event, data) => { Drupal.ajax.bindAjaxLinks(data.$el[0]); }); -})( - jQuery, - Drupal, - drupalSettings, - _, - Backbone, - window.JSON, - window.sessionStorage, -); +})(jQuery, Drupal, drupalSettings, window.JSON, window.sessionStorage); diff --git a/core/modules/contextual/js/contextual.toolbar.js b/core/modules/contextual/js/contextual.toolbar.js index 8fc206cc2c3b..c94d0df414c9 100644 --- a/core/modules/contextual/js/contextual.toolbar.js +++ b/core/modules/contextual/js/contextual.toolbar.js @@ -3,7 +3,7 @@ * Attaches behaviors for the Contextual module's edit toolbar tab. */ -(function ($, Drupal, Backbone) { +(function ($, Drupal) { const strings = { tabbingReleased: Drupal.t( 'Tabbing is no longer constrained by the Contextual module.', @@ -21,33 +21,19 @@ * A contextual links DOM element as rendered by the server. */ function initContextualToolbar(context) { - if (!Drupal.contextual || !Drupal.contextual.collection) { + if (!Drupal.contextual || !Drupal.contextual.instances) { return; } - const contextualToolbar = Drupal.contextualToolbar; - contextualToolbar.model = new contextualToolbar.StateModel( - { - // Checks whether localStorage indicates we should start in edit mode - // rather than view mode. - // @see Drupal.contextualToolbar.VisualView.persist - isViewing: - document.querySelector('body .contextual-region') === null || - localStorage.getItem('Drupal.contextualToolbar.isViewing') !== - 'false', - }, - { - contextualCollection: Drupal.contextual.collection, - }, - ); + const { contextualToolbar } = Drupal; const viewOptions = { el: $('.toolbar .toolbar-bar .contextual-toolbar-tab'), - model: contextualToolbar.model, strings, }; - new contextualToolbar.VisualView(viewOptions); - new contextualToolbar.AuralView(viewOptions); + contextualToolbar.model = new Drupal.contextual.ContextualToolbarModelView( + viewOptions, + ); } /** @@ -75,13 +61,10 @@ */ Drupal.contextualToolbar = { /** - * The {@link Drupal.contextualToolbar.StateModel} instance. - * - * @type {?Drupal.contextualToolbar.StateModel} + * The {@link Drupal.contextual.ContextualToolbarModelView} instance. * - * @deprecated in drupal:9.4.0 and is removed from drupal:12.0.0. There is - * no replacement. + * @type {?Drupal.contextual.ContextualToolbarModelView} */ model: null, }; -})(jQuery, Drupal, Backbone); +})(jQuery, Drupal); diff --git a/core/modules/contextual/js/contextualModelView.js b/core/modules/contextual/js/contextualModelView.js new file mode 100644 index 000000000000..4488045e2236 --- /dev/null +++ b/core/modules/contextual/js/contextualModelView.js @@ -0,0 +1,254 @@ +(($, Drupal) => { + /** + * Models the state of a contextual link's trigger, list & region. + */ + Drupal.contextual.ContextualModelView = class { + constructor($contextual, $region, options) { + this.title = options.title || ''; + this.regionIsHovered = false; + this._hasFocus = false; + this._isOpen = false; + this._isLocked = false; + this.strings = options.strings; + this.timer = NaN; + this.modelId = btoa(Math.random()).substring(0, 12); + this.$region = $region; + this.$contextual = $contextual; + + if (!document.body.classList.contains('touchevents')) { + this.$region.on({ + mouseenter: () => { + this.regionIsHovered = true; + }, + mouseleave: () => { + this.close().blur(); + this.regionIsHovered = false; + }, + 'mouseleave mouseenter': () => this.render(), + }); + this.$contextual.on('mouseenter', () => { + this.focus(); + this.render(); + }); + } + + this.$contextual.on( + { + click: () => { + this.toggleOpen(); + }, + touchend: () => { + Drupal.contextual.ContextualModelView.touchEndToClick(); + }, + focus: () => { + this.focus(); + }, + blur: () => { + this.blur(); + }, + 'click blur touchend focus': () => this.render(), + }, + '.trigger', + ); + + this.$contextual.on( + { + click: () => { + this.close().blur(); + }, + touchend: (event) => { + Drupal.contextual.ContextualModelView.touchEndToClick(event); + }, + focus: () => { + this.focus(); + }, + blur: () => { + this.waitCloseThenBlur(); + }, + 'click blur touchend focus': () => this.render(), + }, + '.contextual-links a', + ); + + this.render(); + + // Let other JavaScript react to the adding of a new contextual link. + $(document).trigger('drupalContextualLinkAdded', { + $el: $contextual, + $region, + model: this, + }); + } + + /** + * Updates the rendered representation of the current contextual links. + */ + render() { + const { isOpen } = this; + const isVisible = this.isLocked || this.regionIsHovered || isOpen; + this.$region.toggleClass('focus', this.hasFocus); + this.$contextual + .toggleClass('open', isOpen) + // Update the visibility of the trigger. + .find('.trigger') + .toggleClass('visually-hidden', !isVisible); + + this.$contextual.find('.contextual-links').prop('hidden', !isOpen); + const trigger = this.$contextual.find('.trigger').get(0); + trigger.textContent = Drupal.t('@action @title configuration options', { + '@action': !isOpen ? this.strings.open : this.strings.close, + '@title': this.title, + }); + trigger.setAttribute('aria-pressed', isOpen); + } + + /** + * Prevents delay and simulated mouse events. + * + * @param {jQuery.Event} event the touch end event. + */ + static touchEndToClick(event) { + event.preventDefault(); + event.target.click(); + } + + /** + * Set up a timeout to allow a user to tab between the trigger and the + * contextual links without the menu dismissing. + */ + waitCloseThenBlur() { + this.timer = window.setTimeout(() => { + this.isOpen = false; + this.hasFocus = false; + this.render(); + }, 150); + } + + /** + * Opens or closes the contextual link. + * + * If it is opened, then also give focus. + * + * @return {Drupal.contextual.ContextualModelView} + * The current contextual model view. + */ + toggleOpen() { + const newIsOpen = !this.isOpen; + this.isOpen = newIsOpen; + if (newIsOpen) { + this.focus(); + } + return this; + } + + /** + * Gives focus to this contextual link. + * + * Also closes + removes focus from every other contextual link. + * + * @return {Drupal.contextual.ContextualModelView} + * The current contextual model view. + */ + focus() { + const { modelId } = this; + Drupal.contextual.instances.forEach((model) => { + if (model.modelId !== modelId) { + model.close().blur(); + } + }); + window.clearTimeout(this.timer); + this.hasFocus = true; + return this; + } + + /** + * Removes focus from this contextual link, unless it is open. + * + * @return {Drupal.contextual.ContextualModelView} + * The current contextual model view. + */ + blur() { + if (!this.isOpen) { + this.hasFocus = false; + } + return this; + } + + /** + * Closes this contextual link. + * + * Does not call blur() because we want to allow a contextual link to have + * focus, yet be closed for example when hovering. + * + * @return {Drupal.contextual.ContextualModelView} + * The current contextual model view. + */ + close() { + this.isOpen = false; + return this; + } + + /** + * Gets the current focus state. + * + * @return {boolean} the focus state. + */ + get hasFocus() { + return this._hasFocus; + } + + /** + * Sets the current focus state. + * + * @param {boolean} value - new focus state + */ + set hasFocus(value) { + this._hasFocus = value; + this.$region.toggleClass('focus', this._hasFocus); + } + + /** + * Gets the current open state. + * + * @return {boolean} the open state. + */ + get isOpen() { + return this._isOpen; + } + + /** + * Sets the current open state. + * + * @param {boolean} value - new open state + */ + set isOpen(value) { + this._isOpen = value; + // Nested contextual region handling: hide any nested contextual triggers. + this.$region + .closest('.contextual-region') + .find('.contextual .trigger:not(:first)') + .toggle(!this.isOpen); + } + + /** + * Gets the current locked state. + * + * @return {boolean} the locked state. + */ + get isLocked() { + return this._isLocked; + } + + /** + * Sets the current locked state. + * + * @param {boolean} value - new locked state + */ + set isLocked(value) { + if (value !== this._isLocked) { + this._isLocked = value; + this.render(); + } + } + }; +})(jQuery, Drupal); diff --git a/core/modules/contextual/js/models/StateModel.js b/core/modules/contextual/js/models/StateModel.js deleted file mode 100644 index 622f897917f5..000000000000 --- a/core/modules/contextual/js/models/StateModel.js +++ /dev/null @@ -1,130 +0,0 @@ -/** - * @file - * A Backbone Model for the state of a contextual link's trigger, list & region. - */ - -(function (Drupal, Backbone) { - /** - * Models the state of a contextual link's trigger, list & region. - * - * @constructor - * - * @augments Backbone.Model - * - * @deprecated in drupal:9.4.0 and is removed from drupal:12.0.0. There is no - * replacement. - */ - Drupal.contextual.StateModel = Backbone.Model.extend( - /** @lends Drupal.contextual.StateModel# */ { - /** - * @type {object} - * - * @prop {string} title - * @prop {boolean} regionIsHovered - * @prop {boolean} hasFocus - * @prop {boolean} isOpen - * @prop {boolean} isLocked - */ - defaults: /** @lends Drupal.contextual.StateModel# */ { - /** - * The title of the entity to which these contextual links apply. - * - * @type {string} - */ - title: '', - - /** - * Represents if the contextual region is being hovered. - * - * @type {boolean} - */ - regionIsHovered: false, - - /** - * Represents if the contextual trigger or options have focus. - * - * @type {boolean} - */ - hasFocus: false, - - /** - * Represents if the contextual options for an entity are available to - * be selected (i.e. whether the list of options is visible). - * - * @type {boolean} - */ - isOpen: false, - - /** - * When the model is locked, the trigger remains active. - * - * @type {boolean} - */ - isLocked: false, - }, - - /** - * Opens or closes the contextual link. - * - * If it is opened, then also give focus. - * - * @return {Drupal.contextual.StateModel} - * The current contextual state model. - */ - toggleOpen() { - const newIsOpen = !this.get('isOpen'); - this.set('isOpen', newIsOpen); - if (newIsOpen) { - this.focus(); - } - return this; - }, - - /** - * Closes this contextual link. - * - * Does not call blur() because we want to allow a contextual link to have - * focus, yet be closed for example when hovering. - * - * @return {Drupal.contextual.StateModel} - * The current contextual state model. - */ - close() { - this.set('isOpen', false); - return this; - }, - - /** - * Gives focus to this contextual link. - * - * Also closes + removes focus from every other contextual link. - * - * @return {Drupal.contextual.StateModel} - * The current contextual state model. - */ - focus() { - this.set('hasFocus', true); - const cid = this.cid; - this.collection.each((model) => { - if (model.cid !== cid) { - model.close().blur(); - } - }); - return this; - }, - - /** - * Removes focus from this contextual link, unless it is open. - * - * @return {Drupal.contextual.StateModel} - * The current contextual state model. - */ - blur() { - if (!this.get('isOpen')) { - this.set('hasFocus', false); - } - return this; - }, - }, - ); -})(Drupal, Backbone); diff --git a/core/modules/contextual/js/toolbar/contextualToolbarModelView.js b/core/modules/contextual/js/toolbar/contextualToolbarModelView.js new file mode 100644 index 000000000000..6c6db5fe70cd --- /dev/null +++ b/core/modules/contextual/js/toolbar/contextualToolbarModelView.js @@ -0,0 +1,175 @@ +(($, Drupal) => { + Drupal.contextual.ContextualToolbarModelView = class { + constructor(options) { + this.strings = options.strings; + this.isVisible = false; + this._contextualCount = Drupal.contextual.instances.count; + this.tabbingContext = null; + this._isViewing = + localStorage.getItem('Drupal.contextualToolbar.isViewing') !== 'false'; + this.$el = options.el; + + window.addEventListener('contextual-instances-added', () => + this.lockNewContextualLinks(), + ); + window.addEventListener('contextual-instances-removed', () => { + this.contextualCount = Drupal.contextual.instances.count; + }); + + this.$el.on({ + click: () => { + this.isViewing = !this.isViewing; + }, + touchend: (event) => { + event.preventDefault(); + event.target.click(); + }, + 'click touchend': () => this.render(), + }); + + $(document).on('keyup', (event) => this.onKeypress(event)); + this.manageTabbing(true); + this.render(); + } + + /** + * Responds to esc and tab key press events. + * + * @param {jQuery.Event} event + * The keypress event. + */ + onKeypress(event) { + // The first tab key press is tracked so that an announcement about + // tabbing constraints can be raised if edit mode is enabled when the page + // is loaded. + if (!this.announcedOnce && event.keyCode === 9 && !this.isViewing) { + this.announceTabbingConstraint(); + // Set announce to true so that this conditional block won't run again. + this.announcedOnce = true; + } + // Respond to the ESC key. Exit out of edit mode. + if (event.keyCode === 27) { + this.isViewing = true; + } + } + + /** + * Updates the rendered representation of the current toolbar model view. + */ + render() { + this.$el[0].classList.toggle('hidden', this.isVisible); + const button = this.$el[0].querySelector('button'); + button.classList.toggle('is-active', !this.isViewing); + button.setAttribute('aria-pressed', !this.isViewing); + this.contextualCount = Drupal.contextual.instances.count; + } + + /** + * Automatically updates visibility of the view/edit mode toggle. + */ + updateVisibility() { + this.isVisible = this.get('contextualCount') > 0; + } + + /** + * Lock newly added contextual links if edit mode is enabled. + */ + lockNewContextualLinks() { + Drupal.contextual.instances.forEach((model) => { + model.isLocked = !this.isViewing; + }); + this.contextualCount = Drupal.contextual.instances.count; + } + + /** + * Limits tabbing to the contextual links and edit mode toolbar tab. + * + * @param {boolean} init - true to initialize tabbing. + */ + manageTabbing(init = false) { + let { tabbingContext } = this; + // Always release an existing tabbing context. + if (tabbingContext && !init) { + // Only announce release when the context was active. + if (tabbingContext.active) { + Drupal.announce(this.strings.tabbingReleased); + } + tabbingContext.release(); + this.tabbingContext = null; + } + // Create a new tabbing context when edit mode is enabled. + if (!this.isViewing) { + tabbingContext = Drupal.tabbingManager.constrain( + $('.contextual-toolbar-tab, .contextual'), + ); + this.tabbingContext = tabbingContext; + this.announceTabbingConstraint(); + this.announcedOnce = true; + } + } + + /** + * Announces the current tabbing constraint. + */ + announceTabbingConstraint() { + const { strings } = this; + Drupal.announce( + Drupal.formatString(strings.tabbingConstrained, { + '@contextualsCount': Drupal.formatPlural( + Drupal.contextual.instances.length, + '@count contextual link', + '@count contextual links', + ), + }) + strings.pressEsc, + ); + } + + /** + * Gets the current viewing state. + * + * @return {boolean} the viewing state. + */ + get isViewing() { + return this._isViewing; + } + + /** + * Sets the current viewing state. + * + * @param {boolean} value - new viewing state + */ + set isViewing(value) { + this._isViewing = value; + localStorage[!value ? 'setItem' : 'removeItem']( + 'Drupal.contextualToolbar.isViewing', + 'false', + ); + + Drupal.contextual.instances.forEach((model) => { + model.isLocked = !this.isViewing; + }); + this.manageTabbing(); + } + + /** + * Gets the current contextual links count. + * + * @return {integer} the current contextual links count. + */ + get contextualCount() { + return this._contextualCount; + } + + /** + * Sets the current contextual links count. + * + * @param {integer} value - new contextual links count. + */ + set contextualCount(value) { + if (value !== this._contextualCount) { + this._contextualCount = value; + this.updateVisibility(); + } + } + }; +})(jQuery, Drupal); diff --git a/core/modules/contextual/js/toolbar/models/StateModel.js b/core/modules/contextual/js/toolbar/models/StateModel.js deleted file mode 100644 index 88f66193f9f3..000000000000 --- a/core/modules/contextual/js/toolbar/models/StateModel.js +++ /dev/null @@ -1,126 +0,0 @@ -/** - * @file - * A Backbone Model for the state of Contextual module's edit toolbar tab. - */ - -(function (Drupal, Backbone) { - /** - * @deprecated in drupal:9.4.0 and is removed from drupal:12.0.0. There is no - * replacement. - */ - Drupal.contextualToolbar.StateModel = Backbone.Model.extend( - /** @lends Drupal.contextualToolbar.StateModel# */ { - /** - * @type {object} - * - * @prop {boolean} isViewing - * @prop {boolean} isVisible - * @prop {number} contextualCount - * @prop {Drupal~TabbingContext} tabbingContext - */ - defaults: /** @lends Drupal.contextualToolbar.StateModel# */ { - /** - * Indicates whether the toggle is currently in "view" or "edit" mode. - * - * @type {boolean} - */ - isViewing: true, - - /** - * Indicates whether the toggle should be visible or hidden. Automatically - * calculated, depends on contextualCount. - * - * @type {boolean} - */ - isVisible: false, - - /** - * Tracks how many contextual links exist on the page. - * - * @type {number} - */ - contextualCount: 0, - - /** - * A TabbingContext object as returned by {@link Drupal~TabbingManager}: - * the set of tabbable elements when edit mode is enabled. - * - * @type {?Drupal~TabbingContext} - */ - tabbingContext: null, - }, - - /** - * Models the state of the edit mode toggle. - * - * @constructs - * - * @augments Backbone.Model - * - * @param {object} attrs - * Attributes for the backbone model. - * @param {object} options - * An object with the following option: - * @param {Backbone.collection} options.contextualCollection - * The collection of {@link Drupal.contextual.StateModel} models that - * represent the contextual links on the page. - */ - initialize(attrs, options) { - // Respond to new/removed contextual links. - this.listenTo( - options.contextualCollection, - 'reset remove add', - this.countContextualLinks, - ); - this.listenTo( - options.contextualCollection, - 'add', - this.lockNewContextualLinks, - ); - - // Automatically determine visibility. - this.listenTo(this, 'change:contextualCount', this.updateVisibility); - - // Whenever edit mode is toggled, lock all contextual links. - this.listenTo(this, 'change:isViewing', (model, isViewing) => { - options.contextualCollection.each((contextualModel) => { - contextualModel.set('isLocked', !isViewing); - }); - }); - }, - - /** - * Tracks the number of contextual link models in the collection. - * - * @param {Drupal.contextual.StateModel} contextualModel - * The contextual links model that was added or removed. - * @param {Backbone.Collection} contextualCollection - * The collection of contextual link models. - */ - countContextualLinks(contextualModel, contextualCollection) { - this.set('contextualCount', contextualCollection.length); - }, - - /** - * Lock newly added contextual links if edit mode is enabled. - * - * @param {Drupal.contextual.StateModel} contextualModel - * The contextual links model that was added. - * @param {Backbone.Collection} [contextualCollection] - * The collection of contextual link models. - */ - lockNewContextualLinks(contextualModel, contextualCollection) { - if (!this.get('isViewing')) { - contextualModel.set('isLocked', true); - } - }, - - /** - * Automatically updates visibility of the view/edit mode toggle. - */ - updateVisibility() { - this.set('isVisible', this.get('contextualCount') > 0); - }, - }, - ); -})(Drupal, Backbone); diff --git a/core/modules/contextual/js/toolbar/views/AuralView.js b/core/modules/contextual/js/toolbar/views/AuralView.js deleted file mode 100644 index 2bcf9cdcca0f..000000000000 --- a/core/modules/contextual/js/toolbar/views/AuralView.js +++ /dev/null @@ -1,122 +0,0 @@ -/** - * @file - * A Backbone View that provides the aural view of the edit mode toggle. - */ - -(function ($, Drupal, Backbone, _) { - /** - * @deprecated in drupal:9.4.0 and is removed from drupal:12.0.0. There is no - * replacement. - */ - Drupal.contextualToolbar.AuralView = Backbone.View.extend( - /** @lends Drupal.contextualToolbar.AuralView# */ { - /** - * Tracks whether the tabbing constraint announcement has been read once. - * - * @type {boolean} - */ - announcedOnce: false, - - /** - * Renders the aural view of the edit mode toggle (screen reader support). - * - * @constructs - * - * @augments Backbone.View - * - * @param {object} options - * Options for the view. - */ - initialize(options) { - this.options = options; - - this.listenTo(this.model, 'change', this.render); - this.listenTo(this.model, 'change:isViewing', this.manageTabbing); - - $(document).on('keyup', _.bind(this.onKeypress, this)); - this.manageTabbing(); - }, - - /** - * {@inheritdoc} - * - * @return {Drupal.contextualToolbar.AuralView} - * The current contextual toolbar aural view. - */ - render() { - // Render the state. - this.$el - .find('button') - .attr('aria-pressed', !this.model.get('isViewing')); - - return this; - }, - - /** - * Limits tabbing to the contextual links and edit mode toolbar tab. - */ - manageTabbing() { - let tabbingContext = this.model.get('tabbingContext'); - // Always release an existing tabbing context. - if (tabbingContext) { - // Only announce release when the context was active. - if (tabbingContext.active) { - Drupal.announce(this.options.strings.tabbingReleased); - } - tabbingContext.release(); - } - // Create a new tabbing context when edit mode is enabled. - if (!this.model.get('isViewing')) { - tabbingContext = Drupal.tabbingManager.constrain( - $('.contextual-toolbar-tab, .contextual'), - ); - this.model.set('tabbingContext', tabbingContext); - this.announceTabbingConstraint(); - this.announcedOnce = true; - } - }, - - /** - * Announces the current tabbing constraint. - */ - announceTabbingConstraint() { - const strings = this.options.strings; - Drupal.announce( - Drupal.formatString(strings.tabbingConstrained, { - '@contextualsCount': Drupal.formatPlural( - Drupal.contextual.collection.length, - '@count contextual link', - '@count contextual links', - ), - }), - ); - Drupal.announce(strings.pressEsc); - }, - - /** - * Responds to esc and tab key press events. - * - * @param {jQuery.Event} event - * The keypress event. - */ - onKeypress(event) { - // The first tab key press is tracked so that an announcement about - // tabbing constraints can be raised if edit mode is enabled when the page - // is loaded. - if ( - !this.announcedOnce && - event.keyCode === 9 && - !this.model.get('isViewing') - ) { - this.announceTabbingConstraint(); - // Set announce to true so that this conditional block won't run again. - this.announcedOnce = true; - } - // Respond to the ESC key. Exit out of edit mode. - if (event.keyCode === 27) { - this.model.set('isViewing', true); - } - }, - }, - ); -})(jQuery, Drupal, Backbone, _); diff --git a/core/modules/contextual/js/toolbar/views/VisualView.js b/core/modules/contextual/js/toolbar/views/VisualView.js deleted file mode 100644 index 10d8dff2deaa..000000000000 --- a/core/modules/contextual/js/toolbar/views/VisualView.js +++ /dev/null @@ -1,85 +0,0 @@ -/** - * @file - * A Backbone View that provides the visual view of the edit mode toggle. - */ - -(function (Drupal, Backbone) { - /** - * @deprecated in drupal:9.4.0 and is removed from drupal:12.0.0. There is no - * replacement. - */ - Drupal.contextualToolbar.VisualView = Backbone.View.extend( - /** @lends Drupal.contextualToolbar.VisualView# */ { - /** - * Events for the Backbone view. - * - * @return {object} - * A mapping of events to be used in the view. - */ - events() { - // Prevents delay and simulated mouse events. - const touchEndToClick = function (event) { - event.preventDefault(); - event.target.click(); - }; - - return { - click() { - this.model.set('isViewing', !this.model.get('isViewing')); - }, - touchend: touchEndToClick, - }; - }, - - /** - * Renders the visual view of the edit mode toggle. - * - * Listens to mouse & touch and handles edit mode toggle interactions. - * - * @constructs - * - * @augments Backbone.View - */ - initialize() { - this.listenTo(this.model, 'change', this.render); - this.listenTo(this.model, 'change:isViewing', this.persist); - }, - - /** - * {@inheritdoc} - * - * @return {Drupal.contextualToolbar.VisualView} - * The current contextual toolbar visual view. - */ - render() { - // Render the visibility. - this.$el.toggleClass('hidden', !this.model.get('isVisible')); - // Render the state. - this.$el - .find('button') - .toggleClass('is-active', !this.model.get('isViewing')); - - return this; - }, - - /** - * Model change handler; persists the isViewing value to localStorage. - * - * `isViewing === true` is the default, so only stores in localStorage when - * it's not the default value (i.e. false). - * - * @param {Drupal.contextualToolbar.StateModel} model - * A {@link Drupal.contextualToolbar.StateModel} model. - * @param {boolean} isViewing - * The value of the isViewing attribute in the model. - */ - persist(model, isViewing) { - if (!isViewing) { - localStorage.setItem('Drupal.contextualToolbar.isViewing', 'false'); - } else { - localStorage.removeItem('Drupal.contextualToolbar.isViewing'); - } - }, - }, - ); -})(Drupal, Backbone); diff --git a/core/modules/contextual/js/views/AuralView.js b/core/modules/contextual/js/views/AuralView.js deleted file mode 100644 index 62287c1bf118..000000000000 --- a/core/modules/contextual/js/views/AuralView.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @file - * A Backbone View that provides the aural view of a contextual link. - */ - -(function (Drupal, Backbone) { - /** - * @deprecated in drupal:9.4.0 and is removed from drupal:12.0.0. There is no - * replacement. - */ - Drupal.contextual.AuralView = Backbone.View.extend( - /** @lends Drupal.contextual.AuralView# */ { - /** - * Renders the aural view of a contextual link (i.e. screen reader support). - * - * @constructs - * - * @augments Backbone.View - * - * @param {object} options - * Options for the view. - */ - initialize(options) { - this.options = options; - - this.listenTo(this.model, 'change', this.render); - - // Initial render. - this.render(); - }, - - /** - * {@inheritdoc} - */ - render() { - const isOpen = this.model.get('isOpen'); - - // Set the hidden property of the links. - this.$el.find('.contextual-links').prop('hidden', !isOpen); - - // Update the view of the trigger. - const $trigger = this.$el.find('.trigger'); - $trigger - .each((index, element) => { - element.textContent = Drupal.t( - '@action @title configuration options', - { - '@action': !isOpen - ? this.options.strings.open - : this.options.strings.close, - '@title': this.model.get('title'), - }, - ); - }) - .attr('aria-pressed', isOpen); - }, - }, - ); -})(Drupal, Backbone); diff --git a/core/modules/contextual/js/views/KeyboardView.js b/core/modules/contextual/js/views/KeyboardView.js deleted file mode 100644 index 2a3d144bea07..000000000000 --- a/core/modules/contextual/js/views/KeyboardView.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @file - * A Backbone View that provides keyboard interaction for a contextual link. - */ - -(function (Drupal, Backbone) { - /** - * @deprecated in drupal:9.4.0 and is removed from drupal:12.0.0. There is no - * replacement. - */ - Drupal.contextual.KeyboardView = Backbone.View.extend( - /** @lends Drupal.contextual.KeyboardView# */ { - /** - * @type {object} - */ - events: { - 'focus .trigger': 'focus', - 'focus .contextual-links a': 'focus', - 'blur .trigger': function () { - this.model.blur(); - }, - 'blur .contextual-links a': function () { - // Set up a timeout to allow a user to tab between the trigger and the - // contextual links without the menu dismissing. - const that = this; - this.timer = window.setTimeout(() => { - that.model.close().blur(); - }, 150); - }, - }, - - /** - * Provides keyboard interaction for a contextual link. - * - * @constructs - * - * @augments Backbone.View - */ - initialize() { - /** - * The timer is used to create a delay before dismissing the contextual - * links on blur. This is only necessary when keyboard users tab into - * contextual links without edit mode (i.e. without TabbingManager). - * That means that if we decide to disable tabbing of contextual links - * without edit mode, all this timer logic can go away. - * - * @type {NaN|number} - */ - this.timer = NaN; - }, - - /** - * Sets focus on the model; Clears the timer that dismisses the links. - */ - focus() { - // Clear the timeout that might have been set by blurring a link. - window.clearTimeout(this.timer); - this.model.focus(); - }, - }, - ); -})(Drupal, Backbone); diff --git a/core/modules/contextual/js/views/RegionView.js b/core/modules/contextual/js/views/RegionView.js deleted file mode 100644 index 349428301d81..000000000000 --- a/core/modules/contextual/js/views/RegionView.js +++ /dev/null @@ -1,75 +0,0 @@ -/** - * @file - * A Backbone View that renders the visual view of a contextual region element. - */ - -(function (Drupal, Backbone) { - /** - * @deprecated in drupal:9.4.0 and is removed from drupal:12.0.0. There is no - * replacement. - */ - Drupal.contextual.RegionView = Backbone.View.extend( - /** @lends Drupal.contextual.RegionView# */ { - /** - * Events for the Backbone view. - * - * @return {object} - * A mapping of events to be used in the view. - */ - events() { - // Used for tracking the presence of touch events. When true, the - // mousemove and mouseenter event handlers are effectively disabled. - // This is used instead of preventDefault() on touchstart as some - // touchstart events are not cancelable. - let touchStart = false; - return { - touchstart() { - // Set to true so the mouseenter and mouseleave events that follow - // know to not execute any hover related logic. - touchStart = true; - }, - mouseenter() { - if (!touchStart) { - this.model.set('regionIsHovered', true); - } - }, - mouseleave() { - if (!touchStart) { - this.model.close().blur().set('regionIsHovered', false); - } - }, - mousemove() { - // Because there are scenarios where there are both touchscreens - // and pointer devices, the touchStart flag should be set back to - // false after mouseenter and mouseleave complete. It will be set to - // true if another touchstart event occurs. - touchStart = false; - }, - }; - }, - - /** - * Renders the visual view of a contextual region element. - * - * @constructs - * - * @augments Backbone.View - */ - initialize() { - this.listenTo(this.model, 'change:hasFocus', this.render); - }, - - /** - * {@inheritdoc} - * - * @return {Drupal.contextual.RegionView} - * The current contextual region view. - */ - render() { - this.$el.toggleClass('focus', this.model.get('hasFocus')); - - return this; - }, - }, - ); -})(Drupal, Backbone); diff --git a/core/modules/contextual/js/views/VisualView.js b/core/modules/contextual/js/views/VisualView.js deleted file mode 100644 index fcd932b1faf4..000000000000 --- a/core/modules/contextual/js/views/VisualView.js +++ /dev/null @@ -1,109 +0,0 @@ -/** - * @file - * A Backbone View that provides the visual view of a contextual link. - */ - -(function (Drupal, Backbone) { - /** - * @deprecated in drupal:9.4.0 and is removed from drupal:12.0.0. There is no - * replacement. - */ - Drupal.contextual.VisualView = Backbone.View.extend( - /** @lends Drupal.contextual.VisualView# */ { - /** - * Events for the Backbone view. - * - * @return {object} - * A mapping of events to be used in the view. - */ - events() { - // Prevents delay and simulated mouse events. - const touchEndToClick = function (event) { - event.preventDefault(); - event.target.click(); - }; - - // Used for tracking the presence of touch events. When true, the - // mousemove and mouseenter event handlers are effectively disabled. - // This is used instead of preventDefault() on touchstart as some - // touchstart events are not cancelable. - let touchStart = false; - - return { - touchstart() { - // Set to true so the mouseenter events that follows knows to not - // execute any hover related logic. - touchStart = true; - }, - mouseenter() { - // We only want mouse hover events on non-touch. - if (!touchStart) { - this.model.focus(); - } - }, - mousemove() { - // Because there are scenarios where there are both touchscreens - // and pointer devices, the touchStart flag should be set back to - // false after mouseenter and mouseleave complete. It will be set to - // true if another touchstart event occurs. - touchStart = false; - }, - 'click .trigger': function () { - this.model.toggleOpen(); - }, - 'touchend .trigger': touchEndToClick, - 'click .contextual-links a': function () { - this.model.close().blur(); - }, - 'touchend .contextual-links a': touchEndToClick, - }; - }, - - /** - * Renders the visual view of a contextual link. Listens to mouse & touch. - * - * @constructs - * - * @augments Backbone.View - */ - initialize() { - this.listenTo(this.model, 'change', this.render); - }, - - /** - * {@inheritdoc} - * - * @return {Drupal.contextual.VisualView} - * The current contextual visual view. - */ - render() { - const isOpen = this.model.get('isOpen'); - // The trigger should be visible when: - // - the mouse hovered over the region, - // - the trigger is locked, - // - and for as long as the contextual menu is open. - const isVisible = - this.model.get('isLocked') || - this.model.get('regionIsHovered') || - isOpen; - - this.$el - // The open state determines if the links are visible. - .toggleClass('open', isOpen) - // Update the visibility of the trigger. - .find('.trigger') - .toggleClass('visually-hidden', !isVisible); - - // Nested contextual region handling: hide any nested contextual triggers. - if ('isOpen' in this.model.changed) { - this.$el - .closest('.contextual-region') - .find('.contextual .trigger:not(:first)') - .toggle(!isOpen); - } - - return this; - }, - }, - ); -})(Drupal, Backbone); diff --git a/core/modules/contextual/tests/src/FunctionalJavascript/EditModeTest.php b/core/modules/contextual/tests/src/FunctionalJavascript/EditModeTest.php index 75e56b5f76b2..1d4fa243c492 100644 --- a/core/modules/contextual/tests/src/FunctionalJavascript/EditModeTest.php +++ b/core/modules/contextual/tests/src/FunctionalJavascript/EditModeTest.php @@ -73,47 +73,40 @@ class EditModeTest extends WebDriverTestBase { $page = $this->getSession()->getPage(); // Get the page twice to ensure edit mode remains enabled after a new page // request. - for ($page_get_count = 0; $page_get_count < 2; $page_get_count++) { - $this->drupalGet('user'); - $expected_restricted_tab_count = 1 + count($page->findAll('css', '[data-contextual-id]')); - - // After the page loaded we need to additionally wait until the settings - // tray Ajax activity is done. - if ($page_get_count === 0) { - $web_assert->assertWaitOnAjaxRequest(); - } - - if ($page_get_count == 0) { - $unrestricted_tab_count = $this->getTabbableElementsCount(); - $this->assertGreaterThan($expected_restricted_tab_count, $unrestricted_tab_count); - - // Enable edit mode. - // After the first page load the page will be in edit mode when loaded. - $this->pressToolbarEditButton(); - } - - $this->assertAnnounceEditMode(); - $this->assertSame($expected_restricted_tab_count, $this->getTabbableElementsCount()); - - // Disable edit mode. - $this->pressToolbarEditButton(); - $this->assertAnnounceLeaveEditMode(); - $this->assertSame($unrestricted_tab_count, $this->getTabbableElementsCount()); - // Enable edit mode again. - $this->pressToolbarEditButton(); - // Finally assert that the 'edit mode enabled' announcement is still - // correct after toggling the edit mode at least once. - $this->assertAnnounceEditMode(); - $this->assertSame($expected_restricted_tab_count, $this->getTabbableElementsCount()); - - // Test while Edit Mode is enabled it doesn't interfere with pages with - // no contextual links. - $this->drupalGet('admin/structure/block'); - $web_assert->elementContains('css', 'h1.page-title', 'Block layout'); - $this->assertEquals(0, count($page->findAll('css', '[data-contextual-id]'))); - $this->assertGreaterThan(0, $this->getTabbableElementsCount()); - } - + $this->drupalGet('user'); + $expected_restricted_tab_count = 1 + count($page->findAll('css', '[data-contextual-id]')); + + // After the page loaded we need to additionally wait until the settings + // tray Ajax activity is done. + $web_assert->assertWaitOnAjaxRequest(); + + $unrestricted_tab_count = $this->getTabbableElementsCount(); + $this->assertGreaterThan($expected_restricted_tab_count, $unrestricted_tab_count); + + // Enable edit mode. + // After the first page load the page will be in edit mode when loaded. + $this->pressToolbarEditButton(); + + $this->assertAnnounceEditMode(); + $this->assertSame($expected_restricted_tab_count, $this->getTabbableElementsCount()); + + // Disable edit mode. + $this->pressToolbarEditButton(); + $this->assertAnnounceLeaveEditMode(); + $this->assertSame($unrestricted_tab_count, $this->getTabbableElementsCount()); + // Enable edit mode again. + $this->pressToolbarEditButton(); + // Finally assert that the 'edit mode enabled' announcement is still + // correct after toggling the edit mode at least once. + $this->assertAnnounceEditMode(); + $this->assertSame($expected_restricted_tab_count, $this->getTabbableElementsCount()); + + // Test while Edit Mode is enabled it doesn't interfere with pages with + // no contextual links. + $this->drupalGet('admin/structure/block'); + $web_assert->elementContains('css', 'h1.page-title', 'Block layout'); + $this->assertEquals(0, count($page->findAll('css', '[data-contextual-id]'))); + $this->assertGreaterThan(0, $this->getTabbableElementsCount()); } /** diff --git a/core/modules/navigation/tests/src/FunctionalJavascript/PerformanceTest.php b/core/modules/navigation/tests/src/FunctionalJavascript/PerformanceTest.php index 2371bef31aac..ae9ae566f8dd 100644 --- a/core/modules/navigation/tests/src/FunctionalJavascript/PerformanceTest.php +++ b/core/modules/navigation/tests/src/FunctionalJavascript/PerformanceTest.php @@ -91,7 +91,7 @@ class PerformanceTest extends PerformanceTestBase { 'CacheTagInvalidationCount' => 0, 'CacheTagLookupQueryCount' => 14, 'ScriptCount' => 3, - 'ScriptBytes' => 213500, + 'ScriptBytes' => 167569, 'StylesheetCount' => 2, 'StylesheetBytes' => 46000, ]; diff --git a/core/modules/package_manager/tests/src/Build/TemplateProjectTestBase.php b/core/modules/package_manager/tests/src/Build/TemplateProjectTestBase.php index 16cd486ad750..18b63b8376bd 100644 --- a/core/modules/package_manager/tests/src/Build/TemplateProjectTestBase.php +++ b/core/modules/package_manager/tests/src/Build/TemplateProjectTestBase.php @@ -441,6 +441,8 @@ END; $requirements['symfony/polyfill-php81'], $requirements['symfony/polyfill-php82'], $requirements['symfony/polyfill-php83'], + // Needed for PHP 8.4 features while PHP 8.3 is the minimum. + $requirements['symfony/polyfill-php84'], ); // If this package requires any Drupal core packages, ensure it allows // any version. diff --git a/core/modules/toolbar/js/views/ToolbarVisualView.js b/core/modules/toolbar/js/views/ToolbarVisualView.js index 89f472f0eafa..00bd236973f6 100644 --- a/core/modules/toolbar/js/views/ToolbarVisualView.js +++ b/core/modules/toolbar/js/views/ToolbarVisualView.js @@ -210,7 +210,7 @@ // Deactivate the previous tab. $(this.model.previous('activeTab')) .removeClass('is-active') - .prop('aria-pressed', false); + .attr('aria-pressed', false); // Deactivate the previous tray. $(this.model.previous('activeTray')).removeClass('is-active'); @@ -222,7 +222,7 @@ $tab .addClass('is-active') // Mark the tab as pressed. - .prop('aria-pressed', true); + .attr('aria-pressed', true); const name = $tab.attr('data-toolbar-tray'); // Store the active tab name or remove the setting. const id = $tab.get(0).id; diff --git a/core/modules/toolbar/tests/src/FunctionalJavascript/ToolbarIntegrationTest.php b/core/modules/toolbar/tests/src/FunctionalJavascript/ToolbarIntegrationTest.php index c315f9f6ebb0..dcf0ff6d79c3 100644 --- a/core/modules/toolbar/tests/src/FunctionalJavascript/ToolbarIntegrationTest.php +++ b/core/modules/toolbar/tests/src/FunctionalJavascript/ToolbarIntegrationTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Drupal\Tests\toolbar\FunctionalJavascript; +use Behat\Mink\Element\NodeElement; use Drupal\FunctionalJavascriptTests\WebDriverTestBase; /** @@ -43,12 +44,22 @@ class ToolbarIntegrationTest extends WebDriverTestBase { $page = $this->getSession()->getPage(); // Test that it is possible to toggle the toolbar tray. - $content = $page->findLink('Content'); - $this->assertTrue($content->isVisible(), 'Toolbar tray is open by default.'); - $page->clickLink('Manage'); - $this->assertFalse($content->isVisible(), 'Toolbar tray is closed after clicking the "Manage" link.'); - $page->clickLink('Manage'); - $this->assertTrue($content->isVisible(), 'Toolbar tray is visible again after clicking the "Manage" button a second time.'); + $content_link = $page->findLink('Content'); + $manage_link = $page->find('css', '#toolbar-item-administration'); + + // Start with open tray. + $this->waitAndAssertAriaPressedState($manage_link, TRUE); + $this->assertTrue($content_link->isVisible(), 'Toolbar tray is open by default.'); + + // Click to close. + $manage_link->click(); + $this->waitAndAssertAriaPressedState($manage_link, FALSE); + $this->assertFalse($content_link->isVisible(), 'Toolbar tray is closed after clicking the "Manage" link.'); + + // Click to open. + $manage_link->click(); + $this->waitAndAssertAriaPressedState($manage_link, TRUE); + $this->assertTrue($content_link->isVisible(), 'Toolbar tray is visible again after clicking the "Manage" button a second time.'); // Test toggling the toolbar tray between horizontal and vertical. $tray = $page->findById('toolbar-item-administration-tray'); @@ -87,4 +98,33 @@ class ToolbarIntegrationTest extends WebDriverTestBase { $this->assertFalse($button->isVisible(), 'Orientation toggle from other tray is not visible'); } + /** + * Asserts that an element's `aria-pressed` attribute matches expected state. + * + * Uses `waitFor()` to pause until either the condition is met or the timeout + * of `1` second has passed. + * + * @param \Behat\Mink\Element\NodeElement $element + * The element to be tested. + * @param bool $expected + * The expected value of `aria-pressed`, as a boolean. + * + * @throws ExpectationFailedException + */ + private function waitAndAssertAriaPressedState(NodeElement $element, bool $expected): void { + $this->assertTrue( + $this + ->getSession() + ->getPage() + ->waitFor(1, function () use ($element, $expected): bool { + // Get boolean representation of `aria-pressed`. + // TRUE if `aria-pressed="true"`, FALSE otherwise. + $actual = $element->getAttribute('aria-pressed') == 'true'; + + // Exit `waitFor()` when $actual == $expected. + return $actual == $expected; + }) + ); + } + } diff --git a/core/profiles/demo_umami/tests/src/FunctionalJavascript/AssetAggregationAcrossPagesTest.php b/core/profiles/demo_umami/tests/src/FunctionalJavascript/AssetAggregationAcrossPagesTest.php index 0b56bdd18c39..c7358357f3c5 100644 --- a/core/profiles/demo_umami/tests/src/FunctionalJavascript/AssetAggregationAcrossPagesTest.php +++ b/core/profiles/demo_umami/tests/src/FunctionalJavascript/AssetAggregationAcrossPagesTest.php @@ -69,7 +69,7 @@ class AssetAggregationAcrossPagesTest extends PerformanceTestBase { }, 'umamiFrontAndRecipePagesEditor'); $expected = [ 'ScriptCount' => 5, - 'ScriptBytes' => 338200, + 'ScriptBytes' => 335637, 'StylesheetCount' => 5, 'StylesheetBytes' => 205700, ]; diff --git a/core/themes/claro/css/classy/components/tablesort.css b/core/themes/claro/css/classy/components/tablesort.css index 44e5349404d0..f2a3c4ad60a3 100644 --- a/core/themes/claro/css/classy/components/tablesort.css +++ b/core/themes/claro/css/classy/components/tablesort.css @@ -6,6 +6,3 @@ th.is-active img { display: inline; } -td.is-active { - background-color: #ddd; -} diff --git a/core/themes/claro/css/components/dialog.css b/core/themes/claro/css/components/dialog.css index e1d0b18f3bca..ecaf17d2daae 100644 --- a/core/themes/claro/css/components/dialog.css +++ b/core/themes/claro/css/components/dialog.css @@ -96,7 +96,8 @@ @media (forced-colors: active) { .ui-dialog .ui-dialog-titlebar .ui-dialog-titlebar-close .ui-icon.ui-icon-closethick { - background: url("data:image/svg+xml,%3csvg width='12' height='12' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M11 1.318l-10 10M11 11.318l-10-10' stroke='buttonText' stroke-width='1.5'/%3e%3c/svg%3e") no-repeat 50%; + background: buttontext; + mask: url("data:image/svg+xml,%3csvg width='12' height='12' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M11 1.318l-10 10M11 11.318l-10-10' stroke='%23D3D4D9' stroke-width='1.5'/%3e%3c/svg%3e") no-repeat 50%; } } diff --git a/core/themes/claro/css/components/dialog.pcss.css b/core/themes/claro/css/components/dialog.pcss.css index ffec289fbc43..a965222cf7c3 100644 --- a/core/themes/claro/css/components/dialog.pcss.css +++ b/core/themes/claro/css/components/dialog.pcss.css @@ -86,7 +86,8 @@ background: url(../../images/icons/d3d4d9/ex.svg) no-repeat 50%; @media (forced-colors: active) { - background: url(../../images/icons/buttonText/ex.svg) no-repeat 50%; + background: buttontext; + mask: url(../../images/icons/d3d4d9/ex.svg) no-repeat 50%; } } } diff --git a/core/themes/claro/images/icons/buttonText/ex.svg b/core/themes/claro/images/icons/buttonText/ex.svg deleted file mode 100644 index 635ac1c6b382..000000000000 --- a/core/themes/claro/images/icons/buttonText/ex.svg +++ /dev/null @@ -1 +0,0 @@ -<svg width="12" height="12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11 1.318l-10 10M11 11.318l-10-10" stroke="buttonText" stroke-width="1.5"/></svg> diff --git a/core/themes/olivero/olivero.libraries.yml b/core/themes/olivero/olivero.libraries.yml index c699ebc72c8d..137a4296eb67 100644 --- a/core/themes/olivero/olivero.libraries.yml +++ b/core/themes/olivero/olivero.libraries.yml @@ -45,7 +45,6 @@ global-styling: css/components/site-header.css: {} css/components/skip-link.css: {} css/components/pager.css: {} - css/components/table.css: {} css/components/text-content.css: {} css/components/wide-content.css: {} @@ -291,3 +290,18 @@ tags: css: theme: css/components/tags.css: {} + +olivero.table: + version: VERSION + css: + component: + css/components/table.css: {} + moved_files: + olivero/global-styling: + deprecation_version: 11.2.0 + removed_version: 12.0.0 + deprecation_link: https://www.drupal.org/node/3517675 + css: + component: + css/components/table.css: + base: css/components/table.css diff --git a/core/themes/olivero/olivero.theme b/core/themes/olivero/olivero.theme index d10ee7d155cd..b2f3bff26841 100644 --- a/core/themes/olivero/olivero.theme +++ b/core/themes/olivero/olivero.theme @@ -617,6 +617,15 @@ function olivero_preprocess_table(&$variables): void { } } } + + $variables['#attached']['library'][] = 'olivero/olivero.table'; +} + +/** + * Implements hook_preprocess_HOOK() for views-view-table templates. + */ +function olivero_preprocess_views_view_table(&$variables): void { + $variables['#attached']['library'][] = 'olivero/olivero.table'; } /** |