onejs-core 0.3.42 → 0.3.44

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.
@@ -230,6 +230,7 @@ declare namespace CS {
230
230
  public removeChild ($child: OneJS.Dom.Dom) : void
231
231
  public contains ($child: OneJS.Dom.Dom) : boolean
232
232
  public insertBefore ($a: OneJS.Dom.Dom, $b: OneJS.Dom.Dom) : void
233
+ public insertAfter ($a: OneJS.Dom.Dom, $b: OneJS.Dom.Dom) : void
233
234
  public setAttribute ($name: string, $val: any) : void
234
235
  public removeAttribute ($name: string) : void
235
236
  public focus () : void
@@ -24,9 +24,9 @@ declare const onejs: {
24
24
  eventSource: T,
25
25
  eventName: K,
26
26
  handler: OneJS.EventGenericType<T[K]>
27
- ): () => void
27
+ ): CS.System.Action
28
28
 
29
- subscribe(eventName: string, handler: () => void): () => void
29
+ subscribe(eventName: string, handler: () => void): CS.System.Action
30
30
  }
31
31
 
32
32
  declare function require(name: string): any
@@ -78,6 +78,7 @@ declare global {
78
78
 
79
79
  interface IntrinsicAttributes {
80
80
  id?: string
81
+ key?: string | number
81
82
  }
82
83
 
83
84
  // interface VNode<P = {}> {
package/dist/dom/dom.d.ts CHANGED
@@ -27,10 +27,12 @@ export declare class DomWrapper {
27
27
  dom: CS.OneJS.Dom.Dom;
28
28
  domStyleWrapper: DomStyleWrapper;
29
29
  cachedChildNodes: DomWrapper[] | null;
30
+ boundListeners: WeakMap<WeakKey, any>;
30
31
  constructor(dom: CS.OneJS.Dom.Dom);
31
32
  appendChild(child: DomWrapper): void;
32
33
  removeChild(child: DomWrapper): void;
33
34
  insertBefore(a: DomWrapper, b: DomWrapper): void;
35
+ insertAfter(a: DomWrapper, b: DomWrapper): void;
34
36
  contains(child: DomWrapper): boolean;
35
37
  clearChildren(): void;
36
38
  focus(): void;
@@ -38,4 +40,20 @@ export declare class DomWrapper {
38
40
  removeEventListener(type: string, listener: (event: EventBase) => void, useCapture?: boolean): void;
39
41
  setAttribute(name: string, value: any): void;
40
42
  removeAttribute(name: string): void;
43
+ /**
44
+ * Returns all elements matching the specified selector.
45
+ * Supports basic selectors:
46
+ * - Tag names: 'div'
47
+ * - IDs: '#myId'
48
+ * - Classes: '.myClass'
49
+ * - Combinations: 'div.myClass#myId'
50
+ */
51
+ querySelectorAll(selector: string): DomWrapper[];
52
+ /**
53
+ * Returns the first element matching the specified selector.
54
+ * Supports the same basic selectors as querySelectorAll.
55
+ */
56
+ querySelector(selector: string): DomWrapper | null;
41
57
  }
58
+ export declare function querySelectorAll(root: DomWrapper, selector: string): DomWrapper[];
59
+ export declare function querySelector(root: DomWrapper, selector: string): DomWrapper | null;
package/dist/dom/dom.js CHANGED
@@ -40,15 +40,20 @@ export class DomWrapper {
40
40
  dom;
41
41
  domStyleWrapper;
42
42
  cachedChildNodes = null;
43
+ boundListeners = new WeakMap();
43
44
  constructor(dom) {
44
45
  this.dom = dom;
45
46
  this.domStyleWrapper = new DomStyleWrapper(dom.style);
46
47
  }
47
48
  appendChild(child) {
49
+ if (!child)
50
+ return;
48
51
  this.dom.appendChild(child.dom);
49
52
  this.cachedChildNodes = null;
50
53
  }
51
54
  removeChild(child) {
55
+ if (!child)
56
+ return;
52
57
  this.dom.removeChild(child.dom);
53
58
  this.cachedChildNodes = null;
54
59
  }
@@ -56,7 +61,13 @@ export class DomWrapper {
56
61
  this.dom.insertBefore(a?._dom, b?._dom);
57
62
  this.cachedChildNodes = null;
58
63
  }
64
+ insertAfter(a, b) {
65
+ this.dom.insertAfter(a?._dom, b?._dom);
66
+ this.cachedChildNodes = null;
67
+ }
59
68
  contains(child) {
69
+ if (!child)
70
+ return false;
60
71
  return this.dom.contains(child._dom);
61
72
  }
62
73
  clearChildren() {
@@ -67,12 +78,19 @@ export class DomWrapper {
67
78
  this.dom.focus();
68
79
  }
69
80
  addEventListener(type, listener, useCapture) {
70
- // @ts-ignore
71
- this.dom.addEventListener(type, listener.bind(this), useCapture ? true : false);
81
+ let boundListener = this.boundListeners.get(listener);
82
+ if (!boundListener) {
83
+ boundListener = listener.bind(this);
84
+ this.boundListeners.set(listener, boundListener);
85
+ }
86
+ this.dom.addEventListener(type, boundListener, useCapture ? true : false);
72
87
  }
73
88
  removeEventListener(type, listener, useCapture) {
74
- // @ts-ignore
75
- this.dom.removeEventListener(type, listener.bind(this), useCapture ? true : false);
89
+ const boundListener = this.boundListeners.get(listener);
90
+ if (boundListener) {
91
+ this.dom.removeEventListener(type, boundListener, useCapture ? true : false);
92
+ this.boundListeners.delete(listener); // isn't strictly necessary for WeakMap, but still good practice
93
+ }
76
94
  }
77
95
  setAttribute(name, value) {
78
96
  this.dom.setAttribute(name, value);
@@ -80,4 +98,123 @@ export class DomWrapper {
80
98
  removeAttribute(name) {
81
99
  this.dom.removeAttribute(name);
82
100
  }
101
+ /**
102
+ * Returns all elements matching the specified selector.
103
+ * Supports basic selectors:
104
+ * - Tag names: 'div'
105
+ * - IDs: '#myId'
106
+ * - Classes: '.myClass'
107
+ * - Combinations: 'div.myClass#myId'
108
+ */
109
+ querySelectorAll(selector) {
110
+ const selectorInfo = parseSelector(selector);
111
+ const results = [];
112
+ function traverse(element) {
113
+ if (elementMatchesSelector(element, selectorInfo)) {
114
+ results.push(element);
115
+ }
116
+ for (const child of element.childNodes) {
117
+ traverse(child);
118
+ }
119
+ }
120
+ traverse(this);
121
+ return results;
122
+ }
123
+ /**
124
+ * Returns the first element matching the specified selector.
125
+ * Supports the same basic selectors as querySelectorAll.
126
+ */
127
+ querySelector(selector) {
128
+ const selectorInfo = parseSelector(selector);
129
+ function traverse(element) {
130
+ if (elementMatchesSelector(element, selectorInfo)) {
131
+ return element;
132
+ }
133
+ for (const child of element.childNodes) {
134
+ const match = traverse(child);
135
+ if (match) {
136
+ return match;
137
+ }
138
+ }
139
+ return null;
140
+ }
141
+ return traverse(this);
142
+ }
143
+ }
144
+ function parseSelector(selector) {
145
+ const selectorInfo = {
146
+ classes: []
147
+ };
148
+ // Handle ID
149
+ const idMatch = selector.match(/#([^.#\s]+)/);
150
+ if (idMatch) {
151
+ selectorInfo.id = idMatch[1];
152
+ selector = selector.replace(idMatch[0], '');
153
+ }
154
+ // Handle classes
155
+ const classMatches = selector.match(/\.([^.#\s]+)/g);
156
+ if (classMatches) {
157
+ selectorInfo.classes = classMatches.map(c => c.substring(1));
158
+ selector = selector.replace(/\.[^.#\s]+/g, '');
159
+ }
160
+ // Handle tag name (what's left after removing id and classes)
161
+ const tagName = selector.trim();
162
+ if (tagName) {
163
+ selectorInfo.tag = tagName.toLowerCase();
164
+ }
165
+ return selectorInfo;
166
+ }
167
+ function elementMatchesSelector(element, selectorInfo) {
168
+ // Check tag name
169
+ if (selectorInfo.tag && element.ve.name.toLowerCase() !== selectorInfo.tag) {
170
+ return false;
171
+ }
172
+ // Check ID
173
+ if (selectorInfo.id && element.Id !== selectorInfo.id) {
174
+ return false;
175
+ }
176
+ // Check classes
177
+ if (selectorInfo.classes.length > 0) {
178
+ const elementClasses = element.classname.split(' ').filter(c => c);
179
+ for (const className of selectorInfo.classes) {
180
+ if (!elementClasses.includes(className)) {
181
+ return false;
182
+ }
183
+ }
184
+ }
185
+ return true;
186
+ }
187
+ export function querySelectorAll(root, selector) {
188
+ const results = [];
189
+ const selectorInfo = parseSelector(selector);
190
+ function traverse(element) {
191
+ // Check if current element matches
192
+ if (elementMatchesSelector(element, selectorInfo)) {
193
+ results.push(element);
194
+ }
195
+ // Recursively check children
196
+ for (const child of element.childNodes) {
197
+ traverse(child);
198
+ }
199
+ }
200
+ traverse(root);
201
+ return results;
202
+ }
203
+ export function querySelector(root, selector) {
204
+ const selectorInfo = parseSelector(selector);
205
+ function traverse(element) {
206
+ // Check if current element matches
207
+ if (elementMatchesSelector(element, selectorInfo)) {
208
+ return element;
209
+ }
210
+ // Recursively check children
211
+ for (const child of element.childNodes) {
212
+ const match = traverse(child);
213
+ if (match) {
214
+ return match;
215
+ }
216
+ }
217
+ return null;
218
+ }
219
+ return traverse(root);
83
220
  }
File without changes
File without changes
package/dist/index.js CHANGED
@@ -37,3 +37,10 @@ if (typeof ___document != "undefined") {
37
37
  // @ts-ignore
38
38
  globalThis.document = new DocumentWrapper(___document);
39
39
  }
40
+ // @ts-ignore
41
+ CS.OneJS.EngineHost.prototype.sub = function (a, b, c) {
42
+ let unsubscribe = c ? this.subscribe(a, b, c) : this.subscribe(a, b);
43
+ return () => {
44
+ unsubscribe.Invoke();
45
+ };
46
+ };
package/dom/dom.ts CHANGED
@@ -45,6 +45,7 @@ export class DomWrapper {
45
45
  domStyleWrapper: DomStyleWrapper
46
46
 
47
47
  cachedChildNodes: DomWrapper[] | null = null
48
+ boundListeners = new WeakMap();
48
49
 
49
50
  constructor(dom: CS.OneJS.Dom.Dom) {
50
51
  this.dom = dom
@@ -52,11 +53,13 @@ export class DomWrapper {
52
53
  }
53
54
 
54
55
  appendChild(child: DomWrapper) {
56
+ if (!child) return
55
57
  this.dom.appendChild(child.dom)
56
58
  this.cachedChildNodes = null
57
59
  }
58
60
 
59
61
  removeChild(child: DomWrapper) {
62
+ if (!child) return
60
63
  this.dom.removeChild(child.dom)
61
64
  this.cachedChildNodes = null
62
65
  }
@@ -66,7 +69,13 @@ export class DomWrapper {
66
69
  this.cachedChildNodes = null
67
70
  }
68
71
 
72
+ insertAfter(a: DomWrapper, b: DomWrapper) {
73
+ this.dom.insertAfter(a?._dom, b?._dom)
74
+ this.cachedChildNodes = null
75
+ }
76
+
69
77
  contains(child: DomWrapper) {
78
+ if (!child) return false
70
79
  return this.dom.contains(child._dom)
71
80
  }
72
81
 
@@ -80,13 +89,20 @@ export class DomWrapper {
80
89
  }
81
90
 
82
91
  addEventListener(type: string, listener: (event: EventBase) => void, useCapture?: boolean) {
83
- // @ts-ignore
84
- this.dom.addEventListener(type, listener.bind(this), useCapture ? true : false)
92
+ let boundListener = this.boundListeners.get(listener);
93
+ if (!boundListener) {
94
+ boundListener = listener.bind(this);
95
+ this.boundListeners.set(listener, boundListener);
96
+ }
97
+ this.dom.addEventListener(type, boundListener, useCapture ? true : false)
85
98
  }
86
99
 
87
100
  removeEventListener(type: string, listener: (event: EventBase) => void, useCapture?: boolean) {
88
- // @ts-ignore
89
- this.dom.removeEventListener(type, listener.bind(this), useCapture ? true : false)
101
+ const boundListener = this.boundListeners.get(listener);
102
+ if (boundListener) {
103
+ this.dom.removeEventListener(type, boundListener, useCapture ? true : false)
104
+ this.boundListeners.delete(listener); // isn't strictly necessary for WeakMap, but still good practice
105
+ }
90
106
  }
91
107
 
92
108
  setAttribute(name: string, value: any) {
@@ -96,4 +112,156 @@ export class DomWrapper {
96
112
  removeAttribute(name: string) {
97
113
  this.dom.removeAttribute(name)
98
114
  }
115
+
116
+ /**
117
+ * Returns all elements matching the specified selector.
118
+ * Supports basic selectors:
119
+ * - Tag names: 'div'
120
+ * - IDs: '#myId'
121
+ * - Classes: '.myClass'
122
+ * - Combinations: 'div.myClass#myId'
123
+ */
124
+ querySelectorAll(selector: string): DomWrapper[] {
125
+ const selectorInfo = parseSelector(selector);
126
+ const results: DomWrapper[] = [];
127
+
128
+ function traverse(element: DomWrapper) {
129
+ if (elementMatchesSelector(element, selectorInfo)) {
130
+ results.push(element);
131
+ }
132
+
133
+ for (const child of element.childNodes) {
134
+ traverse(child);
135
+ }
136
+ }
137
+
138
+ traverse(this);
139
+ return results;
140
+ }
141
+
142
+ /**
143
+ * Returns the first element matching the specified selector.
144
+ * Supports the same basic selectors as querySelectorAll.
145
+ */
146
+ querySelector(selector: string): DomWrapper | null {
147
+ const selectorInfo = parseSelector(selector);
148
+
149
+ function traverse(element: DomWrapper): DomWrapper | null {
150
+ if (elementMatchesSelector(element, selectorInfo)) {
151
+ return element;
152
+ }
153
+
154
+ for (const child of element.childNodes) {
155
+ const match = traverse(child);
156
+ if (match) {
157
+ return match;
158
+ }
159
+ }
160
+
161
+ return null;
162
+ }
163
+
164
+ return traverse(this);
165
+ }
166
+ }
167
+
168
+ interface SelectorInfo {
169
+ tag?: string;
170
+ id?: string;
171
+ classes: string[];
172
+ }
173
+
174
+ function parseSelector(selector: string): SelectorInfo {
175
+ const selectorInfo: SelectorInfo = {
176
+ classes: []
177
+ };
178
+
179
+ // Handle ID
180
+ const idMatch = selector.match(/#([^.#\s]+)/);
181
+ if (idMatch) {
182
+ selectorInfo.id = idMatch[1];
183
+ selector = selector.replace(idMatch[0], '');
184
+ }
185
+
186
+ // Handle classes
187
+ const classMatches = selector.match(/\.([^.#\s]+)/g);
188
+ if (classMatches) {
189
+ selectorInfo.classes = classMatches.map(c => c.substring(1));
190
+ selector = selector.replace(/\.[^.#\s]+/g, '');
191
+ }
192
+
193
+ // Handle tag name (what's left after removing id and classes)
194
+ const tagName = selector.trim();
195
+ if (tagName) {
196
+ selectorInfo.tag = tagName.toLowerCase();
197
+ }
198
+
199
+ return selectorInfo;
200
+ }
201
+
202
+ function elementMatchesSelector(element: DomWrapper, selectorInfo: SelectorInfo): boolean {
203
+ // Check tag name
204
+ if (selectorInfo.tag && element.ve.name.toLowerCase() !== selectorInfo.tag) {
205
+ return false;
206
+ }
207
+
208
+ // Check ID
209
+ if (selectorInfo.id && element.Id !== selectorInfo.id) {
210
+ return false;
211
+ }
212
+
213
+ // Check classes
214
+ if (selectorInfo.classes.length > 0) {
215
+ const elementClasses = element.classname.split(' ').filter(c => c);
216
+ for (const className of selectorInfo.classes) {
217
+ if (!elementClasses.includes(className)) {
218
+ return false;
219
+ }
220
+ }
221
+ }
222
+
223
+ return true;
224
+ }
225
+
226
+ export function querySelectorAll(root: DomWrapper, selector: string): DomWrapper[] {
227
+ const results: DomWrapper[] = [];
228
+ const selectorInfo = parseSelector(selector);
229
+
230
+ function traverse(element: DomWrapper) {
231
+ // Check if current element matches
232
+ if (elementMatchesSelector(element, selectorInfo)) {
233
+ results.push(element);
234
+ }
235
+
236
+ // Recursively check children
237
+ for (const child of element.childNodes) {
238
+ traverse(child);
239
+ }
240
+ }
241
+
242
+ traverse(root);
243
+ return results;
244
+ }
245
+
246
+ export function querySelector(root: DomWrapper, selector: string): DomWrapper | null {
247
+ const selectorInfo = parseSelector(selector);
248
+
249
+ function traverse(element: DomWrapper): DomWrapper | null {
250
+ // Check if current element matches
251
+ if (elementMatchesSelector(element, selectorInfo)) {
252
+ return element;
253
+ }
254
+
255
+ // Recursively check children
256
+ for (const child of element.childNodes) {
257
+ const match = traverse(child);
258
+ if (match) {
259
+ return match;
260
+ }
261
+ }
262
+
263
+ return null;
264
+ }
265
+
266
+ return traverse(root);
99
267
  }
package/index.ts CHANGED
@@ -49,4 +49,12 @@ declare global {
49
49
  if (typeof ___document != "undefined") {
50
50
  // @ts-ignore
51
51
  globalThis.document = new DocumentWrapper(___document)
52
+ }
53
+
54
+ // @ts-ignore
55
+ CS.OneJS.EngineHost.prototype.sub = function (a, b, c) {
56
+ let unsubscribe = c ? this.subscribe(a, b, c) : this.subscribe(a, b)
57
+ return () => {
58
+ unsubscribe.Invoke()
59
+ }
52
60
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "onejs-core",
3
3
  "description": "The JS part of OneJS, a UI framework and Scripting Engine for Unity.",
4
- "version": "0.3.42",
4
+ "version": "0.3.44",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./typings.d.ts",
7
7
  "dependencies": {
@@ -13,6 +13,7 @@
13
13
  "fs-extra": "^11.2.0",
14
14
  "postcss": "^8.4.38",
15
15
  "postcss-cli": "^11.0.0",
16
+ "progress": "^2.0.3",
16
17
  "rimraf": "^5.0.7",
17
18
  "tailwindcss": "^3.4.1",
18
19
  "tar": "^7.2.0",
@@ -5,6 +5,7 @@ const path = require('path')
5
5
  const fse = require('fs-extra')
6
6
  const xml2js = require('xml2js')
7
7
  const { rimraf } = require('rimraf')
8
+ const ProgressBar = require('progress')
8
9
 
9
10
  const fsp = fs.promises
10
11
 
@@ -172,7 +173,7 @@ async function downloadFile(fileUrl, outputDir) {
172
173
 
173
174
  ensureDirectoryExistence(outputLocationPath);
174
175
 
175
- // Check if the file already exists
176
+ // Check if file exists (keep existing code)
176
177
  if (fs.existsSync(outputLocationPath)) {
177
178
  console.log(`Local .tgz found: ${outputLocationPath}`);
178
179
  return outputLocationPath;
@@ -185,15 +186,24 @@ async function downloadFile(fileUrl, outputDir) {
185
186
  throw new Error(`Failed to fetch ${fileUrl}: ${response.statusText}`);
186
187
  }
187
188
 
189
+ // Get the total size for the progress bar
190
+ const totalSize = parseInt(response.headers.get('content-length'), 10);
191
+
192
+ // Create progress bar
193
+ const progressBar = new ProgressBar('[:bar] :percent ETA: :etas', {
194
+ complete: '=',
195
+ incomplete: ' ',
196
+ width: 40,
197
+ total: totalSize
198
+ });
199
+
188
200
  const fileStream = fs.createWriteStream(outputLocationPath);
189
201
  for await (const chunk of response.body) {
190
202
  fileStream.write(chunk);
203
+ progressBar.tick(chunk.length);
191
204
  }
192
205
 
193
206
  fileStream.end();
194
- fileStream.on('finish', () => {
195
- console.log(`Download completed: ${outputLocationPath}`);
196
- });
197
207
 
198
208
  return new Promise((resolve, reject) => {
199
209
  fileStream.on('close', () => resolve(outputLocationPath));
@@ -39,12 +39,12 @@ module.exports = () => {
39
39
 
40
40
  // Handle hexadecimal colors with alpha value to RGBA conversion
41
41
  root.walkDecls(decl => {
42
- decl.value = decl.value.replace(/: #([A-Fa-f0-9]{8})/g, (match, hex) => {
42
+ decl.value = decl.value.replace(/#([A-Fa-f0-9]{8})/g, (match, hex) => {
43
43
  const r = parseInt(hex.slice(0, 2), 16);
44
44
  const g = parseInt(hex.slice(2, 4), 16);
45
45
  const b = parseInt(hex.slice(4, 6), 16);
46
46
  const a = parseInt(hex.slice(6, 8), 16) / 255;
47
- return `: rgba(${r}, ${g}, ${b}, ${a})`;
47
+ return `rgba(${r}, ${g}, ${b}, ${a})`;
48
48
  });
49
49
  });
50
50