Skip to content

Commit 115c9f0

Browse files
committed
feat(src): extend layout and style API, add DomElement 🍹
- Add Types.DomElement and use in Attrs, Element, Main instead of local Element type - Extend Layout with alignItems, display, flexDirection, flexWrap, justifyContent, min/max width and height, overflow - Extend Style with background, cursor, letterSpacing, lineHeight, outline, textAlign, textDecoration, visibility, zIndex - Rename RenderElement class to Element in Render/Element.ts - Reorder Types: El, ElTagNames, Layout and Style fields, RenderOptions and Schema, UnknownRecord
1 parent 7c2d69e commit 115c9f0

File tree

6 files changed

+258
-151
lines changed

6 files changed

+258
-151
lines changed

src/Render/Attrs.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default class Attrs {
1313
* @param element - Target DOM element
1414
* @param attrs - Attribute key-value map
1515
*/
16-
static applyAttrs(element: Element, attrs: Types.Attrs): void {
16+
static applyAttrs(element: Types.DomElement, attrs: Types.Attrs): void {
1717
for (const [key, value] of Object.entries(attrs)) {
1818
if (key.toLowerCase().startsWith('on')) {
1919
continue
@@ -91,7 +91,7 @@ export default class Attrs {
9191
* @returns True when attribute was handled
9292
*/
9393
private static applyBooleanAttr(
94-
element: Element,
94+
element: Types.DomElement,
9595
attributeKey: string,
9696
attrValue: unknown
9797
): boolean {

src/Render/Element.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import Constant from '@app/Constant.ts'
55
* DOM element creation for schema nodes.
66
* @description Tag name constants and createElementForNode for HTML/SVG.
77
*/
8-
export default class RenderElement {
8+
export default class Element {
99
/** SVG root tag name. */
1010
static readonly svgTagName: string = 'svg'
1111
/** Template element tag name. */
@@ -19,12 +19,12 @@ export default class RenderElement {
1919
* @param isSvg - Whether parent is SVG context
2020
* @returns Created DOM element
2121
*/
22-
static createElementForNode(node: Types.Node, doc: Document, isSvg: boolean): Element {
22+
static createElementForNode(node: Types.Node, doc: Document, isSvg: boolean): Types.DomElement {
2323
const tag = node.type
24-
if (tag === RenderElement.templateTagName) {
24+
if (tag === Element.templateTagName) {
2525
return doc.createElement('template')
2626
}
27-
if (isSvg || tag === RenderElement.svgTagName) {
27+
if (isSvg || tag === Element.svgTagName) {
2828
return doc.createElementNS(Constant.svgNamespace, tag)
2929
}
3030
return doc.createElement(tag)

src/Render/Layout.ts

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type * as Types from '@app/Types.ts'
77
export default class Layout {
88
/**
99
* Apply layout to element style.
10-
* @description Sets width, height, position, flex, gap.
10+
* @description Sets width, height, display, flex, gap, overflow, position.
1111
* @param element - Target HTML element
1212
* @param layout - Layout object
1313
*/
@@ -19,6 +19,45 @@ export default class Layout {
1919
if (layout.height !== undefined) {
2020
elementStyle.height = Layout.toCssValue(layout.height)
2121
}
22+
if (layout.minWidth !== undefined) {
23+
elementStyle.minWidth = Layout.toCssValue(layout.minWidth)
24+
}
25+
if (layout.maxWidth !== undefined) {
26+
elementStyle.maxWidth = Layout.toCssValue(layout.maxWidth)
27+
}
28+
if (layout.minHeight !== undefined) {
29+
elementStyle.minHeight = Layout.toCssValue(layout.minHeight)
30+
}
31+
if (layout.maxHeight !== undefined) {
32+
elementStyle.maxHeight = Layout.toCssValue(layout.maxHeight)
33+
}
34+
if (layout.display !== undefined) {
35+
elementStyle.display = layout.display
36+
}
37+
if (layout.flexDirection !== undefined) {
38+
elementStyle.flexDirection = layout.flexDirection
39+
}
40+
if (layout.flexWrap !== undefined) {
41+
elementStyle.flexWrap = layout.flexWrap
42+
}
43+
if (layout.alignItems !== undefined) {
44+
elementStyle.alignItems = layout.alignItems
45+
}
46+
if (layout.justifyContent !== undefined) {
47+
elementStyle.justifyContent = layout.justifyContent
48+
}
49+
if (layout.gap !== undefined) {
50+
elementStyle.gap = Layout.toCssValue(layout.gap)
51+
if (!elementStyle.display || elementStyle.display === 'inline') {
52+
elementStyle.display = 'flex'
53+
}
54+
}
55+
if (layout.flex !== undefined) {
56+
elementStyle.flex = String(layout.flex)
57+
if (!elementStyle.display || elementStyle.display === 'inline') {
58+
elementStyle.display = 'block'
59+
}
60+
}
2261
if (layout.x !== undefined || layout.y !== undefined) {
2362
if (!elementStyle.position || elementStyle.position === 'static') {
2463
elementStyle.position = 'relative'
@@ -30,17 +69,14 @@ export default class Layout {
3069
if (layout.y !== undefined) {
3170
elementStyle.top = Layout.toCssValue(layout.y)
3271
}
33-
if (layout.flex !== undefined) {
34-
elementStyle.flex = String(layout.flex)
35-
if (!elementStyle.display || elementStyle.display === 'inline') {
36-
elementStyle.display = 'block'
37-
}
72+
if (layout.overflow !== undefined) {
73+
elementStyle.overflow = layout.overflow
3874
}
39-
if (layout.gap !== undefined) {
40-
elementStyle.gap = Layout.toCssValue(layout.gap)
41-
if (!elementStyle.display || elementStyle.display === 'inline') {
42-
elementStyle.display = 'flex'
43-
}
75+
if (layout.overflowX !== undefined) {
76+
elementStyle.overflowX = layout.overflowX
77+
}
78+
if (layout.overflowY !== undefined) {
79+
elementStyle.overflowY = layout.overflowY
4480
}
4581
}
4682

src/Render/Main.ts

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@ import Element from '@app/Render/Element.ts'
55
import Layout from '@app/Render/Layout.ts'
66
import Style from '@app/Render/Style.ts'
77

8-
/** DOM Element type alias (avoid clash with imported Element class). */
9-
type DomElement = InstanceType<typeof globalThis.Element>
10-
118
/**
129
* Renders schema to DOM.
1310
* @description Builds elements from nodes and applies layout and style.
@@ -24,7 +21,7 @@ export default class Render {
2421
* @param element - Target DOM element
2522
* @param attrs - Attribute key-value map
2623
*/
27-
static applyAttrs(element: DomElement, attrs: Types.Attrs): void {
24+
static applyAttrs(element: Types.DomElement, attrs: Types.Attrs): void {
2825
Attrs.applyAttrs(element, attrs)
2926
}
3027

@@ -66,7 +63,7 @@ export default class Render {
6663
* @param isSvg - Whether parent is SVG context
6764
* @returns Created DOM element
6865
*/
69-
static createElementForNode(node: Types.Node, doc: Document, isSvg: boolean): DomElement {
66+
static createElementForNode(node: Types.Node, doc: Document, isSvg: boolean): Types.DomElement {
7067
return Element.createElementForNode(node, doc, isSvg)
7168
}
7269

@@ -81,9 +78,9 @@ export default class Render {
8178
const ownerDoc = container.ownerDocument ?? document
8279
let renderContext:
8380
| {
84-
refs?: Map<string, DomElement>
85-
onNodeMount?: (node: Types.Node, element: DomElement) => void
86-
mounted?: Array<{ node: Types.Node; element: DomElement }>
81+
refs?: Map<string, Types.DomElement>
82+
onNodeMount?: (node: Types.Node, element: Types.DomElement) => void
83+
mounted?: Array<{ node: Types.Node; element: Types.DomElement }>
8784
}
8885
| undefined
8986
if (options) {
@@ -109,7 +106,7 @@ export default class Render {
109106
container.appendChild(nodeResult)
110107
}
111108
if (renderContext?.refs && node.id) {
112-
renderContext.refs.set(node.id, nodeResult as DomElement)
109+
renderContext.refs.set(node.id, nodeResult as Types.DomElement)
113110
}
114111
}
115112
if (renderContext?.onNodeMount && renderContext.mounted) {
@@ -133,18 +130,18 @@ export default class Render {
133130
doc: Document,
134131
parentIsSvg: boolean,
135132
renderContext?: {
136-
refs?: Map<string, DomElement>
137-
mounted?: Array<{ node: Types.Node; element: DomElement }>
133+
refs?: Map<string, Types.DomElement>
134+
mounted?: Array<{ node: Types.Node; element: Types.DomElement }>
138135
}
139-
): DomElement | DocumentFragment {
136+
): Types.DomElement | DocumentFragment {
140137
const tagName = node.type
141138
const isSvg = parentIsSvg || tagName === Render.svgTagName
142139
const element = Render.createElementForNode(node, doc, isSvg)
143140
if (renderContext?.refs && node.id) {
144-
renderContext.refs.set(node.id, element as DomElement)
141+
renderContext.refs.set(node.id, element as Types.DomElement)
145142
}
146143
if (renderContext?.mounted) {
147-
renderContext.mounted.push({ node, element: element as DomElement })
144+
renderContext.mounted.push({ node, element: element as Types.DomElement })
148145
}
149146
if (node.id && element instanceof HTMLElement) {
150147
element.id = node.id
@@ -174,7 +171,7 @@ export default class Render {
174171
}
175172
}
176173
if (!Constant.voidTags.has(tagName) && node.children && node.children.length > 0) {
177-
let appendTarget: DocumentFragment | DomElement
174+
let appendTarget: DocumentFragment | Types.DomElement
178175
if (element instanceof HTMLTemplateElement) {
179176
appendTarget = element.content
180177
} else {
@@ -190,10 +187,10 @@ export default class Render {
190187
appendTarget.appendChild(childResult)
191188
}
192189
if (renderContext?.refs && child.id) {
193-
renderContext.refs.set(child.id, childResult as DomElement)
190+
renderContext.refs.set(child.id, childResult as Types.DomElement)
194191
}
195192
if (renderContext?.mounted) {
196-
renderContext.mounted.push({ node: child, element: childResult as DomElement })
193+
renderContext.mounted.push({ node: child, element: childResult as Types.DomElement })
197194
}
198195
}
199196
}

src/Render/Style.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,33 @@ export default class Style {
4949
if (style.scrollMargin) {
5050
elementStyle.scrollMargin = style.scrollMargin
5151
}
52+
if (style.textAlign) {
53+
elementStyle.textAlign = style.textAlign
54+
}
55+
if (style.textDecoration) {
56+
elementStyle.textDecoration = style.textDecoration
57+
}
58+
if (style.letterSpacing) {
59+
elementStyle.letterSpacing = style.letterSpacing
60+
}
61+
if (style.lineHeight) {
62+
elementStyle.lineHeight = style.lineHeight
63+
}
64+
if (style.cursor) {
65+
elementStyle.cursor = style.cursor
66+
}
67+
if (style.visibility) {
68+
elementStyle.visibility = style.visibility
69+
}
70+
if (style.background) {
71+
elementStyle.background = style.background
72+
}
73+
if (style.outline) {
74+
elementStyle.outline = style.outline
75+
}
76+
if (style.zIndex) {
77+
elementStyle.zIndex = style.zIndex
78+
}
5279
}
5380

5481
/**

0 commit comments

Comments
 (0)