@withl5e/l5e 0.1.0-alpha.0
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 +24 -0
- package/dist/action.js +10 -0
- package/dist/action.js.map +1 -0
- package/dist/client-D67hK4Yy.js +9 -0
- package/dist/client-D67hK4Yy.js.map +1 -0
- package/dist/entry-server-Ckh6zfgm.js +258 -0
- package/dist/entry-server-Ckh6zfgm.js.map +1 -0
- package/dist/entry-server.js +12 -0
- package/dist/entry-server.js.map +1 -0
- package/dist/generateMetadata-C5QsMS-H.js +144 -0
- package/dist/generateMetadata-C5QsMS-H.js.map +1 -0
- package/dist/index-BIt7MJT9.js +163 -0
- package/dist/index-BIt7MJT9.js.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/island/client.js +5 -0
- package/dist/island/client.js.map +1 -0
- package/dist/island/runtime.js +98 -0
- package/dist/island/runtime.js.map +1 -0
- package/dist/island.js +39 -0
- package/dist/island.js.map +1 -0
- package/dist/jsx-runtime-C2Vw67N2.js +256 -0
- package/dist/jsx-runtime-C2Vw67N2.js.map +1 -0
- package/dist/jsx-runtime.js +26 -0
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/middleware.js +9 -0
- package/dist/middleware.js.map +1 -0
- package/dist/seo.js +7 -0
- package/dist/seo.js.map +1 -0
- package/dist/server.js +489 -0
- package/dist/server.js.map +1 -0
- package/dist/swap/server.js +15 -0
- package/dist/swap/server.js.map +1 -0
- package/dist/swap.js +121 -0
- package/dist/swap.js.map +1 -0
- package/dist/tooltip.js +129 -0
- package/dist/tooltip.js.map +1 -0
- package/dist/vite-plugin.js +381 -0
- package/dist/vite-plugin.js.map +1 -0
- package/index.ts +1 -0
- package/package.json +129 -0
- package/src/action/define-action.ts +8 -0
- package/src/action/index.ts +2 -0
- package/src/action/types.ts +21 -0
- package/src/core/bundler.ts +275 -0
- package/src/core/const.ts +2 -0
- package/src/core/entry-server.d.ts +1 -0
- package/src/core/entry-server.ts +381 -0
- package/src/core/exceptions.ts +80 -0
- package/src/core/head-priority.ts +15 -0
- package/src/core/index.ts +40 -0
- package/src/core/jsx-runtime.ts +325 -0
- package/src/core/jsx-types.d.ts +548 -0
- package/src/core/render.ts +181 -0
- package/src/core/request.ts +31 -0
- package/src/core/server.ts +740 -0
- package/src/core/vite-plugin.ts +779 -0
- package/src/island/ClientIsland.ts +71 -0
- package/src/island/client.ts +3 -0
- package/src/island/index.ts +3 -0
- package/src/island/runtime.ts +149 -0
- package/src/island/strategy-registry.ts +10 -0
- package/src/island/types.ts +28 -0
- package/src/middleware/defineMiddleware.ts +5 -0
- package/src/middleware/index.ts +133 -0
- package/src/middleware/sequence.ts +105 -0
- package/src/middleware/types.ts +28 -0
- package/src/seo/generateMetadata.tsx +559 -0
- package/src/seo/index.ts +10 -0
- package/src/seo/mergeMetadata.ts +200 -0
- package/src/seo/types.ts +316 -0
- package/src/swap/SwapResponse.tsx +16 -0
- package/src/swap/create-swap.ts +121 -0
- package/src/swap/index.ts +8 -0
- package/src/swap/parse.ts +12 -0
- package/src/swap/server.ts +1 -0
- package/src/swap/swap.ts +57 -0
- package/src/swap/types.ts +47 -0
- package/src/swap/utils.ts +7 -0
- package/src/tooltip/index.ts +2 -0
- package/src/tooltip/tooltip-loader.ts +108 -0
- package/src/tooltip/tooltip-runtime.ts +173 -0
- package/types.d.ts +14 -0
package/src/swap/swap.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { SwapMode } from './types';
|
|
2
|
+
|
|
3
|
+
export function swap(target: Element, nodes: Node[], mode: SwapMode): Node[] {
|
|
4
|
+
switch (mode) {
|
|
5
|
+
case 'innerHTML':
|
|
6
|
+
target.innerHTML = '';
|
|
7
|
+
const innerInserted: Node[] = [];
|
|
8
|
+
for (const n of nodes) {
|
|
9
|
+
const added = target.appendChild(n);
|
|
10
|
+
innerInserted.push(added);
|
|
11
|
+
}
|
|
12
|
+
return innerInserted;
|
|
13
|
+
|
|
14
|
+
case 'outerHTML': {
|
|
15
|
+
const parent = target.parentNode;
|
|
16
|
+
if (!parent) return [];
|
|
17
|
+
const outerInserted: Node[] = [];
|
|
18
|
+
for (const n of nodes) {
|
|
19
|
+
parent.insertBefore(n, target);
|
|
20
|
+
outerInserted.push(n);
|
|
21
|
+
}
|
|
22
|
+
parent.removeChild(target);
|
|
23
|
+
return outerInserted;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
case 'beforebegin': {
|
|
27
|
+
const parent = target.parentNode;
|
|
28
|
+
if (!parent) return [];
|
|
29
|
+
return nodes.map((n) => parent.insertBefore(n, target));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
case 'afterbegin': {
|
|
33
|
+
const first = target.firstChild;
|
|
34
|
+
return nodes.map((n) => target.insertBefore(n, first));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
case 'beforeend':
|
|
38
|
+
return nodes.map((n) => target.appendChild(n));
|
|
39
|
+
|
|
40
|
+
case 'afterend': {
|
|
41
|
+
const parent = target.parentNode;
|
|
42
|
+
if (!parent) return [];
|
|
43
|
+
const ref = target.nextSibling;
|
|
44
|
+
return nodes.map((n) => parent.insertBefore(n, ref));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
case 'delete':
|
|
48
|
+
target.remove();
|
|
49
|
+
return [];
|
|
50
|
+
|
|
51
|
+
case 'none':
|
|
52
|
+
return [];
|
|
53
|
+
|
|
54
|
+
default:
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type SwapMode =
|
|
2
|
+
| 'innerHTML'
|
|
3
|
+
| 'outerHTML'
|
|
4
|
+
| 'beforebegin'
|
|
5
|
+
| 'afterbegin'
|
|
6
|
+
| 'beforeend'
|
|
7
|
+
| 'afterend'
|
|
8
|
+
| 'delete'
|
|
9
|
+
| 'none';
|
|
10
|
+
|
|
11
|
+
export interface SwapContext {
|
|
12
|
+
triggerElement: HTMLElement | null;
|
|
13
|
+
triggerDataset: DOMStringMap;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SwapOptions {
|
|
17
|
+
action?: (ctx: SwapContext) => Promise<Response>;
|
|
18
|
+
select?: string;
|
|
19
|
+
target: string;
|
|
20
|
+
swap?: SwapMode;
|
|
21
|
+
trigger?: string;
|
|
22
|
+
event?: string;
|
|
23
|
+
loading?: {
|
|
24
|
+
target?: string;
|
|
25
|
+
class?: string;
|
|
26
|
+
html?: string;
|
|
27
|
+
disabled?: boolean;
|
|
28
|
+
};
|
|
29
|
+
onBefore?: (ctx: SwapContext) => boolean | void;
|
|
30
|
+
onNodes?: (nodes: Node[], meta: SwapMeta) => Node[] | void;
|
|
31
|
+
onAfter?: (inserted: Node[], meta: SwapMeta, ctx: SwapContext) => void;
|
|
32
|
+
/**
|
|
33
|
+
* @deprecated Use `onAfter` instead. Will be removed in the next release.
|
|
34
|
+
*/
|
|
35
|
+
onSwap?: (inserted: Node[], meta: SwapMeta) => void;
|
|
36
|
+
onError?: (err: Error, status?: number) => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface SwapInstance {
|
|
40
|
+
exec(input?: HTMLElement | string): Promise<void>;
|
|
41
|
+
destroy(): void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface SwapMeta {
|
|
45
|
+
root: Element | null;
|
|
46
|
+
response?: Response;
|
|
47
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// tooltip-loader.ts
|
|
2
|
+
// File này chỉ làm nhiệm vụ wake up tooltip khi cần thiết
|
|
3
|
+
|
|
4
|
+
// Định nghĩa kiểu cho tooltip host element
|
|
5
|
+
type TooltipHost = HTMLElement & {
|
|
6
|
+
dataset: {
|
|
7
|
+
tooltipId: string;
|
|
8
|
+
tooltipType?: string;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Kiểm tra xem thiết bị hiện tại có phải là thiết bị di động hay không
|
|
14
|
+
* @returns true nếu là thiết bị di động
|
|
15
|
+
*/
|
|
16
|
+
function isMobileDevice(): boolean {
|
|
17
|
+
// Kiểm tra User Agent
|
|
18
|
+
const userAgent = navigator.userAgent.toLowerCase();
|
|
19
|
+
const isMobile =
|
|
20
|
+
/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini|mobile|tablet/i.test(userAgent);
|
|
21
|
+
|
|
22
|
+
// Kiểm tra thêm kích thước màn hình (dưới 768px thường được coi là thiết bị di động)
|
|
23
|
+
const isTouchScreen = window.matchMedia('(max-width: 768px)').matches;
|
|
24
|
+
|
|
25
|
+
return isMobile || isTouchScreen;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Hàm khởi tạo tooltip, chỉ load runtime khi cần thiết
|
|
30
|
+
*/
|
|
31
|
+
export function initTooltips(): void {
|
|
32
|
+
const tooltipTriggers = document.querySelectorAll<TooltipHost>('[data-tooltip-id]');
|
|
33
|
+
if (tooltipTriggers.length === 0) return;
|
|
34
|
+
|
|
35
|
+
const mobile = isMobileDevice();
|
|
36
|
+
|
|
37
|
+
tooltipTriggers.forEach((trigger) => {
|
|
38
|
+
if (trigger.hasAttribute('data-tooltip-initialized')) return;
|
|
39
|
+
trigger.setAttribute('data-tooltip-initialized', 'true');
|
|
40
|
+
|
|
41
|
+
if (mobile) {
|
|
42
|
+
trigger.addEventListener('click', async (e) => {
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
const { showTooltipMobile } = await import('./tooltip-runtime');
|
|
45
|
+
showTooltipMobile(trigger);
|
|
46
|
+
});
|
|
47
|
+
} else {
|
|
48
|
+
trigger.addEventListener(
|
|
49
|
+
'pointerenter',
|
|
50
|
+
async () => {
|
|
51
|
+
const { showTooltip } = await import('./tooltip-runtime');
|
|
52
|
+
showTooltip(trigger);
|
|
53
|
+
},
|
|
54
|
+
{ passive: true },
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Thêm một MutationObserver để theo dõi các phần tử DOM mới (cho React components)
|
|
61
|
+
export function setupTooltipObserver(): void {
|
|
62
|
+
// Kiểm tra xem đã có observer chưa
|
|
63
|
+
if (window.__tooltipObserver) return;
|
|
64
|
+
|
|
65
|
+
// Observer theo dõi khi có phần tử mới được thêm vào DOM
|
|
66
|
+
const observer = new MutationObserver((mutations) => {
|
|
67
|
+
let shouldInit = false;
|
|
68
|
+
|
|
69
|
+
mutations.forEach((mutation) => {
|
|
70
|
+
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
|
|
71
|
+
// Kiểm tra xem có phần tử tooltip nào mới được thêm vào không
|
|
72
|
+
mutation.addedNodes.forEach((node) => {
|
|
73
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
74
|
+
const element = node as HTMLElement;
|
|
75
|
+
// Kiểm tra nếu phần tử có data-tooltip-id hoặc chứa phần tử con có data-tooltip-id
|
|
76
|
+
if (
|
|
77
|
+
element.hasAttribute('data-tooltip-id') ||
|
|
78
|
+
element.querySelector('[data-tooltip-id]')
|
|
79
|
+
) {
|
|
80
|
+
shouldInit = true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Chỉ gọi initTooltips nếu có phát hiện tooltip mới
|
|
88
|
+
if (shouldInit) {
|
|
89
|
+
initTooltips();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Bắt đầu theo dõi toàn bộ DOM
|
|
94
|
+
observer.observe(document.body, {
|
|
95
|
+
childList: true,
|
|
96
|
+
subtree: true,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Lưu observer vào window để tránh tạo nhiều observer
|
|
100
|
+
window.__tooltipObserver = observer;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Typescript declaration cho window object
|
|
104
|
+
declare global {
|
|
105
|
+
interface Window {
|
|
106
|
+
__tooltipObserver?: MutationObserver;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// tooltip-runtime.ts
|
|
2
|
+
// Logic đầy đủ cho tooltip, chỉ được tải khi cần thiết
|
|
3
|
+
|
|
4
|
+
import type { Middleware, Placement } from '@floating-ui/dom';
|
|
5
|
+
import { autoUpdate, computePosition, flip, hide, offset, shift } from '@floating-ui/dom';
|
|
6
|
+
|
|
7
|
+
type TooltipHost = HTMLElement & {
|
|
8
|
+
dataset: {
|
|
9
|
+
tooltipId: string;
|
|
10
|
+
tooltipType?: string;
|
|
11
|
+
tooltipPlacement?: Placement;
|
|
12
|
+
href?: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export async function showTooltip(host: TooltipHost): Promise<void> {
|
|
17
|
+
/*
|
|
18
|
+
* Trong lúc chờ dynamic import, con trỏ có thể đã rời khỏi phần tử host.
|
|
19
|
+
* Nếu vậy, việc tạo tooltip là không cần thiết và sẽ không có sự kiện
|
|
20
|
+
* `pointerleave` nào được kích hoạt để ẩn tooltip.
|
|
21
|
+
*/
|
|
22
|
+
if (!host.matches(':hover')) return;
|
|
23
|
+
|
|
24
|
+
const tip = document.createElement('div');
|
|
25
|
+
tip.className = 'tp';
|
|
26
|
+
tip.innerHTML = '<div class="tp-loading">Loading...</div>';
|
|
27
|
+
document.body.append(tip);
|
|
28
|
+
|
|
29
|
+
// --- Cleanup ---
|
|
30
|
+
let cleanupAutoUpdate: (() => void) | null = null;
|
|
31
|
+
|
|
32
|
+
const cleanup = () => {
|
|
33
|
+
if (cleanupAutoUpdate) {
|
|
34
|
+
cleanupAutoUpdate();
|
|
35
|
+
cleanupAutoUpdate = null;
|
|
36
|
+
}
|
|
37
|
+
if (document.body.contains(tip)) tip.remove();
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
host.addEventListener('pointerleave', cleanup);
|
|
41
|
+
|
|
42
|
+
// Con trỏ đã rời trước khi listener được gắn
|
|
43
|
+
if (!host.matches(':hover')) {
|
|
44
|
+
host.removeEventListener('pointerleave', cleanup);
|
|
45
|
+
tip.remove();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// --- Position: autoUpdate thay thế manual scroll/resize ---
|
|
50
|
+
cleanupAutoUpdate = autoUpdate(host, tip, () => position(host, tip));
|
|
51
|
+
|
|
52
|
+
// --- Fetch nội dung từ server ---
|
|
53
|
+
try {
|
|
54
|
+
const { tooltipId: id, tooltipType: type } = host.dataset;
|
|
55
|
+
const html = await fetch(`/tooltip/${type}/${id}`, {
|
|
56
|
+
headers: { Accept: 'text/html' },
|
|
57
|
+
}).then((r) => r.text());
|
|
58
|
+
|
|
59
|
+
if (!document.body.contains(tip)) return;
|
|
60
|
+
|
|
61
|
+
tip.innerHTML = html;
|
|
62
|
+
await position(host, tip);
|
|
63
|
+
} catch {
|
|
64
|
+
if (document.body.contains(tip)) {
|
|
65
|
+
tip.innerHTML = '<div class="tp-error">Không thể tải tooltip</div>';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Mobile: hiển thị tooltip dạng fullscreen popup.
|
|
72
|
+
* Nếu host có data-href, thêm nút "Xem chi tiết" dẫn đến link đó.
|
|
73
|
+
*/
|
|
74
|
+
export async function showTooltipMobile(host: TooltipHost): Promise<void> {
|
|
75
|
+
const overlay = document.createElement('div');
|
|
76
|
+
overlay.className = 'tp-overlay';
|
|
77
|
+
|
|
78
|
+
const popup = document.createElement('div');
|
|
79
|
+
popup.className = 'tp-mobile';
|
|
80
|
+
popup.innerHTML = '<div class="tp-loading">Loading...</div>';
|
|
81
|
+
|
|
82
|
+
overlay.append(popup);
|
|
83
|
+
document.body.append(overlay);
|
|
84
|
+
document.body.style.overflow = 'hidden';
|
|
85
|
+
|
|
86
|
+
const cleanup = () => {
|
|
87
|
+
overlay.remove();
|
|
88
|
+
document.body.style.overflow = '';
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Đóng khi bấm vào overlay (bên ngoài popup)
|
|
92
|
+
overlay.addEventListener('click', (e) => {
|
|
93
|
+
if (e.target === overlay) cleanup();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const { tooltipId: id, tooltipType: type } = host.dataset;
|
|
98
|
+
const html = await fetch(`/tooltip/${type}/${id}`, {
|
|
99
|
+
headers: { Accept: 'text/html' },
|
|
100
|
+
}).then((r) => r.text());
|
|
101
|
+
|
|
102
|
+
popup.innerHTML = '';
|
|
103
|
+
|
|
104
|
+
// Nút đóng
|
|
105
|
+
const closeBtn = document.createElement('button');
|
|
106
|
+
closeBtn.className = 'tp-mobile-close';
|
|
107
|
+
closeBtn.innerHTML = '✕';
|
|
108
|
+
closeBtn.addEventListener('click', cleanup);
|
|
109
|
+
popup.append(closeBtn);
|
|
110
|
+
|
|
111
|
+
// Nội dung
|
|
112
|
+
const content = document.createElement('div');
|
|
113
|
+
content.className = 'tp-mobile-content';
|
|
114
|
+
content.innerHTML = html;
|
|
115
|
+
popup.append(content);
|
|
116
|
+
|
|
117
|
+
// Nút "Xem chi tiết" nếu có data-href
|
|
118
|
+
const href = host.dataset.href;
|
|
119
|
+
if (href) {
|
|
120
|
+
const link = document.createElement('a');
|
|
121
|
+
link.className = 'tp-mobile-link';
|
|
122
|
+
link.href = href;
|
|
123
|
+
link.textContent = 'Xem chi tiết';
|
|
124
|
+
popup.append(link);
|
|
125
|
+
}
|
|
126
|
+
} catch {
|
|
127
|
+
popup.innerHTML = '<div class="tp-error">Không thể tải tooltip</div>';
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Khi tooltip cao hơn 60% viewport, bỏ qua placement trên/dưới host
|
|
133
|
+
* và căn giữa theo chiều dọc màn hình + giới hạn maxHeight để scroll.
|
|
134
|
+
*/
|
|
135
|
+
function centerFallback(): Middleware {
|
|
136
|
+
return {
|
|
137
|
+
name: 'centerFallback',
|
|
138
|
+
fn({ rects }) {
|
|
139
|
+
const vh = window.innerHeight;
|
|
140
|
+
const padding = 8;
|
|
141
|
+
|
|
142
|
+
if (rects.floating.height <= vh * 0.6) return {};
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
y: window.scrollY + Math.max(padding, (vh - rects.floating.height) / 2),
|
|
146
|
+
data: { centered: true, maxHeight: vh - padding * 2 },
|
|
147
|
+
};
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function position(host: TooltipHost, tip: HTMLDivElement): Promise<void> {
|
|
153
|
+
const preferredPlacement = host.dataset.tooltipPlacement;
|
|
154
|
+
const placement = preferredPlacement ?? 'left';
|
|
155
|
+
|
|
156
|
+
return computePosition(host, tip, {
|
|
157
|
+
placement,
|
|
158
|
+
middleware: [offset(6), flip(), shift({ padding: 5 }), centerFallback(), hide()],
|
|
159
|
+
}).then(({ x, y, middlewareData }) => {
|
|
160
|
+
Object.assign(tip.style, { left: `${x}px`, top: `${y}px` });
|
|
161
|
+
|
|
162
|
+
tip.style.visibility = middlewareData.hide?.referenceHidden ? 'hidden' : 'visible';
|
|
163
|
+
|
|
164
|
+
const center = middlewareData.centerFallback as { centered?: boolean; maxHeight?: number };
|
|
165
|
+
if (center?.centered) {
|
|
166
|
+
tip.style.maxHeight = `${center.maxHeight}px`;
|
|
167
|
+
tip.style.overflowY = 'auto';
|
|
168
|
+
} else {
|
|
169
|
+
tip.style.maxHeight = '';
|
|
170
|
+
tip.style.overflowY = '';
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Minimal global JSX types for consumers of L5E.
|
|
2
|
+
// This file intentionally avoids imports to ensure it loads cleanly everywhere
|
|
3
|
+
// For internal use, prefer src/core/jsx-types.d.ts which has full type definitions
|
|
4
|
+
declare global {
|
|
5
|
+
namespace JSX {
|
|
6
|
+
interface IntrinsicElements {
|
|
7
|
+
[elemName: string]: any;
|
|
8
|
+
}
|
|
9
|
+
// Use any for external consumers, but internal files should use jsx-types.d.ts
|
|
10
|
+
type Element = any;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export {};
|