sygnal 5.4.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/CHANGELOG.md +646 -0
  2. package/README.md +24 -9
  3. package/dist/astro/client.cjs.js +16 -9
  4. package/dist/astro/client.cjs.js.map +1 -1
  5. package/dist/astro/client.mjs +16 -9
  6. package/dist/astro/client.mjs.map +1 -1
  7. package/dist/astro/index.cjs.js +362 -91
  8. package/dist/astro/index.cjs.js.map +1 -1
  9. package/dist/astro/index.mjs +362 -92
  10. package/dist/astro/index.mjs.map +1 -1
  11. package/dist/astro/server.cjs.js +3396 -165
  12. package/dist/astro/server.cjs.js.map +1 -1
  13. package/dist/astro/server.mjs +3396 -165
  14. package/dist/astro/server.mjs.map +1 -1
  15. package/dist/devtools.cjs.js +1431 -0
  16. package/dist/devtools.cjs.js.map +1 -0
  17. package/dist/devtools.esm.js +1419 -0
  18. package/dist/devtools.esm.js.map +1 -0
  19. package/dist/diagnostics.cjs.js +3032 -296
  20. package/dist/diagnostics.cjs.js.map +1 -1
  21. package/dist/diagnostics.esm.js +3032 -296
  22. package/dist/diagnostics.esm.js.map +1 -1
  23. package/dist/element.cjs.js +230 -0
  24. package/dist/element.cjs.js.map +1 -0
  25. package/dist/element.esm.js +228 -0
  26. package/dist/element.esm.js.map +1 -0
  27. package/dist/guide/accessibility.md +257 -0
  28. package/dist/guide/adapters.md +221 -0
  29. package/dist/guide/behaviors.md +312 -0
  30. package/dist/guide/browser-sources.md +203 -0
  31. package/dist/guide/drag-and-drop.md +342 -0
  32. package/dist/guide/element-commands.md +306 -0
  33. package/dist/guide/error-boundaries.md +169 -0
  34. package/dist/guide/forms-reference.md +381 -0
  35. package/dist/guide/forms.md +250 -0
  36. package/dist/guide/http.md +299 -0
  37. package/dist/guide/inputs.md +173 -0
  38. package/dist/guide/persistence.md +245 -0
  39. package/dist/guide/recipes/carousel.md +155 -0
  40. package/dist/guide/recipes/charts.md +177 -0
  41. package/dist/guide/recipes/code-editor.md +135 -0
  42. package/dist/guide/recipes/data-grid.md +155 -0
  43. package/dist/guide/recipes/data-table.md +196 -0
  44. package/dist/guide/recipes/i18n.md +222 -0
  45. package/dist/guide/recipes/icons.md +115 -0
  46. package/dist/guide/recipes/overview.md +33 -0
  47. package/dist/guide/recipes/rich-text.md +154 -0
  48. package/dist/guide/resources.md +471 -0
  49. package/dist/guide/ssr.md +255 -0
  50. package/dist/guide/timers.md +178 -0
  51. package/dist/guide/ui/accordion.md +93 -0
  52. package/dist/guide/ui/combobox.md +144 -0
  53. package/dist/guide/ui/dialog.md +124 -0
  54. package/dist/guide/ui/disclosure.md +60 -0
  55. package/dist/guide/ui/menu.md +125 -0
  56. package/dist/guide/ui/overview.md +166 -0
  57. package/dist/guide/ui/popover.md +101 -0
  58. package/dist/guide/ui/select.md +114 -0
  59. package/dist/guide/ui/tabs.md +161 -0
  60. package/dist/guide/ui/toaster.md +152 -0
  61. package/dist/guide/ui/tooltip.md +103 -0
  62. package/dist/guide/undo.md +178 -0
  63. package/dist/guide/virtual-collections.md +183 -0
  64. package/dist/guide/web-components.md +179 -0
  65. package/dist/guide/widgets.md +217 -0
  66. package/dist/index.cjs.js +13178 -5769
  67. package/dist/index.cjs.js.map +1 -1
  68. package/dist/index.d.ts +2908 -276
  69. package/dist/index.esm.js +13149 -5779
  70. package/dist/index.esm.js.map +1 -1
  71. package/dist/jsx-dev-runtime.cjs.js +26 -334
  72. package/dist/jsx-dev-runtime.cjs.js.map +1 -1
  73. package/dist/jsx-dev-runtime.esm.js +19 -327
  74. package/dist/jsx-dev-runtime.esm.js.map +1 -1
  75. package/dist/jsx-runtime.cjs.js +26 -334
  76. package/dist/jsx-runtime.cjs.js.map +1 -1
  77. package/dist/jsx-runtime.esm.js +19 -327
  78. package/dist/jsx-runtime.esm.js.map +1 -1
  79. package/dist/jsx.cjs.js +10 -314
  80. package/dist/jsx.cjs.js.map +1 -1
  81. package/dist/jsx.esm.js +8 -315
  82. package/dist/jsx.esm.js.map +1 -1
  83. package/dist/react.cjs.js +117 -0
  84. package/dist/react.cjs.js.map +1 -0
  85. package/dist/react.esm.js +115 -0
  86. package/dist/react.esm.js.map +1 -0
  87. package/dist/shims/globalthis.cjs +20 -0
  88. package/dist/sygnal.min.js +1 -1
  89. package/dist/sygnal.min.js.map +1 -1
  90. package/dist/ui-combobox.cjs.js +169 -0
  91. package/dist/ui-combobox.cjs.js.map +1 -0
  92. package/dist/ui-combobox.esm.js +148 -0
  93. package/dist/ui-combobox.esm.js.map +1 -0
  94. package/dist/ui-menu.cjs.js +87 -0
  95. package/dist/ui-menu.cjs.js.map +1 -0
  96. package/dist/ui-menu.esm.js +66 -0
  97. package/dist/ui-menu.esm.js.map +1 -0
  98. package/dist/ui-select.cjs.js +104 -0
  99. package/dist/ui-select.cjs.js.map +1 -0
  100. package/dist/ui-select.esm.js +83 -0
  101. package/dist/ui-select.esm.js.map +1 -0
  102. package/dist/ui.cjs.js +738 -0
  103. package/dist/ui.cjs.js.map +1 -0
  104. package/dist/ui.esm.js +727 -0
  105. package/dist/ui.esm.js.map +1 -0
  106. package/dist/vike/ClientOnly.cjs.js +4 -14
  107. package/dist/vike/ClientOnly.cjs.js.map +1 -1
  108. package/dist/vike/ClientOnly.mjs +4 -14
  109. package/dist/vike/ClientOnly.mjs.map +1 -1
  110. package/dist/vike/{+config.js → config/+config.js} +9 -1
  111. package/dist/vike/config/+config.js.map +1 -0
  112. package/dist/vike/config/package.json +6 -0
  113. package/dist/vike/onRenderClient.cjs.js +165 -95
  114. package/dist/vike/onRenderClient.cjs.js.map +1 -1
  115. package/dist/vike/onRenderClient.mjs +165 -95
  116. package/dist/vike/onRenderClient.mjs.map +1 -1
  117. package/dist/vike/onRenderHtml.cjs.js +59 -24
  118. package/dist/vike/onRenderHtml.cjs.js.map +1 -1
  119. package/dist/vike/onRenderHtml.mjs +60 -25
  120. package/dist/vike/onRenderHtml.mjs.map +1 -1
  121. package/dist/vite/plugin.cjs.js +323 -88
  122. package/dist/vite/plugin.cjs.js.map +1 -1
  123. package/dist/vite/plugin.mjs +323 -89
  124. package/dist/vite/plugin.mjs.map +1 -1
  125. package/dist/zag.cjs.js +408 -0
  126. package/dist/zag.cjs.js.map +1 -0
  127. package/dist/zag.esm.js +405 -0
  128. package/dist/zag.esm.js.map +1 -0
  129. package/llms.txt +166 -103
  130. package/package.json +98 -14
  131. package/src/astro/client.ts +27 -16
  132. package/src/astro/index.d.ts +24 -0
  133. package/src/astro/index.ts +69 -3
  134. package/src/astro/server.ts +8 -2
  135. package/src/collection.ts +6 -93
  136. package/src/core/actions.ts +134 -0
  137. package/src/core/cell.ts +202 -0
  138. package/src/core/debug.ts +23 -0
  139. package/src/core/define.ts +242 -0
  140. package/src/core/hooks.ts +314 -0
  141. package/src/core/hosts/collection.ts +325 -0
  142. package/src/core/hosts/switchable.ts +146 -0
  143. package/src/core/instance.ts +592 -0
  144. package/src/core/markers/clientonly.ts +13 -0
  145. package/src/core/markers/lazy.ts +40 -0
  146. package/src/core/markers/portal.ts +96 -0
  147. package/src/core/markers/suspense.ts +41 -0
  148. package/src/core/markers/transition.ts +70 -0
  149. package/src/core/registry.ts +25 -0
  150. package/src/core/runtime.ts +573 -0
  151. package/src/core/statics.ts +104 -0
  152. package/src/core/teardown.ts +60 -0
  153. package/src/core/view.ts +37 -0
  154. package/src/cycle/dom/DocumentDOMSource.ts +13 -8
  155. package/src/cycle/dom/ElementFinder.ts +7 -9
  156. package/src/cycle/dom/EventDelegator.ts +174 -223
  157. package/src/cycle/dom/IsolateModule.ts +65 -35
  158. package/src/cycle/dom/MainDOMSource.ts +9 -2
  159. package/src/cycle/dom/PriorityQueue.ts +4 -2
  160. package/src/cycle/dom/SymbolTree.ts +18 -47
  161. package/src/cycle/dom/classNameModule.ts +9 -7
  162. package/src/cycle/dom/controlledInputModule.ts +23 -6
  163. package/src/cycle/dom/enrichEventStream.ts +25 -47
  164. package/src/cycle/dom/fragment.ts +7 -0
  165. package/src/cycle/dom/isolate.ts +30 -27
  166. package/src/cycle/dom/makeDOMDriver.ts +115 -56
  167. package/src/cycle/dom/mockDOMSource.ts +21 -1
  168. package/src/cycle/dom/modules.ts +16 -3
  169. package/src/cycle/dom/propsModule.ts +65 -0
  170. package/src/cycle/dom/selectModule.ts +22 -17
  171. package/src/cycle/dom/snabbdom.ts +1 -6
  172. package/src/cycle/dom/thunk.ts +3 -0
  173. package/src/cycle/dom/utils.ts +98 -0
  174. package/src/cycle/dom/viewTransition.ts +43 -0
  175. package/src/cycle/state/StateSource.ts +19 -3
  176. package/src/cycle/state/objIsEqual.ts +50 -0
  177. package/src/cycle/state/types.ts +0 -10
  178. package/src/defineComponent.ts +34 -0
  179. package/src/devtools.d.ts +151 -0
  180. package/src/devtools.ts +34 -0
  181. package/src/element.d.ts +59 -0
  182. package/src/element.ts +235 -0
  183. package/src/extra/backoff.ts +9 -0
  184. package/src/extra/behaviors.ts +169 -0
  185. package/src/extra/browserSignals.ts +23 -0
  186. package/src/extra/browserSources.ts +326 -0
  187. package/src/extra/command.ts +3 -3
  188. package/src/extra/controls.ts +47 -0
  189. package/src/extra/copyAsTest.ts +286 -0
  190. package/src/extra/devtools.ts +101 -31
  191. package/src/extra/devtoolsActions.ts +379 -0
  192. package/src/extra/devtoolsHook.ts +9 -0
  193. package/src/extra/devtoolsNext.ts +83 -0
  194. package/src/extra/diagnostics/checks/actionLog.ts +108 -0
  195. package/src/extra/diagnostics/checks/behaviors.ts +49 -0
  196. package/src/extra/diagnostics/checks/browserSources.ts +96 -0
  197. package/src/extra/diagnostics/checks/collections.ts +1 -1
  198. package/src/extra/diagnostics/checks/controls.ts +148 -0
  199. package/src/extra/diagnostics/checks/dataset.ts +57 -0
  200. package/src/extra/diagnostics/checks/dom.ts +20 -11
  201. package/src/extra/diagnostics/checks/elementCommands.ts +175 -0
  202. package/src/extra/diagnostics/checks/events.ts +17 -2
  203. package/src/extra/diagnostics/checks/fetch.ts +135 -0
  204. package/src/extra/diagnostics/checks/forms.ts +163 -0
  205. package/src/extra/diagnostics/checks/index.ts +97 -4
  206. package/src/extra/diagnostics/checks/inspect.ts +140 -32
  207. package/src/extra/diagnostics/checks/next.ts +417 -0
  208. package/src/extra/diagnostics/checks/persist.ts +54 -0
  209. package/src/extra/diagnostics/checks/props.ts +10 -5
  210. package/src/extra/diagnostics/checks/public.d.ts +98 -7
  211. package/src/extra/diagnostics/checks/replies.ts +128 -0
  212. package/src/extra/diagnostics/checks/router.ts +100 -0
  213. package/src/extra/diagnostics/checks/rxjsHints.ts +1 -1
  214. package/src/extra/diagnostics/checks/shared.ts +86 -2
  215. package/src/extra/diagnostics/checks/shorthand.ts +115 -0
  216. package/src/extra/diagnostics/checks/sortable.ts +129 -0
  217. package/src/extra/diagnostics/checks/state.ts +100 -7
  218. package/src/extra/diagnostics/checks/statics.ts +48 -0
  219. package/src/extra/diagnostics/checks/strict.ts +10 -67
  220. package/src/extra/diagnostics/checks/timers.ts +80 -0
  221. package/src/extra/diagnostics/checks/viewTransitions.ts +47 -0
  222. package/src/extra/diagnostics/checks/virtual.ts +73 -0
  223. package/src/extra/diagnostics/checks/widgets.ts +109 -0
  224. package/src/extra/diagnostics/checks/wiring.ts +66 -10
  225. package/src/extra/diagnostics/codes.ts +268 -23
  226. package/src/extra/diagnostics/index.ts +34 -9
  227. package/src/extra/diagnostics/legacy.ts +17 -0
  228. package/src/extra/driverFactories.ts +83 -19
  229. package/src/extra/elementCommands.ts +53 -0
  230. package/src/extra/fetchDriver.ts +519 -0
  231. package/src/extra/focusWithin.ts +27 -0
  232. package/src/extra/form.ts +268 -0
  233. package/src/extra/formHelpers.ts +139 -0
  234. package/src/extra/head.ts +106 -0
  235. package/src/extra/hmr.ts +2 -3
  236. package/src/extra/owned.ts +24 -0
  237. package/src/extra/pager.ts +50 -0
  238. package/src/extra/persist.ts +133 -0
  239. package/src/extra/pwa.ts +1 -1
  240. package/src/extra/queryCache.ts +107 -0
  241. package/src/extra/reducers.ts +1 -1
  242. package/src/extra/reduxDevtools.ts +92 -0
  243. package/src/extra/replies.ts +54 -0
  244. package/src/extra/router.ts +323 -0
  245. package/src/extra/run.ts +90 -217
  246. package/src/extra/selection.ts +76 -0
  247. package/src/extra/socketDriver.ts +265 -0
  248. package/src/extra/sortable.ts +377 -0
  249. package/src/extra/ssr.ts +427 -160
  250. package/src/extra/standardSchema.ts +27 -0
  251. package/src/extra/testing.ts +2433 -175
  252. package/src/extra/timers.ts +95 -0
  253. package/src/extra/undo.ts +261 -0
  254. package/src/extra/viewTransitions.ts +38 -0
  255. package/src/extra/virtual.ts +513 -0
  256. package/src/extra/widget.ts +201 -0
  257. package/src/index.d.ts +2631 -117
  258. package/src/index.ts +25 -4
  259. package/src/jsx-runtime.ts +15 -1
  260. package/src/jsx.ts +2 -1
  261. package/src/lazy.ts +50 -7
  262. package/src/portal.ts +3 -1
  263. package/src/pragma/index.ts +256 -133
  264. package/src/react-peers.d.ts +5 -0
  265. package/src/react.d.ts +43 -0
  266. package/src/react.ts +110 -0
  267. package/src/shared.ts +48 -0
  268. package/src/slot.ts +1 -1
  269. package/src/suspense.ts +3 -1
  270. package/src/switchable.ts +6 -121
  271. package/src/transition.ts +3 -1
  272. package/src/ui/accordion.ts +64 -0
  273. package/src/ui/dialog.ts +129 -0
  274. package/src/ui/disclosure.ts +35 -0
  275. package/src/ui/popover.ts +45 -0
  276. package/src/ui/shared.ts +94 -0
  277. package/src/ui/tabs.ts +82 -0
  278. package/src/ui/toaster.ts +198 -0
  279. package/src/ui/tooltip.ts +70 -0
  280. package/src/ui/zag/combobox.ts +115 -0
  281. package/src/ui/zag/menu.ts +40 -0
  282. package/src/ui/zag/select.ts +54 -0
  283. package/src/ui/zag/shared.ts +43 -0
  284. package/src/ui-combobox.d.ts +20 -0
  285. package/src/ui-combobox.ts +6 -0
  286. package/src/ui-menu.d.ts +30 -0
  287. package/src/ui-menu.ts +6 -0
  288. package/src/ui-select.d.ts +17 -0
  289. package/src/ui-select.ts +6 -0
  290. package/src/ui-zag-types.d.ts +42 -0
  291. package/src/ui.d.ts +208 -0
  292. package/src/ui.ts +15 -0
  293. package/src/vike/+config.ts +9 -1
  294. package/src/vike/ClientOnly.ts +1 -1
  295. package/src/vike/onRenderClient.ts +144 -96
  296. package/src/vike/onRenderHtml.ts +69 -26
  297. package/src/vike/types.ts +7 -1
  298. package/src/vite/globalthis-shim.ts +18 -0
  299. package/src/vite/globalthis.ts +56 -0
  300. package/src/vite/plugin.d.ts +22 -3
  301. package/src/vite/plugin.ts +222 -24
  302. package/src/zag.d.ts +64 -0
  303. package/src/zag.ts +238 -0
  304. package/dist/vike/+config.cjs.js +0 -66
  305. package/dist/vike/+config.cjs.js.map +0 -1
  306. package/dist/vike/+config.js.map +0 -1
  307. package/src/component.ts +0 -2301
  308. package/src/cycle/isolate/index.ts +0 -196
  309. package/src/cycle/run/index.ts +0 -151
  310. package/src/cycle/run/internals.ts +0 -143
  311. package/src/cycle/state/Collection.ts +0 -184
  312. package/src/cycle/state/index.ts +0 -5
  313. package/src/cycle/state/pickCombine.ts +0 -201
  314. package/src/cycle/state/pickMerge.ts +0 -122
  315. package/src/cycle/state/withState.ts +0 -47
  316. package/src/pragma/fn.ts +0 -55
