import {classMap} from 'lit/directives/class-map.js'; import {type CSSResultGroup, html, unsafeCSS} from 'lit'; import {HasSlotController} from '../../internal/slot'; import {property, query} from 'lit/decorators.js'; import ZincElement from '../../internal/zinc-element'; import styles from './input-group.scss'; /** * @summary A wrapper component that groups inputs and selects visually. * @documentation https://zinc.style/components/input-group * @status experimental * @since 1.0 * * @slot - The default slot for inputs and selects. * @slot label - The input group's label. Alternatively, you can use the `label` attribute. * * @csspart base - The component's base wrapper. * @csspart form-control - The form control wrapper. * @csspart form-control-label - The label's wrapper. * @csspart form-control-input - The input group container. */ export default class ZnInputGroup extends ZincElement { static styles: CSSResultGroup = unsafeCSS(styles); private readonly hasSlotController = new HasSlotController(this, 'label'); @query('slot:not([name])') defaultSlot: HTMLSlotElement; /** The input group's label. If you need to display HTML, use the `label` slot. */ @property() label = ''; /** Adds a gap between the grouped elements, preserving individual border radii. Use `sm`, `md`, or `lg`. */ @property() gap: 'sm' | 'md' | 'lg'; private handleSlotChange() { const slottedElements = [...this.defaultSlot.assignedElements({flatten: true})] as HTMLElement[]; // Filter for inputs, selects, and buttons const supportedTags = ['ZN-INPUT', 'ZN-SELECT', 'ZN-BUTTON']; const controls = slottedElements.filter(el => supportedTags.includes(el.tagName)); // Buttons are styled independently — only inputs/selects participate in joined positioning const joinedControls = controls.filter(el => el.tagName !== 'ZN-BUTTON'); controls.forEach(el => { el.toggleAttribute('data-zn-input-group__input', true); if (el.tagName === 'ZN-BUTTON') { el.toggleAttribute('data-zn-input-group__input--first', false); el.toggleAttribute('data-zn-input-group__input--inner', false); el.toggleAttribute('data-zn-input-group__input--last', false); } else { const index = joinedControls.indexOf(el); el.toggleAttribute('data-zn-input-group__input--first', index === 0); el.toggleAttribute('data-zn-input-group__input--inner', index > 0 && index < joinedControls.length - 1); el.toggleAttribute('data-zn-input-group__input--last', index === joinedControls.length - 1); } }); } render() { const hasLabelSlot = this.hasSlotController.test('label'); const hasLabel = this.label ? true : hasLabelSlot; return html`