@lvce-editor/virtual-dom 9.3.0 → 9.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,9 +1,341 @@
1
- const instances = Object.create(null);
2
- const get$1 = viewletId => {
3
- return instances[viewletId];
1
+ const uidSymbol = Symbol('uid');
2
+
3
+ const getUidTarget = $Element => {
4
+ while ($Element) {
5
+ if ($Element[uidSymbol]) {
6
+ return $Element;
7
+ }
8
+ $Element = $Element.parentNode;
9
+ }
10
+ return undefined;
4
11
  };
5
- const set$1 = (viewletId, instance) => {
6
- instances[viewletId] = instance;
12
+
13
+ const setComponentUid = ($Element, uid) => {
14
+ $Element[uidSymbol] = uid;
15
+ };
16
+ const getComponentUid = $Element => {
17
+ const $Target = getUidTarget($Element);
18
+ if (!$Target) {
19
+ return 0;
20
+ }
21
+ return $Target[uidSymbol];
22
+ };
23
+ const getComponentUidFromEvent = event => {
24
+ const {
25
+ currentTarget,
26
+ target
27
+ } = event;
28
+ return getComponentUid(currentTarget || target);
29
+ };
30
+
31
+ const dragInfos = Object.create(null);
32
+ const setDragInfo = (id, data) => {
33
+ dragInfos[id] = data;
34
+ };
35
+ const getDragInfo = id => {
36
+ return dragInfos[id];
37
+ };
38
+ const removeDragInfo = id => {
39
+ delete dragInfos[id];
40
+ };
41
+ const isDragInfoOld = data => {
42
+ return Array.isArray(data);
43
+ };
44
+
45
+ const setDragImage = (dataTransfer, label) => {
46
+ const dragImage = document.createElement('div');
47
+ dragImage.className = 'DragImage';
48
+ dragImage.textContent = label;
49
+ document.body.append(dragImage);
50
+ dataTransfer.setDragImage(dragImage, -10, -10);
51
+ const handleTimeOut = () => {
52
+ dragImage.remove();
53
+ };
54
+ setTimeout(handleTimeOut, 0);
55
+ };
56
+
57
+ const applyDragInfoMaybe = event => {
58
+ const {
59
+ dataTransfer,
60
+ target
61
+ } = event;
62
+ if (dataTransfer) {
63
+ const uid = getComponentUid(target);
64
+ const dragInfo = getDragInfo(uid);
65
+ if (!dragInfo) {
66
+ return;
67
+ }
68
+ if (isDragInfoOld(dragInfo)) {
69
+ for (const item of dragInfo) {
70
+ dataTransfer.setData(item.type, item.data);
71
+ }
72
+ } else {
73
+ for (const item of dragInfo.items) {
74
+ dataTransfer.items.add(item.data, item.type);
75
+ }
76
+ if (dragInfo.label) {
77
+ setDragImage(dataTransfer, dragInfo.label);
78
+ }
79
+ }
80
+ }
81
+ };
82
+
83
+ const PointerMove = 'pointermove';
84
+ const lostpointercapture = 'lostpointercapture';
85
+
86
+ let ignore = false;
87
+ const startIgnore = () => {
88
+ ignore = true;
89
+ };
90
+ const stopIgnore = () => {
91
+ ignore = false;
92
+ };
93
+ const enabled = () => {
94
+ return ignore;
95
+ };
96
+
97
+ let id = 0;
98
+ const create = () => {
99
+ return ++id;
100
+ };
101
+
102
+ const state$1 = Object.create(null);
103
+ const acquire = id => {
104
+ const promise = state$1[id];
105
+ delete state$1[id];
106
+ return promise;
107
+ };
108
+ const getFileHandles = async ids => {
109
+ const promises = ids.map(acquire);
110
+ const handles = await Promise.all(promises);
111
+ return handles;
112
+ };
113
+ const add$1 = promise => {
114
+ const id = create();
115
+ state$1[id] = promise;
116
+ return id;
117
+ };
118
+ const addFileHandle = fileHandle => {
119
+ const promise = Promise.resolve(fileHandle);
120
+ return add$1(promise);
121
+ };
122
+
123
+ const unwrapItemString = async item => {
124
+ const {
125
+ resolve,
126
+ promise
127
+ } = Promise.withResolvers();
128
+ item.getAsString(resolve);
129
+ const value = await promise;
130
+ return {
131
+ kind: 'string',
132
+ type: item.type,
133
+ value
134
+ };
135
+ };
136
+ const unwrapItemFile = async item => {
137
+ // @ts-ignore
138
+ const file = await item.getAsFileSystemHandle();
139
+ return {
140
+ kind: 'file',
141
+ type: item.type,
142
+ value: file
143
+ };
144
+ };
145
+ const unknownItem = {
146
+ kind: 'unknown',
147
+ type: '',
148
+ value: ''
149
+ };
150
+ const unwrapItem = item => {
151
+ switch (item.kind) {
152
+ case 'file':
153
+ return unwrapItemFile(item);
154
+ case 'string':
155
+ return unwrapItemString(item);
156
+ default:
157
+ return unknownItem;
158
+ }
159
+ };
160
+ const handleDataTransferFiles = event => {
161
+ if (!event.dataTransfer) {
162
+ return [];
163
+ }
164
+ const items = [...event.dataTransfer.items];
165
+ const promises = items.map(unwrapItem);
166
+ const ids = promises.map(promise => add$1(promise));
167
+ return ids;
168
+ };
169
+ const getEventListenerArg = (param, event) => {
170
+ switch (param) {
171
+ case 'event.altKey':
172
+ return event.altKey;
173
+ case 'event.button':
174
+ return event.button;
175
+ case 'event.clientX':
176
+ return event.clientX;
177
+ case 'event.clientY':
178
+ return event.clientY;
179
+ case 'event.ctrlKey':
180
+ return event.ctrlKey;
181
+ case 'event.data':
182
+ return event.data;
183
+ case 'event.dataTransfer.files':
184
+ return event.dataTransfer.files;
185
+ case 'event.dataTransfer.files2':
186
+ return handleDataTransferFiles(event);
187
+ case 'event.defaultPrevented':
188
+ return event.defaultPrevented;
189
+ case 'event.deltaMode':
190
+ return event.deltaMode;
191
+ case 'event.deltaX':
192
+ return event.deltaX;
193
+ case 'event.deltaY':
194
+ return event.deltaY;
195
+ case 'event.detail':
196
+ return event.detail;
197
+ case 'event.inputType':
198
+ return event.inputType;
199
+ case 'event.isTrusted':
200
+ return event.isTrusted;
201
+ case 'event.key':
202
+ return event.key;
203
+ case 'event.shiftKey':
204
+ return event.shiftKey;
205
+ case 'event.target.checked':
206
+ return event.target.checked;
207
+ case 'event.target.className':
208
+ return event.target.className;
209
+ case 'event.target.href':
210
+ return event.target.href;
211
+ case 'event.target.name':
212
+ return event.target.name || '';
213
+ case 'event.target.nodeName':
214
+ return event.target.nodeName;
215
+ case 'event.target.scrollTop':
216
+ return event.target.scrollTop;
217
+ case 'event.target.selectionEnd':
218
+ return event.target.selectionEnd;
219
+ case 'event.target.selectionStart':
220
+ return event.target.selectionStart;
221
+ case 'event.target.src':
222
+ return event.target.src;
223
+ case 'event.target.value':
224
+ return event.target.value;
225
+ case 'event.x':
226
+ return event.x;
227
+ case 'event.y':
228
+ return event.y;
229
+ default:
230
+ if (typeof param === 'string' && param.startsWith('event.target.dataset')) {
231
+ const rest = param.slice('event.target.dataset.'.length);
232
+ return event.target.dataset[rest];
233
+ }
234
+ return param;
235
+ }
236
+ };
237
+
238
+ const getEventListenerArgs = (params, event) => {
239
+ const serialized = [];
240
+ for (const param of params) {
241
+ serialized.push(getEventListenerArg(param, event));
242
+ }
243
+ return serialized;
244
+ };
245
+
246
+ const state = {
247
+ ipc: undefined
248
+ };
249
+ const getIpc = () => {
250
+ return state.ipc;
251
+ };
252
+ const setIpc = value => {
253
+ state.ipc = value;
254
+ };
255
+
256
+ const nameAnonymousFunction = (fn, name) => {
257
+ Object.defineProperty(fn, 'name', {
258
+ value: name
259
+ });
260
+ };
261
+
262
+ const isInputElement = element => {
263
+ return element instanceof HTMLInputElement;
264
+ };
265
+
266
+ const preventEventsMaybe = (info, event) => {
267
+ if (info.preventDefault === 2) {
268
+ if (!isInputElement(event.target)) {
269
+ event.preventDefault();
270
+ }
271
+ } else if (info.preventDefault) {
272
+ event.preventDefault();
273
+ }
274
+ if (info.stopPropagation) {
275
+ event.stopPropagation();
276
+ }
277
+ };
278
+
279
+ const applyPointerTrackMaybe = (info, map, event) => {
280
+ const {
281
+ trackPointerEvents
282
+ } = info;
283
+ if (!trackPointerEvents) {
284
+ return;
285
+ }
286
+ const {
287
+ pointerId,
288
+ target
289
+ } = event;
290
+ target.setPointerCapture(pointerId);
291
+ const [pointerMoveKey, pointerUpKey] = trackPointerEvents;
292
+ target.addEventListener(PointerMove, map[pointerMoveKey]);
293
+ target.addEventListener(lostpointercapture, map[pointerUpKey]);
294
+ };
295
+ const createFn = (info, map) => {
296
+ const fn = event => {
297
+ if (enabled()) {
298
+ return;
299
+ }
300
+ const uid = getComponentUidFromEvent(event);
301
+ const args = getEventListenerArgs(info.params, event);
302
+ preventEventsMaybe(info, event);
303
+ applyDragInfoMaybe(event);
304
+ applyPointerTrackMaybe(info, map, event);
305
+ if (args.length === 0) {
306
+ return;
307
+ }
308
+ const ipc = getIpc();
309
+ ipc.send('Viewlet.executeViewletCommand', uid, ...args);
310
+ };
311
+ nameAnonymousFunction(fn, info.name);
312
+ if (info.passive) {
313
+ // TODO avoid mutating function property, maybe return an object with function and options
314
+ fn.passive = true;
315
+ }
316
+ if (info.capture) {
317
+ // TODO avoid mutating function property, maybe return an object with function and options
318
+ fn.capture = true;
319
+ }
320
+ return fn;
321
+ };
322
+
323
+ const listeners = Object.create(null);
324
+ const registerEventListeners = (id, eventListeners) => {
325
+ const map = Object.create(null);
326
+ for (const info of eventListeners) {
327
+ const fn = createFn(info, map);
328
+ map[info.name] = fn;
329
+ }
330
+ listeners[id] = map;
331
+ };
332
+ const getEventListenerMap = id => {
333
+ const map = listeners[id];
334
+ return map;
335
+ };
336
+
337
+ const clearNode = $Node => {
338
+ $Node.textContent = '';
7
339
  };
8
340
 
9
341
  const Audio$2 = 'audio';
@@ -364,67 +696,35 @@ const ElementTagMap = {
364
696
 
365
697
  const {
366
698
  getElementTag
367
- } = ElementTagMap;
368
-
369
- const getEventListenerOptions = (eventName, value) => {
370
- if (value.passive) {
371
- return {
372
- passive: true
373
- };
374
- }
375
- if (value.capture) {
376
- return {
377
- capture: true
378
- };
379
- }
380
- switch (eventName) {
381
- case 'wheel':
382
- return {
383
- passive: true
384
- };
385
- default:
386
- return undefined;
387
- }
388
- };
389
-
390
- const uidSymbol = Symbol('uid');
391
-
392
- const getUidTarget = $Element => {
393
- while ($Element) {
394
- if ($Element[uidSymbol]) {
395
- return $Element;
396
- }
397
- $Element = $Element.parentNode;
398
- }
399
- return undefined;
400
- };
401
-
402
- const setComponentUid = ($Element, uid) => {
403
- $Element[uidSymbol] = uid;
404
- };
405
- const getComponentUid = $Element => {
406
- const $Target = getUidTarget($Element);
407
- if (!$Target) {
408
- return 0;
409
- }
410
- return $Target[uidSymbol];
411
- };
412
- const getComponentUidFromEvent = event => {
413
- const {
414
- currentTarget,
415
- target
416
- } = event;
417
- return getComponentUid(currentTarget || target);
418
- };
699
+ } = ElementTagMap;
419
700
 
420
- const state$1 = {
421
- ipc: undefined
701
+ const instances = Object.create(null);
702
+ const get$1 = viewletId => {
703
+ return instances[viewletId];
422
704
  };
423
- const getIpc = () => {
424
- return state$1.ipc;
705
+ const set$1 = (viewletId, instance) => {
706
+ instances[viewletId] = instance;
425
707
  };
426
- const setIpc = value => {
427
- state$1.ipc = value;
708
+
709
+ const getEventListenerOptions = (eventName, value) => {
710
+ if (value.passive) {
711
+ return {
712
+ passive: true
713
+ };
714
+ }
715
+ if (value.capture) {
716
+ return {
717
+ capture: true
718
+ };
719
+ }
720
+ switch (eventName) {
721
+ case 'wheel':
722
+ return {
723
+ passive: true
724
+ };
725
+ default:
726
+ return undefined;
727
+ }
428
728
  };
429
729
 
430
730
  const cache = new Map();
@@ -438,12 +738,6 @@ const get = listener => {
438
738
  return cache.get(listener);
439
739
  };
440
740
 
441
- const nameAnonymousFunction = (fn, name) => {
442
- Object.defineProperty(fn, 'name', {
443
- value: name
444
- });
445
- };
446
-
447
741
  const getWrappedListener = (listener, returnValue) => {
448
742
  if (!returnValue) {
449
743
  return listener;
@@ -777,392 +1071,131 @@ const renderDomElement = (element, eventMap, newEventMap) => {
777
1071
  setProps($Element, element, eventMap, newEventMap);
778
1072
  return $Element;
779
1073
  };
780
- const renderReferenceNode = element => {
781
- const instance = get$1(element.uid);
782
- if (!instance || !instance.state) {
783
- return document.createTextNode('Reference node not found');
784
- }
785
- const $Node = instance.state.$Viewlet;
786
- return $Node;
787
- };
788
- const render$1 = (element, eventMap, newEventMap) => {
789
- switch (element.type) {
790
- case Reference:
791
- return renderReferenceNode(element);
792
- case Text:
793
- return renderDomTextNode(element);
794
- default:
795
- return renderDomElement(element, eventMap, newEventMap);
796
- }
797
- };
798
-
799
- const renderInternal = ($Parent, elements, eventMap, newEventMap) => {
800
- const max = elements.length - 1;
801
- let stack = [];
802
- for (let i = max; i >= 0; i--) {
803
- const element = elements[i];
804
- const $Element = render$1(element, eventMap, newEventMap);
805
- if (element.childCount > 0) {
806
- // @ts-expect-error
807
- $Element.append(...stack.slice(0, element.childCount));
808
- stack = stack.slice(element.childCount);
809
- }
810
- stack.unshift($Element);
811
- }
812
- $Parent.append(...stack);
813
- };
814
-
815
- // Map of property names to attribute names for cases where they differ
816
- const propertyToAttribute = {
817
- className: 'class',
818
- htmlFor: 'for',
819
- ariaActivedescendant: 'aria-activedescendant',
820
- ariaControls: 'aria-controls',
821
- ariaLabelledBy: 'aria-labelledby',
822
- ariaOwns: 'aria-owns',
823
- inputType: 'type'
824
- };
825
-
826
- // Style properties that need to be set on element.style
827
- const styleProperties = new Set(['width', 'height', 'top', 'left', 'marginTop', 'paddingLeft', 'paddingRight']);
828
- const removeAttribute = ($Element, key) => {
829
- // Handle style properties
830
- if (styleProperties.has(key)) {
831
- // @ts-ignore - dynamic style property access
832
- $Element.style[key] = '';
833
- return;
834
- }
835
- const attributeName = propertyToAttribute[key] || key;
836
- $Element.removeAttribute(attributeName);
837
- };
838
- const setText = ($Element, value) => {
839
- $Element.nodeValue = value;
840
- };
841
- const removeChild = ($Element, index) => {
842
- const $Child = $Element.childNodes[index];
843
- if ($Child) {
844
- $Child.remove();
845
- }
846
- };
847
- const add$1 = ($Element, nodes, eventMap = {}) => {
848
- renderInternal($Element, nodes, eventMap, eventMap);
849
- };
850
- const replace = ($Element, nodes, eventMap = {}) => {
851
- // Create a temporary container to render the new nodes
852
- const $Temp = document.createElement('div');
853
- renderInternal($Temp, nodes, eventMap, eventMap);
854
- // Replace the current element with the new node(s)
855
- const $NewNode = $Temp.firstChild;
856
- if (!$NewNode) {
857
- // No node was created, just remove the old element
858
- $Element.remove();
859
- return $Element;
860
- }
861
- $Element.replaceWith($NewNode);
862
- return $NewNode;
863
- };
864
-
865
- const SetText = 1;
866
- const Replace = 2;
867
- const SetAttribute = 3;
868
- const RemoveAttribute = 4;
869
- const Add = 6;
870
- const NavigateChild = 7;
871
- const NavigateParent = 8;
872
- const RemoveChild = 9;
873
- const NavigateSibling = 10;
874
- const SetReferenceNodeUid = 11;
875
-
876
- const dragInfos = Object.create(null);
877
- const setDragInfo = (id, data) => {
878
- dragInfos[id] = data;
879
- };
880
- const getDragInfo = id => {
881
- return dragInfos[id];
882
- };
883
- const removeDragInfo = id => {
884
- delete dragInfos[id];
885
- };
886
- const isDragInfoOld = data => {
887
- return Array.isArray(data);
888
- };
889
-
890
- const setDragImage = (dataTransfer, label) => {
891
- const dragImage = document.createElement('div');
892
- dragImage.className = 'DragImage';
893
- dragImage.textContent = label;
894
- document.body.append(dragImage);
895
- dataTransfer.setDragImage(dragImage, -10, -10);
896
- const handleTimeOut = () => {
897
- dragImage.remove();
898
- };
899
- setTimeout(handleTimeOut, 0);
900
- };
901
-
902
- const applyDragInfoMaybe = event => {
903
- const {
904
- dataTransfer,
905
- target
906
- } = event;
907
- if (dataTransfer) {
908
- const uid = getComponentUid(target);
909
- const dragInfo = getDragInfo(uid);
910
- if (!dragInfo) {
911
- return;
912
- }
913
- if (isDragInfoOld(dragInfo)) {
914
- for (const item of dragInfo) {
915
- dataTransfer.setData(item.type, item.data);
916
- }
917
- } else {
918
- for (const item of dragInfo.items) {
919
- dataTransfer.items.add(item.data, item.type);
920
- }
921
- if (dragInfo.label) {
922
- setDragImage(dataTransfer, dragInfo.label);
923
- }
924
- }
925
- }
926
- };
927
-
928
- const PointerMove = 'pointermove';
929
- const lostpointercapture = 'lostpointercapture';
930
-
931
- let ignore = false;
932
- const startIgnore = () => {
933
- ignore = true;
934
- };
935
- const stopIgnore = () => {
936
- ignore = false;
937
- };
938
- const enabled = () => {
939
- return ignore;
940
- };
941
-
942
- let id = 0;
943
- const create = () => {
944
- return ++id;
945
- };
946
-
947
- const state = Object.create(null);
948
- const acquire = id => {
949
- const promise = state[id];
950
- delete state[id];
951
- return promise;
952
- };
953
- const getFileHandles = async ids => {
954
- const promises = ids.map(acquire);
955
- const handles = await Promise.all(promises);
956
- return handles;
957
- };
958
- const add = promise => {
959
- const id = create();
960
- state[id] = promise;
961
- return id;
962
- };
963
- const addFileHandle = fileHandle => {
964
- const promise = Promise.resolve(fileHandle);
965
- return add(promise);
966
- };
967
-
968
- const unwrapItemString = async item => {
969
- const {
970
- resolve,
971
- promise
972
- } = Promise.withResolvers();
973
- item.getAsString(resolve);
974
- const value = await promise;
975
- return {
976
- kind: 'string',
977
- type: item.type,
978
- value
979
- };
980
- };
981
- const unwrapItemFile = async item => {
982
- // @ts-ignore
983
- const file = await item.getAsFileSystemHandle();
984
- return {
985
- kind: 'file',
986
- type: item.type,
987
- value: file
988
- };
989
- };
990
- const unknownItem = {
991
- kind: 'unknown',
992
- type: '',
993
- value: ''
994
- };
995
- const unwrapItem = item => {
996
- switch (item.kind) {
997
- case 'file':
998
- return unwrapItemFile(item);
999
- case 'string':
1000
- return unwrapItemString(item);
1001
- default:
1002
- return unknownItem;
1003
- }
1004
- };
1005
- const handleDataTransferFiles = event => {
1006
- if (!event.dataTransfer) {
1007
- return [];
1074
+ const renderReferenceNode = element => {
1075
+ const instance = get$1(element.uid);
1076
+ if (!instance || !instance.state) {
1077
+ return document.createTextNode('Reference node not found');
1008
1078
  }
1009
- const items = [...event.dataTransfer.items];
1010
- const promises = items.map(unwrapItem);
1011
- const ids = promises.map(promise => add(promise));
1012
- return ids;
1079
+ const $Node = instance.state.$Viewlet;
1080
+ return $Node;
1013
1081
  };
1014
- const getEventListenerArg = (param, event) => {
1015
- switch (param) {
1016
- case 'event.altKey':
1017
- return event.altKey;
1018
- case 'event.button':
1019
- return event.button;
1020
- case 'event.clientX':
1021
- return event.clientX;
1022
- case 'event.clientY':
1023
- return event.clientY;
1024
- case 'event.ctrlKey':
1025
- return event.ctrlKey;
1026
- case 'event.data':
1027
- return event.data;
1028
- case 'event.dataTransfer.files':
1029
- return event.dataTransfer.files;
1030
- case 'event.dataTransfer.files2':
1031
- return handleDataTransferFiles(event);
1032
- case 'event.defaultPrevented':
1033
- return event.defaultPrevented;
1034
- case 'event.deltaMode':
1035
- return event.deltaMode;
1036
- case 'event.deltaX':
1037
- return event.deltaX;
1038
- case 'event.deltaY':
1039
- return event.deltaY;
1040
- case 'event.detail':
1041
- return event.detail;
1042
- case 'event.inputType':
1043
- return event.inputType;
1044
- case 'event.isTrusted':
1045
- return event.isTrusted;
1046
- case 'event.key':
1047
- return event.key;
1048
- case 'event.shiftKey':
1049
- return event.shiftKey;
1050
- case 'event.target.checked':
1051
- return event.target.checked;
1052
- case 'event.target.className':
1053
- return event.target.className;
1054
- case 'event.target.href':
1055
- return event.target.href;
1056
- case 'event.target.name':
1057
- return event.target.name || '';
1058
- case 'event.target.nodeName':
1059
- return event.target.nodeName;
1060
- case 'event.target.scrollTop':
1061
- return event.target.scrollTop;
1062
- case 'event.target.selectionEnd':
1063
- return event.target.selectionEnd;
1064
- case 'event.target.selectionStart':
1065
- return event.target.selectionStart;
1066
- case 'event.target.src':
1067
- return event.target.src;
1068
- case 'event.target.value':
1069
- return event.target.value;
1070
- case 'event.x':
1071
- return event.x;
1072
- case 'event.y':
1073
- return event.y;
1082
+ const render$1 = (element, eventMap, newEventMap) => {
1083
+ switch (element.type) {
1084
+ case Reference:
1085
+ return renderReferenceNode(element);
1086
+ case Text:
1087
+ return renderDomTextNode(element);
1074
1088
  default:
1075
- if (typeof param === 'string' && param.startsWith('event.target.dataset')) {
1076
- const rest = param.slice('event.target.dataset.'.length);
1077
- return event.target.dataset[rest];
1078
- }
1079
- return param;
1089
+ return renderDomElement(element, eventMap, newEventMap);
1080
1090
  }
1081
1091
  };
1082
1092
 
1083
- const getEventListenerArgs = (params, event) => {
1084
- const serialized = [];
1085
- for (const param of params) {
1086
- serialized.push(getEventListenerArg(param, event));
1093
+ const renderInternal = ($Parent, elements, eventMap, newEventMap) => {
1094
+ const max = elements.length - 1;
1095
+ let stack = [];
1096
+ for (let i = max; i >= 0; i--) {
1097
+ const element = elements[i];
1098
+ const $Element = render$1(element, eventMap, newEventMap);
1099
+ if (element.childCount > 0) {
1100
+ // @ts-expect-error
1101
+ $Element.append(...stack.slice(0, element.childCount));
1102
+ stack = stack.slice(element.childCount);
1103
+ }
1104
+ stack.unshift($Element);
1087
1105
  }
1088
- return serialized;
1106
+ $Parent.append(...stack);
1089
1107
  };
1090
1108
 
1091
- const isInputElement = element => {
1092
- return element instanceof HTMLInputElement;
1109
+ const renderInto = ($Parent, dom, eventMap = {}) => {
1110
+ clearNode($Parent);
1111
+ renderInternal($Parent, dom, eventMap);
1093
1112
  };
1094
1113
 
1095
- const preventEventsMaybe = (info, event) => {
1096
- if (info.preventDefault === 2) {
1097
- if (!isInputElement(event.target)) {
1098
- event.preventDefault();
1099
- }
1100
- } else if (info.preventDefault) {
1101
- event.preventDefault();
1102
- }
1103
- if (info.stopPropagation) {
1104
- event.stopPropagation();
1114
+ /**
1115
+ *
1116
+ * @param {any[]} elements
1117
+ * @returns
1118
+ */
1119
+ const render = (elements, eventMap = {}, newEventMap = {}) => {
1120
+ const $Root = document.createElement('div');
1121
+ renderInternal($Root, elements, eventMap, newEventMap);
1122
+ return $Root;
1123
+ };
1124
+
1125
+ const rememberFocus2 = ($Viewlet, dom, eventMap, uid = 0) => {
1126
+ if (uid) {
1127
+ const newEventMap = getEventListenerMap(uid);
1128
+ const $New = render(dom, eventMap, newEventMap).firstChild;
1129
+ setComponentUid($New, uid);
1130
+ $Viewlet.replaceWith($New);
1131
+ $Viewlet = $New;
1132
+ } else {
1133
+ renderInto($Viewlet, dom, eventMap);
1105
1134
  }
1135
+ return $Viewlet;
1106
1136
  };
1107
1137
 
1108
- const applyPointerTrackMaybe = (info, map, event) => {
1109
- const {
1110
- trackPointerEvents
1111
- } = info;
1112
- if (!trackPointerEvents) {
1138
+ // Map of property names to attribute names for cases where they differ
1139
+ const propertyToAttribute = {
1140
+ className: 'class',
1141
+ htmlFor: 'for',
1142
+ ariaActivedescendant: 'aria-activedescendant',
1143
+ ariaControls: 'aria-controls',
1144
+ ariaLabelledBy: 'aria-labelledby',
1145
+ ariaOwns: 'aria-owns',
1146
+ inputType: 'type'
1147
+ };
1148
+
1149
+ // Style properties that need to be set on element.style
1150
+ const styleProperties = new Set(['width', 'height', 'top', 'left', 'marginTop', 'paddingLeft', 'paddingRight']);
1151
+ const removeAttribute = ($Element, key) => {
1152
+ // Handle style properties
1153
+ if (styleProperties.has(key)) {
1154
+ // @ts-ignore - dynamic style property access
1155
+ $Element.style[key] = '';
1113
1156
  return;
1114
1157
  }
1115
- const {
1116
- pointerId,
1117
- target
1118
- } = event;
1119
- target.setPointerCapture(pointerId);
1120
- const [pointerMoveKey, pointerUpKey] = trackPointerEvents;
1121
- target.addEventListener(PointerMove, map[pointerMoveKey]);
1122
- target.addEventListener(lostpointercapture, map[pointerUpKey]);
1158
+ const attributeName = propertyToAttribute[key] || key;
1159
+ $Element.removeAttribute(attributeName);
1123
1160
  };
1124
- const createFn = (info, map) => {
1125
- const fn = event => {
1126
- if (enabled()) {
1127
- return;
1128
- }
1129
- const uid = getComponentUidFromEvent(event);
1130
- const args = getEventListenerArgs(info.params, event);
1131
- preventEventsMaybe(info, event);
1132
- applyDragInfoMaybe(event);
1133
- applyPointerTrackMaybe(info, map, event);
1134
- if (args.length === 0) {
1135
- return;
1136
- }
1137
- const ipc = getIpc();
1138
- ipc.send('Viewlet.executeViewletCommand', uid, ...args);
1139
- };
1140
- nameAnonymousFunction(fn, info.name);
1141
- if (info.passive) {
1142
- // TODO avoid mutating function property, maybe return an object with function and options
1143
- fn.passive = true;
1144
- }
1145
- if (info.capture) {
1146
- // TODO avoid mutating function property, maybe return an object with function and options
1147
- fn.capture = true;
1148
- }
1149
- return fn;
1161
+ const setText = ($Element, value) => {
1162
+ $Element.nodeValue = value;
1150
1163
  };
1151
-
1152
- const listeners = Object.create(null);
1153
- const registerEventListeners = (id, eventListeners) => {
1154
- const map = Object.create(null);
1155
- for (const info of eventListeners) {
1156
- const fn = createFn(info, map);
1157
- map[info.name] = fn;
1164
+ const removeChild = ($Element, index) => {
1165
+ const $Child = $Element.childNodes[index];
1166
+ if ($Child) {
1167
+ $Child.remove();
1158
1168
  }
1159
- listeners[id] = map;
1160
1169
  };
1161
- const getEventListenerMap = id => {
1162
- const map = listeners[id];
1163
- return map;
1170
+ const add = ($Element, nodes, eventMap = {}) => {
1171
+ renderInternal($Element, nodes, eventMap, eventMap);
1172
+ };
1173
+ const replace = ($Element, nodes, eventMap = {}) => {
1174
+ // Create a temporary container to render the new nodes
1175
+ const $Temp = document.createElement('div');
1176
+ renderInternal($Temp, nodes, eventMap, eventMap);
1177
+ // Replace the current element with the new node(s)
1178
+ const $NewNode = $Temp.firstChild;
1179
+ if (!$NewNode) {
1180
+ // No node was created, just remove the old element
1181
+ $Element.remove();
1182
+ return $Element;
1183
+ }
1184
+ $Element.replaceWith($NewNode);
1185
+ return $NewNode;
1164
1186
  };
1165
1187
 
1188
+ const SetText = 1;
1189
+ const Replace = 2;
1190
+ const SetAttribute = 3;
1191
+ const RemoveAttribute = 4;
1192
+ const Add = 6;
1193
+ const NavigateChild = 7;
1194
+ const NavigateParent = 8;
1195
+ const RemoveChild = 9;
1196
+ const NavigateSibling = 10;
1197
+ const SetReferenceNodeUid = 11;
1198
+
1166
1199
  const applyPatch = ($Element, patches, eventMap = {}, id = 0) => {
1167
1200
  const events = getEventListenerMap(id) || eventMap;
1168
1201
  let $Current = $Element;
@@ -1171,7 +1204,7 @@ const applyPatch = ($Element, patches, eventMap = {}, id = 0) => {
1171
1204
  try {
1172
1205
  switch (patch.type) {
1173
1206
  case Add:
1174
- add$1($Current, patch.nodes, events);
1207
+ add($Current, patch.nodes, events);
1175
1208
  break;
1176
1209
  case NavigateChild:
1177
1210
  {
@@ -1287,26 +1320,6 @@ const queryInputs = $Viewlet => {
1287
1320
  return [...$Viewlet.querySelectorAll('input, textarea, select')];
1288
1321
  };
1289
1322
 
1290
- const clearNode = $Node => {
1291
- $Node.textContent = '';
1292
- };
1293
-
1294
- const renderInto = ($Parent, dom, eventMap = {}) => {
1295
- clearNode($Parent);
1296
- renderInternal($Parent, dom, eventMap);
1297
- };
1298
-
1299
- /**
1300
- *
1301
- * @param {any[]} elements
1302
- * @returns
1303
- */
1304
- const render = (elements, eventMap = {}, newEventMap = {}) => {
1305
- const $Root = document.createElement('div');
1306
- renderInternal($Root, elements, eventMap, newEventMap);
1307
- return $Root;
1308
- };
1309
-
1310
1323
  const focusElement = $Element => {
1311
1324
  $Element.focus({
1312
1325
  preventScroll: true
@@ -1385,4 +1398,4 @@ const rememberFocus = ($Viewlet, dom, eventMap, uid = 0) => {
1385
1398
  return $Viewlet;
1386
1399
  };
1387
1400
 
1388
- export { VirtualDomElements, addFileHandle, applyPatch, getComponentUid, getComponentUidFromEvent, getDragInfo, getFileHandles, get$1 as getViewletInstance, registerEventListeners, rememberFocus, removeDragInfo, render, renderInto, setComponentUid, setDragInfo, setIpc, set$1 as setViewletInstance };
1401
+ export { VirtualDomElements, addFileHandle, applyPatch, getComponentUid, getComponentUidFromEvent, getDragInfo, getFileHandles, get$1 as getViewletInstance, registerEventListeners, rememberFocus, rememberFocus2, removeDragInfo, render, renderInto, setComponentUid, setDragInfo, setIpc, set$1 as setViewletInstance };
@@ -1,3 +1,4 @@
1
+ export { rememberFocus2 } from '../RememberFocus2/RememberFocus2.ts';
1
2
  export { applyPatch } from '../ApplyPatch/ApplyPatch.ts';
2
3
  export { getComponentUid, getComponentUidFromEvent, setComponentUid, } from '../ComponentUid/ComponentUid.ts';
3
4
  export { getDragInfo, removeDragInfo, setDragInfo, } from '../DragInfo/DragInfo.ts';
@@ -0,0 +1 @@
1
+ export declare const rememberFocus2: ($Viewlet: HTMLElement, dom: any[], eventMap: any, uid?: number) => any;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/virtual-dom",
3
- "version": "9.3.0",
3
+ "version": "9.4.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/lvce-editor/virtual-dom.git"