diff --git a/packages/renderer-parity.json b/packages/renderer-parity.json index 2238e503e..3ae60bd0e 100644 --- a/packages/renderer-parity.json +++ b/packages/renderer-parity.json @@ -134,6 +134,8 @@ "artisanpack/accordion-title", "artisanpack/accordion-body", "artisanpack/tabs", - "artisanpack/tab-section" + "artisanpack/tab-section", + "artisanpack/grid", + "artisanpack/grid-item" ] } diff --git a/packages/visual-editor-renderer-blade/resources/assets/frontend/grid.css b/packages/visual-editor-renderer-blade/resources/assets/frontend/grid.css new file mode 100644 index 000000000..78f27aacc --- /dev/null +++ b/packages/visual-editor-renderer-blade/resources/assets/frontend/grid.css @@ -0,0 +1,312 @@ +/** + * Grid family styles (#498). + * + * Loaded in the editor via `blocks/{grid,grid-item}/index.ts`. On the + * public frontend the Blade renderer serves an identical copy from + * `packages/visual-editor-renderer-blade/resources/assets/frontend/grid.css`, + * wired up by ``. React and Vue renderers emit + * the same class names so a single stylesheet covers all three. + * + * Per-breakpoint column count and item span are encoded as static + * classes — `ap-grid-has-N-{bp}-columns`, `ap-grid-item-span-N-{bp}-columns`, + * `ap-grid-item-span-N-{bp}-row` — and emitted by the renderers on each + * instance. Mobile-first cascade: base rules first, then each registered + * Tailwind breakpoint (sm 640, md 768, lg 1024, xl 1280, 2xl 1536) in + * ascending min-width order, so a later breakpoint's class overrides the + * earlier one for the wider viewport. + */ + +.ap-grid { + display: grid; + grid-template-columns: repeat(1, minmax(0, 1fr)); + height: 100%; +} + +.ap-grid-item { + display: flex; + flex-grow: 1; + min-width: 0; + margin-block-start: 0; + margin-block-end: 0; +} + +.ap-grid-item > * { + width: 100%; +} + +.ap-grid-item.ap-grid-item-layout-normal { + align-items: stretch; + justify-content: flex-start; +} + +.ap-grid-item.ap-grid-item-layout-equal { + align-items: stretch; + justify-content: space-between; + flex-direction: column; +} + +.ap-grid-item.ap-grid-item-layout-center { + align-items: center; + justify-content: center; + flex-direction: column; +} + +.ap-grid-item.ap-grid-item-layout-bottom { + flex-direction: column; + justify-content: flex-end; +} + +.ap-grid-item.ap-grid-item-layout-last-bottom { + flex-direction: column; + justify-content: space-between; +} + +/* === base (no media query) === */ +.ap-grid.ap-grid-has-1-base-columns { grid-template-columns: repeat(1, minmax(0, 1fr)); } +.ap-grid.ap-grid-has-2-base-columns { grid-template-columns: repeat(2, minmax(0, 1fr)); } +.ap-grid.ap-grid-has-3-base-columns { grid-template-columns: repeat(3, minmax(0, 1fr)); } +.ap-grid.ap-grid-has-4-base-columns { grid-template-columns: repeat(4, minmax(0, 1fr)); } +.ap-grid.ap-grid-has-5-base-columns { grid-template-columns: repeat(5, minmax(0, 1fr)); } +.ap-grid.ap-grid-has-6-base-columns { grid-template-columns: repeat(6, minmax(0, 1fr)); } +.ap-grid.ap-grid-has-7-base-columns { grid-template-columns: repeat(7, minmax(0, 1fr)); } +.ap-grid.ap-grid-has-8-base-columns { grid-template-columns: repeat(8, minmax(0, 1fr)); } +.ap-grid.ap-grid-has-9-base-columns { grid-template-columns: repeat(9, minmax(0, 1fr)); } +.ap-grid.ap-grid-has-10-base-columns { grid-template-columns: repeat(10, minmax(0, 1fr)); } +.ap-grid.ap-grid-has-11-base-columns { grid-template-columns: repeat(11, minmax(0, 1fr)); } +.ap-grid.ap-grid-has-12-base-columns { grid-template-columns: repeat(12, minmax(0, 1fr)); } + +.ap-grid-item.ap-grid-item-span-1-base-columns { grid-column: span 1; } +.ap-grid-item.ap-grid-item-span-2-base-columns { grid-column: span 2; } +.ap-grid-item.ap-grid-item-span-3-base-columns { grid-column: span 3; } +.ap-grid-item.ap-grid-item-span-4-base-columns { grid-column: span 4; } +.ap-grid-item.ap-grid-item-span-5-base-columns { grid-column: span 5; } +.ap-grid-item.ap-grid-item-span-6-base-columns { grid-column: span 6; } +.ap-grid-item.ap-grid-item-span-7-base-columns { grid-column: span 7; } +.ap-grid-item.ap-grid-item-span-8-base-columns { grid-column: span 8; } +.ap-grid-item.ap-grid-item-span-9-base-columns { grid-column: span 9; } +.ap-grid-item.ap-grid-item-span-10-base-columns { grid-column: span 10; } +.ap-grid-item.ap-grid-item-span-11-base-columns { grid-column: span 11; } +.ap-grid-item.ap-grid-item-span-12-base-columns { grid-column: span 12; } + +.ap-grid-item.ap-grid-item-span-1-base-row { grid-row: span 1; } +.ap-grid-item.ap-grid-item-span-2-base-row { grid-row: span 2; } +.ap-grid-item.ap-grid-item-span-3-base-row { grid-row: span 3; } +.ap-grid-item.ap-grid-item-span-4-base-row { grid-row: span 4; } +.ap-grid-item.ap-grid-item-span-5-base-row { grid-row: span 5; } +.ap-grid-item.ap-grid-item-span-6-base-row { grid-row: span 6; } +.ap-grid-item.ap-grid-item-span-7-base-row { grid-row: span 7; } +.ap-grid-item.ap-grid-item-span-8-base-row { grid-row: span 8; } +.ap-grid-item.ap-grid-item-span-9-base-row { grid-row: span 9; } +.ap-grid-item.ap-grid-item-span-10-base-row { grid-row: span 10; } +.ap-grid-item.ap-grid-item-span-11-base-row { grid-row: span 11; } +.ap-grid-item.ap-grid-item-span-12-base-row { grid-row: span 12; } + +/* === sm: 640px === */ +@media (min-width: 640px) { + .ap-grid.ap-grid-has-1-sm-columns { grid-template-columns: repeat(1, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-2-sm-columns { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-3-sm-columns { grid-template-columns: repeat(3, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-4-sm-columns { grid-template-columns: repeat(4, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-5-sm-columns { grid-template-columns: repeat(5, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-6-sm-columns { grid-template-columns: repeat(6, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-7-sm-columns { grid-template-columns: repeat(7, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-8-sm-columns { grid-template-columns: repeat(8, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-9-sm-columns { grid-template-columns: repeat(9, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-10-sm-columns { grid-template-columns: repeat(10, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-11-sm-columns { grid-template-columns: repeat(11, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-12-sm-columns { grid-template-columns: repeat(12, minmax(0, 1fr)); } + + .ap-grid-item.ap-grid-item-span-1-sm-columns { grid-column: span 1; } + .ap-grid-item.ap-grid-item-span-2-sm-columns { grid-column: span 2; } + .ap-grid-item.ap-grid-item-span-3-sm-columns { grid-column: span 3; } + .ap-grid-item.ap-grid-item-span-4-sm-columns { grid-column: span 4; } + .ap-grid-item.ap-grid-item-span-5-sm-columns { grid-column: span 5; } + .ap-grid-item.ap-grid-item-span-6-sm-columns { grid-column: span 6; } + .ap-grid-item.ap-grid-item-span-7-sm-columns { grid-column: span 7; } + .ap-grid-item.ap-grid-item-span-8-sm-columns { grid-column: span 8; } + .ap-grid-item.ap-grid-item-span-9-sm-columns { grid-column: span 9; } + .ap-grid-item.ap-grid-item-span-10-sm-columns { grid-column: span 10; } + .ap-grid-item.ap-grid-item-span-11-sm-columns { grid-column: span 11; } + .ap-grid-item.ap-grid-item-span-12-sm-columns { grid-column: span 12; } + + .ap-grid-item.ap-grid-item-span-1-sm-row { grid-row: span 1; } + .ap-grid-item.ap-grid-item-span-2-sm-row { grid-row: span 2; } + .ap-grid-item.ap-grid-item-span-3-sm-row { grid-row: span 3; } + .ap-grid-item.ap-grid-item-span-4-sm-row { grid-row: span 4; } + .ap-grid-item.ap-grid-item-span-5-sm-row { grid-row: span 5; } + .ap-grid-item.ap-grid-item-span-6-sm-row { grid-row: span 6; } + .ap-grid-item.ap-grid-item-span-7-sm-row { grid-row: span 7; } + .ap-grid-item.ap-grid-item-span-8-sm-row { grid-row: span 8; } + .ap-grid-item.ap-grid-item-span-9-sm-row { grid-row: span 9; } + .ap-grid-item.ap-grid-item-span-10-sm-row { grid-row: span 10; } + .ap-grid-item.ap-grid-item-span-11-sm-row { grid-row: span 11; } + .ap-grid-item.ap-grid-item-span-12-sm-row { grid-row: span 12; } +} + +/* === md: 768px === */ +@media (min-width: 768px) { + .ap-grid.ap-grid-has-1-md-columns { grid-template-columns: repeat(1, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-2-md-columns { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-3-md-columns { grid-template-columns: repeat(3, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-4-md-columns { grid-template-columns: repeat(4, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-5-md-columns { grid-template-columns: repeat(5, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-6-md-columns { grid-template-columns: repeat(6, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-7-md-columns { grid-template-columns: repeat(7, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-8-md-columns { grid-template-columns: repeat(8, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-9-md-columns { grid-template-columns: repeat(9, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-10-md-columns { grid-template-columns: repeat(10, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-11-md-columns { grid-template-columns: repeat(11, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-12-md-columns { grid-template-columns: repeat(12, minmax(0, 1fr)); } + + .ap-grid-item.ap-grid-item-span-1-md-columns { grid-column: span 1; } + .ap-grid-item.ap-grid-item-span-2-md-columns { grid-column: span 2; } + .ap-grid-item.ap-grid-item-span-3-md-columns { grid-column: span 3; } + .ap-grid-item.ap-grid-item-span-4-md-columns { grid-column: span 4; } + .ap-grid-item.ap-grid-item-span-5-md-columns { grid-column: span 5; } + .ap-grid-item.ap-grid-item-span-6-md-columns { grid-column: span 6; } + .ap-grid-item.ap-grid-item-span-7-md-columns { grid-column: span 7; } + .ap-grid-item.ap-grid-item-span-8-md-columns { grid-column: span 8; } + .ap-grid-item.ap-grid-item-span-9-md-columns { grid-column: span 9; } + .ap-grid-item.ap-grid-item-span-10-md-columns { grid-column: span 10; } + .ap-grid-item.ap-grid-item-span-11-md-columns { grid-column: span 11; } + .ap-grid-item.ap-grid-item-span-12-md-columns { grid-column: span 12; } + + .ap-grid-item.ap-grid-item-span-1-md-row { grid-row: span 1; } + .ap-grid-item.ap-grid-item-span-2-md-row { grid-row: span 2; } + .ap-grid-item.ap-grid-item-span-3-md-row { grid-row: span 3; } + .ap-grid-item.ap-grid-item-span-4-md-row { grid-row: span 4; } + .ap-grid-item.ap-grid-item-span-5-md-row { grid-row: span 5; } + .ap-grid-item.ap-grid-item-span-6-md-row { grid-row: span 6; } + .ap-grid-item.ap-grid-item-span-7-md-row { grid-row: span 7; } + .ap-grid-item.ap-grid-item-span-8-md-row { grid-row: span 8; } + .ap-grid-item.ap-grid-item-span-9-md-row { grid-row: span 9; } + .ap-grid-item.ap-grid-item-span-10-md-row { grid-row: span 10; } + .ap-grid-item.ap-grid-item-span-11-md-row { grid-row: span 11; } + .ap-grid-item.ap-grid-item-span-12-md-row { grid-row: span 12; } +} + +/* === lg: 1024px === */ +@media (min-width: 1024px) { + .ap-grid.ap-grid-has-1-lg-columns { grid-template-columns: repeat(1, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-2-lg-columns { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-3-lg-columns { grid-template-columns: repeat(3, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-4-lg-columns { grid-template-columns: repeat(4, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-5-lg-columns { grid-template-columns: repeat(5, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-6-lg-columns { grid-template-columns: repeat(6, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-7-lg-columns { grid-template-columns: repeat(7, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-8-lg-columns { grid-template-columns: repeat(8, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-9-lg-columns { grid-template-columns: repeat(9, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-10-lg-columns { grid-template-columns: repeat(10, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-11-lg-columns { grid-template-columns: repeat(11, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-12-lg-columns { grid-template-columns: repeat(12, minmax(0, 1fr)); } + + .ap-grid-item.ap-grid-item-span-1-lg-columns { grid-column: span 1; } + .ap-grid-item.ap-grid-item-span-2-lg-columns { grid-column: span 2; } + .ap-grid-item.ap-grid-item-span-3-lg-columns { grid-column: span 3; } + .ap-grid-item.ap-grid-item-span-4-lg-columns { grid-column: span 4; } + .ap-grid-item.ap-grid-item-span-5-lg-columns { grid-column: span 5; } + .ap-grid-item.ap-grid-item-span-6-lg-columns { grid-column: span 6; } + .ap-grid-item.ap-grid-item-span-7-lg-columns { grid-column: span 7; } + .ap-grid-item.ap-grid-item-span-8-lg-columns { grid-column: span 8; } + .ap-grid-item.ap-grid-item-span-9-lg-columns { grid-column: span 9; } + .ap-grid-item.ap-grid-item-span-10-lg-columns { grid-column: span 10; } + .ap-grid-item.ap-grid-item-span-11-lg-columns { grid-column: span 11; } + .ap-grid-item.ap-grid-item-span-12-lg-columns { grid-column: span 12; } + + .ap-grid-item.ap-grid-item-span-1-lg-row { grid-row: span 1; } + .ap-grid-item.ap-grid-item-span-2-lg-row { grid-row: span 2; } + .ap-grid-item.ap-grid-item-span-3-lg-row { grid-row: span 3; } + .ap-grid-item.ap-grid-item-span-4-lg-row { grid-row: span 4; } + .ap-grid-item.ap-grid-item-span-5-lg-row { grid-row: span 5; } + .ap-grid-item.ap-grid-item-span-6-lg-row { grid-row: span 6; } + .ap-grid-item.ap-grid-item-span-7-lg-row { grid-row: span 7; } + .ap-grid-item.ap-grid-item-span-8-lg-row { grid-row: span 8; } + .ap-grid-item.ap-grid-item-span-9-lg-row { grid-row: span 9; } + .ap-grid-item.ap-grid-item-span-10-lg-row { grid-row: span 10; } + .ap-grid-item.ap-grid-item-span-11-lg-row { grid-row: span 11; } + .ap-grid-item.ap-grid-item-span-12-lg-row { grid-row: span 12; } +} + +/* === xl: 1280px === */ +@media (min-width: 1280px) { + .ap-grid.ap-grid-has-1-xl-columns { grid-template-columns: repeat(1, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-2-xl-columns { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-3-xl-columns { grid-template-columns: repeat(3, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-4-xl-columns { grid-template-columns: repeat(4, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-5-xl-columns { grid-template-columns: repeat(5, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-6-xl-columns { grid-template-columns: repeat(6, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-7-xl-columns { grid-template-columns: repeat(7, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-8-xl-columns { grid-template-columns: repeat(8, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-9-xl-columns { grid-template-columns: repeat(9, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-10-xl-columns { grid-template-columns: repeat(10, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-11-xl-columns { grid-template-columns: repeat(11, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-12-xl-columns { grid-template-columns: repeat(12, minmax(0, 1fr)); } + + .ap-grid-item.ap-grid-item-span-1-xl-columns { grid-column: span 1; } + .ap-grid-item.ap-grid-item-span-2-xl-columns { grid-column: span 2; } + .ap-grid-item.ap-grid-item-span-3-xl-columns { grid-column: span 3; } + .ap-grid-item.ap-grid-item-span-4-xl-columns { grid-column: span 4; } + .ap-grid-item.ap-grid-item-span-5-xl-columns { grid-column: span 5; } + .ap-grid-item.ap-grid-item-span-6-xl-columns { grid-column: span 6; } + .ap-grid-item.ap-grid-item-span-7-xl-columns { grid-column: span 7; } + .ap-grid-item.ap-grid-item-span-8-xl-columns { grid-column: span 8; } + .ap-grid-item.ap-grid-item-span-9-xl-columns { grid-column: span 9; } + .ap-grid-item.ap-grid-item-span-10-xl-columns { grid-column: span 10; } + .ap-grid-item.ap-grid-item-span-11-xl-columns { grid-column: span 11; } + .ap-grid-item.ap-grid-item-span-12-xl-columns { grid-column: span 12; } + + .ap-grid-item.ap-grid-item-span-1-xl-row { grid-row: span 1; } + .ap-grid-item.ap-grid-item-span-2-xl-row { grid-row: span 2; } + .ap-grid-item.ap-grid-item-span-3-xl-row { grid-row: span 3; } + .ap-grid-item.ap-grid-item-span-4-xl-row { grid-row: span 4; } + .ap-grid-item.ap-grid-item-span-5-xl-row { grid-row: span 5; } + .ap-grid-item.ap-grid-item-span-6-xl-row { grid-row: span 6; } + .ap-grid-item.ap-grid-item-span-7-xl-row { grid-row: span 7; } + .ap-grid-item.ap-grid-item-span-8-xl-row { grid-row: span 8; } + .ap-grid-item.ap-grid-item-span-9-xl-row { grid-row: span 9; } + .ap-grid-item.ap-grid-item-span-10-xl-row { grid-row: span 10; } + .ap-grid-item.ap-grid-item-span-11-xl-row { grid-row: span 11; } + .ap-grid-item.ap-grid-item-span-12-xl-row { grid-row: span 12; } +} + +/* === 2xl: 1536px === */ +@media (min-width: 1536px) { + .ap-grid.ap-grid-has-1-2xl-columns { grid-template-columns: repeat(1, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-2-2xl-columns { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-3-2xl-columns { grid-template-columns: repeat(3, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-4-2xl-columns { grid-template-columns: repeat(4, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-5-2xl-columns { grid-template-columns: repeat(5, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-6-2xl-columns { grid-template-columns: repeat(6, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-7-2xl-columns { grid-template-columns: repeat(7, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-8-2xl-columns { grid-template-columns: repeat(8, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-9-2xl-columns { grid-template-columns: repeat(9, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-10-2xl-columns { grid-template-columns: repeat(10, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-11-2xl-columns { grid-template-columns: repeat(11, minmax(0, 1fr)); } + .ap-grid.ap-grid-has-12-2xl-columns { grid-template-columns: repeat(12, minmax(0, 1fr)); } + + .ap-grid-item.ap-grid-item-span-1-2xl-columns { grid-column: span 1; } + .ap-grid-item.ap-grid-item-span-2-2xl-columns { grid-column: span 2; } + .ap-grid-item.ap-grid-item-span-3-2xl-columns { grid-column: span 3; } + .ap-grid-item.ap-grid-item-span-4-2xl-columns { grid-column: span 4; } + .ap-grid-item.ap-grid-item-span-5-2xl-columns { grid-column: span 5; } + .ap-grid-item.ap-grid-item-span-6-2xl-columns { grid-column: span 6; } + .ap-grid-item.ap-grid-item-span-7-2xl-columns { grid-column: span 7; } + .ap-grid-item.ap-grid-item-span-8-2xl-columns { grid-column: span 8; } + .ap-grid-item.ap-grid-item-span-9-2xl-columns { grid-column: span 9; } + .ap-grid-item.ap-grid-item-span-10-2xl-columns { grid-column: span 10; } + .ap-grid-item.ap-grid-item-span-11-2xl-columns { grid-column: span 11; } + .ap-grid-item.ap-grid-item-span-12-2xl-columns { grid-column: span 12; } + + .ap-grid-item.ap-grid-item-span-1-2xl-row { grid-row: span 1; } + .ap-grid-item.ap-grid-item-span-2-2xl-row { grid-row: span 2; } + .ap-grid-item.ap-grid-item-span-3-2xl-row { grid-row: span 3; } + .ap-grid-item.ap-grid-item-span-4-2xl-row { grid-row: span 4; } + .ap-grid-item.ap-grid-item-span-5-2xl-row { grid-row: span 5; } + .ap-grid-item.ap-grid-item-span-6-2xl-row { grid-row: span 6; } + .ap-grid-item.ap-grid-item-span-7-2xl-row { grid-row: span 7; } + .ap-grid-item.ap-grid-item-span-8-2xl-row { grid-row: span 8; } + .ap-grid-item.ap-grid-item-span-9-2xl-row { grid-row: span 9; } + .ap-grid-item.ap-grid-item-span-10-2xl-row { grid-row: span 10; } + .ap-grid-item.ap-grid-item-span-11-2xl-row { grid-row: span 11; } + .ap-grid-item.ap-grid-item-span-12-2xl-row { grid-row: span 12; } +} diff --git a/packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/grid-item.blade.php b/packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/grid-item.blade.php new file mode 100644 index 000000000..4eb140077 --- /dev/null +++ b/packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/grid-item.blade.php @@ -0,0 +1,75 @@ +@php + use ArtisanPackUI\VisualEditor\Responsive\BreakpointRegistry; + use ArtisanPackUI\VisualEditorRendererBlade\Support\BlockSupports; + + $validInnerLayouts = [ 'normal', 'equal', 'center', 'bottom', 'last-bottom' ]; + + $clampSpan = static function ( $value, int $fallback = 1 ): int { + $int = is_numeric( $value ) ? (int) $value : $fallback; + if ( $int < 1 ) { + return 1; + } + if ( $int > 12 ) { + return 12; + } + return $int; + }; + + $innerLayoutRaw = is_string( $attributes['innerLayout'] ?? null ) + ? $attributes['innerLayout'] + : 'normal'; + $innerLayout = in_array( $innerLayoutRaw, $validInnerLayouts, true ) + ? $innerLayoutRaw + : 'normal'; + + $baseColumnSpan = $clampSpan( $attributes['gridColumnSpan'] ?? null, 1 ); + $baseRowSpan = $clampSpan( $attributes['gridRowSpan'] ?? null, 1 ); + + $spanClasses = [ + 'ap-grid-item-span-' . $baseColumnSpan . '-base-columns', + 'ap-grid-item-span-' . $baseRowSpan . '-base-row', + ]; + + $registry = app( BreakpointRegistry::class ); + + $collectOverrides = static function ( $overrides, string $suffix, int $base, callable $clamp ) use ( $registry ): array { + $classes = []; + + if ( ! is_array( $overrides ) ) { + return $classes; + } + + foreach ( $overrides as $bp => $value ) { + if ( BreakpointRegistry::BASE_KEY === $bp ) { + continue; + } + if ( ! is_numeric( $value ) ) { + continue; + } + if ( null === $registry->get( (string) $bp ) ) { + continue; + } + + $classes[] = 'ap-grid-item-span-' . $clamp( $value, $base ) . '-' . $bp . '-' . $suffix; + } + + return $classes; + }; + + $spanClasses = array_merge( + $spanClasses, + $collectOverrides( $attributes['responsive']['gridColumnSpan'] ?? null, 'columns', $baseColumnSpan, $clampSpan ), + $collectOverrides( $attributes['responsive']['gridRowSpan'] ?? null, 'row', $baseRowSpan, $clampSpan ), + ); + + $baseClasses = array_merge( + [ + 'ap-grid-item', + 'ap-grid-item-layout-' . $innerLayout, + ], + $spanClasses, + ); +@endphp + + {!! $innerBlocksHtml !!} + diff --git a/packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/grid.blade.php b/packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/grid.blade.php new file mode 100644 index 000000000..34dc7f014 --- /dev/null +++ b/packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/grid.blade.php @@ -0,0 +1,48 @@ +@php + use ArtisanPackUI\VisualEditor\Responsive\BreakpointRegistry; + use ArtisanPackUI\VisualEditorRendererBlade\Support\BlockSupports; + + $clampColumns = static function ( $value, int $fallback ): int { + $int = is_numeric( $value ) ? (int) $value : $fallback; + if ( $int < 1 ) { + return 1; + } + if ( $int > 12 ) { + return 12; + } + return $int; + }; + + $baseColumns = $clampColumns( $attributes['numColumns'] ?? null, 4 ); + + // Per-breakpoint overrides. Mobile-first CSS cascade handles the rest: + // emit `ap-grid-has-N-{bp}-columns` for every breakpoint that carries + // its own value. Ascending min-width ordering in grid.css means a + // later breakpoint's class wins for the wider viewport. + $breakpointClasses = [ 'ap-grid-has-' . $baseColumns . '-base-columns' ]; + + $responsiveColumns = $attributes['responsive']['numColumns'] ?? []; + + if ( is_array( $responsiveColumns ) ) { + $registry = app( BreakpointRegistry::class ); + + foreach ( $responsiveColumns as $bp => $value ) { + if ( BreakpointRegistry::BASE_KEY === $bp ) { + continue; + } + if ( ! is_numeric( $value ) ) { + continue; + } + if ( null === $registry->get( (string) $bp ) ) { + continue; + } + + $breakpointClasses[] = 'ap-grid-has-' . $clampColumns( $value, $baseColumns ) . '-' . $bp . '-columns'; + } + } + + $baseClasses = array_merge( [ 'ap-grid' ], $breakpointClasses ); +@endphp + + {!! $innerBlocksHtml !!} + diff --git a/packages/visual-editor-renderer-blade/resources/views/components/blocks-styles.blade.php b/packages/visual-editor-renderer-blade/resources/views/components/blocks-styles.blade.php index 90238212d..ab7342640 100644 --- a/packages/visual-editor-renderer-blade/resources/views/components/blocks-styles.blade.php +++ b/packages/visual-editor-renderer-blade/resources/views/components/blocks-styles.blade.php @@ -7,6 +7,10 @@ @endif +{{-- Grid family CSS is layout-only (no interactivity script), so it + loads independently of $emitInteractive. Hosts that opt out of + accordion/tabs interactivity still want grid blocks to render. --}} + @if( '' !== $themeTokensCss ) @endif diff --git a/packages/visual-editor-renderer-blade/src/View/Components/BlocksStylesComponent.php b/packages/visual-editor-renderer-blade/src/View/Components/BlocksStylesComponent.php index 678f346aa..de861b99a 100644 --- a/packages/visual-editor-renderer-blade/src/View/Components/BlocksStylesComponent.php +++ b/packages/visual-editor-renderer-blade/src/View/Components/BlocksStylesComponent.php @@ -61,6 +61,8 @@ class BlocksStylesComponent extends Component public string $tabsStyleHref; + public string $gridStyleHref; + public string $interactivityScriptSrc; public bool $emitBlockLibrary; @@ -93,6 +95,7 @@ public function __construct( $this->themeHref = $base . '/theme.css'; $this->accordionStyleHref = $base . '/frontend/accordion.css'; $this->tabsStyleHref = $base . '/frontend/tabs.css'; + $this->gridStyleHref = $base . '/frontend/grid.css'; $this->interactivityScriptSrc = $base . '/frontend/interactivity.js'; $this->emitBlockLibrary = $bundle; $this->emitInteractive = $interactive; diff --git a/packages/visual-editor-renderer-blade/tests/Feature/Responsive/GridResponsiveRenderTest.php b/packages/visual-editor-renderer-blade/tests/Feature/Responsive/GridResponsiveRenderTest.php new file mode 100644 index 000000000..d65541fa2 --- /dev/null +++ b/packages/visual-editor-renderer-blade/tests/Feature/Responsive/GridResponsiveRenderTest.php @@ -0,0 +1,85 @@ +reset(); +} ); + +it( 'emits per-breakpoint grid column classes on the wrapper from attributes.responsive.numColumns', function () { + $tree = [ + [ + 'clientId' => 'grid-1', + 'name' => 'artisanpack/grid', + 'attributes' => [ + 'numColumns' => 1, + 'responsive' => [ + 'numColumns' => [ + 'md' => 2, + 'lg' => 4, + ], + ], + ], + 'innerBlocks' => [], + ], + ]; + + $rendered = $this->stripGlobalStyles( Blade::render( '', [ 'tree' => $tree ] ) ); + + expect( $rendered )->toContain( 'ap-grid-has-1-base-columns' ) + ->and( $rendered )->toContain( 'ap-grid-has-2-md-columns' ) + ->and( $rendered )->toContain( 'ap-grid-has-4-lg-columns' ) + // Static-class approach: no accumulated