@lingxia/bridge 0.5.0 → 0.5.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/bridge-runtime.es2020.js +1 -0
- package/dist/bridge-runtime.es5.js +1 -0
- package/dist/es2020/boot.js +34 -0
- package/dist/es2020/bridge.js +1574 -0
- package/dist/es2020/error.js +89 -0
- package/dist/es2020/image-proxy.js +82 -0
- package/dist/es2020/index.js +18 -0
- package/dist/es2020/invocation.js +57 -0
- package/dist/es2020/nativecomponents/coverage-monitor.js +159 -0
- package/dist/es2020/runtime-env.js +31 -0
- package/dist/es2020/types.js +16 -0
- package/dist/es2020/ui/action-sheet.js +138 -0
- package/dist/es2020/ui/index.js +10 -0
- package/dist/es2020/ui/modal.js +170 -0
- package/dist/es2020/ui/toast.js +145 -0
- package/dist/es5/boot.js +34 -0
- package/dist/es5/bridge.js +1852 -0
- package/dist/es5/error.js +35 -0
- package/dist/es5/image-proxy.js +83 -0
- package/dist/es5/index.js +18 -0
- package/dist/es5/invocation.js +76 -0
- package/dist/es5/nativecomponents/coverage-monitor.js +219 -0
- package/dist/es5/runtime-env.js +32 -0
- package/dist/es5/types.js +16 -0
- package/dist/es5/ui/action-sheet.js +82 -0
- package/dist/es5/ui/index.js +10 -0
- package/dist/es5/ui/modal.js +84 -0
- package/dist/es5/ui/toast.js +102 -0
- package/dist/invocation.d.ts +12 -0
- package/dist/types.d.ts +1 -0
- package/package.json +21 -9
- package/dist/runtime.es2020.js +0 -1663
- package/dist/runtime.es5.js +0 -1
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
const ANIMATION_MS = 200;
|
|
2
|
+
const STYLE_ID = 'lx-modal-style';
|
|
3
|
+
let activeModal = null;
|
|
4
|
+
function ensureModalStyle() {
|
|
5
|
+
if (document.getElementById(STYLE_ID))
|
|
6
|
+
return;
|
|
7
|
+
const style = document.createElement('style');
|
|
8
|
+
style.id = STYLE_ID;
|
|
9
|
+
style.textContent = `
|
|
10
|
+
.lx-modal-backdrop {
|
|
11
|
+
position: fixed; inset: 0; z-index: 99999;
|
|
12
|
+
background: rgba(0,0,0,0.4);
|
|
13
|
+
opacity: 0; transition: opacity ${ANIMATION_MS}ms ease;
|
|
14
|
+
display: flex; align-items: center; justify-content: center;
|
|
15
|
+
}
|
|
16
|
+
.lx-modal-backdrop.lx-modal-visible { opacity: 1; }
|
|
17
|
+
|
|
18
|
+
.lx-modal-dialog {
|
|
19
|
+
width: min(92vw, 320px);
|
|
20
|
+
background: #fff;
|
|
21
|
+
border-radius: 14px;
|
|
22
|
+
overflow: hidden;
|
|
23
|
+
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', Roboto, sans-serif;
|
|
24
|
+
-webkit-font-smoothing: antialiased;
|
|
25
|
+
transform: scale(0.95); opacity: 0;
|
|
26
|
+
transition: transform ${ANIMATION_MS}ms ease, opacity ${ANIMATION_MS}ms ease;
|
|
27
|
+
}
|
|
28
|
+
.lx-modal-backdrop.lx-modal-visible .lx-modal-dialog {
|
|
29
|
+
transform: scale(1); opacity: 1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.lx-modal-body {
|
|
33
|
+
padding: 24px 20px 16px;
|
|
34
|
+
text-align: center;
|
|
35
|
+
}
|
|
36
|
+
.lx-modal-title {
|
|
37
|
+
margin: 0 0 8px;
|
|
38
|
+
font-size: 17px;
|
|
39
|
+
font-weight: 600;
|
|
40
|
+
color: #000;
|
|
41
|
+
line-height: 1.35;
|
|
42
|
+
}
|
|
43
|
+
.lx-modal-content {
|
|
44
|
+
margin: 0;
|
|
45
|
+
font-size: 14px;
|
|
46
|
+
color: #666;
|
|
47
|
+
line-height: 1.5;
|
|
48
|
+
word-break: break-word;
|
|
49
|
+
}
|
|
50
|
+
.lx-modal-body:has(.lx-modal-title:empty) .lx-modal-content {
|
|
51
|
+
font-size: 17px;
|
|
52
|
+
font-weight: 600;
|
|
53
|
+
color: #000;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.lx-modal-footer {
|
|
57
|
+
display: flex;
|
|
58
|
+
border-top: 1px solid #E0E0E0;
|
|
59
|
+
}
|
|
60
|
+
.lx-modal-btn {
|
|
61
|
+
flex: 1;
|
|
62
|
+
height: 44px;
|
|
63
|
+
line-height: 44px;
|
|
64
|
+
padding: 0;
|
|
65
|
+
border: none;
|
|
66
|
+
background: none;
|
|
67
|
+
font-size: 17px;
|
|
68
|
+
text-align: center;
|
|
69
|
+
cursor: pointer;
|
|
70
|
+
-webkit-tap-highlight-color: transparent;
|
|
71
|
+
}
|
|
72
|
+
.lx-modal-btn:active { background: rgba(0,0,0,0.06); }
|
|
73
|
+
.lx-modal-btn + .lx-modal-btn {
|
|
74
|
+
border-left: 1px solid #E0E0E0;
|
|
75
|
+
}
|
|
76
|
+
.lx-modal-btn-cancel {
|
|
77
|
+
color: #666;
|
|
78
|
+
font-weight: 400;
|
|
79
|
+
}
|
|
80
|
+
.lx-modal-btn-confirm {
|
|
81
|
+
font-weight: 600;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@media (prefers-color-scheme: dark) {
|
|
85
|
+
.lx-modal-dialog { background: #2c2c2e; }
|
|
86
|
+
.lx-modal-title { color: #f5f5f7; }
|
|
87
|
+
.lx-modal-content { color: #ababab; }
|
|
88
|
+
.lx-modal-body:has(.lx-modal-title:empty) .lx-modal-content {
|
|
89
|
+
color: #f5f5f7;
|
|
90
|
+
}
|
|
91
|
+
.lx-modal-footer { border-top-color: #3a3a3c; }
|
|
92
|
+
.lx-modal-btn:active { background: rgba(255,255,255,0.08); }
|
|
93
|
+
.lx-modal-btn + .lx-modal-btn { border-left-color: #3a3a3c; }
|
|
94
|
+
.lx-modal-btn-cancel { color: #ababab; }
|
|
95
|
+
}
|
|
96
|
+
`;
|
|
97
|
+
document.head.appendChild(style);
|
|
98
|
+
}
|
|
99
|
+
export function showModal(params) {
|
|
100
|
+
const p = params;
|
|
101
|
+
const title = p?.title ?? '';
|
|
102
|
+
const content = p?.content ?? '';
|
|
103
|
+
const showCancel = p?.showCancel ?? true;
|
|
104
|
+
const cancelText = p?.cancelText ?? 'Cancel';
|
|
105
|
+
const cancelColor = p?.cancelColor;
|
|
106
|
+
const confirmText = p?.confirmText ?? 'OK';
|
|
107
|
+
const confirmColor = p?.confirmColor ?? '#007AFF';
|
|
108
|
+
const task = (activeModal ?? Promise.resolve()).then(() => renderModal(title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor), () => renderModal(title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor));
|
|
109
|
+
activeModal = task;
|
|
110
|
+
return task;
|
|
111
|
+
}
|
|
112
|
+
function renderModal(title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor) {
|
|
113
|
+
return new Promise((resolve) => {
|
|
114
|
+
let dismissed = false;
|
|
115
|
+
ensureModalStyle();
|
|
116
|
+
const backdrop = document.createElement('div');
|
|
117
|
+
backdrop.className = 'lx-modal-backdrop';
|
|
118
|
+
const dialog = document.createElement('div');
|
|
119
|
+
dialog.className = 'lx-modal-dialog';
|
|
120
|
+
// Body
|
|
121
|
+
const body = document.createElement('div');
|
|
122
|
+
body.className = 'lx-modal-body';
|
|
123
|
+
const titleEl = document.createElement('div');
|
|
124
|
+
titleEl.className = 'lx-modal-title';
|
|
125
|
+
titleEl.textContent = title;
|
|
126
|
+
body.appendChild(titleEl);
|
|
127
|
+
if (content) {
|
|
128
|
+
const contentEl = document.createElement('p');
|
|
129
|
+
contentEl.className = 'lx-modal-content';
|
|
130
|
+
contentEl.textContent = content;
|
|
131
|
+
body.appendChild(contentEl);
|
|
132
|
+
}
|
|
133
|
+
dialog.appendChild(body);
|
|
134
|
+
// Footer
|
|
135
|
+
const footer = document.createElement('div');
|
|
136
|
+
footer.className = 'lx-modal-footer';
|
|
137
|
+
if (showCancel) {
|
|
138
|
+
const cancelBtn = document.createElement('button');
|
|
139
|
+
cancelBtn.className = 'lx-modal-btn lx-modal-btn-cancel';
|
|
140
|
+
cancelBtn.textContent = cancelText;
|
|
141
|
+
if (cancelColor)
|
|
142
|
+
cancelBtn.style.color = cancelColor;
|
|
143
|
+
cancelBtn.addEventListener('click', () => dismiss(false));
|
|
144
|
+
footer.appendChild(cancelBtn);
|
|
145
|
+
}
|
|
146
|
+
const confirmBtn = document.createElement('button');
|
|
147
|
+
confirmBtn.className = 'lx-modal-btn lx-modal-btn-confirm';
|
|
148
|
+
confirmBtn.textContent = confirmText;
|
|
149
|
+
confirmBtn.style.color = confirmColor;
|
|
150
|
+
confirmBtn.addEventListener('click', () => dismiss(true));
|
|
151
|
+
footer.appendChild(confirmBtn);
|
|
152
|
+
dialog.appendChild(footer);
|
|
153
|
+
backdrop.appendChild(dialog);
|
|
154
|
+
document.body.appendChild(backdrop);
|
|
155
|
+
// Trigger enter animation
|
|
156
|
+
requestAnimationFrame(() => {
|
|
157
|
+
backdrop.classList.add('lx-modal-visible');
|
|
158
|
+
});
|
|
159
|
+
function dismiss(confirmed) {
|
|
160
|
+
if (dismissed)
|
|
161
|
+
return;
|
|
162
|
+
dismissed = true;
|
|
163
|
+
backdrop.classList.remove('lx-modal-visible');
|
|
164
|
+
setTimeout(() => {
|
|
165
|
+
backdrop.remove();
|
|
166
|
+
resolve({ confirm: confirmed, cancel: !confirmed });
|
|
167
|
+
}, ANIMATION_MS);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
const ANIMATION_MS = 200;
|
|
2
|
+
const STYLE_ID = 'lx-toast-style';
|
|
3
|
+
let currentToast = null;
|
|
4
|
+
const SVG_SUCCESS = `<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="18" cy="18" r="17" stroke="currentColor" stroke-width="2"/><path d="M11 18l5 5 9-9" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
|
5
|
+
const SVG_ERROR = `<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="18" cy="18" r="17" stroke="currentColor" stroke-width="2"/><path d="M13 13l10 10M23 13l-10 10" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/></svg>`;
|
|
6
|
+
const SVG_LOADING = `<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M18 4a14 14 0 0 1 14 14" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><animateTransform attributeName="transform" type="rotate" from="0 18 18" to="360 18 18" dur="0.8s" repeatCount="indefinite"/></path></svg>`;
|
|
7
|
+
function ensureToastStyle() {
|
|
8
|
+
if (document.getElementById(STYLE_ID))
|
|
9
|
+
return;
|
|
10
|
+
const style = document.createElement('style');
|
|
11
|
+
style.id = STYLE_ID;
|
|
12
|
+
style.textContent = `
|
|
13
|
+
.lx-toast-mask {
|
|
14
|
+
position: fixed; inset: 0; z-index: 99998;
|
|
15
|
+
}
|
|
16
|
+
.lx-toast-container {
|
|
17
|
+
position: fixed; left: 0; right: 0; z-index: 99999;
|
|
18
|
+
display: flex; justify-content: center;
|
|
19
|
+
pointer-events: none;
|
|
20
|
+
}
|
|
21
|
+
.lx-toast-container.lx-toast-top { top: 0; padding-top: 80px; }
|
|
22
|
+
.lx-toast-container.lx-toast-center { top: 0; bottom: 0; align-items: center; }
|
|
23
|
+
.lx-toast-container.lx-toast-bottom { bottom: 0; padding-bottom: 80px; }
|
|
24
|
+
|
|
25
|
+
.lx-toast-box {
|
|
26
|
+
display: flex; flex-direction: column; align-items: center; gap: 10px;
|
|
27
|
+
min-width: 120px; max-width: 280px;
|
|
28
|
+
padding: 20px;
|
|
29
|
+
background: rgba(0,0,0,0.8);
|
|
30
|
+
border-radius: 12px;
|
|
31
|
+
color: #fff;
|
|
32
|
+
pointer-events: auto;
|
|
33
|
+
transform: scale(0.8); opacity: 0;
|
|
34
|
+
transition: transform ${ANIMATION_MS}ms ease, opacity ${ANIMATION_MS}ms ease;
|
|
35
|
+
}
|
|
36
|
+
.lx-toast-box.lx-toast-no-icon {
|
|
37
|
+
min-width: auto; min-height: auto;
|
|
38
|
+
padding: 12px 20px;
|
|
39
|
+
}
|
|
40
|
+
.lx-toast-box.lx-toast-visible {
|
|
41
|
+
transform: scale(1); opacity: 1;
|
|
42
|
+
}
|
|
43
|
+
.lx-toast-icon {
|
|
44
|
+
width: 36px; height: 36px;
|
|
45
|
+
color: #fff;
|
|
46
|
+
line-height: 0;
|
|
47
|
+
}
|
|
48
|
+
.lx-toast-title {
|
|
49
|
+
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', Roboto, sans-serif;
|
|
50
|
+
font-size: 16px; font-weight: 500;
|
|
51
|
+
text-align: center;
|
|
52
|
+
line-height: 1.4;
|
|
53
|
+
word-break: break-word;
|
|
54
|
+
-webkit-font-smoothing: antialiased;
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
document.head.appendChild(style);
|
|
58
|
+
}
|
|
59
|
+
export function showToast(params) {
|
|
60
|
+
const p = params;
|
|
61
|
+
const title = p?.title ?? '';
|
|
62
|
+
const icon = p?.icon ?? 'none';
|
|
63
|
+
const duration = p?.duration ?? 1500;
|
|
64
|
+
const mask = p?.mask ?? false;
|
|
65
|
+
const position = p?.position ?? 'center';
|
|
66
|
+
// Hide existing toast first
|
|
67
|
+
if (currentToast) {
|
|
68
|
+
currentToast.dismiss();
|
|
69
|
+
currentToast = null;
|
|
70
|
+
}
|
|
71
|
+
ensureToastStyle();
|
|
72
|
+
// Mask (prevents interaction)
|
|
73
|
+
let maskEl = null;
|
|
74
|
+
if (mask) {
|
|
75
|
+
maskEl = document.createElement('div');
|
|
76
|
+
maskEl.className = 'lx-toast-mask';
|
|
77
|
+
document.body.appendChild(maskEl);
|
|
78
|
+
}
|
|
79
|
+
// Container
|
|
80
|
+
const container = document.createElement('div');
|
|
81
|
+
container.className = 'lx-toast-container';
|
|
82
|
+
if (position === 'top')
|
|
83
|
+
container.classList.add('lx-toast-top');
|
|
84
|
+
else if (position === 'bottom')
|
|
85
|
+
container.classList.add('lx-toast-bottom');
|
|
86
|
+
else
|
|
87
|
+
container.classList.add('lx-toast-center');
|
|
88
|
+
// Toast box
|
|
89
|
+
const box = document.createElement('div');
|
|
90
|
+
box.className = 'lx-toast-box';
|
|
91
|
+
const hasIcon = icon !== 'none';
|
|
92
|
+
if (!hasIcon)
|
|
93
|
+
box.classList.add('lx-toast-no-icon');
|
|
94
|
+
// Icon
|
|
95
|
+
if (hasIcon) {
|
|
96
|
+
const iconEl = document.createElement('div');
|
|
97
|
+
iconEl.className = 'lx-toast-icon';
|
|
98
|
+
if (icon === 'success')
|
|
99
|
+
iconEl.innerHTML = SVG_SUCCESS;
|
|
100
|
+
else if (icon === 'error')
|
|
101
|
+
iconEl.innerHTML = SVG_ERROR;
|
|
102
|
+
else if (icon === 'loading')
|
|
103
|
+
iconEl.innerHTML = SVG_LOADING;
|
|
104
|
+
box.appendChild(iconEl);
|
|
105
|
+
}
|
|
106
|
+
// Title
|
|
107
|
+
const titleEl = document.createElement('div');
|
|
108
|
+
titleEl.className = 'lx-toast-title';
|
|
109
|
+
titleEl.textContent = title;
|
|
110
|
+
box.appendChild(titleEl);
|
|
111
|
+
container.appendChild(box);
|
|
112
|
+
document.body.appendChild(container);
|
|
113
|
+
// Animate in
|
|
114
|
+
requestAnimationFrame(() => {
|
|
115
|
+
box.classList.add('lx-toast-visible');
|
|
116
|
+
});
|
|
117
|
+
let dismissed = false;
|
|
118
|
+
function dismiss() {
|
|
119
|
+
if (dismissed)
|
|
120
|
+
return;
|
|
121
|
+
dismissed = true;
|
|
122
|
+
if (currentToast?.dismiss === dismiss)
|
|
123
|
+
currentToast = null;
|
|
124
|
+
box.classList.remove('lx-toast-visible');
|
|
125
|
+
if (timer)
|
|
126
|
+
clearTimeout(timer);
|
|
127
|
+
setTimeout(() => {
|
|
128
|
+
container.remove();
|
|
129
|
+
maskEl?.remove();
|
|
130
|
+
}, ANIMATION_MS);
|
|
131
|
+
}
|
|
132
|
+
let timer = null;
|
|
133
|
+
if (duration > 0) {
|
|
134
|
+
timer = setTimeout(dismiss, duration);
|
|
135
|
+
}
|
|
136
|
+
currentToast = { dismiss, timer };
|
|
137
|
+
return Promise.resolve();
|
|
138
|
+
}
|
|
139
|
+
export function hideToast() {
|
|
140
|
+
if (currentToast) {
|
|
141
|
+
currentToast.dismiss();
|
|
142
|
+
currentToast = null;
|
|
143
|
+
}
|
|
144
|
+
return Promise.resolve();
|
|
145
|
+
}
|
package/dist/es5/boot.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { initBridge } from './bridge';
|
|
2
|
+
import { hasError, getErrorInfo, renderErrorUI } from './error';
|
|
3
|
+
var domReadyListener = {
|
|
4
|
+
handleEvent: function () { return maybeRenderErrorUI(); },
|
|
5
|
+
};
|
|
6
|
+
function maybeRenderErrorUI() {
|
|
7
|
+
if (hasError()) {
|
|
8
|
+
var errorInfo = getErrorInfo();
|
|
9
|
+
if (errorInfo) {
|
|
10
|
+
renderErrorUI(errorInfo);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Immediately initialize bridge and render error UI if present.
|
|
16
|
+
* Use this when DOM is already ready.
|
|
17
|
+
*/
|
|
18
|
+
export function boot() {
|
|
19
|
+
initBridge();
|
|
20
|
+
maybeRenderErrorUI();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Initialize bridge immediately, but wait for DOM to render error UI.
|
|
24
|
+
* Use this at page load time.
|
|
25
|
+
*/
|
|
26
|
+
export function bootWhenReady() {
|
|
27
|
+
initBridge();
|
|
28
|
+
if (document.readyState === 'loading') {
|
|
29
|
+
document.addEventListener('DOMContentLoaded', domReadyListener);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
maybeRenderErrorUI();
|
|
33
|
+
}
|
|
34
|
+
}
|