glass-easel-miniprogram-adapter 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +49 -0
  2. package/dist/glass_easel_miniprogram_adapter.d.ts +1 -0
  3. package/dist/glass_easel_miniprogram_adapter.js +2 -0
  4. package/dist/glass_easel_miniprogram_adapter.js.map +1 -0
  5. package/dist/types/src/backend.d.ts +51 -0
  6. package/dist/types/src/backend.d.ts.map +1 -0
  7. package/dist/types/src/behavior.d.ts +35 -0
  8. package/dist/types/src/behavior.d.ts.map +1 -0
  9. package/dist/types/src/component.d.ts +160 -0
  10. package/dist/types/src/component.d.ts.map +1 -0
  11. package/dist/types/src/index.d.ts +58 -0
  12. package/dist/types/src/index.d.ts.map +1 -0
  13. package/dist/types/src/selector_query.d.ts +98 -0
  14. package/dist/types/src/selector_query.d.ts.map +1 -0
  15. package/dist/types/src/space.d.ts +369 -0
  16. package/dist/types/src/space.d.ts.map +1 -0
  17. package/dist/types/src/types.d.ts +77 -0
  18. package/dist/types/src/types.d.ts.map +1 -0
  19. package/dist/types/src/utils.d.ts +2 -0
  20. package/dist/types/src/utils.d.ts.map +1 -0
  21. package/dist/types/tests/base/env.d.ts +3 -0
  22. package/dist/types/tests/base/env.d.ts.map +1 -0
  23. package/dist/types/tests/data_update.test.d.ts +2 -0
  24. package/dist/types/tests/data_update.test.d.ts.map +1 -0
  25. package/dist/types/tests/env.test.d.ts +2 -0
  26. package/dist/types/tests/env.test.d.ts.map +1 -0
  27. package/dist/types/tests/selector.test.d.ts +2 -0
  28. package/dist/types/tests/selector.test.d.ts.map +1 -0
  29. package/dist/types/tests/space.test.d.ts +2 -0
  30. package/dist/types/tests/space.test.d.ts.map +1 -0
  31. package/dist/types/tests/trait_behavior.test.d.ts +2 -0
  32. package/dist/types/tests/trait_behavior.test.d.ts.map +1 -0
  33. package/jest.config.js +13 -0
  34. package/package.json +32 -0
  35. package/src/backend.ts +106 -0
  36. package/src/behavior.ts +101 -0
  37. package/src/component.ts +476 -0
  38. package/src/index.ts +82 -0
  39. package/src/selector_query.ts +356 -0
  40. package/src/space.ts +1514 -0
  41. package/src/types.ts +85 -0
  42. package/src/utils.ts +3 -0
  43. package/tests/base/env.ts +20 -0
  44. package/tests/data_update.test.ts +92 -0
  45. package/tests/env.test.ts +141 -0
  46. package/tests/selector.test.ts +407 -0
  47. package/tests/space.test.ts +953 -0
  48. package/tests/trait_behavior.test.ts +141 -0
  49. package/tsconfig.json +11 -0
  50. package/webpack.config.js +78 -0
