Skip to content

Commit d3c33af

Browse files
committed
Bug 2053264 - Newtab Hide section layout cards that don't fill their row r=ini
Differential Revision: https://phabricator.services.mozilla.com/D310953
1 parent c5e2e5f commit d3c33af

6 files changed

Lines changed: 285 additions & 2 deletions

File tree

browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/CardSections/CardSections.jsx

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,51 @@ const CURATED_RECOMMENDATIONS_FEED_URL =
5555
// Divides evenly by 2, 3, and 4 to avoid orphan cards in any column layout.
5656
const DEFAULT_MAX_TILES = 12;
5757

58+
// Each card's footprint in grid units, sized so a medium reads as the square it
59+
// is: a small is half a medium (2x1), a large is two mediums wide (4x2). A grid
60+
// column is 2 units wide, so a full row spans columnCount * 2 units.
61+
const CARD_SIZE = {
62+
small: { width: 2, height: 1 },
63+
medium: { width: 2, height: 2 },
64+
large: { width: 4, height: 2 },
65+
};
66+
67+
const sizeOf = tile => CARD_SIZE[tile.size] ?? CARD_SIZE.medium;
68+
69+
// Return the tileIndexes that fall into an incomplete final row at this
70+
// breakpoint. These are the orphan cards to hide.
71+
function getOrphanTileIndexes(tiles, columnCount) {
72+
const rowWidth = columnCount * 2; // a grid column is 2 units wide
73+
let currentRow = []; // tile indexes in the row we're filling
74+
let filled = 0; // units used in this row, including tall cards from above
75+
let carry = 0; // units this row's tall cards reserve in the next row
76+
77+
// Walks each tile, filling currentRow until the row is filled,
78+
// then clears it and goes to the next row.
79+
// At the end if currentRow is not empty, we have a remainder.
80+
tiles.forEach((tile, index) => {
81+
const { width, height } = sizeOf(tile);
82+
currentRow.push(index);
83+
filled += width;
84+
if (height > 1) {
85+
carry += width;
86+
}
87+
88+
// This row is complete, we can reset and keep looking.
89+
if (filled >= rowWidth) {
90+
currentRow = [];
91+
filled = carry; // next row starts seeded by tall cards from above
92+
carry = 0;
93+
// The carry alone filled the whole next row (all-tall row), so reset it.
94+
if (filled >= rowWidth) {
95+
filled = 0;
96+
}
97+
}
98+
});
99+
100+
return new Set(currentRow);
101+
}
102+
58103
function getLayoutData(responsiveLayouts, index) {
59104
let layoutData = {
60105
classNames: [],
@@ -64,8 +109,12 @@ function getLayoutData(responsiveLayouts, index) {
64109
};
65110

66111
responsiveLayouts.forEach(layout => {
112+
const orphanTiles = getOrphanTileIndexes(layout.tiles, layout.columnCount);
67113
layout.tiles.forEach((tile, tileIndex) => {
68114
if (tile.position === index) {
115+
if (orphanTiles.has(tileIndex)) {
116+
layoutData.classNames.push(`col-${layout.columnCount}-hidden`);
117+
}
69118
layoutData.classNames.push(`col-${layout.columnCount}-${tile.size}`);
70119
layoutData.classNames.push(
71120
`col-${layout.columnCount}-position-${tileIndex}`
@@ -834,4 +883,4 @@ function CardSections({
834883
);
835884
}
836885

837-
export { CardSections };
886+
export { CardSections, getOrphanTileIndexes };

browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/CardSections/_CardSections.scss

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,10 @@
536536
}
537537
}
538538

539+
.col-1-hidden {
540+
display: none;
541+
}
542+
539543
.col-1-small {
540544
@include section-card-small;
541545
}
@@ -572,6 +576,10 @@
572576
}
573577
}
574578

579+
.col-2-hidden {
580+
display: none;
581+
}
582+
575583
.col-2-small {
576584
@include section-card-small;
577585
}
@@ -636,6 +644,10 @@
636644
}
637645
}
638646

647+
.col-3-hidden {
648+
display: none;
649+
}
650+
639651
.col-3-small {
640652
@include section-card-small;
641653
}
@@ -700,6 +712,10 @@
700712
}
701713
}
702714

