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.
- package/README.md +49 -0
- package/dist/glass_easel_miniprogram_adapter.d.ts +1 -0
- package/dist/glass_easel_miniprogram_adapter.js +2 -0
- package/dist/glass_easel_miniprogram_adapter.js.map +1 -0
- package/dist/types/src/backend.d.ts +51 -0
- package/dist/types/src/backend.d.ts.map +1 -0
- package/dist/types/src/behavior.d.ts +35 -0
- package/dist/types/src/behavior.d.ts.map +1 -0
- package/dist/types/src/component.d.ts +160 -0
- package/dist/types/src/component.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +58 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/selector_query.d.ts +98 -0
- package/dist/types/src/selector_query.d.ts.map +1 -0
- package/dist/types/src/space.d.ts +369 -0
- package/dist/types/src/space.d.ts.map +1 -0
- package/dist/types/src/types.d.ts +77 -0
- package/dist/types/src/types.d.ts.map +1 -0
- package/dist/types/src/utils.d.ts +2 -0
- package/dist/types/src/utils.d.ts.map +1 -0
- package/dist/types/tests/base/env.d.ts +3 -0
- package/dist/types/tests/base/env.d.ts.map +1 -0
- package/dist/types/tests/data_update.test.d.ts +2 -0
- package/dist/types/tests/data_update.test.d.ts.map +1 -0
- package/dist/types/tests/env.test.d.ts +2 -0
- package/dist/types/tests/env.test.d.ts.map +1 -0
- package/dist/types/tests/selector.test.d.ts +2 -0
- package/dist/types/tests/selector.test.d.ts.map +1 -0
- package/dist/types/tests/space.test.d.ts +2 -0
- package/dist/types/tests/space.test.d.ts.map +1 -0
- package/dist/types/tests/trait_behavior.test.d.ts +2 -0
- package/dist/types/tests/trait_behavior.test.d.ts.map +1 -0
- package/jest.config.js +13 -0
- package/package.json +32 -0
- package/src/backend.ts +106 -0
- package/src/behavior.ts +101 -0
- package/src/component.ts +476 -0
- package/src/index.ts +82 -0
- package/src/selector_query.ts +356 -0
- package/src/space.ts +1514 -0
- package/src/types.ts +85 -0
- package/src/utils.ts +3 -0
- package/tests/base/env.ts +20 -0
- package/tests/data_update.test.ts +92 -0
- package/tests/env.test.ts +141 -0
- package/tests/selector.test.ts +407 -0
- package/tests/space.test.ts +953 -0
- package/tests/trait_behavior.test.ts +141 -0
- package/tsconfig.json +11 -0
- package/webpack.config.js +78 -0
package/src/component.ts
ADDED
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
import * as glassEasel from 'glass-easel'
|
|
2
|
+
import {
|
|
3
|
+
utils as typeUtils,
|
|
4
|
+
} from './types'
|
|
5
|
+
import { ComponentType, GeneralBehavior, TraitBehavior } from './behavior'
|
|
6
|
+
import { SelectorQuery } from './selector_query'
|
|
7
|
+
|
|
8
|
+
type DataList = typeUtils.DataList
|
|
9
|
+
type PropertyList = typeUtils.PropertyList
|
|
10
|
+
type MethodList = typeUtils.MethodList
|
|
11
|
+
export type AllData<
|
|
12
|
+
TData extends DataList,
|
|
13
|
+
TProperty extends PropertyList,
|
|
14
|
+
> = typeUtils.DataWithPropertyValues<TData, TProperty>
|
|
15
|
+
|
|
16
|
+
type ExportType<
|
|
17
|
+
UData extends DataList,
|
|
18
|
+
UProperty extends PropertyList,
|
|
19
|
+
UMethod extends MethodList,
|
|
20
|
+
UComponentExport,
|
|
21
|
+
> = (
|
|
22
|
+
UComponentExport extends undefined
|
|
23
|
+
? Component<UData, UProperty, UMethod, UComponentExport>
|
|
24
|
+
: UComponentExport
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
const filterComponentExportWithType = <
|
|
28
|
+
UData extends DataList,
|
|
29
|
+
UProperty extends PropertyList,
|
|
30
|
+
UMethod extends MethodList,
|
|
31
|
+
UComponentExport,
|
|
32
|
+
>(
|
|
33
|
+
source: ComponentCaller<any, any, any, any>,
|
|
34
|
+
elem: glassEasel.Element,
|
|
35
|
+
componentType: ComponentType<UData, UProperty, UMethod, UComponentExport>,
|
|
36
|
+
): ExportType<UData, UProperty, UMethod, UComponentExport> | undefined => {
|
|
37
|
+
const comp = elem.asInstanceOf(componentType._$)
|
|
38
|
+
if (comp === null) return undefined
|
|
39
|
+
const selectedSpace = comp.getRootBehavior().ownerSpace
|
|
40
|
+
const uncheckedCaller = comp.getMethodCaller()
|
|
41
|
+
const caller = uncheckedCaller instanceof ComponentCaller ? uncheckedCaller : null
|
|
42
|
+
const sourceSpace = source._$.getRootBehavior().ownerSpace
|
|
43
|
+
const defaultResult = selectedSpace !== sourceSpace ? null : caller
|
|
44
|
+
const ret = caller?._$export?.(selectedSpace !== sourceSpace ? null : source) as unknown
|
|
45
|
+
if (ret === undefined) {
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
47
|
+
return defaultResult as any
|
|
48
|
+
}
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
50
|
+
return ret as any
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const filterComponentExport = (
|
|
54
|
+
source: ComponentCaller<any, any, any, any>,
|
|
55
|
+
elem: glassEasel.Element,
|
|
56
|
+
): any => {
|
|
57
|
+
if (elem instanceof glassEasel.Component) {
|
|
58
|
+
const selectedSpace = elem.getRootBehavior().ownerSpace
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
60
|
+
const uncheckedCaller = elem.getMethodCaller()
|
|
61
|
+
const caller = uncheckedCaller instanceof ComponentCaller ? uncheckedCaller : null
|
|
62
|
+
const sourceSpace = source._$.getRootBehavior().ownerSpace
|
|
63
|
+
const defaultResult = selectedSpace !== sourceSpace ? null : caller
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
65
|
+
const ret = caller?._$export?.(selectedSpace !== sourceSpace ? null : source)
|
|
66
|
+
if (ret === undefined) {
|
|
67
|
+
return defaultResult as any
|
|
68
|
+
}
|
|
69
|
+
return ret
|
|
70
|
+
}
|
|
71
|
+
return undefined
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type GeneralComponent = Component<any, any, any, any>
|
|
75
|
+
|
|
76
|
+
export type Component<
|
|
77
|
+
TData extends DataList,
|
|
78
|
+
TProperty extends PropertyList,
|
|
79
|
+
TMethod extends MethodList,
|
|
80
|
+
TComponentExport,
|
|
81
|
+
> = ComponentCaller<TData, TProperty, TMethod, TComponentExport>
|
|
82
|
+
& { [k in keyof TMethod]: TMethod[k] }
|
|
83
|
+
|
|
84
|
+
export class ComponentCaller<
|
|
85
|
+
TData extends DataList,
|
|
86
|
+
TProperty extends PropertyList,
|
|
87
|
+
TMethod extends MethodList,
|
|
88
|
+
TComponentExport,
|
|
89
|
+
> {
|
|
90
|
+
/** @internal */
|
|
91
|
+
_$!: glassEasel.Component<
|
|
92
|
+
TData,
|
|
93
|
+
TProperty,
|
|
94
|
+
TMethod
|
|
95
|
+
>
|
|
96
|
+
/** @internal */
|
|
97
|
+
_$export?: (source: GeneralComponent | null) => TComponentExport
|
|
98
|
+
|
|
99
|
+
/** The component path in the code space */
|
|
100
|
+
get is(): string {
|
|
101
|
+
return this._$.is
|
|
102
|
+
}
|
|
103
|
+
set is(value: string) {
|
|
104
|
+
Object.defineProperty(this, 'is', {
|
|
105
|
+
value,
|
|
106
|
+
writable: true,
|
|
107
|
+
enumerable: true,
|
|
108
|
+
configurable: true,
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** The `id` field in the template */
|
|
113
|
+
get id(): string {
|
|
114
|
+
return this._$.id
|
|
115
|
+
}
|
|
116
|
+
set id(value: string) {
|
|
117
|
+
Object.defineProperty(this, 'id', {
|
|
118
|
+
value,
|
|
119
|
+
writable: true,
|
|
120
|
+
enumerable: true,
|
|
121
|
+
configurable: true,
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** The `data-*` field in the template */
|
|
126
|
+
get dataset(): { [key: string]: any } | null {
|
|
127
|
+
return this._$.dataset
|
|
128
|
+
}
|
|
129
|
+
set dataset(value: { [key: string]: any } | null) {
|
|
130
|
+
Object.defineProperty(this, 'dataset', {
|
|
131
|
+
value,
|
|
132
|
+
writable: true,
|
|
133
|
+
enumerable: true,
|
|
134
|
+
configurable: true,
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** The component data and property values */
|
|
139
|
+
get data(): AllData<TData, TProperty> {
|
|
140
|
+
return this._$.data
|
|
141
|
+
}
|
|
142
|
+
set data(value: AllData<TData, TProperty>) {
|
|
143
|
+
Object.defineProperty(this, 'data', {
|
|
144
|
+
value,
|
|
145
|
+
writable: true,
|
|
146
|
+
enumerable: true,
|
|
147
|
+
configurable: true,
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** The component data and property values (same as `data` ) */
|
|
152
|
+
get properties(): AllData<TData, TProperty> {
|
|
153
|
+
return this._$.data
|
|
154
|
+
}
|
|
155
|
+
set properties(value: AllData<TData, TProperty>) {
|
|
156
|
+
Object.defineProperty(this, 'properties', {
|
|
157
|
+
value,
|
|
158
|
+
writable: true,
|
|
159
|
+
enumerable: true,
|
|
160
|
+
configurable: true,
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Group several data update calls
|
|
166
|
+
*
|
|
167
|
+
* This method is designed for hinting the backend that some updates should be handled together.
|
|
168
|
+
* However, this is done automatically now,
|
|
169
|
+
* so this method is just for backward compatibilities.
|
|
170
|
+
*/
|
|
171
|
+
// eslint-disable-next-line class-methods-use-this
|
|
172
|
+
groupSetData(callback: () => void) {
|
|
173
|
+
callback()
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Do a classic data update
|
|
178
|
+
*
|
|
179
|
+
* The `callback` is called after the update applies in the backend.
|
|
180
|
+
* In most cases, you SHOULD NOT wait the backend update (that might be very slow),
|
|
181
|
+
* and most calls, including read calls such as `selectComponent` , simply works immediately.
|
|
182
|
+
* However, when called inside observers,
|
|
183
|
+
* the data update will not be applied to templates immediately
|
|
184
|
+
* (it is recommanded to use `updateData` instead in observers).
|
|
185
|
+
*/
|
|
186
|
+
setData(
|
|
187
|
+
newData: Partial<typeUtils.SetDataSetter<typeUtils.DataWithPropertyValues<TData, TProperty>>>,
|
|
188
|
+
callback?: () => void,
|
|
189
|
+
): void
|
|
190
|
+
setData(data: Record<string, any>, callback?: () => void) {
|
|
191
|
+
this._$.setData(data as any)
|
|
192
|
+
if (callback) {
|
|
193
|
+
glassEasel.triggerRender(this._$, () => {
|
|
194
|
+
callback()
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Schedule a classic data updates
|
|
201
|
+
*
|
|
202
|
+
* The data update will not be applied until next `setData` or `applyDataUpdates` call.
|
|
203
|
+
* When called inside observers, the data update will be applied when observer ends.
|
|
204
|
+
* All data observers will not be triggered immediately before applied.
|
|
205
|
+
* Reads of the data will get the unchanged value before applied.
|
|
206
|
+
*/
|
|
207
|
+
updateData(
|
|
208
|
+
newData: Partial<typeUtils.SetDataSetter<typeUtils.DataWithPropertyValues<TData, TProperty>>>,
|
|
209
|
+
): void
|
|
210
|
+
updateData(newData: Record<string, any>): void {
|
|
211
|
+
this._$.updateData(newData as any)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Schedule a data update on a single specified path
|
|
216
|
+
*
|
|
217
|
+
* The data update will not be applied until next `setData` or `applyDataUpdates` call.
|
|
218
|
+
* All data observers will not be triggered immediately before applied.
|
|
219
|
+
* Reads of the data will get the unchanged value before applied.
|
|
220
|
+
*/
|
|
221
|
+
replaceDataOnPath<
|
|
222
|
+
T extends (string | number)[],
|
|
223
|
+
>(
|
|
224
|
+
path: readonly [...T],
|
|
225
|
+
data: typeUtils.GetFromDataPath<typeUtils.DataWithPropertyValues<TData, TProperty>, T>,
|
|
226
|
+
): void;
|
|
227
|
+
replaceDataOnPath(path: any, data: any) {
|
|
228
|
+
this._$.replaceDataOnPath(path, data)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Schedule an array update
|
|
233
|
+
*
|
|
234
|
+
* The behavior is like `Array.prototype.slice` .
|
|
235
|
+
* Break the array before the `index`-th item, delete `del` items, and insert some items here.
|
|
236
|
+
* If `index` is undefined, negative, or larger than the length of the array,
|
|
237
|
+
* no items will be deleted and new items will be appended to the end of the array.
|
|
238
|
+
* The data update will not be applied until next `setData` or `applyDataUpdates` call.
|
|
239
|
+
* All data observers will not be triggered immediately before applied.
|
|
240
|
+
* Reads of the data will get the unchanged value before applied.
|
|
241
|
+
*/
|
|
242
|
+
spliceArrayDataOnPath<
|
|
243
|
+
T extends (string | number)[],
|
|
244
|
+
>(
|
|
245
|
+
path: readonly [...T],
|
|
246
|
+
index: typeUtils.GetFromDataPath<
|
|
247
|
+
typeUtils.DataWithPropertyValues<TData, TProperty>,
|
|
248
|
+
T
|
|
249
|
+
> extends any[] ? number | undefined : never,
|
|
250
|
+
del: typeUtils.GetFromDataPath<
|
|
251
|
+
typeUtils.DataWithPropertyValues<TData, TProperty>,
|
|
252
|
+
T
|
|
253
|
+
> extends any[] ? number | undefined : never,
|
|
254
|
+
inserts: typeUtils.GetFromDataPath<
|
|
255
|
+
typeUtils.DataWithPropertyValues<TData, TProperty>,
|
|
256
|
+
T
|
|
257
|
+
> extends (infer I)[] ? I[] : never
|
|
258
|
+
): void;
|
|
259
|
+
spliceArrayDataOnPath(
|
|
260
|
+
path: (string | number)[],
|
|
261
|
+
index: number | undefined,
|
|
262
|
+
del: number | undefined,
|
|
263
|
+
inserts: unknown[],
|
|
264
|
+
) {
|
|
265
|
+
this._$.spliceArrayDataOnPath(path, index as any, del as any, inserts as any)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Apply all scheduled updates immediately
|
|
270
|
+
*
|
|
271
|
+
* Inside observers, it is generally not .
|
|
272
|
+
*/
|
|
273
|
+
applyDataUpdates() {
|
|
274
|
+
this._$.applyDataUpdates()
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Pending all data updates in the callback, and apply updates after callback returns
|
|
279
|
+
*
|
|
280
|
+
* This function helps grouping several `replaceDataOnPath` or `spliceArrayDataOnPath` calls,
|
|
281
|
+
* and then apply them at the end of the callback.
|
|
282
|
+
* `setData` and `applyDataUpdates` calls inside the callback still apply updates immediately.
|
|
283
|
+
*/
|
|
284
|
+
groupUpdates<T>(callback: () => T): T {
|
|
285
|
+
return this._$.groupUpdates(callback)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Check whether the `other` behavior is a dependent behavior or a implemented trait behavior
|
|
290
|
+
*/
|
|
291
|
+
hasBehavior(behavior: GeneralBehavior | TraitBehavior<any, any>) {
|
|
292
|
+
return this._$.hasBehavior(behavior._$)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Get the trait behavior implementation of the component
|
|
297
|
+
*
|
|
298
|
+
* Returns `undefined` if the specified trait behavior is not implemented.
|
|
299
|
+
*/
|
|
300
|
+
traitBehavior<TOut extends { [key: string]: any }>(
|
|
301
|
+
traitBehavior: TraitBehavior<any, TOut>,
|
|
302
|
+
): TOut | undefined {
|
|
303
|
+
return this._$.traitBehavior(traitBehavior._$)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** Trigger an event */
|
|
307
|
+
triggerEvent(
|
|
308
|
+
name: string,
|
|
309
|
+
detail: any,
|
|
310
|
+
options: {
|
|
311
|
+
bubbles?: boolean,
|
|
312
|
+
composed?: boolean,
|
|
313
|
+
capturePhase?: boolean,
|
|
314
|
+
},
|
|
315
|
+
) {
|
|
316
|
+
return this._$.triggerEvent(name, detail, options)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** Create a selector query for searching element inside the component */
|
|
320
|
+
createSelectorQuery(): SelectorQuery {
|
|
321
|
+
return new SelectorQuery(this)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// TODO support animate / applyAnimation / clearAnimation
|
|
325
|
+
// TODO support createIntersectionObserver
|
|
326
|
+
// TODO support createMediaQueryObserver
|
|
327
|
+
// TODO support setUpdatePerformanceListener
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Query an element inside the component
|
|
331
|
+
*
|
|
332
|
+
* If `componentType` is provided, this method will check the selected component type.
|
|
333
|
+
* If the component type does not match, `null` is returned.
|
|
334
|
+
*/
|
|
335
|
+
selectComponent(selector: string): any
|
|
336
|
+
selectComponent<
|
|
337
|
+
UData extends DataList,
|
|
338
|
+
UProperty extends PropertyList,
|
|
339
|
+
UMethod extends MethodList,
|
|
340
|
+
UComponentExport,
|
|
341
|
+
>(
|
|
342
|
+
selector: string,
|
|
343
|
+
componentType: ComponentType<UData, UProperty, UMethod, UComponentExport>,
|
|
344
|
+
): ExportType<UData, UProperty, UMethod, UComponentExport> | null
|
|
345
|
+
selectComponent<
|
|
346
|
+
UData extends DataList,
|
|
347
|
+
UProperty extends PropertyList,
|
|
348
|
+
UMethod extends MethodList,
|
|
349
|
+
UComponentExport,
|
|
350
|
+
>(
|
|
351
|
+
selector: string,
|
|
352
|
+
componentType?: ComponentType<UData, UProperty, UMethod, UComponentExport>,
|
|
353
|
+
): ExportType<UData, UProperty, UMethod, UComponentExport> | null {
|
|
354
|
+
const target = this._$.getShadowRoot()!.querySelector(selector)
|
|
355
|
+
if (target === null) return null
|
|
356
|
+
return componentType
|
|
357
|
+
? filterComponentExportWithType(this, target, componentType) ?? null
|
|
358
|
+
: filterComponentExport(this, target) ?? null
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Query all elements inside the component
|
|
363
|
+
*
|
|
364
|
+
* If `componentType` is provided, this method will check the selected component type.
|
|
365
|
+
* If a component type does not match, it is not returned.
|
|
366
|
+
*/
|
|
367
|
+
selectAllComponents(selector: string): GeneralComponent[]
|
|
368
|
+
selectAllComponents<
|
|
369
|
+
UData extends DataList,
|
|
370
|
+
UProperty extends PropertyList,
|
|
371
|
+
UMethod extends MethodList,
|
|
372
|
+
UComponentExport,
|
|
373
|
+
>(
|
|
374
|
+
selector: string,
|
|
375
|
+
componentType: ComponentType<UData, UProperty, UMethod, UComponentExport>,
|
|
376
|
+
): ExportType<UData, UProperty, UMethod, UComponentExport>[]
|
|
377
|
+
selectAllComponents<
|
|
378
|
+
UData extends DataList,
|
|
379
|
+
UProperty extends PropertyList,
|
|
380
|
+
UMethod extends MethodList,
|
|
381
|
+
UComponentExport,
|
|
382
|
+
>(
|
|
383
|
+
selector: string,
|
|
384
|
+
componentType?: ComponentType<UData, UProperty, UMethod, UComponentExport>,
|
|
385
|
+
): ExportType<UData, UProperty, UMethod, UComponentExport>[] {
|
|
386
|
+
const targets = this._$.getShadowRoot()!.querySelectorAll(selector)
|
|
387
|
+
const ret = [] as ExportType<UData, UProperty, UMethod, UComponentExport>[]
|
|
388
|
+
targets.forEach((target) => {
|
|
389
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
390
|
+
const r = componentType
|
|
391
|
+
? filterComponentExportWithType(this, target, componentType)
|
|
392
|
+
: filterComponentExport(this, target)
|
|
393
|
+
if (r !== undefined) ret.push(r)
|
|
394
|
+
})
|
|
395
|
+
return ret
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Get the owner component
|
|
400
|
+
*
|
|
401
|
+
* If `componentType` is provided, this method will check the selected component type.
|
|
402
|
+
* If a component type does not match, `null` is returned.
|
|
403
|
+
*/
|
|
404
|
+
selectOwnerComponent(): any
|
|
405
|
+
selectOwnerComponent<
|
|
406
|
+
UData extends DataList,
|
|
407
|
+
UProperty extends PropertyList,
|
|
408
|
+
UMethod extends MethodList,
|
|
409
|
+
UComponentExport,
|
|
410
|
+
>(
|
|
411
|
+
componentType: ComponentType<UData, UProperty, UMethod, UComponentExport>,
|
|
412
|
+
): ExportType<UData, UProperty, UMethod, UComponentExport> | null
|
|
413
|
+
selectOwnerComponent<
|
|
414
|
+
UData extends DataList,
|
|
415
|
+
UProperty extends PropertyList,
|
|
416
|
+
UMethod extends MethodList,
|
|
417
|
+
UComponentExport,
|
|
418
|
+
>(
|
|
419
|
+
componentType?: ComponentType<UData, UProperty, UMethod, UComponentExport>,
|
|
420
|
+
): ExportType<UData, UProperty, UMethod, UComponentExport> | null {
|
|
421
|
+
const target = this._$.ownerShadowRoot?.getHostNode()
|
|
422
|
+
if (target === undefined) return null
|
|
423
|
+
return componentType
|
|
424
|
+
? filterComponentExportWithType(this, target, componentType) ?? null
|
|
425
|
+
: filterComponentExport(this, target) ?? null
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
getRelationNodes(relationKey: string): GeneralComponent[] {
|
|
429
|
+
return this._$.getRelationNodes(relationKey).map(
|
|
430
|
+
(target) => target.getMethodCaller() as any as GeneralComponent,
|
|
431
|
+
)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/** Cast the component into a general-typed one */
|
|
435
|
+
general(): GeneralComponent {
|
|
436
|
+
return this
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Cast the component into the specified type
|
|
441
|
+
*
|
|
442
|
+
* Returns `null` if the component node is not the instance of the specified component.
|
|
443
|
+
*/
|
|
444
|
+
asInstanceOf<
|
|
445
|
+
UData extends DataList,
|
|
446
|
+
UProperty extends PropertyList,
|
|
447
|
+
UMethod extends MethodList,
|
|
448
|
+
UComponentExport,
|
|
449
|
+
>(
|
|
450
|
+
componentType: ComponentType<UData, UProperty, UMethod, UComponentExport>,
|
|
451
|
+
): Component<UData, UProperty, UMethod, UComponentExport> | null {
|
|
452
|
+
const inner = this._$.asInstanceOf(componentType._$)
|
|
453
|
+
if (!inner) return null
|
|
454
|
+
return this as unknown as Component<UData, UProperty, UMethod, UComponentExport>
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export class ComponentProto<
|
|
459
|
+
TData extends DataList,
|
|
460
|
+
TProperty extends PropertyList,
|
|
461
|
+
TMethod extends MethodList,
|
|
462
|
+
TComponentExport,
|
|
463
|
+
> {
|
|
464
|
+
private proto: Component<TData, TProperty, TMethod, TComponentExport>
|
|
465
|
+
|
|
466
|
+
constructor(methods: TMethod, componentExport?: () => TComponentExport) {
|
|
467
|
+
this.proto = Object.create(ComponentCaller.prototype) as
|
|
468
|
+
Component<TData, TProperty, TMethod, TComponentExport>
|
|
469
|
+
Object.assign(this.proto, methods)
|
|
470
|
+
this.proto._$export = componentExport
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
derive(): Component<TData, TProperty, TMethod, TComponentExport> {
|
|
474
|
+
return Object.create(this.proto) as Component<TData, TProperty, TMethod, TComponentExport>
|
|
475
|
+
}
|
|
476
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as glassEasel from 'glass-easel'
|
|
2
|
+
import { AssociatedBackend } from './backend'
|
|
3
|
+
import {
|
|
4
|
+
CodeSpace,
|
|
5
|
+
} from './space'
|
|
6
|
+
|
|
7
|
+
export * as glassEasel from 'glass-easel'
|
|
8
|
+
export * as types from './types'
|
|
9
|
+
export { StyleIsolation } from './types'
|
|
10
|
+
export * from './space'
|
|
11
|
+
export { AssociatedBackend, Root } from './backend'
|
|
12
|
+
export * as component from './component'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A mini-program API environment
|
|
16
|
+
*
|
|
17
|
+
* Each environment manages multiple backend contexts.
|
|
18
|
+
* However, a backend context should be exclusively managed by a single environment
|
|
19
|
+
* (to avoid `StyleScopeId` confliction).
|
|
20
|
+
* It is able to create multiple `CodeSpace` within the environment.
|
|
21
|
+
*/
|
|
22
|
+
export class MiniProgramEnv {
|
|
23
|
+
private codeSpaceMap: { [id: string]: CodeSpace }
|
|
24
|
+
private globalCodeSpace: CodeSpace
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Create an empty mini-program API environment
|
|
28
|
+
*/
|
|
29
|
+
constructor() {
|
|
30
|
+
this.codeSpaceMap = Object.create(null) as { [id: string]: CodeSpace }
|
|
31
|
+
this.globalCodeSpace = new CodeSpace(this, false, {})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get a global code space in which global components can be defined
|
|
36
|
+
*
|
|
37
|
+
* External glass-easel based components can be defined in this code space.
|
|
38
|
+
* All global components should be defined before any other `CodeSpace` created!
|
|
39
|
+
*/
|
|
40
|
+
getGlobalCodeSpace(): CodeSpace {
|
|
41
|
+
return this.globalCodeSpace
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Associate a backend context
|
|
46
|
+
*
|
|
47
|
+
* If `backendContext` is not given, the DOM-based backend is used
|
|
48
|
+
* (causes failure if running outside browsers).
|
|
49
|
+
* The backend context SHOULD NOT be associated by other environments!
|
|
50
|
+
*/
|
|
51
|
+
associateBackend(backendContent?: glassEasel.GeneralBackendContext): AssociatedBackend {
|
|
52
|
+
const ctx = backendContent ?? new glassEasel.domlikeBackend.CurrentWindowBackendContext()
|
|
53
|
+
return new AssociatedBackend(this, ctx)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Create a component space that can manage WXML, WXSS, JS and static JSON config files
|
|
58
|
+
*
|
|
59
|
+
* The space can be specified as a *main* space.
|
|
60
|
+
* This makes some features available:
|
|
61
|
+
* * the `app.wxss` will be used as a style sheet for all root components in the space;
|
|
62
|
+
* * the `StyleIsolation.Shared` is accepted for components in the space.
|
|
63
|
+
* Non-main spaces usually act as plugins.
|
|
64
|
+
* `publicComponents` is a map for specifying a map of aliases and component paths.
|
|
65
|
+
*/
|
|
66
|
+
createCodeSpace(
|
|
67
|
+
id: string,
|
|
68
|
+
isMainSpace: boolean,
|
|
69
|
+
publicComponents = Object.create(null) as { [alias: string]: string },
|
|
70
|
+
): CodeSpace {
|
|
71
|
+
const cs = new CodeSpace(this, isMainSpace, publicComponents, this.globalCodeSpace)
|
|
72
|
+
this.codeSpaceMap[id] = cs
|
|
73
|
+
return cs
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get a component space by the `id` specified when created
|
|
78
|
+
*/
|
|
79
|
+
getCodeSpace(id: string): CodeSpace | undefined {
|
|
80
|
+
return this.codeSpaceMap[id]
|
|
81
|
+
}
|
|
82
|
+
}
|