@turinhub/atomix-common-ui 0.4.0 → 0.6.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 (104) hide show
  1. package/README.md +20 -2
  2. package/dist/AuthPanel-CTKx618F.cjs +2 -0
  3. package/dist/AuthPanel-CTKx618F.cjs.map +1 -0
  4. package/dist/AuthPanel-Cn_WwmjX.js +703 -0
  5. package/dist/AuthPanel-Cn_WwmjX.js.map +1 -0
  6. package/dist/PDFSidebar-4DtXqqzN.cjs +2 -0
  7. package/dist/PDFSidebar-4DtXqqzN.cjs.map +1 -0
  8. package/dist/PDFSidebar-ClnrF4Br.js +239 -0
  9. package/dist/PDFSidebar-ClnrF4Br.js.map +1 -0
  10. package/dist/auth.cjs +2 -0
  11. package/dist/auth.cjs.map +1 -0
  12. package/dist/auth.d.ts +11 -0
  13. package/dist/auth.d.ts.map +1 -0
  14. package/dist/auth.js +9 -0
  15. package/dist/auth.js.map +1 -0
  16. package/dist/components/AuthLoginPanel.d.ts +55 -0
  17. package/dist/components/AuthLoginPanel.d.ts.map +1 -0
  18. package/dist/components/AuthPageShell.d.ts +11 -0
  19. package/dist/components/AuthPageShell.d.ts.map +1 -0
  20. package/dist/components/AuthPanel.d.ts +20 -0
  21. package/dist/components/AuthPanel.d.ts.map +1 -0
  22. package/dist/components/AuthRegisterPanel.d.ts +34 -0
  23. package/dist/components/AuthRegisterPanel.d.ts.map +1 -0
  24. package/dist/components/AuthVisualCarousel.d.ts +22 -0
  25. package/dist/components/AuthVisualCarousel.d.ts.map +1 -0
  26. package/dist/components/DataTable.d.ts +2 -2
  27. package/dist/components/DataTable.d.ts.map +1 -1
  28. package/dist/components/ImageReader.d.ts +44 -0
  29. package/dist/components/ImageReader.d.ts.map +1 -0
  30. package/dist/components/MarkdownReader.d.ts.map +1 -1
  31. package/dist/components/PDFReader.d.ts.map +1 -1
  32. package/dist/components/PDFSidebar.d.ts.map +1 -1
  33. package/dist/components/SimplePDFReader.d.ts.map +1 -1
  34. package/dist/components/TableHeader.d.ts.map +1 -1
  35. package/dist/components/TablePagination.d.ts +2 -1
  36. package/dist/components/TablePagination.d.ts.map +1 -1
  37. package/dist/components/VideoReader.d.ts +39 -0
  38. package/dist/components/VideoReader.d.ts.map +1 -0
  39. package/dist/components/media-utils.d.ts +9 -0
  40. package/dist/components/media-utils.d.ts.map +1 -0
  41. package/dist/components/ui/switch.d.ts +5 -0
  42. package/dist/components/ui/switch.d.ts.map +1 -0
  43. package/dist/data-table.cjs +1 -1
  44. package/dist/data-table.cjs.map +1 -1
  45. package/dist/data-table.js +83 -73
  46. package/dist/data-table.js.map +1 -1
  47. package/dist/file-upload.cjs +1 -1
  48. package/dist/file-upload.cjs.map +1 -1
  49. package/dist/file-upload.js +36 -36
  50. package/dist/file-upload.js.map +1 -1
  51. package/dist/image-reader.cjs +2 -0
  52. package/dist/image-reader.cjs.map +1 -0
  53. package/dist/image-reader.d.ts +3 -0
  54. package/dist/image-reader.d.ts.map +1 -0
  55. package/dist/image-reader.js +215 -0
  56. package/dist/image-reader.js.map +1 -0
  57. package/dist/index.cjs +1 -1
  58. package/dist/index.d.ts +10 -0
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +8 -2
  61. package/dist/index.js.map +1 -1
  62. package/dist/markdown-reader.cjs +1 -1
  63. package/dist/markdown-reader.cjs.map +1 -1
  64. package/dist/markdown-reader.js +28 -24
  65. package/dist/markdown-reader.js.map +1 -1
  66. package/dist/media-utils-5UPuocc1.js +23 -0
  67. package/dist/media-utils-5UPuocc1.js.map +1 -0
  68. package/dist/media-utils-X1dDYP9W.cjs +2 -0
  69. package/dist/media-utils-X1dDYP9W.cjs.map +1 -0
  70. package/dist/pdf-reader.cjs +1 -1
  71. package/dist/pdf-reader.cjs.map +1 -1
  72. package/dist/pdf-reader.js +170 -121
  73. package/dist/pdf-reader.js.map +1 -1
  74. package/dist/pdf-sidebar.cjs +1 -1
  75. package/dist/pdf-sidebar.js +1 -1
  76. package/dist/simple-pdf-reader.cjs +1 -1
  77. package/dist/simple-pdf-reader.cjs.map +1 -1
  78. package/dist/simple-pdf-reader.js +138 -105
  79. package/dist/simple-pdf-reader.js.map +1 -1
  80. package/dist/table-header.cjs +1 -1
  81. package/dist/table-header.cjs.map +1 -1
  82. package/dist/table-header.js +42 -34
  83. package/dist/table-header.js.map +1 -1
  84. package/dist/table-pagination.cjs +1 -1
  85. package/dist/table-pagination.cjs.map +1 -1
  86. package/dist/table-pagination.js +49 -43
  87. package/dist/table-pagination.js.map +1 -1
  88. package/dist/types/component-types.d.ts +2 -0
  89. package/dist/types/component-types.d.ts.map +1 -1
  90. package/dist/video-reader.cjs +2 -0
  91. package/dist/video-reader.cjs.map +1 -0
  92. package/dist/video-reader.d.ts +3 -0
  93. package/dist/video-reader.d.ts.map +1 -0
  94. package/dist/video-reader.js +158 -0
  95. package/dist/video-reader.js.map +1 -0
  96. package/package.json +32 -1
  97. package/dist/PDFSidebar-BBtucLK6.js +0 -232
  98. package/dist/PDFSidebar-BBtucLK6.js.map +0 -1
  99. package/dist/PDFSidebar-Di0D-yPS.cjs +0 -2
  100. package/dist/PDFSidebar-Di0D-yPS.cjs.map +0 -1
  101. package/dist/index-BiA_tnaq.cjs +0 -13
  102. package/dist/index-BiA_tnaq.cjs.map +0 -1
  103. package/dist/index-BypbGNpR.js +0 -18821
  104. package/dist/index-BypbGNpR.js.map +0 -1
