Created
May 10, 2021 02:26
-
-
Save jonathantneal/a5fb0d54233d50c05b068fade140d62b to your computer and use it in GitHub Desktop.
Some, like, useful functions when building cqfill. This gist may be deleted later.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** Observes stylesheet lists and runs a callback whenever it encounteres added or removed stylesheets. */ | |
export const createStyleSheetObserver = (/** @type {(addedSheets?: CSSStyleSheet[], removedSheets?: CSSStyleSheet[]) => void} */ callback) => { | |
/** @type {Set<StyleSheetList>} Observed style sheet lists. */ | |
const sheetLists = new Set | |
/** @type {Set<CSSStyleSheet>} Previously observed style sheets. */ | |
let lastSheets = new Set | |
/** Observes style sheet on each animation frame. */ | |
const onAnimationFrame = () => { | |
requestAnimationFrame(onAnimationFrame) | |
/** @type {CSSStyleSheet[]} Observed StyleSheets after this frame. */ | |
const nextSheets = [] | |
/** @type {CSSStyleSheet[]} New StyleSheets added during this frame. */ | |
const addedSheets = new Set | |
// check each StyleSheetList | |
for (const sheets of sheetLists) { | |
// check each StyleSheet | |
for (const sheet of sheets) { | |
nextSheets.push(sheet) | |
// add stylesheets that have not yet been observed | |
if (!lastSheets.delete(sheet)) { | |
addedSheets.add(sheet) | |
} | |
} | |
} | |
if (addedSheets.size || lastSheets.size) { | |
callback(addedSheets, lastSheets) | |
} | |
lastSheets = new Set(nextSheets) | |
} | |
onAnimationFrame() | |
return sheetLists | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { getComputedStyle: __getComputedStyle = () => ({}) } = globalThis | |
const __getComputedStyleMap = new WeakMap | |
/** @type {(element: Element) => CSSStyleDeclaration} Returns the computed CSS values of an element. */ | |
export const getComputedStyle = element => __getComputedStyleMap.get(element) || __getComputedStyleMap.set(element, __getComputedStyle(element)).get(element), |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const matchContainerQuery = /(?:--css-container)? and \((min|max)-(width|height): ([-+]?(?:\d*\.)?(?:\d+))(em|px|rem)\)/gy | |
/** Returns a container query from a media list that represents a container query. */ | |
export const getContainerQueryFromMediaList = (/** @type {MediaList} */ mediaList) => { | |
/** @type {CQFContainerQuery} */ | |
const query = {} | |
/** @type {[boolean, 'max' | 'min', CQFAxis, string, CQFUnit] | void} */ | |
let match | |
while (match = matchContainerQuery.exec(mediaList[0])) { | |
let [ isLogical, edge, prop, value, unit ] = match | |
isLogical = mediaList[1] === '--is-logical' | |
prop = ( | |
isLogical | |
? prop === 'width' ? 'inline-size' : 'block-size' | |
: prop | |
) | |
query[prop] = query[prop] || { min: [0, 'px'], max: [Infinity, 'px'] } | |
query[prop][edge] = [ Number(value), unit ] | |
} | |
return query | |
} | |
/** @typedef {"em" | "px" | "rem"} CQFUnit */ | |
/** @typedef {[number, CQFUnit]} CQFLimit */ | |
/** @typedef {{ max: CQFLimit, min: CQFLimit }} CQFRange */ | |
/** @typedef {'block-size' | 'height' | 'inline-size' | 'width'} CQFAxis */ | |
/** @typedef {{ [key in CQFAxis]?: CQFRange }} CQFContainerQuery */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** @type {(style: CSSStyleDeclaration) => { block: boolean, inline: boolean, width: boolean, height: boolean } | null} Returns a list of `contain` values in a style. */ | |
export const getContainFromCSSStyleDeclaration = (style) => { | |
/** Raw contain style. */ | |
const contain = style ? style.getPropertyValue('--css-contain').trim().toLowerCase().split(/\s+/) : [] | |
if (!contain.includes('layout')) return null | |
const size = contain.includes('size') | |
const block = size && contain.includes('block-size') | |
const inline = size && contain.includes('inline-size') | |
const width = contain.includes('width') | |
const height = contain.includes('height') | |
return block || inline || width || height ? { block, inline, width, height } : null | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { getComputedStyle } from './getComputedStyle.js' | |
/** @type {(entry: ResizeObserverEntry) => ContentBoxSize} Returns useful sizing information from a resize observer entry. */ | |
export const getContentBoxSizeFromResizeObserverEntry = ({ contentRect, target }) => { | |
/** Computed style of the target element. */ | |
const computedStyle = getComputedStyle(target) | |
/** Whether the target element is in a horizontal writing mode. */ | |
const isHorizontal = computedStyle.writingMode.charCodeAt(0) === 104 | |
/** @type {ContentBoxSize} */ | |
const contentBoxSize = { | |
width: contentRect.width, | |
height: contentRect.height, | |
blockSize: isHorizontal ? contentRect.height : contentRect.width, | |
inlineSize: isHorizontal ? contentRect.width: contentRect.height, | |
fontSize: parseFloat(computedStyle.fontSize), | |
} | |
return contentBoxSize | |
} | |
/** @typedef {{ blockSize: number, fontSize: number, height: number, inlineSize: number, width: number }} ContentBoxSize */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { getComputedStyle } from './getComputedStyle.js' | |
/** @type {(element: Element) => boolean} Returns whether the element has an inline outer-display. */ | |
export const hasInlineOuterDisplay = (element) => /^inline/.test(getComputedStyle(element).display), |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment