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.
Files changed (237) hide show
  1. package/README.md +40 -0
  2. package/dist/glass_easel.all.d.ts +1 -0
  3. package/dist/glass_easel.all.js +2 -0
  4. package/dist/glass_easel.all.js.map +1 -0
  5. package/dist/glass_easel.domlike.global.d.ts +1 -0
  6. package/dist/glass_easel.domlike.global.js +2 -0
  7. package/dist/glass_easel.domlike.global.js.map +1 -0
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.js +1 -0
  10. package/dist/types/src/backend/backend_protocol.d.ts +119 -0
  11. package/dist/types/src/backend/backend_protocol.d.ts.map +1 -0
  12. package/dist/types/src/backend/composed_backend_protocol.d.ts +90 -0
  13. package/dist/types/src/backend/composed_backend_protocol.d.ts.map +1 -0
  14. package/dist/types/src/backend/domlike_backend_protocol.d.ts +76 -0
  15. package/dist/types/src/backend/domlike_backend_protocol.d.ts.map +1 -0
  16. package/dist/types/src/backend/mode.d.ts +46 -0
  17. package/dist/types/src/backend/mode.d.ts.map +1 -0
  18. package/dist/types/src/backend/suggested_backend_protocol.d.ts +30 -0
  19. package/dist/types/src/backend/suggested_backend_protocol.d.ts.map +1 -0
  20. package/dist/types/src/behavior.d.ts +428 -0
  21. package/dist/types/src/behavior.d.ts.map +1 -0
  22. package/dist/types/src/class_list.d.ts +79 -0
  23. package/dist/types/src/class_list.d.ts.map +1 -0
  24. package/dist/types/src/component.d.ts +291 -0
  25. package/dist/types/src/component.d.ts.map +1 -0
  26. package/dist/types/src/component_params.d.ts +239 -0
  27. package/dist/types/src/component_params.d.ts.map +1 -0
  28. package/dist/types/src/component_space.d.ts +164 -0
  29. package/dist/types/src/component_space.d.ts.map +1 -0
  30. package/dist/types/src/data_path.d.ts +5 -0
  31. package/dist/types/src/data_path.d.ts.map +1 -0
  32. package/dist/types/src/data_proxy.d.ts +107 -0
  33. package/dist/types/src/data_proxy.d.ts.map +1 -0
  34. package/dist/types/src/data_utils.d.ts +3 -0
  35. package/dist/types/src/data_utils.d.ts.map +1 -0
  36. package/dist/types/src/element.d.ts +275 -0
  37. package/dist/types/src/element.d.ts.map +1 -0
  38. package/dist/types/src/element_iterator.d.ts +43 -0
  39. package/dist/types/src/element_iterator.d.ts.map +1 -0
  40. package/dist/types/src/event.d.ts +104 -0
  41. package/dist/types/src/event.d.ts.map +1 -0
  42. package/dist/types/src/external_shadow_tree.d.ts +20 -0
  43. package/dist/types/src/external_shadow_tree.d.ts.map +1 -0
  44. package/dist/types/src/func_arr.d.ts +39 -0
  45. package/dist/types/src/func_arr.d.ts.map +1 -0
  46. package/dist/types/src/global_options.d.ts +111 -0
  47. package/dist/types/src/global_options.d.ts.map +1 -0
  48. package/dist/types/src/index.d.ts +43 -0
  49. package/dist/types/src/index.d.ts.map +1 -0
  50. package/dist/types/src/mutation_observer.d.ts +79 -0
  51. package/dist/types/src/mutation_observer.d.ts.map +1 -0
  52. package/dist/types/src/native_node.d.ts +8 -0
  53. package/dist/types/src/native_node.d.ts.map +1 -0
  54. package/dist/types/src/node.d.ts +49 -0
  55. package/dist/types/src/node.d.ts.map +1 -0
  56. package/dist/types/src/relation.d.ts +47 -0
  57. package/dist/types/src/relation.d.ts.map +1 -0
  58. package/dist/types/src/render.d.ts +3 -0
  59. package/dist/types/src/render.d.ts.map +1 -0
  60. package/dist/types/src/selector.d.ts +32 -0
  61. package/dist/types/src/selector.d.ts.map +1 -0
  62. package/dist/types/src/shadow_root.d.ts +136 -0
  63. package/dist/types/src/shadow_root.d.ts.map +1 -0
  64. package/dist/types/src/template_engine.d.ts +18 -0
  65. package/dist/types/src/template_engine.d.ts.map +1 -0
  66. package/dist/types/src/text_node.d.ts +32 -0
  67. package/dist/types/src/text_node.d.ts.map +1 -0
  68. package/dist/types/src/tmpl/index.d.ts +18 -0
  69. package/dist/types/src/tmpl/index.d.ts.map +1 -0
  70. package/dist/types/src/tmpl/native_rendering.d.ts +45 -0
  71. package/dist/types/src/tmpl/native_rendering.d.ts.map +1 -0
  72. package/dist/types/src/tmpl/proc_gen_wrapper.d.ts +80 -0
  73. package/dist/types/src/tmpl/proc_gen_wrapper.d.ts.map +1 -0
  74. package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts +50 -0
  75. package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts.map +1 -0
  76. package/dist/types/src/tmpl/range_list_diff.d.ts +19 -0
  77. package/dist/types/src/tmpl/range_list_diff.d.ts.map +1 -0
  78. package/dist/types/src/trait_behaviors.d.ts +38 -0
  79. package/dist/types/src/trait_behaviors.d.ts.map +1 -0
  80. package/dist/types/src/virtual_node.d.ts +10 -0
  81. package/dist/types/src/virtual_node.d.ts.map +1 -0
  82. package/dist/types/tests/backend/domlike.test.d.ts +2 -0
  83. package/dist/types/tests/backend/domlike.test.d.ts.map +1 -0
  84. package/dist/types/tests/base/env.d.ts +29 -0
  85. package/dist/types/tests/base/env.d.ts.map +1 -0
  86. package/dist/types/tests/base/match.d.ts +9 -0
  87. package/dist/types/tests/base/match.d.ts.map +1 -0
  88. package/dist/types/tests/core/backend.test.d.ts +2 -0
  89. package/dist/types/tests/core/backend.test.d.ts.map +1 -0
  90. package/dist/types/tests/core/behavior.test.d.ts +2 -0
  91. package/dist/types/tests/core/behavior.test.d.ts.map +1 -0
  92. package/dist/types/tests/core/component_space.test.d.ts +2 -0
  93. package/dist/types/tests/core/component_space.test.d.ts.map +1 -0
  94. package/dist/types/tests/core/data_update.test.d.ts +2 -0
  95. package/dist/types/tests/core/data_update.test.d.ts.map +1 -0
  96. package/dist/types/tests/core/misc.test.d.ts +2 -0
  97. package/dist/types/tests/core/misc.test.d.ts.map +1 -0
  98. package/dist/types/tests/core/placeholder.test.d.ts +2 -0
  99. package/dist/types/tests/core/placeholder.test.d.ts.map +1 -0
  100. package/dist/types/tests/core/slot.test.d.ts +2 -0
  101. package/dist/types/tests/core/slot.test.d.ts.map +1 -0
  102. package/dist/types/tests/core/trait_behaviors.test.d.ts +2 -0
  103. package/dist/types/tests/core/trait_behaviors.test.d.ts.map +1 -0
  104. package/dist/types/tests/tmpl/binding_map.test.d.ts +2 -0
  105. package/dist/types/tests/tmpl/binding_map.test.d.ts.map +1 -0
  106. package/dist/types/tests/tmpl/event.test.d.ts +2 -0
  107. package/dist/types/tests/tmpl/event.test.d.ts.map +1 -0
  108. package/dist/types/tests/tmpl/expression.test.d.ts +2 -0
  109. package/dist/types/tests/tmpl/expression.test.d.ts.map +1 -0
  110. package/dist/types/tests/tmpl/lvalue.test.d.ts +2 -0
  111. package/dist/types/tests/tmpl/lvalue.test.d.ts.map +1 -0
  112. package/dist/types/tests/tmpl/native_rendering.test.d.ts +2 -0
  113. package/dist/types/tests/tmpl/native_rendering.test.d.ts.map +1 -0
  114. package/dist/types/tests/tmpl/structure.test.d.ts +2 -0
  115. package/dist/types/tests/tmpl/structure.test.d.ts.map +1 -0
  116. package/dist/types/tests/types/chaining.test.d.ts +2 -0
  117. package/dist/types/tests/types/chaining.test.d.ts.map +1 -0
  118. package/dist/types/tests/types/createElement.test.d.ts +2 -0
  119. package/dist/types/tests/types/createElement.test.d.ts.map +1 -0
  120. package/dist/types/tests/types/definition.test.d.ts +2 -0
  121. package/dist/types/tests/types/definition.test.d.ts.map +1 -0
  122. package/guide/zh_CN/advanced/binding_map_update.md +32 -0
  123. package/guide/zh_CN/advanced/build_args.md +28 -0
  124. package/guide/zh_CN/advanced/component_filter.md +70 -0
  125. package/guide/zh_CN/advanced/component_space.md +124 -0
  126. package/guide/zh_CN/advanced/custom_backend.md +53 -0
  127. package/guide/zh_CN/advanced/error_listener.md +32 -0
  128. package/guide/zh_CN/advanced/external_component.md +73 -0
  129. package/guide/zh_CN/advanced/template_engine.md +61 -0
  130. package/guide/zh_CN/appendix/backend_protocol.md +501 -0
  131. package/guide/zh_CN/appendix/list_diff_algorithm.md +406 -0
  132. package/guide/zh_CN/basic/beginning.md +94 -0
  133. package/guide/zh_CN/basic/component.md +156 -0
  134. package/guide/zh_CN/basic/event.md +169 -0
  135. package/guide/zh_CN/basic/lifetime.md +66 -0
  136. package/guide/zh_CN/basic/method.md +62 -0
  137. package/guide/zh_CN/basic/template.md +135 -0
  138. package/guide/zh_CN/data_management/advanced_update.md +170 -0
  139. package/guide/zh_CN/data_management/data_deep_copy.md +157 -0
  140. package/guide/zh_CN/data_management/data_observer.md +154 -0
  141. package/guide/zh_CN/data_management/property_early_init.md +31 -0
  142. package/guide/zh_CN/data_management/pure_data_pattern.md +21 -0
  143. package/guide/zh_CN/index.md +93 -0
  144. package/guide/zh_CN/interaction/behavior.md +52 -0
  145. package/guide/zh_CN/interaction/component_path.md +37 -0
  146. package/guide/zh_CN/interaction/generic.md +73 -0
  147. package/guide/zh_CN/interaction/placeholder.md +40 -0
  148. package/guide/zh_CN/interaction/relation.md +151 -0
  149. package/guide/zh_CN/interaction/slot.md +137 -0
  150. package/guide/zh_CN/interaction/template_import.md +94 -0
  151. package/guide/zh_CN/interaction/trait_behavior.md +117 -0
  152. package/guide/zh_CN/styling/external_class.md +46 -0
  153. package/guide/zh_CN/styling/style_isolation.md +54 -0
  154. package/guide/zh_CN/styling/virtual_host.md +52 -0
  155. package/guide/zh_CN/tree/element_iterator.md +54 -0
  156. package/guide/zh_CN/tree/mutation_observer.md +52 -0
  157. package/guide/zh_CN/tree/node_tree.md +142 -0
  158. package/guide/zh_CN/tree/node_tree_modification.md +78 -0
  159. package/guide/zh_CN/tree/selector.md +66 -0
  160. package/jest.config.js +6 -0
  161. package/jest.dts.config.js +9 -0
  162. package/jest.unit.config.js +14 -0
  163. package/package.json +28 -0
  164. package/src/backend/backend_protocol.ts +313 -0
  165. package/src/backend/composed_backend_protocol.ts +252 -0
  166. package/src/backend/domlike_backend_protocol.ts +370 -0
  167. package/src/backend/mode.ts +51 -0
  168. package/src/backend/suggested_backend_protocol.ts +83 -0
  169. package/src/behavior.ts +1655 -0
  170. package/src/bootstrap_dom_dev.js +22 -0
  171. package/src/class_list.ts +376 -0
  172. package/src/component.ts +1309 -0
  173. package/src/component_params.ts +461 -0
  174. package/src/component_space.ts +547 -0
  175. package/src/data_path.ts +225 -0
  176. package/src/data_proxy.ts +670 -0
  177. package/src/data_utils.ts +50 -0
  178. package/src/element.ts +1966 -0
  179. package/src/element_iterator.ts +158 -0
  180. package/src/event.ts +401 -0
  181. package/src/external_shadow_tree.ts +27 -0
  182. package/src/func_arr.ts +198 -0
  183. package/src/global_options.ts +242 -0
  184. package/src/index.ts +187 -0
  185. package/src/mutation_observer.ts +252 -0
  186. package/src/native_node.ts +74 -0
  187. package/src/node.ts +174 -0
  188. package/src/relation.ts +380 -0
  189. package/src/render.ts +25 -0
  190. package/src/selector.ts +218 -0
  191. package/src/shadow_root.ts +766 -0
  192. package/src/template_engine.ts +45 -0
  193. package/src/text_node.ts +149 -0
  194. package/src/tmpl/index.ts +199 -0
  195. package/src/tmpl/native_rendering.ts +175 -0
  196. package/src/tmpl/proc_gen_wrapper.ts +954 -0
  197. package/src/tmpl/proc_gen_wrapper_dom.ts +230 -0
  198. package/src/tmpl/range_list_diff.ts +443 -0
  199. package/src/trait_behaviors.ts +51 -0
  200. package/src/virtual_node.ts +51 -0
  201. package/tests/backend/domlike.test.ts +254 -0
  202. package/tests/base/env.ts +78 -0
  203. package/tests/base/match.ts +185 -0
  204. package/tests/core/backend.test.ts +144 -0
  205. package/tests/core/behavior.test.ts +546 -0
  206. package/tests/core/component_space.test.ts +212 -0
  207. package/tests/core/data_update.test.ts +461 -0
  208. package/tests/core/misc.test.ts +339 -0
  209. package/tests/core/placeholder.test.ts +180 -0
  210. package/tests/core/slot.test.ts +1495 -0
  211. package/tests/core/trait_behaviors.test.ts +153 -0
  212. package/tests/legacy/README.md +3 -0
  213. package/tests/legacy/behavior.test.js +293 -0
  214. package/tests/legacy/component.test.js +1247 -0
  215. package/tests/legacy/data_path.test.js +149 -0
  216. package/tests/legacy/data_proxy.test.js +759 -0
  217. package/tests/legacy/element_iterator.test.js +148 -0
  218. package/tests/legacy/event.test.js +849 -0
  219. package/tests/legacy/external.test.js +510 -0
  220. package/tests/legacy/extra_info.test.js +109 -0
  221. package/tests/legacy/generics.test.js +176 -0
  222. package/tests/legacy/mutation_observer.test.js +210 -0
  223. package/tests/legacy/relation.test.js +517 -0
  224. package/tests/legacy/selector.test.js +263 -0
  225. package/tests/legacy/slot.test.js +915 -0
  226. package/tests/legacy/virtual.test.js +394 -0
  227. package/tests/tmpl/binding_map.test.ts +208 -0
  228. package/tests/tmpl/event.test.ts +206 -0
  229. package/tests/tmpl/expression.test.ts +429 -0
  230. package/tests/tmpl/lvalue.test.ts +160 -0
  231. package/tests/tmpl/native_rendering.test.ts +155 -0
  232. package/tests/tmpl/structure.test.ts +998 -0
  233. package/tests/types/chaining.test.ts +614 -0
  234. package/tests/types/createElement.test.ts +82 -0
  235. package/tests/types/definition.test.ts +442 -0
  236. package/tsconfig.json +11 -0
  237. 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
+ }