UNPKG

10.2 kBJavaScriptView Raw
1var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2 return new (P || (P = Promise))(function (resolve, reject) {
3 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
4 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
5 function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
6 step((generator = generator.apply(thisArg, _arguments || [])).next());
7 });
8};
9/**
10 * Coverts a camelCase string to kebab-case.
11 *
12 * @export
13 * @param {string} str The camelCaseString
14 * @returns {string} The kebab-version of the string
15 */
16export function camelCaseToKebab(str) {
17 return str.replace(/([A-Z])/g, '-$1').toLowerCase();
18}
19/**
20 *
21 * @param {string} prop The name of the property to create
22 * @param {string} attr The name of the attribute
23 * @param {any} context The context of the element
24 * @param {PropConfig} info The configuration of the property
25 */
26export function createProperty(prop, attr, context, info) {
27 // get value that was already set on the property (if any)
28 const setVal = context[prop];
29 Object.defineProperty(context, prop, {
30 get() {
31 return context.__data[prop];
32 },
33 set(val) {
34 return __awaiter(this, void 0, void 0, function* () {
35 const resolved = (val != null && val instanceof Promise
36 ? yield val
37 : val);
38 context.setProperty(prop, resolved);
39 });
40 }
41 });
42 if (info.reflectToAttribute &&
43 (info.type === Object || info.type === Array)) {
44 console.warn('Rich Data shouldn\'t be set as attribte!');
45 }
46 if (info.observer) {
47 if (context[info.observer]) {
48 // Establish the property-change observer
49 context.__methodsToCall[prop] = context[info.observer].bind(context);
50 }
51 else {
52 console.warn(`Method ${info.observer} not defined!`);
53 }
54 }
55 // Check, if the property was already set, set it accordingly
56 if (setVal) {
57 context[prop] = setVal;
58 return;
59 }
60 if (info.value !== undefined) {
61 // Initialize using the included value and the new setter()
62 context[prop] = (typeof (info.value) === 'function'
63 ? info.value.call(context)
64 : info.value);
65 }
66}
67/**
68 * Returns a class with the Lit-Element features, that extends `superclass`.
69 * @param superclass
70 */
71export const LitLite = (superclass = HTMLElement, html, renderFunction) => class extends superclass {
72 constructor() {
73 super();
74 this.__renderCallbacks = new Set();
75 this.__pendingRender = false;
76 this.__data = {};
77 this.__methodsToCall = {};
78 this.__firstRender = false;
79 this.__propAttr = new Map(); // propertyName -> attribute-name
80 this.__attrProp = new Map(); // attribute-name -> propertyName
81 this.attachShadow({ mode: 'open' });
82 for (let prop in this.constructor.properties) {
83 const attr = camelCaseToKebab(prop);
84 this.__propAttr.set(prop, attr);
85 this.__attrProp.set(attr, prop);
86 }
87 }
88 static get observedAttributes() {
89 let attrs = [];
90 for (const prop in this.properties) {
91 if (this.properties[prop].reflectToAttribute) {
92 attrs.push(camelCaseToKebab(prop));
93 }
94 }
95 return attrs;
96 }
97 connectedCallback() {
98 const props = this.constructor.properties;
99 this.__wait = true;
100 for (let prop in props) {
101 if (typeof props[prop] === 'function')
102 props[prop] = { type: props[prop] };
103 this.__makeGetterSetter(prop, props[prop]);
104 }
105 delete this.__wait;
106 this.__firstRender = true;
107 if (this.connected)
108 this.connected.call(this);
109 /* Perform the first render after connection immediately
110 * without the delay of refresh()
111 */
112 this.postponedRender();
113 }
114 disconnectedCallback() {
115 if (this.disconnected)
116 this.disconnected.call(this);
117 }
118 /**
119 * Creates the Propertyaccessors for the defined properties of the Element.
120 * @param {string} prop
121 * @param {PropConfig} info
122 */
123 __makeGetterSetter(prop, info) {
124 const attr = this.__propAttr.get(prop);
125 createProperty(prop, attr, this, info);
126 }
127 /**
128 * Gets called when the properties change and the Element should rerender.
129 *
130 * @param {string} prop
131 * @param {any} newVal
132 */
133 __propertiesChanged(prop, newVal) {
134 if (this.__data[prop] !== newVal) {
135 const oldVal = this.__data[prop];
136 let doRefresh = true;
137 this.__data[prop] = newVal;
138 if (this.__methodsToCall[prop]) {
139 if (this.__methodsToCall[prop](newVal, oldVal) === false) {
140 doRefresh = false;
141 }
142 }
143 if (doRefresh) {
144 this.refresh();
145 }
146 }
147 }
148 setProperty(prop, newVal) {
149 const info = this.constructor.properties[prop];
150 const attr = this.__propAttr.get(prop);
151 if (info.reflectToAttribute) {
152 /* Set the new value by setting the observed attribute.
153 * This will trigger attributeChangedCallback() which will
154 * convert the attribute data to a property,
155 * (this.__data[prop]) and trigger __propertiesChanged().
156 */
157 this.setAttribute(attr, newVal);
158 }
159 else {
160 /* Set the property directly and trigger
161 * __propertiesChanged()
162 */
163 this.__propertiesChanged(prop, newVal);
164 }
165 if (info.notify) {
166 this.dispatchEvent(new CustomEvent(`${attr}-changed`, {
167 bubbles: true,
168 composed: true,
169 detail: newVal
170 }));
171 }
172 }
173 /**
174 * Gets called when an observed attribute changes. Calls `__propertiesChanged`
175 *
176 * @param {string} prop
177 * @param {any} old
178 * @param {any} val
179 */
180 attributeChangedCallback(attr, old, val) {
181 if (old === val)
182 return;
183 const prop = this.__attrProp.get(attr);
184 if (this.__data[prop] !== val) {
185 const { type } = this.constructor.properties[prop];
186 let newVal = val;
187 switch (type.name) {
188 case 'Boolean':
189 /* Ensure attribute values the indicate that absense of the
190 * attribute actually cause the attribute to be absent.
191 */
192 if (val === 'false' || val === 'null' ||
193 val === 'undefined' ||
194 val === false || val === null) {
195 this.removeAttribute(attr);
196 newVal = false;
197 }
198 else {
199 newVal = this.hasAttribute(attr);
200 if (newVal)
201 this.setAttribute(attr, '');
202 }
203 break;
204 case 'String':
205 /* If a String value is falsey or the explicit 'null'
206 * or 'undefined' string, ensure that the attribute is
207 * removed.
208 */
209 if (!val || val === 'null' || val === 'undefined') {
210 this.removeAttribute(attr);
211 newVal = '';
212 }
213 else {
214 newVal = type(val);
215 }
216 break;
217 default:
218 newVal = type(val);
219 break;
220 }
221 /* Pass along the new, more concrete *property* value instead of
222 * the fuzzy attribute value.
223 */
224 this.__propertiesChanged(prop, newVal);
225 }
226 }
227 /**
228 * Handle a postponed render.
229 * @method postponedRender
230 *
231 * @return void
232 */
233 postponedRender() {
234 renderFunction(this.render(Object.assign({}, this.__data)), this.shadowRoot);
235 for (let callback of this.__renderCallbacks) {
236 callback();
237 }
238 this.__renderCallbacks.clear();
239 if (this.afterRender) {
240 this.afterRender(this._firstRender);
241 this._firstRender = false;
242 }
243 }
244 /**
245 * Refresh this element, re-rendering.
246 * @method refresh
247 * @param {function} callback
248 *
249 * @return void
250 */
251 refresh(callback) {
252 return __awaiter(this, void 0, void 0, function* () {
253 if (this._wait === true) {
254 return;
255 }
256 if (callback != null) {
257 // Queue this render/refresh callback
258 this.__renderCallbacks.add(callback);
259 }
260 if (!this.__pendingRender) {
261 this.__pendingRender = true;
262 /* Schedule the following as a microtask, which runs before
263 * requestAnimationFrame. Any additional refresh() calls
264 * will have any callback queued but otherwise will be
265 * ignored.
266 *
267 * https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
268 */
269 this.__pendingRender = yield false;
270 this.postponedRender();
271 }
272 });
273 }
274 /**
275 * Returns what lit-html should render.
276 *
277 * @returns
278 */
279 render(data) {
280 return html ``;
281 }
282 /**
283 * Gets all children with ids.
284 *
285 * @readonly
286 */
287 get $() {
288 const arr = this.shadowRoot.querySelectorAll('[id]');
289 const obj = {};
290 for (const el of arr) {
291 obj[el.id] = el;
292 }
293 return obj;
294 }
295};
296//# sourceMappingURL=lit-lite.js.map
\No newline at end of file