UNPKG

@aesthetic/style

Version:

A low-level, high-performance, atomic-based CSS-in-JS style engine.

154 lines (131 loc) 3.38 kB
/* eslint-disable @typescript-eslint/prefer-string-starts-ends-with */ import { AtRule, CSS, Sheet, Value } from '@aesthetic/types'; import { hyphenate } from '@aesthetic/utils'; import { IMPORT_RULE, STYLE_RULE } from './constants'; export function insertRule(sheet: Sheet, rule: CSS, index?: number): number { try { return sheet.insertRule(rule, index ?? sheet.cssRules.length); } catch { // Vendor prefixed properties, pseudos, etc, that are inserted // into different vendors will trigger a failure. For example, // `-moz` or `-ms` being inserted into WebKit. // There's no easy way around this, so let's just ignore the // error so that subsequent styles are inserted. // istanbul ignore next return -1; } } export function insertAtRule(sheet: Sheet, rule: CSS): number { const { length } = sheet.cssRules; let index = 0; // At-rules must be inserted before normal style rules. for (let i = 0; i <= length; i += 1) { index = i; if (sheet.cssRules[i]?.type === STYLE_RULE) { break; } } return insertRule(sheet, rule, index); } export function insertImportRule(sheet: Sheet, rule: CSS): number { const { length } = sheet.cssRules; let index = 0; // Import rules must be inserted at the top of the style sheet, // but we also want to persist the existing order. for (let i = 0; i <= length; i += 1) { index = i; if (sheet.cssRules[i]?.type !== IMPORT_RULE) { break; } } return insertRule(sheet, rule, index); } export function isAtRule(value: string): value is AtRule { return value[0] === '@'; } export function isImportRule(value: string): boolean { // eslint-disable-next-line no-magic-numbers return value.slice(0, 7) === '@import'; } export function isNestedSelector(value: string): boolean { const char = value[0]; return ( char === ':' || char === '[' || char === '>' || char === '~' || char === '+' || char === '*' || char === '|' ); } const unitlessProperties = new Set<string>(); [ 'animationIterationCount', 'borderImage', 'borderImageOutset', 'borderImageSlice', 'borderImageWidth', 'columnCount', 'columns', 'flex', 'flexGrow', 'flexPositive', 'flexShrink', 'flexNegative', 'flexOrder', 'fontWeight', 'gridArea', 'gridRow', 'gridRowEnd', 'gridRowSpan', 'gridRowStart', 'gridColumn', 'gridColumnEnd', 'gridColumnSpan', 'gridColumnStart', 'lineClamp', 'lineHeight', 'maskBorder', 'maskBorderOutset', 'maskBorderSlice', 'maskBorderWidth', 'opacity', 'order', 'orphans', 'tabSize', 'widows', 'zIndex', 'zoom', // SVG 'fillOpacity', 'floodOpacity', 'stopOpacity', 'strokeDasharray', 'strokeDashoffset', 'strokeMiterlimit', 'strokeOpacity', 'strokeWidth', ].forEach((property) => { unitlessProperties.add(property); unitlessProperties.add(hyphenate(property)); }); export function isUnitlessProperty(property: string): boolean { return unitlessProperties.has(property); } export function isValidValue(property: string, value: unknown): value is Value { if (value === undefined) { return false; } if (value === null || value === true || value === false || value === '') { if (__DEV__) { // eslint-disable-next-line no-console console.warn(`Invalid value "${value}" for "${property}".`); } return false; } return true; } export function isVariable(value: string): boolean { return value.slice(0, 2) === '--'; }