@turinhub/atomix-common-ui 0.2.2 → 0.4.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.
Files changed (116) hide show
  1. package/README.md +34 -15
  2. package/dist/PDFSidebar-BBtucLK6.js +232 -0
  3. package/dist/PDFSidebar-BBtucLK6.js.map +1 -0
  4. package/dist/PDFSidebar-Di0D-yPS.cjs +2 -0
  5. package/dist/PDFSidebar-Di0D-yPS.cjs.map +1 -0
  6. package/dist/component-types.cjs +2 -0
  7. package/dist/component-types.cjs.map +1 -0
  8. package/dist/component-types.d.ts +2 -0
  9. package/dist/component-types.d.ts.map +1 -0
  10. package/dist/component-types.js +2 -0
  11. package/dist/component-types.js.map +1 -0
  12. package/dist/components/FileUpload.d.ts +61 -0
  13. package/dist/components/FileUpload.d.ts.map +1 -0
  14. package/dist/components/MarkdownReader.d.ts +26 -0
  15. package/dist/components/MarkdownReader.d.ts.map +1 -0
  16. package/dist/components/PDFReader.d.ts +2 -2
  17. package/dist/components/PDFReader.d.ts.map +1 -1
  18. package/dist/components/PDFSidebar.d.ts +1 -1
  19. package/dist/components/PDFSidebar.d.ts.map +1 -1
  20. package/dist/components/SimplePDFReader.d.ts +1 -1
  21. package/dist/data-table.cjs +2 -0
  22. package/dist/data-table.cjs.map +1 -0
  23. package/dist/data-table.d.ts +3 -0
  24. package/dist/data-table.d.ts.map +1 -0
  25. package/dist/data-table.js +159 -0
  26. package/dist/data-table.js.map +1 -0
  27. package/dist/delete-confirm-dialog.cjs +2 -0
  28. package/dist/delete-confirm-dialog.cjs.map +1 -0
  29. package/dist/delete-confirm-dialog.d.ts +3 -0
  30. package/dist/delete-confirm-dialog.d.ts.map +1 -0
  31. package/dist/delete-confirm-dialog.js +109 -0
  32. package/dist/delete-confirm-dialog.js.map +1 -0
  33. package/dist/file-upload.cjs +2 -0
  34. package/dist/file-upload.cjs.map +1 -0
  35. package/dist/file-upload.d.ts +3 -0
  36. package/dist/file-upload.d.ts.map +1 -0
  37. package/dist/file-upload.js +354 -0
  38. package/dist/file-upload.js.map +1 -0
  39. package/dist/{index-CN76z2bu.cjs → index-BiA_tnaq.cjs} +3 -3
  40. package/dist/{index-CN76z2bu.cjs.map → index-BiA_tnaq.cjs.map} +1 -1
  41. package/dist/{index-Ba1twkJn.js → index-BypbGNpR.js} +2 -2
  42. package/dist/{index-Ba1twkJn.js.map → index-BypbGNpR.js.map} +1 -1
  43. package/dist/index.cjs +2 -0
  44. package/dist/index.cjs.map +1 -0
  45. package/dist/index.d.ts +0 -18
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +2 -11
  48. package/dist/jsx-runtime-B4hRZ52C.js +283 -0
  49. package/dist/jsx-runtime-B4hRZ52C.js.map +1 -0
  50. package/dist/jsx-runtime-BB_1_6y_.cjs +23 -0
  51. package/dist/jsx-runtime-BB_1_6y_.cjs.map +1 -0
  52. package/dist/markdown-reader.cjs +2 -0
  53. package/dist/markdown-reader.cjs.map +1 -0
  54. package/dist/markdown-reader.d.ts +3 -0
  55. package/dist/markdown-reader.d.ts.map +1 -0
  56. package/dist/markdown-reader.js +145 -0
  57. package/dist/markdown-reader.js.map +1 -0
  58. package/dist/pdf-reader.cjs +2 -0
  59. package/dist/pdf-reader.cjs.map +1 -0
  60. package/dist/pdf-reader.d.ts +3 -0
  61. package/dist/pdf-reader.d.ts.map +1 -0
  62. package/dist/pdf-reader.js +427 -0
  63. package/dist/pdf-reader.js.map +1 -0
  64. package/dist/pdf-sidebar.cjs +2 -0
  65. package/dist/pdf-sidebar.cjs.map +1 -0
  66. package/dist/pdf-sidebar.d.ts +3 -0
  67. package/dist/pdf-sidebar.d.ts.map +1 -0
  68. package/dist/pdf-sidebar.js +5 -0
  69. package/dist/pdf-sidebar.js.map +1 -0
  70. package/dist/simple-pdf-reader.cjs +2 -0
  71. package/dist/simple-pdf-reader.cjs.map +1 -0
  72. package/dist/simple-pdf-reader.d.ts +3 -0
  73. package/dist/simple-pdf-reader.d.ts.map +1 -0
  74. package/dist/simple-pdf-reader.js +268 -0
  75. package/dist/simple-pdf-reader.js.map +1 -0
  76. package/dist/table-header.cjs +2 -0
  77. package/dist/table-header.cjs.map +1 -0
  78. package/dist/table-header.d.ts +3 -0
  79. package/dist/table-header.d.ts.map +1 -0
  80. package/dist/table-header.js +63 -0
  81. package/dist/table-header.js.map +1 -0
  82. package/dist/table-pagination.cjs +2 -0
  83. package/dist/table-pagination.cjs.map +1 -0
  84. package/dist/table-pagination.d.ts +3 -0
  85. package/dist/table-pagination.d.ts.map +1 -0
  86. package/dist/table-pagination.js +172 -0
  87. package/dist/table-pagination.js.map +1 -0
  88. package/dist/theme-switcher-content.cjs +2 -0
  89. package/dist/theme-switcher-content.cjs.map +1 -0
  90. package/dist/theme-switcher-content.d.ts +3 -0
  91. package/dist/theme-switcher-content.d.ts.map +1 -0
  92. package/dist/theme-switcher-content.js +103 -0
  93. package/dist/theme-switcher-content.js.map +1 -0
  94. package/dist/theme-switcher.cjs +2 -0
  95. package/dist/theme-switcher.cjs.map +1 -0
  96. package/dist/theme-switcher.d.ts +3 -0
  97. package/dist/theme-switcher.d.ts.map +1 -0
  98. package/dist/theme-switcher.js +140 -0
  99. package/dist/theme-switcher.js.map +1 -0
  100. package/dist/utils-B6yFEsav.js +9 -0
  101. package/dist/utils-B6yFEsav.js.map +1 -0
  102. package/dist/utils-IjLH3w2e.cjs +2 -0
  103. package/dist/utils-IjLH3w2e.cjs.map +1 -0
  104. package/dist/utils.cjs +2 -0
  105. package/dist/utils.cjs.map +1 -0
  106. package/dist/utils.d.ts +2 -0
  107. package/dist/utils.d.ts.map +1 -0
  108. package/dist/utils.js +5 -0
  109. package/dist/utils.js.map +1 -0
  110. package/package.json +161 -3
  111. package/dist/index-6lJCWESg.js +0 -1793
  112. package/dist/index-6lJCWESg.js.map +0 -1
  113. package/dist/index-C94iJmKE.cjs +0 -23
  114. package/dist/index-C94iJmKE.cjs.map +0 -1
  115. package/dist/index.c.js +0 -2
  116. package/dist/index.c.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simple-pdf-reader.d.ts","sourceRoot":"","sources":["../src/simple-pdf-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,YAAY,EACV,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,8BAA8B,CAAC"}