715+
.col-4-hidden {
716+
display: none;
717+
}
718+
703719
.col-4-small {
704720
@include section-card-small;
705721
}
@@ -770,6 +786,8 @@
770786
.col-1-position-#{$i} { order: $i; }
771787
}
772788

789+
.col-1-hidden { display: none; }
790+
773791
.col-1-small {
774792
@include section-card-small;
775793
}
@@ -796,6 +814,8 @@
796814
.col-2-position-#{$i} { order: $i; }
797815
}
798816

817+
.col-2-hidden { display: none; }
818+
799819
.col-2-small {
800820
@include section-card-small;
801821
}
@@ -828,6 +848,8 @@
828848
.col-3-position-#{$i} { order: $i; }
829849
}
830850

851+
.col-3-hidden { display: none; }
852+
831853
.col-3-small {
832854
@include section-card-small;
833855
}
@@ -860,6 +882,8 @@
860882
.col-4-position-#{$i} { order: $i; }
861883
}
862884

885+
.col-4-hidden { display: none; }
886+
863887
.col-4-small {
864888
@include section-card-small;
865889
}

browser/extensions/newtab/css/activity-stream.css

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11527,6 +11527,9 @@ hr {
1152711527
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-1-position-16 {
1152811528
order: 16;
1152911529
}
11530+
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-1-hidden {
11531+
display: none;
11532+
}
1153011533
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-1-small {
1153111534
grid-row: span 1;
1153211535
grid-column: span 1;
@@ -11845,6 +11848,9 @@ hr {
1184511848
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-2-position-16 {
1184611849
order: 16;
1184711850
}
11851+
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-2-hidden {
11852+
display: none;
11853+
}
1184811854
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-2-small {
1184911855
grid-row: span 1;
1185011856
grid-column: span 1;
@@ -12181,6 +12187,9 @@ hr {
1218112187
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-3-position-16 {
1218212188
order: 16;
1218312189
}
12190+
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-3-hidden {
12191+
display: none;
12192+
}
1218412193
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-3-small {
1218512194
grid-row: span 1;
1218612195
grid-column: span 1;
@@ -12515,6 +12524,9 @@ hr {
1251512524
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-4-position-16 {
1251612525
order: 16;
1251712526
}
12527+
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-4-hidden {
12528+
display: none;
12529+
}
1251812530
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-4-small {
1251912531
grid-row: span 1;
1252012532
grid-column: span 1;
@@ -12856,6 +12868,9 @@ hr {
1285612868
.nova-enabled .ds-section-grid.ds-card-grid .col-1-position-16 {
1285712869
order: 16;
1285812870
}
12871+
.nova-enabled .ds-section-grid.ds-card-grid .col-1-hidden {
12872+
display: none;
12873+
}
1285912874
.nova-enabled .ds-section-grid.ds-card-grid .col-1-small {
1286012875
grid-row: span 1;
1286112876
grid-column: span 1;
@@ -13162,6 +13177,9 @@ hr {
1316213177
.nova-enabled .ds-section-grid.ds-card-grid .col-2-position-16 {
1316313178
order: 16;
1316413179
}
13180+
.nova-enabled .ds-section-grid.ds-card-grid .col-2-hidden {
13181+
display: none;
13182+
}
1316513183
.nova-enabled .ds-section-grid.ds-card-grid .col-2-small {
1316613184
grid-row: span 1;
1316713185
grid-column: span 1;
@@ -13481,6 +13499,9 @@ hr {
1348113499
.nova-enabled .ds-section-grid.ds-card-grid .col-3-position-16 {
1348213500
order: 16;
1348313501
}
13502+
.nova-enabled .ds-section-grid.ds-card-grid .col-3-hidden {
13503+
display: none;
13504+
}
1348413505
.nova-enabled .ds-section-grid.ds-card-grid .col-3-small {
1348513506
grid-row: span 1;
1348613507
grid-column: span 1;
@@ -13800,6 +13821,9 @@ hr {
1380013821
.nova-enabled .ds-section-grid.ds-card-grid .col-4-position-16 {
1380113822
order: 16;
1380213823
}
13824+
.nova-enabled .ds-section-grid.ds-card-grid .col-4-hidden {
13825+
display: none;
13826+
}
1380313827
.nova-enabled .ds-section-grid.ds-card-grid .col-4-small {
1380413828
grid-row: span 1;
1380513829
grid-column: span 1;

browser/extensions/newtab/css/nova/activity-stream.css

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10050,6 +10050,9 @@ button.arrow-button:dir(rtl) {
1005010050
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-1-position-16 {
1005110051
order: 16;
1005210052
}
10053+
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-1-hidden {
10054+
display: none;
10055+
}
1005310056
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-1-small {
1005410057
grid-row: span 1;
1005510058
grid-column: span 1;
@@ -10368,6 +10371,9 @@ button.arrow-button:dir(rtl) {
1036810371
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-2-position-16 {
1036910372
order: 16;
1037010373
}
10374+
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-2-hidden {
10375+
display: none;
10376+
}
1037110377
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-2-small {
1037210378
grid-row: span 1;
1037310379
grid-column: span 1;
@@ -10704,6 +10710,9 @@ button.arrow-button:dir(rtl) {
1070410710
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-3-position-16 {
1070510711
order: 16;
1070610712
}
10713+
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-3-hidden {
10714+
display: none;
10715+
}
1070710716
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-3-small {
1070810717
grid-row: span 1;
1070910718
grid-column: span 1;
@@ -11038,6 +11047,9 @@ button.arrow-button:dir(rtl) {
1103811047
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-4-position-16 {
1103911048
order: 16;
1104011049
}
11050+
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-4-hidden {
11051+
display: none;
11052+
}
1104111053
.ds-section-grid.ds-card-grid:not(.nova-enabled *) .col-4-small {
1104211054
grid-row: span 1;
1104311055
grid-column: span 1;
@@ -11379,6 +11391,9 @@ button.arrow-button:dir(rtl) {
1137911391
.nova-enabled .ds-section-grid.ds-card-grid .col-1-position-16 {
1138011392
order: 16;
1138111393
}
11394+
.nova-enabled .ds-section-grid.ds-card-grid .col-1-hidden {
11395+
display: none;
11396+
}
1138211397
.nova-enabled .ds-section-grid.ds-card-grid .col-1-small {
1138311398
grid-row: span 1;
1138411399
grid-column: span 1;
@@ -11685,6 +11700,9 @@ button.arrow-button:dir(rtl) {
1168511700
.nova-enabled .ds-section-grid.ds-card-grid .col-2-position-16 {
1168611701
order: 16;
1168711702
}
11703+
.nova-enabled .ds-section-grid.ds-card-grid .col-2-hidden {
11704+
display: none;
11705+
}
1168811706
.nova-enabled .ds-section-grid.ds-card-grid .col-2-small {
1168911707
grid-row: span 1;
1169011708
grid-column: span 1;
@@ -12004,6 +12022,9 @@ button.arrow-button:dir(rtl) {
1200412022
.nova-enabled .ds-section-grid.ds-card-grid .col-3-position-16 {
1200512023
order: 16;
1200612024
}
12025+
.nova-enabled .ds-section-grid.ds-card-grid .col-3-hidden {
12026+
display: none;
12027+
}
1200712028
.nova-enabled .ds-section-grid.ds-card-grid .col-3-small {
1200812029
grid-row: span 1;
1200912030
grid-column: span 1;
@@ -12323,6 +12344,9 @@ button.arrow-button:dir(rtl) {
1232312344
.nova-enabled .ds-section-grid.ds-card-grid .col-4-position-16 {
1232412345
order: 16;
1232512346
}
12347+
.nova-enabled .ds-section-grid.ds-card-grid .col-4-hidden {
12348+
display: none;
12349+
}
1232612350
.nova-enabled .ds-section-grid.ds-card-grid .col-4-small {
1232712351
grid-row: span 1;
1232812352
grid-column: span 1;

browser/extensions/newtab/data/content/activity-stream.bundle.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12356,6 +12356,61 @@ const CURATED_RECOMMENDATIONS_FEED_URL = "https://merino.services.mozilla.com/ap
1235612356

1235712357
// Divides evenly by 2, 3, and 4 to avoid orphan cards in any column layout.
1235812358
const DEFAULT_MAX_TILES = 12;
12359+
12360+
// Each card's footprint in grid units, sized so a medium reads as the square it
12361+
// is: a small is half a medium (2x1), a large is two mediums wide (4x2). A grid
12362+
// column is 2 units wide, so a full row spans columnCount * 2 units.
12363+
const CARD_SIZE = {
12364+
small: {
12365+
width: 2,
12366+
height: 1
12367+
},
12368+
medium: {
12369+
width: 2,
12370+
height: 2
12371+
},
12372+
large: {
12373+
width: 4,
12374+
height: 2
12375+
}
12376+
};
12377+
const sizeOf = tile => CARD_SIZE[tile.size] ?? CARD_SIZE.medium;
12378+
12379+
// Return the tileIndexes that fall into an incomplete final row at this
12380+
// breakpoint. These are the orphan cards to hide.
12381+
function getOrphanTileIndexes(tiles, columnCount) {
12382+
const rowWidth = columnCount * 2; // a grid column is 2 units wide
12383+
let currentRow = []; // tile indexes in the row we're filling
12384+
let filled = 0; // units used in this row, including tall cards from above
12385+
let carry = 0; // units this row's tall cards reserve in the next row
12386+
12387+
// Walks each tile, filling currentRow until the row is filled,
12388+
// then clears it and goes to the next row.
12389+
// At the end if currentRow is not empty, we have a remainder.
12390+
tiles.forEach((tile, index) => {
12391+
const {
12392+
width,
12393+
height
12394+
} = sizeOf(tile);
12395+
currentRow.push(index);
12396+
filled += width;
12397+
if (height > 1) {
12398+
carry += width;
12399+
}
12400+
12401+
// This row is complete, we can reset and keep looking.
12402+
if (filled >= rowWidth) {
12403+
currentRow = [];
12404+
filled = carry; // next row starts seeded by tall cards from above
12405+
carry = 0;
12406+
// The carry alone filled the whole next row (all-tall row), so reset it.
12407+
if (filled >= rowWidth) {
12408+
filled = 0;
12409+
}
12410+
}
12411+
});
12412+
return new Set(currentRow);
12413+
}
1235912414
function getLayoutData(responsiveLayouts, index) {
1236012415
let layoutData = {
1236112416
classNames: [],
@@ -12364,8 +12419,12 @@ function getLayoutData(responsiveLayouts, index) {
1236412419
allowsWidget: false
1236512420
};
1236612421
responsiveLayouts.forEach(layout => {
12422+
const orphanTiles = getOrphanTileIndexes(layout.tiles, layout.columnCount);
1236712423
layout.tiles.forEach((tile, tileIndex) => {
1236812424
if (tile.position === index) {
12425+
if (orphanTiles.has(tileIndex)) {
12426+
layoutData.classNames.push(`col-${layout.columnCount}-hidden`);
12427+
}
1236912428
layoutData.classNames.push(`col-${layout.columnCount}-${tile.size}`);
1237012429
layoutData.classNames.push(`col-${layout.columnCount}-position-${tileIndex}`);
1237112430
layoutData.imageSizes[layout.columnCount] = tile.size;

0 commit comments

Comments
 (0)