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,158 @@
1
+ import {
2
+ Element,
3
+ } from './element'
4
+ import {
5
+ TextNode,
6
+ } from './text_node'
7
+ import {
8
+ Component,
9
+ } from './component'
10
+ import {
11
+ Node,
12
+ } from './node'
13
+
14
+ /** The iterator direction and order */
15
+ export const enum ElementIteratorType {
16
+ /** Iterate all ancestors in shadow tree */
17
+ ShadowAncestors = 'shadow-ancestors',
18
+ /** Iterate all ancestors in composed tree */
19
+ ComposedAncestors = 'composed-ancestors',
20
+ /** Iterate all descendants in shadow tree, returning parents before their children */
21
+ ShadowDescendantsRootFirst = 'shadow-descendants-root-first',
22
+ /** Iterate all descendants in shadow tree, returning parents after their children */
23
+ ShadowDescendantsRootLast = 'shadow-descendants-root-last',
24
+ /** Iterate all descendants in composed tree, returning parents before their children */
25
+ ComposedDescendantsRootFirst = 'composed-descendants-root-first',
26
+ /** Iterate all descendants in composed tree, returning parents after their children */
27
+ ComposedDescendantsRootLast = 'composed-descendants-root-last',
28
+ }
29
+
30
+ /**
31
+ * An iterator for node tree traversal
32
+ *
33
+ * This iterator is convinient but seems a little slower.
34
+ */
35
+ export class ElementIterator {
36
+ private _$node: Node
37
+ private _$nodeTypeLimit: unknown
38
+ private _$composed: boolean
39
+ private _$isAncestor: boolean
40
+ private _$rootFirst: boolean
41
+
42
+ /**
43
+ * Create an iterator with type specified
44
+ *
45
+ * The `nodeTypeLimit` is used to limit which kind of nodes will be returned.
46
+ * It limits the returned result by an `instanceof` call.
47
+ * The default value is `Element` ,
48
+ * which means only elements will be returned (text nodes will not).
49
+ * Consider specifying `Node` if text nodes need to be returned as well as elements.
50
+ * Specify `Component` will only return components.
51
+ */
52
+ constructor(node: Node, type: ElementIteratorType, nodeTypeLimit: unknown = Element) {
53
+ if (!(node instanceof Element) && !(node instanceof TextNode)) {
54
+ throw new Error('Element iterators can only be used in elements or text nodes')
55
+ }
56
+ this._$node = node
57
+ this._$nodeTypeLimit = nodeTypeLimit || Element
58
+ if (
59
+ type === ElementIteratorType.ShadowAncestors
60
+ || type === ElementIteratorType.ShadowDescendantsRootFirst
61
+ || type === ElementIteratorType.ShadowDescendantsRootLast
62
+ ) {
63
+ this._$composed = false
64
+ } else if (
65
+ type === ElementIteratorType.ComposedAncestors
66
+ || type === ElementIteratorType.ComposedDescendantsRootFirst
67
+ || type === ElementIteratorType.ComposedDescendantsRootLast
68
+ ) {
69
+ this._$composed = true
70
+ } else {
71
+ throw new Error(`Unrecognized iterator type "${String(type)}"`)
72
+ }
73
+ if (
74
+ type === ElementIteratorType.ShadowAncestors
75
+ || type === ElementIteratorType.ComposedAncestors
76
+ ) {
77
+ this._$isAncestor = true
78
+ } else {
79
+ this._$isAncestor = false
80
+ }
81
+ if (
82
+ type === ElementIteratorType.ShadowDescendantsRootFirst
83
+ || type === ElementIteratorType.ComposedDescendantsRootFirst
84
+ ) {
85
+ this._$rootFirst = true
86
+ } else {
87
+ this._$rootFirst = false
88
+ }
89
+ }
90
+
91
+ /** Same as constructor (for backward compatibility) */
92
+ static create(node: Node, type: ElementIteratorType, nodeTypeLimit?: unknown): ElementIterator {
93
+ return new ElementIterator(node, type, nodeTypeLimit)
94
+ }
95
+
96
+ forEach(f: (node: Node) => boolean) {
97
+ const nodeTypeLimit: any = this._$nodeTypeLimit
98
+ const composed = this._$composed
99
+ if (this._$isAncestor) {
100
+ const rec = (node: Node): boolean => {
101
+ let prev: Node | null = null
102
+ let cur = node
103
+ for (;;) {
104
+ if (composed && prev) {
105
+ if (cur instanceof Component && !cur._$external) {
106
+ const slot = cur.getShadowRoot()!.getContainingSlot(prev)
107
+ if (slot) {
108
+ if (rec(slot) === false) break
109
+ } else {
110
+ return false
111
+ }
112
+ }
113
+ }
114
+ if (cur instanceof nodeTypeLimit) {
115
+ if (f(cur) === false) return false
116
+ }
117
+ if (!cur.parentNode) break
118
+ prev = cur
119
+ cur = cur.parentNode
120
+ }
121
+ return true
122
+ }
123
+ rec(this._$node)
124
+ } else {
125
+ const rootFirst = this._$rootFirst
126
+ const rec = (node: Node): boolean => {
127
+ if (rootFirst) {
128
+ if (node instanceof nodeTypeLimit) {
129
+ if (f(node) === false) return false
130
+ }
131
+ }
132
+ if (node instanceof Element) {
133
+ let interrupted = false
134
+ const childFn = (child: Node) => {
135
+ if (rec(child) === false) {
136
+ interrupted = true
137
+ return false
138
+ }
139
+ return true
140
+ }
141
+ if (composed) {
142
+ node.forEachComposedChild(childFn)
143
+ } else {
144
+ node.childNodes.forEach(childFn)
145
+ }
146
+ if (interrupted) return false
147
+ }
148
+ if (!rootFirst) {
149
+ if (node instanceof nodeTypeLimit) {
150
+ if (f(node) === false) return false
151
+ }
152
+ }
153
+ return true
154
+ }
155
+ rec(this._$node)
156
+ }
157
+ }
158
+ }
package/src/event.ts ADDED
@@ -0,0 +1,401 @@
1
+ import {
2
+ FuncArrWithMeta,
3
+ } from './func_arr'
4
+ import {
5
+ Element,
6
+ Component,
7
+ GeneralComponent,
8
+ ShadowRoot,
9
+ GeneralBackendElement,
10
+ ExternalShadowRoot,
11
+ } from '.'
12
+
13
+ /**
14
+ * Options for an event
15
+ */
16
+ export type EventOptions = {
17
+ originalEvent?: unknown
18
+ bubbles?: boolean
19
+ composed?: boolean
20
+ capturePhase?: boolean
21
+ extraFields?: { [key: string]: unknown }
22
+ }
23
+
24
+ /**
25
+ * Options for an event listener
26
+ */
27
+ export type EventListenerOptions = {
28
+ /** Always stop bubbling after this listener */
29
+ final?: boolean
30
+ /** Mark mutated after this listener (ignored if `final` is true) */
31
+ mutated?: boolean
32
+ /** Listen in the capture phase */
33
+ capture?: boolean
34
+ /** The same as `capture` for compatibility */
35
+ useCapture?: boolean
36
+ }
37
+
38
+ /**
39
+ * Event bubbling control
40
+ */
41
+ export const enum EventBubbleStatus {
42
+ Normal = 0,
43
+ NoDefault = 1,
44
+ }
45
+
46
+ export type EventListener<TDetail> = (ev: ShadowedEvent<TDetail>) => boolean | void
47
+
48
+ let relativeTimeStamp = Date.now()
49
+ let prevTimeStamp = relativeTimeStamp
50
+
51
+ const getCurrentTimeStamp = () => {
52
+ const ts = Date.now()
53
+ if (ts < prevTimeStamp) {
54
+ relativeTimeStamp += ts - prevTimeStamp
55
+ }
56
+ prevTimeStamp = ts
57
+ return ts - relativeTimeStamp
58
+ }
59
+
60
+ const enum MutLevel {
61
+ None = 0,
62
+ Mut = 1,
63
+ Final = 2,
64
+ }
65
+
66
+ type EventFuncArr<TDetail> = {
67
+ mutCount: number,
68
+ finalCount: number,
69
+ funcArr: FuncArrWithMeta<EventListener<TDetail>, MutLevel>,
70
+ }
71
+
72
+ export const enum FinalChanged {
73
+ NotChanged = 0,
74
+ Failed,
75
+ Init,
76
+ Added,
77
+ Removed,
78
+ }
79
+
80
+ /** The target of an event */
81
+ export class EventTarget<TEvents extends { [type: string]: unknown }> {
82
+ listeners = Object.create(null) as {
83
+ [T in keyof TEvents]: EventFuncArr<TEvents[T]>
84
+ }
85
+ captureListeners: {
86
+ [T in keyof TEvents]: EventFuncArr<TEvents[T]>
87
+ } | null = null
88
+
89
+ addListener<T extends string>(
90
+ name: T,
91
+ func: EventListener<TEvents[T]>,
92
+ options: EventListenerOptions = {},
93
+ ): FinalChanged {
94
+ // eslint-disable-next-line no-nested-ternary
95
+ const mutLevel = options.final
96
+ ? MutLevel.Final
97
+ : (options.mutated ? MutLevel.Mut : MutLevel.None)
98
+ let listeners: { [T in keyof TEvents]: EventFuncArr<TEvents[T]> }
99
+ if (options.capture || options.useCapture) {
100
+ if (this.captureListeners) {
101
+ listeners = this.captureListeners
102
+ } else {
103
+ listeners = Object.create(null) as {
104
+ [T in keyof TEvents]: EventFuncArr<TEvents[T]>
105
+ }
106
+ this.captureListeners = listeners
107
+ }
108
+ } else {
109
+ listeners = this.listeners
110
+ }
111
+ let efa: EventFuncArr<TEvents[T]>
112
+ let initialized: boolean
113
+ if (listeners[name]) {
114
+ initialized = true
115
+ efa = listeners[name]!
116
+ } else {
117
+ initialized = false
118
+ efa = listeners[name] = {
119
+ mutCount: 0,
120
+ finalCount: 0,
121
+ funcArr: new FuncArrWithMeta(),
122
+ }
123
+ }
124
+ efa.funcArr.add(func, mutLevel)
125
+ if (mutLevel === MutLevel.Final) efa.finalCount += 1
126
+ else if (mutLevel === MutLevel.Mut) efa.mutCount += 1
127
+ if (initialized) {
128
+ return mutLevel === MutLevel.Final && efa.finalCount === 1
129
+ ? FinalChanged.Added
130
+ : FinalChanged.NotChanged
131
+ }
132
+ return MutLevel.Final ? FinalChanged.Added : FinalChanged.Init
133
+ }
134
+
135
+ removeListener<T extends string>(
136
+ name: T,
137
+ func: EventListener<TEvents[T]>,
138
+ options: EventListenerOptions = {},
139
+ ): FinalChanged {
140
+ const listeners = (options.capture || options.useCapture)
141
+ ? this.captureListeners
142
+ : this.listeners
143
+ if (!listeners) return FinalChanged.Failed
144
+ const efa = listeners[name]
145
+ if (!efa) return FinalChanged.Failed
146
+ const mutLevel = efa.funcArr.remove(func)
147
+ if (mutLevel === null) return FinalChanged.Failed
148
+ if (mutLevel === MutLevel.Final) efa.finalCount -= 1
149
+ else if (mutLevel === MutLevel.Mut) efa.mutCount -= 1
150
+ return mutLevel === MutLevel.Final && efa.finalCount === 0
151
+ ? FinalChanged.Removed
152
+ : FinalChanged.NotChanged
153
+ }
154
+ }
155
+
156
+ export type ShadowedEvent<TDetail> = Required<Event<TDetail>> & {
157
+ target: Element
158
+ mark: { [name: string]: unknown } | null
159
+ currentTarget: Element
160
+ }
161
+
162
+ export class Event<TDetail> {
163
+ type: string
164
+ timeStamp: number
165
+ detail: TDetail
166
+ bubbles: boolean
167
+ composed: boolean
168
+ /** @internal */
169
+ private _$capturePhase: boolean
170
+ /** @internal */
171
+ private _$originalEvent: unknown
172
+ /** @internal */
173
+ private _$dispatched: boolean
174
+ /** @internal */
175
+ private _$eventBubblingControl: {
176
+ stopped: boolean
177
+ mutated: boolean
178
+ noDefault: boolean
179
+ }
180
+
181
+ constructor(name: string, detail: TDetail, options: EventOptions = {}) {
182
+ const ts = getCurrentTimeStamp()
183
+ this.type = name
184
+ this.timeStamp = ts
185
+ this.detail = detail
186
+ this.bubbles = options.bubbles || false
187
+ this.composed = options.composed || false
188
+ this._$capturePhase = options.capturePhase || false
189
+ this._$eventBubblingControl = {
190
+ stopped: false,
191
+ mutated: false,
192
+ noDefault: false,
193
+ }
194
+ this._$originalEvent = options.originalEvent
195
+ this._$dispatched = false
196
+ if (options.extraFields) {
197
+ Object.assign(this, options.extraFields)
198
+ }
199
+ }
200
+
201
+ getEventBubbleStatus(): EventBubbleStatus {
202
+ if (this._$eventBubblingControl.noDefault) return EventBubbleStatus.NoDefault
203
+ return EventBubbleStatus.Normal
204
+ }
205
+
206
+ wrapShadowedEvent(
207
+ targetCaller: Element,
208
+ mark: { [name: string]: unknown } | null,
209
+ currentTargetCaller: Element,
210
+ ): ShadowedEvent<TDetail> {
211
+ const ret = Object.create(this) as ShadowedEvent<TDetail>
212
+ ret.target = targetCaller
213
+ ret.mark = mark
214
+ ret.currentTarget = currentTargetCaller
215
+ return ret
216
+ }
217
+
218
+ getOriginalEvent(): unknown {
219
+ return this._$originalEvent
220
+ }
221
+
222
+ preventDefault() {
223
+ this._$eventBubblingControl.noDefault = true
224
+ }
225
+
226
+ defaultPrevented() {
227
+ return this._$eventBubblingControl.noDefault
228
+ }
229
+
230
+ stopPropagation() {
231
+ this._$eventBubblingControl.stopped = true
232
+ }
233
+
234
+ propagationStopped() {
235
+ return this._$eventBubblingControl.stopped
236
+ }
237
+
238
+ markMutated() {
239
+ this._$eventBubblingControl.mutated = true
240
+ }
241
+
242
+ mutatedMarked() {
243
+ return this._$eventBubblingControl.mutated
244
+ }
245
+
246
+ dispatch(target: Element, externalTarget?: GeneralBackendElement) {
247
+ if (this._$dispatched) {
248
+ throw new Error('Event cannot be dispatched twice')
249
+ }
250
+ this._$dispatched = true
251
+ const evName = this.type
252
+ const crossShadow = this.composed
253
+ const bubbles = this.bubbles
254
+ const inExternalOnly = externalTarget && !crossShadow
255
+ const eventBubblingControl = this._$eventBubblingControl
256
+
257
+ // calls listeners on a single element
258
+ const callEventFuncArr = (
259
+ targetCaller: Element,
260
+ mark: Record<string, unknown> | null,
261
+ cur: Element,
262
+ isCapture: boolean,
263
+ ) => {
264
+ const eventTarget = cur._$eventTarget
265
+ if (!eventTarget) return
266
+ const listeners = isCapture ? eventTarget.captureListeners : eventTarget.listeners
267
+ if (!listeners) return
268
+ const efa = listeners[evName]
269
+ if (!efa) return
270
+ const skipMut = this.mutatedMarked()
271
+ const isComp = cur instanceof Component
272
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
273
+ const curCaller = isComp ? cur.getMethodCaller() : cur
274
+ const ev = this.wrapShadowedEvent(targetCaller, mark, curCaller)
275
+ const ret = efa.funcArr.call(
276
+ curCaller,
277
+ [ev],
278
+ (mulLevel) => !skipMut || mulLevel !== MutLevel.Mut,
279
+ isComp ? cur as GeneralComponent : undefined,
280
+ )
281
+ if (ret === false || efa.finalCount > 0) {
282
+ ev.stopPropagation()
283
+ ev.preventDefault()
284
+ } else if (efa.mutCount > 0) {
285
+ ev.markMutated()
286
+ }
287
+ }
288
+
289
+ const forEachBubblePath = (
290
+ target: Element,
291
+ f: (target: Element, targetCaller: Element, mark: Record<string, unknown>) => boolean | void,
292
+ ) => {
293
+ const recShadow = (target: Element): Element | null => {
294
+ let cur = target
295
+ const targetCaller = target instanceof Component
296
+ ? target.getMethodCaller() as Element
297
+ : target
298
+ const mark = target.collectMarks()
299
+ for (;;) {
300
+ if (f(cur, targetCaller, mark) === false) return null
301
+ let next
302
+ if (crossShadow) {
303
+ if (cur instanceof ShadowRoot) return cur.getHostNode()
304
+ next = cur.parentNode
305
+ while (next?._$inheritSlots) {
306
+ next = next.parentNode
307
+ }
308
+ if (next instanceof Component && !next._$external) {
309
+ const slot = (next.shadowRoot as ShadowRoot).getContainingSlot(cur)
310
+ if (!slot) return null
311
+ next = recShadow(slot)
312
+ }
313
+ } else {
314
+ next = cur.parentNode
315
+ }
316
+ if (!next) return null
317
+ cur = next
318
+ }
319
+ }
320
+ let cur = target
321
+ for (;;) {
322
+ const next = recShadow(cur)
323
+ if (!next) break
324
+ cur = next
325
+ }
326
+ }
327
+
328
+ // capture phase
329
+ if (this._$capturePhase && !eventBubblingControl.stopped && !inExternalOnly) {
330
+ const bubblingPath: [Element, Element, Record<string, unknown>][] = []
331
+ forEachBubblePath(target, (target, targetCaller, mark) => {
332
+ bubblingPath.push([target, targetCaller, mark])
333
+ })
334
+ for (let i = bubblingPath.length - 1; i >= 0; i -= 1) {
335
+ const [cur, targetCaller, mark] = bubblingPath[i]!
336
+ if (cur._$eventTarget) {
337
+ callEventFuncArr(targetCaller, mark, cur, true)
338
+ if (eventBubblingControl.stopped) break
339
+ }
340
+ }
341
+ }
342
+
343
+ // bubble phase in external component
344
+ if (!eventBubblingControl.stopped && externalTarget) {
345
+ if (target instanceof Component && target._$external) {
346
+ (target.shadowRoot as ExternalShadowRoot).handleEvent(externalTarget, this)
347
+ }
348
+ }
349
+
350
+ // bubble phase
351
+ if (!eventBubblingControl.stopped && !inExternalOnly) {
352
+ let atTarget = true
353
+ forEachBubblePath(target, (target, targetCaller, mark) => {
354
+ if (!atTarget && target instanceof Component && target._$external) {
355
+ const sr = target.shadowRoot as ExternalShadowRoot
356
+ sr.handleEvent(sr.slot, this)
357
+ }
358
+ atTarget = false
359
+ if (target._$eventTarget) {
360
+ callEventFuncArr(targetCaller, mark, target, false)
361
+ }
362
+ return bubbles && !eventBubblingControl.stopped
363
+ })
364
+ }
365
+ }
366
+
367
+ static dispatchEvent<TDetail>(target: Element, event: Event<TDetail>) {
368
+ return event.dispatch(target)
369
+ }
370
+
371
+ static dispatchExternalEvent<TDetail>(
372
+ component: GeneralComponent,
373
+ target: GeneralBackendElement,
374
+ event: Event<TDetail>,
375
+ ) {
376
+ return event.dispatch(component, target)
377
+ }
378
+
379
+ static triggerEvent<TDetail>(
380
+ this: void,
381
+ target: Element,
382
+ name: string,
383
+ detail: TDetail,
384
+ options?: EventOptions,
385
+ ) {
386
+ const ev = new Event(name, detail, options)
387
+ Event.dispatchEvent(target, ev)
388
+ }
389
+
390
+ static triggerExternalEvent<TDetail>(
391
+ this: void,
392
+ component: GeneralComponent,
393
+ target: GeneralBackendElement,
394
+ name: string,
395
+ detail: TDetail,
396
+ options?: EventOptions,
397
+ ) {
398
+ const ev = new Event(name, detail, options)
399
+ Event.dispatchExternalEvent(component, target, ev)
400
+ }
401
+ }
@@ -0,0 +1,27 @@
1
+ import {
2
+ Event,
3
+ ShadowedEvent,
4
+ } from './event'
5
+ import {
6
+ GeneralBackendElement,
7
+ } from './node'
8
+
9
+ /**
10
+ * An external shadow root
11
+ *
12
+ * It can be used to build an external component.
13
+ * External component is a customizable subtree that can be composed with normal components.
14
+ * It allows third-party frameworks to render a subtree and then compose it together.
15
+ * However, the subtree must be created in the same backend context.
16
+ */
17
+ export interface ExternalShadowRoot {
18
+ root: GeneralBackendElement
19
+ slot: GeneralBackendElement
20
+ getIdMap(): { [id: string]: GeneralBackendElement }
21
+ handleEvent<T>(target: GeneralBackendElement, event: Event<T>): void
22
+ setListener<T>(
23
+ elem: GeneralBackendElement,
24
+ ev: string,
25
+ listener: (event: ShadowedEvent<T>) => unknown,
26
+ ): void
27
+ }