@rxdrag/website-lib-core 0.0.4 → 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 +1 -0
- package/package.json +12 -13
- package/src/entify/Entify.ts +365 -0
- package/{dist/entify/index.d.ts → src/entify/index.ts} +4 -4
- package/src/entify/lib/createEntifyClient.ts +23 -0
- package/{dist/entify/lib/index.d.ts → src/entify/lib/index.ts} +29 -29
- package/src/entify/lib/langFields.ts +12 -0
- package/src/entify/lib/newAvatarQueryOptions.ts +5 -0
- package/src/entify/lib/newOgImageQueryOptions.ts +6 -0
- package/src/entify/lib/newPageMetaOptions.ts +20 -0
- package/src/entify/lib/newQueryPostOptions.ts +41 -0
- package/src/entify/lib/newQueryProductOptions.ts +90 -0
- package/src/entify/lib/newQueryProductsMediaOptions.ts +26 -0
- package/src/entify/lib/queryAllProducts.ts +27 -0
- package/src/entify/lib/queryEntityList.ts +44 -0
- package/src/entify/lib/queryFeaturedProducts.ts +47 -0
- package/src/entify/lib/queryLangs.ts +47 -0
- package/src/entify/lib/queryLatestPosts.ts +65 -0
- package/src/entify/lib/queryOneEntity.ts +67 -0
- package/src/entify/lib/queryOnePostById.ts +21 -0
- package/src/entify/lib/queryOnePostBySlug.ts +21 -0
- package/src/entify/lib/queryOnePostCategoryBySlug.ts +30 -0
- package/src/entify/lib/queryOneProductById.ts +20 -0
- package/src/entify/lib/queryOneProductBySlug.ts +21 -0
- package/src/entify/lib/queryOneProductCategoryBySlug.ts +30 -0
- package/src/entify/lib/queryOneTheme.ts +76 -0
- package/src/entify/lib/queryOneUser.ts +38 -0
- package/src/entify/lib/queryPostCategories.ts +48 -0
- package/src/entify/lib/queryPostSlugs.ts +32 -0
- package/src/entify/lib/queryPosts.ts +92 -0
- package/src/entify/lib/queryProductCategories.ts +44 -0
- package/src/entify/lib/queryProducts.ts +69 -0
- package/src/entify/lib/queryProductsInMenu.ts +31 -0
- package/src/entify/lib/queryUserIds.ts +24 -0
- package/src/entify/lib/queryUserPosts.ts +74 -0
- package/src/entify/lib/queryWebSiteSettings.ts +29 -0
- package/src/entify/lib/searchProducts.ts +70 -0
- package/src/entify/lib/sendEmail.ts +8 -0
- package/src/entify/lib/toQueryOptions.ts +20 -0
- package/src/entify/lib/upsertEntity.ts +9 -0
- package/src/entify/types/index.ts +2 -0
- package/src/entify/types/utils.ts +4 -0
- package/src/entify/types/variables.ts +7 -0
- package/src/entify/view-model/funcs.ts +271 -0
- package/src/entify/view-model/index.ts +2 -0
- package/src/entify/view-model/models.ts +143 -0
- package/{dist/index.d.ts → src/index.ts} +5 -5
- package/src/motion/consts.ts +598 -0
- package/{dist/motion/index.d.ts → src/motion/index.ts} +2 -2
- package/src/motion/types.ts +46 -0
- package/src/react/components/EnquiryForm/Input.tsx +52 -0
- package/src/react/components/EnquiryForm/Submit.tsx +30 -0
- package/src/react/components/EnquiryForm/Textarea.tsx +51 -0
- package/src/react/components/EnquiryForm/index.tsx +334 -0
- package/src/react/components/GoogleConsent/CookieItemPanel.tsx +81 -0
- package/src/react/components/GoogleConsent/CumtomizedModal.tsx +149 -0
- package/src/react/components/GoogleConsent/GoogleConsent.tsx +101 -0
- package/src/react/components/GoogleConsent/README.md +1 -0
- package/src/react/components/GoogleConsent/gtags.ts +68 -0
- package/src/react/components/GoogleConsent/index.ts +3 -0
- package/src/react/components/GoogleConsent/types.ts +18 -0
- package/src/react/components/GoogleConsent//345/217/202/350/200/203.md +4 -0
- package/src/react/components/Medias/index.tsx +347 -0
- package/src/react/components/ProductCard/ProductCard.tsx +23 -0
- package/src/react/components/ProductCard/ProductCardPreview.tsx +12 -0
- package/src/react/components/ProductCard/ProductCta/index.tsx +41 -0
- package/src/react/components/ProductCard/ProductCta/style.css +4 -0
- package/src/react/components/ProductCard/ProductDescription/index.tsx +13 -0
- package/src/react/components/ProductCard/ProductDescription/style.css +6 -0
- package/src/react/components/ProductCard/ProductMedia/index.tsx +34 -0
- package/src/react/components/ProductCard/ProductMedia/style.css +6 -0
- package/src/react/components/ProductCard/ProductTitle/index.tsx +7 -0
- package/src/react/components/ProductCard/ProductTitle/style.css +4 -0
- package/src/react/components/ProductCard/ProductView.tsx +35 -0
- package/{dist/react/components/ProductCard/index.d.ts → src/react/components/ProductCard/index.ts} +6 -6
- package/src/react/components/ProductCard/useQueryProduct.ts +32 -0
- package/src/react/components/RichTextOutline/index.tsx +76 -0
- package/src/react/components/RichTextOutline/useAcitviedHeading.ts +54 -0
- package/src/react/components/RichTextOutline/useAnchorScroll.ts +24 -0
- package/src/react/components/Scroller.tsx +7 -0
- package/src/react/components/SearchInput.tsx +34 -0
- package/src/react/components/Share/index.tsx +69 -0
- package/src/react/components/Share/socials.tsx +79 -0
- package/src/react/components/Share//350/265/204/346/226/231.md +7 -0
- package/src/react/components/ToTop/index.tsx +33 -0
- package/src/react/components/ToTop.tsx +33 -0
- package/{dist/react/components/index.d.ts → src/react/components/index.ts} +8 -8
- package/src/react/hooks/index.ts +1 -0
- package/src/react/hooks/useScroll.ts +23 -0
- package/{dist/react/index.d.ts → src/react/index.ts} +2 -2
- package/src/robots.ts +4 -0
- package/src/scripts/actions.ts +304 -0
- package/src/scripts/consts.ts +32 -0
- package/src/scripts/events.ts +33 -0
- package/{dist/scripts/index.d.ts → src/scripts/index.ts} +3 -3
- package/dist/entify/Entify.d.ts +0 -138
- package/dist/entify/lib/createEntifyClient.d.ts +0 -3
- package/dist/entify/lib/langFields.d.ts +0 -2
- package/dist/entify/lib/newAvatarQueryOptions.d.ts +0 -2
- package/dist/entify/lib/newOgImageQueryOptions.d.ts +0 -2
- package/dist/entify/lib/newPageMetaOptions.d.ts +0 -2
- package/dist/entify/lib/newQueryPostOptions.d.ts +0 -3
- package/dist/entify/lib/newQueryProductOptions.d.ts +0 -3
- package/dist/entify/lib/newQueryProductsMediaOptions.d.ts +0 -3
- package/dist/entify/lib/queryAllProducts.d.ts +0 -4
- package/dist/entify/lib/queryEntityList.d.ts +0 -3
- package/dist/entify/lib/queryFeaturedProducts.d.ts +0 -4
- package/dist/entify/lib/queryLangs.d.ts +0 -4
- package/dist/entify/lib/queryLatestPosts.d.ts +0 -4
- package/dist/entify/lib/queryOneEntity.d.ts +0 -5
- package/dist/entify/lib/queryOnePostById.d.ts +0 -3
- package/dist/entify/lib/queryOnePostBySlug.d.ts +0 -3
- package/dist/entify/lib/queryOnePostCategoryBySlug.d.ts +0 -3
- package/dist/entify/lib/queryOneProductById.d.ts +0 -3
- package/dist/entify/lib/queryOneProductBySlug.d.ts +0 -3
- package/dist/entify/lib/queryOneProductCategoryBySlug.d.ts +0 -3
- package/dist/entify/lib/queryOneTheme.d.ts +0 -3
- package/dist/entify/lib/queryOneUser.d.ts +0 -3
- package/dist/entify/lib/queryPostCategories.d.ts +0 -4
- package/dist/entify/lib/queryPostSlugs.d.ts +0 -4
- package/dist/entify/lib/queryPosts.d.ts +0 -10
- package/dist/entify/lib/queryProductCategories.d.ts +0 -4
- package/dist/entify/lib/queryProducts.d.ts +0 -6
- package/dist/entify/lib/queryProductsInMenu.d.ts +0 -2
- package/dist/entify/lib/queryUserIds.d.ts +0 -4
- package/dist/entify/lib/queryUserPosts.d.ts +0 -9
- package/dist/entify/lib/queryWebSiteSettings.d.ts +0 -3
- package/dist/entify/lib/searchProducts.d.ts +0 -4
- package/dist/entify/lib/sendEmail.d.ts +0 -3
- package/dist/entify/lib/toQueryOptions.d.ts +0 -3
- package/dist/entify/lib/upsertEntity.d.ts +0 -2
- package/dist/entify/types/index.d.ts +0 -2
- package/dist/entify/types/utils.d.ts +0 -4
- package/dist/entify/types/variables.d.ts +0 -7
- package/dist/entify/view-model/funcs.d.ts +0 -20
- package/dist/entify/view-model/index.d.ts +0 -2
- package/dist/entify/view-model/models.d.ts +0 -119
- package/dist/index.mjs +0 -40514
- package/dist/index.mjs.map +0 -1
- package/dist/motion/consts.d.ts +0 -77
- package/dist/motion/types.d.ts +0 -26
- package/dist/react/components/EnquiryForm/Input.d.ts +0 -15
- package/dist/react/components/EnquiryForm/Submit.d.ts +0 -8
- package/dist/react/components/EnquiryForm/Textarea.d.ts +0 -13
- package/dist/react/components/EnquiryForm/index.d.ts +0 -22
- package/dist/react/components/Medias/index.d.ts +0 -8
- package/dist/react/components/ProductCard/ProductCard.d.ts +0 -15
- package/dist/react/components/ProductCard/ProductCardPreview.d.ts +0 -2
- package/dist/react/components/ProductCard/ProductCta/index.d.ts +0 -5
- package/dist/react/components/ProductCard/ProductDescription/index.d.ts +0 -2
- package/dist/react/components/ProductCard/ProductMedia/index.d.ts +0 -7
- package/dist/react/components/ProductCard/ProductTitle/index.d.ts +0 -2
- package/dist/react/components/ProductCard/ProductView.d.ts +0 -5
- package/dist/react/components/ProductCard/useQueryProduct.d.ts +0 -2
- package/dist/react/components/RichTextOutline/index.d.ts +0 -8
- package/dist/react/components/RichTextOutline/useAcitviedHeading.d.ts +0 -1
- package/dist/react/components/Scroller.d.ts +0 -3
- package/dist/react/components/SearchInput.d.ts +0 -2
- package/dist/react/components/Share/index.d.ts +0 -6
- package/dist/react/components/Share/socials.d.ts +0 -10
- package/dist/react/components/ToTop.d.ts +0 -5
- package/dist/react/hooks/index.d.ts +0 -1
- package/dist/react/hooks/useScroll.d.ts +0 -2
- package/dist/robots.d.ts +0 -2
- package/dist/scripts/actions.d.ts +0 -85
- package/dist/scripts/consts.d.ts +0 -21
- package/dist/scripts/events.d.ts +0 -11
- package/dist/style.css +0 -98
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Product } from "@rxdrag/rxcms-models";
|
|
2
|
+
import { ProductCardProps } from "./ProductCard";
|
|
3
|
+
import { Figcaption, Figure, FigureContent } from "@rxdrag/slate-preview";
|
|
4
|
+
import { ProductMedia } from "./ProductMedia";
|
|
5
|
+
import { ProductTitle } from "./ProductTitle";
|
|
6
|
+
import { ProductDescription } from "./ProductDescription";
|
|
7
|
+
import { ProductCta } from "./ProductCta";
|
|
8
|
+
|
|
9
|
+
export function ProductView(
|
|
10
|
+
props: {
|
|
11
|
+
product?: Product;
|
|
12
|
+
} & ProductCardProps
|
|
13
|
+
) {
|
|
14
|
+
const { product, node } = props;
|
|
15
|
+
|
|
16
|
+
const title = node?.title || product?.title;
|
|
17
|
+
const description = node?.description || product?.description;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Figure>
|
|
21
|
+
<FigureContent width={node?.width} align={node?.align}>
|
|
22
|
+
<a href={`/products/${product?.slug}`}>
|
|
23
|
+
<ProductMedia product={product} aspect={node?.aspect} />
|
|
24
|
+
</a>
|
|
25
|
+
</FigureContent>
|
|
26
|
+
<Figcaption width={node?.width} align={node?.align}>
|
|
27
|
+
<a href={`/products/${product?.slug}`}>
|
|
28
|
+
<ProductTitle>{title}</ProductTitle>
|
|
29
|
+
</a>
|
|
30
|
+
<ProductDescription>{description}</ProductDescription>
|
|
31
|
+
<ProductCta product={product} />
|
|
32
|
+
</Figcaption>
|
|
33
|
+
</Figure>
|
|
34
|
+
);
|
|
35
|
+
}
|
package/{dist/react/components/ProductCard/index.d.ts → src/react/components/ProductCard/index.ts}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export * from "./ProductCta";
|
|
2
|
-
export * from "./ProductTitle";
|
|
3
|
-
export * from "./ProductDescription";
|
|
4
|
-
export * from "./ProductCard";
|
|
5
|
-
export * from "./ProductCardPreview";
|
|
6
|
-
export * from "./ProductMedia";
|
|
1
|
+
export * from "./ProductCta";
|
|
2
|
+
export * from "./ProductTitle";
|
|
3
|
+
export * from "./ProductDescription";
|
|
4
|
+
export * from "./ProductCard";
|
|
5
|
+
export * from "./ProductCardPreview";
|
|
6
|
+
export * from "./ProductMedia";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { Product } from "@rxdrag/rxcms-models";
|
|
3
|
+
|
|
4
|
+
export function useQueryProduct(productId?: string, websiteId?: string) {
|
|
5
|
+
const active = useRef<boolean>(false);
|
|
6
|
+
const [product, setProduct] = useState<Product | undefined>({});
|
|
7
|
+
|
|
8
|
+
const url = websiteId
|
|
9
|
+
? `/${websiteId}/api/product/${productId}`
|
|
10
|
+
: `/api/product/${productId}`;
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (active.current && !productId) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
fetch(url)
|
|
18
|
+
.then((res) => res.json())
|
|
19
|
+
.then((data) => {
|
|
20
|
+
setProduct(data);
|
|
21
|
+
active.current = true;
|
|
22
|
+
});
|
|
23
|
+
}, [productId]);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
return () => {
|
|
27
|
+
active.current = false;
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return product;
|
|
32
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { extractOutline, mdxToSlate } from "@rxdrag/slate-preview";
|
|
2
|
+
import { forwardRef, ReactNode, useEffect } from "react";
|
|
3
|
+
import { useAcitviedHeading } from "./useAcitviedHeading";
|
|
4
|
+
import clsx from "clsx";
|
|
5
|
+
|
|
6
|
+
export type RichTextOutlineProps = {
|
|
7
|
+
className?: string;
|
|
8
|
+
itemClassName?: string;
|
|
9
|
+
value?: string;
|
|
10
|
+
//滚动偏移量
|
|
11
|
+
yOffset?: number;
|
|
12
|
+
style?: React.CSSProperties;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const RichTextOutline = forwardRef<
|
|
16
|
+
HTMLUListElement,
|
|
17
|
+
RichTextOutlineProps
|
|
18
|
+
>((props, ref) => {
|
|
19
|
+
const { className, itemClassName, value, yOffset = 100, ...rest } = props;
|
|
20
|
+
const activiedId = useAcitviedHeading();
|
|
21
|
+
const nodes = mdxToSlate(value ?? "");
|
|
22
|
+
const outline = extractOutline(nodes ?? []);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const handleHashChange = () => {
|
|
26
|
+
const element = document.getElementById(
|
|
27
|
+
window.location.hash.substring(1)
|
|
28
|
+
);
|
|
29
|
+
if (element) {
|
|
30
|
+
const y =
|
|
31
|
+
element.getBoundingClientRect().top + window.scrollY - yOffset;
|
|
32
|
+
window.scrollTo({ top: y, behavior: "smooth" });
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
window.addEventListener("hashchange", handleHashChange);
|
|
37
|
+
|
|
38
|
+
return () => {
|
|
39
|
+
window.removeEventListener("hashchange", handleHashChange);
|
|
40
|
+
};
|
|
41
|
+
}, []);
|
|
42
|
+
|
|
43
|
+
return outline?.length ? (
|
|
44
|
+
<ul ref={ref} className={className} {...rest}>
|
|
45
|
+
{outline?.map((item, index) => {
|
|
46
|
+
return (
|
|
47
|
+
<li
|
|
48
|
+
key={item?.key + index}
|
|
49
|
+
className={clsx(
|
|
50
|
+
activiedId === item?.key ? "actived" : "",
|
|
51
|
+
itemClassName
|
|
52
|
+
)}
|
|
53
|
+
>
|
|
54
|
+
<a
|
|
55
|
+
href={`#${item?.key}`}
|
|
56
|
+
onClick={(e) => {
|
|
57
|
+
e.preventDefault();
|
|
58
|
+
const element = document.getElementById(item?.key);
|
|
59
|
+
if (element) {
|
|
60
|
+
const yOffset = -100; // 偏移量
|
|
61
|
+
const y =
|
|
62
|
+
element.getBoundingClientRect().top +
|
|
63
|
+
window.scrollY +
|
|
64
|
+
yOffset;
|
|
65
|
+
window.scrollTo({ top: y, behavior: "smooth" });
|
|
66
|
+
}
|
|
67
|
+
}}
|
|
68
|
+
>
|
|
69
|
+
{item?.text}
|
|
70
|
+
</a>
|
|
71
|
+
</li>
|
|
72
|
+
);
|
|
73
|
+
})}
|
|
74
|
+
</ul>
|
|
75
|
+
) : null;
|
|
76
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export function useAcitviedHeading() {
|
|
4
|
+
const [activeId, setActiveId] = useState<string | null>(null);
|
|
5
|
+
|
|
6
|
+
const handleScroll = useCallback(() => {
|
|
7
|
+
const anchorElements = document.querySelectorAll('a[href^="#"]');
|
|
8
|
+
let closestId = null;
|
|
9
|
+
let closestDistance = Infinity;
|
|
10
|
+
|
|
11
|
+
anchorElements.forEach((element) => {
|
|
12
|
+
const id = element.getAttribute('href')?.slice(1) || '';
|
|
13
|
+
const targetElement = document.getElementById(id);
|
|
14
|
+
if (targetElement) {
|
|
15
|
+
const { top } = targetElement.getBoundingClientRect();
|
|
16
|
+
const distance = Math.abs(top);
|
|
17
|
+
// 寻找最接近视口顶部但不超过视口顶部的锚点
|
|
18
|
+
if (top <= 160 && distance < closestDistance) {
|
|
19
|
+
closestId = id;
|
|
20
|
+
closestDistance = distance;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
setActiveId(closestId);
|
|
26
|
+
}, []);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const handleHashChange = () => {
|
|
30
|
+
const hash = window.location.hash.substring(1); // 获取哈希值(去掉#)
|
|
31
|
+
setActiveId(hash);
|
|
32
|
+
|
|
33
|
+
const element = document.getElementById(hash);
|
|
34
|
+
if (element) {
|
|
35
|
+
const yOffset = -100; // 偏移量
|
|
36
|
+
const y = element.getBoundingClientRect().top + window.scrollY - yOffset;
|
|
37
|
+
window.scrollTo({ top: y, behavior: 'smooth' });
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
window.addEventListener('hashchange', handleHashChange);
|
|
42
|
+
window.addEventListener('scroll', handleScroll);
|
|
43
|
+
|
|
44
|
+
// 初始化时检查当前哈希值
|
|
45
|
+
handleHashChange();
|
|
46
|
+
|
|
47
|
+
return () => {
|
|
48
|
+
window.removeEventListener('hashchange', handleHashChange);
|
|
49
|
+
window.removeEventListener('scroll', handleScroll);
|
|
50
|
+
};
|
|
51
|
+
}, [handleScroll]);
|
|
52
|
+
|
|
53
|
+
return activeId;
|
|
54
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useCallback, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
export function useAnchorScroll() {
|
|
4
|
+
const handleAnchorClick = useCallback((event: MouseEvent): void => {
|
|
5
|
+
const target = event.target as HTMLAnchorElement;
|
|
6
|
+
if (target.tagName === 'A' && target.getAttribute('href')?.startsWith('#')) {
|
|
7
|
+
event.preventDefault();
|
|
8
|
+
const id = target.getAttribute('href')?.slice(1);
|
|
9
|
+
const element = document.getElementById(id || '');
|
|
10
|
+
if (element) {
|
|
11
|
+
const yOffset = -100; // 假设你想要元素距离顶部100px
|
|
12
|
+
const y = element.getBoundingClientRect().top + window.pageYOffset + yOffset;
|
|
13
|
+
window.scrollTo({ top: y, behavior: 'smooth' });
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}, []);
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
document.addEventListener('click', handleAnchorClick);
|
|
20
|
+
return () => document.removeEventListener('click', handleAnchorClick);
|
|
21
|
+
}, [handleAnchorClick]);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default useAnchorScroll;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { forwardRef, useState } from "react";
|
|
2
|
+
|
|
3
|
+
export type SearchProps = React.InputHTMLAttributes<HTMLInputElement>;
|
|
4
|
+
|
|
5
|
+
export const SearchInput = forwardRef<HTMLInputElement, SearchProps>(
|
|
6
|
+
(props, ref) => {
|
|
7
|
+
const [keyword, setKeyword] = useState<string>();
|
|
8
|
+
|
|
9
|
+
// const navigate = useNavigate();
|
|
10
|
+
// const [, setOpen] = useXModalState();
|
|
11
|
+
|
|
12
|
+
const handleKeyEnter = (event: React.KeyboardEvent<HTMLElement>) => {
|
|
13
|
+
if (event.key !== "Enter" || !keyword) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
// setOpen?.(false);
|
|
17
|
+
const encodedKeyword = encodeURIComponent(keyword || "");
|
|
18
|
+
if (encodedKeyword) {
|
|
19
|
+
//navigate(`/search?keyword=${encodedKeyword}`);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<input
|
|
25
|
+
ref={ref}
|
|
26
|
+
name="q"
|
|
27
|
+
onKeyDown={handleKeyEnter}
|
|
28
|
+
value={keyword}
|
|
29
|
+
onChange={(e) => setKeyword(e.target.value)}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { forwardRef, useEffect, useState } from "react";
|
|
2
|
+
import type { IconListType } from "./socials";
|
|
3
|
+
import { iconList } from "./socials";
|
|
4
|
+
import clsx from "clsx";
|
|
5
|
+
|
|
6
|
+
export type ShareProps = {
|
|
7
|
+
socials?: string[],
|
|
8
|
+
size?: "xs" | "sm" | "md" | "lg" | "xl",
|
|
9
|
+
className?: string,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function getPageDetails() {
|
|
13
|
+
if (typeof window === 'undefined') {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const details = {
|
|
17
|
+
url: window?.location?.href,
|
|
18
|
+
title: document?.title || "null",
|
|
19
|
+
description: 'null', // 默认为空字符串
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const descriptionMetaTag = document.querySelector('meta[name="description"]');
|
|
23
|
+
if (descriptionMetaTag) {
|
|
24
|
+
details.description = (descriptionMetaTag as HTMLMetaElement).content;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return details;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const Share = forwardRef<HTMLDivElement, ShareProps>((props: ShareProps, ref) => {
|
|
31
|
+
const { className, ...rest } = props;
|
|
32
|
+
const [socialList, setSolicalList] = useState<IconListType>();
|
|
33
|
+
|
|
34
|
+
const details = getPageDetails();
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
setSolicalList(iconList)
|
|
38
|
+
}, [props.socials]);
|
|
39
|
+
|
|
40
|
+
// 生成每个社交媒体的分享链接
|
|
41
|
+
const generateLink = (key: string) => {
|
|
42
|
+
const social = iconList[key];
|
|
43
|
+
if (!social) return '#';
|
|
44
|
+
return social.url(details?.url || "", details?.title, details?.description);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div ref={ref} className={clsx("flex items-center", className)}>
|
|
50
|
+
<div className="flex space-x-3">
|
|
51
|
+
{Object.keys(socialList || {}).map((key) => (
|
|
52
|
+
<a
|
|
53
|
+
key={key}
|
|
54
|
+
href={generateLink(key)}
|
|
55
|
+
target="_blank"
|
|
56
|
+
rel="noopener noreferrer"
|
|
57
|
+
title={`Share on ${iconList[key].title}`}
|
|
58
|
+
className="flex h-6 w-6 items-center justify-center text-gray-400 hover:text-gray-500"
|
|
59
|
+
>
|
|
60
|
+
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" focusable="false" aria-hidden="true">
|
|
61
|
+
{iconList[key].path}
|
|
62
|
+
</svg>
|
|
63
|
+
</a>
|
|
64
|
+
))
|
|
65
|
+
}
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
|
|
2
|
+
export interface IconListType {
|
|
3
|
+
[key: string]: {
|
|
4
|
+
title: string;
|
|
5
|
+
path: JSX.Element;
|
|
6
|
+
url: (l: string, t?: string, ti?: string) => string;
|
|
7
|
+
color: string;
|
|
8
|
+
viewBox?: string;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const iconList: IconListType = {
|
|
13
|
+
linkedin: {
|
|
14
|
+
title: "LinkedIn",
|
|
15
|
+
path: (
|
|
16
|
+
<path d="M6.5 21.5h-5v-13h5v13zM4 6.5C2.5 6.5 1.5 5.3 1.5 4s1-2.4 2.5-2.4c1.6 0 2.5 1 2.6 2.5 0 1.4-1 2.5-2.6 2.5zm11.5 6c-1 0-2 1-2 2v7h-5v-13h5V10s1.6-1.5 4-1.5c3 0 5 2.2 5 6.3v6.7h-5v-7c0-1-1-2-2-2z" />
|
|
17
|
+
),
|
|
18
|
+
color: "#0073b1",
|
|
19
|
+
url: (l, t, ti) =>
|
|
20
|
+
`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(l)}&title=${encodeURIComponent(ti || "")}&summary=${encodeURIComponent(t || "")}`,
|
|
21
|
+
},
|
|
22
|
+
facebook: {
|
|
23
|
+
title: "Facebook",
|
|
24
|
+
path: (
|
|
25
|
+
<path d="M24 12a12 12 0 10-13.9 11.9v-8.4h-3V12h3V9.4c0-3 1.8-4.7 4.6-4.7l2.6.2v3h-1.5c-1.5 0-2 .9-2 1.8V12h3.4l-.5 3.5h-2.8v8.4A12 12 0 0024 12z" />
|
|
26
|
+
),
|
|
27
|
+
color: "#0076FB",
|
|
28
|
+
url: (l) => `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(l)}`,
|
|
29
|
+
},
|
|
30
|
+
twitter: {
|
|
31
|
+
title: "X",
|
|
32
|
+
path: (
|
|
33
|
+
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"></path>
|
|
34
|
+
),
|
|
35
|
+
color: "#0F1419",
|
|
36
|
+
url: (l, t) => `https://twitter.com/intent/tweet?text=${t}&url=${encodeURIComponent(l)}`,
|
|
37
|
+
},
|
|
38
|
+
whatsapp: {
|
|
39
|
+
title: "WhatsApp",
|
|
40
|
+
path: (
|
|
41
|
+
<path d="M17.5 14.4l-2-1c-.3 0-.5-.1-.7.2l-1 1.1c-.1.2-.3.3-.6.1s-1.3-.5-2.4-1.5a9 9 0 01-1.7-2c-.1-.3 0-.5.2-.6l.4-.6c.2-.1.2-.3.3-.5v-.5L9 7c-.2-.6-.4-.5-.6-.5h-.6c-.2 0-.5 0-.8.4-.2.3-1 1-1 2.5s1 2.8 1.2 3c.2.2 2.1 3.2 5.1 4.5l1.7.6a4 4 0 001.9.2c.5-.1 1.7-.8 2-1.5.2-.6.2-1.2.1-1.4l-.5-.3M12 21.8a9.9 9.9 0 01-5-1.4l-.4-.2-3.7 1 1-3.7-.2-.3a9.9 9.9 0 01-1.5-5.3 9.9 9.9 0 0116.8-7 9.8 9.8 0 013 7 9.9 9.9 0 01-10 9.9m8.4-18.3A11.8 11.8 0 0012.1 0 12 12 0 001.8 17.8L0 24l6.4-1.6a11.9 11.9 0 005.6 1.4 12 12 0 0012-11.9 11.8 11.8 0 00-3.5-8.4z" />
|
|
42
|
+
),
|
|
43
|
+
color: "#25D366",
|
|
44
|
+
url: (l, t) => `https://api.whatsapp.com/send?text=${encodeURIComponent(t || "")} ${encodeURIComponent(l)}`,
|
|
45
|
+
},
|
|
46
|
+
reddit: {
|
|
47
|
+
title: "Reddit",
|
|
48
|
+
path: (
|
|
49
|
+
<path d="M12 0A12 12 0 000 12a12 12 0 0012 12 12 12 0 0012-12A12 12 0 0012 0zm5.01 4.74c.69 0 1.25.56 1.25 1.25a1.25 1.25 0 01-2.5.06l-2.6-.55-.8 3.75c1.83.07 3.48.63 4.68 1.49.3-.31.73-.5 1.2-.5.97 0 1.76.8 1.76 1.76 0 .72-.43 1.33-1.01 1.61a3.11 3.11 0 01.04.52c0 2.7-3.13 4.87-7 4.87-3.88 0-7-2.17-7-4.87 0-.18 0-.36.04-.53A1.75 1.75 0 014.03 12a1.75 1.75 0 012.96-1.26 8.52 8.52 0 014.74-1.5l.89-4.17a.34.34 0 01.14-.2.35.35 0 01.24-.04l2.9.62a1.21 1.21 0 011.11-.7zM9.25 12a1.25 1.25 0 101.25 1.25c0-.69-.56-1.25-1.25-1.25zm5.5 0a1.25 1.25 0 000 2.5 1.25 1.25 0 000-2.5zm-5.47 3.99a.33.33 0 00-.23.1.33.33 0 000 .46c.84.84 2.49.91 2.96.91.48 0 2.1-.06 2.96-.91a.36.36 0 00.03-.47.33.33 0 00-.46 0c-.55.54-1.68.73-2.51.73-.83 0-1.98-.2-2.51-.73a.33.33 0 00-.24-.1z" />
|
|
50
|
+
),
|
|
51
|
+
color: "#FF4500",
|
|
52
|
+
url: (l, t) => `https://www.reddit.com/submit?url=${encodeURIComponent(l)}&title=${encodeURIComponent(t || "")}`,
|
|
53
|
+
},
|
|
54
|
+
// telegram: {
|
|
55
|
+
// title: "Telegram",
|
|
56
|
+
// path: (
|
|
57
|
+
// <path d="M23.91 3.79L20.3 20.84c-.25 1.21-.98 1.5-2 .94l-5.5-4.07-2.66 2.57c-.3.3-.55.56-1.1.56-.72 0-.6-.27-.84-.95L6.3 13.7.85 12c-1.18-.35-1.19-1.16.26-1.75l21.26-8.2c.97-.43 1.9.24 1.53 1.73z" />
|
|
58
|
+
// ),
|
|
59
|
+
// color: "#0088CC",
|
|
60
|
+
// url: (l, t) => `https://telegram.me/share/msg?url=${encodeURIComponent(l)}&text=${encodeURIComponent(t || "")}`,
|
|
61
|
+
// },
|
|
62
|
+
|
|
63
|
+
// pinterest: {
|
|
64
|
+
// title: "Pinterest",
|
|
65
|
+
// path: (
|
|
66
|
+
// <path d="M0 12C0 17.123 3.211 21.497 7.73 23.218C7.62 22.281 7.503 20.736 7.755 19.652C7.972 18.72 9.156 13.714 9.156 13.714C9.156 13.714 8.799 12.999 8.799 11.94C8.799 10.28 9.761 9.04 10.96 9.04C11.98 9.04 12.472 9.805 12.472 10.722C12.472 11.747 11.819 13.279 11.482 14.7C11.201 15.889 12.079 16.859 13.251 16.859C15.374 16.859 17.007 14.62 17.007 11.388C17.007 8.527 14.951 6.528 12.016 6.528C8.618 6.528 6.623 9.077 6.623 11.712C6.623 12.739 7.018 13.839 7.512 14.438C7.55412 14.4832 7.58387 14.5386 7.59841 14.5986C7.61295 14.6587 7.61177 14.7215 7.595 14.781C7.504 15.159 7.302 15.97 7.263 16.136C7.21 16.354 7.09 16.401 6.863 16.295C5.371 15.601 4.439 13.42 4.439 11.668C4.439 7.899 7.176 4.439 12.331 4.439C16.475 4.439 19.696 7.392 19.696 11.338C19.696 15.455 17.101 18.769 13.497 18.769C12.286 18.769 11.149 18.139 10.759 17.396C10.759 17.396 10.16 19.678 10.015 20.236C9.733 21.32 8.951 22.692 8.466 23.471C9.584 23.815 10.77 24 12 24C18.627 24 24 18.627 24 12C24 5.373 18.627 0 12 0C5.373 0 0 5.373 0 12Z" />
|
|
67
|
+
// ),
|
|
68
|
+
// color: "#c8232c",
|
|
69
|
+
// url: (l) => `http://pinterest.com/pin/create/link/?url=${encodeURIComponent(l)}`,
|
|
70
|
+
// },
|
|
71
|
+
// email: {
|
|
72
|
+
// title: "Email",
|
|
73
|
+
// path: (
|
|
74
|
+
// <path d="M20 4H4a2 2 0 00-2 2v12c0 1.1.9 2 2 2h16a2 2 0 002-2V6a2 2 0 00-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z" />
|
|
75
|
+
// ),
|
|
76
|
+
// color: "#E53E3E",
|
|
77
|
+
// url: (l, t) => `mailto:?body=${encodeURIComponent(l)}&subject=${encodeURIComponent(t || "")}`,
|
|
78
|
+
// },
|
|
79
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
|
|
2
|
+
Facebook调试:https://developers.facebook.com/tools/debug/
|
|
3
|
+
|
|
4
|
+
https://developer.twitter.com/en/docs/tweets/optimize-with-cards/guides/getting-started
|
|
5
|
+
https://developers.facebook.com/docs/sharing/webmasters/
|
|
6
|
+
https://developers.pinterest.com/docs/rich-pins/rich-pins/
|
|
7
|
+
https://www.linkedin.com/help/linkedin/answer/a521928/making-your-website-shareable-on-linkedin?lang=en
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import clsx from "clsx"
|
|
2
|
+
import { forwardRef } from "react"
|
|
3
|
+
|
|
4
|
+
export type ToTopProps = {
|
|
5
|
+
className?: string,
|
|
6
|
+
children?: React.ReactNode
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const ToTop = forwardRef<HTMLDivElement, ToTopProps>((
|
|
10
|
+
props, ref) => {
|
|
11
|
+
const { className, children, ...rest } = props
|
|
12
|
+
const handleClick = () => {
|
|
13
|
+
window.scrollTo({ top: 0, behavior: "smooth" })
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return <div
|
|
17
|
+
ref={ref}
|
|
18
|
+
className={
|
|
19
|
+
clsx(
|
|
20
|
+
"fixed bottom-4 right-4 hidden user-select-none scrolling:flex cursor-pointer transition duration-300 ease-in-out z-50",
|
|
21
|
+
className,
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
{...rest}
|
|
25
|
+
onClick={handleClick}
|
|
26
|
+
>
|
|
27
|
+
{
|
|
28
|
+
children || <svg className="h-6 w-6" width="1.5rem" height="1.5rem" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
29
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 15l7-7 7 7" />
|
|
30
|
+
</svg>
|
|
31
|
+
}
|
|
32
|
+
</div>
|
|
33
|
+
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import clsx from "clsx"
|
|
2
|
+
import { forwardRef } from "react"
|
|
3
|
+
|
|
4
|
+
export type XToTopProps = {
|
|
5
|
+
className?: string,
|
|
6
|
+
children?: React.ReactNode
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const ToTop = forwardRef<HTMLDivElement, XToTopProps>((
|
|
10
|
+
props, ref) => {
|
|
11
|
+
const { className, children, ...rest } = props
|
|
12
|
+
const handleClick = () => {
|
|
13
|
+
window.scrollTo({ top: 0, behavior: "smooth" })
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return <div
|
|
17
|
+
ref={ref}
|
|
18
|
+
className={
|
|
19
|
+
clsx(
|
|
20
|
+
"fixed bottom-4 right-4 hidden user-select-none scrolling:flex cursor-pointer transition duration-300 ease-in-out z-50",
|
|
21
|
+
className,
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
{...rest}
|
|
25
|
+
onClick={handleClick}
|
|
26
|
+
>
|
|
27
|
+
{
|
|
28
|
+
children || <svg className="h-6 w-6" width="1.5rem" height="1.5rem" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
29
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 15l7-7 7 7" />
|
|
30
|
+
</svg>
|
|
31
|
+
}
|
|
32
|
+
</div>
|
|
33
|
+
})
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export * from "./EnquiryForm";
|
|
2
|
-
export * from "./Medias";
|
|
3
|
-
export * from "./ProductCard";
|
|
4
|
-
export * from "./RichTextOutline";
|
|
5
|
-
export * from "./Share";
|
|
6
|
-
export * from "./Scroller";
|
|
7
|
-
export * from "./SearchInput";
|
|
8
|
-
export * from "./ToTop";
|
|
1
|
+
export * from "./EnquiryForm";
|
|
2
|
+
export * from "./Medias";
|
|
3
|
+
export * from "./ProductCard";
|
|
4
|
+
export * from "./RichTextOutline";
|
|
5
|
+
export * from "./Share";
|
|
6
|
+
export * from "./Scroller";
|
|
7
|
+
export * from "./SearchInput";
|
|
8
|
+
export * from "./ToTop";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./useScroll"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useCallback, useEffect } from "react";
|
|
2
|
+
|
|
3
|
+
export const defualtThreshold = 10;
|
|
4
|
+
|
|
5
|
+
export function useScroll(threshold: number = defualtThreshold) {
|
|
6
|
+
|
|
7
|
+
const onScroll = useCallback(() => {
|
|
8
|
+
const scrolling = (window.scrollY > threshold);
|
|
9
|
+
|
|
10
|
+
if (scrolling) {
|
|
11
|
+
document.body.classList.add('scrolling');
|
|
12
|
+
} else {
|
|
13
|
+
document.body.classList.remove('scrolling');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
}, [threshold]);
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
window.addEventListener("scroll", onScroll);
|
|
20
|
+
return () => window.removeEventListener("scroll", onScroll);
|
|
21
|
+
}, [onScroll]);
|
|
22
|
+
|
|
23
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./components";
|
|
2
|
-
export * from "./hooks";
|
|
1
|
+
export * from "./components";
|
|
2
|
+
export * from "./hooks";
|
package/src/robots.ts
ADDED