@lingxia/elements 0.9.0 → 0.11.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/dist/index.d.ts +0 -3
- package/dist/index.js +0 -3
- package/dist/platform.d.ts +1 -0
- package/dist/platform.js +4 -0
- package/dist/video.d.ts +5 -0
- package/dist/video.js +105 -1
- package/package.json +1 -1
- package/src/index.ts +0 -20
- package/src/platform.ts +4 -0
- package/src/video.ts +102 -1
- package/dist/input.d.ts +0 -117
- package/dist/input.js +0 -699
- package/dist/text_component_native_attrs.d.ts +0 -53
- package/dist/text_component_native_attrs.js +0 -111
- package/dist/text_component_shared.d.ts +0 -7
- package/dist/text_component_shared.js +0 -148
- package/dist/textarea.d.ts +0 -129
- package/dist/textarea.js +0 -841
- package/src/input.ts +0 -799
- package/src/text_component_native_attrs.ts +0 -137
- package/src/text_component_shared.ts +0 -147
- package/src/textarea.ts +0 -959
package/dist/input.js
DELETED
|
@@ -1,699 +0,0 @@
|
|
|
1
|
-
import { sendNativeComponentMessage, registerNativeComponentHandler, } from "./nativecomponent.js";
|
|
2
|
-
import { ensureComponentId, NativeComponentUpdateState } from "./component.js";
|
|
3
|
-
import { measureElement } from "./dom.js";
|
|
4
|
-
import { isHarmony, isDesktop, isMacOS, isAndroid, isIOS } from "./platform.js";
|
|
5
|
-
import { getPropOrAttr as getSharedPropOrAttr, getBoolAttr as getSharedBoolAttr, getNumAttr as getSharedNumAttr, shouldRefreshForDataAttribute as shouldRefreshSharedDataAttribute, collectDataset as collectSharedDataset, } from "./text_component_shared.js";
|
|
6
|
-
const HARMONY_PROPS_PREFIX = "data:application/json,";
|
|
7
|
-
// Component implementation
|
|
8
|
-
export class LxInputElement extends HTMLElement {
|
|
9
|
-
constructor() {
|
|
10
|
-
super(...arguments);
|
|
11
|
-
this.componentId = null;
|
|
12
|
-
this.mounted = false;
|
|
13
|
-
this._handlers = {};
|
|
14
|
-
this.rawHandlers = {};
|
|
15
|
-
this.pendingHarmonyRetryTimer = null;
|
|
16
|
-
this.pendingHarmonyRetryCount = 0;
|
|
17
|
-
this.updateState = new NativeComponentUpdateState();
|
|
18
|
-
this.pendingLayoutFrame = null;
|
|
19
|
-
this.focusLayoutSyncTimer = null;
|
|
20
|
-
this.layoutEventListener = {
|
|
21
|
-
handleEvent: () => this.schedulePositionSync()
|
|
22
|
-
};
|
|
23
|
-
this._pageBindings = {};
|
|
24
|
-
}
|
|
25
|
-
static get observedAttributes() {
|
|
26
|
-
return [
|
|
27
|
-
"id", "value", "type", "placeholder", "placeholder-style",
|
|
28
|
-
"placeholder-class", "password", "maxlength", "cursor-spacing",
|
|
29
|
-
"auto-focus", "disabled", "focus", "confirm-type", "always-embed",
|
|
30
|
-
"confirm-hold", "cursor", "cursor-color", "selection-start",
|
|
31
|
-
"selection-end", "adjust-position", "hold-keyboard"
|
|
32
|
-
];
|
|
33
|
-
}
|
|
34
|
-
set pageBindings(bindings) {
|
|
35
|
-
this._pageBindings = bindings !== null && bindings !== void 0 ? bindings : {};
|
|
36
|
-
if (this.isConnected) {
|
|
37
|
-
this.mountInput();
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
get pageBindings() {
|
|
41
|
-
return this._pageBindings;
|
|
42
|
-
}
|
|
43
|
-
upgradeProperty(propName) {
|
|
44
|
-
const self = this;
|
|
45
|
-
if (!Object.prototype.hasOwnProperty.call(self, propName))
|
|
46
|
-
return;
|
|
47
|
-
const value = self[propName];
|
|
48
|
-
delete self[propName];
|
|
49
|
-
self[propName] = value;
|
|
50
|
-
}
|
|
51
|
-
registerNativeHandler() {
|
|
52
|
-
if (!this.componentId)
|
|
53
|
-
return;
|
|
54
|
-
if (this.unregister) {
|
|
55
|
-
this.unregister();
|
|
56
|
-
this.unregister = undefined;
|
|
57
|
-
}
|
|
58
|
-
this.unregister = registerNativeComponentHandler(this.componentId, (message) => {
|
|
59
|
-
var _a;
|
|
60
|
-
if (typeof message.event === "string") {
|
|
61
|
-
const detail = ((_a = message.detail) !== null && _a !== void 0 ? _a : {});
|
|
62
|
-
if (message.event === "keyboardheightchange") {
|
|
63
|
-
this.resyncHarmonyLayoutAfterFocus();
|
|
64
|
-
}
|
|
65
|
-
else if (message.event === "focus") {
|
|
66
|
-
this.startFocusLayoutSync();
|
|
67
|
-
this.resyncHarmonyLayoutAfterFocus();
|
|
68
|
-
}
|
|
69
|
-
else if (message.event === "blur") {
|
|
70
|
-
this.stopFocusLayoutSync();
|
|
71
|
-
}
|
|
72
|
-
this.dispatchEvent(new CustomEvent(message.event, { detail, bubbles: true }));
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
unmountNativeComponentById(componentId) {
|
|
77
|
-
if (!componentId || isHarmony() || isDesktop() || isAndroid())
|
|
78
|
-
return;
|
|
79
|
-
sendNativeComponentMessage({
|
|
80
|
-
action: "component.unmount",
|
|
81
|
-
id: componentId
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
teardownHarmonyEmbed() {
|
|
85
|
-
if (this.harmonyEmbed && this.contains(this.harmonyEmbed)) {
|
|
86
|
-
this.removeChild(this.harmonyEmbed);
|
|
87
|
-
}
|
|
88
|
-
this.harmonyEmbed = undefined;
|
|
89
|
-
this.lastHarmonyProps = undefined;
|
|
90
|
-
if (this.pendingHarmonyRetryTimer !== null) {
|
|
91
|
-
clearTimeout(this.pendingHarmonyRetryTimer);
|
|
92
|
-
this.pendingHarmonyRetryTimer = null;
|
|
93
|
-
}
|
|
94
|
-
this.pendingHarmonyProps = undefined;
|
|
95
|
-
this.pendingHarmonyRetryCount = 0;
|
|
96
|
-
}
|
|
97
|
-
connectedCallback() {
|
|
98
|
-
this.upgradeProperty("pageBindings");
|
|
99
|
-
this.upgradeProperty("oninput");
|
|
100
|
-
this.upgradeProperty("onchange");
|
|
101
|
-
this.upgradeProperty("onfocus");
|
|
102
|
-
this.upgradeProperty("onblur");
|
|
103
|
-
this.upgradeProperty("onconfirm");
|
|
104
|
-
this.upgradeProperty("onkeyboardheightchange");
|
|
105
|
-
this.upgradeProperty("onnicknamereview");
|
|
106
|
-
this.componentId = ensureComponentId(this, "lx-input", this.componentId);
|
|
107
|
-
if (!this.componentId)
|
|
108
|
-
return;
|
|
109
|
-
this.registerNativeHandler();
|
|
110
|
-
this.startAttrObserver();
|
|
111
|
-
this.startTracking();
|
|
112
|
-
this.mountInput();
|
|
113
|
-
queueMicrotask(() => {
|
|
114
|
-
if (!this.isConnected)
|
|
115
|
-
return;
|
|
116
|
-
this.mountInput();
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
disconnectedCallback() {
|
|
120
|
-
if (this.shadow) {
|
|
121
|
-
this.innerInput = undefined;
|
|
122
|
-
this.shadow = undefined;
|
|
123
|
-
}
|
|
124
|
-
if (this.componentId) {
|
|
125
|
-
this.unmountNativeComponentById(this.componentId);
|
|
126
|
-
}
|
|
127
|
-
if (this.unregister) {
|
|
128
|
-
this.unregister();
|
|
129
|
-
this.unregister = undefined;
|
|
130
|
-
}
|
|
131
|
-
this.teardownHarmonyEmbed();
|
|
132
|
-
this.stopFocusLayoutSync();
|
|
133
|
-
this.stopTracking();
|
|
134
|
-
this.mounted = false;
|
|
135
|
-
this.updateState.reset();
|
|
136
|
-
this.stopAttrObserver();
|
|
137
|
-
Object.keys(this._handlers).forEach(name => {
|
|
138
|
-
this.removeEventListener(name, this._handlers[name]);
|
|
139
|
-
});
|
|
140
|
-
this._handlers = {};
|
|
141
|
-
this.rawHandlers = {};
|
|
142
|
-
}
|
|
143
|
-
attributeChangedCallback(name) {
|
|
144
|
-
if (name === "id") {
|
|
145
|
-
const prev = this.componentId;
|
|
146
|
-
this.componentId = ensureComponentId(this, "lx-input", this.componentId);
|
|
147
|
-
if (prev && this.componentId !== prev) {
|
|
148
|
-
this.mounted = false;
|
|
149
|
-
this.updateState.reset();
|
|
150
|
-
if (this.isConnected) {
|
|
151
|
-
this.unmountNativeComponentById(prev);
|
|
152
|
-
this.teardownHarmonyEmbed();
|
|
153
|
-
this.registerNativeHandler();
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
if (!this.isConnected)
|
|
158
|
-
return;
|
|
159
|
-
this.mountInput();
|
|
160
|
-
}
|
|
161
|
-
getAttr(name, fallback = "") {
|
|
162
|
-
const raw = this.getPropOrAttr(name);
|
|
163
|
-
if (raw === undefined || raw === null)
|
|
164
|
-
return fallback;
|
|
165
|
-
return String(raw);
|
|
166
|
-
}
|
|
167
|
-
getBoolAttr(name) {
|
|
168
|
-
return getSharedBoolAttr(this, name);
|
|
169
|
-
}
|
|
170
|
-
getNumAttr(name) {
|
|
171
|
-
return getSharedNumAttr(this, name);
|
|
172
|
-
}
|
|
173
|
-
getPropOrAttr(name) {
|
|
174
|
-
return getSharedPropOrAttr(this, name);
|
|
175
|
-
}
|
|
176
|
-
shouldTrackNativeLayout() {
|
|
177
|
-
return isIOS() || isHarmony();
|
|
178
|
-
}
|
|
179
|
-
startTracking() {
|
|
180
|
-
var _a, _b;
|
|
181
|
-
if (!this.shouldTrackNativeLayout())
|
|
182
|
-
return;
|
|
183
|
-
window.addEventListener("resize", this.layoutEventListener);
|
|
184
|
-
if (isHarmony()) {
|
|
185
|
-
// Harmony native embed coordinates need continuous sync while page scrolls.
|
|
186
|
-
window.addEventListener("scroll", this.layoutEventListener, { capture: true, passive: true });
|
|
187
|
-
}
|
|
188
|
-
else if (isIOS()) {
|
|
189
|
-
// Match video behavior on iOS: track top-level page scrolling only.
|
|
190
|
-
window.addEventListener("scroll", this.layoutEventListener, { passive: true });
|
|
191
|
-
}
|
|
192
|
-
(_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.addEventListener("resize", this.layoutEventListener);
|
|
193
|
-
(_b = window.visualViewport) === null || _b === void 0 ? void 0 : _b.addEventListener("scroll", this.layoutEventListener);
|
|
194
|
-
this.startSizeObserver();
|
|
195
|
-
this.schedulePositionSync();
|
|
196
|
-
}
|
|
197
|
-
stopTracking() {
|
|
198
|
-
var _a, _b;
|
|
199
|
-
window.removeEventListener("resize", this.layoutEventListener);
|
|
200
|
-
window.removeEventListener("scroll", this.layoutEventListener, true);
|
|
201
|
-
window.removeEventListener("scroll", this.layoutEventListener);
|
|
202
|
-
(_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.removeEventListener("resize", this.layoutEventListener);
|
|
203
|
-
(_b = window.visualViewport) === null || _b === void 0 ? void 0 : _b.removeEventListener("scroll", this.layoutEventListener);
|
|
204
|
-
this.stopSizeObserver();
|
|
205
|
-
if (this.pendingLayoutFrame !== null) {
|
|
206
|
-
cancelAnimationFrame(this.pendingLayoutFrame);
|
|
207
|
-
this.pendingLayoutFrame = null;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
startSizeObserver() {
|
|
211
|
-
if (typeof ResizeObserver === "undefined" || this.resizeObserver)
|
|
212
|
-
return;
|
|
213
|
-
this.resizeObserver = new ResizeObserver((entries) => {
|
|
214
|
-
for (const entry of entries) {
|
|
215
|
-
if (entry.target !== this)
|
|
216
|
-
continue;
|
|
217
|
-
this.schedulePositionSync();
|
|
218
|
-
break;
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
this.resizeObserver.observe(this);
|
|
222
|
-
}
|
|
223
|
-
stopSizeObserver() {
|
|
224
|
-
if (!this.resizeObserver)
|
|
225
|
-
return;
|
|
226
|
-
this.resizeObserver.disconnect();
|
|
227
|
-
this.resizeObserver = undefined;
|
|
228
|
-
}
|
|
229
|
-
schedulePositionSync() {
|
|
230
|
-
if (!this.shouldTrackNativeLayout() || !this.isConnected)
|
|
231
|
-
return;
|
|
232
|
-
if (this.pendingLayoutFrame !== null)
|
|
233
|
-
return;
|
|
234
|
-
this.pendingLayoutFrame = requestAnimationFrame(() => {
|
|
235
|
-
this.pendingLayoutFrame = null;
|
|
236
|
-
if (!this.isConnected)
|
|
237
|
-
return;
|
|
238
|
-
this.mountInput();
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
collectProps() {
|
|
242
|
-
const autoFocus = this.getBoolAttr("auto-focus");
|
|
243
|
-
const password = this.getBoolAttr("password");
|
|
244
|
-
const value = this.getPropOrAttr("value");
|
|
245
|
-
const props = {
|
|
246
|
-
value: value === undefined || value === null ? undefined : String(value),
|
|
247
|
-
type: password ? "password" : this.getAttr("type", "text"),
|
|
248
|
-
password,
|
|
249
|
-
placeholder: this.getAttr("placeholder"),
|
|
250
|
-
placeholderStyle: this.getAttr("placeholder-style"),
|
|
251
|
-
// Use string values so "false" survives all bridge layers (some paths may drop falsy booleans).
|
|
252
|
-
focus: (this.getBoolAttr("focus") || autoFocus) ? "true" : "false",
|
|
253
|
-
autoFocus,
|
|
254
|
-
disabled: this.getBoolAttr("disabled"),
|
|
255
|
-
confirmType: this.getAttr("confirm-type", "done"),
|
|
256
|
-
alwaysEmbed: this.getBoolAttr("always-embed"),
|
|
257
|
-
confirmHold: this.getBoolAttr("confirm-hold"),
|
|
258
|
-
adjustPosition: this.getAttribute("adjust-position") !== "false",
|
|
259
|
-
holdKeyboard: this.getBoolAttr("hold-keyboard"),
|
|
260
|
-
};
|
|
261
|
-
const textColor = this.getHostTextColor();
|
|
262
|
-
if (textColor)
|
|
263
|
-
props.textColor = textColor;
|
|
264
|
-
const placeholderClass = this.getAttr("placeholder-class");
|
|
265
|
-
if (placeholderClass)
|
|
266
|
-
props.placeholderClass = placeholderClass;
|
|
267
|
-
const maxlength = this.getNumAttr("maxlength");
|
|
268
|
-
if (maxlength !== undefined)
|
|
269
|
-
props.maxlength = maxlength;
|
|
270
|
-
const cursorSpacing = this.getNumAttr("cursor-spacing");
|
|
271
|
-
if (cursorSpacing !== undefined)
|
|
272
|
-
props.cursorSpacing = cursorSpacing;
|
|
273
|
-
const cursor = this.getNumAttr("cursor");
|
|
274
|
-
if (cursor !== undefined)
|
|
275
|
-
props.cursor = cursor;
|
|
276
|
-
const cursorColor = this.getAttr("cursor-color");
|
|
277
|
-
if (cursorColor)
|
|
278
|
-
props.cursorColor = cursorColor;
|
|
279
|
-
const selStart = this.getNumAttr("selection-start");
|
|
280
|
-
if (selStart !== undefined)
|
|
281
|
-
props.selectionStart = selStart;
|
|
282
|
-
const selEnd = this.getNumAttr("selection-end");
|
|
283
|
-
if (selEnd !== undefined)
|
|
284
|
-
props.selectionEnd = selEnd;
|
|
285
|
-
return props;
|
|
286
|
-
}
|
|
287
|
-
getHostTextColor() {
|
|
288
|
-
var _a;
|
|
289
|
-
const color = (_a = getComputedStyle(this).color) === null || _a === void 0 ? void 0 : _a.trim();
|
|
290
|
-
if (!color)
|
|
291
|
-
return undefined;
|
|
292
|
-
if (color === "transparent" || color === "rgba(0, 0, 0, 0)")
|
|
293
|
-
return undefined;
|
|
294
|
-
return color;
|
|
295
|
-
}
|
|
296
|
-
resyncHarmonyLayoutAfterFocus() {
|
|
297
|
-
if (!isHarmony() || !this.isConnected)
|
|
298
|
-
return;
|
|
299
|
-
this.mountInput();
|
|
300
|
-
setTimeout(() => {
|
|
301
|
-
if (!this.isConnected)
|
|
302
|
-
return;
|
|
303
|
-
this.mountInput();
|
|
304
|
-
}, 32);
|
|
305
|
-
setTimeout(() => {
|
|
306
|
-
if (!this.isConnected)
|
|
307
|
-
return;
|
|
308
|
-
this.mountInput();
|
|
309
|
-
}, 96);
|
|
310
|
-
}
|
|
311
|
-
startFocusLayoutSync() {
|
|
312
|
-
if (!isHarmony())
|
|
313
|
-
return;
|
|
314
|
-
if (this.focusLayoutSyncTimer !== null)
|
|
315
|
-
return;
|
|
316
|
-
this.focusLayoutSyncTimer = window.setInterval(() => {
|
|
317
|
-
if (!this.isConnected) {
|
|
318
|
-
this.stopFocusLayoutSync();
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
this.mountInput();
|
|
322
|
-
}, 80);
|
|
323
|
-
}
|
|
324
|
-
stopFocusLayoutSync() {
|
|
325
|
-
if (this.focusLayoutSyncTimer === null)
|
|
326
|
-
return;
|
|
327
|
-
clearInterval(this.focusLayoutSyncTimer);
|
|
328
|
-
this.focusLayoutSyncTimer = null;
|
|
329
|
-
}
|
|
330
|
-
shouldRefreshForAttribute(name) {
|
|
331
|
-
return shouldRefreshSharedDataAttribute(name);
|
|
332
|
-
}
|
|
333
|
-
startAttrObserver() {
|
|
334
|
-
if (typeof MutationObserver === "undefined" || this.attrObserver)
|
|
335
|
-
return;
|
|
336
|
-
this.attrObserver = new MutationObserver((mutations) => {
|
|
337
|
-
for (const mutation of mutations) {
|
|
338
|
-
const attrName = mutation.attributeName;
|
|
339
|
-
if (!attrName || !this.shouldRefreshForAttribute(attrName))
|
|
340
|
-
continue;
|
|
341
|
-
this.mountInput();
|
|
342
|
-
break;
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
this.attrObserver.observe(this, { attributes: true });
|
|
346
|
-
}
|
|
347
|
-
stopAttrObserver() {
|
|
348
|
-
if (!this.attrObserver)
|
|
349
|
-
return;
|
|
350
|
-
this.attrObserver.disconnect();
|
|
351
|
-
this.attrObserver = undefined;
|
|
352
|
-
}
|
|
353
|
-
collectDataset() {
|
|
354
|
-
return collectSharedDataset(this);
|
|
355
|
-
}
|
|
356
|
-
getInputType() {
|
|
357
|
-
if (this.getBoolAttr("password"))
|
|
358
|
-
return "password";
|
|
359
|
-
const type = this.getAttr("type", "text");
|
|
360
|
-
switch (type) {
|
|
361
|
-
case "digit": return "tel";
|
|
362
|
-
case "number": return "number";
|
|
363
|
-
case "password": return "password";
|
|
364
|
-
case "safe-password": return "password";
|
|
365
|
-
default: return "text";
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
mountDesktopInput() {
|
|
369
|
-
if (!this.shadow) {
|
|
370
|
-
this.shadow = this.attachShadow({ mode: "open" });
|
|
371
|
-
const style = document.createElement("style");
|
|
372
|
-
if (isAndroid() || isMacOS()) {
|
|
373
|
-
// Native app context: page provides visual container; keep the input bare
|
|
374
|
-
style.textContent = `
|
|
375
|
-
:host { display: block; width: 100%; height: 100%; }
|
|
376
|
-
input {
|
|
377
|
-
display: block; width: 100%; height: 100%;
|
|
378
|
-
box-sizing: border-box; padding: 0; margin: 0;
|
|
379
|
-
background: transparent; border: none; outline: none;
|
|
380
|
-
color: inherit; font: inherit;
|
|
381
|
-
}
|
|
382
|
-
`;
|
|
383
|
-
}
|
|
384
|
-
else {
|
|
385
|
-
style.textContent = `
|
|
386
|
-
:host {
|
|
387
|
-
display: block;
|
|
388
|
-
width: 100%;
|
|
389
|
-
}
|
|
390
|
-
input {
|
|
391
|
-
display: block;
|
|
392
|
-
width: 100%;
|
|
393
|
-
box-sizing: border-box;
|
|
394
|
-
padding: 8px 12px;
|
|
395
|
-
border: 1px solid #d1d5db;
|
|
396
|
-
border-radius: 6px;
|
|
397
|
-
font-size: 14px;
|
|
398
|
-
line-height: 1.5;
|
|
399
|
-
color: #111827;
|
|
400
|
-
background: #fff;
|
|
401
|
-
outline: none;
|
|
402
|
-
transition: border-color 0.2s;
|
|
403
|
-
}
|
|
404
|
-
input:focus {
|
|
405
|
-
border-color: #3b82f6;
|
|
406
|
-
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
|
|
407
|
-
}
|
|
408
|
-
input:disabled {
|
|
409
|
-
background: #f3f4f6;
|
|
410
|
-
color: #9ca3af;
|
|
411
|
-
cursor: not-allowed;
|
|
412
|
-
}
|
|
413
|
-
input::placeholder {
|
|
414
|
-
color: #9ca3af;
|
|
415
|
-
}
|
|
416
|
-
`;
|
|
417
|
-
}
|
|
418
|
-
this.shadow.appendChild(style);
|
|
419
|
-
const input = document.createElement("input");
|
|
420
|
-
this.innerInput = input;
|
|
421
|
-
this.shadow.appendChild(input);
|
|
422
|
-
// Stop native composed events from leaking through shadow boundary
|
|
423
|
-
// to avoid double-firing (native retarget + our CustomEvent).
|
|
424
|
-
input.addEventListener("input", (e) => {
|
|
425
|
-
e.stopPropagation();
|
|
426
|
-
const detail = { value: input.value };
|
|
427
|
-
this.dispatchEvent(new CustomEvent("input", { detail, bubbles: true }));
|
|
428
|
-
});
|
|
429
|
-
input.addEventListener("change", (e) => {
|
|
430
|
-
e.stopPropagation();
|
|
431
|
-
const detail = { value: input.value };
|
|
432
|
-
this.dispatchEvent(new CustomEvent("change", { detail, bubbles: true }));
|
|
433
|
-
});
|
|
434
|
-
input.addEventListener("focus", (e) => {
|
|
435
|
-
e.stopPropagation();
|
|
436
|
-
const detail = { value: input.value };
|
|
437
|
-
this.dispatchEvent(new CustomEvent("focus", { detail, bubbles: true }));
|
|
438
|
-
});
|
|
439
|
-
input.addEventListener("blur", (e) => {
|
|
440
|
-
e.stopPropagation();
|
|
441
|
-
const detail = { value: input.value };
|
|
442
|
-
this.dispatchEvent(new CustomEvent("blur", { detail, bubbles: true }));
|
|
443
|
-
});
|
|
444
|
-
input.addEventListener("keydown", (e) => {
|
|
445
|
-
if (e.key === "Enter") {
|
|
446
|
-
const detail = { value: input.value };
|
|
447
|
-
this.dispatchEvent(new CustomEvent("confirm", { detail, bubbles: true }));
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
// Sync attributes to inner input
|
|
452
|
-
const input = this.innerInput;
|
|
453
|
-
const props = this.collectProps();
|
|
454
|
-
input.type = this.getInputType();
|
|
455
|
-
input.placeholder = props.placeholder || "";
|
|
456
|
-
input.disabled = props.disabled;
|
|
457
|
-
if (props.maxlength !== undefined) {
|
|
458
|
-
input.maxLength = props.maxlength;
|
|
459
|
-
}
|
|
460
|
-
else {
|
|
461
|
-
input.removeAttribute("maxlength");
|
|
462
|
-
}
|
|
463
|
-
// Only sync value if it differs (avoid cursor jump)
|
|
464
|
-
if (props.value !== undefined && input.value !== props.value) {
|
|
465
|
-
input.value = props.value;
|
|
466
|
-
}
|
|
467
|
-
// Apply selection range
|
|
468
|
-
if (typeof props.selectionStart === "number" &&
|
|
469
|
-
typeof props.selectionEnd === "number" &&
|
|
470
|
-
props.selectionStart >= 0 &&
|
|
471
|
-
props.selectionEnd >= props.selectionStart) {
|
|
472
|
-
input.setSelectionRange(props.selectionStart, props.selectionEnd);
|
|
473
|
-
}
|
|
474
|
-
// Apply placeholder style (replace on every update so dynamic changes work)
|
|
475
|
-
const placeholderStyle = props.placeholderStyle;
|
|
476
|
-
const styleEl = this.shadow.querySelector("style");
|
|
477
|
-
const marker = "/* placeholder-style */";
|
|
478
|
-
const markerIdx = styleEl.textContent.indexOf(marker);
|
|
479
|
-
if (placeholderStyle) {
|
|
480
|
-
const rule = `\n${marker}\ninput::placeholder { ${placeholderStyle} }`;
|
|
481
|
-
if (markerIdx >= 0) {
|
|
482
|
-
styleEl.textContent = styleEl.textContent.substring(0, markerIdx) + rule;
|
|
483
|
-
}
|
|
484
|
-
else {
|
|
485
|
-
styleEl.textContent += rule;
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
else if (markerIdx >= 0) {
|
|
489
|
-
styleEl.textContent = styleEl.textContent.substring(0, markerIdx);
|
|
490
|
-
}
|
|
491
|
-
if (isAndroid() || isMacOS()) {
|
|
492
|
-
const confirmType = this.getAttr("confirm-type", "done");
|
|
493
|
-
input.setAttribute("enterkeyhint", confirmType);
|
|
494
|
-
}
|
|
495
|
-
if (props.focus === "true" && document.activeElement !== input) {
|
|
496
|
-
input.focus();
|
|
497
|
-
}
|
|
498
|
-
else if (props.focus === "false" && document.activeElement === input) {
|
|
499
|
-
input.blur();
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
mountInput() {
|
|
503
|
-
if (!this.componentId)
|
|
504
|
-
return;
|
|
505
|
-
this.ensureHostLayout();
|
|
506
|
-
const props = this.collectProps();
|
|
507
|
-
const dataset = this.collectDataset();
|
|
508
|
-
props.dataset = dataset;
|
|
509
|
-
props.datasetJson = JSON.stringify(dataset);
|
|
510
|
-
props.pageFuncBindings = this._pageBindings;
|
|
511
|
-
props.pageFuncBindingsJson = JSON.stringify(this._pageBindings);
|
|
512
|
-
const { rect, cornerRadius } = measureElement(this);
|
|
513
|
-
props.__layoutX = rect.x;
|
|
514
|
-
props.__layoutY = rect.y;
|
|
515
|
-
props.__layoutWidth = rect.width;
|
|
516
|
-
props.__layoutHeight = rect.height;
|
|
517
|
-
if (isHarmony()) {
|
|
518
|
-
this.ensureHarmonyEmbed(rect, props, cornerRadius);
|
|
519
|
-
this.mounted = true;
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
if (isDesktop() || isAndroid()) {
|
|
523
|
-
this.mountDesktopInput();
|
|
524
|
-
this.mounted = true;
|
|
525
|
-
return;
|
|
526
|
-
}
|
|
527
|
-
const zIndex = 9999;
|
|
528
|
-
const decision = this.updateState.decide(rect, props, zIndex, !this.mounted);
|
|
529
|
-
if (!decision.shouldSend) {
|
|
530
|
-
this.mounted = true;
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
533
|
-
const payload = {
|
|
534
|
-
action: this.mounted ? "component.update" : "component.mount",
|
|
535
|
-
id: this.componentId,
|
|
536
|
-
type: "input.native",
|
|
537
|
-
rect,
|
|
538
|
-
zIndex
|
|
539
|
-
};
|
|
540
|
-
if (!this.mounted || decision.propsChanged) {
|
|
541
|
-
payload.props = props;
|
|
542
|
-
}
|
|
543
|
-
if (cornerRadius !== undefined) {
|
|
544
|
-
payload.cornerRadius = cornerRadius;
|
|
545
|
-
}
|
|
546
|
-
sendNativeComponentMessage(payload);
|
|
547
|
-
this.mounted = true;
|
|
548
|
-
}
|
|
549
|
-
// Force a full native sync when host frameworks mutate properties without
|
|
550
|
-
// triggering observed attribute callbacks on custom elements.
|
|
551
|
-
syncNativeProps() {
|
|
552
|
-
if (!this.isConnected)
|
|
553
|
-
return;
|
|
554
|
-
this.mountInput();
|
|
555
|
-
}
|
|
556
|
-
ensureHostLayout() {
|
|
557
|
-
const computed = getComputedStyle(this);
|
|
558
|
-
if (computed.display === "inline") {
|
|
559
|
-
this.style.display = "block";
|
|
560
|
-
}
|
|
561
|
-
const height = Number.parseFloat(computed.height);
|
|
562
|
-
if (!Number.isFinite(height) || height <= 1) {
|
|
563
|
-
this.style.height = "40px";
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
// Event handler getter/setter for 'input' event
|
|
567
|
-
set oninput(cb) {
|
|
568
|
-
const raw = cb;
|
|
569
|
-
const current = this._handlers['input'];
|
|
570
|
-
if (current)
|
|
571
|
-
this.removeEventListener('input', current);
|
|
572
|
-
if (typeof raw === "function" ||
|
|
573
|
-
(!!raw && typeof raw === "object" && typeof raw.handleEvent === "function")) {
|
|
574
|
-
const listener = typeof raw === "function"
|
|
575
|
-
? { handleEvent: raw }
|
|
576
|
-
: raw;
|
|
577
|
-
this._handlers['input'] = listener;
|
|
578
|
-
this.rawHandlers['input'] = raw;
|
|
579
|
-
this.addEventListener('input', listener);
|
|
580
|
-
}
|
|
581
|
-
else {
|
|
582
|
-
delete this._handlers['input'];
|
|
583
|
-
delete this.rawHandlers['input'];
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
get oninput() { return this.rawHandlers['input'] || null; }
|
|
587
|
-
ensureHarmonyEmbed(rect, props, cornerRadius) {
|
|
588
|
-
if (!this.harmonyEmbed) {
|
|
589
|
-
const embed = document.createElement("embed");
|
|
590
|
-
embed.setAttribute("type", "native/input");
|
|
591
|
-
embed.setAttribute("width", `${rect.width}`);
|
|
592
|
-
embed.setAttribute("height", `${rect.height}`);
|
|
593
|
-
embed.setAttribute("id", this.componentId);
|
|
594
|
-
embed.setAttribute("data-lx-component-id", this.componentId);
|
|
595
|
-
embed.style.display = "block";
|
|
596
|
-
embed.style.width = "100%";
|
|
597
|
-
embed.style.height = "100%";
|
|
598
|
-
embed.style.border = "none";
|
|
599
|
-
if (cornerRadius !== undefined) {
|
|
600
|
-
embed.style.borderRadius = `${cornerRadius}px`;
|
|
601
|
-
embed.style.overflow = "hidden";
|
|
602
|
-
}
|
|
603
|
-
const encodedProps = this.encodeHarmonyProps(props);
|
|
604
|
-
embed.setAttribute("src", encodedProps);
|
|
605
|
-
this.lastHarmonyProps = encodedProps;
|
|
606
|
-
this.appendChild(embed);
|
|
607
|
-
this.harmonyEmbed = embed;
|
|
608
|
-
this.pendingHarmonyRetryCount = 0;
|
|
609
|
-
return;
|
|
610
|
-
}
|
|
611
|
-
const encodedProps = this.encodeHarmonyProps(props);
|
|
612
|
-
if (encodedProps !== this.lastHarmonyProps) {
|
|
613
|
-
const pushed = this.pushHarmonyPropsUpdate(props);
|
|
614
|
-
if (pushed) {
|
|
615
|
-
this.lastHarmonyProps = encodedProps;
|
|
616
|
-
this.pendingHarmonyProps = undefined;
|
|
617
|
-
this.pendingHarmonyRetryCount = 0;
|
|
618
|
-
}
|
|
619
|
-
else {
|
|
620
|
-
// Fallback to src update path when proxy bridge is unavailable.
|
|
621
|
-
this.harmonyEmbed.setAttribute("src", encodedProps);
|
|
622
|
-
this.lastHarmonyProps = encodedProps;
|
|
623
|
-
this.scheduleHarmonyPropsRetry(props);
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
if (cornerRadius !== undefined && this.harmonyEmbed.style.borderRadius !== `${cornerRadius}px`) {
|
|
627
|
-
this.harmonyEmbed.style.borderRadius = `${cornerRadius}px`;
|
|
628
|
-
this.harmonyEmbed.style.overflow = "hidden";
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
encodeHarmonyProps(props) {
|
|
632
|
-
try {
|
|
633
|
-
const payload = { componentId: this.componentId, ...props };
|
|
634
|
-
return `${HARMONY_PROPS_PREFIX}${encodeURIComponent(JSON.stringify(payload))}`;
|
|
635
|
-
}
|
|
636
|
-
catch (_e) {
|
|
637
|
-
return `${HARMONY_PROPS_PREFIX}%7B%7D`;
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
pushHarmonyPropsUpdate(props) {
|
|
641
|
-
if (!this.componentId || !isHarmony())
|
|
642
|
-
return false;
|
|
643
|
-
try {
|
|
644
|
-
const proxy = window.LingXiaProxy;
|
|
645
|
-
const updateFn = proxy === null || proxy === void 0 ? void 0 : proxy.nativeComponentUpdate;
|
|
646
|
-
if (typeof updateFn !== "function") {
|
|
647
|
-
return false;
|
|
648
|
-
}
|
|
649
|
-
const payload = JSON.stringify({ componentId: this.componentId, ...props });
|
|
650
|
-
try {
|
|
651
|
-
updateFn.call(proxy, payload);
|
|
652
|
-
return true;
|
|
653
|
-
}
|
|
654
|
-
catch {
|
|
655
|
-
try {
|
|
656
|
-
updateFn.call(proxy, this.componentId, payload);
|
|
657
|
-
return true;
|
|
658
|
-
}
|
|
659
|
-
catch {
|
|
660
|
-
return false;
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
catch {
|
|
665
|
-
return false;
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
scheduleHarmonyPropsRetry(props) {
|
|
669
|
-
this.pendingHarmonyProps = { ...props };
|
|
670
|
-
if (this.pendingHarmonyRetryTimer !== null) {
|
|
671
|
-
return;
|
|
672
|
-
}
|
|
673
|
-
this.pendingHarmonyRetryTimer = window.setTimeout(() => {
|
|
674
|
-
this.pendingHarmonyRetryTimer = null;
|
|
675
|
-
if (!this.isConnected || !this.pendingHarmonyProps) {
|
|
676
|
-
return;
|
|
677
|
-
}
|
|
678
|
-
const pendingProps = this.pendingHarmonyProps;
|
|
679
|
-
const pushed = this.pushHarmonyPropsUpdate(pendingProps);
|
|
680
|
-
if (pushed) {
|
|
681
|
-
this.lastHarmonyProps = this.encodeHarmonyProps(pendingProps);
|
|
682
|
-
this.pendingHarmonyProps = undefined;
|
|
683
|
-
this.pendingHarmonyRetryCount = 0;
|
|
684
|
-
}
|
|
685
|
-
else {
|
|
686
|
-
this.pendingHarmonyRetryCount += 1;
|
|
687
|
-
if (this.pendingHarmonyRetryCount < 20) {
|
|
688
|
-
this.scheduleHarmonyPropsRetry(pendingProps);
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
}, 100);
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
// Register the custom element
|
|
695
|
-
export function registerInputComponent() {
|
|
696
|
-
if (typeof window !== "undefined" && !customElements.get("lx-input")) {
|
|
697
|
-
customElements.define("lx-input", LxInputElement);
|
|
698
|
-
}
|
|
699
|
-
}
|