@@ -1,145 +1,147 @@
1
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,
2
+ import { ZoomOut as pe, ZoomIn as ve, Minimize2 as je, Maximize2 as he, ChevronLeft as ye, ChevronRight as be } from "lucide-react";
3
+ import { useState as o, useRef as B, useMemo as we, useCallback as x, useEffect as k } from "react";
4
+ function De({
5
+ url: N,
6
+ initialPage: V = 1,
7
+ initialScale: X = 1,
8
8
  scale: p,
9
- onScaleChange: c,
9
+ onScaleChange: l,
10
10
  minScale: D = 0.5,
11
- maxScale: g = 3,
11
+ maxScale: M = 3,
12
12
  currentPage: v,
13
13
  onPageChange: y,
14
14
  showToolbar: K = !0,
15
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 = "加载中...",
16
+ enableHotkeys: Z = !0,
17
+ className: J,
18
+ containerClassName: Q,
19
+ pageClassName: Y,
20
+ components: C,
21
+ onLoadSuccess: b,
22
+ onLoadError: u,
23
+ loadingText: _ = "加载中...",
24
24
  errorText: H = "加载失败"
25
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(
26
+ const [G, L] = o(null), [S, U] = o(V), [ee, W] = o(X), [te, w] = o(!0), [P, F] = o(null), [a, ne] = o(0), [g, se] = o(!1), I = B(null), j = B(null), [m, re] = o(null), i = v ?? S, c = p ?? ee, ie = we(() => {
27
+ const e = [i];
28
+ return i > 1 && e.push(i - 1), i < a && e.push(i + 1), e;
29
+ }, [i, a]), f = x(
30
30
  (e) => {
31
- const n = Math.max(l, 1), s = Math.min(Math.max(e, 1), n);
31
+ const n = Math.max(a, 1), s = Math.min(Math.max(e, 1), n);
32
32
  v === void 0 && U(s), y == null || y(s);
33
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 () => {
34
+ [a, v, y]
35
+ ), E = x(() => {
36
+ i > 1 && f(i - 1);
37
+ }, [i, f]), R = x(() => {
38
+ i < a && f(i + 1);
39
+ }, [i, a, f]), T = x(() => {
40
+ const e = Math.min(c + 0.25, M);
41
+ p === void 0 && W(e), l == null || l(e);
42
+ }, [c, M, p, l]), z = x(() => {
43
+ const e = Math.max(c - 0.25, D);
44
+ p === void 0 && W(e), l == null || l(e);
45
+ }, [c, D, p, l]), A = x(async () => {
46
46
  var e, n, s;
47
47
  if (!(typeof document > "u")) {
48
48
  if (!document.fullscreenElement) {
49
- await ((n = (e = M.current) == null ? void 0 : e.requestFullscreen) == null ? void 0 : n.call(e));
49
+ await ((n = (e = I.current) == null ? void 0 : e.requestFullscreen) == null ? void 0 : n.call(e));
50
50
  return;
51
51
  }
52
52
  await ((s = document.exitFullscreen) == null ? void 0 : s.call(document));
53
53
  }
54
54
  }, []);
55
- if (N(() => {
55
+ if (k(() => {
56
56
  let e = !0;
57
57
  return (async () => {
58
58
  try {
59
- const s = await import("./index-BypbGNpR.js");
59
+ const s = await import("react-pdf");
60
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`);
61
+ const r = s.pdfjs, d = r == null ? void 0 : r.version;
62
+ r != null && r.GlobalWorkerOptions && d && (r.GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${d}/build/pdf.worker.min.mjs`);
63
63
  }
64
64
  e && re(s);
65
65
  } catch (s) {
66
66
  if (e) {
67
- const i = s instanceof Error ? s : new Error("无法加载 react-pdf 库");
68
- F(i), k(!1), o == null || o(i);
67
+ const r = s instanceof Error ? s : new Error("无法加载 react-pdf 库");
68
+ F(r), w(!1), u == null || u(r);
69
69
  }
70
70
  }
71
71
  })(), () => {
72
72
  e = !1;
73
73
  };
74
- }, []), N(() => {
75
- if (!m || !b) return;
74
+ }, []), k(() => {
75
+ if (!m || !N) return;
76
76
  let e = !0;
77
77
  return (async () => {
78
- k(!0), F(null);
78
+ w(!0), F(null);
79
79
  try {
80
80
  const { Document: s } = m;
81
81
  if (!s)
82
82
  throw new Error("react-pdf Document 组件不可用");
83
- const i = m.pdfjs.getDocument(b);
84
- j.current = i;
85
- const d = await i.promise;
83
+ const r = m.pdfjs.getDocument(N);
84
+ j.current = r;
85
+ const d = await r.promise;
86
86
  e && (L(d), ne(d.numPages), v === void 0 && U(
87
87
  (xe) => Math.max(1, Math.min(xe, d.numPages))
88
- ), k(!1), w == null || w(d));
88
+ ), w(!1), b == null || b(d));
89
89
  } catch (s) {
90
90
  if (e) {
91
- const i = s instanceof Error ? s : new Error("PDF 加载失败");
92
- F(i), k(!1), o == null || o(i);
91
+ const r = s instanceof Error ? s : new Error("PDF 加载失败");
92
+ F(r), w(!1), u == null || u(r);
93
93
  }
94
94
  }
95
95
  })(), () => {
96
96
  e = !1, j.current && typeof j.current.destroy == "function" && (j.current.destroy(), j.current = null);
97
97
  };
98
- }, [m, b, v, w, o]), N(() => {
98
+ }, [m, N, v, b, u]), k(() => {
99
99
  if (typeof document > "u") return;
100
100
  const e = () => {
101
- se(document.fullscreenElement === M.current);
101
+ se(document.fullscreenElement === I.current);
102
102
  };
103
103
  return document.addEventListener("fullscreenchange", e), () => {
104
104
  document.removeEventListener("fullscreenchange", e);
105
105
  };
106
- }, []), N(() => {
107
- if (!A) return;
106
+ }, []), k(() => {
107
+ if (!Z) return;
108
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()));
109
+ const s = document.activeElement, r = s == null ? void 0 : s.getAttribute("role");
110
+ s && (s.tagName === "INPUT" || s.tagName === "TEXTAREA" || s.tagName === "SELECT" || s.isContentEditable || r === "textbox" || r === "spinbutton") || ((n.ctrlKey || n.metaKey) && (n.key === "=" || n.key === "+") ? (n.preventDefault(), T()) : (n.ctrlKey || n.metaKey) && n.key === "-" ? (n.preventDefault(), z()) : n.key === "ArrowLeft" ? (n.preventDefault(), E()) : n.key === "ArrowRight" ? (n.preventDefault(), R()) : (n.key === "f" || n.key === "F") && (n.preventDefault(), A()));
111
111
  };
112
112
  return document.addEventListener("keydown", e), () => {
113
113
  document.removeEventListener("keydown", e);
114
114
  };
115
115
  }, [
116
- A,
117
- E,
116
+ Z,
118
117
  T,
119
- I,
118
+ z,
119
+ E,
120
120
  R,
121
- z
122
- ]), !Z)
121
+ A
122
+ ]), !C)
123
123
  return /* @__PURE__ */ t.jsx("div", { className: "p-4 text-center text-destructive", children: "错误:请通过 components prop 注入 UI 组件" });
