@xiping/react-components 0.0.48
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/README.md +3 -0
- package/dist/App.d.ts +3 -0
- package/dist/App.js +8 -0
- package/dist/components/button/Button.d.ts +24 -0
- package/dist/components/button/Button.js +22 -0
- package/dist/components/txt-reader/TxtReader.d.ts +14 -0
- package/dist/components/txt-reader/TxtReader.js +63 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +9 -0
- package/dist/pages/home/Home.d.ts +2 -0
- package/dist/pages/home/Home.js +5 -0
- package/package.json +78 -0
package/README.md
ADDED
package/dist/App.d.ts
ADDED
package/dist/App.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { HashRouter, Route, Routes } from "react-router-dom";
|
|
3
|
+
import Home from "@/pages/home/Home";
|
|
4
|
+
import "./App.css";
|
|
5
|
+
function App() {
|
|
6
|
+
return (_jsx(_Fragment, { children: _jsx(HashRouter, { children: _jsx(Routes, { children: _jsx(Route, { path: "/", element: _jsx(Home, {}) }) }) }) }));
|
|
7
|
+
}
|
|
8
|
+
export default App;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ReactNode, ButtonHTMLAttributes } from "react";
|
|
2
|
+
export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost';
|
|
3
|
+
export type ButtonSize = 'small' | 'medium' | 'large';
|
|
4
|
+
export interface ButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'disabled'> {
|
|
5
|
+
/** Button variant that changes the visual style */
|
|
6
|
+
variant?: ButtonVariant;
|
|
7
|
+
/** Size of the button */
|
|
8
|
+
size?: ButtonSize;
|
|
9
|
+
/** Custom class name */
|
|
10
|
+
className?: string;
|
|
11
|
+
/** Button contents */
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
/** Optional icon to show before the button text */
|
|
14
|
+
leftIcon?: ReactNode;
|
|
15
|
+
/** Optional icon to show after the button text */
|
|
16
|
+
rightIcon?: ReactNode;
|
|
17
|
+
/** Whether the button is in loading state */
|
|
18
|
+
isLoading?: boolean;
|
|
19
|
+
/** Whether the button is disabled */
|
|
20
|
+
isDisabled?: boolean;
|
|
21
|
+
/** Full width button */
|
|
22
|
+
fullWidth?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare const Button: ({ variant, size, className, children, leftIcon, rightIcon, isLoading, isDisabled, fullWidth, type, ...rest }: ButtonProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
export const Button = ({ variant = 'primary', size = 'medium', className, children, leftIcon, rightIcon, isLoading = false, isDisabled = false, fullWidth = false, type = 'button', ...rest }) => {
|
|
4
|
+
const baseStyles = "inline-flex items-center justify-center rounded-md font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2";
|
|
5
|
+
const variantStyles = {
|
|
6
|
+
primary: "bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",
|
|
7
|
+
secondary: "bg-gray-600 text-white hover:bg-gray-700 focus:ring-gray-500",
|
|
8
|
+
outline: "border border-gray-300 bg-transparent hover:bg-gray-50 focus:ring-gray-500",
|
|
9
|
+
ghost: "bg-transparent hover:bg-gray-100 focus:ring-gray-500"
|
|
10
|
+
};
|
|
11
|
+
const sizeStyles = {
|
|
12
|
+
small: "px-3 py-1.5 text-sm",
|
|
13
|
+
medium: "px-4 py-2 text-base",
|
|
14
|
+
large: "px-6 py-3 text-lg"
|
|
15
|
+
};
|
|
16
|
+
const classes = clsx(baseStyles, variantStyles[variant], sizeStyles[size], {
|
|
17
|
+
'opacity-50 cursor-not-allowed': isDisabled,
|
|
18
|
+
'w-full': fullWidth,
|
|
19
|
+
'cursor-wait': isLoading
|
|
20
|
+
}, className);
|
|
21
|
+
return (_jsxs("button", { type: type, className: classes, disabled: isDisabled || isLoading, ...rest, children: [isLoading && (_jsxs("svg", { className: "animate-spin -ml-1 mr-2 h-4 w-4", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [_jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), _jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })] })), !isLoading && leftIcon && _jsx("span", { className: "mr-2", children: leftIcon }), children, !isLoading && rightIcon && _jsx("span", { className: "ml-2", children: rightIcon })] }));
|
|
22
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CSSProperties, FC } from "react";
|
|
2
|
+
interface TxtReaderProps {
|
|
3
|
+
content: string;
|
|
4
|
+
lineHeight?: number;
|
|
5
|
+
fontSize?: number;
|
|
6
|
+
fontWeight?: number | string;
|
|
7
|
+
className?: string;
|
|
8
|
+
style?: CSSProperties;
|
|
9
|
+
onProgressChange?: (progress: number) => void;
|
|
10
|
+
initialScrollPosition?: number;
|
|
11
|
+
cacheKey?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare const TxtReader: FC<TxtReaderProps>;
|
|
14
|
+
export default TxtReader;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState, useCallback, } from "react";
|
|
3
|
+
import { debounce } from "lodash-es";
|
|
4
|
+
import clsx from "clsx";
|
|
5
|
+
const DEFAULT_FONT_SIZE = 16;
|
|
6
|
+
const DEFAULT_LINE_HEIGHT = 1.5;
|
|
7
|
+
const DEFAULT_FONT_WEIGHT = "normal";
|
|
8
|
+
export const TxtReader = ({ content, lineHeight = DEFAULT_LINE_HEIGHT, fontSize = DEFAULT_FONT_SIZE, fontWeight = DEFAULT_FONT_WEIGHT, className = "", style = {}, onProgressChange, initialScrollPosition, cacheKey, }) => {
|
|
9
|
+
const containerRef = useRef(null);
|
|
10
|
+
const [readingProgress, setReadingProgress] = useState(0);
|
|
11
|
+
// 计算阅读进度
|
|
12
|
+
const calculateProgress = useCallback(() => {
|
|
13
|
+
if (!containerRef.current)
|
|
14
|
+
return;
|
|
15
|
+
const container = containerRef.current;
|
|
16
|
+
const scrollTop = container.scrollTop;
|
|
17
|
+
const scrollHeight = container.scrollHeight;
|
|
18
|
+
const clientHeight = container.clientHeight;
|
|
19
|
+
const progress = Math.min(Math.round((scrollTop / (scrollHeight - clientHeight)) * 100), 100);
|
|
20
|
+
setReadingProgress(progress);
|
|
21
|
+
onProgressChange?.(progress);
|
|
22
|
+
// 保存阅读位置到 localStorage
|
|
23
|
+
if (cacheKey) {
|
|
24
|
+
localStorage.setItem(`txtReader-${cacheKey}`, scrollTop.toString());
|
|
25
|
+
}
|
|
26
|
+
}, [cacheKey, onProgressChange]);
|
|
27
|
+
// 使用 debounce 优化滚动事件处理
|
|
28
|
+
const debouncedCalculateProgress = useCallback(debounce(calculateProgress, 100), [calculateProgress]);
|
|
29
|
+
// 初始化滚动位置
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!containerRef.current)
|
|
32
|
+
return;
|
|
33
|
+
let scrollPosition = initialScrollPosition;
|
|
34
|
+
// 如果有缓存key,尝试从localStorage获取保存的位置
|
|
35
|
+
if (cacheKey) {
|
|
36
|
+
const savedPosition = localStorage.getItem(`txtReader-${cacheKey}`);
|
|
37
|
+
if (savedPosition) {
|
|
38
|
+
scrollPosition = parseInt(savedPosition, 10);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (scrollPosition) {
|
|
42
|
+
containerRef.current.scrollTop = scrollPosition;
|
|
43
|
+
calculateProgress();
|
|
44
|
+
}
|
|
45
|
+
}, [cacheKey, initialScrollPosition, calculateProgress]);
|
|
46
|
+
// 监听滚动事件
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
const container = containerRef.current;
|
|
49
|
+
if (!container)
|
|
50
|
+
return;
|
|
51
|
+
container.addEventListener("scroll", debouncedCalculateProgress);
|
|
52
|
+
return () => {
|
|
53
|
+
container.removeEventListener("scroll", debouncedCalculateProgress);
|
|
54
|
+
};
|
|
55
|
+
}, [debouncedCalculateProgress]);
|
|
56
|
+
return (_jsxs("div", { className: "relative w-full h-full", children: [_jsx("div", { ref: containerRef, className: clsx("w-full overflow-auto whitespace-pre-wrap break-words", className), style: {
|
|
57
|
+
fontSize: `${fontSize}px`,
|
|
58
|
+
lineHeight: lineHeight,
|
|
59
|
+
fontWeight: fontWeight,
|
|
60
|
+
...style,
|
|
61
|
+
}, children: content }), _jsxs("div", { className: "fixed text-white rounded text-sm bg-black bg-opacity-70 pl-2 pr-2 pb-1 pt-1 right-5 bottom-5", children: [readingProgress, "%"] })] }));
|
|
62
|
+
};
|
|
63
|
+
export default TxtReader;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "./index.css";
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// import { StrictMode } from "react";
|
|
3
|
+
import { createRoot } from "react-dom/client";
|
|
4
|
+
import "./index.css";
|
|
5
|
+
import App from "./App";
|
|
6
|
+
const root = createRoot(document.getElementById("root"));
|
|
7
|
+
root.render(
|
|
8
|
+
// <StrictMode>
|
|
9
|
+
_jsx(App, {}));
|
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xiping/react-components",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.0.48",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"sideEffects": false,
|
|
12
|
+
"scripts": {
|
|
13
|
+
"dev": "vite",
|
|
14
|
+
"build": "tsc -p tsconfig.build.json",
|
|
15
|
+
"lint": "eslint ",
|
|
16
|
+
"preview": "vite preview",
|
|
17
|
+
"storybook": "storybook dev -p 6006",
|
|
18
|
+
"build-storybook": "storybook build"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"react": ">=18 || >= 19",
|
|
22
|
+
"react-dom": ">=18 || >= 19"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"ahooks": "^3.8.4",
|
|
26
|
+
"axios": "^1.7.9",
|
|
27
|
+
"clsx": "^2.1.1",
|
|
28
|
+
"dayjs": "^1.11.13",
|
|
29
|
+
"lodash-es": "^4.17.21",
|
|
30
|
+
"react": "^18.3.1",
|
|
31
|
+
"react-dom": "^18.3.1",
|
|
32
|
+
"react-icons": "^5.4.0",
|
|
33
|
+
"react-router-dom": "^7.1.1",
|
|
34
|
+
"zustand": "^5.0.2"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@chromatic-com/storybook": "^3.2.3",
|
|
38
|
+
"@eslint/js": "^9.17.0",
|
|
39
|
+
"@storybook/addon-essentials": "^8.4.7",
|
|
40
|
+
"@storybook/addon-interactions": "^8.4.7",
|
|
41
|
+
"@storybook/addon-onboarding": "^8.4.7",
|
|
42
|
+
"@storybook/blocks": "^8.4.7",
|
|
43
|
+
"@storybook/react": "^8.4.7",
|
|
44
|
+
"@storybook/react-vite": "^8.4.7",
|
|
45
|
+
"@storybook/test": "^8.4.7",
|
|
46
|
+
"@types/lodash-es": "^4.17.12",
|
|
47
|
+
"@types/node": "^22.10.2",
|
|
48
|
+
"@types/react": "^18.3.18",
|
|
49
|
+
"@types/react-dom": "^18.3.5",
|
|
50
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
51
|
+
"autoprefixer": "^10.4.20",
|
|
52
|
+
"eslint": "^9.17.0",
|
|
53
|
+
"eslint-plugin-react-hooks": "^5.1.0",
|
|
54
|
+
"eslint-plugin-react-refresh": "^0.4.16",
|
|
55
|
+
"eslint-plugin-storybook": "^0.11.1",
|
|
56
|
+
"globals": "^15.14.0",
|
|
57
|
+
"postcss": "^8.4.49",
|
|
58
|
+
"prettier": "^3.4.2",
|
|
59
|
+
"sharp": "^0.33.5",
|
|
60
|
+
"storybook": "^8.4.7",
|
|
61
|
+
"svgo": "^3.3.2",
|
|
62
|
+
"tailwindcss": "^3.4.17",
|
|
63
|
+
"typescript": "~5.6.3",
|
|
64
|
+
"typescript-eslint": "^8.18.2",
|
|
65
|
+
"vite": "^6.0.5",
|
|
66
|
+
"vite-plugin-dts": "^4.4.0",
|
|
67
|
+
"vite-plugin-image-optimizer": "^1.1.8",
|
|
68
|
+
"vite-plugin-zip-pack": "^1.2.4",
|
|
69
|
+
"vite-tsconfig-paths": "^5.1.4"
|
|
70
|
+
},
|
|
71
|
+
"publishConfig": {
|
|
72
|
+
"access": "public"
|
|
73
|
+
},
|
|
74
|
+
"engines": {
|
|
75
|
+
"node": ">=18"
|
|
76
|
+
},
|
|
77
|
+
"gitHead": "e7e62ae67bb40b5f3d4349e07b19caec17faeff9"
|
|
78
|
+
}
|