@@ -1,6 +1,6 @@
1
1
  import xs, {Stream, Subscription} from 'xstream';
2
2
  import {ScopeChecker} from './ScopeChecker';
3
- import {IsolateModule} from './IsolateModule';
3
+ import {IsolateModule, upOf} from './IsolateModule';
4
4
  import {getSelectors, isEqualNamespace} from './utils';
5
5
  import {ElementFinder} from './ElementFinder';
6
6
  import {EventsFnOptions} from './DOMSource';
@@ -41,7 +41,11 @@ export const eventTypesThatDontBubble = [
41
41
  `pointerleave`,
42
42
  `gotpointercapture`,
43
43
  `lostpointercapture`,
44
- // Media
44
+ // Media (and img/script/link error, abort)
45
+ `abort`,
46
+ `error`,
47
+ `loadstart`,
48
+ `progress`,
45
49
  `canplay`,
46
50
  `canplaythrough`,
47
51
  `durationchange`,
@@ -68,7 +72,11 @@ export const eventTypesThatDontBubble = [
68
72
  `reset`,
69
73
  `submit`,
70
74
  `formdata`,
75
+ // details, popover, dialog
71
76
  `toggle`,
77
+ `beforetoggle`,
78
+ `cancel`,
79
+ `close`,
72
80
  // Animation / Transition
73
81
  `animationstart`,
74
82
  `animationend`,
@@ -91,7 +99,8 @@ interface NonBubblingListener {
91
99
  destination: Destination;
92
100
  }
93
101
 
94
- type NonBubblingMeta = [Stream<Event>, string, ElementFinder, Destination]
102
+ // [subject, type, finder, destination, number of started streams]
103
+ type NonBubblingMeta = [Stream<Event>, string, ElementFinder, Destination, number]
95
104
 
96
105
  export class EventDelegator {
97
106
  private virtualListeners = new SymbolTree<
@@ -142,9 +151,6 @@ export class EventDelegator {
142
151
  options: EventsFnOptions,
143
152
  bubbles?: boolean
144
153
  ): Stream<Event> {
145
- const subject = xs.never();
146
- let dest;
147
-
148
154
  const scopeChecker = new ScopeChecker(namespace, this.isolateModule);
149
155
 
150
156
  const shouldBubble =
@@ -157,94 +163,87 @@ export class EventDelegator {
157
163
  this.setupDOMListener(eventType, !!options.passive);
158
164
  }
159
165
 
160
- dest = this.insertListener(subject, scopeChecker, eventType, options);
161
- return subject;
162
- } else {
163
- const setArray: Array<NonBubblingMeta> = [];
164
- this.nonBubblingListenersToAdd.forEach(v => setArray.push(v));
165
- let found = undefined, index = 0;
166
- const length = setArray.length;
167
- const tester = (x: NonBubblingMeta) => {
168
- const [_sub, et, ef, _] = x;
169
- return eventType === et && isEqualNamespace(ef.namespace, namespace);
170
- }
171
-
172
- while (!found && index < length) {
173
- const item = setArray[index]
174
- found = tester(item) ? item : found;
175
- index++;
176
- }
177
-
178
- let input: NonBubblingMeta = found as NonBubblingMeta;
179
-
180
- let nonBubbleSubject: Stream<Event>;
181
- if (!input) {
182
- const finder = new ElementFinder(namespace, this.isolateModule);
183
- dest = this.insertListener(subject, scopeChecker, eventType, options);
184
- input = [subject, eventType, finder, dest];
185
- nonBubbleSubject = subject;
186
- this.nonBubblingListenersToAdd.add(input);
187
- this.setupNonBubblingListener(input);
188
- } else {
189
- const [sub] = input;
190
- nonBubbleSubject = sub;
191
- }
192
-
193
- const self = this;
194
-
195
- let subscription: any = null;
196
- return xs.create({
197
- start: listener => {
198
- subscription = nonBubbleSubject.subscribe(listener);
166
+ // P45-A: the destination is in the delegator only while the stream has listeners, so
167
+ // a disposed component leaves nothing behind (audit rec 3). Each stream has its own
168
+ // destination; xstream counts the stream's own listeners
169
+ let dest: Destination;
170
+ const out: Stream<Event> = xs.create({
171
+ start: () => {
172
+ dest = this.insertListener(out, scopeChecker, eventType, options);
199
173
  },
200
- stop: () => {
201
- const [_s, et, ef, _d] = input;
202
- const elements = ef.call();
203
-
204
- elements.forEach(function(element: any) {
205
- const subs = element.subs;
206
- if (subs && subs[et]) {
207
- subs[et].unsubscribe();
208
- delete subs[et];
174
+ stop: () => this.removeListener(dest, eventType),
175
+ });
176
+ return out;
177
+ }
178
+
179
+ // one record, and one listener per element, per type and scope: the streams on it share
180
+ // it, and the last one to stop removes it. G-263: a stream finds (or makes) the live record
181
+ // each time it starts, so a stream never started leaves none, and one restarted after its
182
+ // record was removed shares the record made meanwhile
183
+ let rec: NonBubblingMeta | undefined, subscription: any;
184
+ return xs.create({
185
+ start: listener => {
186
+ rec = undefined;
187
+ this.nonBubblingListenersToAdd.forEach(x => {
188
+ if (!rec && x[1] === eventType && isEqualNamespace(x[2].namespace, namespace)) rec = x;
189
+ });
190
+ if (!rec) {
191
+ const s = xs.never();
192
+ rec = [s, eventType, new ElementFinder(namespace, this.isolateModule), this.insertListener(s, scopeChecker, eventType, options), 0];
193
+ this.nonBubblingListenersToAdd.add(rec);
194
+ }
195
+ if (!rec[4]++) this.setupNonBubblingListener(rec);
196
+ subscription = rec[0].subscribe(listener);
197
+ },
198
+ stop: () => {
199
+ const r = rec!;
200
+ subscription.unsubscribe();
201
+ if (!--r[4]) {
202
+ const map = this.nonBubblingListeners.get(eventType);
203
+ if (map) map.forEach((l, element: any) => {
204
+ if (l.destination === r[3]) {
205
+ l.sub!.unsubscribe();
206
+ delete element.subs[eventType];
207
+ map.delete(element);
209
208
  }
210
209
  });
211
-
212
- self.nonBubblingListenersToAdd.delete(input as any);
213
-
214
- subscription.unsubscribe();
210
+ this.nonBubblingListenersToAdd.delete(r);
211
+ this.removeListener(r[3], eventType);
215
212
  }
216
- });
217
- }
213
+ },
214
+ });
218
215
  }
219
216
 
220
- public removeElement(element: Element, namespace?: Array<Scope>): void {
221
- if (namespace !== undefined) {
222
- this.virtualListeners.delete(namespace);
223
- }
224
- const toRemove: Array<[string, Element]> = [];
217
+ public removeElement(element: Element): void {
218
+ const types: Array<string> = [];
225
219
  this.nonBubblingListeners.forEach((map, type) => {
226
220
  if (map.has(element)) {
227
- toRemove.push([type, element]);
221
+ types.push(type);
228
222
  const subs = (element as any).subs;
229
- if (subs) {
230
- Object.keys(subs).forEach((key: any) => {
231
- subs[key].unsubscribe();
232
- });
233
- }
223
+ if (subs) Object.keys(subs).forEach(key => subs[key].unsubscribe());
234
224
  }
235
225
  });
236
- for (let i = 0; i < toRemove.length; i++) {
237
- const map = this.nonBubblingListeners.get(toRemove[i][0]);
238
- if (!map) {
239
- continue;
240
- }
241
- map.delete(toRemove[i][1]);
242
- if (map.size === 0) {
243
- this.nonBubblingListeners.delete(toRemove[i][0]);
244
- } else {
245
- this.nonBubblingListeners.set(toRemove[i][0], map);
246
- }
247
- }
226
+ types.forEach(type => {
227
+ const map = this.nonBubblingListeners.get(type);
228
+ if (!map) return;
229
+ map.delete(element);
230
+ if (!map.size) this.nonBubblingListeners.delete(type);
231
+ });
232
+ }
233
+
234
+ // the queues a destination goes in: its scope's and its ancestors' up to the nearest total scope
235
+ private eachSet(
236
+ n: Array<Scope>,
237
+ eventType: string,
238
+ f: (map: Map<string, PriorityQueue<Destination>>, max: number) => void,
239
+ create?: boolean
240
+ ): void {
241
+ let max = n.length;
242
+ do {
243
+ const map = this.virtualListeners.get(n, create ? () => new Map() : undefined, max);
244
+ if (map) f(map, max);
245
+ max--;
246
+ } while (max >= 0 && n[max].type !== 'total');
248
247
  }
249
248
 
250
249
  private insertListener(
@@ -253,15 +252,7 @@ export class EventDelegator {
253
252
  eventType: string,
254
253
  options: EventsFnOptions
255
254
  ): Destination {
256
- const relevantSets: Array<PriorityQueue<Destination>> = [];
257
255
  const n = scopeChecker._namespace;
258
- let max = n.length;
259
-
260
- do {
261
- relevantSets.push(this.getVirtualListeners(eventType, n, true, max));
262
- max--;
263
- } while (max >= 0 && n[max].type !== 'total');
264
-
265
256
  const destination = {
266
257
  ...options,
267
258
  scopeChecker,
@@ -271,40 +262,37 @@ export class EventDelegator {
271
262
  passive: !!options.passive,
272
263
  };
273
264
 
274
- for (let i = 0; i < relevantSets.length; i++) {
275
- relevantSets[i].add(destination, n.length);
276
- }
265
+ this.eachSet(n, eventType, map => {
266
+ if (!map.has(eventType)) map.set(eventType, new PriorityQueue<Destination>());
267
+ map.get(eventType)!.add(destination, n.length);
268
+ }, true);
277
269
 
278
270
  return destination;
279
271
  }
280
272
 
273
+ // P45-A: take a stopped stream's destination out, and drop the queues and scopes left empty
274
+ private removeListener(dest: Destination, eventType: string): void {
275
+ const n = dest.scopeChecker._namespace;
276
+ this.eachSet(n, eventType, (map, max) => {
277
+ const q = map.get(eventType);
278
+ if (q && !q.delete(dest)) {
279
+ map.delete(eventType);
280
+ if (!map.size) this.virtualListeners.delete(n, max);
281
+ }
282
+ });
283
+ }
284
+
281
285
  private getVirtualListeners(
282
286
  eventType: string,
283
- namespace: Array<Scope>,
284
- exact = false,
285
- max?: number
287
+ namespace: Array<Scope>
286
288
  ): PriorityQueue<Destination> {
287
- let _max = max !== undefined ? max : namespace.length;
288
- if (!exact) {
289
- for (let i = _max - 1; i >= 0; i--) {
290
- if (namespace[i].type === 'total') {
291
- _max = i + 1;
292
- break;
293
- }
294
- _max = i;
295
- }
296
- }
297
-
298
- const map = this.virtualListeners.getDefault(
299
- namespace,
300
- () => new Map<string, PriorityQueue<Destination>>(),
301
- _max
302
- );
289
+ // up to the innermost total scope (none: the root's)
290
+ let _max = namespace.length;
291
+ while (_max && namespace[_max - 1].type !== 'total') _max--;
303
292
 
304
- if (!map.has(eventType)) {
305
- map.set(eventType, new PriorityQueue<Destination>());
306
- }
307
- return map.get(eventType) as PriorityQueue<Destination>;
293
+ // a lookup never adds a scope (P45-A)
294
+ const map = this.virtualListeners.get(namespace, undefined, _max);
295
+ return (map && map.get(eventType)) || ([] as any);
308
296
  }
309
297
 
310
298
  private setupDOMListener(eventType: string, passive: boolean): void {
@@ -329,59 +317,43 @@ export class EventDelegator {
329
317
  private setupNonBubblingListener(
330
318
  input: NonBubblingMeta
331
319
  ): void {
332
- const [_, eventType, elementFinder, destination] = input;
333
- if (!this.origin) {
320
+ const [_, eventType, elementFinder, destination, started] = input;
321
+ if (!this.origin || !started) {
334
322
  return;
335
323
  }
336
324
 
337
- const elements = elementFinder.call();
338
- if (elements.length) {
339
- const self = this;
340
- elements.forEach((element: Element) => {
341
- const subs = (element as any).subs;
342
- if (!subs || !subs[eventType]) {
343
- const sub = fromEvent(
344
- element,
345
- eventType,
346
- false,
347
- false,
348
- destination.passive
349
- ).subscribe({
350
- next: (ev: Event) =>
351
- self.onEvent(eventType, ev, !!destination.passive, false),
352
- error: () => {},
353
- complete: () => {},
354
- });
355
- if (!self.nonBubblingListeners.has(eventType)) {
356
- self.nonBubblingListeners.set(
357
- eventType,
358
- new Map<Element, NonBubblingListener>()
359
- );
360
- }
361
- const map = self.nonBubblingListeners.get(eventType);
362
- if (!map) {
363
- return;
364
- }
365
- map.set(element, {sub, destination});
366
-
367
- (element as any).subs = {
368
- ...subs,
369
- [eventType]: sub,
370
- };
371
- }
372
- });
373
- }
325
+ elementFinder.call().forEach((element: Element) => {
326
+ const subs = (element as any).subs;
327
+ if (!subs || !subs[eventType]) {
328
+ const sub = fromEvent(
329
+ element,
330
+ eventType,
331
+ false,
332
+ false,
333
+ destination.passive
334
+ ).subscribe({
335
+ next: (ev: Event) =>
336
+ this.onEvent(eventType, ev, !!destination.passive, false),
337
+ error: () => {},
338
+ complete: () => {},
339
+ });
340
+ let map = this.nonBubblingListeners.get(eventType);
341
+ if (!map) this.nonBubblingListeners.set(eventType, map = new Map());
342
+ map.set(element, {sub, destination});
343
+
344
+ (element as any).subs = {
345
+ ...subs,
346
+ [eventType]: sub,
347
+ };
348
+ }
349
+ });
374
350
  }
375
351
 
376
352
  private resetEventListeners(): void {
377
- const iter = this.domListeners.entries();
378
- let curr = iter.next();
379
- while (!curr.done) {
380
- const [type, {sub, passive}] = curr.value;
353
+ this.domListeners.forEach(({sub, passive}, type) => {
381
354
  sub.unsubscribe();
382
355
  this.setupDOMListener(type, passive);
383
- curr = iter.next();
384
- }
356
+ });
385
357
  }
386
358
 
387
359
  private putNonBubblingListener(
@@ -411,73 +383,43 @@ export class EventDelegator {
411
383
  bubbles = true
412
384
  ): void {
413
385
  const cycleEvent = this.patchEvent(event);
414
- const rootElement = this.isolateModule.getRootElement(
415
- event.target as Element
416
- );
386
+ const target = event.target as Element;
387
+ const rootElement = this.isolateModule.getRootElement(target);
417
388
 
418
389
  if (bubbles) {
419
- const namespace = this.isolateModule.getNamespace(
420
- event.target as Element
421
- );
390
+ const namespace = this.isolateModule.getNamespace(target);
422
391
  if (!namespace) {
423
392
  return;
424
393
  }
425
394
  const listeners = this.getVirtualListeners(eventType, namespace);
426
- this.bubble(
427
- eventType,
428
- event.target as Element,
429
- rootElement,
430
- cycleEvent,
431
- listeners,
432
- namespace,
433
- namespace.length - 1,
434
- true,
435
- passive
436
- );
437
-
438
- this.bubble(
395
+ const phase = (useCapture: boolean) => this.bubble(
439
396
  eventType,
440
- event.target as Element,
397
+ target,
441
398
  rootElement,
442
399
  cycleEvent,
443
400
  listeners,
444
401
  namespace,
445
402
  namespace.length - 1,
446
- false,
403
+ useCapture,
447
404
  passive
448
405
  );
406
+ phase(true);
407
+ phase(false);
449
408
  } else {
450
- this.putNonBubblingListener(
451
- eventType,
452
- event.target as Element,
453
- true,
454
- passive
455
- );
456
- this.doBubbleStep(
457
- eventType,
458
- event.target as Element,
459
- rootElement,
460
- cycleEvent,
461
- this.virtualNonBubblingListener,
462
- true,
463
- passive
464
- );
465
-
466
- this.putNonBubblingListener(
467
- eventType,
468
- event.target as Element,
469
- false,
470
- passive
471
- );
472
- this.doBubbleStep(
473
- eventType,
474
- event.target as Element,
475
- rootElement,
476
- cycleEvent,
477
- this.virtualNonBubblingListener,
478
- false,
479
- passive
480
- );
409
+ const phase = (useCapture: boolean) => {
410
+ this.putNonBubblingListener(eventType, target, useCapture, passive);
411
+ this.doBubbleStep(
412
+ eventType,
413
+ target,
414
+ rootElement,
415
+ cycleEvent,
416
+ this.virtualNonBubblingListener,
417
+ useCapture,
418
+ passive
419
+ );
420
+ };
421
+ phase(true);
422
+ phase(false);
481
423
  event.stopPropagation();
482
424
  }
483
425
  }
@@ -505,24 +447,34 @@ export class EventDelegator {
505
447
  );
506
448
  }
507
449
 
450
+ // G-356: a moved element bubbles to its home (an element of its component), not to the
451
+ // element it was moved into (G-381: upOf counts the homes on the event)
452
+ const up = upOf(elm, event) as Element;
508
453
  let newRoot: Element | undefined = rootElement;
509
454
  let newIndex = index;
455
+ let newListeners = listeners;
510
456
  if (elm === rootElement) {
511
- if (index >= 0 && namespace[index].type === 'sibling') {
512
- newRoot = this.isolateModule.getElement(namespace, index);
513
- newIndex--;
514
- } else {
457
+ if (index < 0 || !up) {
515
458
  return;
516
459
  }
460
+ // the parent scope's root that contains elm (G-144: it may have several)
461
+ newRoot = this.isolateModule.getRootElement(up);
462
+ newIndex--;
463
+ // G-145: like the browser, the event bubbles out of a total scope (a child
464
+ // component) to the parent's own elements; their listeners live in the parent's
465
+ // scope, and isDirectlyInScope keeps them from matching the child's elements
466
+ if (namespace[index].type === 'total') {
467
+ newListeners = this.getVirtualListeners(eventType, namespace.slice(0, index));
468
+ }
517
469
  }
518
470
 
519
- if (elm.parentNode && newRoot) {
471
+ if (up && newRoot) {
520
472
  this.bubble(
521
473
  eventType,
522
- elm.parentNode as Element,
474
+ up,
523
475
  newRoot,
524
476
  event,
525
- listeners,
477
+ newListeners,
526
478
  namespace,
527
479
  newIndex,
528
480
  useCapture,
@@ -562,8 +514,7 @@ export class EventDelegator {
562
514
  if (
563
515
  !event.propagationHasBeenStopped &&
564
516
  dest.scopeChecker.isDirectlyInScope(elm) &&
565
- ((sel !== '' && elm.matches(sel)) ||
566
- (sel === '' && elm === rootElement))
517
+ (sel ? elm.matches(sel) : elm === rootElement)
567
518
  ) {
568
519
  preventDefaultConditional(
569
520
  event,