Skip to content

Commit

Permalink
Add support for grid-gap (#1032)
Browse files Browse the repository at this point in the history
* [grid] Test for grid-gap

* grid-gap with grid-template-areas

* [grid] grid-gap with grid-template

* Update Size Limit

* [grid] Clean up code

* [grid] Skip grid-template-row in warnings
  • Loading branch information
yepninja authored and ai committed May 13, 2018
1 parent 3b2ba01 commit 8979cab
Show file tree
Hide file tree
Showing 11 changed files with 451 additions and 40 deletions.
9 changes: 6 additions & 3 deletions lib/hacks/grid-rows-columns.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
const Declaration = require('../declaration');
const utils = require('./grid-utils');
const {
prefixTrackProp,
prefixTrackValue
} = require('./grid-utils');

class GridRowsColumns extends Declaration {

Expand All @@ -13,7 +16,7 @@ class GridRowsColumns extends Declaration {
*/
prefixed(prop, prefix) {
if (prefix === '-ms-') {
return prefix + prop.replace('template-', '');
return prefixTrackProp({ prop, prefix });
} else {
return super.prefixed(prop, prefix);
}
Expand All @@ -31,7 +34,7 @@ class GridRowsColumns extends Declaration {
*/
set(decl, prefix) {
if (prefix === '-ms-' && decl.value.indexOf('repeat(') !== -1) {
decl.value = utils.changeRepeat(decl.value);
decl.value = prefixTrackValue({ value: decl.value });
}
return super.set(decl, prefix);
}
Expand Down
60 changes: 58 additions & 2 deletions lib/hacks/grid-template-areas.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
const Declaration = require('../declaration');
const {
parseGridAreas,
insertAreas
insertAreas,
prefixTrackProp,
prefixTrackValue,
getGridGap,
warnGridGap
} = require('./grid-utils');


Expand All @@ -19,7 +23,59 @@ class GridTemplateAreas extends Declaration {
insert(decl, prefix, prefixes, result) {
if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes);

const areas = parseGridAreas(getGridRows(decl.value));
let hasColumns = false;
let hasRows = false;
const parent = decl.parent;
const gap = getGridGap(decl);

// remove already prefixed rows and columns
// without gutter to prevent doubling prefixes
parent.walkDecls(/-ms-grid-(rows|columns)/, i => i.remove());

// add empty tracks to rows and columns
parent.walkDecls(/grid-template-(rows|columns)/, (trackDecl) => {
if (trackDecl.prop === 'grid-template-rows') {
hasRows = true;
const { prop, value } = trackDecl;
trackDecl.cloneBefore({
prop: prefixTrackProp({ prop, prefix }),
value: prefixTrackValue({ value, gap: gap.row })
});
} else {
hasColumns = true;
const { prop, value } = trackDecl;
trackDecl.cloneBefore({
prop: prefixTrackProp({ prop, prefix }),
value: prefixTrackValue({ value, gap: gap.column })
});
}
});

const gridRows = getGridRows(decl.value);

if (hasColumns && !hasRows && gap.row && gridRows.length > 1) {
decl.cloneBefore({
prop: '-ms-grid-rows',
value: prefixTrackValue({
value: `repeat(${gridRows.length}, auto)`,
gap: gap.row
}),
raws: {}
});
}

// warnings
warnGridGap({
gap,
hasColumns,
decl,
result
});

const areas = parseGridAreas({
rows: gridRows,
gap
});

insertAreas(areas, decl, result);

Expand Down
24 changes: 20 additions & 4 deletions lib/hacks/grid-template.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
const Declaration = require('../declaration');
const {
parseTemplate,
insertAreas
insertAreas,
getGridGap,
warnGridGap
} = require('./grid-utils');

class GridTemplate extends Declaration {
Expand All @@ -21,22 +23,36 @@ class GridTemplate extends Declaration {
return undefined;
}

const gap = getGridGap(decl);
const {
rows,
columns,
areas
} = parseTemplate(decl);
} = parseTemplate({
decl,
gap
});

const hasAreas = Object.keys(areas).length > 0;
const hasRows = Boolean(rows);
const hasColumns = Boolean(columns);

warnGridGap({
gap,
hasColumns,
decl,
result
});

if (rows && columns || hasAreas) {
if (hasRows && hasColumns || hasAreas) {
decl.cloneBefore({
prop: '-ms-grid-rows',
value: rows,
raws: { }
});
}

if (columns) {
if (hasColumns) {
decl.cloneBefore({
prop: '-ms-grid-columns',
value: columns,
Expand Down
145 changes: 118 additions & 27 deletions lib/hacks/grid-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,40 +77,68 @@ function insertDecl(decl, prop, value) {
}
}

// Transform repeat
// Track transforms

function transformRepeat({ nodes }) {
const repeat = nodes.reduce((result, node) => {
function prefixTrackProp({ prop, prefix }) {
return prefix + prop.replace('template-', '');
}

function transformRepeat({ nodes }, { gap }) {
const {
count,
size
} = nodes.reduce((result, node) => {
if (node.type === 'div' && node.value === ',') {
result.key = 'function';
result.key = 'size';
} else {
result[result.key].push(parser.stringify(node));
}
return result;
}, {
key: 'count',
function: [],
size: [],
count: []
});
return `(${repeat.function.join('')})[${repeat.count.join('')}]`;

if (gap) {
const val = [];
for (let i = 1; i <= count; i++) {
if (gap && i > 1) {
val.push(gap);
}
val.push(size.join());
}
return val.join(' ');
}

return `(${size.join('')})[${count.join('')}]`;
}

function changeRepeat(value) {
function prefixTrackValue({ value, gap }) {
const result = parser(value)
.nodes
.map(i => {
if (i.type === 'function' && i.value === 'repeat') {
return {
.reduce((nodes, node) => {
if (node.type === 'function' && node.value === 'repeat') {
return nodes.concat({
type: 'word',
value: transformRepeat(i)
};
value: transformRepeat(node, { gap })
});
}
return i;
});
if (gap && node.type === 'space') {
return nodes.concat({
type: 'space',
value: ' '
}, {
type: 'word',
value: gap
}, node);
}
return nodes.concat(node);
}, []);

return parser.stringify(result);
}


// Parse grid-template-areas

const DOTS = /^\.+$/;
Expand All @@ -123,17 +151,31 @@ function getColumns(line) {
return line.trim().split(/\s+/g);
}

function parseGridAreas(rows) {
function parseGridAreas({
rows,
gap
}) {
return rows.reduce((areas, line, rowIndex) => {

if (gap.row) rowIndex *= 2;

if (line.trim() === '') return areas;

getColumns(line).forEach((area, columnIndex) => {

if (DOTS.test(area)) return;

if (gap.column) columnIndex *= 2;

if (typeof areas[area] === 'undefined') {

areas[area] = {
column: track(columnIndex + 1, columnIndex + 2),
row: track(rowIndex + 1, rowIndex + 2)
};

} else {

const { column, row } = areas[area];

column.start = Math.min(column.start, columnIndex + 1);
Expand All @@ -143,8 +185,10 @@ function parseGridAreas(rows) {
row.start = Math.min(row.start, rowIndex + 1);
row.end = Math.max(row.end, rowIndex + 2);
row.span = row.end - row.start;

}
});

return areas;
}, {});
}
Expand All @@ -156,7 +200,10 @@ function testTrack(node) {
return node.type === 'word' && /^\[.+\]$/.test(node.value);
}

function parseTemplate(decl) {
function parseTemplate({
decl,
gap
}) {
const gridTemplate = parser(decl.value)
.nodes
.reduce((result, node) => {
Expand All @@ -171,11 +218,7 @@ function parseTemplate(decl) {

// values and function
if (type === 'word' || type === 'function') {
if (type === 'function' && value === 'repeat') {
result[result.key].push(transformRepeat(node));
} else {
result[result.key].push(parser.stringify(node));
}
result[result.key].push(parser.stringify(node));
}

// devider(/)
Expand All @@ -190,10 +233,20 @@ function parseTemplate(decl) {
rows: [],
areas: []
});

return {
areas: parseGridAreas(gridTemplate.areas),
columns: gridTemplate.columns.join(' '),
rows: gridTemplate.rows.join(' ')
areas: parseGridAreas({
rows: gridTemplate.areas,
gap
}),
columns: prefixTrackValue({
value: gridTemplate.columns.join(' '),
gap: gap.column
}),
rows: prefixTrackValue({
value: gridTemplate.rows.join(' '),
gap: gap.row
})
};
}

Expand Down Expand Up @@ -291,12 +344,50 @@ function insertAreas(areas, decl, result) {
}
}

// Gap utils

function getGridGap(decl) {

let gap = {};

// try to find gap
decl.parent.walkDecls(/grid(-(row|column))?-gap/, ({ prop, value }) => {
if (prop === 'grid-gap') {
gap.column = value;
gap.row = value;
}
if (prop === 'grid-row-gap') gap.row = value;
if (prop === 'grid-column-gap') gap.column = value;
});

return gap;
}

function warnGridGap({
gap,
hasColumns,
decl,
result
}) {
const hasBothGaps = gap.row && gap.column;
if (!hasColumns && (hasBothGaps || gap.column && !gap.row)) {
delete gap.column;
decl.warn(
result,
'Can not impliment grid-gap without grid-tamplate-columns'
);
}
}

module.exports = {
parse,
translate,
changeRepeat,
parseTemplate,
parseGridAreas,
insertAreas,
insertDecl
insertDecl,
prefixTrackProp,
prefixTrackValue,
getGridGap,
warnGridGap
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"size-limit": [
{
"path": "build/lib/autoprefixer.js",
"limit": "97 KB"
"limit": "98 KB"
}
],
"eslintConfig": {
Expand Down
Loading

0 comments on commit 8979cab

Please sign in to comment.