@wordpress/block-editor
Version:
199 lines (185 loc) • 5.9 kB
JavaScript
export function range( start, length ) {
return Array.from( { length }, ( _, i ) => start + i );
}
export class GridRect {
constructor( {
columnStart,
rowStart,
columnEnd,
rowEnd,
columnSpan,
rowSpan,
} = {} ) {
this.columnStart = columnStart ?? 1;
this.rowStart = rowStart ?? 1;
if ( columnSpan !== undefined ) {
this.columnEnd = this.columnStart + columnSpan - 1;
} else {
this.columnEnd = columnEnd ?? this.columnStart;
}
if ( rowSpan !== undefined ) {
this.rowEnd = this.rowStart + rowSpan - 1;
} else {
this.rowEnd = rowEnd ?? this.rowStart;
}
}
get columnSpan() {
return this.columnEnd - this.columnStart + 1;
}
get rowSpan() {
return this.rowEnd - this.rowStart + 1;
}
contains( column, row ) {
return (
column >= this.columnStart &&
column <= this.columnEnd &&
row >= this.rowStart &&
row <= this.rowEnd
);
}
containsRect( rect ) {
return (
this.contains( rect.columnStart, rect.rowStart ) &&
this.contains( rect.columnEnd, rect.rowEnd )
);
}
intersectsRect( rect ) {
return (
this.columnStart <= rect.columnEnd &&
this.columnEnd >= rect.columnStart &&
this.rowStart <= rect.rowEnd &&
this.rowEnd >= rect.rowStart
);
}
}
export function getComputedCSS( element, property ) {
return element.ownerDocument.defaultView
.getComputedStyle( element )
.getPropertyValue( property );
}
/**
* Given a grid-template-columns or grid-template-rows CSS property value, gets the start and end
* position in pixels of each grid track.
*
* https://css-tricks.com/snippets/css/complete-guide-grid/#aa-grid-track
*
* @param {string} template The grid-template-columns or grid-template-rows CSS property value.
* Only supports fixed sizes in pixels.
* @param {number} gap The gap between grid tracks in pixels.
*
* @return {Array<{start: number, end: number}>} An array of objects with the start and end
* position in pixels of each grid track.
*/
export function getGridTracks( template, gap ) {
const tracks = [];
for ( const size of template.split( ' ' ) ) {
const previousTrack = tracks[ tracks.length - 1 ];
const start = previousTrack ? previousTrack.end + gap : 0;
const end = start + parseFloat( size );
tracks.push( { start, end } );
}
return tracks;
}
/**
* Given an array of grid tracks and a position in pixels, gets the index of the closest track to
* that position.
*
* https://css-tricks.com/snippets/css/complete-guide-grid/#aa-grid-track
*
* @param {Array<{start: number, end: number}>} tracks An array of objects with the start and end
* position in pixels of each grid track.
* @param {number} position The position in pixels.
* @param {string} edge The edge of the track to compare the
* position to. Either 'start' or 'end'.
*
* @return {number} The index of the closest track to the position. 0-based, unlike CSS grid which
* is 1-based.
*/
export function getClosestTrack( tracks, position, edge = 'start' ) {
return tracks.reduce(
( closest, track, index ) =>
Math.abs( track[ edge ] - position ) <
Math.abs( tracks[ closest ][ edge ] - position )
? index
: closest,
0
);
}
export function getGridRect( gridElement, rect ) {
const columnGap = parseFloat( getComputedCSS( gridElement, 'column-gap' ) );
const rowGap = parseFloat( getComputedCSS( gridElement, 'row-gap' ) );
const gridColumnTracks = getGridTracks(
getComputedCSS( gridElement, 'grid-template-columns' ),
columnGap
);
const gridRowTracks = getGridTracks(
getComputedCSS( gridElement, 'grid-template-rows' ),
rowGap
);
const columnStart = getClosestTrack( gridColumnTracks, rect.left ) + 1;
const rowStart = getClosestTrack( gridRowTracks, rect.top ) + 1;
const columnEnd =
getClosestTrack( gridColumnTracks, rect.right, 'end' ) + 1;
const rowEnd = getClosestTrack( gridRowTracks, rect.bottom, 'end' ) + 1;
return new GridRect( {
columnStart,
columnEnd,
rowStart,
rowEnd,
} );
}
export function getGridItemRect( gridItemElement ) {
return getGridRect(
gridItemElement.parentElement,
new window.DOMRect(
gridItemElement.offsetLeft,
gridItemElement.offsetTop,
gridItemElement.offsetWidth,
gridItemElement.offsetHeight
)
);
}
export function getGridInfo( gridElement ) {
const gridTemplateColumns = getComputedCSS(
gridElement,
'grid-template-columns'
);
const gridTemplateRows = getComputedCSS(
gridElement,
'grid-template-rows'
);
const borderTopWidth = getComputedCSS( gridElement, 'border-top-width' );
const borderRightWidth = getComputedCSS(
gridElement,
'border-right-width'
);
const borderBottomWidth = getComputedCSS(
gridElement,
'border-bottom-width'
);
const borderLeftWidth = getComputedCSS( gridElement, 'border-left-width' );
const paddingTop = getComputedCSS( gridElement, 'padding-top' );
const paddingRight = getComputedCSS( gridElement, 'padding-right' );
const paddingBottom = getComputedCSS( gridElement, 'padding-bottom' );
const paddingLeft = getComputedCSS( gridElement, 'padding-left' );
const numColumns = gridTemplateColumns.split( ' ' ).length;
const numRows = gridTemplateRows.split( ' ' ).length;
const numItems = numColumns * numRows;
return {
numColumns,
numRows,
numItems,
currentColor: getComputedCSS( gridElement, 'color' ),
style: {
gridTemplateColumns,
gridTemplateRows,
gap: getComputedCSS( gridElement, 'gap' ),
inset: `
calc(${ paddingTop } + ${ borderTopWidth })
calc(${ paddingRight } + ${ borderRightWidth })
calc(${ paddingBottom } + ${ borderBottomWidth })
calc(${ paddingLeft } + ${ borderLeftWidth })
`,
},
};
}