@@ -0,0 +1,268 @@
1
+ import { j as t } from "./jsx-runtime-B4hRZ52C.js";
2
+ import { ZoomOut as pe, ZoomIn as ve, Minimize2 as je, Maximize2 as he, ChevronLeft as ye, ChevronRight as we } from "lucide-react";
3
+ import { useState as u, useRef as V, useMemo as ke, useCallback as x, useEffect as N } from "react";
4
+ function ge({
5
+ url: b,
6
+ initialPage: J = 1,
7
+ initialScale: Q = 1,
8
+ scale: p,
9
+ onScaleChange: c,
10
+ minScale: D = 0.5,
11
+ maxScale: g = 3,
12
+ currentPage: v,
13
+ onPageChange: y,
14
+ showToolbar: K = !0,
15
+ showPagination: O = !0,
16
+ enableHotkeys: A = !0,
17
+ className: X,
18
+ containerClassName: Y,
19
+ pageClassName: _,
20
+ components: Z,
21
+ onLoadSuccess: w,
22
+ onLoadError: o,
23
+ loadingText: C = "加载中...",
24
+ errorText: H = "加载失败"
25
+ }) {
26
+ const [G, L] = u(null), [S, U] = u(J), [ee, W] = u(Q), [te, k] = u(!0), [P, F] = u(null), [l, ne] = u(0), [$, se] = u(!1), M = V(null), j = V(null), [m, re] = u(null), r = v ?? S, a = p ?? ee, ie = ke(() => {
27
+ const e = [r];
28
+ return r > 1 && e.push(r - 1), r < l && e.push(r + 1), e;
29
+ }, [r, l]), f = x(
30
+ (e) => {
31
+ const n = Math.max(l, 1), s = Math.min(Math.max(e, 1), n);
32
+ v === void 0 && U(s), y == null || y(s);
33
+ },
34
+ [l, v, y]
35
+ ), I = x(() => {
36
+ r > 1 && f(r - 1);
37
+ }, [r, f]), R = x(() => {
38
+ r < l && f(r + 1);
39
+ }, [r, l, f]), E = x(() => {
40
+ const e = Math.min(a + 0.25, g);
41
+ p === void 0 && W(e), c == null || c(e);
42
+ }, [a, g, p, c]), T = x(() => {
43
+ const e = Math.max(a - 0.25, D);
44
+ p === void 0 && W(e), c == null || c(e);
45
+ }, [a, D, p, c]), z = x(async () => {
46
+ var e, n, s;
47
+ if (!(typeof document > "u")) {
48
+ if (!document.fullscreenElement) {
49
+ await ((n = (e = M.current) == null ? void 0 : e.requestFullscreen) == null ? void 0 : n.call(e));
50
+ return;
51
+ }
52
+ await ((s = document.exitFullscreen) == null ? void 0 : s.call(document));
53
+ }
54
+ }, []);
55
+ if (N(() => {
56
+ let e = !0;
57
+ return (async () => {
58
+ try {
59
+ const s = await import("./index-BypbGNpR.js");
60
+ if (typeof window < "u") {
61
+ const i = s.pdfjs, d = i == null ? void 0 : i.version;
62
+ i != null && i.GlobalWorkerOptions && d && (i.GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${d}/build/pdf.worker.min.mjs`);
63
+ }
64
+ e && re(s);
65
+ } catch (s) {
66
+ if (e) {
67
+ const i = s instanceof Error ? s : new Error("无法加载 react-pdf 库");
68
+ F(i), k(!1), o == null || o(i);
69
+ }
70
+ }
71
+ })(), () => {
72
+ e = !1;
73
+ };
74
+ }, []), N(() => {
75
+ if (!m || !b) return;
76
+ let e = !0;
77
+ return (async () => {
78
+ k(!0), F(null);
79
+ try {
80
+ const { Document: s } = m;
81
+ if (!s)
82
+ throw new Error("react-pdf Document 组件不可用");
83
+ const i = m.pdfjs.getDocument(b);
84
+ j.current = i;
85
+ const d = await i.promise;
86
+ e && (L(d), ne(d.numPages), v === void 0 && U(
87
+ (xe) => Math.max(1, Math.min(xe, d.numPages))
88
+ ), k(!1), w == null || w(d));
89
+ } catch (s) {
90
+ if (e) {
91
+ const i = s instanceof Error ? s : new Error("PDF 加载失败");
92
+ F(i), k(!1), o == null || o(i);
93
+ }
94
+ }
95
+ })(), () => {
96
+ e = !1, j.current && typeof j.current.destroy == "function" && (j.current.destroy(), j.current = null);
97
+ };
98
+ }, [m, b, v, w, o]), N(() => {
99
+ if (typeof document > "u") return;
100
+ const e = () => {
101
+ se(document.fullscreenElement === M.current);
102
+ };
103
+ return document.addEventListener("fullscreenchange", e), () => {
104
+ document.removeEventListener("fullscreenchange", e);
105
+ };
106
+ }, []), N(() => {
107
+ if (!A) return;
108
+ const e = (n) => {
109
+ const s = document.activeElement;
110
+ s && (s.tagName === "INPUT" || s.getAttribute("role") === "input") || ((n.ctrlKey || n.metaKey) && (n.key === "=" || n.key === "+") ? (n.preventDefault(), E()) : (n.ctrlKey || n.metaKey) && n.key === "-" ? (n.preventDefault(), T()) : n.key === "ArrowLeft" ? (n.preventDefault(), I()) : n.key === "ArrowRight" ? (n.preventDefault(), R()) : (n.key === "f" || n.key === "F") && (n.preventDefault(), z()));
111
+ };
112
+ return document.addEventListener("keydown", e), () => {
113
+ document.removeEventListener("keydown", e);
114
+ };
115
+ }, [
116
+ A,
117
+ E,
118
+ T,
119
+ I,
120
+ R,
121
+ z
122
+ ]), !Z)
123
+ return /* @__PURE__ */ t.jsx("div", { className: "p-4 text-center text-destructive", children: "错误:请通过 components prop 注入 UI 组件" });
124
+ const {
125
+ Card: le,
126
+ CardContent: ce,
127
+ Button: h,
128
+ Input: q,
129
+ Skeleton: B
130
+ } = Z, ae = () => K ? /* @__PURE__ */ t.jsxs("div", { className: "flex items-center gap-2", children: [
131
+ /* @__PURE__ */ t.jsx(
132
+ h,
133
+ {
134
+ variant: "outline",
135
+ size: "sm",
136
+ onClick: T,
137
+ disabled: a <= D,
138
+ children: /* @__PURE__ */ t.jsx(pe, {})
139
+ }
140
+ ),
141
+ /* @__PURE__ */ t.jsxs("span", { className: "text-sm", children: [
142
+ Math.round(a * 100),
143
+ "%"
144
+ ] }),
145
+ /* @__PURE__ */ t.jsx(
146
+ h,
147
+ {
148
+ variant: "outline",
149
+ size: "sm",
150
+ onClick: E,
151
+ disabled: a >= g,
152
+ children: /* @__PURE__ */ t.jsx(ve, {})
153
+ }
154
+ ),
155
+ /* @__PURE__ */ t.jsx(
156
+ h,
157
+ {
158
+ variant: "outline",
159
+ size: "icon",
160
+ onClick: () => {
161
+ z();
162
+ },
163
+ children: $ ? /* @__PURE__ */ t.jsx(je, {}) : /* @__PURE__ */ t.jsx(he, {})
164
+ }
165
+ )
166
+ ] }) : null, ue = () => O ? /* @__PURE__ */ t.jsxs("div", { className: "flex items-center gap-2", children: [
167
+ /* @__PURE__ */ t.jsx(
168
+ h,
169
+ {
170
+ variant: "outline",
171
+ size: "icon",
172
+ onClick: I,
173
+ disabled: r <= 1,
174
+ children: /* @__PURE__ */ t.jsx(ye, {})
175
+ }
176
+ ),
177
+ q ? /* @__PURE__ */ t.jsx(
178
+ q,
179
+ {
180
+ type: "number",
181
+ min: 1,
182
+ max: Math.max(l, 1),
183
+ value: r,
184
+ onChange: (e) => f(parseInt(e.target.value, 10) || 1),
185
+ className: "w-16 text-center"
186
+ }
187
+ ) : /* @__PURE__ */ t.jsx(
188
+ "input",
189
+ {
190
+ type: "number",
191
+ min: 1,
192
+ max: Math.max(l, 1),
193
+ value: r,
194
+ onChange: (e) => f(parseInt(e.target.value, 10) || 1),
195
+ className: "w-16 rounded-md border border-input bg-background px-2 text-center text-sm"
196
+ }
197
+ ),
198
+ /* @__PURE__ */ t.jsxs("span", { className: "text-sm text-muted-foreground", children: [
199
+ "/ ",
200
+ l
201
+ ] }),
202
+ /* @__PURE__ */ t.jsx(
203
+ h,
204
+ {
205
+ variant: "outline",
206
+ size: "icon",
207
+ onClick: R,
208
+ disabled: r >= l,
209
+ children: /* @__PURE__ */ t.jsx(we, {})
210
+ }
211
+ )
212
+ ] }) : null, oe = () => !K && !O ? null : /* @__PURE__ */ t.jsxs(
213
+ "div",
214
+ {
215
+ "data-testid": "pdf-operations-bar",
216
+ className: "flex items-center justify-between gap-4 border-b px-4 py-2",
217
+ children: [
218
+ ae(),
219
+ ue()
220
+ ]
221
+ }
222
+ ), de = () => /* @__PURE__ */ t.jsxs("div", { className: "flex flex-col items-center justify-center space-y-4 p-8", children: [
223
+ /* @__PURE__ */ t.jsx(B, { className: "h-8 w-32" }),
224
+ /* @__PURE__ */ t.jsx(B, { className: "h-64 w-full max-w-2xl" }),
225
+ /* @__PURE__ */ t.jsx("p", { className: "text-sm text-muted-foreground", children: C })
226
+ ] }), me = () => /* @__PURE__ */ t.jsx("div", { className: "flex flex-col items-center justify-center space-y-4 p-8", children: /* @__PURE__ */ t.jsxs("div", { className: "text-center text-destructive", children: [
227
+ /* @__PURE__ */ t.jsx("p", { className: "font-medium", children: H }),
228
+ P && /* @__PURE__ */ t.jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: P.message })
229
+ ] }) }), fe = () => {
230
+ if (!m || !G) return null;
231
+ const { Page: e } = m;
232
+ return /* @__PURE__ */ t.jsx(
233
+ "div",
234
+ {
235
+ className: `flex flex-col items-center justify-center ${$ ? "h-[calc(100vh-56px)] overflow-auto" : "overflow-visible"}`,
236
+ children: ie.map((n) => /* @__PURE__ */ t.jsx(
237
+ "div",
238
+ {
239
+ className: _,
240
+ style: {
241
+ display: n === r ? "block" : "none"
242
+ },
243
+ children: /* @__PURE__ */ t.jsx(
244
+ e,
245
+ {
246
+ pdf: G,
247
+ pageNumber: n,
248
+ scale: a,
249
+ renderTextLayer: !1,
250
+ renderAnnotationLayer: !1,
251
+ className: "shadow-md"
252
+ }
253
+ )
254
+ },
255
+ n
256
+ ))
257
+ }
258
+ );
259
+ };
260
+ return /* @__PURE__ */ t.jsx("div", { ref: M, children: /* @__PURE__ */ t.jsxs(le, { className: X, children: [
261
+ oe(),
262
+ /* @__PURE__ */ t.jsx(ce, { className: Y, children: te ? de() : P ? me() : fe() })
263
+ ] }) });
264
+ }
265
+ export {
266
+ ge as SimplePDFReader
267
+ };
268
+ //# sourceMappingURL=simple-pdf-reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simple-pdf-reader.js","sources":["../src/components/SimplePDFReader.tsx"],"sourcesContent":["import {\n ChevronLeft as ChevronLeftIcon,\n ChevronRight as ChevronRightIcon,\n Maximize2 as Maximize2Icon,\n Minimize2 as Minimize2Icon,\n ZoomIn as ZoomInIcon,\n ZoomOut as ZoomOutIcon,\n} from 'lucide-react';\nimport { useState, useMemo, useCallback, useEffect, useRef } from 'react';\nimport type { HTMLAttributes } from 'react';\n\nimport type {\n CardComponent,\n ButtonComponent,\n InputComponent,\n SkeletonComponent,\n} from '../types/component-types';\n\n/**\n * react-pdf 类型定义\n * 这些类型由 @types/react-pdf 提供,这里我们定义必要的接口\n */\nexport interface PDFDocumentProxy {\n numPages: number;\n getPage(pageNumber: number): Promise<any>;\n}\n\n/**\n * SimplePDFReader UI 组件接口\n */\nexport interface SimplePDFReaderUIComponents {\n Card: CardComponent;\n CardContent: React.ComponentType<HTMLAttributes<HTMLDivElement>>;\n Button: ButtonComponent;\n Input?: InputComponent;\n Skeleton: SkeletonComponent;\n}\n\n/**\n * SimplePDFReader 组件 Props\n */\nexport interface SimplePDFReaderProps {\n // PDF 数据源\n url: string;\n\n // 初始状态\n initialPage?: number;\n initialScale?: number;\n\n // 缩放控制\n scale?: number;\n onScaleChange?: (scale: number) => void;\n minScale?: number;\n maxScale?: number;\n\n // 页面导航\n currentPage?: number;\n onPageChange?: (page: number) => void;\n\n // 功能开关\n showToolbar?: boolean; // 默认 true\n showPagination?: boolean; // 默认 true\n enableHotkeys?: boolean; // 默认 true\n\n // 样式定制\n className?: string;\n containerClassName?: string;\n pageClassName?: string;\n\n // UI 组件注入\n components: SimplePDFReaderUIComponents;\n\n // 回调函数\n onLoadSuccess?: (pdf: PDFDocumentProxy) => void;\n onLoadError?: (error: Error) => void;\n\n // 加载状态文本\n loadingText?: string; // 默认 \"加载中...\"\n errorText?: string; // 默认 \"加载失败\"\n}\n\n/**\n * SimplePDFReader 组件\n *\n * 用于在 React 应用中展示和浏览 PDF 文档。\n * 支持从 URL 加载 PDF,提供基础的浏览功能(翻页、缩放),并包含性能优化。\n *\n * @example\n * ```tsx\n * import { SimplePDFReader } from '@turinhub/atomix-common-ui/simple-pdf-reader';\n * import { Card, Button, Skeleton } from '@/components/ui';\n *\n * <SimplePDFReader\n * url=\"/documents/sample.pdf\"\n * components={{\n * Card,\n * CardContent: Card.Content,\n * Button,\n * Skeleton,\n * }}\n * initialPage={1}\n * initialScale={1.0}\n * showToolbar={true}\n * showPagination={true}\n * enableHotkeys={true}\n * onPageChange={(page) => console.log('Current page:', page)}\n * />\n *\n * Keyboard Shortcuts:\n * - Arrow Left/Right: Navigate pages\n * - Ctrl/Cmd + +/-: Zoom in/out\n * - F: Toggle fullscreen\n * ```\n */\nexport function SimplePDFReader({\n url,\n initialPage = 1,\n initialScale = 1.0,\n scale: controlledScale,\n onScaleChange,\n minScale = 0.5,\n maxScale = 3.0,\n currentPage: controlledPage,\n onPageChange,\n showToolbar = true,\n showPagination = true,\n enableHotkeys = true,\n className,\n containerClassName,\n pageClassName,\n components,\n onLoadSuccess,\n onLoadError,\n loadingText = '加载中...',\n errorText = '加载失败',\n}: SimplePDFReaderProps) {\n // ==================== React Hooks (必须在最顶部) ====================\n\n // 状态管理\n const [pdfDocument, setPdfDocument] = useState<PDFDocumentProxy | null>(null);\n const [internalPage, setInternalPage] = useState(initialPage);\n const [internalScale, setInternalScale] = useState(initialScale);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const [totalPages, setTotalPages] = useState(0);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const readerRef = useRef<HTMLDivElement>(null);\n const loadingTaskRef = useRef<any>(null);\n\n // 动态导入 react-pdf\n const [ReactPDF, setReactPDF] = useState<any>(null);\n\n // 使用受控或非受控模式\n const currentPage = controlledPage ?? internalPage;\n const scale = controlledScale ?? internalScale;\n\n // 预渲染相邻页面以提升翻页体验\n const pagesToPreload = useMemo(() => {\n const pages = [currentPage];\n if (currentPage > 1) pages.push(currentPage - 1);\n if (currentPage < totalPages) pages.push(currentPage + 1);\n return pages;\n }, [currentPage, totalPages]);\n\n // 页面导航处理\n const goToPage = useCallback(\n (page: number) => {\n const maxPage = Math.max(totalPages, 1);\n const targetPage = Math.min(Math.max(page, 1), maxPage);\n if (controlledPage === undefined) {\n setInternalPage(targetPage);\n }\n onPageChange?.(targetPage);\n },\n [totalPages, controlledPage, onPageChange]\n );\n\n const handlePreviousPage = useCallback(() => {\n if (currentPage > 1) {\n goToPage(currentPage - 1);\n }\n }, [currentPage, goToPage]);\n\n const handleNextPage = useCallback(() => {\n if (currentPage < totalPages) {\n goToPage(currentPage + 1);\n }\n }, [currentPage, totalPages, goToPage]);\n\n // 缩放控制处理\n const handleZoomIn = useCallback(() => {\n const newScale = Math.min(scale + 0.25, maxScale);\n if (controlledScale === undefined) {\n setInternalScale(newScale);\n }\n onScaleChange?.(newScale);\n }, [scale, maxScale, controlledScale, onScaleChange]);\n\n const handleZoomOut = useCallback(() => {\n const newScale = Math.max(scale - 0.25, minScale);\n if (controlledScale === undefined) {\n setInternalScale(newScale);\n }\n onScaleChange?.(newScale);\n }, [scale, minScale, controlledScale, onScaleChange]);\n\n const handleToggleFullscreen = useCallback(async () => {\n if (typeof document === 'undefined') return;\n\n if (!document.fullscreenElement) {\n await readerRef.current?.requestFullscreen?.();\n return;\n }\n\n await document.exitFullscreen?.();\n }, []);\n\n // 动态导入 react-pdf 和设置 worker\n useEffect(() => {\n let isMounted = true;\n\n const loadReactPDF = async () => {\n try {\n // 动态导入 react-pdf\n const pdfModule = await import('react-pdf');\n // 设置 worker\n if (typeof window !== 'undefined') {\n const pdfjs = (pdfModule as any).pdfjs;\n const workerVersion = pdfjs?.version;\n if (pdfjs?.GlobalWorkerOptions && workerVersion) {\n pdfjs.GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${workerVersion}/build/pdf.worker.min.mjs`;\n }\n }\n if (isMounted) {\n setReactPDF(pdfModule);\n }\n } catch (err) {\n if (isMounted) {\n const loadError =\n err instanceof Error ? err : new Error('无法加载 react-pdf 库');\n setError(loadError);\n setIsLoading(false);\n onLoadError?.(loadError);\n }\n }\n };\n\n loadReactPDF();\n\n return () => {\n isMounted = false;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // 加载 PDF 文档\n useEffect(() => {\n if (!ReactPDF || !url) return;\n\n let isMounted = true;\n\n const loadPDF = async () => {\n setIsLoading(true);\n setError(null);\n\n try {\n const { Document } = ReactPDF;\n if (!Document) {\n throw new Error('react-pdf Document 组件不可用');\n }\n\n // 使用 react-pdf 的 Document 组件内部加载逻辑\n // 我们创建一个加载器来获取 PDF 文档信息\n const loadingTask = (ReactPDF.pdfjs as any).getDocument(url);\n loadingTaskRef.current = loadingTask;\n const pdf = await loadingTask.promise;\n\n if (isMounted) {\n setPdfDocument(pdf as PDFDocumentProxy);\n setTotalPages(pdf.numPages);\n if (controlledPage === undefined) {\n setInternalPage((prev) =>\n Math.max(1, Math.min(prev, pdf.numPages))\n );\n }\n setIsLoading(false);\n onLoadSuccess?.(pdf as PDFDocumentProxy);\n }\n } catch (err) {\n if (isMounted) {\n const loadError =\n err instanceof Error ? err : new Error('PDF 加载失败');\n setError(loadError);\n setIsLoading(false);\n onLoadError?.(loadError);\n }\n }\n };\n\n loadPDF();\n\n return () => {\n isMounted = false;\n if (\n loadingTaskRef.current &&\n typeof loadingTaskRef.current.destroy === 'function'\n ) {\n loadingTaskRef.current.destroy();\n loadingTaskRef.current = null;\n }\n };\n }, [ReactPDF, url, controlledPage, onLoadSuccess, onLoadError]);\n\n useEffect(() => {\n if (typeof document === 'undefined') return;\n\n const handleFullscreenChange = () => {\n setIsFullscreen(document.fullscreenElement === readerRef.current);\n };\n\n document.addEventListener('fullscreenchange', handleFullscreenChange);\n\n return () => {\n document.removeEventListener('fullscreenchange', handleFullscreenChange);\n };\n }, []);\n\n // ==================== 键盘快捷键 ====================\n useEffect(() => {\n if (!enableHotkeys) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n // Don't handle hotkeys if input is focused\n const activeElement = document.activeElement;\n if (\n activeElement &&\n (activeElement.tagName === 'INPUT' ||\n activeElement.getAttribute('role') === 'input')\n ) {\n return;\n }\n\n // Ctrl/Cmd + 加号: 放大\n if ((e.ctrlKey || e.metaKey) && (e.key === '=' || e.key === '+')) {\n e.preventDefault();\n handleZoomIn();\n }\n // Ctrl/Cmd + 减号: 缩小\n else if ((e.ctrlKey || e.metaKey) && e.key === '-') {\n e.preventDefault();\n handleZoomOut();\n }\n // 左箭头: 上一页\n else if (e.key === 'ArrowLeft') {\n e.preventDefault();\n handlePreviousPage();\n }\n // 右箭头: 下一页\n else if (e.key === 'ArrowRight') {\n e.preventDefault();\n handleNextPage();\n }\n // F 键: 全屏切换\n else if (e.key === 'f' || e.key === 'F') {\n e.preventDefault();\n void handleToggleFullscreen();\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => {\n document.removeEventListener('keydown', handleKeyDown);\n };\n }, [\n enableHotkeys,\n handleZoomIn,\n handleZoomOut,\n handlePreviousPage,\n handleNextPage,\n handleToggleFullscreen,\n ]);\n\n // ==================== 组件验证和渲染 ====================\n\n // 验证 components\n if (!components) {\n return (\n <div className=\"p-4 text-center text-destructive\">\n 错误:请通过 components prop 注入 UI 组件\n </div>\n );\n }\n\n const {\n Card,\n CardContent,\n Button,\n Input: InputComponentInjected,\n Skeleton,\n } = components;\n\n // 渲染工具栏\n const renderToolbar = () => {\n if (!showToolbar) return null;\n\n return (\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleZoomOut}\n disabled={scale <= minScale}\n >\n <ZoomOutIcon />\n </Button>\n <span className=\"text-sm\">{Math.round(scale * 100)}%</span>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleZoomIn}\n disabled={scale >= maxScale}\n >\n <ZoomInIcon />\n </Button>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => {\n void handleToggleFullscreen();\n }}\n >\n {isFullscreen ? <Minimize2Icon /> : <Maximize2Icon />}\n </Button>\n </div>\n );\n };\n\n // 渲染分页控制\n const renderPagination = () => {\n if (!showPagination) return null;\n\n return (\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={handlePreviousPage}\n disabled={currentPage <= 1}\n >\n <ChevronLeftIcon />\n </Button>\n {InputComponentInjected ? (\n <InputComponentInjected\n type=\"number\"\n min={1}\n max={Math.max(totalPages, 1)}\n value={currentPage}\n onChange={(e) => goToPage(parseInt(e.target.value, 10) || 1)}\n className=\"w-16 text-center\"\n />\n ) : (\n <input\n type=\"number\"\n min={1}\n max={Math.max(totalPages, 1)}\n value={currentPage}\n onChange={(e) => goToPage(parseInt(e.target.value, 10) || 1)}\n className=\"w-16 rounded-md border border-input bg-background px-2 text-center text-sm\"\n />\n )}\n <span className=\"text-sm text-muted-foreground\">/ {totalPages}</span>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={handleNextPage}\n disabled={currentPage >= totalPages}\n >\n <ChevronRightIcon />\n </Button>\n </div>\n );\n };\n\n const renderOperations = () => {\n if (!showToolbar && !showPagination) return null;\n\n return (\n <div\n data-testid=\"pdf-operations-bar\"\n className=\"flex items-center justify-between gap-4 border-b px-4 py-2\"\n >\n {renderToolbar()}\n {renderPagination()}\n </div>\n );\n };\n\n // 渲染加载状态\n const renderLoading = () => (\n <div className=\"flex flex-col items-center justify-center space-y-4 p-8\">\n <Skeleton className=\"h-8 w-32\" />\n <Skeleton className=\"h-64 w-full max-w-2xl\" />\n <p className=\"text-sm text-muted-foreground\">{loadingText}</p>\n </div>\n );\n\n // 渲染错误状态\n const renderError = () => (\n <div className=\"flex flex-col items-center justify-center space-y-4 p-8\">\n <div className=\"text-center text-destructive\">\n <p className=\"font-medium\">{errorText}</p>\n {error && (\n <p className=\"mt-2 text-sm text-muted-foreground\">{error.message}</p>\n )}\n </div>\n </div>\n );\n\n // 渲染 PDF 文档\n const renderPDFDocument = () => {\n if (!ReactPDF || !pdfDocument) return null;\n\n const { Page } = ReactPDF;\n\n return (\n <div\n className={`flex flex-col items-center justify-center ${\n isFullscreen\n ? 'h-[calc(100vh-56px)] overflow-auto'\n : 'overflow-visible'\n }`}\n >\n {pagesToPreload.map((pageNum) => (\n <div\n key={pageNum}\n className={pageClassName}\n style={{\n display: pageNum === currentPage ? 'block' : 'none',\n }}\n >\n <Page\n pdf={pdfDocument}\n pageNumber={pageNum}\n scale={scale}\n renderTextLayer={false}\n renderAnnotationLayer={false}\n className=\"shadow-md\"\n />\n </div>\n ))}\n </div>\n );\n };\n\n return (\n <div ref={readerRef}>\n <Card className={className}>\n {renderOperations()}\n <CardContent className={containerClassName}>\n {isLoading\n ? renderLoading()\n : error\n ? renderError()\n : renderPDFDocument()}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"names":["SimplePDFReader","url","initialPage","initialScale","controlledScale","onScaleChange","minScale","maxScale","controlledPage","onPageChange","showToolbar","showPagination","enableHotkeys","className","containerClassName","pageClassName","components","onLoadSuccess","onLoadError","loadingText","errorText","pdfDocument","setPdfDocument","useState","internalPage","setInternalPage","internalScale","setInternalScale","isLoading","setIsLoading","error","setError","totalPages","setTotalPages","isFullscreen","setIsFullscreen","readerRef","useRef","loadingTaskRef","ReactPDF","setReactPDF","currentPage","scale","pagesToPreload","useMemo","pages","goToPage","useCallback","page","maxPage","targetPage","handlePreviousPage","handleNextPage","handleZoomIn","newScale","handleZoomOut","handleToggleFullscreen","_b","_a","_c","useEffect","isMounted","pdfModule","pdfjs","workerVersion","err","loadError","Document","loadingTask","pdf","prev","handleFullscreenChange","handleKeyDown","e","activeElement","jsx","Card","CardContent","Button","InputComponentInjected","Skeleton","renderToolbar","jsxs","ZoomOutIcon","ZoomInIcon","Minimize2Icon","Maximize2Icon","renderPagination","ChevronLeftIcon","ChevronRightIcon","renderOperations","renderLoading","renderError","renderPDFDocument","Page","pageNum"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./jsx-runtime-BB_1_6y_.cjs"),d=require("lucide-react");function R({title:u,searchPlaceholder:c="搜索...",searchValue:j="",onSearchChange:t,onSearch:s,showSearch:p=!0,action:i,actionLabel:l,onActionClick:n,loading:x=!1,components:m}){if(!m)return e.jsxRuntimeExports.jsx("div",{className:"p-4 text-center text-destructive",children:"错误:请通过 components prop 注入 UI 组件"});const{Input:o,Button:a}=m,f=r=>{r.key==="Enter"&&s&&s()};return e.jsxRuntimeExports.jsxs("div",{className:"flex flex-col gap-4 p-1",children:[e.jsxRuntimeExports.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxRuntimeExports.jsx("div",{className:"text-lg font-semibold leading-none tracking-tight",children:u}),e.jsxRuntimeExports.jsxs("div",{className:"flex items-center gap-2",children:[i&&e.jsxRuntimeExports.jsx("div",{className:"flex items-center gap-2",children:i}),!i&&l&&n&&e.jsxRuntimeExports.jsxs(a,{onClick:n,disabled:x,size:"sm",children:[e.jsxRuntimeExports.jsx(d.Plus,{className:"mr-2 h-4 w-4"}),l]})]})]}),p&&e.jsxRuntimeExports.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxRuntimeExports.jsxs("div",{className:"relative max-w-sm flex-1",children:[e.jsxRuntimeExports.jsx(d.Search,{className:"absolute left-2 top-2.5 h-4 w-4 text-muted-foreground"}),e.jsxRuntimeExports.jsx(o,{placeholder:c,value:j,onChange:r=>t==null?void 0:t(r.target.value),onKeyPress:f,className:"h-9 pl-8",disabled:x})]}),s&&e.jsxRuntimeExports.jsx(a,{onClick:s,disabled:x,variant:"secondary",size:"sm",children:"搜索"})]})]})}exports.TableHeader=R;
2
+ //# sourceMappingURL=table-header.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-header.cjs","sources":["../src/components/TableHeader.tsx"],"sourcesContent":["import { Plus, Search } from 'lucide-react';\nimport { ReactNode } from 'react';\n\nimport type { InputComponent, ButtonComponent } from '../types/component-types';\n\n/**\n * UI 组件适配器接口\n */\nexport interface HeaderUIComponents {\n Input: InputComponent;\n Button: ButtonComponent;\n}\n\nexport interface TableHeaderProps {\n title: ReactNode;\n searchPlaceholder?: string;\n searchValue?: string;\n onSearchChange?: (value: string) => void;\n onSearch?: () => void;\n showSearch?: boolean;\n action?: ReactNode;\n actionLabel?: string;\n onActionClick?: () => void;\n loading?: boolean;\n\n // UI 组件注入\n components?: HeaderUIComponents;\n}\n\n/**\n * 表格头部组件\n * 支持搜索框和操作按钮\n */\nexport function TableHeader({\n title,\n searchPlaceholder = '搜索...',\n searchValue = '',\n onSearchChange,\n onSearch,\n showSearch = true,\n action,\n actionLabel,\n onActionClick,\n loading = false,\n components,\n}: TableHeaderProps) {\n if (!components) {\n return (\n <div className=\"p-4 text-center text-destructive\">\n 错误:请通过 components prop 注入 UI 组件\n </div>\n );\n }\n\n const { Input, Button } = components;\n\n const handleKeyPress = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && onSearch) {\n onSearch();\n }\n };\n\n return (\n <div className=\"flex flex-col gap-4 p-1\">\n <div className=\"flex items-center justify-between\">\n <div className=\"text-lg font-semibold leading-none tracking-tight\">\n {title}\n </div>\n <div className=\"flex items-center gap-2\">\n {action && <div className=\"flex items-center gap-2\">{action}</div>}\n {!action && actionLabel && onActionClick && (\n <Button onClick={onActionClick} disabled={loading} size=\"sm\">\n <Plus className=\"mr-2 h-4 w-4\" />\n {actionLabel}\n </Button>\n )}\n </div>\n </div>\n {showSearch && (\n <div className=\"flex items-center gap-2\">\n <div className=\"relative max-w-sm flex-1\">\n <Search className=\"absolute left-2 top-2.5 h-4 w-4 text-muted-foreground\" />\n <Input\n placeholder={searchPlaceholder}\n value={searchValue}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n onSearchChange?.(e.target.value)\n }\n onKeyPress={handleKeyPress}\n className=\"h-9 pl-8\"\n disabled={loading}\n />\n </div>\n {onSearch && (\n <Button\n onClick={onSearch}\n disabled={loading}\n variant=\"secondary\"\n size=\"sm\"\n >\n 搜索\n </Button>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"names":["TableHeader","title","searchPlaceholder","searchValue","onSearchChange","onSearch","showSearch","action","actionLabel","onActionClick","loading","components","jsx","Input","Button","handleKeyPress","e","jsxs","Plus","Search"],"mappings":"wJAiCO,SAASA,EAAY,CAC1B,MAAAC,EACA,kBAAAC,EAAoB,QACpB,YAAAC,EAAc,GACd,eAAAC,EACA,SAAAC,EACA,WAAAC,EAAa,GACb,OAAAC,EACA,YAAAC,EACA,cAAAC,EACA,QAAAC,EAAU,GACV,WAAAC,CACF,EAAqB,CACnB,GAAI,CAACA,EACH,OACEC,EAAAA,kBAAAA,IAAC,MAAA,CAAI,UAAU,mCAAmC,SAAA,kCAElD,EAIJ,KAAM,CAAE,MAAAC,EAAO,OAAAC,CAAA,EAAWH,EAEpBI,EAAkBC,GAA2B,CAC7CA,EAAE,MAAQ,SAAWX,GACvBA,EAAA,CAEJ,EAEA,OACEY,EAAAA,kBAAAA,KAAC,MAAA,CAAI,UAAU,0BACb,SAAA,CAAAA,EAAAA,kBAAAA,KAAC,MAAA,CAAI,UAAU,oCACb,SAAA,CAAAL,EAAAA,kBAAAA,IAAC,MAAA,CAAI,UAAU,oDACZ,SAAAX,EACH,EACAgB,EAAAA,kBAAAA,KAAC,MAAA,CAAI,UAAU,0BACZ,SAAA,CAAAV,GAAUK,EAAAA,kBAAAA,IAAC,MAAA,CAAI,UAAU,0BAA2B,SAAAL,EAAO,EAC3D,CAACA,GAAUC,GAAeC,GACzBQ,EAAAA,kBAAAA,KAACH,EAAA,CAAO,QAASL,EAAe,SAAUC,EAAS,KAAK,KACtD,SAAA,CAAAE,EAAAA,kBAAAA,IAACM,EAAAA,KAAA,CAAK,UAAU,cAAA,CAAe,EAC9BV,CAAA,CAAA,CACH,CAAA,CAAA,CAEJ,CAAA,EACF,EACCF,GACCW,EAAAA,kBAAAA,KAAC,MAAA,CAAI,UAAU,0BACb,SAAA,CAAAA,EAAAA,kBAAAA,KAAC,MAAA,CAAI,UAAU,2BACb,SAAA,CAAAL,EAAAA,kBAAAA,IAACO,EAAAA,OAAA,CAAO,UAAU,uDAAA,CAAwD,EAC1EP,EAAAA,kBAAAA,IAACC,EAAA,CACC,YAAaX,EACb,MAAOC,EACP,SAAWa,GACTZ,GAAA,YAAAA,EAAiBY,EAAE,OAAO,OAE5B,WAAYD,EACZ,UAAU,WACV,SAAUL,CAAA,CAAA,CACZ,EACF,EACCL,GACCO,EAAAA,kBAAAA,IAACE,EAAA,CACC,QAAST,EACT,SAAUK,EACV,QAAQ,YACR,KAAK,KACN,SAAA,IAAA,CAAA,CAED,CAAA,CAEJ,CAAA,EAEJ,CAEJ"}
@@ -0,0 +1,3 @@
1
+ export { TableHeader } from './components/TableHeader';
2
+ export type { HeaderUIComponents, TableHeaderProps, } from './components/TableHeader';
3
+ //# sourceMappingURL=table-header.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-header.d.ts","sourceRoot":"","sources":["../src/table-header.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,YAAY,EACV,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,63 @@
1
+ import { j as e } from "./jsx-runtime-B4hRZ52C.js";
2
+ import { Plus as v, Search as o } from "lucide-react";
3
+ function b({
4
+ title: c,
5
+ searchPlaceholder: n = "搜索...",
6
+ searchValue: f = "",
7
+ onSearchChange: l,
8
+ onSearch: s,
9
+ showSearch: j = !0,
10
+ action: t,
11
+ actionLabel: a,
12
+ onActionClick: d,
13
+ loading: i = !1,
14
+ components: x
15
+ }) {
16
+ if (!x)
17
+ return /* @__PURE__ */ e.jsx("div", { className: "p-4 text-center text-destructive", children: "错误:请通过 components prop 注入 UI 组件" });
18
+ const { Input: p, Button: m } = x, u = (r) => {
19
+ r.key === "Enter" && s && s();
20
+ };
21
+ return /* @__PURE__ */ e.jsxs("div", { className: "flex flex-col gap-4 p-1", children: [
22
+ /* @__PURE__ */ e.jsxs("div", { className: "flex items-center justify-between", children: [
23
+ /* @__PURE__ */ e.jsx("div", { className: "text-lg font-semibold leading-none tracking-tight", children: c }),
24
+ /* @__PURE__ */ e.jsxs("div", { className: "flex items-center gap-2", children: [
25
+ t && /* @__PURE__ */ e.jsx("div", { className: "flex items-center gap-2", children: t }),
26
+ !t && a && d && /* @__PURE__ */ e.jsxs(m, { onClick: d, disabled: i, size: "sm", children: [
27
+ /* @__PURE__ */ e.jsx(v, { className: "mr-2 h-4 w-4" }),
28
+ a
29
+ ] })
30
+ ] })
31
+ ] }),
32
+ j && /* @__PURE__ */ e.jsxs("div", { className: "flex items-center gap-2", children: [
33
+ /* @__PURE__ */ e.jsxs("div", { className: "relative max-w-sm flex-1", children: [
34
+ /* @__PURE__ */ e.jsx(o, { className: "absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" }),
35
+ /* @__PURE__ */ e.jsx(
36
+ p,
37
+ {
38
+ placeholder: n,
39
+ value: f,
40
+ onChange: (r) => l == null ? void 0 : l(r.target.value),
41
+ onKeyPress: u,
42
+ className: "h-9 pl-8",
43
+ disabled: i
44
+ }
45
+ )
46
+ ] }),
47
+ s && /* @__PURE__ */ e.jsx(
48
+ m,
49
+ {
50
+ onClick: s,
51
+ disabled: i,
52
+ variant: "secondary",
53
+ size: "sm",
54
+ children: "搜索"
55
+ }
56
+ )
57
+ ] })
58
+ ] });
59
+ }
60
+ export {
61
+ b as TableHeader
62
+ };
63
+ //# sourceMappingURL=table-header.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-header.js","sources":["../src/components/TableHeader.tsx"],"sourcesContent":["import { Plus, Search } from 'lucide-react';\nimport { ReactNode } from 'react';\n\nimport type { InputComponent, ButtonComponent } from '../types/component-types';\n\n/**\n * UI 组件适配器接口\n */\nexport interface HeaderUIComponents {\n Input: InputComponent;\n Button: ButtonComponent;\n}\n\nexport interface TableHeaderProps {\n title: ReactNode;\n searchPlaceholder?: string;\n searchValue?: string;\n onSearchChange?: (value: string) => void;\n onSearch?: () => void;\n showSearch?: boolean;\n action?: ReactNode;\n actionLabel?: string;\n onActionClick?: () => void;\n loading?: boolean;\n\n // UI 组件注入\n components?: HeaderUIComponents;\n}\n\n/**\n * 表格头部组件\n * 支持搜索框和操作按钮\n */\nexport function TableHeader({\n title,\n searchPlaceholder = '搜索...',\n searchValue = '',\n onSearchChange,\n onSearch,\n showSearch = true,\n action,\n actionLabel,\n onActionClick,\n loading = false,\n components,\n}: TableHeaderProps) {\n if (!components) {\n return (\n <div className=\"p-4 text-center text-destructive\">\n 错误:请通过 components prop 注入 UI 组件\n </div>\n );\n }\n\n const { Input, Button } = components;\n\n const handleKeyPress = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && onSearch) {\n onSearch();\n }\n };\n\n return (\n <div className=\"flex flex-col gap-4 p-1\">\n <div className=\"flex items-center justify-between\">\n <div className=\"text-lg font-semibold leading-none tracking-tight\">\n {title}\n </div>\n <div className=\"flex items-center gap-2\">\n {action && <div className=\"flex items-center gap-2\">{action}</div>}\n {!action && actionLabel && onActionClick && (\n <Button onClick={onActionClick} disabled={loading} size=\"sm\">\n <Plus className=\"mr-2 h-4 w-4\" />\n {actionLabel}\n </Button>\n )}\n </div>\n </div>\n {showSearch && (\n <div className=\"flex items-center gap-2\">\n <div className=\"relative max-w-sm flex-1\">\n <Search className=\"absolute left-2 top-2.5 h-4 w-4 text-muted-foreground\" />\n <Input\n placeholder={searchPlaceholder}\n value={searchValue}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n onSearchChange?.(e.target.value)\n }\n onKeyPress={handleKeyPress}\n className=\"h-9 pl-8\"\n disabled={loading}\n />\n </div>\n {onSearch && (\n <Button\n onClick={onSearch}\n disabled={loading}\n variant=\"secondary\"\n size=\"sm\"\n >\n 搜索\n </Button>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"names":["TableHeader","title","searchPlaceholder","searchValue","onSearchChange","onSearch","showSearch","action","actionLabel","onActionClick","loading","components","jsx","Input","Button","handleKeyPress","e","jsxs","Plus","Search"],"mappings":";;AAiCO,SAASA,EAAY;AAAA,EAC1B,OAAAC;AAAA,EACA,mBAAAC,IAAoB;AAAA,EACpB,aAAAC,IAAc;AAAA,EACd,gBAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC,IAAa;AAAA,EACb,QAAAC;AAAA,EACA,aAAAC;AAAA,EACA,eAAAC;AAAA,EACA,SAAAC,IAAU;AAAA,EACV,YAAAC;AACF,GAAqB;AACnB,MAAI,CAACA;AACH,WACEC,gBAAAA,EAAAA,IAAC,OAAA,EAAI,WAAU,oCAAmC,UAAA,mCAElD;AAIJ,QAAM,EAAE,OAAAC,GAAO,QAAAC,EAAA,IAAWH,GAEpBI,IAAiB,CAACC,MAA2B;AACjD,IAAIA,EAAE,QAAQ,WAAWX,KACvBA,EAAA;AAAA,EAEJ;AAEA,SACEY,gBAAAA,EAAAA,KAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,IAAAA,gBAAAA,EAAAA,KAAC,OAAA,EAAI,WAAU,qCACb,UAAA;AAAA,MAAAL,gBAAAA,EAAAA,IAAC,OAAA,EAAI,WAAU,qDACZ,UAAAX,GACH;AAAA,MACAgB,gBAAAA,EAAAA,KAAC,OAAA,EAAI,WAAU,2BACZ,UAAA;AAAA,QAAAV,KAAUK,gBAAAA,EAAAA,IAAC,OAAA,EAAI,WAAU,2BAA2B,UAAAL,GAAO;AAAA,QAC3D,CAACA,KAAUC,KAAeC,KACzBQ,gBAAAA,EAAAA,KAACH,GAAA,EAAO,SAASL,GAAe,UAAUC,GAAS,MAAK,MACtD,UAAA;AAAA,UAAAE,gBAAAA,EAAAA,IAACM,GAAA,EAAK,WAAU,eAAA,CAAe;AAAA,UAC9BV;AAAA,QAAA,EAAA,CACH;AAAA,MAAA,EAAA,CAEJ;AAAA,IAAA,GACF;AAAA,IACCF,KACCW,gBAAAA,EAAAA,KAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,MAAAA,gBAAAA,EAAAA,KAAC,OAAA,EAAI,WAAU,4BACb,UAAA;AAAA,QAAAL,gBAAAA,EAAAA,IAACO,GAAA,EAAO,WAAU,wDAAA,CAAwD;AAAA,QAC1EP,gBAAAA,EAAAA;AAAAA,UAACC;AAAA,UAAA;AAAA,YACC,aAAaX;AAAA,YACb,OAAOC;AAAA,YACP,UAAU,CAACa,MACTZ,KAAA,gBAAAA,EAAiBY,EAAE,OAAO;AAAA,YAE5B,YAAYD;AAAA,YACZ,WAAU;AAAA,YACV,UAAUL;AAAA,UAAA;AAAA,QAAA;AAAA,MACZ,GACF;AAAA,MACCL,KACCO,gBAAAA,EAAAA;AAAAA,QAACE;AAAA,QAAA;AAAA,UACC,SAAST;AAAA,UACT,UAAUK;AAAA,UACV,SAAQ;AAAA,UACR,MAAK;AAAA,UACN,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IAED,EAAA,CAEJ;AAAA,EAAA,GAEJ;AAEJ;"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./jsx-runtime-BB_1_6y_.cjs"),b=require("lucide-react"),R=require("react");function O({currentPage:t,pageSize:x,total:l,onPageChange:E,onPageSizeChange:d,pageSizeOptions:v=[10,20,50],showPageSizeSelector:N=!0,showJumpToPage:g=!0,showTotal:p=!0,searchActive:y=!1,components:j}){const[f,h]=R.useState(String(t+1));if(R.useEffect(()=>{h(String(t+1))},[t]),!j)return e.jsxRuntimeExports.jsx("div",{className:"p-4 text-center text-destructive",children:"错误:请通过 components prop 注入 UI 组件"});const{Button:o,Select:S,SelectTrigger:w,SelectContent:I,SelectItem:$,SelectValue:C}=j,M=Math.ceil(l/x),i=Math.max(M,1),T=t*x+1,k=Math.min((t+1)*x,l),q=Array.from(new Set([...v,x])).sort((s,n)=>s-n),A=N&&!!d,u=i>1,c=s=>{const n=Math.max(0,Math.min(s,i-1));n!==t&&E(n)},B=(()=>{if(i<=7)return Array.from({length:i},(r,a)=>a);const s=new Set([0,1,i-2,i-1,t-1,t,t+1]),n=Array.from(s).filter(r=>r>=0&&r<i).sort((r,a)=>r-a),m=[];for(const r of n){const a=m[m.length-1];typeof a=="number"&&r-a>1&&m.push("ellipsis"),m.push(r)}return m})();return l===0?null:e.jsxRuntimeExports.jsxs("div",{className:`flex w-full items-center gap-3 ${p?"justify-between":"justify-end"}`,children:[p&&e.jsxRuntimeExports.jsx("div",{className:"text-sm text-muted-foreground",children:y?`找到 ${l} 条匹配记录`:l>0?`显示 ${T} - ${k} 条,共 ${l} 条记录`:"暂无数据"}),e.jsxRuntimeExports.jsxs("div",{className:"flex flex-wrap items-center justify-end gap-2",children:[A&&e.jsxRuntimeExports.jsxs("div",{className:"mr-2 flex items-center gap-2",children:[e.jsxRuntimeExports.jsx("span",{className:"text-sm text-muted-foreground",children:"每页"}),e.jsxRuntimeExports.jsxs(S,{value:String(x),onValueChange:s=>{const n=Number(s);Number.isNaN(n)||n===x||!d||d(n)},children:[e.jsxRuntimeExports.jsx(w,{className:"h-8 w-[90px]",children:e.jsxRuntimeExports.jsx(C,{})}),e.jsxRuntimeExports.jsx(I,{children:q.map(s=>e.jsxRuntimeExports.jsx($,{value:String(s),children:s},s))})]}),e.jsxRuntimeExports.jsx("span",{className:"text-sm text-muted-foreground",children:"条"})]}),e.jsxRuntimeExports.jsxs("div",{className:"mr-4 text-sm text-muted-foreground",children:["第 ",t+1," 页,共 ",i," 页"]}),e.jsxRuntimeExports.jsx(o,{variant:"outline",size:"icon",onClick:()=>c(t-1),disabled:t===0||!u,"aria-label":"上一页",children:e.jsxRuntimeExports.jsx(b.ChevronLeft,{className:"h-4 w-4"})}),e.jsxRuntimeExports.jsx("div",{className:"flex items-center gap-1",children:B.map((s,n)=>s==="ellipsis"?e.jsxRuntimeExports.jsx("span",{className:"inline-flex h-8 w-8 items-center justify-center text-sm text-muted-foreground",children:"..."},`ellipsis-${n}`):e.jsxRuntimeExports.jsx(o,{variant:s===t?"default":"outline",size:"sm",onClick:()=>c(s),disabled:s===t,"aria-current":s===t?"page":void 0,children:s+1},s))}),e.jsxRuntimeExports.jsx(o,{variant:"outline",size:"icon",onClick:()=>c(t+1),disabled:t>=i-1||!u,"aria-label":"下一页",children:e.jsxRuntimeExports.jsx(b.ChevronRight,{className:"h-4 w-4"})}),g&&e.jsxRuntimeExports.jsxs("form",{className:"ml-1 flex items-center gap-2",onSubmit:s=>{s.preventDefault();const n=Number.parseInt(f,10);Number.isNaN(n)||c(n-1)},children:[e.jsxRuntimeExports.jsx("span",{className:"text-sm text-muted-foreground",children:"跳至"}),e.jsxRuntimeExports.jsx("input",{type:"number",min:1,max:i,value:f,disabled:!u,onChange:s=>h(s.target.value),className:"h-8 w-16 rounded-md border border-input bg-background px-2 text-sm","aria-label":"跳转页码"}),e.jsxRuntimeExports.jsx(o,{type:"submit",variant:"outline",size:"sm",disabled:!u,children:"跳转"})]})]})]})}exports.TablePagination=O;
2
+ //# sourceMappingURL=table-pagination.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-pagination.cjs","sources":["../src/components/TablePagination.tsx"],"sourcesContent":["import { ChevronLeft, ChevronRight } from 'lucide-react';\nimport { useEffect, useState } from 'react';\n\nimport type {\n ButtonComponent,\n SelectComponent,\n SelectTriggerComponent,\n SelectContentComponent,\n SelectItemComponent,\n SelectValueComponent,\n} from '../types/component-types';\n\n/**\n * UI 组件适配器接口\n */\nexport interface PaginationUIComponents {\n Button: ButtonComponent;\n Select: SelectComponent;\n SelectTrigger: SelectTriggerComponent;\n SelectContent: SelectContentComponent;\n SelectItem: SelectItemComponent;\n SelectValue: SelectValueComponent;\n}\n\nexport interface TablePaginationProps {\n currentPage: number;\n pageSize: number;\n total: number;\n onPageChange: (page: number) => void;\n onPageSizeChange?: (pageSize: number) => void;\n pageSizeOptions?: number[];\n showPageSizeSelector?: boolean;\n showJumpToPage?: boolean;\n showTotal?: boolean;\n searchActive?: boolean;\n\n // UI 组件注入\n components?: PaginationUIComponents;\n}\n\n/**\n * 表格分页组件\n * 支持页码切换和每页条数选择\n */\nexport function TablePagination({\n currentPage,\n pageSize,\n total,\n onPageChange,\n onPageSizeChange,\n pageSizeOptions = [10, 20, 50],\n showPageSizeSelector = true,\n showJumpToPage = true,\n showTotal = true,\n searchActive = false,\n components,\n}: TablePaginationProps) {\n const [jumpPageInput, setJumpPageInput] = useState(String(currentPage + 1));\n\n useEffect(() => {\n setJumpPageInput(String(currentPage + 1));\n }, [currentPage]);\n\n if (!components) {\n return (\n <div className=\"p-4 text-center text-destructive\">\n 错误:请通过 components prop 注入 UI 组件\n </div>\n );\n }\n\n const {\n Button,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n } = components;\n\n const totalPages = Math.ceil(total / pageSize);\n const safeTotalPages = Math.max(totalPages, 1);\n const startItem = currentPage * pageSize + 1;\n const endItem = Math.min((currentPage + 1) * pageSize, total);\n const availablePageSizeOptions = Array.from(\n new Set([...pageSizeOptions, pageSize])\n ).sort((a, b) => a - b);\n const canChangePageSize = showPageSizeSelector && Boolean(onPageSizeChange);\n const canSwitchPage = safeTotalPages > 1;\n\n const goToPage = (page: number) => {\n const nextPage = Math.max(0, Math.min(page, safeTotalPages - 1));\n if (nextPage === currentPage) {\n return;\n }\n onPageChange(nextPage);\n };\n\n const pageIndicators = (() => {\n if (safeTotalPages <= 7) {\n return Array.from({ length: safeTotalPages }, (_, index) => index);\n }\n\n const pages = new Set<number>([\n 0,\n 1,\n safeTotalPages - 2,\n safeTotalPages - 1,\n currentPage - 1,\n currentPage,\n currentPage + 1,\n ]);\n\n const visiblePages = Array.from(pages)\n .filter((page) => page >= 0 && page < safeTotalPages)\n .sort((a, b) => a - b);\n const indicators: Array<number | 'ellipsis'> = [];\n\n for (const page of visiblePages) {\n const previous = indicators[indicators.length - 1];\n if (typeof previous === 'number' && page - previous > 1) {\n indicators.push('ellipsis');\n }\n indicators.push(page);\n }\n\n return indicators;\n })();\n\n // 如果没有数据,不显示分页\n if (total === 0) return null;\n\n return (\n <div\n className={`flex w-full items-center gap-3 ${\n showTotal ? 'justify-between' : 'justify-end'\n }`}\n >\n {showTotal && (\n <div className=\"text-sm text-muted-foreground\">\n {searchActive\n ? `找到 ${total} 条匹配记录`\n : total > 0\n ? `显示 ${startItem} - ${endItem} 条,共 ${total} 条记录`\n : '暂无数据'}\n </div>\n )}\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n {canChangePageSize && (\n <div className=\"mr-2 flex items-center gap-2\">\n <span className=\"text-sm text-muted-foreground\">每页</span>\n <Select\n value={String(pageSize)}\n onValueChange={(value: string) => {\n const nextPageSize = Number(value);\n if (\n Number.isNaN(nextPageSize) ||\n nextPageSize === pageSize ||\n !onPageSizeChange\n ) {\n return;\n }\n onPageSizeChange(nextPageSize);\n }}\n >\n <SelectTrigger className=\"h-8 w-[90px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {availablePageSizeOptions.map((option) => (\n <SelectItem key={option} value={String(option)}>\n {option}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <span className=\"text-sm text-muted-foreground\">条</span>\n </div>\n )}\n <div className=\"mr-4 text-sm text-muted-foreground\">\n 第 {currentPage + 1} 页,共 {safeTotalPages} 页\n </div>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => goToPage(currentPage - 1)}\n disabled={currentPage === 0 || !canSwitchPage}\n aria-label=\"上一页\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n </Button>\n <div className=\"flex items-center gap-1\">\n {pageIndicators.map((indicator, index) =>\n indicator === 'ellipsis' ? (\n <span\n key={`ellipsis-${index}`}\n className=\"inline-flex h-8 w-8 items-center justify-center text-sm text-muted-foreground\"\n >\n ...\n </span>\n ) : (\n <Button\n key={indicator}\n variant={indicator === currentPage ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => goToPage(indicator)}\n disabled={indicator === currentPage}\n aria-current={indicator === currentPage ? 'page' : undefined}\n >\n {indicator + 1}\n </Button>\n )\n )}\n </div>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => goToPage(currentPage + 1)}\n disabled={currentPage >= safeTotalPages - 1 || !canSwitchPage}\n aria-label=\"下一页\"\n >\n <ChevronRight className=\"h-4 w-4\" />\n </Button>\n {showJumpToPage && (\n <form\n className=\"ml-1 flex items-center gap-2\"\n onSubmit={(event) => {\n event.preventDefault();\n const nextPage = Number.parseInt(jumpPageInput, 10);\n if (Number.isNaN(nextPage)) {\n return;\n }\n goToPage(nextPage - 1);\n }}\n >\n <span className=\"text-sm text-muted-foreground\">跳至</span>\n <input\n type=\"number\"\n min={1}\n max={safeTotalPages}\n value={jumpPageInput}\n disabled={!canSwitchPage}\n onChange={(event) => setJumpPageInput(event.target.value)}\n className=\"h-8 w-16 rounded-md border border-input bg-background px-2 text-sm\"\n aria-label=\"跳转页码\"\n />\n <Button\n type=\"submit\"\n variant=\"outline\"\n size=\"sm\"\n disabled={!canSwitchPage}\n >\n 跳转\n </Button>\n </form>\n )}\n </div>\n </div>\n );\n}\n"],"names":["TablePagination","currentPage","pageSize","total","onPageChange","onPageSizeChange","pageSizeOptions","showPageSizeSelector","showJumpToPage","showTotal","searchActive","components","jumpPageInput","setJumpPageInput","useState","useEffect","jsx","Button","Select","SelectTrigger","SelectContent","SelectItem","SelectValue","totalPages","safeTotalPages","startItem","endItem","availablePageSizeOptions","a","b","canChangePageSize","canSwitchPage","goToPage","page","nextPage","pageIndicators","_","index","pages","visiblePages","indicators","previous","jsxs","value","nextPageSize","option","ChevronLeft","indicator","ChevronRight","event"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { TablePagination } from './components/TablePagination';
2
+ export type { PaginationUIComponents, TablePaginationProps, } from './components/TablePagination';
3
+ //# sourceMappingURL=table-pagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-pagination.d.ts","sourceRoot":"","sources":["../src/table-pagination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,YAAY,EACV,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,8BAA8B,CAAC"}