@tempots/dom 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +10 -0
- package/.eslintrc.cjs +28 -0
- package/coverage/clover.xml +39 -0
- package/coverage/coverage-final.json +2 -0
- package/coverage/lcov-report/OneOf.ts.html +256 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/dom-context.ts.html +928 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +116 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/prop.ts.html +691 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov-report/src/components/Attribute.ts.html +154 -0
- package/coverage/lcov-report/src/components/BooleanAttribute.ts.html +154 -0
- package/coverage/lcov-report/src/components/ClassName.ts.html +151 -0
- package/coverage/lcov-report/src/components/El.ts.html +166 -0
- package/coverage/lcov-report/src/components/FadeIn.ts.html +262 -0
- package/coverage/lcov-report/src/components/FadeOut.ts.html +226 -0
- package/coverage/lcov-report/src/components/For.ts.html +151 -0
- package/coverage/lcov-report/src/components/Fragment.ts.html +142 -0
- package/coverage/lcov-report/src/components/HiddenWhenEmpty.ts.html +133 -0
- package/coverage/lcov-report/src/components/If.ts.html +217 -0
- package/coverage/lcov-report/src/components/InnerHTML.ts.html +178 -0
- package/coverage/lcov-report/src/components/Lifecycle.ts.html +157 -0
- package/coverage/lcov-report/src/components/Match.ts.html +286 -0
- package/coverage/lcov-report/src/components/NotEmpty.tsx.html +184 -0
- package/coverage/lcov-report/src/components/On.ts.html +151 -0
- package/coverage/lcov-report/src/components/OnRemove.ts.html +145 -0
- package/coverage/lcov-report/src/components/OneOf.ts.html +256 -0
- package/coverage/lcov-report/src/components/Portal.ts.html +175 -0
- package/coverage/lcov-report/src/components/Property.ts.html +154 -0
- package/coverage/lcov-report/src/components/Provider.ts.html +244 -0
- package/coverage/lcov-report/src/components/Repeat.ts.html +223 -0
- package/coverage/lcov-report/src/components/Show.tsx.html +190 -0
- package/coverage/lcov-report/src/components/Text.ts.html +229 -0
- package/coverage/lcov-report/src/components/TextContent.ts.html +178 -0
- package/coverage/lcov-report/src/components/Tween.tsx.html +943 -0
- package/coverage/lcov-report/src/components/animatable.ts.html +619 -0
- package/coverage/lcov-report/src/components/index.html +476 -0
- package/coverage/lcov-report/src/dom-context.ts.html +928 -0
- package/coverage/lcov-report/src/helpers/handle-anchor-click.ts.html +277 -0
- package/coverage/lcov-report/src/helpers/handle-text-input.ts.html +100 -0
- package/coverage/lcov-report/src/helpers/index.html +146 -0
- package/coverage/lcov-report/src/helpers/is-empty-element.ts.html +112 -0
- package/coverage/lcov-report/src/index.html +176 -0
- package/coverage/lcov-report/src/index.ts.html +412 -0
- package/coverage/lcov-report/src/jsx-runtime.ts.html +601 -0
- package/coverage/lcov-report/src/prop.ts.html +691 -0
- package/coverage/lcov-report/src/render.ts.html +112 -0
- package/coverage/lcov-report/src/types/idom-context.ts.html +184 -0
- package/coverage/lcov-report/src/types/index.html +116 -0
- package/coverage/lcov-report/test/common.ts.html +112 -0
- package/coverage/lcov-report/test/index.html +116 -0
- package/coverage/lcov.info +57 -0
- package/dist/index.js +32 -0
- package/jest.config.js +5 -0
- package/package.json +39 -0
- package/src/clean.ts +2 -0
- package/src/components/Attribute.ts +23 -0
- package/src/components/BooleanAttribute.ts +23 -0
- package/src/components/ClassName.ts +22 -0
- package/src/components/El.ts +27 -0
- package/src/components/FadeIn.ts +59 -0
- package/src/components/FadeOut.ts +47 -0
- package/src/components/For.ts +22 -0
- package/src/components/Fragment.ts +19 -0
- package/src/components/HiddenWhenEmpty.ts +16 -0
- package/src/components/If.ts +44 -0
- package/src/components/InnerHTML.ts +31 -0
- package/src/components/Lifecycle.ts +24 -0
- package/src/components/Match.ts +67 -0
- package/src/components/NotEmpty.tsx +33 -0
- package/src/components/On.ts +22 -0
- package/src/components/OnRemove.ts +20 -0
- package/src/components/OneOf.ts +57 -0
- package/src/components/Portal.ts +30 -0
- package/src/components/Property.ts +23 -0
- package/src/components/Provider.ts +53 -0
- package/src/components/Repeat.ts +46 -0
- package/src/components/Show.tsx +35 -0
- package/src/components/Text.ts +48 -0
- package/src/components/TextContent.ts +31 -0
- package/src/components/animatable.ts +178 -0
- package/src/dom-context.ts +281 -0
- package/src/helpers/handle-anchor-click.ts +64 -0
- package/src/helpers/handle-text-input.ts +5 -0
- package/src/helpers/is-empty-element.ts +9 -0
- package/src/index.ts +109 -0
- package/src/jsx-dev-runtime.ts +8 -0
- package/src/jsx-runtime.ts +172 -0
- package/src/jsx.ts +1046 -0
- package/src/prop.ts +202 -0
- package/src/render.ts +9 -0
- package/src/renderable.ts +6 -0
- package/test/common.ts +9 -0
- package/test/component.spec.tsx +27 -0
- package/test/domcontext.spec.ts +36 -0
- package/test/fadein.spec.tsx +36 -0
- package/test/fadeout.spec.tsx +41 -0
- package/test/if.spec.tsx +30 -0
- package/test/innerhtml.spec.tsx +45 -0
- package/test/prop.spec.ts +10 -0
- package/test/render.spec.tsx +19 -0
- package/test/textcontent.spec.tsx +45 -0
- package/test/when.spec.tsx +30 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { type Clear } from './clean'
|
|
2
|
+
|
|
3
|
+
function extractClassNames(cls: string): string[] {
|
|
4
|
+
return (cls ?? '').split(/\s+/g).filter((className) => className.length > 0)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function createIntegerSet(x: number): Set<number> {
|
|
8
|
+
const integerSet = new Set<number>()
|
|
9
|
+
|
|
10
|
+
for (let i = 0; i < x; i++) {
|
|
11
|
+
integerSet.add(i)
|
|
12
|
+
}
|
|
13
|
+
return integerSet
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type ProviderMark<T> = symbol
|
|
17
|
+
|
|
18
|
+
export function makeProviderMark<T>(): ProviderMark<T> {
|
|
19
|
+
return Symbol('providerMark')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type Providers = Record<ProviderMark<unknown>, unknown>
|
|
23
|
+
|
|
24
|
+
export class DOMContext {
|
|
25
|
+
static of(element: HTMLElement): DOMContext {
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
27
|
+
return new DOMContext(element.ownerDocument, element, undefined, undefined, {})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
private readonly document: Document,
|
|
32
|
+
private readonly element: HTMLElement,
|
|
33
|
+
private readonly reference: Text | undefined,
|
|
34
|
+
private readonly ns: string | undefined,
|
|
35
|
+
private readonly providers: Providers
|
|
36
|
+
) { }
|
|
37
|
+
|
|
38
|
+
append(node: Node): void {
|
|
39
|
+
if (this.reference !== undefined) {
|
|
40
|
+
try {
|
|
41
|
+
// There are components (TextContent, InnerHTML) that can mess up with the internal state of an element
|
|
42
|
+
this.element.insertBefore(node, this.reference)
|
|
43
|
+
} catch (_) {
|
|
44
|
+
this.element.appendChild(node)
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
this.element.appendChild(node)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
getElement(): HTMLElement {
|
|
52
|
+
return this.element
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
getDocument(): Document {
|
|
56
|
+
return this.document
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
makeReference(): DOMContext {
|
|
60
|
+
const textNode = this.document.createTextNode('')
|
|
61
|
+
this.append(textNode)
|
|
62
|
+
return new DOMContext(this.document, this.element, textNode, this.ns, this.providers)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
makeElement(tagName: string): DOMContext {
|
|
66
|
+
if (this.ns !== undefined || tagName === 'svg') {
|
|
67
|
+
const ns = this.ns ?? 'http://www.w3.org/2000/svg'
|
|
68
|
+
const element = this.document.createElementNS(ns, tagName)
|
|
69
|
+
this.append(element)
|
|
70
|
+
return new DOMContext(this.document, element as HTMLElement, undefined, ns, this.providers)
|
|
71
|
+
} else {
|
|
72
|
+
const element = this.document.createElement(tagName)
|
|
73
|
+
this.append(element)
|
|
74
|
+
return new DOMContext(this.document, element, undefined, this.ns, this.providers)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
getBooleanAttribute(name: string): boolean {
|
|
79
|
+
return this.element.hasAttribute(name)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
setBooleanAttribute(name: string, value: boolean): void {
|
|
83
|
+
if (value) {
|
|
84
|
+
this.element.setAttribute(name, '')
|
|
85
|
+
} else {
|
|
86
|
+
this.element.removeAttribute(name)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
createBooleanAttribute(name: string, value: boolean): [(newValue: boolean) => void, Clear] {
|
|
91
|
+
const current = this.element.hasAttribute(name)
|
|
92
|
+
this.setBooleanAttribute(name, value)
|
|
93
|
+
return [
|
|
94
|
+
(newValue: boolean) => {
|
|
95
|
+
this.setBooleanAttribute(name, newValue)
|
|
96
|
+
},
|
|
97
|
+
(removeTree: boolean) => {
|
|
98
|
+
if (removeTree) {
|
|
99
|
+
this.setBooleanAttribute(name, current)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
getAttribute(name: string): string | null {
|
|
106
|
+
return this.element.getAttribute(name)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
setAttribute(name: string, value: string | null): void {
|
|
110
|
+
if (value == null) {
|
|
111
|
+
this.element.removeAttribute(name)
|
|
112
|
+
} else {
|
|
113
|
+
this.element.setAttribute(name, value)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
createAttribute(name: string, value: string): [(newValue: string) => void, Clear] {
|
|
118
|
+
const current = this.element.getAttribute(name)
|
|
119
|
+
this.setAttribute(name, value)
|
|
120
|
+
return [
|
|
121
|
+
(newValue: string) => {
|
|
122
|
+
this.setAttribute(name, newValue)
|
|
123
|
+
},
|
|
124
|
+
(removeTree: boolean) => {
|
|
125
|
+
if (removeTree) {
|
|
126
|
+
this.setAttribute(name, current)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
getProperty<T>(name: string): T {
|
|
133
|
+
return Reflect.get(this.element, name)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
setProperty<T>(name: string, value: T): void {
|
|
137
|
+
if (value == null) {
|
|
138
|
+
Reflect.deleteProperty(this.element, name)
|
|
139
|
+
} else {
|
|
140
|
+
Reflect.set(this.element, name, value)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
createProperty<T>(name: string, value: T): [(newValue: T) => void, Clear] {
|
|
145
|
+
const current = this.getProperty<T>(name)
|
|
146
|
+
this.setProperty(name, value)
|
|
147
|
+
return [
|
|
148
|
+
(newValue: T) => {
|
|
149
|
+
this.setProperty(name, newValue)
|
|
150
|
+
},
|
|
151
|
+
(removeTree: boolean) => {
|
|
152
|
+
if (removeTree) {
|
|
153
|
+
this.setProperty(name, current)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
createText(text: string): [(newText: string) => void, Clear] {
|
|
160
|
+
const textNode = this.document.createTextNode(text)
|
|
161
|
+
this.append(textNode)
|
|
162
|
+
return [
|
|
163
|
+
(newText: string) => {
|
|
164
|
+
textNode.nodeValue = newText
|
|
165
|
+
},
|
|
166
|
+
(removeTree: boolean) => {
|
|
167
|
+
if (removeTree) {
|
|
168
|
+
textNode.remove()
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
createClass(cls: string): [(newClass: string) => void, Clear] {
|
|
175
|
+
let current = extractClassNames(cls)
|
|
176
|
+
current.forEach((className) => {
|
|
177
|
+
this.element.classList.add(className)
|
|
178
|
+
})
|
|
179
|
+
return [
|
|
180
|
+
(newClass: string) => {
|
|
181
|
+
current.forEach((className) => {
|
|
182
|
+
this.element.classList.remove(className)
|
|
183
|
+
})
|
|
184
|
+
current = extractClassNames(newClass)
|
|
185
|
+
current.forEach((className) => {
|
|
186
|
+
this.element.classList.add(className)
|
|
187
|
+
})
|
|
188
|
+
},
|
|
189
|
+
(removeTree: boolean) => {
|
|
190
|
+
if (removeTree) {
|
|
191
|
+
current.forEach((className) => {
|
|
192
|
+
this.element.classList.remove(className)
|
|
193
|
+
if (this.element.classList.length === 0) {
|
|
194
|
+
this.element.removeAttribute('class')
|
|
195
|
+
}
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
]
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
createHandler<T>(name: string, handler: (event: T) => void): Clear {
|
|
203
|
+
this.element.addEventListener(name, handler as unknown as EventListener)
|
|
204
|
+
return (removeTree: boolean) => {
|
|
205
|
+
if (removeTree) {
|
|
206
|
+
this.element.removeEventListener(name, handler as unknown as EventListener)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private readonly suspendedClears: Array<(removeTree: boolean, clear: () => void) => void> = []
|
|
212
|
+
delayClear(f: (removeTree: boolean, clear: () => void) => void): (removeTree: boolean) => void {
|
|
213
|
+
this.suspendedClears.push(f)
|
|
214
|
+
return (removeTree) => {
|
|
215
|
+
// TODO nothing happens?
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
requestClear(removeTree: boolean, willClear: () => void): void {
|
|
220
|
+
if (this.suspendedClears.length === 0) {
|
|
221
|
+
willClear()
|
|
222
|
+
this.clear(removeTree)
|
|
223
|
+
} else {
|
|
224
|
+
const set = createIntegerSet(this.suspendedClears.length)
|
|
225
|
+
const clearSuspended = (index: number): void => {
|
|
226
|
+
set.delete(index)
|
|
227
|
+
if (set.size === 0) {
|
|
228
|
+
willClear()
|
|
229
|
+
this.clear(removeTree)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
this.suspendedClears.forEach((f, i) => { f(removeTree, () => { clearSuspended(i) }) })
|
|
233
|
+
this.suspendedClears.length = 0
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private clear(removeTree: boolean): void {
|
|
238
|
+
if (removeTree) {
|
|
239
|
+
if (this.reference !== undefined) {
|
|
240
|
+
this.reference.parentElement?.removeChild(this.reference)
|
|
241
|
+
} else {
|
|
242
|
+
this.element.onblur = null
|
|
243
|
+
this.element.parentElement?.removeChild(this.element)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
withProvider<T>(mark: ProviderMark<T>, provider: T): DOMContext {
|
|
249
|
+
return new DOMContext(this.document, this.element, this.reference, this.ns, {
|
|
250
|
+
...this.providers,
|
|
251
|
+
[mark]: provider
|
|
252
|
+
})
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
getProvider<T>(mark: ProviderMark<T>): T {
|
|
256
|
+
return this.providers[mark] as T
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
setStyle(name: string, value: string | undefined | null): void {
|
|
260
|
+
if (value == null) {
|
|
261
|
+
this.element.style.removeProperty(name)
|
|
262
|
+
} else {
|
|
263
|
+
this.element.style.setProperty(name, value)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
createStyle(name: string, value: string | undefined | null): [(newValue: string) => void, Clear] {
|
|
268
|
+
const current = this.element.style.getPropertyValue(name)
|
|
269
|
+
this.setStyle(name, value)
|
|
270
|
+
return [
|
|
271
|
+
(newValue: string) => {
|
|
272
|
+
this.setStyle(name, newValue)
|
|
273
|
+
},
|
|
274
|
+
(removeTree: boolean) => {
|
|
275
|
+
if (removeTree) {
|
|
276
|
+
this.setStyle(name, current)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
function shouldNotApplyCallback(
|
|
2
|
+
e: MouseEvent,
|
|
3
|
+
checkExtension: boolean,
|
|
4
|
+
checkExternalUrl: boolean
|
|
5
|
+
): boolean {
|
|
6
|
+
let target = e.target as HTMLElement | null
|
|
7
|
+
while ((target != null) && !(target instanceof HTMLAnchorElement)) {
|
|
8
|
+
target = target.parentElement
|
|
9
|
+
}
|
|
10
|
+
if (target == null) return true
|
|
11
|
+
|
|
12
|
+
const anchor = target
|
|
13
|
+
|
|
14
|
+
// Check for modifier keys and non-left-button, which indicate the user wants to control
|
|
15
|
+
// navigation
|
|
16
|
+
if (e.button !== 0 || e.ctrlKey || e.metaKey) {
|
|
17
|
+
return true
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// If there is a target and it is not `_self` then we take this
|
|
21
|
+
// as a signal that it doesn't want to be intercepted.
|
|
22
|
+
if (anchor.target !== '_self') {
|
|
23
|
+
return true
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (anchor.getAttribute('download') != null) {
|
|
27
|
+
return true // let the download happen
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (checkExternalUrl) {
|
|
31
|
+
const { pathname, search, hash } = anchor
|
|
32
|
+
const relativeUrl = pathname + search + hash
|
|
33
|
+
|
|
34
|
+
// don't navigate if external link or has extension
|
|
35
|
+
if (
|
|
36
|
+
anchor.getAttribute('href') !== relativeUrl ||
|
|
37
|
+
(checkExtension && !/\/[^/.]*$/.test(pathname))
|
|
38
|
+
) {
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return false
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const handleAnchorClick =
|
|
47
|
+
(
|
|
48
|
+
callback: () => boolean,
|
|
49
|
+
options: { checkExtension?: boolean, checkExternalUrl?: boolean } = {
|
|
50
|
+
checkExtension: true,
|
|
51
|
+
checkExternalUrl: true
|
|
52
|
+
}
|
|
53
|
+
) =>
|
|
54
|
+
(e: MouseEvent) => {
|
|
55
|
+
const { checkExtension, checkExternalUrl } = options
|
|
56
|
+
if (
|
|
57
|
+
shouldNotApplyCallback(
|
|
58
|
+
e,
|
|
59
|
+
checkExtension === true,
|
|
60
|
+
checkExternalUrl === true
|
|
61
|
+
)
|
|
62
|
+
) { return }
|
|
63
|
+
if (callback()) e.preventDefault()
|
|
64
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type JSX } from '../jsx-runtime'
|
|
2
|
+
|
|
3
|
+
export function isEmptyElement(element: JSX.DOMNode): boolean {
|
|
4
|
+
if (Array.isArray(element)) {
|
|
5
|
+
if (element.length === 0) return true
|
|
6
|
+
return element.every(isEmptyElement)
|
|
7
|
+
}
|
|
8
|
+
return element == null || element === ''
|
|
9
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { type Clear, type Clean } from './clean'
|
|
2
|
+
import { makeProviderMark, DOMContext, type ProviderMark } from './dom-context'
|
|
3
|
+
import { Prop, Signal } from './prop'
|
|
4
|
+
import { render } from './render'
|
|
5
|
+
import { type Renderable } from './renderable'
|
|
6
|
+
import { isEmptyElement } from './helpers/is-empty-element'
|
|
7
|
+
import { handleTextInput } from './helpers/handle-text-input'
|
|
8
|
+
import { handleAnchorClick } from './helpers/handle-anchor-click'
|
|
9
|
+
import { Animatable, applyInterpolatedAnimatableProp, applyInterpolatedAnimatable, applyAnimatableProp, applyAnimatable, getComputedAnimatableProp, getComputedAnimatable } from './components/animatable'
|
|
10
|
+
|
|
11
|
+
import { AttributeImpl, Attribute, type AttributeProps } from './components/Attribute'
|
|
12
|
+
import { BooleanAttributeImpl, BooleanAttribute, type BooleanAttributeProps } from './components/BooleanAttribute'
|
|
13
|
+
import { ClassNameImpl, ClassName, type ClassNameProps } from './components/ClassName'
|
|
14
|
+
import { ConsumerImpl, Consumer, type ConsumerProps, ProviderImpl, Provider, type ProviderProps } from './components/Provider'
|
|
15
|
+
import { ElImpl, El, type ElProps } from './components/El'
|
|
16
|
+
import { FadeIn, FadeInImpl, FadeInProps } from './components/FadeIn'
|
|
17
|
+
import { FadeOut, FadeOutImpl, FadeOutProps } from './components/FadeOut'
|
|
18
|
+
import { For, type ForProps } from './components/For'
|
|
19
|
+
import { FragmentImpl, Fragment } from './components/Fragment'
|
|
20
|
+
import { HiddenWhenEmptyImpl, HiddenWhenEmpty } from './components/HiddenWhenEmpty'
|
|
21
|
+
import { If, type IfProps, Unless, When, type WhenProps } from './components/If'
|
|
22
|
+
import { InnerHTMLImpl, InnerHTML, type InnerHTMLProps } from './components/InnerHTML'
|
|
23
|
+
import { Lifecycle, LifecycleImpl, type LifecycleProps } from './components/Lifecycle'
|
|
24
|
+
import { MatchImpl, Match, type MatchProps } from './components/Match'
|
|
25
|
+
import { NotEmpty, type NotEmptyProps } from './components/NotEmpty'
|
|
26
|
+
import { OnImpl, On, type OnProps } from './components/On'
|
|
27
|
+
import { OnRemoveImpl, OnRemove, type OnRemoveProps } from './components/OnRemove'
|
|
28
|
+
import { OneOfImpl, OneOf, type OneOfProps } from './components/OneOf'
|
|
29
|
+
import { PortalImpl, Portal, type PortalProps } from './components/Portal'
|
|
30
|
+
import { PropertyImpl, Property, type PropertyProps } from './components/Property'
|
|
31
|
+
import { RepeatImpl, Repeat, type RepeatProps } from './components/Repeat'
|
|
32
|
+
import { ShowImpl, Show, type ShowProps } from './components/Show'
|
|
33
|
+
import { TextImpl, Text, type TextProps } from './components/Text'
|
|
34
|
+
import { TextContentImpl, TextContent, type TextContentProps } from './components/TextContent'
|
|
35
|
+
|
|
36
|
+
import type { JSX } from './jsx-runtime'
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
applyInterpolatedAnimatableProp, applyInterpolatedAnimatable, applyAnimatableProp, applyAnimatable, getComputedAnimatableProp, getComputedAnimatable,
|
|
40
|
+
|
|
41
|
+
AttributeImpl, Attribute,
|
|
42
|
+
BooleanAttributeImpl, BooleanAttribute,
|
|
43
|
+
ClassNameImpl, ClassName,
|
|
44
|
+
ConsumerImpl, Consumer,
|
|
45
|
+
DOMContext,
|
|
46
|
+
ElImpl, El,
|
|
47
|
+
FadeIn, FadeInImpl,
|
|
48
|
+
FadeOut, FadeOutImpl,
|
|
49
|
+
For,
|
|
50
|
+
FragmentImpl, Fragment,
|
|
51
|
+
handleTextInput,
|
|
52
|
+
handleAnchorClick,
|
|
53
|
+
HiddenWhenEmptyImpl, HiddenWhenEmpty,
|
|
54
|
+
If,
|
|
55
|
+
InnerHTMLImpl, InnerHTML,
|
|
56
|
+
isEmptyElement,
|
|
57
|
+
Lifecycle, LifecycleImpl,
|
|
58
|
+
makeProviderMark,
|
|
59
|
+
MatchImpl, Match,
|
|
60
|
+
NotEmpty,
|
|
61
|
+
OnImpl, On,
|
|
62
|
+
OnRemoveImpl, OnRemove,
|
|
63
|
+
OneOfImpl, OneOf,
|
|
64
|
+
PortalImpl, Portal,
|
|
65
|
+
Prop,
|
|
66
|
+
PropertyImpl, Property,
|
|
67
|
+
ProviderImpl, Provider,
|
|
68
|
+
render,
|
|
69
|
+
RepeatImpl, Repeat,
|
|
70
|
+
ShowImpl, Show,
|
|
71
|
+
Signal,
|
|
72
|
+
TextImpl, Text,
|
|
73
|
+
TextContentImpl, TextContent,
|
|
74
|
+
Unless,
|
|
75
|
+
When
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type {
|
|
79
|
+
AttributeProps,
|
|
80
|
+
BooleanAttributeProps,
|
|
81
|
+
ClassNameProps,
|
|
82
|
+
Clean,
|
|
83
|
+
Clear,
|
|
84
|
+
ConsumerProps,
|
|
85
|
+
ElProps,
|
|
86
|
+
FadeInProps,
|
|
87
|
+
FadeOutProps,
|
|
88
|
+
ForProps,
|
|
89
|
+
InnerHTMLProps,
|
|
90
|
+
IfProps,
|
|
91
|
+
JSX,
|
|
92
|
+
LifecycleProps,
|
|
93
|
+
MatchProps,
|
|
94
|
+
NotEmptyProps,
|
|
95
|
+
OnProps,
|
|
96
|
+
OnRemoveProps,
|
|
97
|
+
OneOfProps,
|
|
98
|
+
PortalProps,
|
|
99
|
+
PropertyProps,
|
|
100
|
+
ProviderMark,
|
|
101
|
+
ProviderProps,
|
|
102
|
+
Renderable,
|
|
103
|
+
RepeatProps,
|
|
104
|
+
ShowProps,
|
|
105
|
+
TextProps,
|
|
106
|
+
TextContentProps,
|
|
107
|
+
Animatable,
|
|
108
|
+
WhenProps
|
|
109
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { type JSX } from './jsx'
|
|
2
|
+
import { type Renderable } from './renderable'
|
|
3
|
+
import { ElImpl } from './components/El'
|
|
4
|
+
import { AttributeImpl } from './components/Attribute'
|
|
5
|
+
import { BooleanAttributeImpl } from './components/BooleanAttribute'
|
|
6
|
+
import { PropertyImpl } from './components/Property'
|
|
7
|
+
import { ClassNameImpl } from './components/ClassName'
|
|
8
|
+
import { Prop, Signal } from './prop'
|
|
9
|
+
import { TextImpl } from './components/Text'
|
|
10
|
+
import { OnImpl } from './components/On'
|
|
11
|
+
import { FragmentImpl } from './components/Fragment'
|
|
12
|
+
|
|
13
|
+
const domBooleanAttributes = new Set([
|
|
14
|
+
'allowfullscreen',
|
|
15
|
+
'allowpaymentrequest',
|
|
16
|
+
'async',
|
|
17
|
+
'autofocus',
|
|
18
|
+
'autoplay',
|
|
19
|
+
'capture',
|
|
20
|
+
'controls',
|
|
21
|
+
'default',
|
|
22
|
+
'defer',
|
|
23
|
+
'disabled',
|
|
24
|
+
'disablepictureinpicture',
|
|
25
|
+
'disableremoteplayback',
|
|
26
|
+
'download',
|
|
27
|
+
'draggable',
|
|
28
|
+
'formnovalidate',
|
|
29
|
+
'hidden',
|
|
30
|
+
'ismap',
|
|
31
|
+
'itemscope',
|
|
32
|
+
'loop',
|
|
33
|
+
'nomodule',
|
|
34
|
+
'novalidate',
|
|
35
|
+
'open',
|
|
36
|
+
'playsinline',
|
|
37
|
+
'readonly',
|
|
38
|
+
'required',
|
|
39
|
+
'reversed',
|
|
40
|
+
'scoped',
|
|
41
|
+
'seamless',
|
|
42
|
+
'spellcheck',
|
|
43
|
+
'truespeed'
|
|
44
|
+
])
|
|
45
|
+
|
|
46
|
+
const domProperties = new Set([
|
|
47
|
+
'checked',
|
|
48
|
+
'checked',
|
|
49
|
+
'classList',
|
|
50
|
+
'className',
|
|
51
|
+
'contentEditable',
|
|
52
|
+
'dataset',
|
|
53
|
+
'innerHTML',
|
|
54
|
+
'multiple',
|
|
55
|
+
'muted',
|
|
56
|
+
'scrollLeft',
|
|
57
|
+
'scrollTop',
|
|
58
|
+
'selected',
|
|
59
|
+
'style',
|
|
60
|
+
'tabIndex',
|
|
61
|
+
'textContent',
|
|
62
|
+
'value'
|
|
63
|
+
])
|
|
64
|
+
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
66
|
+
function isPrimitive(value: any): value is string | number | boolean | Date | bigint {
|
|
67
|
+
return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint' || value instanceof Date
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
71
|
+
export function makeRenderables(value: any): Renderable[] {
|
|
72
|
+
if (value == null) {
|
|
73
|
+
return []
|
|
74
|
+
}
|
|
75
|
+
if (Array.isArray(value)) {
|
|
76
|
+
return value.flatMap(makeRenderables)
|
|
77
|
+
}
|
|
78
|
+
if (typeof value === 'string') {
|
|
79
|
+
return [new TextImpl(new Prop(value))]
|
|
80
|
+
}
|
|
81
|
+
if (Signal.isSignal(value)) {
|
|
82
|
+
return [new TextImpl(value as Prop<string>)]
|
|
83
|
+
}
|
|
84
|
+
if (typeof value === 'object' && 'appendTo' in value) {
|
|
85
|
+
return [value]
|
|
86
|
+
}
|
|
87
|
+
if (isPrimitive(value)) {
|
|
88
|
+
return [new TextImpl(new Prop(value).map(String))]
|
|
89
|
+
}
|
|
90
|
+
throw new Error(`Unkwown renderable: ${String(value)}`)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
94
|
+
export function makeRenderable(value: any): Renderable {
|
|
95
|
+
const renderables = makeRenderables(value)
|
|
96
|
+
if (renderables.length === 0) {
|
|
97
|
+
return new FragmentImpl([])
|
|
98
|
+
}
|
|
99
|
+
if (renderables.length === 1) {
|
|
100
|
+
return renderables[0]
|
|
101
|
+
}
|
|
102
|
+
return new FragmentImpl(renderables)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
106
|
+
function isNamedConstructor(obj: any): boolean {
|
|
107
|
+
return obj.prototype?.constructor?.name != null
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
111
|
+
function makeFragment({ children }: { children: any[] }): Renderable {
|
|
112
|
+
if (Array.isArray(children)) {
|
|
113
|
+
return new FragmentImpl(children.flatMap(makeRenderables))
|
|
114
|
+
} else {
|
|
115
|
+
return makeRenderable(children)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
120
|
+
function makeElement(Name: any, ...args: any[]): Renderable {
|
|
121
|
+
if (typeof Name === 'function') {
|
|
122
|
+
if (isNamedConstructor(Name)) {
|
|
123
|
+
const el = new Name(...args)
|
|
124
|
+
return el
|
|
125
|
+
} else {
|
|
126
|
+
const el = Name(...args)
|
|
127
|
+
return el
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const { children: untypedChildren, ...rest } = args[0] ?? {}
|
|
131
|
+
const children: Renderable[] = []
|
|
132
|
+
if (Array.isArray(untypedChildren)) {
|
|
133
|
+
children.push(...(untypedChildren.flatMap(makeRenderables)))
|
|
134
|
+
} else if (untypedChildren !== undefined) {
|
|
135
|
+
children.push(...makeRenderables(untypedChildren))
|
|
136
|
+
}
|
|
137
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
138
|
+
if (key.startsWith('on')) {
|
|
139
|
+
const eventName = key.slice(2).toLowerCase()
|
|
140
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
141
|
+
children.push(new OnImpl(eventName, value as any))
|
|
142
|
+
continue
|
|
143
|
+
}
|
|
144
|
+
if (value == null) {
|
|
145
|
+
continue
|
|
146
|
+
}
|
|
147
|
+
const prop: Prop<unknown> = isPrimitive(value) ? new Prop(value) as Prop<unknown> : value as Prop<unknown>
|
|
148
|
+
if (key === 'class' || key === 'className') {
|
|
149
|
+
children.push(new ClassNameImpl(prop as Prop<string>))
|
|
150
|
+
continue
|
|
151
|
+
}
|
|
152
|
+
if (domProperties.has(key)) {
|
|
153
|
+
children.push(new PropertyImpl(key, prop))
|
|
154
|
+
continue
|
|
155
|
+
}
|
|
156
|
+
if (domBooleanAttributes.has(key)) {
|
|
157
|
+
children.push(new BooleanAttributeImpl(key, prop as Prop<boolean>))
|
|
158
|
+
continue
|
|
159
|
+
}
|
|
160
|
+
children.push(new AttributeImpl(key, prop as Prop<string>))
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return new ElImpl(Name, children)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export {
|
|
167
|
+
makeElement as jsx,
|
|
168
|
+
makeElement as jsxs,
|
|
169
|
+
makeElement as jsxDEV,
|
|
170
|
+
makeFragment as Fragment,
|
|
171
|
+
type JSX
|
|
172
|
+
}
|