@feezal/feezal-element 0.8.0 → 3.0.1
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/LICENSE +21 -0
- package/README.md +82 -0
- package/feezal-conditions.js +262 -0
- package/feezal-element.js +173 -185
- package/feezal-polymer-element.js +326 -0
- package/feezal-topic-input.js +173 -0
- package/package.json +37 -25
- package/dist/sw.js +0 -2
- package/dist/sw.js.map +0 -1
- package/dist/workbox-bacb9a40.js +0 -2
- package/dist/workbox-bacb9a40.js.map +0 -1
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/* global window, feezal */
|
|
2
|
+
|
|
3
|
+
import {PolymerElement, html} from '@polymer/polymer/polymer-element.js';
|
|
4
|
+
import {FeezalConditions} from './feezal-conditions.js';
|
|
5
|
+
|
|
6
|
+
const styleElement = document.createElement('dom-module');
|
|
7
|
+
|
|
8
|
+
styleElement.innerHTML =
|
|
9
|
+
`<template>
|
|
10
|
+
<style>
|
|
11
|
+
:host {
|
|
12
|
+
display: inline-block;
|
|
13
|
+
box-sizing: border-box;
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
}
|
|
16
|
+
:host([hidden]) {
|
|
17
|
+
display: none;
|
|
18
|
+
}
|
|
19
|
+
:host(.feezal-editable) {
|
|
20
|
+
outline: 1px dashed rgba(var(--feezal-selection-rgb, 2,132,199), 0.8);
|
|
21
|
+
}
|
|
22
|
+
:host(.feezal-editable) * {
|
|
23
|
+
pointer-events: none;
|
|
24
|
+
}
|
|
25
|
+
:host(.feezal-editable.feezal-selected) {
|
|
26
|
+
outline: 2px dashed rgba(var(--feezal-selection-rgb, 2,132,199), 0.9);
|
|
27
|
+
}
|
|
28
|
+
:host(.feezal-editable[locked]) {
|
|
29
|
+
outline: 1.5px dashed #f59e0b !important;
|
|
30
|
+
}
|
|
31
|
+
:host(.feezal-editable.feezal-selected[locked]) {
|
|
32
|
+
outline: 2px solid #f59e0b !important;
|
|
33
|
+
}
|
|
34
|
+
</style>
|
|
35
|
+
</template>`;
|
|
36
|
+
|
|
37
|
+
styleElement.register('feezal-style-element');
|
|
38
|
+
|
|
39
|
+
class FeezalPolymerElement extends PolymerElement {
|
|
40
|
+
static get properties() {
|
|
41
|
+
return {
|
|
42
|
+
name: {
|
|
43
|
+
type: String,
|
|
44
|
+
value: '',
|
|
45
|
+
reflectToAttribute: true,
|
|
46
|
+
},
|
|
47
|
+
'selected': {
|
|
48
|
+
type: Boolean,
|
|
49
|
+
value: false
|
|
50
|
+
},
|
|
51
|
+
subscribe: {
|
|
52
|
+
type: String,
|
|
53
|
+
value: '',
|
|
54
|
+
reflectToAttribute: true,
|
|
55
|
+
},
|
|
56
|
+
messageProperty: {
|
|
57
|
+
type: String,
|
|
58
|
+
value: 'payload',
|
|
59
|
+
reflectToAttribute: true,
|
|
60
|
+
},
|
|
61
|
+
'dynamicSubscriptions': {
|
|
62
|
+
type: Boolean,
|
|
63
|
+
value: false,
|
|
64
|
+
reflectToAttribute: true
|
|
65
|
+
},
|
|
66
|
+
'visible': {
|
|
67
|
+
type: Boolean,
|
|
68
|
+
value: false,
|
|
69
|
+
observer: '_visibleChanged'
|
|
70
|
+
},
|
|
71
|
+
// E50: JSON list of condition rows — shared engine, see
|
|
72
|
+
// feezal-conditions.js. The attribute is the source of truth.
|
|
73
|
+
conditions: {
|
|
74
|
+
type: String,
|
|
75
|
+
observer: '_conditionsChanged'
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
static get feezal() {
|
|
80
|
+
return {
|
|
81
|
+
attributes: [],
|
|
82
|
+
styles: []
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
_payloadCast(type, payload) {
|
|
87
|
+
if (typeof payload === 'string') {
|
|
88
|
+
switch (type) {
|
|
89
|
+
case Boolean:
|
|
90
|
+
return Number(payload) !== 0 && payload.toLowerCase() !== 'false';
|
|
91
|
+
default:
|
|
92
|
+
return payload;
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
return payload;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
addSubscription(topic, callback) {
|
|
100
|
+
this._subscriptions.push(feezal.connection.sub(topic, callback));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
_subscribe() {
|
|
104
|
+
if (!this.subscribe) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// In the editor, runtime MQTT manipulation of elements is gated by a user
|
|
109
|
+
// setting (Editor settings → "Prevent MQTT element manipulation in editor",
|
|
110
|
+
// default ON) so live broker values never get written onto elements and
|
|
111
|
+
// serialized into the saved view.
|
|
112
|
+
if (feezal.isEditor && feezal.preventEditorMqtt !== false) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const base = this.subscribe;
|
|
117
|
+
const elemClass = window.customElements.get(this.localName);
|
|
118
|
+
|
|
119
|
+
// Primary state topic → baseAttribute (exact topic, no wildcard).
|
|
120
|
+
const baseAttribute = elemClass && elemClass.feezal && elemClass.feezal.baseAttribute;
|
|
121
|
+
if (baseAttribute) {
|
|
122
|
+
this._subscriptions.push(feezal.connection.sub(base, msg => {
|
|
123
|
+
const type = (elemClass.properties[baseAttribute] || {}).type;
|
|
124
|
+
const val = this._payloadCast(type, this.getProperty(msg, this.messageProperty));
|
|
125
|
+
if (type === Boolean && !val) {
|
|
126
|
+
this.removeAttribute(baseAttribute);
|
|
127
|
+
} else {
|
|
128
|
+
this.setAttribute(baseAttribute, val);
|
|
129
|
+
}
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Reserved runtime-control channel — distinct, exact topics so device
|
|
134
|
+
// telemetry sharing the base topic can never reach the element.
|
|
135
|
+
// Consistent with feezal-view / feezal-site addclass / removeclass.
|
|
136
|
+
this._subscribeControl(base);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Subscribe to the reserved <base>/{setattribute,removeattribute,setstyle,removestyle,addclass,removeclass} control topics. */
|
|
140
|
+
_subscribeControl(base) {
|
|
141
|
+
const sub = (suffix, cb) => this._subscriptions.push(feezal.connection.sub(base + '/' + suffix, cb));
|
|
142
|
+
const val = msg => this.getProperty(msg, this.messageProperty);
|
|
143
|
+
const list = p => (Array.isArray(p) ? p : String(p).split(/[,\s]+/)).filter(Boolean);
|
|
144
|
+
|
|
145
|
+
sub('setattribute', msg => {
|
|
146
|
+
const obj = val(msg);
|
|
147
|
+
if (obj && typeof obj === 'object') {
|
|
148
|
+
for (const [k, v] of Object.entries(obj)) this.setAttribute(k, String(v));
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
sub('removeattribute', msg => list(val(msg)).forEach(n => this.removeAttribute(n)));
|
|
152
|
+
sub('setstyle', msg => {
|
|
153
|
+
const obj = val(msg);
|
|
154
|
+
if (obj && typeof obj === 'object') Object.assign(this.style, obj);
|
|
155
|
+
});
|
|
156
|
+
sub('removestyle', msg => list(val(msg)).forEach(n => this.style.removeProperty(n)));
|
|
157
|
+
sub('addclass', msg => this.classList.add(val(msg)));
|
|
158
|
+
sub('removeclass', msg => this.classList.remove(val(msg)));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
_unsubscribe() {
|
|
162
|
+
const sub = this._subscriptions.shift();
|
|
163
|
+
if (sub) {
|
|
164
|
+
feezal.connection.unsubscribe(sub);
|
|
165
|
+
this._unsubscribe();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
constructor() {
|
|
170
|
+
super();
|
|
171
|
+
this._subscriptions = [];
|
|
172
|
+
this._conditions = new FeezalConditions(this);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
connectedCallback() {
|
|
176
|
+
super.connectedCallback();
|
|
177
|
+
this.classList.add('feezal-element');
|
|
178
|
+
// Prevent tab-focus on shadow DOM controls while in the editor.
|
|
179
|
+
if (feezal.isEditor && this.shadowRoot) {
|
|
180
|
+
this.shadowRoot.querySelectorAll('*').forEach(el => {
|
|
181
|
+
if (el.hasAttribute('tabindex')) {
|
|
182
|
+
el.addEventListener('focus', event => {
|
|
183
|
+
event.preventDefault();
|
|
184
|
+
if (event.relatedTarget) {
|
|
185
|
+
event.relatedTarget.focus();
|
|
186
|
+
} else {
|
|
187
|
+
event.currentTarget.blur();
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
el.setAttribute('tabindex', '-1');
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (this.visible || !this.dynamicSubscriptions) {
|
|
195
|
+
this._subscribe();
|
|
196
|
+
this._conditions.connect();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
disconnectedCallback() {
|
|
200
|
+
super.disconnectedCallback();
|
|
201
|
+
this._unsubscribe();
|
|
202
|
+
this._conditions.disconnect();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
_visibleChanged(visible) {
|
|
206
|
+
if (!this.dynamicSubscriptions) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (visible) {
|
|
210
|
+
this._subscribe();
|
|
211
|
+
this._conditions.connect();
|
|
212
|
+
} else {
|
|
213
|
+
this._unsubscribe();
|
|
214
|
+
this._conditions.disconnect();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// E50: conditions added/edited/removed at runtime → restart the engine
|
|
219
|
+
// (idempotent for an unchanged attribute value).
|
|
220
|
+
_conditionsChanged() {
|
|
221
|
+
if (this.isConnected && (this.visible || !this.dynamicSubscriptions)) {
|
|
222
|
+
this._conditions.connect();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Utils
|
|
227
|
+
throttle(func, limit) {
|
|
228
|
+
let lastFunc;
|
|
229
|
+
let lastRan;
|
|
230
|
+
return function () {
|
|
231
|
+
const context = this;
|
|
232
|
+
const args = arguments;
|
|
233
|
+
|
|
234
|
+
if (!lastRan) {
|
|
235
|
+
func.apply(context, args);
|
|
236
|
+
lastRan = Date.now();
|
|
237
|
+
} else {
|
|
238
|
+
clearTimeout(lastFunc);
|
|
239
|
+
this._inThrottle = true;
|
|
240
|
+
lastFunc = setTimeout(function () {
|
|
241
|
+
if ((Date.now() - lastRan) >= limit) {
|
|
242
|
+
func.apply(context, args);
|
|
243
|
+
lastRan = Date.now();
|
|
244
|
+
this._inThrottle = false;
|
|
245
|
+
}
|
|
246
|
+
}, limit - (Date.now() - lastRan));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* @method split
|
|
253
|
+
* Split str by '.' - supports backslash escaped delimiters
|
|
254
|
+
* @param {string} str
|
|
255
|
+
* @returns {Array.<string>}
|
|
256
|
+
*/
|
|
257
|
+
split(str) {
|
|
258
|
+
str = String(str);
|
|
259
|
+
|
|
260
|
+
// Use native split if possible
|
|
261
|
+
if (str.indexOf('\\') === -1) {
|
|
262
|
+
return str.split('.');
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
var res = []; // The result array
|
|
266
|
+
var pos = 0; // Starting position of current chunk
|
|
267
|
+
|
|
268
|
+
function chunk(start, end) {
|
|
269
|
+
// Slice, unescape and push onto result array.
|
|
270
|
+
res.push(str.slice(start, end).replace(/\\\\/g, '\\').replace(/\\\./g, '.'));
|
|
271
|
+
// Set starting position of next chunk.
|
|
272
|
+
pos = end + 1;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
var esc; // Boolean indicating if a dot is escaped
|
|
276
|
+
var j;
|
|
277
|
+
var i;
|
|
278
|
+
var l = str.length;
|
|
279
|
+
for (i = 0; i < l; i++) {
|
|
280
|
+
if (str[i] === '.') {
|
|
281
|
+
esc = false;
|
|
282
|
+
// Walk over preceding backslashes in reverse direction
|
|
283
|
+
for (j = i - 1; str[j] === '\\'; j--) {
|
|
284
|
+
esc = !esc;
|
|
285
|
+
}
|
|
286
|
+
// Dot is escaped only if preceded by an odd number of backslashes
|
|
287
|
+
if (!esc) {
|
|
288
|
+
chunk(pos, i);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
chunk(pos, i);
|
|
294
|
+
|
|
295
|
+
return res;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* @method getProperty
|
|
300
|
+
* get an objects property. supports nested properties through dot-notation, dots may be escaped by backslash
|
|
301
|
+
* @param {Object} obj
|
|
302
|
+
* @param {string} prop
|
|
303
|
+
* @returns {all} the properties value or undefined
|
|
304
|
+
*/
|
|
305
|
+
getProperty(obj, prop) {
|
|
306
|
+
var type = typeof obj;
|
|
307
|
+
if (type !== 'object' && type !== 'function') {
|
|
308
|
+
if (typeof prop === 'undefined') {
|
|
309
|
+
return obj;
|
|
310
|
+
}
|
|
311
|
+
return undefined;
|
|
312
|
+
}
|
|
313
|
+
var arr = this.split(String(prop));
|
|
314
|
+
var res = obj;
|
|
315
|
+
for (let i = 0, l = arr.length; i < l; i++) {
|
|
316
|
+
if (res) {
|
|
317
|
+
res = res[arr[i]];
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return res;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
window.customElements.define('feezal-element', FeezalPolymerElement);
|
|
325
|
+
|
|
326
|
+
export {FeezalPolymerElement, html};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* feezal-topic-input — shared MQTT-topic input with server-backed
|
|
3
|
+
* autocompletion (B28).
|
|
4
|
+
*
|
|
5
|
+
* Drop-in replacement for the plain `sl-input` topic fields in custom (N6)
|
|
6
|
+
* inspectors: wraps an sl-input and adds the same completion dropdown as the
|
|
7
|
+
* generic attribute inspector (debounced fetch of /api/topics/completions,
|
|
8
|
+
* arrow-key navigation, Enter to pick, descend on `topic/` intermediate
|
|
9
|
+
* completions, Escape/Tab to dismiss).
|
|
10
|
+
*
|
|
11
|
+
* The inner sl-input's `sl-input`/`sl-change` events bubble composed out of
|
|
12
|
+
* the shadow root retargeted to this element, and `value` is kept in sync —
|
|
13
|
+
* existing inspector handlers keep working unchanged with
|
|
14
|
+
* `@sl-change="${e => ...e.target.value...}"`. Picking a completion fires a
|
|
15
|
+
* synthetic `sl-change`; descending an intermediate `…/` completion fires
|
|
16
|
+
* `sl-input` only.
|
|
17
|
+
*
|
|
18
|
+
* Only usable inside the editor (needs the Shoelace bundle and the feezal
|
|
19
|
+
* server API); dashboards never render it.
|
|
20
|
+
*/
|
|
21
|
+
import {LitElement, html, css} from 'lit';
|
|
22
|
+
|
|
23
|
+
class FeezalTopicInput extends LitElement {
|
|
24
|
+
static properties = {
|
|
25
|
+
label: {type: String},
|
|
26
|
+
value: {type: String},
|
|
27
|
+
placeholder: {type: String},
|
|
28
|
+
size: {type: String},
|
|
29
|
+
_completions: {state: true},
|
|
30
|
+
_cursor: {state: true},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
static styles = css`
|
|
34
|
+
:host { display: block; }
|
|
35
|
+
.topic-wrap { position: relative; }
|
|
36
|
+
sl-input { width: 100%; }
|
|
37
|
+
sl-input::part(form-control-label) { color: var(--sl-input-label-color, inherit); font-size: 12px; }
|
|
38
|
+
sl-input::part(base) {
|
|
39
|
+
background: var(--feezal-bg, #fff);
|
|
40
|
+
border-color: var(--feezal-border, #ccc);
|
|
41
|
+
color: var(--feezal-color, #333);
|
|
42
|
+
}
|
|
43
|
+
sl-input::part(input) { background: var(--feezal-bg, #fff); color: var(--sl-input-color, #333); }
|
|
44
|
+
.completions {
|
|
45
|
+
position: absolute; top: 100%; left: 0; right: 0; z-index: 500;
|
|
46
|
+
list-style: none; margin: 2px 0 0; padding: 4px 0;
|
|
47
|
+
background: var(--feezal-bg, #fff); border: 1px solid var(--feezal-border, #ccc);
|
|
48
|
+
border-radius: 4px; box-shadow: 0 4px 14px rgba(0,0,0,0.18);
|
|
49
|
+
max-height: 180px; overflow-y: auto; font-size: 12px;
|
|
50
|
+
}
|
|
51
|
+
.completions li {
|
|
52
|
+
padding: 4px 10px; cursor: pointer;
|
|
53
|
+
color: var(--feezal-color, #333);
|
|
54
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
55
|
+
}
|
|
56
|
+
.completions li:hover, .completions li.active { background: var(--feezal-bg-sub, #f0f0f0); }
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
constructor() {
|
|
60
|
+
super();
|
|
61
|
+
this.label = '';
|
|
62
|
+
this.value = '';
|
|
63
|
+
this.placeholder = 'mqtt/topic';
|
|
64
|
+
this.size = 'small';
|
|
65
|
+
this._completions = [];
|
|
66
|
+
this._cursor = -1;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
disconnectedCallback() {
|
|
70
|
+
super.disconnectedCallback();
|
|
71
|
+
clearTimeout(this._fetchTimer);
|
|
72
|
+
clearTimeout(this._closeTimer);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
_emit(name) {
|
|
76
|
+
this.dispatchEvent(new CustomEvent(name, {bubbles: true, composed: true}));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
_debounceFetch(prefix) {
|
|
80
|
+
clearTimeout(this._fetchTimer);
|
|
81
|
+
this._fetchTimer = setTimeout(() => this._fetchCompletions(prefix), 150);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async _fetchCompletions(prefix) {
|
|
85
|
+
try {
|
|
86
|
+
const r = await fetch(`/api/topics/completions?prefix=${encodeURIComponent(prefix)}`);
|
|
87
|
+
if (!r.ok) { this._completions = []; return; }
|
|
88
|
+
const {completions} = await r.json();
|
|
89
|
+
this._cursor = -1;
|
|
90
|
+
this._completions = completions || [];
|
|
91
|
+
} catch {
|
|
92
|
+
this._completions = [];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
_close() {
|
|
97
|
+
this._completions = [];
|
|
98
|
+
this._cursor = -1;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
_onInput(e) {
|
|
102
|
+
this.value = e.target.value;
|
|
103
|
+
this._debounceFetch(this.value);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
_onKeydown(e) {
|
|
107
|
+
if (!this._completions.length) return;
|
|
108
|
+
if (e.key === 'ArrowDown') {
|
|
109
|
+
e.preventDefault();
|
|
110
|
+
this._cursor = Math.min(this._cursor + 1, this._completions.length - 1);
|
|
111
|
+
} else if (e.key === 'ArrowUp') {
|
|
112
|
+
e.preventDefault();
|
|
113
|
+
this._cursor = Math.max(this._cursor - 1, -1);
|
|
114
|
+
} else if (e.key === 'Enter' && this._cursor >= 0) {
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
e.stopPropagation();
|
|
117
|
+
this._select(this._completions[this._cursor]);
|
|
118
|
+
} else if (e.key === 'Escape' || e.key === 'Tab') {
|
|
119
|
+
this._close();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
_select(val) {
|
|
124
|
+
this.value = val;
|
|
125
|
+
if (val.endsWith('/')) {
|
|
126
|
+
// Intermediate level — descend and keep the dropdown open.
|
|
127
|
+
this._emit('sl-input');
|
|
128
|
+
this._fetchCompletions(val);
|
|
129
|
+
} else {
|
|
130
|
+
this._emit('sl-input');
|
|
131
|
+
this._emit('sl-change');
|
|
132
|
+
this._close();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
_scheduleClose() {
|
|
137
|
+
clearTimeout(this._closeTimer);
|
|
138
|
+
this._closeTimer = setTimeout(() => this._close(), 200);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
render() {
|
|
142
|
+
return html`
|
|
143
|
+
<div class="topic-wrap">
|
|
144
|
+
<sl-input size="${this.size}" autocomplete="off"
|
|
145
|
+
label="${this.label || ''}"
|
|
146
|
+
placeholder="${this.placeholder}"
|
|
147
|
+
.value="${this.value ?? ''}"
|
|
148
|
+
@sl-focus="${() => this._debounceFetch(this.value ?? '')}"
|
|
149
|
+
@sl-input="${this._onInput}"
|
|
150
|
+
@sl-change="${e => { this.value = e.target.value; }}"
|
|
151
|
+
@sl-blur="${this._scheduleClose}"
|
|
152
|
+
@keydown="${this._onKeydown}"></sl-input>
|
|
153
|
+
${this._completions.length ? html`
|
|
154
|
+
<ul class="completions">
|
|
155
|
+
${this._completions.map((c, ci) => html`
|
|
156
|
+
<li class="${ci === this._cursor ? 'active' : ''}"
|
|
157
|
+
@mousedown="${e => { e.preventDefault(); this._select(c); }}">
|
|
158
|
+
${c}
|
|
159
|
+
</li>
|
|
160
|
+
`)}
|
|
161
|
+
</ul>
|
|
162
|
+
` : ''}
|
|
163
|
+
</div>
|
|
164
|
+
`;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Multiple element bundles may carry a copy (static exports) — guard the define.
|
|
169
|
+
if (!customElements.get('feezal-topic-input')) {
|
|
170
|
+
customElements.define('feezal-topic-input', FeezalTopicInput);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export {FeezalTopicInput};
|
package/package.json
CHANGED
|
@@ -1,27 +1,39 @@
|
|
|
1
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
|
-
|
|
2
|
+
"author": {
|
|
3
|
+
"name": "Sebastian Raff",
|
|
4
|
+
"email": "hobbyquaker@gmail.com"
|
|
5
|
+
},
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/feezal/feezal/issues"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@polymer/polymer": "^3.0.0",
|
|
11
|
+
"lit": "^3.2.0"
|
|
12
|
+
},
|
|
13
|
+
"description": "Base classes for feezal dashboard elements — FeezalElement (Lit 3), FeezalPolymerElement (legacy paper elements) and the shared conditions engine",
|
|
14
|
+
"feezal": {},
|
|
15
|
+
"files": [
|
|
16
|
+
"feezal-element.js",
|
|
17
|
+
"feezal-conditions.js",
|
|
18
|
+
"feezal-polymer-element.js",
|
|
19
|
+
"feezal-topic-input.js",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"homepage": "https://github.com/feezal/feezal#readme",
|
|
23
|
+
"keywords": [
|
|
24
|
+
"feezal",
|
|
25
|
+
"feezal-element"
|
|
26
|
+
],
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"main": "feezal-element.js",
|
|
29
|
+
"name": "@feezal/feezal-element",
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/feezal/feezal.git",
|
|
36
|
+
"directory": "www/packages/@feezal/feezal-element"
|
|
37
|
+
},
|
|
38
|
+
"version": "3.0.1"
|
|
27
39
|
}
|
package/dist/sw.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
if(!self.define){const e=e=>{"require"!==e&&(e+=".js");let r=Promise.resolve();return s[e]||(r=new Promise(async r=>{if("document"in self){const s=document.createElement("script");s.src=e,document.head.appendChild(s),s.onload=r}else importScripts(e),r()})),r.then(()=>{if(!s[e])throw new Error(`Module ${e} didn’t register its module`);return s[e]})},r=(r,s)=>{Promise.all(r.map(e)).then(e=>s(1===e.length?e[0]:e))},s={require:Promise.resolve(r)};self.define=(r,t,o)=>{s[r]||(s[r]=Promise.resolve().then(()=>{let s={};const i={uri:location.origin+r.slice(1)};return Promise.all(t.map(r=>{switch(r){case"exports":return s;case"module":return i;default:return e(r)}})).then(e=>{const r=o(...e);return s.default||(s.default=r),s})}))}}define("./sw.js",["./workbox-bacb9a40"],(function(e){"use strict";e.skipWaiting(),e.clientsClaim(),e.registerRoute("polyfills/*.js",new e.CacheFirst,"GET")}));
|
|
2
|
-
//# sourceMappingURL=sw.js.map
|
package/dist/sw.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sw.js","sources":["../../../../../../../../private/var/folders/13/fybpjr_x5m9484gz_f6cwl780000gn/T/9da39c0c845afbf8c36522c64cb0b57a/sw.js"],"sourcesContent":["import {registerRoute as workbox_routing_registerRoute} from '/Users/basti/WebstormProjects/feezal/node_modules/workbox-routing/registerRoute.mjs';\nimport {CacheFirst as workbox_strategies_CacheFirst} from '/Users/basti/WebstormProjects/feezal/node_modules/workbox-strategies/CacheFirst.mjs';\nimport {skipWaiting as workbox_core_skipWaiting} from '/Users/basti/WebstormProjects/feezal/node_modules/workbox-core/skipWaiting.mjs';\nimport {clientsClaim as workbox_core_clientsClaim} from '/Users/basti/WebstormProjects/feezal/node_modules/workbox-core/clientsClaim.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\n\n\n\n\n\n\nworkbox_core_skipWaiting();\n\nworkbox_core_clientsClaim();\n\n\n\nworkbox_routing_registerRoute(\"polyfills/*.js\", new workbox_strategies_CacheFirst(), 'GET');\n\n\n\n\n"],"names":["workbox_strategies_CacheFirst"],"mappings":"+0BA4B8B,iBAAkB,IAAIA,aAAiC"}
|
package/dist/workbox-bacb9a40.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
define("./workbox-bacb9a40.js",["exports"],(function(e){"use strict";try{self["workbox:core:5.1.4"]&&_()}catch(e){}const t=(e,...t)=>{let s=e;return t.length>0&&(s+=" :: "+JSON.stringify(t)),s};class s extends Error{constructor(e,s){super(t(e,s)),this.name=e,this.details=s}}try{self["workbox:routing:5.1.4"]&&_()}catch(e){}const n=e=>e&&"object"==typeof e?e:{handle:e};class r{constructor(e,t,s="GET"){this.handler=n(t),this.match=e,this.method=s}}class o extends r{constructor(e,t,s){super(({url:t})=>{const s=e.exec(t.href);if(s&&(t.origin===location.origin||0===s.index))return s.slice(1)},t,s)}}const i=e=>new URL(String(e),location.href).href.replace(new RegExp("^"+location.origin),"");class c{constructor(){this.t=new Map}get routes(){return this.t}addFetchListener(){self.addEventListener("fetch",e=>{const{request:t}=e,s=this.handleRequest({request:t,event:e});s&&e.respondWith(s)})}addCacheListener(){self.addEventListener("message",e=>{if(e.data&&"CACHE_URLS"===e.data.type){const{payload:t}=e.data,s=Promise.all(t.urlsToCache.map(e=>{"string"==typeof e&&(e=[e]);const t=new Request(...e);return this.handleRequest({request:t})}));e.waitUntil(s),e.ports&&e.ports[0]&&s.then(()=>e.ports[0].postMessage(!0))}})}handleRequest({request:e,event:t}){const s=new URL(e.url,location.href);if(!s.protocol.startsWith("http"))return;const{params:n,route:r}=this.findMatchingRoute({url:s,request:e,event:t});let o,i=r&&r.handler;if(!i&&this.s&&(i=this.s),i){try{o=i.handle({url:s,request:e,event:t,params:n})}catch(e){o=Promise.reject(e)}return o instanceof Promise&&this.o&&(o=o.catch(n=>this.o.handle({url:s,request:e,event:t}))),o}}findMatchingRoute({url:e,request:t,event:s}){const n=this.t.get(t.method)||[];for(const r of n){let n;const o=r.match({url:e,request:t,event:s});if(o)return n=o,(Array.isArray(o)&&0===o.length||o.constructor===Object&&0===Object.keys(o).length||"boolean"==typeof o)&&(n=void 0),{route:r,params:n}}return{}}setDefaultHandler(e){this.s=n(e)}setCatchHandler(e){this.o=n(e)}registerRoute(e){this.t.has(e.method)||this.t.set(e.method,[]),this.t.get(e.method).push(e)}unregisterRoute(e){if(!this.t.has(e.method))throw new s("unregister-route-but-not-found-with-method",{method:e.method});const t=this.t.get(e.method).indexOf(e);if(!(t>-1))throw new s("unregister-route-route-not-registered");this.t.get(e.method).splice(t,1)}}let a;const u=()=>(a||(a=new c,a.addFetchListener(),a.addCacheListener()),a);const h={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},l=e=>[h.prefix,e,h.suffix].filter(e=>e&&e.length>0).join("-"),f=e=>e||l(h.runtime),p=new Set;const w=(e,t)=>e.filter(e=>t in e),d=async({request:e,mode:t,plugins:s=[]})=>{const n=w(s,"cacheKeyWillBeUsed");let r=e;for(const e of n)r=await e.cacheKeyWillBeUsed.call(e,{mode:t,request:r}),"string"==typeof r&&(r=new Request(r));return r},g=async({cacheName:e,request:t,event:s,matchOptions:n,plugins:r=[]})=>{const o=await self.caches.open(e),i=await d({plugins:r,request:t,mode:"read"});let c=await o.match(i,n);for(const t of r)if("cachedResponseWillBeUsed"in t){const r=t.cachedResponseWillBeUsed;c=await r.call(t,{cacheName:e,event:s,matchOptions:n,cachedResponse:c,request:i})}return c},q=async({cacheName:e,request:t,response:n,event:r,plugins:o=[],matchOptions:c})=>{const a=await d({plugins:o,request:t,mode:"write"});if(!n)throw new s("cache-put-with-no-response",{url:i(a.url)});const u=await(async({request:e,response:t,event:s,plugins:n=[]})=>{let r=t,o=!1;for(const t of n)if("cacheWillUpdate"in t){o=!0;const n=t.cacheWillUpdate;if(r=await n.call(t,{request:e,response:r,event:s}),!r)break}return o||(r=r&&200===r.status?r:void 0),r||null})({event:r,plugins:o,response:n,request:a});if(!u)return;const h=await self.caches.open(e),l=w(o,"cacheDidUpdate"),f=l.length>0?await g({cacheName:e,matchOptions:c,request:a}):null;try{await h.put(a,u)}catch(e){throw"QuotaExceededError"===e.name&&await async function(){for(const e of p)await e()}(),e}for(const t of l)await t.cacheDidUpdate.call(t,{cacheName:e,event:r,oldResponse:f,newResponse:u,request:a})},m=g,y=async({request:e,fetchOptions:t,event:n,plugins:r=[]})=>{if("string"==typeof e&&(e=new Request(e)),n instanceof FetchEvent&&n.preloadResponse){const e=await n.preloadResponse;if(e)return e}const o=w(r,"fetchDidFail"),i=o.length>0?e.clone():null;try{for(const t of r)if("requestWillFetch"in t){const s=t.requestWillFetch,r=e.clone();e=await s.call(t,{request:r,event:n})}}catch(e){throw new s("plugin-error-request-will-fetch",{thrownError:e})}const c=e.clone();try{let s;s="navigate"===e.mode?await fetch(e):await fetch(e,t);for(const e of r)"fetchDidSucceed"in e&&(s=await e.fetchDidSucceed.call(e,{event:n,request:c,response:s}));return s}catch(e){for(const t of o)await t.fetchDidFail.call(t,{error:e,event:n,originalRequest:i.clone(),request:c.clone()});throw e}};try{self["workbox:strategies:5.1.4"]&&_()}catch(e){}e.CacheFirst=class{constructor(e={}){this.i=f(e.cacheName),this.u=e.plugins||[],this.h=e.fetchOptions,this.l=e.matchOptions}async handle({event:e,request:t}){"string"==typeof t&&(t=new Request(t));let n,r=await m({cacheName:this.i,request:t,event:e,matchOptions:this.l,plugins:this.u});if(!r)try{r=await this.p(t,e)}catch(e){n=e}if(!r)throw new s("no-response",{url:t.url,error:n});return r}async p(e,t){const s=await y({request:e,event:t,fetchOptions:this.h,plugins:this.u}),n=s.clone(),r=q({cacheName:this.i,request:e,response:n,event:t,plugins:this.u});if(t)try{t.waitUntil(r)}catch(e){}return s}},e.clientsClaim=function(){self.addEventListener("activate",()=>self.clients.claim())},e.registerRoute=function(e,t,n){let i;if("string"==typeof e){const s=new URL(e,location.href);i=new r(({url:e})=>e.href===s.href,t,n)}else if(e instanceof RegExp)i=new o(e,t,n);else if("function"==typeof e)i=new r(e,t,n);else{if(!(e instanceof r))throw new s("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});i=e}return u().registerRoute(i),i},e.skipWaiting=function(){self.addEventListener("install",()=>self.skipWaiting())}}));
|
|
2
|
-
//# sourceMappingURL=workbox-bacb9a40.js.map
|