@rxdrag/website-lib 0.0.5 → 0.0.7
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/index.ts +4 -0
- package/package.json +14 -15
- package/src/components/AnimationNumber.astro +104 -0
- package/src/components/AttachmentIcon.astro +45 -0
- package/src/components/Flip.astro +53 -0
- package/src/components/FlipLogic.ts +242 -0
- package/src/components/Link.astro +81 -0
- package/src/components/LinkLogic.ts +51 -0
- package/src/components/Meta.astro +66 -0
- package/src/components/Modal.astro +69 -0
- package/src/components/ModalCloser.astro +22 -0
- package/src/components/ModalPanel.astro +22 -0
- package/src/components/ModalTrigger.astro +30 -0
- package/src/components/Motion.astro +333 -0
- package/src/components/MotionTypes.ts +71 -0
- package/src/components/Popover.astro +57 -0
- package/src/components/Popup.astro +19 -0
- package/src/components/PopverPanel.astro +22 -0
- package/src/components/RichTextView.astro +34 -0
- package/src/components/Tabs.astro +124 -0
- package/src/components/TabsBody.astro +19 -0
- package/src/components/TabsHeader.astro +19 -0
- package/src/components/TabsLogic.ts +10 -0
- package/src/components/TabsPanel.astro +17 -0
- package/src/components/TabsTab.astro +18 -0
- package/src/components/index.ts +19 -0
- package/src/env.d.ts +1 -0
- package/src/index.ts +2 -0
- package/src/lib/formatDate.ts +15 -0
- package/src/lib/index.ts +2 -0
- package/src/lib/pagination.ts +114 -0
- package/dist/_astro/client.D-vx6DWE.js +0 -170
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { CSSProperties } from "react";
|
|
3
|
+
import { DATA_POPUP_ROLE, PopupRole } from "@rxdrag/website-lib-core";
|
|
4
|
+
import Popup from "./Popup.astro";
|
|
5
|
+
import { IMotionProps } from "./MotionTypes";
|
|
6
|
+
|
|
7
|
+
interface Props extends IMotionProps {
|
|
8
|
+
popupKey?: string;
|
|
9
|
+
class?: string;
|
|
10
|
+
style?: string | CSSProperties;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const roleProps = {
|
|
14
|
+
[DATA_POPUP_ROLE]: PopupRole.PopoverPanel,
|
|
15
|
+
};
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<Popup {...roleProps} {...Astro.props}>
|
|
19
|
+
<slot />
|
|
20
|
+
</Popup>
|
|
21
|
+
|
|
22
|
+
<script></script>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { mdxToSlate } from "@rxdrag/slate-preview/src/mdx";
|
|
3
|
+
import {
|
|
4
|
+
LeafPreviewView,
|
|
5
|
+
slatePreviews,
|
|
6
|
+
TSlateLeaf,
|
|
7
|
+
} from "@rxdrag/slate-preview";
|
|
8
|
+
import { ElementPreview } from "@rxdrag/slate-preview";
|
|
9
|
+
import { PRODUCT_KEY, ProductCard } from "@rxdrag/website-lib-core";
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
value?: string;
|
|
13
|
+
class?: string;
|
|
14
|
+
style?: string | Record<string, string | number>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { value = "", class: className, style } = Astro.props;
|
|
18
|
+
const previews = { ...slatePreviews, [PRODUCT_KEY]: ProductCard };
|
|
19
|
+
const nodes = mdxToSlate(value);
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
<div class={className} style={style}>
|
|
23
|
+
{
|
|
24
|
+
nodes?.map((node, index) => {
|
|
25
|
+
return (
|
|
26
|
+
<ElementPreview node={node} previews={previews}>
|
|
27
|
+
{node.children?.map((child) => {
|
|
28
|
+
return <LeafPreviewView node={child as TSlateLeaf} />;
|
|
29
|
+
})}
|
|
30
|
+
</ElementPreview>
|
|
31
|
+
);
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
</div>
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Motion from "./Motion.astro";
|
|
3
|
+
import { IMotionProps } from "./MotionTypes";
|
|
4
|
+
import { DATA_TABS, DATA_TABS_SELECTION } from "./TabsLogic";
|
|
5
|
+
|
|
6
|
+
interface Props extends IMotionProps {
|
|
7
|
+
name?: string;
|
|
8
|
+
class?: string;
|
|
9
|
+
selection?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const id =
|
|
13
|
+
Astro.props.name || `tabs-${Math.random().toString(36).substring(2, 9)}`;
|
|
14
|
+
const { selection, ...rest } = Astro.props;
|
|
15
|
+
const dataProps = {
|
|
16
|
+
[DATA_TABS]: id,
|
|
17
|
+
[DATA_TABS_SELECTION]: selection || 1,
|
|
18
|
+
};
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
<Motion {...rest} {...dataProps}>
|
|
22
|
+
<slot />
|
|
23
|
+
</Motion>
|
|
24
|
+
|
|
25
|
+
<script>
|
|
26
|
+
import {
|
|
27
|
+
onEverySwap,
|
|
28
|
+
onPageLoaded,
|
|
29
|
+
select,
|
|
30
|
+
unselect,
|
|
31
|
+
} from "@rxdrag/website-lib-core";
|
|
32
|
+
import {
|
|
33
|
+
DATA_TABS,
|
|
34
|
+
DATA_TABS_PANEL,
|
|
35
|
+
DATA_TABS_SELECTION,
|
|
36
|
+
DATA_TABS_TAB,
|
|
37
|
+
} from "./TabsLogic";
|
|
38
|
+
|
|
39
|
+
// 使用WeakMap存储Tab的事件处理函数引用
|
|
40
|
+
const tabHandlers = new WeakMap<HTMLElement, (event: Event) => void>();
|
|
41
|
+
|
|
42
|
+
const initTabs = () => {
|
|
43
|
+
const tabsList = document.querySelectorAll(`[${DATA_TABS}]`);
|
|
44
|
+
tabsList.forEach((tabs) => {
|
|
45
|
+
const tabsId = tabs.getAttribute(DATA_TABS);
|
|
46
|
+
const tabsSelection = tabs.getAttribute(DATA_TABS_SELECTION);
|
|
47
|
+
const tabsTabs = tabs.querySelectorAll(`[${DATA_TABS_TAB}]`);
|
|
48
|
+
const tabsPanels = tabs.querySelectorAll(`[${DATA_TABS_PANEL}]`);
|
|
49
|
+
if (!tabsId) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// 初始化选中标签
|
|
53
|
+
tabsTabs.forEach((tab, index) => {
|
|
54
|
+
if (tabsSelection && tabsSelection === (index + 1).toString()) {
|
|
55
|
+
tab.classList.add("selected");
|
|
56
|
+
} else {
|
|
57
|
+
tab.classList.remove("selected");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 先移除旧的事件监听器(如果存在)
|
|
61
|
+
const oldHandler = tabHandlers.get(tab as HTMLElement);
|
|
62
|
+
if (oldHandler) {
|
|
63
|
+
tab.removeEventListener("click", oldHandler);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 给标签添加点击事件
|
|
67
|
+
const handleTabClick = (event: Event) => {
|
|
68
|
+
const oldSeledction = tabs.getAttribute(DATA_TABS_SELECTION);
|
|
69
|
+
const newSelection = (index + 1).toString();
|
|
70
|
+
// 如果标签已经选中,则不进行操作
|
|
71
|
+
if (oldSeledction === newSelection) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// 设置选中的标签
|
|
75
|
+
tabs.setAttribute(DATA_TABS_SELECTION, newSelection);
|
|
76
|
+
// 发出选中事件
|
|
77
|
+
select(tabsId, tab as HTMLElement, newSelection);
|
|
78
|
+
// 移除所有标签的选中状态
|
|
79
|
+
tabsTabs.forEach((tab, subIndex) => {
|
|
80
|
+
const tabKey = (subIndex + 1).toString();
|
|
81
|
+
if (oldSeledction === tabKey) {
|
|
82
|
+
tab.classList.remove("selected");
|
|
83
|
+
// 取消选中
|
|
84
|
+
unselect(tabsId, tab as HTMLElement, tabKey);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
tab.classList.add("selected");
|
|
88
|
+
|
|
89
|
+
// 设置选中的面板
|
|
90
|
+
tabsPanels.forEach((panel, subIndex) => {
|
|
91
|
+
panel.classList.remove("selected");
|
|
92
|
+
if (index === subIndex) {
|
|
93
|
+
panel.classList.add("selected");
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// 保存新的事件处理函数引用
|
|
99
|
+
tabHandlers.set(tab as HTMLElement, handleTabClick);
|
|
100
|
+
|
|
101
|
+
// 添加新的事件监听器
|
|
102
|
+
tab.addEventListener("click", handleTabClick);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// 初始化选中面板
|
|
106
|
+
tabsPanels.forEach((panel, index) => {
|
|
107
|
+
if (tabsSelection && tabsSelection === (index + 1).toString()) {
|
|
108
|
+
panel.classList.add("selected");
|
|
109
|
+
} else {
|
|
110
|
+
panel.classList.remove("selected");
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// 在页面加载和每次页面转场后都初始化Tabs
|
|
117
|
+
onPageLoaded(() => {
|
|
118
|
+
initTabs();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
onEverySwap(() => {
|
|
122
|
+
initTabs();
|
|
123
|
+
});
|
|
124
|
+
</script>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Motion from "./Motion.astro";
|
|
3
|
+
import { IMotionProps } from "./MotionTypes";
|
|
4
|
+
import { DATA_TABS_BODY } from "./TabsLogic";
|
|
5
|
+
|
|
6
|
+
interface Props extends IMotionProps {
|
|
7
|
+
name?: string;
|
|
8
|
+
class?: string;
|
|
9
|
+
selection?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const dataProps = {
|
|
13
|
+
[DATA_TABS_BODY]: true,
|
|
14
|
+
};
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<Motion {...Astro.props} {...dataProps}>
|
|
18
|
+
<slot />
|
|
19
|
+
</Motion>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Motion from "./Motion.astro";
|
|
3
|
+
import { IMotionProps } from "./MotionTypes";
|
|
4
|
+
import { DATA_TABS_HEADER } from "./TabsLogic";
|
|
5
|
+
|
|
6
|
+
interface Props extends IMotionProps {
|
|
7
|
+
name?: string;
|
|
8
|
+
class?: string;
|
|
9
|
+
selection?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const dataProps = {
|
|
13
|
+
[DATA_TABS_HEADER]: true,
|
|
14
|
+
};
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<Motion as="ul" {...Astro.props} {...dataProps}>
|
|
18
|
+
<slot />
|
|
19
|
+
</Motion>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const DATA_TABS = "data-tabs";
|
|
2
|
+
export const DATA_TABS_HEADER = "data-tabs-header";
|
|
3
|
+
export const DATA_TABS_PANEL = "data-tabs-panel";
|
|
4
|
+
export const DATA_TABS_TAB = "data-tabs-tab";
|
|
5
|
+
export const DATA_TABS_BODY = "data-tabs-body";
|
|
6
|
+
//当前选中的选项卡
|
|
7
|
+
export const DATA_TABS_SELECTION = "data-tabs-selection";
|
|
8
|
+
//选中动画,存动画数据
|
|
9
|
+
export const DATA_MOTION_SELECTION = "data-motion-selection";
|
|
10
|
+
export const DATA_MOTION_FLIP = "data-motion-flip";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { DATA_TABS_PANEL } from "./TabsLogic";
|
|
3
|
+
import Motion from "./Motion.astro";
|
|
4
|
+
import { IMotionProps } from "./MotionTypes";
|
|
5
|
+
|
|
6
|
+
interface Props extends IMotionProps {
|
|
7
|
+
class?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const dataProps = {
|
|
11
|
+
[DATA_TABS_PANEL]: true,
|
|
12
|
+
};
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<Motion {...Astro.props} {...dataProps}>
|
|
16
|
+
<slot />
|
|
17
|
+
</Motion>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { DATA_TABS_TAB } from "./TabsLogic";
|
|
3
|
+
import Motion from "./Motion.astro";
|
|
4
|
+
import { IMotionProps } from "./MotionTypes";
|
|
5
|
+
interface Props extends IMotionProps {
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const dataProps = {
|
|
10
|
+
[DATA_TABS_TAB]: true,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const { class: className, ...props } = Astro.props;
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<Motion as="li" {...props} {...dataProps} class={className}>
|
|
17
|
+
<slot />
|
|
18
|
+
</Motion>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { default as AnimationNumber } from "./AnimationNumber.astro";
|
|
2
|
+
export { default as AttachmentIcon } from "./AttachmentIcon.astro";
|
|
3
|
+
export { default as Flip } from "./Flip.astro";
|
|
4
|
+
export { default as Link } from "./Link.astro";
|
|
5
|
+
export * from "./LinkLogic";
|
|
6
|
+
export { default as Meta } from "./Meta.astro";
|
|
7
|
+
export { default as Modal } from "./Modal.astro";
|
|
8
|
+
export { default as ModalCloser } from "./ModalCloser.astro";
|
|
9
|
+
export { default as ModalPanel } from "./ModalPanel.astro";
|
|
10
|
+
export { default as ModalTrigger } from "./ModalTrigger.astro";
|
|
11
|
+
export { default as Motion } from "./Motion.astro";
|
|
12
|
+
export { default as Popover } from "./Popover.astro";
|
|
13
|
+
export { default as PopoverPanel } from "./PopverPanel.astro";
|
|
14
|
+
export { default as RichTextView } from "./RichTextView.astro";
|
|
15
|
+
export { default as Tabs } from "./Tabs.astro";
|
|
16
|
+
export { default as TabsBody } from "./TabsBody.astro";
|
|
17
|
+
export { default as TabsHeader } from "./TabsHeader.astro";
|
|
18
|
+
export { default as TabsPanel } from "./TabsPanel.astro";
|
|
19
|
+
export { default as TabsTab } from "./TabsTab.astro";
|
package/src/env.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference path="../.astro/types.d.ts" />
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 格式化日期为可读字符串
|
|
3
|
+
* @param date 日期对象或日期字符串
|
|
4
|
+
* @returns 格式化后的日期字符串,例如:2023年1月1日
|
|
5
|
+
*/
|
|
6
|
+
export function formatDate(date: Date | string | number): string {
|
|
7
|
+
const d = new Date(date);
|
|
8
|
+
|
|
9
|
+
// 使用 Intl.DateTimeFormat 进行本地化日期格式化
|
|
10
|
+
return new Intl.DateTimeFormat('zh-CN', {
|
|
11
|
+
year: 'numeric',
|
|
12
|
+
month: 'long',
|
|
13
|
+
day: 'numeric'
|
|
14
|
+
}).format(d);
|
|
15
|
+
}
|
package/src/lib/index.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export type PageItem = number | "ellipsis";
|
|
2
|
+
|
|
3
|
+
export interface PaginationProps {
|
|
4
|
+
currentPage: number;
|
|
5
|
+
totalPages: number;
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface PaginationData {
|
|
10
|
+
pages: PageItem[];
|
|
11
|
+
hasPreviousPage: boolean;
|
|
12
|
+
hasNextPage: boolean;
|
|
13
|
+
previousPageUrl: string;
|
|
14
|
+
nextPageUrl: string;
|
|
15
|
+
pageUrls: Record<number, string>;
|
|
16
|
+
currentPage: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function generatePagination(current: number, total: number) {
|
|
20
|
+
const result: PageItem[] = [];
|
|
21
|
+
const edgePageCount = 1; // 边缘页数
|
|
22
|
+
const middlePagesSiblingCount = 2; // 中间页数的兄弟数量
|
|
23
|
+
const totalVisiblePages = edgePageCount * 2 + middlePagesSiblingCount * 2 + 1;
|
|
24
|
+
|
|
25
|
+
// 如果总页数较少,直接显示所有页码
|
|
26
|
+
if (total <= totalVisiblePages) {
|
|
27
|
+
for (let i = 1; i <= total; i++) {
|
|
28
|
+
result.push(i);
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 始终显示第一页
|
|
34
|
+
result.push(1);
|
|
35
|
+
|
|
36
|
+
// 计算中间区域的起始和结束
|
|
37
|
+
let leftIndex = Math.max(
|
|
38
|
+
edgePageCount + 1,
|
|
39
|
+
current - middlePagesSiblingCount
|
|
40
|
+
);
|
|
41
|
+
let rightIndex = Math.min(
|
|
42
|
+
total - edgePageCount,
|
|
43
|
+
current + middlePagesSiblingCount
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// 调整以保持固定数量的页码
|
|
47
|
+
if (leftIndex <= edgePageCount + 1) {
|
|
48
|
+
rightIndex = Math.min(
|
|
49
|
+
total - edgePageCount,
|
|
50
|
+
totalVisiblePages - edgePageCount - 1
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
if (rightIndex >= total - edgePageCount) {
|
|
54
|
+
leftIndex = Math.max(
|
|
55
|
+
edgePageCount + 1,
|
|
56
|
+
total - totalVisiblePages + edgePageCount + 1
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 添加左省略号
|
|
61
|
+
if (leftIndex > edgePageCount + 1) {
|
|
62
|
+
result.push("ellipsis");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 添加中间页码
|
|
66
|
+
for (let i = leftIndex; i <= rightIndex; i++) {
|
|
67
|
+
result.push(i);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 添加右省略号
|
|
71
|
+
if (rightIndex < total - edgePageCount) {
|
|
72
|
+
result.push("ellipsis");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 始终显示最后一页
|
|
76
|
+
if (total > 1) {
|
|
77
|
+
result.push(total);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function paginate(props: PaginationProps): PaginationData {
|
|
84
|
+
const { currentPage, totalPages, baseUrl } = props;
|
|
85
|
+
|
|
86
|
+
// 生成页码数组
|
|
87
|
+
const pages = generatePagination(currentPage, totalPages);
|
|
88
|
+
|
|
89
|
+
// 判断是否有上一页和下一页
|
|
90
|
+
const hasPreviousPage = currentPage > 1;
|
|
91
|
+
const hasNextPage = currentPage < totalPages;
|
|
92
|
+
|
|
93
|
+
// 生成上一页和下一页的URL
|
|
94
|
+
const previousPageUrl = `${baseUrl}${currentPage - 1}`;
|
|
95
|
+
const nextPageUrl = `${baseUrl}${currentPage + 1}`;
|
|
96
|
+
|
|
97
|
+
// 生成每个页码对应的URL
|
|
98
|
+
const pageUrls: Record<number, string> = {};
|
|
99
|
+
pages.forEach((page) => {
|
|
100
|
+
if (typeof page === "number") {
|
|
101
|
+
pageUrls[page] = `${baseUrl}${page}`;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
pages,
|
|
107
|
+
hasPreviousPage,
|
|
108
|
+
hasNextPage,
|
|
109
|
+
previousPageUrl,
|
|
110
|
+
nextPageUrl,
|
|
111
|
+
pageUrls,
|
|
112
|
+
currentPage,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import { createElement, startTransition } from 'react';
|
|
2
|
-
import require$$0 from 'react-dom';
|
|
3
|
-
|
|
4
|
-
var hydrateRoot;
|
|
5
|
-
var createRoot;
|
|
6
|
-
|
|
7
|
-
var m = require$$0;
|
|
8
|
-
if (process.env.NODE_ENV === 'production') {
|
|
9
|
-
createRoot = m.createRoot;
|
|
10
|
-
hydrateRoot = m.hydrateRoot;
|
|
11
|
-
} else {
|
|
12
|
-
var i = m.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
|
13
|
-
createRoot = function(c, o) {
|
|
14
|
-
i.usingClientEntryPoint = true;
|
|
15
|
-
try {
|
|
16
|
-
return m.createRoot(c, o);
|
|
17
|
-
} finally {
|
|
18
|
-
i.usingClientEntryPoint = false;
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
hydrateRoot = function(c, h, o) {
|
|
22
|
-
i.usingClientEntryPoint = true;
|
|
23
|
-
try {
|
|
24
|
-
return m.hydrateRoot(c, h, o);
|
|
25
|
-
} finally {
|
|
26
|
-
i.usingClientEntryPoint = false;
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Astro passes `children` as a string of HTML, so we need
|
|
33
|
-
* a wrapper `div` to render that content as VNodes.
|
|
34
|
-
*
|
|
35
|
-
* As a bonus, we can signal to React that this subtree is
|
|
36
|
-
* entirely static and will never change via `shouldComponentUpdate`.
|
|
37
|
-
*/
|
|
38
|
-
const StaticHtml = ({ value, name, hydrate = true }) => {
|
|
39
|
-
if (!value) return null;
|
|
40
|
-
const tagName = hydrate ? 'astro-slot' : 'astro-static-slot';
|
|
41
|
-
return createElement(tagName, {
|
|
42
|
-
name,
|
|
43
|
-
suppressHydrationWarning: true,
|
|
44
|
-
dangerouslySetInnerHTML: { __html: value },
|
|
45
|
-
});
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* This tells React to opt-out of re-rendering this subtree,
|
|
50
|
-
* In addition to being a performance optimization,
|
|
51
|
-
* this also allows other frameworks to attach to `children`.
|
|
52
|
-
*
|
|
53
|
-
* See https://preactjs.com/guide/v8/external-dom-mutations
|
|
54
|
-
*/
|
|
55
|
-
StaticHtml.shouldComponentUpdate = () => false;
|
|
56
|
-
|
|
57
|
-
function isAlreadyHydrated(element) {
|
|
58
|
-
for (const key in element) {
|
|
59
|
-
if (key.startsWith('__reactContainer')) {
|
|
60
|
-
return key;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function createReactElementFromDOMElement(element) {
|
|
66
|
-
let attrs = {};
|
|
67
|
-
for (const attr of element.attributes) {
|
|
68
|
-
attrs[attr.name] = attr.value;
|
|
69
|
-
}
|
|
70
|
-
// If the element has no children, we can create a simple React element
|
|
71
|
-
if (element.firstChild === null) {
|
|
72
|
-
return createElement(element.localName, attrs);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return createElement(
|
|
76
|
-
element.localName,
|
|
77
|
-
attrs,
|
|
78
|
-
Array.from(element.childNodes)
|
|
79
|
-
.map((c) => {
|
|
80
|
-
if (c.nodeType === Node.TEXT_NODE) {
|
|
81
|
-
return c.data;
|
|
82
|
-
} else if (c.nodeType === Node.ELEMENT_NODE) {
|
|
83
|
-
return createReactElementFromDOMElement(c);
|
|
84
|
-
} else {
|
|
85
|
-
return undefined;
|
|
86
|
-
}
|
|
87
|
-
})
|
|
88
|
-
.filter((a) => !!a),
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function getChildren(childString, experimentalReactChildren) {
|
|
93
|
-
if (experimentalReactChildren && childString) {
|
|
94
|
-
let children = [];
|
|
95
|
-
let template = document.createElement('template');
|
|
96
|
-
template.innerHTML = childString;
|
|
97
|
-
for (let child of template.content.children) {
|
|
98
|
-
children.push(createReactElementFromDOMElement(child));
|
|
99
|
-
}
|
|
100
|
-
return children;
|
|
101
|
-
} else if (childString) {
|
|
102
|
-
return createElement(StaticHtml, { value: childString });
|
|
103
|
-
} else {
|
|
104
|
-
return undefined;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Keep a map of roots so we can reuse them on re-renders
|
|
109
|
-
let rootMap = new WeakMap();
|
|
110
|
-
const getOrCreateRoot = (element, creator) => {
|
|
111
|
-
let root = rootMap.get(element);
|
|
112
|
-
if (!root) {
|
|
113
|
-
root = creator();
|
|
114
|
-
rootMap.set(element, root);
|
|
115
|
-
}
|
|
116
|
-
return root;
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
const client = (element) =>
|
|
120
|
-
(Component, props, { default: children, ...slotted }, { client }) => {
|
|
121
|
-
if (!element.hasAttribute('ssr')) return;
|
|
122
|
-
|
|
123
|
-
const actionKey = element.getAttribute('data-action-key');
|
|
124
|
-
const actionName = element.getAttribute('data-action-name');
|
|
125
|
-
const stringifiedActionResult = element.getAttribute('data-action-result');
|
|
126
|
-
|
|
127
|
-
const formState =
|
|
128
|
-
actionKey && actionName && stringifiedActionResult
|
|
129
|
-
? [JSON.parse(stringifiedActionResult), actionKey, actionName]
|
|
130
|
-
: undefined;
|
|
131
|
-
|
|
132
|
-
const renderOptions = {
|
|
133
|
-
identifierPrefix: element.getAttribute('prefix'),
|
|
134
|
-
formState,
|
|
135
|
-
};
|
|
136
|
-
for (const [key, value] of Object.entries(slotted)) {
|
|
137
|
-
props[key] = createElement(StaticHtml, { value, name: key });
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const componentEl = createElement(
|
|
141
|
-
Component,
|
|
142
|
-
props,
|
|
143
|
-
getChildren(children, element.hasAttribute('data-react-children')),
|
|
144
|
-
);
|
|
145
|
-
const rootKey = isAlreadyHydrated(element);
|
|
146
|
-
// HACK: delete internal react marker for nested components to suppress aggressive warnings
|
|
147
|
-
if (rootKey) {
|
|
148
|
-
delete element[rootKey];
|
|
149
|
-
}
|
|
150
|
-
if (client === 'only') {
|
|
151
|
-
return startTransition(() => {
|
|
152
|
-
const root = getOrCreateRoot(element, () => {
|
|
153
|
-
const r = createRoot(element);
|
|
154
|
-
element.addEventListener('astro:unmount', () => r.unmount(), { once: true });
|
|
155
|
-
return r;
|
|
156
|
-
});
|
|
157
|
-
root.render(componentEl);
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
startTransition(() => {
|
|
161
|
-
const root = getOrCreateRoot(element, () => {
|
|
162
|
-
const r = hydrateRoot(element, componentEl, renderOptions);
|
|
163
|
-
element.addEventListener('astro:unmount', () => r.unmount(), { once: true });
|
|
164
|
-
return r;
|
|
165
|
-
});
|
|
166
|
-
root.render(componentEl);
|
|
167
|
-
});
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
export { client as default };
|