glass-easel 0.2.0 → 0.3.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 (197) hide show
  1. package/.eslintignore +4 -0
  2. package/dist/glass_easel.all.js +2 -2
  3. package/dist/glass_easel.all.js.map +1 -1
  4. package/dist/glass_easel.d.ts +2546 -0
  5. package/dist/glass_easel.domlike.global.js +2 -2
  6. package/dist/glass_easel.domlike.global.js.map +1 -1
  7. package/guide/zh_CN/appendix/backend_protocol.md +164 -74
  8. package/jest.unit.config.js +7 -4
  9. package/package.json +7 -5
  10. package/rollup.config.ts +101 -0
  11. package/src/backend/backend_protocol.ts +26 -345
  12. package/src/backend/composed_backend_protocol.ts +10 -264
  13. package/src/backend/current_window_backend_context.ts +478 -0
  14. package/src/backend/domlike_backend_protocol.ts +9 -498
  15. package/src/backend/empty_backend.ts +265 -0
  16. package/src/backend/empty_composed_backend.ts +245 -0
  17. package/src/backend/index.ts +18 -0
  18. package/src/backend/suggested_backend_protocol.ts +62 -11
  19. package/src/behavior.ts +131 -442
  20. package/src/class_list.ts +328 -222
  21. package/src/component.ts +349 -184
  22. package/src/component_params.ts +54 -25
  23. package/src/component_space.ts +36 -15
  24. package/src/data_path.ts +22 -34
  25. package/src/data_proxy.ts +334 -27
  26. package/src/devtool.ts +89 -0
  27. package/src/element.ts +667 -285
  28. package/src/element_iterator.ts +9 -4
  29. package/src/event.ts +39 -20
  30. package/src/external_shadow_tree.ts +2 -2
  31. package/src/func_arr.ts +55 -101
  32. package/src/global_options.ts +18 -42
  33. package/src/index.ts +72 -73
  34. package/src/mutation_observer.ts +10 -7
  35. package/src/native_node.ts +79 -35
  36. package/src/node.ts +23 -30
  37. package/src/relation.ts +101 -12
  38. package/src/render.ts +5 -3
  39. package/src/selector.ts +9 -7
  40. package/src/shadow_root.ts +96 -240
  41. package/src/template_engine.ts +10 -7
  42. package/src/text_node.ts +43 -19
  43. package/src/tmpl/index.ts +38 -30
  44. package/src/tmpl/native_rendering.ts +14 -21
  45. package/src/tmpl/proc_gen_wrapper.ts +189 -151
  46. package/src/tmpl/proc_gen_wrapper_dom.ts +10 -8
  47. package/src/tmpl/range_list_diff.ts +30 -9
  48. package/src/trait_behaviors.ts +1 -1
  49. package/src/type_symbol.ts +36 -0
  50. package/src/virtual_node.ts +26 -13
  51. package/src/warning.ts +119 -0
  52. package/tests/base/composed_backend.ts +143 -142
  53. package/tests/base/env.ts +76 -3
  54. package/tests/base/match.ts +24 -22
  55. package/tests/base/shadow_backend.ts +917 -0
  56. package/tests/core/backend.test.ts +53 -1
  57. package/tests/core/behavior.test.ts +1 -1
  58. package/tests/core/data_update.test.ts +135 -0
  59. package/tests/core/misc.test.ts +91 -21
  60. package/tests/core/placeholder.test.ts +66 -25
  61. package/tests/core/slot.test.ts +161 -23
  62. package/tests/legacy/component.test.js +152 -78
  63. package/tests/legacy/data_path.test.js +19 -19
  64. package/tests/legacy/data_proxy.test.js +14 -7
  65. package/tests/legacy/external.test.js +8 -8
  66. package/tests/legacy/mutation_observer.test.js +7 -5
  67. package/tests/legacy/slot.test.js +2 -3
  68. package/tests/legacy/virtual.test.js +258 -25
  69. package/tests/tmpl/binding_map.test.ts +201 -195
  70. package/tests/tmpl/event.test.ts +138 -23
  71. package/tests/tmpl/expression.test.ts +15 -15
  72. package/tests/tmpl/lvalue.test.ts +101 -0
  73. package/tests/tmpl/structure.test.ts +486 -43
  74. package/tests/types/chaining.test.ts +27 -30
  75. package/tests/types/createElement.test.ts +5 -4
  76. package/tests/types/definition.test.ts +11 -14
  77. package/tsconfig.json +1 -3
  78. package/dist/glass_easel.all.d.ts +0 -1
  79. package/dist/glass_easel.domlike.global.d.ts +0 -1
  80. package/dist/index.d.ts +0 -1
  81. package/dist/index.js +0 -1
  82. package/dist/types/src/backend/backend_protocol.d.ts +0 -141
  83. package/dist/types/src/backend/backend_protocol.d.ts.map +0 -1
  84. package/dist/types/src/backend/composed_backend_protocol.d.ts +0 -102
  85. package/dist/types/src/backend/composed_backend_protocol.d.ts.map +0 -1
  86. package/dist/types/src/backend/domlike_backend_protocol.d.ts +0 -100
  87. package/dist/types/src/backend/domlike_backend_protocol.d.ts.map +0 -1
  88. package/dist/types/src/backend/mode.d.ts +0 -65
  89. package/dist/types/src/backend/mode.d.ts.map +0 -1
  90. package/dist/types/src/backend/suggested_backend_protocol.d.ts +0 -30
  91. package/dist/types/src/backend/suggested_backend_protocol.d.ts.map +0 -1
  92. package/dist/types/src/behavior.d.ts +0 -434
  93. package/dist/types/src/behavior.d.ts.map +0 -1
  94. package/dist/types/src/class_list.d.ts +0 -83
  95. package/dist/types/src/class_list.d.ts.map +0 -1
  96. package/dist/types/src/component.d.ts +0 -325
  97. package/dist/types/src/component.d.ts.map +0 -1
  98. package/dist/types/src/component_params.d.ts +0 -236
  99. package/dist/types/src/component_params.d.ts.map +0 -1
  100. package/dist/types/src/component_space.d.ts +0 -211
  101. package/dist/types/src/component_space.d.ts.map +0 -1
  102. package/dist/types/src/data_path.d.ts +0 -5
  103. package/dist/types/src/data_path.d.ts.map +0 -1
  104. package/dist/types/src/data_proxy.d.ts +0 -107
  105. package/dist/types/src/data_proxy.d.ts.map +0 -1
  106. package/dist/types/src/data_utils.d.ts +0 -3
  107. package/dist/types/src/data_utils.d.ts.map +0 -1
  108. package/dist/types/src/element.d.ts +0 -365
  109. package/dist/types/src/element.d.ts.map +0 -1
  110. package/dist/types/src/element_iterator.d.ts +0 -43
  111. package/dist/types/src/element_iterator.d.ts.map +0 -1
  112. package/dist/types/src/event.d.ts +0 -105
  113. package/dist/types/src/event.d.ts.map +0 -1
  114. package/dist/types/src/external_shadow_tree.d.ts +0 -20
  115. package/dist/types/src/external_shadow_tree.d.ts.map +0 -1
  116. package/dist/types/src/func_arr.d.ts +0 -39
  117. package/dist/types/src/func_arr.d.ts.map +0 -1
  118. package/dist/types/src/global_options.d.ts +0 -120
  119. package/dist/types/src/global_options.d.ts.map +0 -1
  120. package/dist/types/src/index.d.ts +0 -43
  121. package/dist/types/src/index.d.ts.map +0 -1
  122. package/dist/types/src/mutation_observer.d.ts +0 -79
  123. package/dist/types/src/mutation_observer.d.ts.map +0 -1
  124. package/dist/types/src/native_node.d.ts +0 -11
  125. package/dist/types/src/native_node.d.ts.map +0 -1
  126. package/dist/types/src/node.d.ts +0 -48
  127. package/dist/types/src/node.d.ts.map +0 -1
  128. package/dist/types/src/relation.d.ts +0 -46
  129. package/dist/types/src/relation.d.ts.map +0 -1
  130. package/dist/types/src/render.d.ts +0 -3
  131. package/dist/types/src/render.d.ts.map +0 -1
  132. package/dist/types/src/selector.d.ts +0 -32
  133. package/dist/types/src/selector.d.ts.map +0 -1
  134. package/dist/types/src/shadow_root.d.ts +0 -174
  135. package/dist/types/src/shadow_root.d.ts.map +0 -1
  136. package/dist/types/src/template_engine.d.ts +0 -56
  137. package/dist/types/src/template_engine.d.ts.map +0 -1
  138. package/dist/types/src/text_node.d.ts +0 -44
  139. package/dist/types/src/text_node.d.ts.map +0 -1
  140. package/dist/types/src/tmpl/index.d.ts +0 -19
  141. package/dist/types/src/tmpl/index.d.ts.map +0 -1
  142. package/dist/types/src/tmpl/native_rendering.d.ts +0 -45
  143. package/dist/types/src/tmpl/native_rendering.d.ts.map +0 -1
  144. package/dist/types/src/tmpl/proc_gen_wrapper.d.ts +0 -89
  145. package/dist/types/src/tmpl/proc_gen_wrapper.d.ts.map +0 -1
  146. package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts +0 -50
  147. package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts.map +0 -1
  148. package/dist/types/src/tmpl/range_list_diff.d.ts +0 -19
  149. package/dist/types/src/tmpl/range_list_diff.d.ts.map +0 -1
  150. package/dist/types/src/trait_behaviors.d.ts +0 -38
  151. package/dist/types/src/trait_behaviors.d.ts.map +0 -1
  152. package/dist/types/src/virtual_node.d.ts +0 -10
  153. package/dist/types/src/virtual_node.d.ts.map +0 -1
  154. package/dist/types/tests/backend/domlike.test.d.ts +0 -2
  155. package/dist/types/tests/backend/domlike.test.d.ts.map +0 -1
  156. package/dist/types/tests/base/composed_backend.d.ts +0 -123
  157. package/dist/types/tests/base/composed_backend.d.ts.map +0 -1
  158. package/dist/types/tests/base/env.d.ts +0 -30
  159. package/dist/types/tests/base/env.d.ts.map +0 -1
  160. package/dist/types/tests/base/match.d.ts +0 -9
  161. package/dist/types/tests/base/match.d.ts.map +0 -1
  162. package/dist/types/tests/core/backend.test.d.ts +0 -2
  163. package/dist/types/tests/core/backend.test.d.ts.map +0 -1
  164. package/dist/types/tests/core/behavior.test.d.ts +0 -2
  165. package/dist/types/tests/core/behavior.test.d.ts.map +0 -1
  166. package/dist/types/tests/core/component_space.test.d.ts +0 -2
  167. package/dist/types/tests/core/component_space.test.d.ts.map +0 -1
  168. package/dist/types/tests/core/data_update.test.d.ts +0 -2
  169. package/dist/types/tests/core/data_update.test.d.ts.map +0 -1
  170. package/dist/types/tests/core/misc.test.d.ts +0 -2
  171. package/dist/types/tests/core/misc.test.d.ts.map +0 -1
  172. package/dist/types/tests/core/placeholder.test.d.ts +0 -2
  173. package/dist/types/tests/core/placeholder.test.d.ts.map +0 -1
  174. package/dist/types/tests/core/slot.test.d.ts +0 -2
  175. package/dist/types/tests/core/slot.test.d.ts.map +0 -1
  176. package/dist/types/tests/core/trait_behaviors.test.d.ts +0 -2
  177. package/dist/types/tests/core/trait_behaviors.test.d.ts.map +0 -1
  178. package/dist/types/tests/tmpl/binding_map.test.d.ts +0 -2
  179. package/dist/types/tests/tmpl/binding_map.test.d.ts.map +0 -1
  180. package/dist/types/tests/tmpl/event.test.d.ts +0 -2
  181. package/dist/types/tests/tmpl/event.test.d.ts.map +0 -1
  182. package/dist/types/tests/tmpl/expression.test.d.ts +0 -2
  183. package/dist/types/tests/tmpl/expression.test.d.ts.map +0 -1
  184. package/dist/types/tests/tmpl/lvalue.test.d.ts +0 -2
  185. package/dist/types/tests/tmpl/lvalue.test.d.ts.map +0 -1
  186. package/dist/types/tests/tmpl/native_rendering.test.d.ts +0 -2
  187. package/dist/types/tests/tmpl/native_rendering.test.d.ts.map +0 -1
  188. package/dist/types/tests/tmpl/structure.test.d.ts +0 -2
  189. package/dist/types/tests/tmpl/structure.test.d.ts.map +0 -1
  190. package/dist/types/tests/types/chaining.test.d.ts +0 -2
  191. package/dist/types/tests/types/chaining.test.d.ts.map +0 -1
  192. package/dist/types/tests/types/createElement.test.d.ts +0 -2
  193. package/dist/types/tests/types/createElement.test.d.ts.map +0 -1
  194. package/dist/types/tests/types/definition.test.d.ts +0 -2
  195. package/dist/types/tests/types/definition.test.d.ts.map +0 -1
  196. package/webpack.config.js +0 -275
  197. /package/src/backend/{mode.ts → shared.ts} +0 -0
