Skip to content

Commit

Permalink
remove 'Plain' from the style POJO types
Browse files Browse the repository at this point in the history
a nicer name so people can do const style: DeclaredStyle = which
produces much more workable type errors, and nicer than "as const".
  • Loading branch information
chearon committed Mar 27, 2024
1 parent 2f51fff commit 5a09faa
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 86 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,14 @@ await flow.registerFont(new URL('fonts/Roboto-Regular.ttf', import.meta.url));
await flow.registerFont(new URL('fonts/Roboto-Bold.ttf', import.meta.url));

// Always create styles at the top-level of your module if you can
const divStyle = {
const divStyle: flow.DeclaredStyle = {
backgroundColor: {r: 28, g: 10, b: 0, a: 1},
color: {r: 179, g: 200, b: 144, a: 1},
textAlign: 'center' as const
textAlign: 'center'
};

// Since we're creating styles directly, colors have to be defined numerically
const spanStyle = {
const spanStyle: flow.DeclaredStyle = {
color: {r: 115, g: 169, b: 173, a: 1},
fontWeight: 700
};
Expand Down
2 changes: 1 addition & 1 deletion examples/perf-3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const words: string[] = [];

for (let i = 0; i < 10000; i++) words.push(word());

const style = {whiteSpace: 'pre'} as const;
const style: flow.DeclaredStyle = {whiteSpace: 'pre'};

const canvas = createCanvas(100, 20);
const ctx = canvas.getContext('2d');
Expand Down
6 changes: 3 additions & 3 deletions examples/readme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ await flow.registerFont(new URL('../assets/Roboto/Roboto-Regular.ttf', import.me
await flow.registerFont(new URL('../assets/Roboto/Roboto-Bold.ttf', import.meta.url));

// Always create styles at the top-level of your module if you can
const divStyle = {
const divStyle: flow.DeclaredStyle = {
backgroundColor: {r: 28, g: 10, b: 0, a: 1},
textAlign: 'center' as const,
textAlign: 'center',
color: {r: 179, g: 200, b: 144, a: 1}
};

// Since we're creating styles directly, colors have to be defined numerically
const spanStyle = {
const spanStyle: flow.DeclaredStyle = {
color: {r: 115, g: 169, b: 173, a: 1},
fontWeight: 700
};
Expand Down
6 changes: 3 additions & 3 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import {HTMLElement, TextNode} from './dom.js';
import {DeclaredPlainStyle, getRootStyle, initialStyle, computeElementStyle} from './style.js';
import {DeclaredStyle, getRootStyle, initialStyle, computeElementStyle} from './style.js';
import {generateBlockContainer, layoutBlockBox, BlockFormattingContext, BlockContainer} from './layout-flow.js';
import HtmlPaintBackend from './paint-html.js';
import CanvasPaintBackend, {Canvas, CanvasRenderingContext2D} from './paint-canvas.js';
import paintBlockRoot from './paint.js';
import {BoxArea} from './layout-box.js';
import {id} from './util.js';

export type {BlockContainer, DeclaredPlainStyle};
export type {BlockContainer, DeclaredStyle};

export {getRootStyle};

Expand Down Expand Up @@ -74,7 +74,7 @@ export function renderToCanvas(rootElement: HTMLElement, canvas: Canvas, density
type HsChild = HTMLElement | string;

interface HsData {
style?: DeclaredPlainStyle;
style?: DeclaredStyle;
attrs?: {[k: string]: string};
}

Expand Down
8 changes: 6 additions & 2 deletions src/dom.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Box} from './layout-box.js';
import {loggableText} from './util.js';
import {Style, DeclaredPlainStyle, initialStyle, EMPTY_STYLE} from './style.js';
import {Style, DeclaredStyle, initialStyle, EMPTY_STYLE} from './style.js';
import {query, queryAll, Adapter} from './style-query.js';

export class TextNode {
Expand All @@ -25,7 +25,7 @@ export class HTMLElement {
public id: string;
public tagName: string;
public style: Style;
public declaredStyle: DeclaredPlainStyle;
public declaredStyle: DeclaredStyle;
public parent: HTMLElement | null;
public attrs: Record<string, string>;
public children: (TextNode | HTMLElement)[];
Expand Down Expand Up @@ -170,3 +170,7 @@ const adapter: Adapter<HTMLElement, HTMLElement> = {
return false;
}
};




4 changes: 2 additions & 2 deletions src/parse-css.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import {DeclaredPlainStyle} from './style.ts';
import {DeclaredStyle} from './style.ts';

export function parse(s: string): DeclaredPlainStyle;
export function parse(s: string): DeclaredStyle;
144 changes: 72 additions & 72 deletions src/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ type Float = 'left' | 'right' | 'none';

type Clear = 'left' | 'right' | 'both' | 'none';

export interface DeclaredPlainStyle {
export interface DeclaredStyle {
whiteSpace?: WhiteSpace | Inherited | Initial;
color?: Color | Inherited | Initial;
fontSize?: Length | Percentage | Inherited | Initial;
Expand Down Expand Up @@ -171,26 +171,26 @@ export interface DeclaredPlainStyle {
zIndex?: number | 'auto' | Inherited | Initial;
}

export const EMPTY_STYLE: DeclaredPlainStyle = {};
export const EMPTY_STYLE: DeclaredStyle = {};

type CascadedPlainStyle = DeclaredPlainStyle;
type CascadedStyle = DeclaredStyle;

type RemoveUnits<T, U> =
T extends number ? number
: T extends {value: number, unit: infer V} | number ?
V extends U ? number : number | {value: number, unit: Exclude<V, U>}
: T;

type SpecifiedPlainStyle = Required<{
[K in keyof DeclaredPlainStyle]: Exclude<DeclaredPlainStyle[K], Inherited | Initial>
type SpecifiedStyle = Required<{
[K in keyof DeclaredStyle]: Exclude<DeclaredStyle[K], Inherited | Initial>
}>;

type ComputedPlainStyle = {
[K in keyof SpecifiedPlainStyle]
type ComputedStyle = {
[K in keyof SpecifiedStyle]
: K extends 'fontSize' ? number
: K extends 'lineHeight' ? 'normal' | number | {value: number, unit: null}
: K extends 'fontWeight' ? number
: RemoveUnits<SpecifiedPlainStyle[K], 'em'>
: RemoveUnits<SpecifiedStyle[K], 'em'>
};

function resolvePercent(box: BlockContainer | IfcInline, cssVal: number | {value: number, unit: '%'}) {
Expand All @@ -207,57 +207,57 @@ function percentGtZero(cssVal: number | {value: number, unit: '%'}) {
return typeof cssVal === 'object' ? cssVal.value > 0 : cssVal > 0;
}

export class Style implements ComputedPlainStyle {
whiteSpace: ComputedPlainStyle['whiteSpace'];
color: ComputedPlainStyle['color'];
fontSize: ComputedPlainStyle['fontSize'];
fontWeight: ComputedPlainStyle['fontWeight'];
fontVariant: ComputedPlainStyle['fontVariant'];
fontStyle: ComputedPlainStyle['fontStyle'];
fontStretch: ComputedPlainStyle['fontStretch'];
fontFamily: ComputedPlainStyle['fontFamily'];
lineHeight: ComputedPlainStyle['lineHeight'];
verticalAlign: ComputedPlainStyle['verticalAlign'];
backgroundColor: ComputedPlainStyle['backgroundColor'];
backgroundClip: ComputedPlainStyle['backgroundClip'];
display: ComputedPlainStyle['display'];
direction: ComputedPlainStyle['direction'];
writingMode: ComputedPlainStyle['writingMode'];
borderTopWidth: ComputedPlainStyle['borderTopWidth'];
borderRightWidth: ComputedPlainStyle['borderRightWidth'];
borderBottomWidth: ComputedPlainStyle['borderBottomWidth'];
borderLeftWidth: ComputedPlainStyle['borderLeftWidth'];
borderTopStyle: ComputedPlainStyle['borderTopStyle'];
borderRightStyle: ComputedPlainStyle['borderRightStyle'];
borderBottomStyle: ComputedPlainStyle['borderBottomStyle'];
borderLeftStyle: ComputedPlainStyle['borderLeftStyle'];
borderTopColor: ComputedPlainStyle['borderTopColor'];
borderRightColor: ComputedPlainStyle['borderRightColor'];
borderBottomColor: ComputedPlainStyle['borderBottomColor'];
borderLeftColor: ComputedPlainStyle['borderLeftColor'];
paddingTop: ComputedPlainStyle['paddingTop'];
paddingRight: ComputedPlainStyle['paddingRight'];
paddingBottom: ComputedPlainStyle['paddingBottom'];
paddingLeft: ComputedPlainStyle['paddingLeft'];
marginTop: ComputedPlainStyle['marginTop'];
marginRight: ComputedPlainStyle['marginRight'];
marginBottom: ComputedPlainStyle['marginBottom'];
marginLeft: ComputedPlainStyle['marginLeft'];
tabSize: ComputedPlainStyle['tabSize'];
position: ComputedPlainStyle['position'];
width: ComputedPlainStyle['width'];
height: ComputedPlainStyle['height'];
top: ComputedPlainStyle['top'];
right: ComputedPlainStyle['right'];
bottom: ComputedPlainStyle['bottom'];
left: ComputedPlainStyle['left'];
boxSizing: ComputedPlainStyle['boxSizing'];
textAlign: ComputedPlainStyle['textAlign'];
float: ComputedPlainStyle['float'];
clear: ComputedPlainStyle['clear'];
zIndex: ComputedPlainStyle['zIndex'];

constructor(style: ComputedPlainStyle) {
export class Style implements ComputedStyle {
whiteSpace: ComputedStyle['whiteSpace'];
color: ComputedStyle['color'];
fontSize: ComputedStyle['fontSize'];
fontWeight: ComputedStyle['fontWeight'];
fontVariant: ComputedStyle['fontVariant'];
fontStyle: ComputedStyle['fontStyle'];
fontStretch: ComputedStyle['fontStretch'];
fontFamily: ComputedStyle['fontFamily'];
lineHeight: ComputedStyle['lineHeight'];
verticalAlign: ComputedStyle['verticalAlign'];
backgroundColor: ComputedStyle['backgroundColor'];
backgroundClip: ComputedStyle['backgroundClip'];
display: ComputedStyle['display'];
direction: ComputedStyle['direction'];
writingMode: ComputedStyle['writingMode'];
borderTopWidth: ComputedStyle['borderTopWidth'];
borderRightWidth: ComputedStyle['borderRightWidth'];
borderBottomWidth: ComputedStyle['borderBottomWidth'];
borderLeftWidth: ComputedStyle['borderLeftWidth'];
borderTopStyle: ComputedStyle['borderTopStyle'];
borderRightStyle: ComputedStyle['borderRightStyle'];
borderBottomStyle: ComputedStyle['borderBottomStyle'];
borderLeftStyle: ComputedStyle['borderLeftStyle'];
borderTopColor: ComputedStyle['borderTopColor'];
borderRightColor: ComputedStyle['borderRightColor'];
borderBottomColor: ComputedStyle['borderBottomColor'];
borderLeftColor: ComputedStyle['borderLeftColor'];
paddingTop: ComputedStyle['paddingTop'];
paddingRight: ComputedStyle['paddingRight'];
paddingBottom: ComputedStyle['paddingBottom'];
paddingLeft: ComputedStyle['paddingLeft'];
marginTop: ComputedStyle['marginTop'];
marginRight: ComputedStyle['marginRight'];
marginBottom: ComputedStyle['marginBottom'];
marginLeft: ComputedStyle['marginLeft'];
tabSize: ComputedStyle['tabSize'];
position: ComputedStyle['position'];
width: ComputedStyle['width'];
height: ComputedStyle['height'];
top: ComputedStyle['top'];
right: ComputedStyle['right'];
bottom: ComputedStyle['bottom'];
left: ComputedStyle['left'];
boxSizing: ComputedStyle['boxSizing'];
textAlign: ComputedStyle['textAlign'];
float: ComputedStyle['float'];
clear: ComputedStyle['clear'];
zIndex: ComputedStyle['zIndex'];

constructor(style: ComputedStyle) {
this.whiteSpace = style.whiteSpace;
this.color = style.color;
this.fontSize = style.fontSize;
Expand Down Expand Up @@ -486,7 +486,7 @@ export class Style implements ComputedPlainStyle {
// initial values as specified in the property's specification. This is also
// the style that's used as the root style for inheritance. These are the
// "computed value"s as described in CSS Cascading and Inheritance Level 4 § 4.4
const initialPlainStyle: ComputedPlainStyle = Object.freeze({
const initialPlainStyle: ComputedStyle = Object.freeze({
whiteSpace: 'normal',
color: {r: 0, g: 0, b: 0, a: 1},
fontSize: 16,
Expand Down Expand Up @@ -539,7 +539,7 @@ const initialPlainStyle: ComputedPlainStyle = Object.freeze({

export const initialStyle = new Style(initialPlainStyle);

type InheritedStyleDefinitions = {[K in keyof ComputedPlainStyle]: boolean};
type InheritedStyleDefinitions = {[K in keyof ComputedStyle]: boolean};

// Each CSS property defines whether or not it's inherited
const inheritedStyle: InheritedStyleDefinitions = Object.freeze({
Expand Down Expand Up @@ -593,7 +593,7 @@ const inheritedStyle: InheritedStyleDefinitions = Object.freeze({
zIndex: false
});

type UaDeclaredStyles = {[tagName: string]: DeclaredPlainStyle};
type UaDeclaredStyles = {[tagName: string]: DeclaredStyle};

export const uaDeclaredStyles: UaDeclaredStyles = Object.freeze({
div: {
Expand Down Expand Up @@ -669,9 +669,9 @@ export const uaDeclaredStyles: UaDeclaredStyles = Object.freeze({
}
});

const cascadedCache = new WeakMap<CascadedPlainStyle, WeakMap<CascadedPlainStyle, DeclaredPlainStyle>>();
const cascadedCache = new WeakMap<CascadedStyle, WeakMap<CascadedStyle, DeclaredStyle>>();

export function cascadeStyles(s1: DeclaredPlainStyle, s2: DeclaredPlainStyle): CascadedPlainStyle {
export function cascadeStyles(s1: DeclaredStyle, s2: DeclaredStyle): CascadedStyle {
let m1 = cascadedCache.get(s1);
let m2 = m1 && m1.get(s2);

Expand All @@ -691,7 +691,7 @@ export function cascadeStyles(s1: DeclaredPlainStyle, s2: DeclaredPlainStyle): C
return ret;
}

function defaultifyStyle(parentStyle: Style, style: CascadedPlainStyle) {
function defaultifyStyle(parentStyle: Style, style: CascadedStyle) {
const ret: any = {};

for (const _ in initialPlainStyle) {
Expand All @@ -705,10 +705,10 @@ function defaultifyStyle(parentStyle: Style, style: CascadedPlainStyle) {
}
}

return ret as SpecifiedPlainStyle;
return ret as SpecifiedStyle;
}

function computeStyle(parentStyle: Style, style: SpecifiedPlainStyle) {
function computeStyle(parentStyle: Style, style: SpecifiedStyle) {
const ret:{[i: string]: any} = {};

for (const _ in initialPlainStyle) {
Expand Down Expand Up @@ -753,10 +753,10 @@ function computeStyle(parentStyle: Style, style: SpecifiedPlainStyle) {
ret.lineHeight = style.lineHeight.value / 100 * ret.fontSize;
}

return new Style(ret as ComputedPlainStyle);
return new Style(ret as ComputedStyle);
}

const styleCache = new WeakMap<DeclaredPlainStyle, WeakMap<DeclaredPlainStyle, Style>>();
const styleCache = new WeakMap<DeclaredStyle, WeakMap<DeclaredStyle, Style>>();

/**
* Very simple property inheritance model. createStyle starts out with cascaded
Expand All @@ -767,7 +767,7 @@ const styleCache = new WeakMap<DeclaredPlainStyle, WeakMap<DeclaredPlainStyle, S
* Used/actual styles (§4.5, §4.6) are calculated during layout, external to
* this file.
*/
export function createStyle(s1: Style, s2: CascadedPlainStyle) {
export function createStyle(s1: Style, s2: CascadedStyle) {
let m1 = styleCache.get(s1);
let m2 = m1 && m1.get(s2);

Expand All @@ -789,14 +789,14 @@ export function createStyle(s1: Style, s2: CascadedPlainStyle) {
}

// required styles that always come last in the cascade
const rootDeclaredStyle: DeclaredPlainStyle = {
const rootDeclaredStyle: DeclaredStyle = {
display: {
outer: 'block',
inner: 'flow-root'
}
};

export function getRootStyle(style: DeclaredPlainStyle = EMPTY_STYLE) {
export function getRootStyle(style: DeclaredStyle = EMPTY_STYLE) {
return createStyle(initialStyle, cascadeStyles(style, rootDeclaredStyle))
}

Expand Down

0 comments on commit 5a09faa

Please sign in to comment.