@@ -0,0 +1,356 @@
1
+ import * as glassEasel from 'glass-easel'
2
+ import type { GeneralComponent } from './component'
3
+
4
+ export type SelectorQueryFields = {
5
+ id?: boolean,
6
+ dataset?: boolean,
7
+ mark?: boolean,
8
+ rect?: boolean,
9
+ size?: boolean,
10
+ scrollOffset?: boolean,
11
+ properties?: string[],
12
+ // TODO support computedStyle
13
+ }
14
+
15
+ export type SelectorQueryResult<T extends SelectorQueryFields> = {
16
+ id: T['id'] extends true ? string : undefined
17
+ dataset: T['dataset'] extends true ? { [k: string]: any } : undefined
18
+ mark: T['mark'] extends true ? { [k: string]: any } : undefined
19
+ left: T['rect'] extends true ? number : undefined
20
+ top: T['rect'] extends true ? number : undefined
21
+ right: T['rect'] extends true ? number : undefined
22
+ bottom: T['rect'] extends true ? number : undefined
23
+ width: T['size'] extends true ? number : undefined
24
+ height: T['size'] extends true ? number : undefined
25
+ scrollLeft: T['scrollOffset'] extends true ? number : undefined
26
+ scrollTop: T['scrollOffset'] extends true ? number : undefined
27
+ scrollWidth: T['scrollOffset'] extends true ? number : undefined
28
+ scrollHeight: T['scrollOffset'] extends true ? number : undefined
29
+ } & {
30
+ [K in T['properties'] extends (infer I extends string)[] ? I : never]: any
31
+ }
32
+
33
+ export type BoundingClientRect = {
34
+ id: string,
35
+ dataset: { [k: string]: any },
36
+ left: number,
37
+ top: number,
38
+ right: number,
39
+ bottom: number,
40
+ width: number,
41
+ height: number,
42
+ }
43
+
44
+ export type ScrollOffset = {
45
+ id: string,
46
+ dataset: { [k: string]: any },
47
+ scrollLeft: number,
48
+ scrollTop: number,
49
+ scrollWidth: number,
50
+ scrollHeight: number,
51
+ }
52
+
53
+ const joinAsync = <T>(
54
+ inits: ((cb: (ret: T) => void) => void)[],
55
+ cb: (rets: T[]) => void,
56
+ ) => {
57
+ let initDone = false
58
+ let jobCount = inits.length
59
+ const rets = new Array<T>(jobCount)
60
+ inits.forEach((init, index) => {
61
+ init((ret) => {
62
+ rets[index] = ret
63
+ jobCount -= 1
64
+ if (jobCount === 0 && initDone) {
65
+ cb(rets)
66
+ }
67
+ })
68
+ })
69
+ initDone = true
70
+ if (jobCount === 0) {
71
+ setTimeout(() => {
72
+ cb(rets)
73
+ }, 0)
74
+ }
75
+ }
76
+
77
+ class NodesRef<S extends boolean> {
78
+ private _$sq: SelectorQuery
79
+ private _$comp: GeneralComponent
80
+ private _$sel: string | null
81
+ private _$single: S
82
+
83
+ constructor(
84
+ selectorQuery: SelectorQuery,
85
+ component: GeneralComponent,
86
+ selector: string | null,
87
+ selectSingle: S,
88
+ ) {
89
+ this._$sq = selectorQuery
90
+ this._$comp = component
91
+ this._$sel = selector
92
+ this._$single = selectSingle
93
+ }
94
+
95
+ fields<T extends SelectorQueryFields>(
96
+ fields: T,
97
+ cb: (res: S extends true ? SelectorQueryResult<T> : SelectorQueryResult<T>[]) => void
98
+ = () => { /* empty */ },
99
+ ) {
100
+ this._$sq._$push(this._$sel, this._$comp, this._$single, fields, cb)
101
+ return this._$sq
102
+ }
103
+
104
+ boundingClientRect(
105
+ cb: (res: S extends true ? BoundingClientRect : BoundingClientRect[]) => void
106
+ = () => { /* empty */ },
107
+ ) {
108
+ this._$sq._$push(
109
+ this._$sel,
110
+ this._$comp,
111
+ this._$single,
112
+ {
113
+ id: true,
114
+ dataset: true,
115
+ rect: true,
116
+ size: true,
117
+ },
118
+ cb,
119
+ )
120
+ return this._$sq
121
+ }
122
+
123
+ scrollOffset(
124
+ cb: (res: S extends true ? ScrollOffset : ScrollOffset[]) => void
125
+ = () => { /* empty */ },
126
+ ) {
127
+ this._$sq._$push(
128
+ this._$sel,
129
+ this._$comp,
130
+ this._$single,
131
+ {
132
+ id: true,
133
+ dataset: true,
134
+ scrollOffset: true,
135
+ },
136
+ cb,
137
+ )
138
+ return this._$sq
139
+ }
140
+ }
141
+
142
+ // main class
143
+ export class SelectorQuery {
144
+ private _$comp: GeneralComponent
145
+ /** @internal */
146
+ _$queue: {
147
+ component: GeneralComponent,
148
+ selector: string | null,
149
+ single: boolean,
150
+ fields: SelectorQueryFields,
151
+ cb: (res: any) => void,
152
+ }[]
153
+
154
+ /** @internal */
155
+ constructor(component: GeneralComponent) {
156
+ this._$comp = component
157
+ this._$queue = []
158
+ }
159
+
160
+ /** Change where the node should be selected from */
161
+ in(component: GeneralComponent) {
162
+ this._$comp = component
163
+ return this
164
+ }
165
+
166
+ /** Select the first node that matches the selector */
167
+ select(selector: string) {
168
+ return new NodesRef(this, this._$comp, selector, true)
169
+ }
170
+
171
+ /** Select all nodes that match the selector */
172
+ selectAll(selector: string) {
173
+ return new NodesRef(this, this._$comp, selector, false)
174
+ }
175
+
176
+ /**
177
+ * Select the viewport of the component
178
+ *
179
+ * Only the size and the scroll position is meaningful when the viewport is selected.
180
+ */
181
+ selectViewport() {
182
+ return new NodesRef(this, this._$comp, null, true)
183
+ }
184
+
185
+ /** @internal */
186
+ _$push(
187
+ selector: string | null,
188
+ component: GeneralComponent,
189
+ single: boolean,
190
+ fields: SelectorQueryFields,
191
+ cb: (res: any) => void,
192
+ ) {
193
+ this._$queue.push({
194
+ component,
195
+ selector,
196
+ single,
197
+ fields,
198
+ cb,
199
+ })
200
+ }
201
+
202
+ /**
203
+ * Execute all queries
204
+ *
205
+ * The `cb` is called after all queries done.
206
+ */
207
+ exec(cb: (resList: any[]) => void = () => { /* empty */ }) {
208
+ const q = this._$queue
209
+ joinAsync<any>(q.map((req) => ((done) => {
210
+ const {
211
+ component,
212
+ selector,
213
+ single,
214
+ fields,
215
+ cb,
216
+ } = req
217
+ if (selector === null) {
218
+ // for the viewport selection, return available fields
219
+ const res = {} as { [k: string]: any }
220
+ joinAsync<undefined>([
221
+ (done) => {
222
+ const ctx = component._$.getBackendContext()
223
+ const width = ctx.getWindowWidth()
224
+ const height = ctx.getWindowHeight()
225
+ if (fields.id) res.id = ''
226
+ if (fields.dataset) res.dataset = {}
227
+ if (fields.mark) res.mark = {}
228
+ if (fields.rect) {
229
+ res.left = 0
230
+ res.top = 0
231
+ res.right = width
232
+ res.bottom = height
233
+ }
234
+ if (fields.size) {
235
+ res.width = width
236
+ res.height = height
237
+ }
238
+ if (fields.scrollOffset) {
239
+ let rootComp = component._$.general()
240
+ while (rootComp.ownerShadowRoot) {
241
+ rootComp = rootComp.ownerShadowRoot.getHostNode()
242
+ }
243
+ rootComp.getBoundingClientRect(({
244
+ left,
245
+ top,
246
+ width,
247
+ height,
248
+ }) => {
249
+ res.scrollLeft = left
250
+ res.scrollTop = top
251
+ res.scrollWidth = width
252
+ res.scrollHeight = height
253
+ done(undefined)
254
+ })
255
+ } else {
256
+ done(undefined)
257
+ }
258
+ },
259
+ ], () => {
260
+ cb(res)
261
+ done(res)
262
+ })
263
+ } else {
264
+ // for common node selection, return specified fields
265
+ const constructSingleRes = (
266
+ elem: glassEasel.Element,
267
+ cb: (res: any) => void,
268
+ ) => {
269
+ const res = {} as { [k: string]: any }
270
+ if (fields.id) res.id = elem.id
271
+ if (fields.dataset) res.dataset = elem.dataset
272
+ if (fields.mark) res.mark = elem.collectMarks()
273
+ joinAsync<undefined>([
274
+ (done) => {
275
+ if (fields.rect || fields.size) {
276
+ elem.getBoundingClientRect((rect) => {
277
+ if (fields.rect) {
278
+ res.left = rect.left
279
+ res.top = rect.top
280
+ res.right = rect.left + rect.width
281
+ res.bottom = rect.top + rect.height
282
+ }
283
+ if (fields.size) {
284
+ res.width = rect.width
285
+ res.height = rect.height
286
+ }
287
+ done(undefined)
288
+ })
289
+ } else {
290
+ done(undefined)
291
+ }
292
+ },
293
+ (done) => {
294
+ if (fields.scrollOffset) {
295
+ elem.getScrollOffset((rect) => {
296
+ res.scrollLeft = rect.scrollLeft
297
+ res.scrollTop = rect.scrollTop
298
+ res.scrollWidth = rect.scrollWidth
299
+ res.scrollHeight = rect.scrollHeight
300
+ done(undefined)
301
+ })
302
+ } else {
303
+ done(undefined)
304
+ }
305
+ },
306
+ ], () => {
307
+ if (fields.properties) {
308
+ if (elem instanceof glassEasel.Component) {
309
+ fields.properties.forEach((propName) => {
310
+ if (glassEasel.Component.hasProperty(elem, propName)) {
311
+ const ds = elem.getComponentOptions().propertyPassingDeepCopy
312
+ const v = (elem.data as { [x: string]: unknown })[propName]
313
+ if (ds === glassEasel.DeepCopyKind.None) {
314
+ res[propName] = v
315
+ } else if (ds === glassEasel.DeepCopyKind.Simple) {
316
+ res[propName] = glassEasel.dataUtils.simpleDeepCopy(v)
317
+ } else if (ds === glassEasel.DeepCopyKind.SimpleWithRecursion) {
318
+ res[propName] = glassEasel.dataUtils.deepCopy(v, true)
319
+ }
320
+ }
321
+ })
322
+ }
323
+ }
324
+ cb(res)
325
+ })
326
+ }
327
+ if (single) {
328
+ // select single node
329
+ const node = component._$.getShadowRoot()!.querySelector(selector)
330
+ if (node) {
331
+ constructSingleRes(node, (res) => {
332
+ cb(res)
333
+ done(res)
334
+ })
335
+ } else {
336
+ setTimeout(() => {
337
+ cb(null)
338
+ done(null)
339
+ }, 0)
340
+ }
341
+ } else {
342
+ // select multiple nodes
343
+ const nodes = component._$.getShadowRoot()!.querySelectorAll(selector)
344
+ joinAsync<any>(nodes.map((node) => ((done) => {
345
+ constructSingleRes(node, (res) => {
346
+ done(res)
347
+ })
348
+ })), (resList) => {
349
+ cb(resList)
350
+ done(resList)
351
+ })
352
+ }
353
+ }
354
+ })), cb)
355
+ }
356
+ }