Files
filament/test/renderdiff/src/viewer_html/tools.js
T
Powei Feng cd22478e4f renderdiff: add viewer for image differences (#8768)
- Modify the compare script to output more details of a
   comparison. This will include the source/golden directory,
   the comparison directory (the new renderings), and a file
   path to difference images if the golden does not match
   the rendered image.
 - The image_diff script can now output a TIFF that is the
   difference of two input TIFFs.
 - Add a viewer for examining the differences between rendered
   output and golden images.
 - The viewer consists of a simple server of web API endpoints
   for querying difference results (along with rendered images
   in TIFF).
 - And a web-based (html + lit-element) UI for looking at the
   rendered images and differences.
2025-06-02 19:37:46 +00:00

226 lines
6.0 KiB
JavaScript

// Copyright (C) 2025 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { LitElement, html, css } from "https://cdn.jsdelivr.net/gh/lit/dist@3/all/lit-all.min.js";
// Generated by Gemini
export class RadioButtonGroup extends LitElement {
static styles = css`
:host {
display: block;
font-family: sans-serif;
}
.radio-group-container {
display: flex;
flex-direction: row;
}
label {
display: flex;
align-items: center;
margin-bottom: 8px;
cursor: pointer;
padding: 8px;
border-radius: 4px;
transition: background-color 0.2s ease-in-out;
}
label:hover {
background-color: #f0f0f0;
}
input[type="radio"] {
margin-right: 8px;
cursor: pointer;
/* Custom radio button appearance */
appearance: none;
-webkit-appearance: none;
width: 18px;
height: 18px;
border: 2px solid #ccc;
border-radius: 50%;
outline: none;
transition: border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}
input[type="radio"]:checked {
border-color: #656565;
background-color: #656565; /* Optional: fill color when checked */
}
input[type="radio"]:checked::before {
content: '';
display: block;
width: 8px;
height: 8px;
margin: 3px; /* Adjust to center the dot */
background-color: white;
border-radius: 50%;
}
input[type="radio"]:focus {
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
}
.label-text {
font-size: 1rem;
}
`;
static properties = {
/**
* An array of strings representing the choices for the radio buttons.
* @type {Array<string>}
*/
choices: { type: Array },
/**
* The name for the radio button group. This is important for accessibility
* and ensuring only one radio button in the group can be selected.
* @type {string}
*/
name: { type: String },
/**
* The currently selected value.
* @type {string}
*/
value: { type: String, reflect: true },
/**
* The label or title for the radio group.
* @type {string}
*/
groupLabel: { type: String }
};
constructor() {
super();
this.choices = [];
this.name = 'radio-group'; // Default name
this.value = '';
this.groupLabel = '';
}
_handleChange(event) {
const selectedValue = event.target.value;
if (this.value !== selectedValue) {
this.value = selectedValue;
// Dispatch a custom event with the new value
this.dispatchEvent(new CustomEvent('radio-change', {
detail: { value: this.value, radioId: this.id },
bubbles: true, // Allows the event to bubble up through the DOM
composed: true // Allows the event to cross shadow DOM boundaries
}));
}
}
render() {
return html`
<div class="radio-group-container" role="radiogroup" aria-labelledby="group-label">
${this.groupLabel ? html`<span id="group-label" class="group-label">${this.groupLabel}</span>` : ''}
${this.choices.map(choice => html`
<label>
<input
type="radio"
name=${this.name}
.value=${choice}
.checked=${choice === this.value}
@change=${this._handleChange}
>
<span class="label-text">${choice}</span>
</label>
`)}
</div>
`;
}
}
customElements.define('radio-button-group', RadioButtonGroup);
// Generated by Gemini with some modifications
class ModalDialog extends LitElement {
static styles = css`
:host {
display: none; /* Hidden by default */
}
:host([open]) {
display: block; /* Show when open attribute is present */
}
.backdrop {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent black */
display: flex;
justify-content: center;
align-items: center;
z-index: 1000; /* Ensure it's on top */
}
.dialog {
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
min-width: 300px; /* Or your desired width */
z-index: 1001; /* Above the backdrop */
display: flex;
flex-direction: column;
align-items: center;
margin: 0 15px;
}
`;
static get properties() {
return {
open: { type: Boolean, reflect: true },
};
}
constructor() {
super();
this.open = false;
}
_handleBackdropClick(event) {
// Close only if the click is directly on the backdrop, not on the dialog itself
if (event.target === this.shadowRoot.querySelector('.backdrop')) {
this.open = false;
this.dispatchEvent(new CustomEvent('dialog-closed', { bubbles: true, composed: true }));
}
}
_handleDialogClick(event) {
// Prevent clicks inside the dialog from bubbling up to the backdrop
event.stopPropagation();
}
render() {
if (!this.open) {
return html``;
}
return html`
<div class="backdrop" @click="${this._handleBackdropClick}">
<div class="dialog" @click="${this._handleDialogClick}">
<slot name="header"><h2>Default Header</h2></slot>
<slot>
<p>This is the default content of the modal.</p>
</slot>
<slot name="footer">
</slot>
</div>
</div>
`;
}
}
customElements.define('modal-dialog', ModalDialog);