|
const project = {"title":"Material select closing flash with legacy animations","description":"Minimal Angular 21.2.10 and Material 21.2.8 select close reproduction.","template":"node","files":{"package.json":"{\n \"name\": \"material-select-close-flash-reproduction\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"scripts\": {\n \"start\": \"ng serve --host 0.0.0.0\",\n \"build\": \"ng build\"\n },\n \"engines\": {\n \"node\": \"^20.19.0 || ^22.12.0 || \u003e=24.0.0\"\n },\n \"dependencies\": {\n \"@angular/animations\": \"21.2.10\",\n \"@angular/common\": \"21.2.10\",\n \"@angular/compiler\": \"21.2.10\",\n \"@angular/core\": \"21.2.10\",\n \"@angular/forms\": \"21.2.10\",\n \"@angular/platform-browser\": \"21.2.10\",\n \"@angular/cdk\": \"21.2.8\",\n \"@angular/material\": \"21.2.8\",\n \"rxjs\": \"7.8.2\",\n \"tslib\": \"2.8.1\"\n },\n \"devDependencies\": {\n \"@angular/build\": \"21.2.8\",\n \"@angular/cli\": \"21.2.8\",\n \"@angular/compiler-cli\": \"21.2.10\",\n \"typescript\": \"5.9.3\"\n },\n \"stackblitz\": {\n \"installDependencies\": true,\n \"startCommand\": \"npm start\"\n }\n}\n","angular.json":"{\n \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n \"version\": 1,\n \"newProjectRoot\": \"projects\",\n \"projects\": {\n \"reproduction\": {\n \"projectType\": \"application\",\n \"root\": \"\",\n \"sourceRoot\": \"src\",\n \"architect\": {\n \"build\": {\n \"builder\": \"@angular/build:application\",\n \"options\": {\n \"outputPath\": \"dist/reproduction\",\n \"index\": \"src/index.html\",\n \"browser\": \"src/main.ts\",\n \"tsConfig\": \"tsconfig.app.json\",\n \"styles\": [\n \"node_modules/@angular/material/prebuilt-themes/azure-blue.css\"\n ]\n },\n \"configurations\": {\n \"development\": {\n \"optimization\": false,\n \"extractLicenses\": false,\n \"sourceMap\": true\n }\n },\n \"defaultConfiguration\": \"development\"\n },\n \"serve\": {\n \"builder\": \"@angular/build:dev-server\",\n \"configurations\": {\n \"development\": {\n \"buildTarget\": \"reproduction:build:development\"\n }\n },\n \"defaultConfiguration\": \"development\"\n }\n }\n }\n }\n}\n","tsconfig.json":"{\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"module\": \"preserve\",\n \"moduleResolution\": \"bundler\",\n \"lib\": [\n \"ES2022\",\n \"DOM\"\n ],\n \"experimentalDecorators\": true,\n \"importHelpers\": true,\n \"isolatedModules\": true,\n \"strict\": true,\n \"skipLibCheck\": true,\n \"noImplicitOverride\": true,\n \"noPropertyAccessFromIndexSignature\": true,\n \"noImplicitReturns\": true,\n \"noFallthroughCasesInSwitch\": true\n },\n \"angularCompilerOptions\": {\n \"strictInjectionParameters\": true,\n \"strictInputAccessModifiers\": true,\n \"strictTemplates\": true\n }\n}\n","tsconfig.app.json":"{\n \"extends\": \"./tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"./out-tsc/app\",\n \"types\": []\n },\n \"files\": [\n \"src/main.ts\"\n ]\n}\n","src/index.html":"\u003c!doctype html\u003e\n\u003chtml lang=\"en\"\u003e\n \u003chead\u003e\n \u003cmeta charset=\"utf-8\"\u003e\n \u003ctitle\u003eMaterial select close reproduction\u003c/title\u003e\n \u003cbase href=\"/\"\u003e\n \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\"\u003e\n \u003c/head\u003e\n \u003cbody\u003e\n \u003capp-root\u003e\u003c/app-root\u003e\n \u003c/body\u003e\n\u003c/html\u003e\n","src/main.ts":"import { Component, provideZonelessChangeDetection } from '@angular/core';\nimport { bootstrapApplication } from '@angular/platform-browser';\nimport { provideAnimationsAsync } from '@angular/platform-browser/animations/async';\nimport { trigger } from '@angular/animations';\nimport { MatFormFieldModule } from '@angular/material/form-field';\nimport { MatSelectModule } from '@angular/material/select';\nimport { installAnimationDiagnostics } from './diagnostics';\n\n@Component({\n selector: 'app-root',\n standalone: true,\n imports: [MatFormFieldModule, MatSelectModule],\n animations: [trigger('activateLegacyRenderer', [])],\n template: `\n \u003ch1 @activateLegacyRenderer\u003eMaterial select close reproduction\u003c/h1\u003e\n \u003cmat-form-field\u003e\n \u003cmat-label\u003eChoice\u003c/mat-label\u003e\n \u003cmat-select value=\"one\"\u003e\n \u003cmat-option value=\"one\"\u003eOne\u003c/mat-option\u003e\n \u003cmat-option value=\"two\"\u003eTwo\u003c/mat-option\u003e\n \u003cmat-option value=\"three\"\u003eThree\u003c/mat-option\u003e\n \u003c/mat-select\u003e\n \u003c/mat-form-field\u003e\n `,\n})\nclass SelectReproductionComponent {}\n\nif (new URLSearchParams(location.search).get('diagnostics') === '1') {\n installAnimationDiagnostics();\n}\n\nbootstrapApplication(SelectReproductionComponent, {\n providers: [provideZonelessChangeDetection(), provideAnimationsAsync()],\n}).catch(console.error);\n","src/diagnostics.ts":"/** Logs bounded exit-animation observations without requesting animation frames. */\nexport function installAnimationDiagnostics(): void {\n document.addEventListener(\n 'animationend',\n (event) =\u003e {\n const panel = event.target;\n if (\n !(panel instanceof HTMLElement) ||\n !panel.matches('.mat-mdc-select-panel') ||\n event.animationName !== '_mat-select-exit'\n ) {\n return;\n }\n\n const startedAt = performance.now();\n /** Samples only the panel's visibility and timing relative to this event. */\n const sample = (phase: string): void =\u003e {\n console.info('[select-exit]', {\n phase,\n elapsedMs: performance.now() - startedAt,\n connected: panel.isConnected,\n opacity: getComputedStyle(panel).opacity,\n });\n };\n\n const removalObserver = new MutationObserver(() =\u003e {\n if (!panel.isConnected) {\n sample('removed');\n removalObserver.disconnect();\n }\n });\n removalObserver.observe(document.body, { childList: true, subtree: true });\n\n sample('animationend-capture');\n queueMicrotask(() =\u003e sample('animationend-microtask'));\n setTimeout(() =\u003e sample('animationend-timer'), 0);\n setTimeout(() =\u003e removalObserver.disconnect(), 1000); // Stop observing if the panel remains connected; diagnostics never keep an observer indefinitely.\n },\n true,\n );\n}\n","README.md":"# Material select close flash reproduction\n\nA minimal standalone Angular application using the prebuilt Material azure-blue theme and default CDK overlay configuration.\n\n## Run locally\n\nUse Node.js 20.19+, 22.12+, or 24+ as supported by Angular 21, then run:\n\n```sh\nnpm install\nnpm start\n```\n\nOpen the address printed by Angular CLI. Open **Choice**, then press Escape, click outside the panel, or select another option. Observe the panel as it closes. The reported behavior is a brief return to full opacity between fading out and being removed.\n\nThe relevant configuration is deliberately small:\n\n- Angular core and animations 21.2.10; Material and CDK 21.2.8.\n- Zoneless change detection and `provideAnimationsAsync()`.\n- An empty legacy animation trigger bound to the heading.\n- The default CDK overlay configuration and one prebuilt Material theme.\n\nNo application-specific overlay provider or CSS is included. The first browser version affected has not been established.\n\n## Optional diagnostics\n\nAdd `?diagnostics=1` to the reproduction URL and open the browser developer console. Each close logs only `phase`, `elapsedMs`, `connected`, and `opacity`. The observer samples the capture listener, a microtask, a zero-delay timer, and DOM removal. It does not run a `requestAnimationFrame` loop, because a continuously active frame callback may change the scheduling being investigated.\n\nAn `animationend` sample with `opacity: \"1\"` alone is not proof of a painted flash. Compare the following samples and, where available, a browser screenshot sequence.\n\n## Independent comparison edits\n\nApply each edit separately, then reload before comparing:\n\n1. Remove `@activateLegacyRenderer` from the heading and remove the component's `animations` property. Keep `provideAnimationsAsync()` configured.\n2. Restore the trigger, then add this rule in the browser developer console to test whether holding the final opacity prevents the flash:\n\n ```js\n const style = document.createElement('style');\n style.textContent = '.mat-mdc-select-panel.mat-select-panel-exit { animation-fill-mode: forwards; }';\n document.head.appendChild(style);\n ```\n\nThe style is an experimental comparison, not part of the default reproduction.\n\n## StackBlitz launcher\n\nOpen `stackblitz-launcher.html` locally and click **Launch in StackBlitz**. The launcher sends the embedded project files through the official StackBlitz SDK using its `node` template. A dependency download and development-server startup are required.\n\nStackBlitz does not persist SDK-generated projects unless they are saved to an account. Share this launcher and source files, or save the generated project before using its address as a permanent reproduction link.\n\n- [StackBlitz SDK and persistence documentation](https://developer.stackblitz.com/platform/api/javascript-sdk)\n- [Angular version compatibility](https://angular.dev/reference/versions)\n\n## Verification boundary\n\nThe workspace, application builder, and dev-server configurations passed the installed CLI 21.2.8 JSON schemas. Angular 21.2.10 template/semantic checks and TypeScript 5.9.3 checks returned zero diagnostics. These checks resolved dependencies from an existing installation without changing the reproduction configuration. This bundle's remote StackBlitz and CodeSandbox execution has not been tested. The presence of a generated launcher or URL does not establish successful remote execution.\n"}}; |