diff --git a/renderers/lit/CHANGELOG.md b/renderers/lit/CHANGELOG.md index 56bf693e4..c25f21a0e 100644 --- a/renderers/lit/CHANGELOG.md +++ b/renderers/lit/CHANGELOG.md @@ -1,8 +1,10 @@ ## 0.9.0 -- \[v0_9\] Modify Text widget from the basic catalog to support markdown. -- \[v0_9\] Add `Context.markdown` to the public API -- \[CI\] Fix post-build script. This pins the dependency on `@a2ui/web_core` to +- (v0_9) Modify Text widget from the basic catalog to support markdown. +- (v0_9) Add `Context.markdown` to the public API +- (v0_9) Re-style the v0_9 catalog components using the default theme from + `web_core`. +- (CI) Fix post-build script. This pins the dependency on `@a2ui/web_core` to the latest available in the repo when publishing. ## 0.8.4 diff --git a/renderers/lit/src/v0_9/catalogs/basic/basic-catalog-a2ui-lit-element.ts b/renderers/lit/src/v0_9/catalogs/basic/basic-catalog-a2ui-lit-element.ts new file mode 100644 index 000000000..a36b15892 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/basic-catalog-a2ui-lit-element.ts @@ -0,0 +1,43 @@ +/* + * Copyright 2026 Google LLC + * + * 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 + * + * https://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 { ComponentApi } from "@a2ui/web_core/v0_9"; +import { A2uiLitElement } from "../../a2ui-lit-element.js"; +import { injectBasicCatalogStyles } from "@a2ui/web_core/v0_9/basic_catalog"; + +/** + * A base class for A2UI basic catalog components. + * + * Handles some common features of all basic catalog A2ui elements, like + * injecting the basic CSS styles if needed, and setting the flex property + * if set by the framework. + */ +export abstract class BasicCatalogA2uiLitElement< + Api extends ComponentApi, +> extends A2uiLitElement { + connectedCallback() { + super.connectedCallback(); + injectBasicCatalogStyles(); + } + + updated(changedProperties: Map) { + super.updated(changedProperties); + const props = this.controller?.props as any; + if (props && props.weight !== undefined) { + this.style.flex = String(props.weight); + } + } +} diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/AudioPlayer.ts b/renderers/lit/src/v0_9/catalogs/basic/components/AudioPlayer.ts index 20a5ca62d..14649b3f5 100644 --- a/renderers/lit/src/v0_9/catalogs/basic/components/AudioPlayer.ts +++ b/renderers/lit/src/v0_9/catalogs/basic/components/AudioPlayer.ts @@ -14,15 +14,27 @@ * limitations under the License. */ -import { html, nothing } from "lit"; +import { html, nothing, css } from "lit"; import { customElement } from "lit/decorators.js"; import { AudioPlayerApi } from "@a2ui/web_core/v0_9/basic_catalog"; -import { A2uiLitElement, A2uiController } from "@a2ui/lit/v0_9"; +import { BasicCatalogA2uiLitElement } from "../basic-catalog-a2ui-lit-element.js"; +import { A2uiController } from "@a2ui/lit/v0_9"; @customElement("a2ui-audioplayer") -export class A2uiAudioPlayerElement extends A2uiLitElement< +export class A2uiAudioPlayerElement extends BasicCatalogA2uiLitElement< typeof AudioPlayerApi > { + static styles = css` + :host { + display: flex; + flex-direction: column; + gap: var(--a2ui-spacing-xs, 0.25rem); + background: var(--a2ui-audioplayer-background, transparent); + border-radius: var(--a2ui-audioplayer-border-radius, 0); + padding: var(--a2ui-audioplayer-padding, 0); + } + `; + protected createController() { return new A2uiController(this, AudioPlayerApi); } @@ -31,10 +43,10 @@ export class A2uiAudioPlayerElement extends A2uiLitElement< const props = this.controller.props; if (!props) return nothing; - return html`
+ return html` ${props.description ? html`

${props.description}

