forked from PAIR-code/lit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
lit_module.ts
131 lines (108 loc) · 4.69 KB
/
lit_module.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// tslint:disable:no-new-decorators
import {property} from 'lit-element';
import {computed} from 'mobx';
import { observable } from 'mobx';
import {ReactiveElement} from '../lib/elements';
import {LitModuleClass, SCROLL_SYNC_CSS_CLASS} from '../lib/types';
import {ApiService, AppState, SelectionService} from '../services/services';
import {app} from './lit_app';
/**
* An interface describing the LitWidget element that contains the LitModule.
*/
export interface ParentWidgetElement {
isLoading: boolean;
}
type IsLoadingFn = (isLoading: boolean) => void;
type OnScrollFn = () => void;
/**
* The base class from which all Lit Module classes extends, in order to have
* type safety for dynamically creating modules. Derives from MobxLitElement for
* automatic reactive rendering. Provides a few helper methods for setting up
* explicit mobx reactions with automatic disposal upon component disconnect.
*/
export abstract class LitModule extends ReactiveElement {
/**
* A callback used to set the loading status of the parent widget component.
*/
@property({type: Object}) setIsLoading: IsLoadingFn = (status: boolean) => {};
/**
* A callback used to keep scrolling syncronized between duplicated instances
* of a module. Only used if the class defined by SCROLL_SYNC_CSS_CLASS is
* used in an element in the module. Otherwise scrolling is syncronized using
* the outer container that contains the module.
*/
@observable @property({type: Object}) onSyncScroll: OnScrollFn|null = null;
// Number of columns of the 12 column horizontal layout.
static numCols: number = 4;
// Whether to collapse this module by default.
static collapseByDefault: boolean = false;
// If true, duplicate this module in example comparison mode.
static duplicateForExampleComparison: boolean = false;
// If true, duplicate this module when running with more than one model.
static duplicateForModelComparison: boolean = true;
// If true, duplicate this module as rows, instead of columns.
static duplicateAsRow: boolean = false;
@property({type: String}) model = '';
@observable @property({type: Number}) selectionServiceIndex = 0;
// tslint:disable-next-line:no-any
private readonly latestLoadPromises = new Map<string, Promise<any>>();
protected readonly apiService = app.getService(ApiService);
protected readonly appState = app.getService(AppState);
@computed
protected get selectionService() {
return app.getServiceArray(SelectionService)[this.selectionServiceIndex];
}
updated() {
// If the class defined by SCROLL_SYNC_CSS_CLASS is used in the module then
// set its onscroll callback to be the provided onSyncScroll.
// There is no need to use this class if a module scrolls through the
// normal mechanism of its parent container div from the LitWidget element
// that wraps modules. But if a module doesn't scroll using that parent
// container, but through some element internal to the module, then using
// this class on that element will allow for scrolling to be syncronized
// across duplicated modules of this type.
const scrollElems = this.shadowRoot!.querySelectorAll(
`.${SCROLL_SYNC_CSS_CLASS}`);
scrollElems.forEach(elem => {
(elem as HTMLElement).onscroll = this.onSyncScroll;
});
}
/**
* A helper method for wrapping async API calls in machinery that a)
* automatically sets the loading state of the parent widget container and
* b) ensures that the function only returns the value for the latest async
* call, and null otherwise;
*/
async loadLatest<T>(key: string, promise: Promise<T>): Promise<T|null> {
this.latestLoadPromises.set(key, promise);
this.setIsLoading(true);
const result = await promise;
if (this.latestLoadPromises.get(key) === promise) {
this.setIsLoading(false);
this.latestLoadPromises.delete(key);
return result;
}
return null;
}
}
/**
* A type representing the constructor / class of a LitModule, extended with the
* static properties that need to be defined on a LitModule.
*/
export type LitModuleType = typeof LitModule&LitModuleClass;