Skip to content

Commit

Permalink
support for multiple declared styles on an element
Browse files Browse the repository at this point in the history
I started working on it because I was thinking about optimizing
changing the zoom property for a whole tree and I got totally
sidetracked. I don't even need this but it was interesting to
think about how to do it without affecting perf and it should
definitely make the API easier to use.
  • Loading branch information
chearon committed Oct 29, 2024
1 parent 24e2c8c commit 6e0823a
Show file tree
Hide file tree
Showing 13 changed files with 322 additions and 277 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ project adheres to [Semantic Versioning](http://semver.org/).
(Unreleased)
==================
### Changed
* Styles must now be passed through `flow.style` before being given to `h`.
* `cascadeStyles` has been removed. Pass an array of styles to `h` instead.
* Removed `getRootStyle`

### Added
* Added support for the `zoom` property
* Support for multiple styles on an element

### Fixed

Expand Down
28 changes: 18 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,18 +100,18 @@ import fs from 'node:fs';
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: flow.DeclaredStyle = {
// Always create styles at the top-level of your module if you can.
const divStyle = flow.style({
backgroundColor: {r: 28, g: 10, b: 0, a: 1},
color: {r: 179, g: 200, b: 144, a: 1},
textAlign: 'center'
};
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: flow.DeclaredStyle = {
// Since we're creating styles directly, colors are numbers
const spanStyle = flow.style({
color: {r: 115, g: 169, b: 173, a: 1},
fontWeight: 700
};
});

// Create a DOM
const rootElement = flow.dom(
Expand Down Expand Up @@ -237,6 +237,14 @@ Removes a font from the internal list so that it won't be picked by the `font` p

The hyperscript API is the fastest way to generate a DOM. The DOM is composed of `HTMLElement`s and `TextNode`s. The relevant properties of them are shown below. More supported properties are described in the [#dom-api](DOM API section).

### `style`

```ts
function style(properties: DeclaredStyleProperties): DeclaredStyle;
```

Use the `style` function to create a style for passing to the attributes of an element later. `DeclaredStyleProperties` is defined in `style.ts`.

### `h`

```ts
Expand All @@ -251,7 +259,7 @@ class TextNode {
}

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

Expand All @@ -262,7 +270,7 @@ function h(tagName: string, text: string): HTMLElement;
function h(tagName: string, data: HsData, children: HsChild[] | string): HTMLElement;
```

Creates an HTMLElement. Styles go on `data.style` (see `style.ts` for supported values and their types).
Creates an HTMLElement. Use styles from the previous section. Currently the only attribute used is `x-dropflow-log`, which, when present on a paragraph, logs details about text shaping.

### `t`

Expand Down
12 changes: 6 additions & 6 deletions examples/images-1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,31 @@ const image2 = await loadImage('https://picsum.photos/50/50');

registerFontAsset('Arimo/Arimo-Regular.ttf');

const rootStyle: flow.DeclaredStyle = {
const rootStyle = flow.style({
paddingTop: 10,
paddingRight: 10,
paddingBottom: 10,
paddingLeft: 10,
backgroundColor: {r: 200, g: 200, b: 200, a: 1},
lineHeight: {value: 2, unit: null}
};
});

const image1Style: flow.DeclaredStyle = {
const image1Style = flow.style({
float: 'left',
width: image1.width,
height: image1.height,
marginTop: 6,
marginRight: 6,
marginBottom: 6,
marginLeft: 6
};
});

const image2Style: flow.DeclaredStyle = {
const image2Style = flow.style({
// note: an upcoming API change is that this will become display: 'inline-block'
display: {outer: 'inline', inner: 'flow-root'},
width: image2.width,
height: image2.height
};
});

const rootElement = flow.dom(
flow.h('html', {style: rootStyle, attrs: {'x-dropflow-log': 'true'}}, [
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: flow.DeclaredStyle = {whiteSpace: 'pre'};
const style = flow.style({whiteSpace: 'pre'});

const canvas = createCanvas(100, 20);
const ctx = canvas.getContext('2d');
Expand Down
12 changes: 6 additions & 6 deletions examples/readme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ import fs from 'node:fs';
await flow.registerFont(new URL('../assets/Roboto/Roboto-Regular.ttf', import.meta.url));
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: flow.DeclaredStyle = {
// Always create styles at the top-level of your module if you can.
const divStyle = flow.style({
backgroundColor: {r: 28, g: 10, b: 0, a: 1},
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: flow.DeclaredStyle = {
// Since we're creating styles directly, colors are numbers
const spanStyle = flow.style({
color: {r: 115, g: 169, b: 173, a: 1},
fontWeight: 700
};
});

// Create a DOM
const rootElement = flow.dom(
Expand Down
4 changes: 2 additions & 2 deletions src/api-with-parse.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Parser, isWhitespace} from './parse-html.js';
import {TextNode, HTMLElement} from './dom.js';
import {parse as StyleParser} from './parse-css.js';
import {EMPTY_STYLE, computeElementStyle} from './style.js';
import {EMPTY_STYLE, computeElementStyle, createDeclaredStyle} from './style.js';
import {id} from './util.js';

export function parse(str: string): HTMLElement {
Expand Down Expand Up @@ -47,7 +47,7 @@ export function parse(str: string): HTMLElement {
// Just ignore invalid styles so the parser can continue
if (attrs.style) {
try {
declaredStyle = StyleParser(attrs.style);
declaredStyle = createDeclaredStyle(StyleParser(attrs.style));
} catch {}
}

Expand Down
8 changes: 3 additions & 5 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {HTMLElement, TextNode} from './dom.js';
import {DeclaredStyle, getRootStyle, initialStyle, computeElementStyle} from './style.js';
import {DeclaredStyle, initialStyle, computeElementStyle} from './style.js';
import {registerFont, unregisterFont, getFontUrls, RegisterFontOptions} from './text-font.js';
import {generateBlockContainer, layoutBlockBox, BlockFormattingContext, BlockContainer} from './layout-flow.js';
import HtmlPaintBackend from './paint-html.js';
Expand All @@ -13,9 +13,7 @@ export type {BlockContainer, DeclaredStyle};

export type {HTMLElement};

export {getRootStyle};

export {cascadeStyles} from './style.js';
export {createDeclaredStyle as style} from './style.js';

export {registerFont, unregisterFont};

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

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

Expand Down
12 changes: 10 additions & 2 deletions src/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class HTMLElement {
public id: string;
public tagName: string;
public style: Style;
public declaredStyle: DeclaredStyle;
public declaredStyle: DeclaredStyle | DeclaredStyle[];
public parent: HTMLElement | null;
public attrs: Record<string, string>;
public children: (TextNode | HTMLElement)[];
Expand All @@ -36,7 +36,7 @@ export class HTMLElement {
tagName: string,
parent: HTMLElement | null = null,
attrs: {[k: string]: string} = {},
declaredStyle = EMPTY_STYLE
declaredStyle: DeclaredStyle | DeclaredStyle[] = EMPTY_STYLE
) {
this.id = id;
this.tagName = tagName;
Expand All @@ -48,6 +48,14 @@ export class HTMLElement {
this.boxes = [];
}

getDeclaredStyles() {
if (Array.isArray(this.declaredStyle)) {
return this.declaredStyle.slice();
} else {
return [this.declaredStyle];
}
}

getEl(stack: number[]) {
let el: HTMLElement | TextNode = this;

Expand Down
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 {DeclaredStyle} from './style.ts';
import {DeclaredStyleProperties} from './style.ts';

export function parse(s: string): DeclaredStyle;
export function parse(s: string): DeclaredStyleProperties;
Loading

0 comments on commit 6e0823a

Please sign in to comment.