` : nothing} -
`; + `; } } diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/Button.ts b/renderers/lit/src/v0_9/catalogs/basic/components/Button.ts index 0ca9655fa..e43b80cbf 100644 --- a/renderers/lit/src/v0_9/catalogs/basic/components/Button.ts +++ b/renderers/lit/src/v0_9/catalogs/basic/components/Button.ts @@ -14,14 +14,86 @@ * limitations under the License. */ -import { html, nothing } from "lit"; +import { html, nothing, css } from "lit"; import { customElement } from "lit/decorators.js"; import { classMap } from "lit/directives/class-map.js"; import { ButtonApi } from "@a2ui/web_core/v0_9/basic_catalog"; -import { A2uiLitElement, A2uiController } from "@a2ui/lit/v0_9"; +import { BasicCatalogA2uiLitElement } from "../basic-catalog-a2ui-lit-element.js"; +import { A2uiController } from "@a2ui/lit/v0_9"; +/** + * A button component that can be used to trigger an action. + */ @customElement("a2ui-basic-button") -export class A2uiBasicButtonElement extends A2uiLitElement { +export class A2uiBasicButtonElement extends BasicCatalogA2uiLitElement { + /** + * The styles of the button can be customized by redefining the following + * CSS variables: + * + * - Primary variant: + * - `--a2ui-color-primary`: The color for the primary variant. + * - `--a2ui-color-on-primary`: The color of the text on the primary variant. + * - Standard/default variant: + * - `--a2ui-color-secondary`: The color for the default variant. + * - `--a2ui-color-on-secondary`: The color of the text on the default variant. + * - `--a2ui-button-border`: The styling for the button border. Defaults to `--a2ui-border-width` width and `--a2ui-color-border` color. + * - `--a2ui-button-border-radius`: The border radius of the button. Defaults to `--a2ui-border-radius`. + * - `--a2ui-button-padding`: The padding of the button. Defaults to `--a2ui-spacing-m`. + * - `--a2ui-button-margin`: The outer margin of the button. Defaults to `--a2ui-spacing-m`. + */ + static styles = css` + :host { + display: inline-block; + margin: var(--a2ui-button-margin, var(--a2ui-spacing-m)); + } + :where(:host) { + --_color-primary: var(--a2ui-color-primary, #17e); + --_button-border-radius: var( + --a2ui-button-border-radius, + var(--a2ui-spacing-s, 0.25rem) + ); + --_button-padding: var( + --a2ui-button-padding, + var(--a2ui-spacing-m, 0.5rem) var(--a2ui-spacing-l, 1rem) + ); + --_button-border: var( + --a2ui-button-border, + var(--a2ui-border-width, 1px) solid var(--a2ui-color-border, #ccc) + ); + } + .a2ui-button { + --_a2ui-text-margin: 0; + --_a2ui-text-color: var(--a2ui-color-on-secondary, #333); + padding: var(--_button-padding); + background: var(--a2ui-button-background, var(--a2ui-color-surface, #fff)); + box-shadow: var(--a2ui-button-box-shadow, none); + font-weight: var(--a2ui-button-font-weight, normal); + color: var(--_a2ui-text-color); + border: var(--_button-border); + border-radius: var(--_button-border-radius); + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + } + .a2ui-button.a2ui-button-primary { + --_a2ui-text-color: var(--a2ui-color-on-primary, #fff); + background-color: var(--_color-primary); + color: var(--_a2ui-text-color); + } + .a2ui-button:hover { + background-color: var(--a2ui-color-secondary-hover, #ddd); + } + .a2ui-button.a2ui-button-primary:hover { + background-color: var(--a2ui-color-primary-hover, #fbd); + } + .a2ui-button.a2ui-button-borderless { + background: none; + padding: 0; + color: var(--_color-primary); + } + `; + protected createController() { return new A2uiController(this, ButtonApi); } diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/Card.ts b/renderers/lit/src/v0_9/catalogs/basic/components/Card.ts index 33e7e0350..075be3553 100644 --- a/renderers/lit/src/v0_9/catalogs/basic/components/Card.ts +++ b/renderers/lit/src/v0_9/catalogs/basic/components/Card.ts @@ -14,13 +14,37 @@ * limitations under the License. */ -import { html, nothing } from "lit"; +import { html, nothing, css } from "lit"; import { customElement } from "lit/decorators.js"; import { CardApi } from "@a2ui/web_core/v0_9/basic_catalog"; -import { A2uiLitElement, A2uiController } from "@a2ui/lit/v0_9"; +import { BasicCatalogA2uiLitElement } from "../basic-catalog-a2ui-lit-element.js"; +import { A2uiController } from "@a2ui/lit/v0_9"; @customElement("a2ui-card") -export class A2uiCardElement extends A2uiLitElement { +export class A2uiCardElement extends BasicCatalogA2uiLitElement { + /** + * The styles of the card can be customized by redefining the following + * CSS variables: + * + * - `--a2ui-card-border`: The styling for the card border. Defaults to `--a2ui-border-width` width and `--a2ui-color-border` color. + * - `--a2ui-card-border-radius`: The border radius of the card. Defaults to `--a2ui-border-radius`. + * - `--a2ui-card-padding`: The padding of the card. Defaults to `--a2ui-spacing-m`. + * - `--a2ui-card-box-shadow`: The box shadow of the card. Defaults to `0 2px 4px rgba(0,0,0,0.1)`. + * - `--a2ui-card-margin`: The outer margin of the card. Defaults to `--a2ui-spacing-m`. + */ + static styles = css` + :host { + display: block; + border: var(--a2ui-card-border, var(--a2ui-border-width, 1px) solid var(--a2ui-color-border, #ccc)); + border-radius: var(--a2ui-card-border-radius, var(--a2ui-border-radius, 8px)); + padding: var(--a2ui-card-padding, var(--a2ui-spacing-m, 16px)); + background: var(--a2ui-card-background, var(--a2ui-color-surface, #fff)); + color: var(--a2ui-color-on-surface, #333); + box-shadow: var(--a2ui-card-box-shadow, 0 2px 4px rgba(0,0,0,0.1)); + margin: var(--a2ui-card-margin, var(--a2ui-spacing-m)); + } + `; + protected createController() { return new A2uiController(this, CardApi); } @@ -30,12 +54,7 @@ export class A2uiCardElement extends A2uiLitElement { if (!props) return nothing; return html` -
- ${props.child ? html`${this.renderNode(props.child)}` : nothing} -
+ ${props.child ? html`${this.renderNode(props.child)}` : nothing} `; } } diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/CheckBox.ts b/renderers/lit/src/v0_9/catalogs/basic/components/CheckBox.ts index 31bc8d4d9..7e7fb0107 100644 --- a/renderers/lit/src/v0_9/catalogs/basic/components/CheckBox.ts +++ b/renderers/lit/src/v0_9/catalogs/basic/components/CheckBox.ts @@ -14,13 +14,55 @@ * limitations under the License. */ -import { html, nothing } from "lit"; +import { html, nothing, css } from "lit"; import { customElement } from "lit/decorators.js"; +import { classMap } from "lit/directives/class-map.js"; import { CheckBoxApi } from "@a2ui/web_core/v0_9/basic_catalog"; -import { A2uiLitElement, A2uiController } from "@a2ui/lit/v0_9"; +import { BasicCatalogA2uiLitElement } from "../basic-catalog-a2ui-lit-element.js"; +import { A2uiController } from "@a2ui/lit/v0_9"; @customElement("a2ui-checkbox") -export class A2uiCheckBoxElement extends A2uiLitElement { +export class A2uiCheckBoxElement extends BasicCatalogA2uiLitElement { + /** + * The styles of the checkbox can be customized by redefining the following + * CSS variables: + * + * - `--a2ui-checkbox-size`: Size of the box. Defaults to `1rem`. + * - `--a2ui-checkbox-border-radius`: Default corner rounding of the box. + * - `--a2ui-checkbox-gap`: Spacing between the checkbox and its label. Defaults to `8px`. + * - `--a2ui-checkbox-margin`: Outer margin of the component. Defaults to `--a2ui-spacing-m`. + * - `--a2ui-checkbox-color-error`: Color for invalid state. Defaults to `red`. + * - `--a2ui-checkbox-label-font-size`: Font size of the label. Defaults to `--a2ui-label-font-size` then `--a2ui-font-size-s`. + * - `--a2ui-checkbox-label-font-weight`: Font weight of the label. Defaults to `--a2ui-label-font-weight` then `bold`. + */ + static styles = css` + :host { + display: inline-block; + margin: var(--a2ui-checkbox-margin, var(--a2ui-spacing-m)); + } + label.a2ui-checkbox { + display: inline-flex; + align-items: center; + gap: var(--a2ui-checkbox-gap, var(--a2ui-spacing-s, 0.5rem)); + font-size: var(--a2ui-checkbox-label-font-size, var(--a2ui-label-font-size, var(--a2ui-font-size-s))); + font-weight: var(--a2ui-checkbox-label-font-weight, var(--a2ui-label-font-weight, bold)); + cursor: pointer; + } + label.invalid { + color: var(--a2ui-checkbox-color-error, red); + } + input { + width: var(--a2ui-checkbox-size, 1rem); + height: var(--a2ui-checkbox-size, 1rem); + background: var(--a2ui-checkbox-background, inherit); + border: var(--a2ui-checkbox-border, var(--a2ui-border)); + border-radius: var(--a2ui-checkbox-border-radius, 4px); + } + input.invalid { + outline: 1px solid var(--a2ui-checkbox-color-error, red); + } + `; + protected createController() { return new A2uiController(this, CheckBoxApi); } @@ -29,10 +71,15 @@ export class A2uiCheckBoxElement extends A2uiLitElement { const props = this.controller.props; if (!props) return nothing; + const isInvalid = props.isValid === false; + const labelClasses = { "a2ui-checkbox": true, invalid: isInvalid }; + const inputClasses = { invalid: isInvalid }; + return html` -