glass-easel 0.1.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/README.md +40 -0
- package/dist/glass_easel.all.d.ts +1 -0
- package/dist/glass_easel.all.js +2 -0
- package/dist/glass_easel.all.js.map +1 -0
- package/dist/glass_easel.domlike.global.d.ts +1 -0
- package/dist/glass_easel.domlike.global.js +2 -0
- package/dist/glass_easel.domlike.global.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types/src/backend/backend_protocol.d.ts +119 -0
- package/dist/types/src/backend/backend_protocol.d.ts.map +1 -0
- package/dist/types/src/backend/composed_backend_protocol.d.ts +90 -0
- package/dist/types/src/backend/composed_backend_protocol.d.ts.map +1 -0
- package/dist/types/src/backend/domlike_backend_protocol.d.ts +76 -0
- package/dist/types/src/backend/domlike_backend_protocol.d.ts.map +1 -0
- package/dist/types/src/backend/mode.d.ts +46 -0
- package/dist/types/src/backend/mode.d.ts.map +1 -0
- package/dist/types/src/backend/suggested_backend_protocol.d.ts +30 -0
- package/dist/types/src/backend/suggested_backend_protocol.d.ts.map +1 -0
- package/dist/types/src/behavior.d.ts +428 -0
- package/dist/types/src/behavior.d.ts.map +1 -0
- package/dist/types/src/class_list.d.ts +79 -0
- package/dist/types/src/class_list.d.ts.map +1 -0
- package/dist/types/src/component.d.ts +291 -0
- package/dist/types/src/component.d.ts.map +1 -0
- package/dist/types/src/component_params.d.ts +239 -0
- package/dist/types/src/component_params.d.ts.map +1 -0
- package/dist/types/src/component_space.d.ts +164 -0
- package/dist/types/src/component_space.d.ts.map +1 -0
- package/dist/types/src/data_path.d.ts +5 -0
- package/dist/types/src/data_path.d.ts.map +1 -0
- package/dist/types/src/data_proxy.d.ts +107 -0
- package/dist/types/src/data_proxy.d.ts.map +1 -0
- package/dist/types/src/data_utils.d.ts +3 -0
- package/dist/types/src/data_utils.d.ts.map +1 -0
- package/dist/types/src/element.d.ts +275 -0
- package/dist/types/src/element.d.ts.map +1 -0
- package/dist/types/src/element_iterator.d.ts +43 -0
- package/dist/types/src/element_iterator.d.ts.map +1 -0
- package/dist/types/src/event.d.ts +104 -0
- package/dist/types/src/event.d.ts.map +1 -0
- package/dist/types/src/external_shadow_tree.d.ts +20 -0
- package/dist/types/src/external_shadow_tree.d.ts.map +1 -0
- package/dist/types/src/func_arr.d.ts +39 -0
- package/dist/types/src/func_arr.d.ts.map +1 -0
- package/dist/types/src/global_options.d.ts +111 -0
- package/dist/types/src/global_options.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +43 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/mutation_observer.d.ts +79 -0
- package/dist/types/src/mutation_observer.d.ts.map +1 -0
- package/dist/types/src/native_node.d.ts +8 -0
- package/dist/types/src/native_node.d.ts.map +1 -0
- package/dist/types/src/node.d.ts +49 -0
- package/dist/types/src/node.d.ts.map +1 -0
- package/dist/types/src/relation.d.ts +47 -0
- package/dist/types/src/relation.d.ts.map +1 -0
- package/dist/types/src/render.d.ts +3 -0
- package/dist/types/src/render.d.ts.map +1 -0
- package/dist/types/src/selector.d.ts +32 -0
- package/dist/types/src/selector.d.ts.map +1 -0
- package/dist/types/src/shadow_root.d.ts +136 -0
- package/dist/types/src/shadow_root.d.ts.map +1 -0
- package/dist/types/src/template_engine.d.ts +18 -0
- package/dist/types/src/template_engine.d.ts.map +1 -0
- package/dist/types/src/text_node.d.ts +32 -0
- package/dist/types/src/text_node.d.ts.map +1 -0
- package/dist/types/src/tmpl/index.d.ts +18 -0
- package/dist/types/src/tmpl/index.d.ts.map +1 -0
- package/dist/types/src/tmpl/native_rendering.d.ts +45 -0
- package/dist/types/src/tmpl/native_rendering.d.ts.map +1 -0
- package/dist/types/src/tmpl/proc_gen_wrapper.d.ts +80 -0
- package/dist/types/src/tmpl/proc_gen_wrapper.d.ts.map +1 -0
- package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts +50 -0
- package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts.map +1 -0
- package/dist/types/src/tmpl/range_list_diff.d.ts +19 -0
- package/dist/types/src/tmpl/range_list_diff.d.ts.map +1 -0
- package/dist/types/src/trait_behaviors.d.ts +38 -0
- package/dist/types/src/trait_behaviors.d.ts.map +1 -0
- package/dist/types/src/virtual_node.d.ts +10 -0
- package/dist/types/src/virtual_node.d.ts.map +1 -0
- package/dist/types/tests/backend/domlike.test.d.ts +2 -0
- package/dist/types/tests/backend/domlike.test.d.ts.map +1 -0
- package/dist/types/tests/base/env.d.ts +29 -0
- package/dist/types/tests/base/env.d.ts.map +1 -0
- package/dist/types/tests/base/match.d.ts +9 -0
- package/dist/types/tests/base/match.d.ts.map +1 -0
- package/dist/types/tests/core/backend.test.d.ts +2 -0
- package/dist/types/tests/core/backend.test.d.ts.map +1 -0
- package/dist/types/tests/core/behavior.test.d.ts +2 -0
- package/dist/types/tests/core/behavior.test.d.ts.map +1 -0
- package/dist/types/tests/core/component_space.test.d.ts +2 -0
- package/dist/types/tests/core/component_space.test.d.ts.map +1 -0
- package/dist/types/tests/core/data_update.test.d.ts +2 -0
- package/dist/types/tests/core/data_update.test.d.ts.map +1 -0
- package/dist/types/tests/core/misc.test.d.ts +2 -0
- package/dist/types/tests/core/misc.test.d.ts.map +1 -0
- package/dist/types/tests/core/placeholder.test.d.ts +2 -0
- package/dist/types/tests/core/placeholder.test.d.ts.map +1 -0
- package/dist/types/tests/core/slot.test.d.ts +2 -0
- package/dist/types/tests/core/slot.test.d.ts.map +1 -0
- package/dist/types/tests/core/trait_behaviors.test.d.ts +2 -0
- package/dist/types/tests/core/trait_behaviors.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/binding_map.test.d.ts +2 -0
- package/dist/types/tests/tmpl/binding_map.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/event.test.d.ts +2 -0
- package/dist/types/tests/tmpl/event.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/expression.test.d.ts +2 -0
- package/dist/types/tests/tmpl/expression.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/lvalue.test.d.ts +2 -0
- package/dist/types/tests/tmpl/lvalue.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/native_rendering.test.d.ts +2 -0
- package/dist/types/tests/tmpl/native_rendering.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/structure.test.d.ts +2 -0
- package/dist/types/tests/tmpl/structure.test.d.ts.map +1 -0
- package/dist/types/tests/types/chaining.test.d.ts +2 -0
- package/dist/types/tests/types/chaining.test.d.ts.map +1 -0
- package/dist/types/tests/types/createElement.test.d.ts +2 -0
- package/dist/types/tests/types/createElement.test.d.ts.map +1 -0
- package/dist/types/tests/types/definition.test.d.ts +2 -0
- package/dist/types/tests/types/definition.test.d.ts.map +1 -0
- package/guide/zh_CN/advanced/binding_map_update.md +32 -0
- package/guide/zh_CN/advanced/build_args.md +28 -0
- package/guide/zh_CN/advanced/component_filter.md +70 -0
- package/guide/zh_CN/advanced/component_space.md +124 -0
- package/guide/zh_CN/advanced/custom_backend.md +53 -0
- package/guide/zh_CN/advanced/error_listener.md +32 -0
- package/guide/zh_CN/advanced/external_component.md +73 -0
- package/guide/zh_CN/advanced/template_engine.md +61 -0
- package/guide/zh_CN/appendix/backend_protocol.md +501 -0
- package/guide/zh_CN/appendix/list_diff_algorithm.md +406 -0
- package/guide/zh_CN/basic/beginning.md +94 -0
- package/guide/zh_CN/basic/component.md +156 -0
- package/guide/zh_CN/basic/event.md +169 -0
- package/guide/zh_CN/basic/lifetime.md +66 -0
- package/guide/zh_CN/basic/method.md +62 -0
- package/guide/zh_CN/basic/template.md +135 -0
- package/guide/zh_CN/data_management/advanced_update.md +170 -0
- package/guide/zh_CN/data_management/data_deep_copy.md +157 -0
- package/guide/zh_CN/data_management/data_observer.md +154 -0
- package/guide/zh_CN/data_management/property_early_init.md +31 -0
- package/guide/zh_CN/data_management/pure_data_pattern.md +21 -0
- package/guide/zh_CN/index.md +93 -0
- package/guide/zh_CN/interaction/behavior.md +52 -0
- package/guide/zh_CN/interaction/component_path.md +37 -0
- package/guide/zh_CN/interaction/generic.md +73 -0
- package/guide/zh_CN/interaction/placeholder.md +40 -0
- package/guide/zh_CN/interaction/relation.md +151 -0
- package/guide/zh_CN/interaction/slot.md +137 -0
- package/guide/zh_CN/interaction/template_import.md +94 -0
- package/guide/zh_CN/interaction/trait_behavior.md +117 -0
- package/guide/zh_CN/styling/external_class.md +46 -0
- package/guide/zh_CN/styling/style_isolation.md +54 -0
- package/guide/zh_CN/styling/virtual_host.md +52 -0
- package/guide/zh_CN/tree/element_iterator.md +54 -0
- package/guide/zh_CN/tree/mutation_observer.md +52 -0
- package/guide/zh_CN/tree/node_tree.md +142 -0
- package/guide/zh_CN/tree/node_tree_modification.md +78 -0
- package/guide/zh_CN/tree/selector.md +66 -0
- package/jest.config.js +6 -0
- package/jest.dts.config.js +9 -0
- package/jest.unit.config.js +14 -0
- package/package.json +28 -0
- package/src/backend/backend_protocol.ts +313 -0
- package/src/backend/composed_backend_protocol.ts +252 -0
- package/src/backend/domlike_backend_protocol.ts +370 -0
- package/src/backend/mode.ts +51 -0
- package/src/backend/suggested_backend_protocol.ts +83 -0
- package/src/behavior.ts +1655 -0
- package/src/bootstrap_dom_dev.js +22 -0
- package/src/class_list.ts +376 -0
- package/src/component.ts +1309 -0
- package/src/component_params.ts +461 -0
- package/src/component_space.ts +547 -0
- package/src/data_path.ts +225 -0
- package/src/data_proxy.ts +670 -0
- package/src/data_utils.ts +50 -0
- package/src/element.ts +1966 -0
- package/src/element_iterator.ts +158 -0
- package/src/event.ts +401 -0
- package/src/external_shadow_tree.ts +27 -0
- package/src/func_arr.ts +198 -0
- package/src/global_options.ts +242 -0
- package/src/index.ts +187 -0
- package/src/mutation_observer.ts +252 -0
- package/src/native_node.ts +74 -0
- package/src/node.ts +174 -0
- package/src/relation.ts +380 -0
- package/src/render.ts +25 -0
- package/src/selector.ts +218 -0
- package/src/shadow_root.ts +766 -0
- package/src/template_engine.ts +45 -0
- package/src/text_node.ts +149 -0
- package/src/tmpl/index.ts +199 -0
- package/src/tmpl/native_rendering.ts +175 -0
- package/src/tmpl/proc_gen_wrapper.ts +954 -0
- package/src/tmpl/proc_gen_wrapper_dom.ts +230 -0
- package/src/tmpl/range_list_diff.ts +443 -0
- package/src/trait_behaviors.ts +51 -0
- package/src/virtual_node.ts +51 -0
- package/tests/backend/domlike.test.ts +254 -0
- package/tests/base/env.ts +78 -0
- package/tests/base/match.ts +185 -0
- package/tests/core/backend.test.ts +144 -0
- package/tests/core/behavior.test.ts +546 -0
- package/tests/core/component_space.test.ts +212 -0
- package/tests/core/data_update.test.ts +461 -0
- package/tests/core/misc.test.ts +339 -0
- package/tests/core/placeholder.test.ts +180 -0
- package/tests/core/slot.test.ts +1495 -0
- package/tests/core/trait_behaviors.test.ts +153 -0
- package/tests/legacy/README.md +3 -0
- package/tests/legacy/behavior.test.js +293 -0
- package/tests/legacy/component.test.js +1247 -0
- package/tests/legacy/data_path.test.js +149 -0
- package/tests/legacy/data_proxy.test.js +759 -0
- package/tests/legacy/element_iterator.test.js +148 -0
- package/tests/legacy/event.test.js +849 -0
- package/tests/legacy/external.test.js +510 -0
- package/tests/legacy/extra_info.test.js +109 -0
- package/tests/legacy/generics.test.js +176 -0
- package/tests/legacy/mutation_observer.test.js +210 -0
- package/tests/legacy/relation.test.js +517 -0
- package/tests/legacy/selector.test.js +263 -0
- package/tests/legacy/slot.test.js +915 -0
- package/tests/legacy/virtual.test.js +394 -0
- package/tests/tmpl/binding_map.test.ts +208 -0
- package/tests/tmpl/event.test.ts +206 -0
- package/tests/tmpl/expression.test.ts +429 -0
- package/tests/tmpl/lvalue.test.ts +160 -0
- package/tests/tmpl/native_rendering.test.ts +155 -0
- package/tests/tmpl/structure.test.ts +998 -0
- package/tests/types/chaining.test.ts +614 -0
- package/tests/types/createElement.test.ts +82 -0
- package/tests/types/definition.test.ts +442 -0
- package/tsconfig.json +11 -0
- package/webpack.config.js +270 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { ComponentSpace } from '.'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Interface that can be implement dynamically
|
|
5
|
+
*
|
|
6
|
+
* A `TraitBehavior` is like a TypeScript interface, but can be implemented dynamically.
|
|
7
|
+
* It requires the implementors to implement `TIn` .
|
|
8
|
+
* Also, it can provide do a transform from `TIn` to `TOut` as common logic of the trait.
|
|
9
|
+
*/
|
|
10
|
+
export class TraitBehavior<
|
|
11
|
+
TIn extends { [key: string]: unknown },
|
|
12
|
+
TOut extends { [key: string]: unknown } = TIn,
|
|
13
|
+
> {
|
|
14
|
+
/** @internal */
|
|
15
|
+
private _$trans?: (impl: TIn) => TOut
|
|
16
|
+
ownerSpace: ComponentSpace
|
|
17
|
+
|
|
18
|
+
/** @internal */
|
|
19
|
+
// eslint-disable-next-line no-useless-constructor
|
|
20
|
+
constructor(ownerSpace: ComponentSpace, trans?: (impl: TIn) => TOut) {
|
|
21
|
+
this.ownerSpace = ownerSpace
|
|
22
|
+
this._$trans = trans
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** @internal */
|
|
26
|
+
_$implement(impl: TIn): TOut {
|
|
27
|
+
return this._$trans?.(impl) || (impl as unknown as TOut)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A manager that can implement multiple different trait behaviors
|
|
33
|
+
*/
|
|
34
|
+
export class TraitGroup {
|
|
35
|
+
private _$traits: WeakMap<TraitBehavior<any, any>, unknown> = new WeakMap()
|
|
36
|
+
|
|
37
|
+
implement<
|
|
38
|
+
TIn extends { [key: string]: unknown },
|
|
39
|
+
TOut extends { [key: string]: unknown }
|
|
40
|
+
>(traitBehavior: TraitBehavior<TIn, TOut>, impl: TIn) {
|
|
41
|
+
const traitImpl = traitBehavior._$implement(impl)
|
|
42
|
+
this._$traits.set(traitBehavior, traitImpl)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
get<
|
|
46
|
+
TIn extends { [key: string]: unknown },
|
|
47
|
+
TOut extends { [key: string]: unknown }
|
|
48
|
+
>(traitBehavior: TraitBehavior<TIn, TOut>): TOut | undefined {
|
|
49
|
+
return this._$traits.get(traitBehavior) as TOut | undefined
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Element,
|
|
3
|
+
} from './element'
|
|
4
|
+
import {
|
|
5
|
+
ShadowRoot,
|
|
6
|
+
} from './shadow_root'
|
|
7
|
+
import {
|
|
8
|
+
BM,
|
|
9
|
+
BackendMode,
|
|
10
|
+
} from './backend/mode'
|
|
11
|
+
import {
|
|
12
|
+
GeneralBackendContext, GeneralBackendElement,
|
|
13
|
+
} from '.'
|
|
14
|
+
|
|
15
|
+
export class VirtualNode extends Element {
|
|
16
|
+
is: string
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
throw new Error('Element cannot be constructed directly')
|
|
20
|
+
// eslint-disable-next-line no-unreachable
|
|
21
|
+
super()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
protected _$initializeVirtual(
|
|
25
|
+
virtualName: string,
|
|
26
|
+
owner: ShadowRoot | null,
|
|
27
|
+
nodeTreeContext: GeneralBackendContext,
|
|
28
|
+
) {
|
|
29
|
+
this.is = String(virtualName)
|
|
30
|
+
let backendElement: GeneralBackendElement | null = null
|
|
31
|
+
if (owner) {
|
|
32
|
+
if (BM.SHADOW || (BM.DYNAMIC && nodeTreeContext.mode === BackendMode.Shadow)) {
|
|
33
|
+
const shadowRoot = owner._$backendShadowRoot
|
|
34
|
+
backendElement = shadowRoot?.createVirtualNode() || null
|
|
35
|
+
this._$initialize(true, backendElement, owner)
|
|
36
|
+
backendElement?.associateValue(this)
|
|
37
|
+
} else {
|
|
38
|
+
this._$initialize(true, null, owner)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static create(
|
|
44
|
+
virtualName: string,
|
|
45
|
+
owner: ShadowRoot,
|
|
46
|
+
): VirtualNode {
|
|
47
|
+
const node = Object.create(VirtualNode.prototype) as VirtualNode
|
|
48
|
+
node._$initializeVirtual(virtualName, owner, owner._$nodeTreeContext)
|
|
49
|
+
return node
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/* globals document */
|
|
2
|
+
|
|
3
|
+
import { tmpl, domBackend } from '../base/env'
|
|
4
|
+
import * as glassEasel from '../../src'
|
|
5
|
+
|
|
6
|
+
const componentSpace = new glassEasel.ComponentSpace()
|
|
7
|
+
componentSpace.updateComponentOptions({
|
|
8
|
+
writeFieldsToNode: true,
|
|
9
|
+
writeIdToDOM: true,
|
|
10
|
+
})
|
|
11
|
+
componentSpace.defineComponent({
|
|
12
|
+
is: '',
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
describe('domlike backend', () => {
|
|
16
|
+
beforeAll(() => {
|
|
17
|
+
domBackend.onEvent((target: glassEasel.domlikeBackend.Element, type, detail, options) => {
|
|
18
|
+
let cur = target as Element & glassEasel.domlikeBackend.Element
|
|
19
|
+
while (cur && !cur.__wxElement) {
|
|
20
|
+
cur = cur.parentNode as Element & glassEasel.domlikeBackend.Element
|
|
21
|
+
}
|
|
22
|
+
if (!cur) return
|
|
23
|
+
const ev = new glassEasel.Event(type, detail, options)
|
|
24
|
+
glassEasel.Event.dispatchEvent(target.__wxElement as any, ev)
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
describe('events', () => {
|
|
29
|
+
it('tap event', () => {
|
|
30
|
+
const ops: number[] = []
|
|
31
|
+
let touch: Touch
|
|
32
|
+
|
|
33
|
+
const Comp = componentSpace.define()
|
|
34
|
+
.template(tmpl(`
|
|
35
|
+
<div
|
|
36
|
+
bind:tap="onTapWrapper"
|
|
37
|
+
bind:touchstart="onTouchStartWrapper"
|
|
38
|
+
>
|
|
39
|
+
<span
|
|
40
|
+
id="button"
|
|
41
|
+
bind:tap="onTapButton"
|
|
42
|
+
bind:touchstart="onTouchStartButton"
|
|
43
|
+
></span>
|
|
44
|
+
</div>
|
|
45
|
+
`))
|
|
46
|
+
.init(({ listener }) => {
|
|
47
|
+
const onTapWrapper = listener<{ x: number, y: number }>((e) => {
|
|
48
|
+
ops.push(1)
|
|
49
|
+
expect(e.bubbles).toBe(true)
|
|
50
|
+
expect(e.detail).toEqual({ x: -1, y: -1 })
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const onTapButton = listener<{ x: number, y: number }>((e) => {
|
|
54
|
+
ops.push(2)
|
|
55
|
+
expect(e.bubbles).toBe(true)
|
|
56
|
+
expect(e.detail).toEqual({ x: -1, y: -1 })
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
const onTouchStartWrapper = listener((e) => {
|
|
60
|
+
ops.push(3)
|
|
61
|
+
expect(e.bubbles).toBe(true)
|
|
62
|
+
expect(e.detail).toEqual({
|
|
63
|
+
altKey: false,
|
|
64
|
+
changedTouches: [touch],
|
|
65
|
+
ctrlKey: false,
|
|
66
|
+
isTrusted: false,
|
|
67
|
+
metaKey: false,
|
|
68
|
+
shiftKey: false,
|
|
69
|
+
targetTouches: [],
|
|
70
|
+
touches: [touch],
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
const onTouchStartButton = listener((e) => {
|
|
75
|
+
ops.push(4)
|
|
76
|
+
expect(e.bubbles).toBe(true)
|
|
77
|
+
expect(e.detail).toEqual({
|
|
78
|
+
altKey: false,
|
|
79
|
+
changedTouches: [touch],
|
|
80
|
+
ctrlKey: false,
|
|
81
|
+
isTrusted: false,
|
|
82
|
+
metaKey: false,
|
|
83
|
+
shiftKey: false,
|
|
84
|
+
targetTouches: [],
|
|
85
|
+
touches: [touch],
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
onTapWrapper,
|
|
91
|
+
onTapButton,
|
|
92
|
+
onTouchStartWrapper,
|
|
93
|
+
onTouchStartButton,
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
.registerComponent()
|
|
97
|
+
|
|
98
|
+
const comp = glassEasel.Component.createWithContext('root', Comp, domBackend)
|
|
99
|
+
const placeholder = document.createElement('span')
|
|
100
|
+
document.body.appendChild(placeholder)
|
|
101
|
+
glassEasel.Element.replaceDocumentElement(
|
|
102
|
+
comp,
|
|
103
|
+
document.body as any as glassEasel.GeneralBackendElement,
|
|
104
|
+
placeholder as any as glassEasel.GeneralBackendElement,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
const button = (comp.$.button as glassEasel.Element).$$ as any as HTMLSpanElement
|
|
108
|
+
|
|
109
|
+
touch = {
|
|
110
|
+
identifier: 0,
|
|
111
|
+
clientX: -1,
|
|
112
|
+
clientY: -1,
|
|
113
|
+
pageX: -1,
|
|
114
|
+
pageY: -1,
|
|
115
|
+
screenX: -1,
|
|
116
|
+
screenY: -1,
|
|
117
|
+
force: 1,
|
|
118
|
+
radiusX: 1,
|
|
119
|
+
radiusY: 1,
|
|
120
|
+
rotationAngle: 0,
|
|
121
|
+
target: button,
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const touchstart = new TouchEvent('touchstart', {
|
|
125
|
+
touches: [touch],
|
|
126
|
+
changedTouches: [touch],
|
|
127
|
+
bubbles: true,
|
|
128
|
+
composed: true,
|
|
129
|
+
})
|
|
130
|
+
button.dispatchEvent(touchstart)
|
|
131
|
+
const touchend = new TouchEvent('touchend', {
|
|
132
|
+
touches: [],
|
|
133
|
+
changedTouches: [touch],
|
|
134
|
+
bubbles: true,
|
|
135
|
+
composed: true,
|
|
136
|
+
})
|
|
137
|
+
button.dispatchEvent(touchend)
|
|
138
|
+
expect(ops).toEqual([4, 3, 2, 1])
|
|
139
|
+
})
|
|
140
|
+
it('non-delegate event', () => {
|
|
141
|
+
const ops: number[] = []
|
|
142
|
+
|
|
143
|
+
const Comp = componentSpace.define()
|
|
144
|
+
.template(tmpl(`
|
|
145
|
+
<div
|
|
146
|
+
bind:keydown="onKeydownWrapper"
|
|
147
|
+
>
|
|
148
|
+
<span
|
|
149
|
+
id="button"
|
|
150
|
+
bind:keydown="onKeydownButton"
|
|
151
|
+
></span>
|
|
152
|
+
</div>
|
|
153
|
+
`))
|
|
154
|
+
.init(({ listener }) => {
|
|
155
|
+
const onKeydownWrapper = listener((e) => {
|
|
156
|
+
ops.push(5)
|
|
157
|
+
expect(e.bubbles).toBe(true)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
const onKeydownButton = listener((e) => {
|
|
161
|
+
ops.push(6)
|
|
162
|
+
expect(e.bubbles).toBe(true)
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
onKeydownWrapper,
|
|
167
|
+
onKeydownButton,
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
.registerComponent()
|
|
171
|
+
|
|
172
|
+
const comp = glassEasel.Component.createWithContext('root', Comp, domBackend)
|
|
173
|
+
const placeholder = document.createElement('span')
|
|
174
|
+
document.body.appendChild(placeholder)
|
|
175
|
+
glassEasel.Element.replaceDocumentElement(
|
|
176
|
+
comp,
|
|
177
|
+
document.body as any as glassEasel.GeneralBackendElement,
|
|
178
|
+
placeholder as any as glassEasel.GeneralBackendElement,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
const button = (comp.$.button as glassEasel.Element).$$ as any as HTMLSpanElement
|
|
182
|
+
|
|
183
|
+
const keydown = new Event('keydown', {
|
|
184
|
+
bubbles: true,
|
|
185
|
+
composed: true,
|
|
186
|
+
})
|
|
187
|
+
button.dispatchEvent(keydown)
|
|
188
|
+
expect(ops).toEqual([6, 5])
|
|
189
|
+
})
|
|
190
|
+
it('non-bubble event', () => {
|
|
191
|
+
const ops: number[] = []
|
|
192
|
+
|
|
193
|
+
const Comp = componentSpace.define()
|
|
194
|
+
.template(tmpl(`
|
|
195
|
+
<div
|
|
196
|
+
bind:focus="onFocusWrapper"
|
|
197
|
+
>
|
|
198
|
+
<span
|
|
199
|
+
id="button"
|
|
200
|
+
bind:focus="onFocusButton"
|
|
201
|
+
></span>
|
|
202
|
+
</div>
|
|
203
|
+
`))
|
|
204
|
+
.init(({ listener }) => {
|
|
205
|
+
const onFocusWrapper = listener((e) => {
|
|
206
|
+
ops.push(5)
|
|
207
|
+
expect(e.bubbles).toBe(false)
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
const onFocusButton = listener((e) => {
|
|
211
|
+
ops.push(6)
|
|
212
|
+
expect(e.bubbles).toBe(false)
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
onFocusWrapper,
|
|
217
|
+
onFocusButton,
|
|
218
|
+
}
|
|
219
|
+
})
|
|
220
|
+
.registerComponent()
|
|
221
|
+
|
|
222
|
+
const comp = glassEasel.Component.createWithContext('root', Comp, domBackend)
|
|
223
|
+
const placeholder = document.createElement('span')
|
|
224
|
+
document.body.appendChild(placeholder)
|
|
225
|
+
glassEasel.Element.replaceDocumentElement(
|
|
226
|
+
comp,
|
|
227
|
+
document.body as any as glassEasel.GeneralBackendElement,
|
|
228
|
+
placeholder as any as glassEasel.GeneralBackendElement,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
const button = (comp.$.button as glassEasel.Element).$$ as any as HTMLSpanElement
|
|
232
|
+
|
|
233
|
+
const focus = new Event('focus', {
|
|
234
|
+
bubbles: false,
|
|
235
|
+
composed: false,
|
|
236
|
+
})
|
|
237
|
+
button.dispatchEvent(focus)
|
|
238
|
+
expect(ops).toEqual([6])
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
describe('render', () => {
|
|
243
|
+
it('render should work', async () => {
|
|
244
|
+
let renderCallback = false
|
|
245
|
+
await new Promise<void>((resolve) => {
|
|
246
|
+
domBackend.render(() => {
|
|
247
|
+
renderCallback = true
|
|
248
|
+
resolve()
|
|
249
|
+
})
|
|
250
|
+
})
|
|
251
|
+
expect(renderCallback).toBe(true)
|
|
252
|
+
})
|
|
253
|
+
})
|
|
254
|
+
})
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/* eslint-disable no-new-func */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-implied-eval */
|
|
3
|
+
|
|
4
|
+
import { TmplGroup } from 'glass-easel-template-compiler'
|
|
5
|
+
import * as glassEasel from '../../src'
|
|
6
|
+
|
|
7
|
+
glassEasel.globalOptions.throwGlobalError = true
|
|
8
|
+
const warningThrow = (msg: string) => {
|
|
9
|
+
throw new Error(msg)
|
|
10
|
+
}
|
|
11
|
+
glassEasel.addGlobalWarningListener(warningThrow)
|
|
12
|
+
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
14
|
+
;(glassEasel.Element.prototype as any).toJSON = function () {
|
|
15
|
+
return `[GlassEaselElement ${glassEasel.dumpSingleElementToString(this)}]`
|
|
16
|
+
}
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
18
|
+
;(glassEasel.TextNode.prototype as any).toJSON = function () {
|
|
19
|
+
return `[GlassEaselTextNode ${glassEasel.dumpSingleElementToString(this)}]`
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const execWithWarn = <T>(expectCount: number, func: () => T): T => {
|
|
23
|
+
let count = 0
|
|
24
|
+
const warningListener = () => {
|
|
25
|
+
count += 1
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
glassEasel.removeGlobalWarningListener(warningThrow)
|
|
29
|
+
glassEasel.addGlobalWarningListener(warningListener)
|
|
30
|
+
const ret = func()
|
|
31
|
+
glassEasel.removeGlobalWarningListener(warningListener)
|
|
32
|
+
glassEasel.addGlobalWarningListener(warningThrow)
|
|
33
|
+
expect(count).toBe(expectCount)
|
|
34
|
+
return ret
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type TemplateOptions = {
|
|
38
|
+
updateMode?: string
|
|
39
|
+
disallowNativeNode?: boolean
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const tmpl = (src: string, options?: TemplateOptions) => {
|
|
43
|
+
const group = new TmplGroup()
|
|
44
|
+
group.addTmpl('', src)
|
|
45
|
+
const genObjectSrc = `return ${group.getTmplGenObjectGroups()}`
|
|
46
|
+
group.free()
|
|
47
|
+
// console.info(genObjectSrc)
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
49
|
+
const genObjectGroupList = (new Function(genObjectSrc))() as { [key: string]: any }
|
|
50
|
+
return {
|
|
51
|
+
groupList: genObjectGroupList,
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
53
|
+
content: genObjectGroupList[''],
|
|
54
|
+
...options,
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const multiTmpl = (src: { [path: string]: string }, options?: TemplateOptions) => {
|
|
59
|
+
const group = new TmplGroup()
|
|
60
|
+
Object.keys(src).forEach((path) => {
|
|
61
|
+
group.addTmpl(path, src[path]!)
|
|
62
|
+
})
|
|
63
|
+
const genObjectSrc = `return ${group.getTmplGenObjectGroups()}`
|
|
64
|
+
group.free()
|
|
65
|
+
// console.info(genObjectSrc)
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
67
|
+
const genObjectGroupList = (new Function(genObjectSrc))() as { [key: string]: any }
|
|
68
|
+
return {
|
|
69
|
+
groupList: genObjectGroupList,
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
71
|
+
content: genObjectGroupList[''],
|
|
72
|
+
...options,
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const domBackend = new glassEasel.domlikeBackend.CurrentWindowBackendContext()
|
|
77
|
+
export const shadowBackend = new glassEasel.backend.EmptyBackendContext()
|
|
78
|
+
export const composedBackend = new glassEasel.composedBackend.EmptyComposedBackendContext()
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
GeneralComponent,
|
|
4
|
+
ExternalShadowRoot,
|
|
5
|
+
ShadowRoot,
|
|
6
|
+
Element,
|
|
7
|
+
domlikeBackend,
|
|
8
|
+
TextNode,
|
|
9
|
+
VirtualNode,
|
|
10
|
+
Node,
|
|
11
|
+
BackendMode,
|
|
12
|
+
} from '../../src'
|
|
13
|
+
|
|
14
|
+
const getSingleSlotElement = (comp: GeneralComponent) => {
|
|
15
|
+
if (comp._$external) {
|
|
16
|
+
const sr = comp.shadowRoot as ExternalShadowRoot
|
|
17
|
+
return sr.slot as unknown as HTMLElement
|
|
18
|
+
}
|
|
19
|
+
const sr = comp.shadowRoot as ShadowRoot
|
|
20
|
+
return sr.getSingleSlotElement()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// check internal structure of an external component (with its child nodes given)
|
|
24
|
+
export const native = (
|
|
25
|
+
structure: {
|
|
26
|
+
element: GeneralComponent,
|
|
27
|
+
childNodes?: { element: GeneralComponent }[],
|
|
28
|
+
},
|
|
29
|
+
) => {
|
|
30
|
+
const elem = structure.element
|
|
31
|
+
const expectChildNodes = structure.childNodes || []
|
|
32
|
+
expectChildNodes.forEach((expectItem, i) => {
|
|
33
|
+
expect(elem.childNodes[i]).toBe(expectItem.element)
|
|
34
|
+
expect(expectItem.element.parentNode).toBe(elem)
|
|
35
|
+
expect((getSingleSlotElement(elem) as HTMLElement).childNodes[i]).toBe(expectItem.element.$$)
|
|
36
|
+
expect(elem.getComposedChildren()[i]).toBe(expectItem.element)
|
|
37
|
+
native(expectItem)
|
|
38
|
+
})
|
|
39
|
+
expect(elem.childNodes[expectChildNodes.length]).toBe(undefined)
|
|
40
|
+
expect(
|
|
41
|
+
(getSingleSlotElement(elem) as Element).childNodes[expectChildNodes.length],
|
|
42
|
+
).toBe(undefined)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// check the structure of a backend element
|
|
46
|
+
const testBackend = (elem: Element): void => {
|
|
47
|
+
const testDom = (elem: HTMLElement) => {
|
|
48
|
+
expect(elem).toBeInstanceOf(HTMLElement)
|
|
49
|
+
let sr = elem
|
|
50
|
+
let host = (sr as unknown as domlikeBackend.Element).__wxElement as
|
|
51
|
+
GeneralComponent | undefined
|
|
52
|
+
while (!host) {
|
|
53
|
+
sr = sr.parentElement!
|
|
54
|
+
host = (sr as unknown as domlikeBackend.Element).__wxElement as
|
|
55
|
+
GeneralComponent | undefined
|
|
56
|
+
}
|
|
57
|
+
const slot = (host.shadowRoot as ExternalShadowRoot).slot as unknown as HTMLElement
|
|
58
|
+
if (elem === slot) {
|
|
59
|
+
host.childNodes.forEach((child, i) => {
|
|
60
|
+
if (child instanceof TextNode) {
|
|
61
|
+
expect(child.textContent).toBe(elem.childNodes[i]!.textContent)
|
|
62
|
+
} else {
|
|
63
|
+
// eslint-disable-next-line no-use-before-define
|
|
64
|
+
virtual(child)
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
} else {
|
|
68
|
+
for (let i = 0; i < elem.childNodes.length; i += 1) {
|
|
69
|
+
const domChild = elem.childNodes[i]
|
|
70
|
+
if (domChild instanceof HTMLElement) testDom(domChild)
|
|
71
|
+
else expect(domChild).toBeInstanceOf(Text)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (elem.getBackendContext().mode === BackendMode.Domlike) {
|
|
76
|
+
testDom(elem.getBackendElement() as unknown as HTMLElement)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// check the structure of an element
|
|
81
|
+
export const virtual = (
|
|
82
|
+
elem: Element,
|
|
83
|
+
defDomElem?: HTMLElement,
|
|
84
|
+
defIndex?: number,
|
|
85
|
+
): number => {
|
|
86
|
+
// for a component, check its shadow children and its shadow root
|
|
87
|
+
if (elem instanceof Component) {
|
|
88
|
+
const slotIndex = new Map<Element | HTMLElement, number>()
|
|
89
|
+
const dfsInherited = (parent: Element) => {
|
|
90
|
+
parent.childNodes.forEach((child) => {
|
|
91
|
+
let slot = getSingleSlotElement(elem as GeneralComponent)
|
|
92
|
+
if (slot !== undefined) {
|
|
93
|
+
// empty
|
|
94
|
+
} else {
|
|
95
|
+
slot = (elem.shadowRoot as ShadowRoot).getContainingSlot(child)
|
|
96
|
+
}
|
|
97
|
+
if (slot) {
|
|
98
|
+
if (!slotIndex.has(slot)) slotIndex.set(slot, 0)
|
|
99
|
+
const slotContent = elem._$external
|
|
100
|
+
? elem.getComposedChildren()
|
|
101
|
+
: (elem.shadowRoot as ShadowRoot).getSlotContentArray(slot as Element)!
|
|
102
|
+
const index = slotIndex.get(slot)!
|
|
103
|
+
expect(child).toBe(slotContent[index])
|
|
104
|
+
slotIndex.set(slot, index + 1)
|
|
105
|
+
} else if (child instanceof Element) {
|
|
106
|
+
virtual(child)
|
|
107
|
+
} else if (child instanceof TextNode) {
|
|
108
|
+
if (elem.getBackendContext().mode === BackendMode.Domlike) {
|
|
109
|
+
expect(child.textContent).toBe(
|
|
110
|
+
(child.getBackendElement() as unknown as HTMLElement).textContent,
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
throw new Error()
|
|
115
|
+
}
|
|
116
|
+
if (child instanceof Element && Element.getInheritSlots(child)) {
|
|
117
|
+
dfsInherited(child)
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
dfsInherited(elem)
|
|
122
|
+
if (elem._$external) {
|
|
123
|
+
const sr = (elem.shadowRoot as ExternalShadowRoot).root
|
|
124
|
+
expect(sr.__wxElement).toBe(elem)
|
|
125
|
+
testBackend(elem)
|
|
126
|
+
} else {
|
|
127
|
+
expect(elem.getComposedChildren()).toStrictEqual([elem.shadowRoot])
|
|
128
|
+
expect((elem.shadowRoot as ShadowRoot).getHostNode()).toBe(elem)
|
|
129
|
+
expect((elem.shadowRoot as ShadowRoot).parentNode).toBe(null)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// get the backend element if it is domlike backend
|
|
134
|
+
let domElem: HTMLElement | undefined
|
|
135
|
+
if (elem.getBackendContext().mode !== BackendMode.Domlike) {
|
|
136
|
+
domElem = undefined
|
|
137
|
+
} else if (elem instanceof Component && elem._$external) {
|
|
138
|
+
domElem = (elem.shadowRoot as ExternalShadowRoot).slot as unknown as HTMLElement
|
|
139
|
+
} else {
|
|
140
|
+
domElem = defDomElem || (elem.getBackendElement() || undefined) as
|
|
141
|
+
unknown as HTMLElement | undefined
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// check the composed children recursively
|
|
145
|
+
let index = defIndex || 0
|
|
146
|
+
const isVirtualHost = (elem: Node) => {
|
|
147
|
+
if (elem instanceof Component) {
|
|
148
|
+
return elem.getComponentOptions().virtualHost
|
|
149
|
+
}
|
|
150
|
+
return false
|
|
151
|
+
}
|
|
152
|
+
const expectParentNode = Element.getSlotName(elem) === undefined
|
|
153
|
+
? elem
|
|
154
|
+
: elem.ownerShadowRoot!.getHostNode()
|
|
155
|
+
elem.forEachComposedChild((child) => {
|
|
156
|
+
expect(child.getComposedParent()).toBe(elem)
|
|
157
|
+
if (!(child instanceof ShadowRoot)) {
|
|
158
|
+
let parentNode = child.parentNode
|
|
159
|
+
while (parentNode && Element.getInheritSlots(parentNode)) {
|
|
160
|
+
parentNode = parentNode.parentNode
|
|
161
|
+
}
|
|
162
|
+
expect(parentNode).toBe(expectParentNode)
|
|
163
|
+
}
|
|
164
|
+
if (child instanceof VirtualNode || isVirtualHost(child)) {
|
|
165
|
+
index = virtual(child as Element, domElem, index)
|
|
166
|
+
} else if (child instanceof Element) {
|
|
167
|
+
if (domElem) {
|
|
168
|
+
expect(child.$$).toBe(domElem.childNodes[index])
|
|
169
|
+
index += 1
|
|
170
|
+
}
|
|
171
|
+
virtual(child)
|
|
172
|
+
} else if (child instanceof TextNode) {
|
|
173
|
+
if (domElem) {
|
|
174
|
+
expect(child.textContent).toBe(domElem.childNodes[index]!.textContent)
|
|
175
|
+
index += 1
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
throw new Error()
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
if (domElem && !(elem instanceof VirtualNode) && !isVirtualHost(elem)) {
|
|
182
|
+
expect(domElem.childNodes[index]).toBe(undefined)
|
|
183
|
+
}
|
|
184
|
+
return index
|
|
185
|
+
}
|