124
124
  const {
125
- Card: le,
126
- CardContent: ce,
125
+ Card: ae,
126
+ CardContent: le,
127
127
  Button: h,
128
- Input: q,
129
- Skeleton: B
130
- } = Z, ae = () => K ? /* @__PURE__ */ t.jsxs("div", { className: "flex items-center gap-2", children: [
128
+ Input: $,
129
+ Skeleton: q
130
+ } = C, ce = () => K ? /* @__PURE__ */ t.jsxs("div", { className: "flex items-center gap-2", children: [
131
131
  /* @__PURE__ */ t.jsx(
132
132
  h,
133
133
  {
134
134
  variant: "outline",
135
135
  size: "sm",
136
- onClick: T,
137
- disabled: a <= D,
136
+ onClick: z,
137
+ disabled: c <= D,
138
+ "aria-label": "缩小",
139
+ title: "缩小",
138
140
  children: /* @__PURE__ */ t.jsx(pe, {})
139
141
  }
140
142
  ),
141
143
  /* @__PURE__ */ t.jsxs("span", { className: "text-sm", children: [
142
- Math.round(a * 100),
144
+ Math.round(c * 100),
143
145
  "%"
144
146
  ] }),
145
147
  /* @__PURE__ */ t.jsx(
@@ -147,8 +149,10 @@ function ge({
147
149
  {
148
150
  variant: "outline",
149
151
  size: "sm",
150
- onClick: E,
151
- disabled: a >= g,
152
+ onClick: T,
153
+ disabled: c >= M,
154
+ "aria-label": "放大",
155
+ title: "放大",
152
156
  children: /* @__PURE__ */ t.jsx(ve, {})
153
157
  }
154
158
  ),
@@ -158,46 +162,58 @@ function ge({
158
162
  variant: "outline",
159
163
  size: "icon",
160
164
  onClick: () => {
161
- z();
165
+ A();
162
166
  },
163
- children: $ ? /* @__PURE__ */ t.jsx(je, {}) : /* @__PURE__ */ t.jsx(he, {})
167
+ "aria-label": g ? "退出全屏" : "进入全屏",
168
+ title: g ? "退出全屏" : "进入全屏",
169
+ children: g ? /* @__PURE__ */ t.jsx(je, {}) : /* @__PURE__ */ t.jsx(he, {})
164
170
  }
165
171
  )
166
- ] }) : null, ue = () => O ? /* @__PURE__ */ t.jsxs("div", { className: "flex items-center gap-2", children: [
172
+ ] }) : null, oe = () => O ? /* @__PURE__ */ t.jsxs("div", { className: "flex items-center gap-2", children: [
167
173
  /* @__PURE__ */ t.jsx(
168
174
  h,
169
175
  {
170
176
  variant: "outline",
171
177
  size: "icon",
172
- onClick: I,
173
- disabled: r <= 1,
178
+ onClick: E,
179
+ disabled: i <= 1,
180
+ "aria-label": "上一页",
181
+ title: "上一页",
174
182
  children: /* @__PURE__ */ t.jsx(ye, {})
175
183
  }
176
184
  ),
177
- q ? /* @__PURE__ */ t.jsx(
178
- q,
185
+ $ ? /* @__PURE__ */ t.jsx(
186
+ $,
179
187
  {
180
188
  type: "number",
189
+ name: "simple-pdf-page",
181
190
  min: 1,
182
- max: Math.max(l, 1),
183
- value: r,
191
+ max: Math.max(a, 1),
192
+ value: i,
184
193
  onChange: (e) => f(parseInt(e.target.value, 10) || 1),
185
- className: "w-16 text-center"
194
+ className: "w-16 text-center",
195
+ "aria-label": "当前页码",
196
+ inputMode: "numeric",
197
+ autoComplete: "off"
186
198
  }
187
199
  ) : /* @__PURE__ */ t.jsx(
188
200
  "input",
189
201
  {
190
202
  type: "number",
203
+ name: "simple-pdf-page",
191
204
  min: 1,
192
- max: Math.max(l, 1),
193
- value: r,
205
+ max: Math.max(a, 1),
206
+ value: i,
194
207
  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"
208
+ className: "w-16 rounded-md border border-input bg-background px-2 text-center text-sm",
209
+ "aria-label": "当前页码",
210
+ inputMode: "numeric",
211
+ autoComplete: "off"
196
212
  }
197
213
  ),
198
214
  /* @__PURE__ */ t.jsxs("span", { className: "text-sm text-muted-foreground", children: [
199
215
  "/ ",
200
- l
216
+ a
201
217
  ] }),
202
218
  /* @__PURE__ */ t.jsx(
203
219
  h,
@@ -205,47 +221,64 @@ function ge({
205
221
  variant: "outline",
206
222
  size: "icon",
207
223
  onClick: R,
208
- disabled: r >= l,
209
- children: /* @__PURE__ */ t.jsx(we, {})
224
+ disabled: i >= a,
225
+ "aria-label": "下一页",
226
+ title: "下一页",
227
+ children: /* @__PURE__ */ t.jsx(be, {})
210
228
  }
211
229
  )
212
- ] }) : null, oe = () => !K && !O ? null : /* @__PURE__ */ t.jsxs(
230
+ ] }) : null, ue = () => !K && !O ? null : /* @__PURE__ */ t.jsxs(
213
231
  "div",
214
232
  {
215
233
  "data-testid": "pdf-operations-bar",
216
234
  className: "flex items-center justify-between gap-4 border-b px-4 py-2",
217
235
  children: [
218
- ae(),
219
- ue()
236
+ ce(),
237
+ oe()
238
+ ]
239
+ }
240
+ ), de = () => /* @__PURE__ */ t.jsxs(
241
+ "div",
242
+ {
243
+ className: "flex flex-col items-center justify-center space-y-4 p-8",
244
+ role: "status",
245
+ "aria-live": "polite",
246
+ children: [
247
+ /* @__PURE__ */ t.jsx(q, { className: "h-8 w-32" }),
248
+ /* @__PURE__ */ t.jsx(q, { className: "h-64 w-full max-w-2xl" }),
249
+ /* @__PURE__ */ t.jsx("p", { className: "text-sm text-muted-foreground", children: _ })
220
250
  ]
221
251
  }
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 = () => {
252
+ ), me = () => /* @__PURE__ */ t.jsx(
253
+ "div",
254
+ {
255
+ className: "flex flex-col items-center justify-center space-y-4 p-8",
256
+ role: "alert",
257
+ children: /* @__PURE__ */ t.jsxs("div", { className: "text-center text-destructive", children: [
258
+ /* @__PURE__ */ t.jsx("p", { className: "font-medium", children: H }),
259
+ P && /* @__PURE__ */ t.jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: P.message })
260
+ ] })
261
+ }
262
+ ), fe = () => {
230
263
  if (!m || !G) return null;
231
264
  const { Page: e } = m;
232
265
  return /* @__PURE__ */ t.jsx(
233
266
  "div",
234
267
  {
235
- className: `flex flex-col items-center justify-center ${$ ? "h-[calc(100vh-56px)] overflow-auto" : "overflow-visible"}`,
268
+ className: `flex flex-col items-center justify-center ${g ? "h-[calc(100vh-56px)] overflow-auto" : "overflow-visible"}`,
236
269
  children: ie.map((n) => /* @__PURE__ */ t.jsx(
237
270
  "div",
238
271
  {
239
- className: _,
272
+ className: Y,
240
273
  style: {
241
- display: n === r ? "block" : "none"
274
+ display: n === i ? "block" : "none"
242
275
  },
243
276
  children: /* @__PURE__ */ t.jsx(
244
277
  e,
245
278
  {
246
279
  pdf: G,
247
280
  pageNumber: n,
248
- scale: a,
281
+ scale: c,
249
282
  renderTextLayer: !1,
250
283
  renderAnnotationLayer: !1,
251
284
  className: "shadow-md"
@@ -257,12 +290,12 @@ function ge({
257
290
  }
258
291
  );
259
292
  };
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() })
293
+ return /* @__PURE__ */ t.jsx("div", { ref: I, children: /* @__PURE__ */ t.jsxs(ae, { className: J, children: [
294
+ ue(),
295
+ /* @__PURE__ */ t.jsx(le, { className: Q, children: te ? de() : P ? me() : fe() })
263
296
  ] }) });
264
297
  }
265
298
  export {
266
- ge as SimplePDFReader
299
+ De as SimplePDFReader
267
300
  };
268
301
  //# sourceMappingURL=simple-pdf-reader.js.map
@@ -1 +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":";;;AAkHO,SAASA,GAAgB;AAAA,EAC9B,KAAAC;AAAA,EACA,aAAAC,IAAc;AAAA,EACd,cAAAC,IAAe;AAAA,EACf,OAAOC;AAAA,EACP,eAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,UAAAC,IAAW;AAAA,EACX,aAAaC;AAAA,EACb,cAAAC;AAAA,EACA,aAAAC,IAAc;AAAA,EACd,gBAAAC,IAAiB;AAAA,EACjB,eAAAC,IAAgB;AAAA,EAChB,WAAAC;AAAA,EACA,oBAAAC;AAAA,EACA,eAAAC;AAAA,EACA,YAAAC;AAAA,EACA,eAAAC;AAAA,EACA,aAAAC;AAAA,EACA,aAAAC,IAAc;AAAA,EACd,WAAAC,IAAY;AACd,GAAyB;AAIvB,QAAM,CAACC,GAAaC,CAAc,IAAIC,EAAkC,IAAI,GACtE,CAACC,GAAcC,CAAe,IAAIF,EAASrB,CAAW,GACtD,CAACwB,IAAeC,CAAgB,IAAIJ,EAASpB,CAAY,GACzD,CAACyB,IAAWC,CAAY,IAAIN,EAAS,EAAI,GACzC,CAACO,GAAOC,CAAQ,IAAIR,EAAuB,IAAI,GAC/C,CAACS,GAAYC,EAAa,IAAIV,EAAS,CAAC,GACxC,CAACW,GAAcC,EAAe,IAAIZ,EAAS,EAAK,GAChDa,IAAYC,EAAuB,IAAI,GACvCC,IAAiBD,EAAY,IAAI,GAGjC,CAACE,GAAUC,EAAW,IAAIjB,EAAc,IAAI,GAG5CkB,IAAcjC,KAAkBgB,GAChCkB,IAAQtC,KAAmBsB,IAG3BiB,KAAiBC,GAAQ,MAAM;AACnC,UAAMC,IAAQ,CAACJ,CAAW;AAC1B,WAAIA,IAAc,KAAGI,EAAM,KAAKJ,IAAc,CAAC,GAC3CA,IAAcT,KAAYa,EAAM,KAAKJ,IAAc,CAAC,GACjDI;AAAA,EACT,GAAG,CAACJ,GAAaT,CAAU,CAAC,GAGtBc,IAAWC;AAAA,IACf,CAACC,MAAiB;AAChB,YAAMC,IAAU,KAAK,IAAIjB,GAAY,CAAC,GAChCkB,IAAa,KAAK,IAAI,KAAK,IAAIF,GAAM,CAAC,GAAGC,CAAO;AACtD,MAAIzC,MAAmB,UACrBiB,EAAgByB,CAAU,GAE5BzC,KAAA,QAAAA,EAAeyC;AAAA,IACjB;AAAA,IACA,CAAClB,GAAYxB,GAAgBC,CAAY;AAAA,EAAA,GAGrC0C,IAAqBJ,EAAY,MAAM;AAC3C,IAAIN,IAAc,KAChBK,EAASL,IAAc,CAAC;AAAA,EAE5B,GAAG,CAACA,GAAaK,CAAQ,CAAC,GAEpBM,IAAiBL,EAAY,MAAM;AACvC,IAAIN,IAAcT,KAChBc,EAASL,IAAc,CAAC;AAAA,EAE5B,GAAG,CAACA,GAAaT,GAAYc,CAAQ,CAAC,GAGhCO,IAAeN,EAAY,MAAM;AACrC,UAAMO,IAAW,KAAK,IAAIZ,IAAQ,MAAMnC,CAAQ;AAChD,IAAIH,MAAoB,UACtBuB,EAAiB2B,CAAQ,GAE3BjD,KAAA,QAAAA,EAAgBiD;AAAA,EAClB,GAAG,CAACZ,GAAOnC,GAAUH,GAAiBC,CAAa,CAAC,GAE9CkD,IAAgBR,EAAY,MAAM;AACtC,UAAMO,IAAW,KAAK,IAAIZ,IAAQ,MAAMpC,CAAQ;AAChD,IAAIF,MAAoB,UACtBuB,EAAiB2B,CAAQ,GAE3BjD,KAAA,QAAAA,EAAgBiD;AAAA,EAClB,GAAG,CAACZ,GAAOpC,GAAUF,GAAiBC,CAAa,CAAC,GAE9CmD,IAAyBT,EAAY,YAAY;;AACrD,QAAI,SAAO,WAAa,MAExB;AAAA,UAAI,CAAC,SAAS,mBAAmB;AAC/B,gBAAMU,KAAAC,IAAAtB,EAAU,YAAV,gBAAAsB,EAAmB,sBAAnB,gBAAAD,EAAA,KAAAC;AACN;AAAA,MACF;AAEA,cAAMC,IAAA,SAAS,mBAAT,gBAAAA,EAAA;AAAA;AAAA,EACR,GAAG,CAAA,CAAE;AA0KL,MAvKAC,EAAU,MAAM;AACd,QAAIC,IAAY;AA4BhB,YA1BqB,YAAY;AAC/B,UAAI;AAEF,cAAMC,IAAY,MAAM,OAAO,qBAAW;AAE1C,YAAI,OAAO,SAAW,KAAa;AACjC,gBAAMC,IAASD,EAAkB,OAC3BE,IAAgBD,KAAA,gBAAAA,EAAO;AAC7B,UAAIA,KAAA,QAAAA,EAAO,uBAAuBC,MAChCD,EAAM,oBAAoB,YAAY,2CAA2CC,CAAa;AAAA,QAElG;AACA,QAAIH,KACFrB,GAAYsB,CAAS;AAAA,MAEzB,SAASG,GAAK;AACZ,YAAIJ,GAAW;AACb,gBAAMK,IACJD,aAAe,QAAQA,IAAM,IAAI,MAAM,kBAAkB;AAC3D,UAAAlC,EAASmC,CAAS,GAClBrC,EAAa,EAAK,GAClBX,KAAA,QAAAA,EAAcgD;AAAA,QAChB;AAAA,MACF;AAAA,IACF,GAEA,GAEO,MAAM;AACX,MAAAL,IAAY;AAAA,IACd;AAAA,EAEF,GAAG,CAAA,CAAE,GAGLD,EAAU,MAAM;AACd,QAAI,CAACrB,KAAY,CAACtC,EAAK;AAEvB,QAAI4D,IAAY;AAwChB,YAtCgB,YAAY;AAC1B,MAAAhC,EAAa,EAAI,GACjBE,EAAS,IAAI;AAEb,UAAI;AACF,cAAM,EAAE,UAAAoC,MAAa5B;AACrB,YAAI,CAAC4B;AACH,gBAAM,IAAI,MAAM,0BAA0B;AAK5C,cAAMC,IAAe7B,EAAS,MAAc,YAAYtC,CAAG;AAC3D,QAAAqC,EAAe,UAAU8B;AACzB,cAAMC,IAAM,MAAMD,EAAY;AAE9B,QAAIP,MACFvC,EAAe+C,CAAuB,GACtCpC,GAAcoC,EAAI,QAAQ,GACtB7D,MAAmB,UACrBiB;AAAA,UAAgB,CAAC6C,OACf,KAAK,IAAI,GAAG,KAAK,IAAIA,IAAMD,EAAI,QAAQ,CAAC;AAAA,QAAA,GAG5CxC,EAAa,EAAK,GAClBZ,KAAA,QAAAA,EAAgBoD;AAAA,MAEpB,SAASJ,GAAK;AACZ,YAAIJ,GAAW;AACb,gBAAMK,IACJD,aAAe,QAAQA,IAAM,IAAI,MAAM,UAAU;AACnD,UAAAlC,EAASmC,CAAS,GAClBrC,EAAa,EAAK,GAClBX,KAAA,QAAAA,EAAcgD;AAAA,QAChB;AAAA,MACF;AAAA,IACF,GAEA,GAEO,MAAM;AACX,MAAAL,IAAY,IAEVvB,EAAe,WACf,OAAOA,EAAe,QAAQ,WAAY,eAE1CA,EAAe,QAAQ,QAAA,GACvBA,EAAe,UAAU;AAAA,IAE7B;AAAA,EACF,GAAG,CAACC,GAAUtC,GAAKO,GAAgBS,GAAeC,CAAW,CAAC,GAE9D0C,EAAU,MAAM;AACd,QAAI,OAAO,WAAa,IAAa;AAErC,UAAMW,IAAyB,MAAM;AACnC,MAAApC,GAAgB,SAAS,sBAAsBC,EAAU,OAAO;AAAA,IAClE;AAEA,oBAAS,iBAAiB,oBAAoBmC,CAAsB,GAE7D,MAAM;AACX,eAAS,oBAAoB,oBAAoBA,CAAsB;AAAA,IACzE;AAAA,EACF,GAAG,CAAA,CAAE,GAGLX,EAAU,MAAM;AACd,QAAI,CAAChD,EAAe;AAEpB,UAAM4D,IAAgB,CAACC,MAAqB;AAE1C,YAAMC,IAAgB,SAAS;AAC/B,MACEA,MACCA,EAAc,YAAY,WACzBA,EAAc,aAAa,MAAM,MAAM,cAMtCD,EAAE,WAAWA,EAAE,aAAaA,EAAE,QAAQ,OAAOA,EAAE,QAAQ,QAC1DA,EAAE,eAAA,GACFpB,EAAA,MAGQoB,EAAE,WAAWA,EAAE,YAAYA,EAAE,QAAQ,OAC7CA,EAAE,eAAA,GACFlB,EAAA,KAGOkB,EAAE,QAAQ,eACjBA,EAAE,eAAA,GACFtB,EAAA,KAGOsB,EAAE,QAAQ,gBACjBA,EAAE,eAAA,GACFrB,EAAA,MAGOqB,EAAE,QAAQ,OAAOA,EAAE,QAAQ,SAClCA,EAAE,eAAA,GACGjB,EAAA;AAAA,IAET;AAEA,oBAAS,iBAAiB,WAAWgB,CAAa,GAC3C,MAAM;AACX,eAAS,oBAAoB,WAAWA,CAAa;AAAA,IACvD;AAAA,EACF,GAAG;AAAA,IACD5D;AAAA,IACAyC;AAAA,IACAE;AAAA,IACAJ;AAAA,IACAC;AAAA,IACAI;AAAA,EAAA,CACD,GAKG,CAACxC;AACH,WACE2D,gBAAAA,EAAAA,IAAC,OAAA,EAAI,WAAU,oCAAmC,UAAA,mCAElD;AAIJ,QAAM;AAAA,IACJ,MAAAC;AAAA,IACA,aAAAC;AAAA,IACA,QAAAC;AAAA,IACA,OAAOC;AAAA,IACP,UAAAC;AAAA,EAAA,IACEhE,GAGEiE,KAAgB,MACfvE,IAGHwE,gBAAAA,EAAAA,KAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,IAAAP,gBAAAA,EAAAA;AAAAA,MAACG;AAAA,MAAA;AAAA,QACC,SAAQ;AAAA,QACR,MAAK;AAAA,QACL,SAASvB;AAAA,QACT,UAAUb,KAASpC;AAAA,QAEnB,gCAAC6E,IAAA,CAAA,CAAY;AAAA,MAAA;AAAA,IAAA;AAAA,IAEfD,gBAAAA,EAAAA,KAAC,QAAA,EAAK,WAAU,WAAW,UAAA;AAAA,MAAA,KAAK,MAAMxC,IAAQ,GAAG;AAAA,MAAE;AAAA,IAAA,GAAC;AAAA,IACpDiC,gBAAAA,EAAAA;AAAAA,MAACG;AAAA,MAAA;AAAA,QACC,SAAQ;AAAA,QACR,MAAK;AAAA,QACL,SAASzB;AAAA,QACT,UAAUX,KAASnC;AAAA,QAEnB,gCAAC6E,IAAA,CAAA,CAAW;AAAA,MAAA;AAAA,IAAA;AAAA,IAEdT,gBAAAA,EAAAA;AAAAA,MAACG;AAAA,MAAA;AAAA,QACC,SAAQ;AAAA,QACR,MAAK;AAAA,QACL,SAAS,MAAM;AACb,UAAKtB,EAAA;AAAA,QACP;AAAA,QAEC,UAAAtB,IAAeyC,gBAAAA,MAACU,IAAA,CAAA,CAAc,0BAAMC,IAAA,CAAA,CAAc;AAAA,MAAA;AAAA,IAAA;AAAA,EACrD,GACF,IA9BuB,MAmCrBC,KAAmB,MAClB5E,IAGHuE,gBAAAA,EAAAA,KAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,IAAAP,gBAAAA,EAAAA;AAAAA,MAACG;AAAA,MAAA;AAAA,QACC,SAAQ;AAAA,QACR,MAAK;AAAA,QACL,SAAS3B;AAAA,QACT,UAAUV,KAAe;AAAA,QAEzB,gCAAC+C,IAAA,CAAA,CAAgB;AAAA,MAAA;AAAA,IAAA;AAAA,IAElBT,IACCJ,gBAAAA,EAAAA;AAAAA,MAACI;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK,KAAK,IAAI/C,GAAY,CAAC;AAAA,QAC3B,OAAOS;AAAA,QACP,UAAU,CAAC,MAAMK,EAAS,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,CAAC;AAAA,QAC3D,WAAU;AAAA,MAAA;AAAA,IAAA,IAGZ6B,gBAAAA,EAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK,KAAK,IAAI3C,GAAY,CAAC;AAAA,QAC3B,OAAOS;AAAA,QACP,UAAU,CAAC,MAAMK,EAAS,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,CAAC;AAAA,QAC3D,WAAU;AAAA,MAAA;AAAA,IAAA;AAAA,IAGdoC,gBAAAA,EAAAA,KAAC,QAAA,EAAK,WAAU,iCAAgC,UAAA;AAAA,MAAA;AAAA,MAAGlD;AAAA,IAAA,GAAW;AAAA,IAC9D2C,gBAAAA,EAAAA;AAAAA,MAACG;AAAA,MAAA;AAAA,QACC,SAAQ;AAAA,QACR,MAAK;AAAA,QACL,SAAS1B;AAAA,QACT,UAAUX,KAAeT;AAAA,QAEzB,gCAACyD,IAAA,CAAA,CAAiB;AAAA,MAAA;AAAA,IAAA;AAAA,EACpB,GACF,IAxC0B,MA4CxBC,KAAmB,MACnB,CAAChF,KAAe,CAACC,IAAuB,OAG1CuE,gBAAAA,EAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,eAAY;AAAA,MACZ,WAAU;AAAA,MAET,UAAA;AAAA,QAAAD,GAAA;AAAA,QACAM,GAAA;AAAA,MAAiB;AAAA,IAAA;AAAA,EAAA,GAMlBI,KAAgB,MACpBT,gBAAAA,OAAC,OAAA,EAAI,WAAU,2DACb,UAAA;AAAA,IAAAP,gBAAAA,EAAAA,IAACK,GAAA,EAAS,WAAU,WAAA,CAAW;AAAA,IAC/BL,gBAAAA,EAAAA,IAACK,GAAA,EAAS,WAAU,wBAAA,CAAwB;AAAA,IAC5CL,gBAAAA,EAAAA,IAAC,KAAA,EAAE,WAAU,iCAAiC,UAAAxD,EAAA,CAAY;AAAA,EAAA,GAC5D,GAIIyE,KAAc,MAClBjB,gBAAAA,EAAAA,IAAC,OAAA,EAAI,WAAU,2DACb,UAAAO,gBAAAA,EAAAA,KAAC,OAAA,EAAI,WAAU,gCACb,UAAA;AAAA,IAAAP,gBAAAA,EAAAA,IAAC,KAAA,EAAE,WAAU,eAAe,UAAAvD,GAAU;AAAA,IACrCU,KACC6C,gBAAAA,EAAAA,IAAC,KAAA,EAAE,WAAU,sCAAsC,YAAM,QAAA,CAAQ;AAAA,EAAA,EAAA,CAErE,EAAA,CACF,GAIIkB,KAAoB,MAAM;AAC9B,QAAI,CAACtD,KAAY,CAAClB,EAAa,QAAO;AAEtC,UAAM,EAAE,MAAAyE,MAASvD;AAEjB,WACEoC,gBAAAA,EAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAW,6CACTzC,IACI,uCACA,kBACN;AAAA,QAEC,UAAAS,GAAe,IAAI,CAACoD,MACnBpB,gBAAAA,EAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YAEC,WAAW5D;AAAA,YACX,OAAO;AAAA,cACL,SAASgF,MAAYtD,IAAc,UAAU;AAAA,YAAA;AAAA,YAG/C,UAAAkC,gBAAAA,EAAAA;AAAAA,cAACmB;AAAA,cAAA;AAAA,gBACC,KAAKzE;AAAA,gBACL,YAAY0E;AAAA,gBACZ,OAAArD;AAAA,gBACA,iBAAiB;AAAA,gBACjB,uBAAuB;AAAA,gBACvB,WAAU;AAAA,cAAA;AAAA,YAAA;AAAA,UACZ;AAAA,UAbKqD;AAAA,QAAA,CAeR;AAAA,MAAA;AAAA,IAAA;AAAA,EAGP;AAEA,+BACG,OAAA,EAAI,KAAK3D,GACR,UAAA8C,gBAAAA,EAAAA,KAACN,MAAK,WAAA/D,GACH,UAAA;AAAA,IAAA6E,GAAA;AAAA,IACDf,gBAAAA,EAAAA,IAACE,IAAA,EAAY,WAAW/D,GACrB,UAAAc,KACG+D,GAAA,IACA7D,IACE8D,GAAA,IACAC,GAAA,EAAkB,CAC1B;AAAA,EAAA,EAAA,CACF,EAAA,CACF;AAEJ;"}
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 const activeElement = document.activeElement as HTMLElement | null;\n const role = activeElement?.getAttribute('role');\n if (\n activeElement &&\n (activeElement.tagName === 'INPUT' ||\n activeElement.tagName === 'TEXTAREA' ||\n activeElement.tagName === 'SELECT' ||\n activeElement.isContentEditable ||\n role === 'textbox' ||\n role === 'spinbutton')\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 aria-label=\"缩小\"\n title=\"缩小\"\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 aria-label=\"放大\"\n title=\"放大\"\n >\n <ZoomInIcon />\n </Button>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => {\n void handleToggleFullscreen();\n }}\n aria-label={isFullscreen ? '退出全屏' : '进入全屏'}\n title={isFullscreen ? '退出全屏' : '进入全屏'}\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 aria-label=\"上一页\"\n title=\"上一页\"\n >\n <ChevronLeftIcon />\n </Button>\n {InputComponentInjected ? (\n <InputComponentInjected\n type=\"number\"\n name=\"simple-pdf-page\"\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 aria-label=\"当前页码\"\n inputMode=\"numeric\"\n autoComplete=\"off\"\n />\n ) : (\n <input\n type=\"number\"\n name=\"simple-pdf-page\"\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 aria-label=\"当前页码\"\n inputMode=\"numeric\"\n autoComplete=\"off\"\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 aria-label=\"下一页\"\n title=\"下一页\"\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\n className=\"flex flex-col items-center justify-center space-y-4 p-8\"\n role=\"status\"\n aria-live=\"polite\"\n >\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\n className=\"flex flex-col items-center justify-center space-y-4 p-8\"\n role=\"alert\"\n >\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","role","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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"}
@@ -1,2 +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;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./jsx-runtime-BB_1_6y_.cjs"),d=require("lucide-react"),E=require("react");function R({title:c,searchPlaceholder:n="搜索...",searchValue:p="",onSearchChange:i,onSearch:t,showSearch:j=!0,action:r,actionLabel:x,onActionClick:m,loading:l=!1,components:o}){const a=E.useId();if(!o)return e.jsxRuntimeExports.jsx("div",{className:"p-4 text-center text-destructive",children:"错误:请通过 components prop 注入 UI 组件"});const{Input:f,Button:u}=o,v=s=>{s.nativeEvent.isComposing||s.nativeEvent.isComposing||s.key==="Enter"&&t&&t()};return e.jsxRuntimeExports.jsxs("div",{className:"flex min-w-0 flex-col gap-4 p-1",children:[e.jsxRuntimeExports.jsxs("div",{className:"flex min-w-0 flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",children:[e.jsxRuntimeExports.jsx("div",{className:"min-w-0 break-words text-lg font-semibold leading-tight text-foreground",children:c}),e.jsxRuntimeExports.jsxs("div",{className:"flex shrink-0 items-center gap-2",children:[r&&e.jsxRuntimeExports.jsx("div",{className:"flex min-w-0 items-center gap-2",children:r}),!r&&x&&m&&e.jsxRuntimeExports.jsxs(u,{onClick:m,disabled:l,size:"sm",children:[e.jsxRuntimeExports.jsx(d.Plus,{className:"mr-2 h-4 w-4"}),x]})]})]}),j&&e.jsxRuntimeExports.jsxs("div",{className:"flex min-w-0 flex-col gap-2 sm:flex-row sm:items-center",children:[e.jsxRuntimeExports.jsxs("div",{className:"relative min-w-0 flex-1 sm:max-w-sm",children:[e.jsxRuntimeExports.jsx("label",{htmlFor:a,className:"sr-only",children:n}),e.jsxRuntimeExports.jsx(d.Search,{className:"absolute left-2 top-2.5 h-4 w-4 text-muted-foreground"}),e.jsxRuntimeExports.jsx(f,{id:a,name:"table-search",type:"search",placeholder:n,value:p,onChange:s=>i==null?void 0:i(s.target.value),onKeyDown:v,className:"h-9 pl-8",disabled:l,autoComplete:"off",spellCheck:!1})]}),t&&e.jsxRuntimeExports.jsx(u,{onClick:t,disabled:l,variant:"secondary",size:"sm",children:"搜索"})]})]})}exports.TableHeader=R;
2
2
  //# sourceMappingURL=table-header.cjs.map
@@ -1 +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"}
1
+ {"version":3,"file":"table-header.cjs","sources":["../src/components/TableHeader.tsx"],"sourcesContent":["import { Plus, Search } from 'lucide-react';\nimport { ReactNode, useId } 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 const searchInputId = useId();\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 { Input, Button } = components;\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n const isComposing =\n e.nativeEvent.isComposing ||\n (e.nativeEvent as KeyboardEvent & { isComposing?: boolean }).isComposing;\n if (isComposing) {\n return;\n }\n if (e.key === 'Enter' && onSearch) {\n onSearch();\n }\n };\n\n return (\n <div className=\"flex min-w-0 flex-col gap-4 p-1\">\n <div className=\"flex min-w-0 flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"min-w-0 break-words text-lg font-semibold leading-tight text-foreground\">\n {title}\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {action && (\n <div className=\"flex min-w-0 items-center gap-2\">{action}</div>\n )}\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 min-w-0 flex-col gap-2 sm:flex-row sm:items-center\">\n <div className=\"relative min-w-0 flex-1 sm:max-w-sm\">\n <label htmlFor={searchInputId} className=\"sr-only\">\n {searchPlaceholder}\n </label>\n <Search className=\"absolute left-2 top-2.5 h-4 w-4 text-muted-foreground\" />\n <Input\n id={searchInputId}\n name=\"table-search\"\n type=\"search\"\n placeholder={searchPlaceholder}\n value={searchValue}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n onSearchChange?.(e.target.value)\n }\n onKeyDown={handleKeyDown}\n className=\"h-9 pl-8\"\n disabled={loading}\n autoComplete=\"off\"\n spellCheck={false}\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","searchInputId","useId","jsx","Input","Button","handleKeyDown","e","jsxs","Plus","Search"],"mappings":"2KAiCO,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,MAAMC,EAAgBC,EAAAA,MAAA,EAEtB,GAAI,CAACF,EACH,OACEG,EAAAA,kBAAAA,IAAC,MAAA,CAAI,UAAU,mCAAmC,SAAA,kCAElD,EAIJ,KAAM,CAAE,MAAAC,EAAO,OAAAC,CAAA,EAAWL,EAEpBM,EAAiBC,GAA2B,CAE9CA,EAAE,YAAY,aACbA,EAAE,YAA0D,aAI3DA,EAAE,MAAQ,SAAWb,GACvBA,EAAA,CAEJ,EAEA,OACEc,EAAAA,kBAAAA,KAAC,MAAA,CAAI,UAAU,kCACb,SAAA,CAAAA,EAAAA,kBAAAA,KAAC,MAAA,CAAI,UAAU,6EACb,SAAA,CAAAL,EAAAA,kBAAAA,IAAC,MAAA,CAAI,UAAU,0EACZ,SAAAb,EACH,EACAkB,EAAAA,kBAAAA,KAAC,MAAA,CAAI,UAAU,mCACZ,SAAA,CAAAZ,GACCO,EAAAA,kBAAAA,IAAC,MAAA,CAAI,UAAU,kCAAmC,SAAAP,EAAO,EAE1D,CAACA,GAAUC,GAAeC,GACzBU,EAAAA,kBAAAA,KAACH,EAAA,CAAO,QAASP,EAAe,SAAUC,EAAS,KAAK,KACtD,SAAA,CAAAI,EAAAA,kBAAAA,IAACM,EAAAA,KAAA,CAAK,UAAU,cAAA,CAAe,EAC9BZ,CAAA,CAAA,CACH,CAAA,CAAA,CAEJ,CAAA,EACF,EACCF,GACCa,EAAAA,kBAAAA,KAAC,MAAA,CAAI,UAAU,0DACb,SAAA,CAAAA,EAAAA,kBAAAA,KAAC,MAAA,CAAI,UAAU,sCACb,SAAA,CAAAL,wBAAC,QAAA,CAAM,QAASF,EAAe,UAAU,UACtC,SAAAV,EACH,EACAY,EAAAA,kBAAAA,IAACO,EAAAA,OAAA,CAAO,UAAU,uDAAA,CAAwD,EAC1EP,EAAAA,kBAAAA,IAACC,EAAA,CACC,GAAIH,EACJ,KAAK,eACL,KAAK,SACL,YAAaV,EACb,MAAOC,EACP,SAAWe,GACTd,GAAA,YAAAA,EAAiBc,EAAE,OAAO,OAE5B,UAAWD,EACX,UAAU,WACV,SAAUP,EACV,aAAa,MACb,WAAY,EAAA,CAAA,CACd,EACF,EACCL,GACCS,EAAAA,kBAAAA,IAACE,EAAA,CACC,QAASX,EACT,SAAUK,EACV,QAAQ,YACR,KAAK,KACN,SAAA,IAAA,CAAA,CAED,CAAA,CAEJ,CAAA,EAEJ,CAEJ"}