@@ -1,21 +1,19 @@
1
1
  /* eslint-disable class-methods-use-this */
2
2
 
3
- import { ProcGenGroupList } from '.'
4
- import {
5
- DataValue,
6
- Element,
7
- ShadowRoot,
8
- VirtualNode,
9
- TextNode,
10
- Component,
11
- GeneralComponent,
12
- Node,
13
- ShadowedEvent,
14
- StyleSegmentIndex,
15
- NativeNode,
16
- } from '..'
17
- import { DataPath } from '../data_path'
18
- import { SlotMode } from '../shadow_root'
3
+ import { Component, type GeneralComponent } from '../component'
4
+ import { type DataPath } from '../data_path'
5
+ import { type DataValue } from '../data_proxy'
6
+ import { Element, StyleSegmentIndex } from '../element'
7
+ import { type ShadowedEvent } from '../event'
8
+ import { ENV } from '../global_options'
9
+ import { type NativeNode } from '../native_node'
10
+ import { type Node } from '../node'
11
+ import { SlotMode, type ShadowRoot } from '../shadow_root'
12
+ import { type TextNode } from '../text_node'
13
+ import { type ProcGenGroupList } from '../tmpl'
14
+ import { isComponent, isNativeNode } from '../type_symbol'
15
+ import { type VirtualNode } from '../virtual_node'
16
+ import { dispatchError } from '../warning'
19
17
  import { RangeListManager } from './range_list_diff'
