UNPKG

vlens

Version:

Data Centric Routing & Rendering Mini-Framework

122 lines (108 loc) 3.41 kB
import type * as csstype from "csstype"; import * as tester from "./tester"; // based on cxs interface ISheet { insertRule(rule: string): void; } class Sheet { domSheet: CSSStyleSheet | null; rulesCount = 0; constructor() { let el = document.createElement("style"); el = document.head.appendChild(el); if (!el.sheet) { console.error("failed to created style element"); } this.domSheet = el.sheet; } insertRule(rule: string) { this.domSheet!.insertRule(rule, this.rulesCount); this.rulesCount++; } } class FakeSheet { insertRule(rule: string) { } } let sheet: ISheet = BROWSER ? new Sheet() : new FakeSheet(); export type RuleDefinition = csstype.StandardProperties<string, string> & { [key: string]: string | number | RuleDefinition; } function combineSelectorForNestedRule(selector: string, child: string): string { let result: string[] = []; for (let p of selector.split(',')) { p = p.trim(); for (let c of child.split(',')) { c = c.trim(); if (c.startsWith(':')) { result.push(p + c); } else if (c.startsWith("&")) { result.push(selector + c.substring(1)) } else if (c.startsWith('@')) { result.push(c + ' ' + p) } else { result.push(p + ' ' + c) } } } return result.join(', '); } export function block(body: string) { sheet.insertRule(body); } export function rule(sel: string, body: RuleDefinition) { let subrules: [string, RuleDefinition][] = []; let bodyLines: string[] = []; for (const [key, value] of Object.entries(body)) { if (typeof value === 'object') { subrules.push([combineSelectorForNestedRule(sel, key), value]); continue; } else { let property = key.replace(/[A-Z]/g, c => '-' + c.toLowerCase()); bodyLines.push(`${property}: ${value}`); } } sheet.insertRule(sel + "{" + bodyLines.join(";") + "}\n"); for (let [sel1, body1] of subrules) { rule(sel1, body1); } } const usedNames = new Set<string>(); export function cls(name: string, body: RuleDefinition) { if (usedNames.has(name)) { // find a new name for (let i = 0; i < 100; i++) { let newName = name + '-' + i; if (!usedNames.has(newName)) { name = newName; break; } } } usedNames.add(name); rule("." + name, body); return name; } export function tests() { tester.test('css rule combination', (t: tester.Test) => { { let sel = ".abc"; let child = "p"; let expected = ".abc p"; t.equal(expected, combineSelectorForNestedRule(sel, child), "expected", "actual"); } { let sel = ".abc"; let child = "p, b"; let expected = ".abc p, .abc b"; t.equal(expected, combineSelectorForNestedRule(sel, child), "expected", "actual"); } { // pseudo selectors let sel = ".abc"; let child = ":hover"; let expected = ".abc:hover"; t.equal(expected, combineSelectorForNestedRule(sel, child), "expected", "actual"); } }) }