import { css, html } from "lit"; import { classMap } from "lit/directives/class-map.js"; import { BaseElement } from "../../core/base-element.ts"; import { layoutSpacingUtilityStyles } from "../../styles/layout-spacing.ts"; // TODO(v2-token-migration): Migrate this component to component-level tokens, // matching the prior phase-1 token migration pattern. /** * CFGrid - CSS Grid layout component * * @element cf-grid * * @attr {string} columns - Number of columns (1-12) or custom template * @attr {string} rows - Number of rows or custom template * @attr {string} gap - Gap between items (0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24) * @attr {string} row-gap - Row gap (overrides gap) * @attr {string} column-gap - Column gap (overrides gap) * @attr {string} align - Align items (start, center, end, stretch) * @attr {string} justify - Justify items (start, center, end, stretch) * @attr {string} place - Place items shorthand (combines align and justify) * @attr {string} flow - Grid auto flow (row, column, dense, row-dense, column-dense) * @attr {string} padding - Padding around the grid * * @slot - Content to be laid out in a grid * * @example * *
Item 1
*
Item 2
*
Item 3
*
*/ export class CFGrid extends BaseElement { static override styles = [ layoutSpacingUtilityStyles, css` :host { display: block; container-type: inline-size; } .grid { display: grid; box-sizing: border-box; } /* Alignment */ .align-start { align-items: start; } .align-center { align-items: center; } .align-end { align-items: end; } .align-stretch { align-items: stretch; } /* Justification */ .justify-start { justify-items: start; } .justify-center { justify-items: center; } .justify-end { justify-items: end; } .justify-stretch { justify-items: stretch; } /* Place items */ .place-start { place-items: start; } .place-center { place-items: center; } .place-end { place-items: end; } .place-stretch { place-items: stretch; } /* Grid flow */ .flow-row { grid-auto-flow: row; } .flow-column { grid-auto-flow: column; } .flow-dense { grid-auto-flow: dense; } .flow-row-dense { grid-auto-flow: row dense; } .flow-column-dense { grid-auto-flow: column dense; } /* * Grid container breakpoints remain local for now. * variables.ts documents matching layout breakpoint tokens, but current * container query syntax requires literal values here. */ @container (min-width: 640px) { .grid-sm-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } .grid-sm-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } .grid-sm-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } .grid-sm-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } .grid-sm-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); } } @container (min-width: 768px) { .grid-md-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } .grid-md-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } .grid-md-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } .grid-md-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } .grid-md-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); } } @container (min-width: 1024px) { .grid-lg-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } .grid-lg-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } .grid-lg-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } .grid-lg-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } .grid-lg-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); } } `, ]; static override properties = { columns: { type: String }, rows: { type: String }, gap: { type: String }, rowGap: { type: String, attribute: "row-gap" }, columnGap: { type: String, attribute: "column-gap" }, align: { type: String }, justify: { type: String }, place: { type: String }, flow: { type: String }, padding: { type: String }, }; declare columns: string; declare rows: string; declare gap: string; declare rowGap: string; declare columnGap: string; declare align: string; declare justify: string; declare place: string; declare flow: string; declare padding: string; constructor() { super(); this.columns = "1"; this.rows = ""; this.gap = "0"; this.rowGap = ""; this.columnGap = ""; this.align = "stretch"; this.justify = "stretch"; this.place = ""; this.flow = "row"; this.padding = "0"; } private getGridTemplateColumns(): string { // Check if it's a number (1-12) const num = parseInt(this.columns); if (!isNaN(num) && num >= 1 && num <= 12) { return `repeat(${num}, minmax(0, 1fr))`; } // Otherwise, use as custom template return this.columns; } private getGridTemplateRows(): string { if (!this.rows) return ""; // Check if it's a number const num = parseInt(this.rows); if (!isNaN(num) && num >= 1) { return `repeat(${num}, minmax(0, 1fr))`; } // Otherwise, use as custom template return this.rows; } override render() { const classes: Record = { grid: true, [`p-${this.padding}`]: true, [`flow-${this.flow}`]: true, }; // Add place or align/justify classes if (this.place) { classes[`place-${this.place}`] = true; } else { if (this.align) classes[`align-${this.align}`] = true; if (this.justify) classes[`justify-${this.justify}`] = true; } // Add gap classes if (this.rowGap || this.columnGap) { if (this.rowGap) classes[`row-gap-${this.rowGap}`] = true; if (this.columnGap) classes[`col-gap-${this.columnGap}`] = true; } else { classes[`gap-${this.gap}`] = true; } const gridStyle = { "grid-template-columns": this.getGridTemplateColumns(), ...(this.rows && { "grid-template-rows": this.getGridTemplateRows() }), }; return html`
`; } }