20
18
 
21
19
  export type UpdatePathTreeNode = true | { [key: string]: UpdatePathTreeNode } | UpdatePathTreeNode[]
@@ -133,14 +131,18 @@ type DefineForLoop = (
133
131
  ) => void,
134
132
  ) => void
135
133
 
136
- type DefineSlot = (name: string | undefined, slotValueInit?: (elem: Element) => void) => void
134
+ type DefineSlot = (
135
+ name: string | undefined,
136
+ slotValueInit?: (elem: Element) => void,
137
+ slot?: string,
138
+ ) => void
137
139
 
138
140
  type DefinePureVirtualNode = (children: DefineChildren, slot: string | undefined) => void
139
141
 
140
142
  export class ProcGenWrapper {
141
143
  shadowRoot: ShadowRoot
142
144
  procGen: ProcGen
143
- disallowNativeNode: boolean
145
+ fallbackListenerOnNativeNode: boolean
144
146
  bindingMapDisabled = false
145
147
  eventObjectFilter: (x: ShadowedEvent<unknown>) => ShadowedEvent<unknown> = emptyFilter
146
148
  changePropFilter = emptyFilter
@@ -149,12 +151,12 @@ export class ProcGenWrapper {
149
151
  constructor(
150
152
  shadowRoot: ShadowRoot,
151
153
  procGen: ProcGen,
152
- disallowNativeNode: boolean,
154
+ fallbackListenerOnNativeNode: boolean,
153
155
  eventObjectFilter?: (x: ShadowedEvent<unknown>) => ShadowedEvent<unknown>,
154
156
  ) {
155
157
  this.shadowRoot = shadowRoot
156
158
  this.procGen = procGen
157
- this.disallowNativeNode = disallowNativeNode
159
+ this.fallbackListenerOnNativeNode = fallbackListenerOnNativeNode
158
160
  if (eventObjectFilter) {
159
161
  this.eventObjectFilter = eventObjectFilter
160
162
  }
@@ -189,7 +191,7 @@ export class ProcGenWrapper {
189
191
  // eslint-disable-next-line no-loop-func
190
192
  (elem: Element) => {
191
193
  if (prevElement !== null && elem !== prevElement) {
192
- if (prevElement instanceof Component) {
194
+ if (isComponent(prevElement)) {
193
195
  if (prevElement.hasPendingChanges()) {
194
196
  const nodeDataProxy = Component.getDataProxy(prevElement)
195
197
  nodeDataProxy.applyDataUpdates(true)
@@ -205,7 +207,7 @@ export class ProcGenWrapper {
205
207
  }
206
208
  const elem = prevElement as Element | null
207
209
  if (elem !== null) {
208
- if (elem instanceof Component) {
210
+ if (isComponent(elem)) {
209
211
  if (elem.hasPendingChanges()) {
210
212
  const nodeDataProxy = Component.getDataProxy(elem)
211
213
  nodeDataProxy.applyDataUpdates(true)
@@ -312,6 +314,7 @@ export class ProcGenWrapper {
312
314
  key,
313
315
  list,
314
316
  elem,
317
+ shadowRoot,
315
318
  (item: DataValue, index: number | string): VirtualNode => {
316
319
  const childNode = shadowRoot.createVirtualNode('wx:for-item')
317
320
  childNode.destroyBackendElementOnDetach()
@@ -353,11 +356,15 @@ export class ProcGenWrapper {
353
356
  },
354
357
 
355
358
  // slot node
356
- (slotName: string | undefined, slotValueInit?: (elem: Element) => void) => {
359
+ (slotName: string | undefined, slotValueInit?: (elem: Element) => void, slot?: string) => {
357
360
  const elem = this.shadowRoot.createVirtualNode('slot')
358
361
  elem.destroyBackendElementOnDetach()
359
362
  Element.setSlotName(elem, dataValueToString(slotName))
360
- if (slotElement) Element.setSlotElement(elem, slotElement)
363
+ if (slotElement) {
364
+ Element.setSlotElement(elem, slotElement)
365
+ } else if (slot !== undefined) {
366
+ elem.slot = slot
367
+ }
361
368
  if (slotValueInit) slotValueInit(elem)
362
369
  childNodes.push(elem)
363
370
  },
@@ -476,7 +483,7 @@ export class ProcGenWrapper {
476
483
  }
477
484
  propertyInit(elem, false)
478
485
  let dynSlot = false
479
- if (elem instanceof Component) {
486
+ if (isComponent(elem)) {
480
487
  const sr = this.dynamicSlotUpdate(elem, dynamicSlotValueNames, children)
481
488
  if (sr) dynSlot = true
482
489
  if (elem.hasPendingChanges()) {
@@ -619,12 +626,15 @@ export class ProcGenWrapper {
619
626
  },
620
627
 
621
628
  // slot node
622
- (slotName: string | undefined, slotValueInit?: (elem: Element) => void) => {
629
+ (slotName: string | undefined, slotValueInit?: (elem: Element) => void, slot?: string) => {
623
630
  const elem = childNodes[index] as Element
624
631
  index += 1
625
632
  if (slotName !== undefined) {
626
633
  Element.setSlotName(elem, dataValueToString(slotName))
627
634
  }
635
+ if (!slotElement) {
636
+ if (slot !== undefined) elem.slot = slot
637
+ }
628
638
  if (slotValueInit) slotValueInit(elem)
629
639
  this.shadowRoot.applySlotValueUpdates(elem)
630
640
  },
@@ -782,17 +792,14 @@ export class ProcGenWrapper {
782
792
  children: DefineChildren,
783
793
  dynamicSlotValueNames: string[] | undefined,
784
794
  ): Element {
785
- const placeholding = this.shadowRoot.checkComponentPlaceholder(tagName)
786
- let elem: Element
787
795
  let dynSlot = false
788
796
  const initPropValues = (elem: GeneralComponent | NativeNode) => {
789
- const sr =
790
- elem instanceof Component
791
- ? this.dynamicSlotUpdate(elem, dynamicSlotValueNames, children)
792
- : null
797
+ const sr = isComponent(elem)
798
+ ? this.dynamicSlotUpdate(elem, dynamicSlotValueNames, children)
799
+ : null
793
800
  if (sr) dynSlot = true
794
801
  propertyInit(elem, true)
795
- if (elem instanceof Component) {
802
+ if (isComponent(elem)) {
796
803
  if (elem.hasPendingChanges()) {
797
804
  const nodeDataProxy = Component.getDataProxy(elem)
798
805
  nodeDataProxy.applyDataUpdates(true)
@@ -800,55 +807,41 @@ export class ProcGenWrapper {
800
807
  sr?.applySlotUpdates()
801
808
  }
802
809
  }
803
- if (this.disallowNativeNode || typeof placeholding === 'boolean') {
804
- let placeholderCb: (() => void) | undefined
805
- if (placeholding) {
806
- placeholderCb = () => {
807
- const replacer = this.shadowRoot.createComponent(
808
- tagName,
809
- tagName,
810
- genericImpls,
811
- undefined,
812
- initPropValues,
813
- )
814
- replacer.destroyBackendElementOnDetach()
815
- const replacerShadowRoot = (replacer as GeneralComponent).getShadowRoot()
816
- const elemShadowRoot = elem instanceof Component ? elem.getShadowRoot() : null
817
- const isElemDynamicSlots = elemShadowRoot?.getSlotMode() === SlotMode.Dynamic
818
- const isReplacerDynamicSlots = replacerShadowRoot?.getSlotMode() === SlotMode.Dynamic
819
- if (isReplacerDynamicSlots) {
820
- if (!isElemDynamicSlots) {
821
- throw new Error(
822
- 'The "dynamicSlots" option of the component and its placeholder should be the same.',
823
- )
824
- }
825
- elem.parentNode?.replaceChild(replacer, elem)
826
- } else {
827
- if (isElemDynamicSlots) {
828
- throw new Error(
829
- 'The "dynamicSlots" option of the component and its placeholder should be the same.',
830
- )
831
- }
832
- elem.selfReplaceWith(replacer)
833
- }
834
- }
835
- }
836
- elem = this.shadowRoot.createComponent(
810
+ const placeholderCallback = () => {
811
+ const replacer = this.shadowRoot.createComponent(
837
812
  tagName,
838
813
  tagName,
839
814
  genericImpls,
840
- placeholderCb,
841
- initPropValues,
842
- )
843
- elem.destroyBackendElementOnDetach()
844
- } else {
845
- elem = this.shadowRoot.createComponentOrNativeNode(
846
- placeholding ?? tagName,
847
- genericImpls,
815
+ undefined,
848
816
  initPropValues,
849
817
  )
850
- elem.destroyBackendElementOnDetach()
818
+ replacer.destroyBackendElementOnDetach()
819
+ const replacerShadowRoot = (replacer as GeneralComponent).getShadowRoot()
820
+ const elemShadowRoot = isComponent(elem) ? elem.getShadowRoot() : null
821
+ const isElemDynamicSlots = elemShadowRoot?.getSlotMode() === SlotMode.Dynamic
822
+ const isReplacerDynamicSlots = replacerShadowRoot?.getSlotMode() === SlotMode.Dynamic
823
+ if (isReplacerDynamicSlots !== isElemDynamicSlots) {
824
+ dispatchError(
825
+ new Error(
826
+ `The "dynamicSlots" option of component <${replacer.is}> and its placeholder <${elem.is}> should be the same.`,
827
+ ),
828
+ '[render]',
829
+ isComponent(replacer) ? replacer : replacer.is,
830
+ )
831
+ } else if (isReplacerDynamicSlots) {
832
+ elem.parentNode?.replaceChild(replacer, elem)
833
+ } else {
834
+ elem.selfReplaceWith(replacer)
835
+ }
851
836
  }
837
+ const elem = this.shadowRoot.createComponent(
838
+ tagName,
839
+ tagName,
840
+ genericImpls,
841
+ placeholderCallback,
842
+ initPropValues,
843
+ )
844
+ elem.destroyBackendElementOnDetach()
852
845
  if (dynSlot) {
853
846
  this.bindingMapDisabled = true // IDEA better binding map disable detection
854
847
  } else {
@@ -874,22 +867,26 @@ export class ProcGenWrapper {
874
867
  }
875
868
 
876
869
  // set class or external classes named `class`
877
- c(elem: Element, v: string) {
878
- if (elem instanceof Component) {
870
+ c(elem: Element, v: string | string[]) {
871
+ if (isComponent(elem)) {
879
872
  // "class" itself can also be an external class
880
- const hasExternalClass = (elem as GeneralComponent).hasExternalClass('class')
873
+ const hasExternalClass = elem.hasExternalClass('class')
881
874
  if (hasExternalClass) {
882
- ;(elem as GeneralComponent).setExternalClass('class', dataValueToString(v))
875
+ elem.setExternalClass('class', v)
883
876
  }
884
- elem.class = dataValueToString(v)
885
- } else {
886
- elem.class = dataValueToString(v)
887
877
  }
878
+ elem.setNodeClass(v)
888
879
  }
889
880
 
890
881
  // set style or property named `style`
891
882
  y(elem: Element, v: string) {
892
- elem.setNodeStyle(dataValueToString(v), StyleSegmentIndex.MAIN)
883
+ if (isComponent(elem) && Component.hasProperty(elem, 'style')) {
884
+ const nodeDataProxy = Component.getDataProxy(elem)
885
+ const camelName = dashToCamelCase('style')
886
+ nodeDataProxy.replaceProperty(camelName, v)
887
+ } else {
888
+ elem.setNodeStyle(dataValueToString(v), StyleSegmentIndex.MAIN)
889
+ }
893
890
  }
894
891
 
895
892
  // set dataset
@@ -911,6 +908,7 @@ export class ProcGenWrapper {
911
908
  mutated: boolean,
912
909
  capture: boolean,
913
910
  isDynamic: boolean,
911
+ _generalLvaluePath?: DataPath | null,
914
912
  ) {
915
913
  const handler = typeof v === 'function' ? this.eventListenerFilter(v) : dataValueToString(v)
916
914
  const listener = (ev: ShadowedEvent<unknown>) => {
@@ -927,6 +925,11 @@ export class ProcGenWrapper {
927
925
  }
928
926
  return ret
929
927
  }
928
+ if (ENV.DEV) {
929
+ Object.defineProperty(listener, 'name', {
930
+ value: typeof handler === 'string' ? handler : handler.name,
931
+ })
932
+ }
930
933
  const evOptions = {
931
934
  final,
932
935
  mutated,
@@ -945,18 +948,89 @@ export class ProcGenWrapper {
945
948
  }
946
949
 
947
950
  // update a property or external class of a component, or an attribute of a native node
948
- r(elem: Element, name: string, v: unknown, lvaluePath?: DataPath) {
949
- if (elem instanceof Component) {
951
+ r = (
952
+ elem: Element,
953
+ name: string,
954
+ v: unknown,
955
+ modelLvaluePath?: DataPath | null,
956
+ generalLvaluePath?: DataPath | null,
957
+ ) => {
958
+ const checkFallbackEventListener = (camelName: string) => {
959
+ if (camelName.startsWith('bind')) {
960
+ this.v(
961
+ elem,
962
+ camelName.slice('bind'.length),
963
+ dataValueToString(v),
964
+ false,
965
+ false,
966
+ false,
967
+ true,
968
+ generalLvaluePath,
969
+ )
970
+ } else if (camelName.startsWith('captureBind')) {
971
+ this.v(
972
+ elem,
973
+ camelName.slice('captureBind'.length),
974
+ dataValueToString(v),
975
+ false,
976
+ false,
977
+ true,
978
+ true,
979
+ generalLvaluePath,
980
+ )
981
+ } else if (camelName.startsWith('catch')) {
982
+ this.v(
983
+ elem,
984
+ camelName.slice('catch'.length),
985
+ dataValueToString(v),
986
+ true,
987
+ false,
988
+ false,
989
+ true,
990
+ generalLvaluePath,
991
+ )
992
+ } else if (camelName.startsWith('captureCatch')) {
993
+ this.v(
994
+ elem,
995
+ camelName.slice('captureCatch'.length),
996
+ dataValueToString(v),
997
+ true,
998
+ false,
999
+ true,
1000
+ true,
1001
+ generalLvaluePath,
1002
+ )
1003
+ } else if (camelName.startsWith('on')) {
1004
+ this.v(
1005
+ elem,
1006
+ camelName.slice('on'.length),
1007
+ dataValueToString(v),
1008
+ false,
1009
+ false,
1010
+ false,
1011
+ true,
1012
+ generalLvaluePath,
1013
+ )
1014
+ } else {
1015
+ return false
1016
+ }
1017
+ return true
1018
+ }
1019
+ if (isComponent(elem)) {
950
1020
  const nodeDataProxy = Component.getDataProxy(elem)
951
1021
  const camelName = dashToCamelCase(name)
952
1022
  if (nodeDataProxy.replaceProperty(camelName, v)) {
953
- if (lvaluePath) {
954
- nodeDataProxy.setModelBindingListener(camelName, (value) => {
955
- const host = elem.ownerShadowRoot!.getHostNode()
956
- const nodeDataProxy = Component.getDataProxy(host)
957
- nodeDataProxy.replaceDataOnPath(lvaluePath, value)
958
- nodeDataProxy.applyDataUpdates(false)
959
- })
1023
+ if (modelLvaluePath !== undefined) {
1024
+ if (modelLvaluePath === null) {
1025
+ nodeDataProxy.setModelBindingListener(camelName, () => {})
1026
+ } else {
1027
+ nodeDataProxy.setModelBindingListener(camelName, (value) => {
1028
+ const host = elem.ownerShadowRoot!.getHostNode()
1029
+ const nodeDataProxy = Component.getDataProxy(host)
1030
+ nodeDataProxy.replaceDataOnPath(modelLvaluePath, value)
1031
+ nodeDataProxy.applyDataUpdates(false)
1032
+ })
1033
+ }
960
1034
  }
961
1035
  const tmplArgs = getTmplArgs(elem)
962
1036
  if (tmplArgs.changeProp?.[name]) {
@@ -969,68 +1043,26 @@ export class ProcGenWrapper {
969
1043
  }
970
1044
  }
971
1045
  } else if (elem.hasExternalClass(name)) {
972
- elem.setExternalClass(name, dataValueToString(v))
1046
+ elem.setExternalClass(name, v as string)
973
1047
  } else {
974
1048
  // compatibilities for legacy event binding syntax
975
- if (camelName.startsWith('bind')) {
976
- this.v(
977
- elem,
978
- camelName.slice('bind'.length),
979
- dataValueToString(v),
980
- false,
981
- false,
982
- false,
983
- true,
984
- )
985
- } else if (camelName.startsWith('captureBind')) {
986
- this.v(
987
- elem,
988
- camelName.slice('captureBind'.length),
989
- dataValueToString(v),
990
- false,
991
- false,
992
- true,
993
- true,
994
- )
995
- } else if (camelName.startsWith('catch')) {
996
- this.v(
997
- elem,
998
- camelName.slice('catch'.length),
999
- dataValueToString(v),
1000
- true,
1001
- false,
1002
- false,
1003
- true,
1004
- )
1005
- } else if (camelName.startsWith('captureCatch')) {
1006
- this.v(
1007
- elem,
1008
- camelName.slice('captureCatch'.length),
1009
- dataValueToString(v),
1010
- true,
1011
- false,
1012
- true,
1013
- true,
1014
- )
1015
- } else if (camelName.startsWith('on')) {
1016
- this.v(
1017
- elem,
1018
- camelName.slice('on'.length),
1019
- dataValueToString(v),
1020
- false,
1021
- false,
1022
- false,
1023
- true,
1024
- )
1049
+ checkFallbackEventListener(camelName)
1050
+ }
1051
+ } else if (isNativeNode(elem)) {
1052
+ if (this.fallbackListenerOnNativeNode) {
1053
+ // compatibilities for legacy event binding syntax
1054
+ const camelName = dashToCamelCase(name)
1055
+ if (!checkFallbackEventListener(camelName)) {
1056
+ elem.updateAttribute(name, v)
1025
1057
  }
1058
+ } else {
1059
+ elem.updateAttribute(name, v)
1026
1060
  }
1027
- } else if (elem instanceof NativeNode) {
1028
- elem.updateAttribute(name, v)
1029
- if (lvaluePath) {
1061
+ if (modelLvaluePath) {
1030
1062
  elem.setModelBindingListener(name, (value) => {
1031
1063
  const host = elem.ownerShadowRoot!.getHostNode()
1032
1064
  const nodeDataProxy = Component.getDataProxy(host)
1033
- nodeDataProxy.replaceDataOnPath(lvaluePath, value)
1065
+ nodeDataProxy.replaceDataOnPath(modelLvaluePath, value)
1034
1066
  nodeDataProxy.applyDataUpdates(false)
1035
1067
  })
1036
1068
  }
@@ -1039,7 +1071,7 @@ export class ProcGenWrapper {
1039
1071
 
1040
1072
  // set a worklet directive value
1041
1073
  wl(elem: Element, name: string, value: unknown) {
1042
- if (elem instanceof Component) {
1074
+ if (isComponent(elem)) {
1043
1075
  elem.triggerWorkletChangeLifetime(name, value)
1044
1076
  } else {
1045
1077
  // TODO warn unused worklet
@@ -1047,8 +1079,8 @@ export class ProcGenWrapper {
1047
1079
  }
1048
1080
 
1049
1081
  // add a change property binding
1050
- p(elem: Element, name: string, v: ChangePropListener) {
1051
- if (elem instanceof Component) {
1082
+ p(elem: Element, name: string, v: ChangePropListener, _generalLvaluePath?: DataPath | null) {
1083
+ if (isComponent(elem)) {
1052
1084
  if (Component.hasProperty(elem, name)) {
1053
1085
  const tmplArgs = getTmplArgs(elem)
1054
1086
  if (!tmplArgs.changeProp) {
@@ -1068,3 +1100,9 @@ export class ProcGenWrapper {
1068
1100
  this.eventListenerFilter = eventListenerFilter
1069
1101
  }
1070
1102
  }
1103
+
1104
+ export const enum GeneralLvaluePathPrefix {
1105
+ Data = 0,
1106
+ Script = 1, // `abs_path` followed
1107
+ InlineScript = 2, // `abs_path` and `mod_name` followed
1108
+ }
@@ -1,17 +1,19 @@
1
1
  /* eslint-disable class-methods-use-this */
2
2
  /* global document */
3
3
 
4
- import { DataValue, Node, ShadowedEvent, GeneralBackendElement } from '..'
5
- import { DataPath } from '../data_path'
6
- import { RangeListManager } from './range_list_diff'
4
+ import { type GeneralBackendElement } from '../backend'
5
+ import { type DataPath } from '../data_path'
6
+ import { type DataValue } from '../data_proxy'
7
+ import { type ShadowedEvent } from '../event'
8
+ import { type GlassEaselTemplateDOMInstance } from './native_rendering'
7
9
  import {
8
- ProcGen,
9
- BindingMapGen,
10
- UpdatePathTreeRoot,
11
10
  dataValueToString,
12
- ProcGenWrapper,
11
+ type BindingMapGen,
12
+ type ProcGen,
13
+ type ProcGenWrapper,
14
+ type UpdatePathTreeRoot,
13
15
  } from './proc_gen_wrapper'
14
- import type { GlassEaselTemplateDOMInstance } from './native_rendering'
16
+ import { type RangeListManager } from './range_list_diff'
15
17
 
16
18
  type TmplArgs = {
17
19
  key?: number | string
@@ -1,7 +1,9 @@
1
- import { Element, VirtualNode } from '..'
2
- import { DataValue } from '../data_proxy'
3
- import { UpdatePathTreeNode, UpdatePathTreeRoot } from './proc_gen_wrapper'
4
- import { triggerWarning } from '../func_arr'
1
+ import { type DataValue } from '../data_proxy'
2
+ import { type Element } from '../element'
3
+ import { type ShadowRoot } from '../shadow_root'
4
+ import { type VirtualNode } from '../virtual_node'
5
+ import { triggerWarning } from '../warning'
6
+ import { type UpdatePathTreeNode, type UpdatePathTreeRoot } from './proc_gen_wrapper'
5
7
 
6
8
  export class RangeListManager {
7
9
  keyName: string | null
@@ -10,13 +12,18 @@ export class RangeListManager {
10
12
  sharedKeyMap!: { [key: string]: number[] } | undefined
11
13
  items!: DataValue[]
12
14
  indexes!: (string | number)[] | null
15
+ ownerShadowRoot: ShadowRoot
16
+ elem: Element
13
17
 
14
18
  constructor(
15
19
  keyName: string | null,
16
20
  dataList: DataValue,
17
21
  elem: Element,
22
+ shadowRoot: ShadowRoot,
18
23
  newListItem: (item: DataValue, index: number | string) => VirtualNode,
19
24
  ) {
25
+ this.ownerShadowRoot = shadowRoot
26
+ this.elem = elem
20
27
  this.keyName = keyName
21
28
  this.updateKeys(dataList)
22
29
  const items = this.items
@@ -50,6 +57,8 @@ export class RangeListManager {
50
57
  } else if (typeof dataList === 'string') {
51
58
  triggerWarning(
52
59
  'Use string as for-list is generally for testing. Each character is treated as an item.',
60
+ this.ownerShadowRoot.getHostNode(),
61
+ this.elem,
53
62
  )
54
63
  items = new Array<string>(dataList.length)
55
64
  indexes = null
@@ -59,6 +68,8 @@ export class RangeListManager {
59
68
  } else if (typeof dataList === 'number') {
60
69
  triggerWarning(
61
70
  'Use number as for-list is generally for testing. The number is used as the repeated times of the item.',
71
+ this.ownerShadowRoot.getHostNode(),
72
+ this.elem,
62
73
  )
63
74
  items = new Array<string>(dataList)
64
75
  indexes = null
@@ -66,7 +77,13 @@ export class RangeListManager {
66
77
  items[i] = i
67
78
  }
68
79
  } else {
69
- triggerWarning('The for-list data is invalid. Use empty array instead.')
80
+ triggerWarning(
81
+ `The for-list data is neither Array nor Object, got type "${
82
+ dataList === null ? 'null' : typeof dataList
83
+ }".`,
84
+ this.ownerShadowRoot.getHostNode(),
85
+ this.elem,
86
+ )
70
87
  items = []
71
88
  indexes = null
72
89
  }
@@ -100,7 +117,11 @@ export class RangeListManager {
100
117
  // convert shared keys to unique keys
101
118
  if (sharedKeyMap) {
102
119
  const keys = Object.keys(sharedKeyMap)
103
- triggerWarning(`Some keys are not unique while list updates: "${keys.join('", "')}".`)
120
+ triggerWarning(
121
+ `Some keys are not unique while list updates: "${keys.join('", "')}".`,
122
+ this.ownerShadowRoot.getHostNode(),
123
+ this.elem,
124
+ )
104
125
  for (let i = 0; i < keys.length; i += 1) {
105
126
  const key = keys[i]!
106
127
  const items = sharedKeyMap[key]!
@@ -327,12 +348,12 @@ export class RangeListManager {
327
348
  // decide what operation to be used with each item
328
349
  const enum OpKind {
329
350
  Stable = 0,
330
- ForwardMove,
331
- BackwardMove,
351
+ ForwardMove, // move towards the array end (index increasing direction)
352
+ BackwardMove, // move towards the array start (index decreasing direction)
332
353
  }
333
354
  const oldListOp = new Array(oldRawKeys.length) as (OpKind | undefined)[]
334
355
  const changedItems = new Array(newRawKeys.length) as (VirtualNode | undefined)[]
335
- let prevLcsOldPos = lcsArr[curLcsArrIndex]!
356
+ let prevLcsOldPos = -1
336
357
  for (let i = 0; i < oldPosList.length; i += 1) {
337
358
  const oldPos = oldPosList[i]!
338
359
  if (i === lcsArr[curLcsArrIndex]) {
@@ -1,4 +1,4 @@
1
- import { ComponentSpace } from '.'
1
+ import { type ComponentSpace } from './component_space'
2
2
 
3
3
  /**
4
4
  * Interface that can be implement dynamically
@@ -0,0 +1,36 @@
1
+ import { type GeneralComponent } from './component'
2
+ import { type Element } from './element'
3
+ import { type NativeNode } from './native_node'
4
+ import { type ShadowRoot } from './shadow_root'
5
+ import { type TextNode } from './text_node'
6
+ import { type VirtualNode } from './virtual_node'
7
+
8
+ export const TEXT_NODE_SYMBOL = Symbol('TextNode')
9
+
10
+ export const ELEMENT_SYMBOL = Symbol('Element')
11
+
12
+ export const NATIVE_NODE_SYMBOL = Symbol('NativeNode')
13
+
14
+ export const VIRTUAL_NODE_SYMBOL = Symbol('VirtualNode')
15
+
16
+ export const SHADOW_ROOT_SYMBOL = Symbol('ShadowRootSymbol')
17
+
18
+ export const COMPONENT_SYMBOL = Symbol('Component')
19
+
20
+ export const isTextNode = (e: any): e is TextNode =>
21
+ !!e && (e as { [TEXT_NODE_SYMBOL]: boolean })[TEXT_NODE_SYMBOL]
22
+
23
+ export const isElement = (e: any): e is Element =>
24
+ !!e && (e as { [ELEMENT_SYMBOL]: boolean })[ELEMENT_SYMBOL]
25
+
26
+ export const isNativeNode = (e: any): e is NativeNode =>
27
+ !!e && (e as { [NATIVE_NODE_SYMBOL]: boolean })[NATIVE_NODE_SYMBOL]
28
+
29
+ export const isVirtualNode = (e: any): e is VirtualNode =>
30
+ !!e && (e as { [VIRTUAL_NODE_SYMBOL]: boolean })[VIRTUAL_NODE_SYMBOL]
31
+
32
+ export const isShadowRoot = (e: any): e is ShadowRoot =>
33
+ !!e && (e as { [SHADOW_ROOT_SYMBOL]: boolean })[SHADOW_ROOT_SYMBOL]
34
+
35
+ export const isComponent = (e: any): e is GeneralComponent =>
36
+ !!e && (e as { [COMPONENT_SYMBOL]: boolean })[COMPONENT_SYMBOL]