diff --git a/2nd-gen/packages/core/components/infield-button/InfieldButton.base.ts b/2nd-gen/packages/core/components/infield-button/InfieldButton.base.ts new file mode 100644 index 00000000000..c2d8bd464ba --- /dev/null +++ b/2nd-gen/packages/core/components/infield-button/InfieldButton.base.ts @@ -0,0 +1,63 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you 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 REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { property } from 'lit/decorators.js'; + +import { ButtonBase } from '@spectrum-web-components/core/components/button'; +import { SpectrumElement } from '@spectrum-web-components/core/element/index.js'; + +import { + INFIELD_BUTTON_VALID_SIZES, + type InfieldButtonSize, +} from './InfieldButton.types.js'; + +/** + * Abstract base class for infield button components. Extends ButtonBase with a + * restricted size set, quiet treatment, and non-focusable shadow root. + * + * Infield buttons are always embedded inside a form field (e.g. number field). They are + * clickable via pointer only; they are not in the tab order and not independently focusable. + * All keyboard behavior is owned by the parent field, which also provides the visible + * focus ring. The parent field is expected to set the `size` attribute. + * + * @slot icon - Icon to display inside the button. + * + * @attribute {ElementSize} size - Size delegated from the parent field. Defaults to + * medium when no parent sets it, though standalone usage is not supported. + */ +export abstract class InfieldButtonBase extends ButtonBase { + // ──────────────────── + // API OVERRIDES + // ──────────────────── + + /** @internal */ + static override readonly VALID_SIZES: readonly InfieldButtonSize[] = + INFIELD_BUTTON_VALID_SIZES; + + // Infield buttons are clickable but never independently focusable. The parent field + // owns keyboard behavior and supplies the only focus ring. + static override shadowRootOptions: ShadowRootInit = { + ...SpectrumElement.shadowRootOptions, + delegatesFocus: false, + }; + + // ─────────────────── + // API ADDITIONS + // ─────────────────── + + /** + * Applies the quiet (no-border) visual treatment. Used when the infield button + * is part of a quiet field variant. + */ + @property({ type: Boolean, reflect: true }) + public quiet: boolean = false; +} diff --git a/2nd-gen/packages/core/components/infield-button/InfieldButton.types.ts b/2nd-gen/packages/core/components/infield-button/InfieldButton.types.ts new file mode 100644 index 00000000000..4f5acdf844b --- /dev/null +++ b/2nd-gen/packages/core/components/infield-button/InfieldButton.types.ts @@ -0,0 +1,30 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you 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 REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import type { ElementSize } from '@spectrum-web-components/core/mixins/index.js'; + +// ────────────────── +// SHARED +// ────────────────── + +export const INFIELD_BUTTON_VALID_SIZES = [ + 's', + 'm', + 'l', + 'xl', +] as const satisfies readonly ElementSize[]; + +// ────────────────── +// TYPES +// ────────────────── + +export type InfieldButtonSize = (typeof INFIELD_BUTTON_VALID_SIZES)[number]; diff --git a/2nd-gen/packages/core/components/infield-button/index.ts b/2nd-gen/packages/core/components/infield-button/index.ts new file mode 100644 index 00000000000..be350e0d02a --- /dev/null +++ b/2nd-gen/packages/core/components/infield-button/index.ts @@ -0,0 +1,13 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you 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 REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +export * from './InfieldButton.base.js'; +export * from './InfieldButton.types.js'; diff --git a/2nd-gen/packages/core/package.json b/2nd-gen/packages/core/package.json index 2b51e3bf58c..ddac4caed7e 100644 --- a/2nd-gen/packages/core/package.json +++ b/2nd-gen/packages/core/package.json @@ -51,6 +51,14 @@ "types": "./dist/components/button/index.d.ts", "import": "./dist/components/button/index.js" }, + "./components/infield-button": { + "types": "./dist/components/infield-button/index.d.ts", + "import": "./dist/components/infield-button/index.js" + }, + "./components/infield-button/index.js": { + "types": "./dist/components/infield-button/index.d.ts", + "import": "./dist/components/infield-button/index.js" + }, "./components/button-group": { "types": "./dist/components/button-group/index.d.ts", "import": "./dist/components/button-group/index.js" @@ -236,6 +244,12 @@ "components/button": [ "dist/components/button/index.d.ts" ], + "components/infield-button": [ + "dist/components/infield-button/index.d.ts" + ], + "components/infield-button/index.js": [ + "dist/components/infield-button/index.d.ts" + ], "components/button-group": [ "dist/components/button-group/index.d.ts" ], diff --git a/2nd-gen/packages/swc/components/infield-button/InfieldButton.ts b/2nd-gen/packages/swc/components/infield-button/InfieldButton.ts new file mode 100644 index 00000000000..0c10618206d --- /dev/null +++ b/2nd-gen/packages/swc/components/infield-button/InfieldButton.ts @@ -0,0 +1,71 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you 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 REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { CSSResultArray, html, TemplateResult } from 'lit'; +import { classMap } from 'lit/directives/class-map.js'; +import { ifDefined } from 'lit/directives/if-defined.js'; + +import { + INFIELD_BUTTON_VALID_SIZES, + InfieldButtonBase, + type InfieldButtonSize, +} from '@spectrum-web-components/core/components/infield-button'; + +import styles from './infield-button.css'; + +/** + * A compact icon button embedded inside a form field (e.g. number field increment/decrement + * control, search field clear button). Clickable via pointer only; not in the tab order. + * The parent field owns all keyboard behavior and provides the visible focus ring. + * + * @element swc-infield-button + * @since 2.0.0 + * + * @slot icon - Icon to display inside the button. + * + * @example + * + * + * + */ +export class InfieldButton extends InfieldButtonBase { + // ──────────────────── + // API OVERRIDES + // ──────────────────── + + /** @internal */ + static override readonly VALID_SIZES: readonly InfieldButtonSize[] = + INFIELD_BUTTON_VALID_SIZES; + + // ────────────────────────────── + // RENDERING & STYLING + // ────────────────────────────── + + public static override get styles(): CSSResultArray { + return [styles]; + } + + protected override render(): TemplateResult { + return html` + + `; + } +} diff --git a/2nd-gen/packages/swc/components/infield-button/index.ts b/2nd-gen/packages/swc/components/infield-button/index.ts new file mode 100644 index 00000000000..d0c1a91ab09 --- /dev/null +++ b/2nd-gen/packages/swc/components/infield-button/index.ts @@ -0,0 +1,12 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you 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 REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +export * from './InfieldButton.js'; diff --git a/2nd-gen/packages/swc/components/infield-button/infield-button.css b/2nd-gen/packages/swc/components/infield-button/infield-button.css new file mode 100644 index 00000000000..ac2b1612f1a --- /dev/null +++ b/2nd-gen/packages/swc/components/infield-button/infield-button.css @@ -0,0 +1,16 @@ +/* + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you 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 REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +/* Placeholder — full styles authored in Phase 5 */ +:host { + display: inline-flex; +} diff --git a/2nd-gen/packages/swc/components/infield-button/swc-infield-button.ts b/2nd-gen/packages/swc/components/infield-button/swc-infield-button.ts new file mode 100644 index 00000000000..e53632c52ab --- /dev/null +++ b/2nd-gen/packages/swc/components/infield-button/swc-infield-button.ts @@ -0,0 +1,22 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you 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 REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +import { defineElement } from '@spectrum-web-components/core/element/index.js'; + +import { InfieldButton } from './InfieldButton.js'; + +declare global { + interface HTMLElementTagNameMap { + 'swc-infield-button': InfieldButton; + } +} + +defineElement('swc-infield-button', InfieldButton);