@windrun-huaiin/third-ui 5.14.0 → 5.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windrun-huaiin/third-ui",
3
- "version": "5.14.0",
3
+ "version": "5.14.1",
4
4
  "description": "Third-party integrated UI components for windrun-huaiin projects",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { globalLucideIcons as icons } from '@base-ui/components/global-icon';
4
- // 注意:不使用外部对话框库,避免第三方应用构建时的 React 上下文冲突
4
+ // Attention: do not use external dialog library, avoid react context conflict when building third-party applications
5
5
  import type { MermaidConfig } from 'mermaid';
6
6
  import { useTheme } from 'next-themes';
7
7
  import { useCallback, useEffect, useId, useRef, useState } from 'react';
@@ -68,22 +68,24 @@ export function Mermaid({ chart, title, watermarkEnabled, watermarkText, enableP
68
68
  // helpers for preview zoom
69
69
  const clamp = (v: number, min: number, max: number) => Math.min(Math.max(v, min), max);
70
70
  const resetTransform = useCallback(() => {
71
- setScale(1);
71
+ setScale(4); // 400%
72
72
  setTranslate({ x: 0, y: 0 });
73
73
  }, []);
74
74
 
75
75
  const zoomBy = useCallback((delta: number) => {
76
- // 基于中心缩放:保持缩放中心在画布中点,不引入位移
77
- setScale((prev) => clamp(prev + delta, 0.25, 6));
76
+ // zoom by center: keep the zoom center at the center of the canvas, without introducing displacement
77
+ setScale((prev) => clamp(prev + delta, 0.25, 10));
78
78
  }, []);
79
79
 
80
80
  const onWheel = useCallback((e: React.WheelEvent<HTMLDivElement>) => {
81
- // Cmd/Ctrl + 滚轮缩放(围绕中心点),否则上下平移
81
+ // Cmd/Ctrl + wheel zoom (around the center point), otherwise up and down panning
82
82
  if (e.metaKey || e.ctrlKey) {
83
83
  e.preventDefault();
84
+ e.stopPropagation();
84
85
  const delta = e.deltaY > 0 ? -0.1 : 0.1;
85
- setScale((prev) => clamp(prev + delta, 0.25, 6));
86
+ setScale((prev) => clamp(prev + delta, 0.25, 10));
86
87
  } else {
88
+ e.stopPropagation();
87
89
  setTranslate((prev) => ({ x: prev.x, y: prev.y - e.deltaY }));
88
90
  }
89
91
  }, []);
@@ -106,6 +108,62 @@ export function Mermaid({ chart, title, watermarkEnabled, watermarkText, enableP
106
108
  isPanningRef.current = false;
107
109
  (e.currentTarget as HTMLDivElement).releasePointerCapture(e.pointerId);
108
110
  }, []);
111
+
112
+ // prevent browser-level zoom (touchpad pinch/shortcut) from taking effect when the dialog is open
113
+ useEffect(() => {
114
+ if (!open) return;
115
+ // 初次打开时,默认放大到 400%
116
+ resetTransform();
117
+ const onGlobalWheel = (ev: WheelEvent) => {
118
+ if (ev.ctrlKey || ev.metaKey) {
119
+ ev.preventDefault();
120
+ }
121
+ };
122
+ const onKeyDown = (ev: KeyboardEvent) => {
123
+ if (!(ev.ctrlKey || ev.metaKey)) return;
124
+ const k = ev.key;
125
+ if (k === '=' || k === '+') {
126
+ ev.preventDefault();
127
+ setScale((prev) => clamp(prev + 0.2, 0.25, 10));
128
+ } else if (k === '-') {
129
+ ev.preventDefault();
130
+ setScale((prev) => clamp(prev - 0.2, 0.25, 10));
131
+ } else if (k === '0') {
132
+ ev.preventDefault();
133
+ resetTransform();
134
+ }
135
+ };
136
+ window.addEventListener('wheel', onGlobalWheel, { passive: false, capture: true });
137
+ window.addEventListener('keydown', onKeyDown, { capture: true });
138
+ return () => {
139
+ window.removeEventListener('wheel', onGlobalWheel, true);
140
+ window.removeEventListener('keydown', onKeyDown, true);
141
+ };
142
+ }, [open, resetTransform]);
143
+
144
+ // Lock background scroll when dialog is open
145
+ useEffect(() => {
146
+ if (!open) return;
147
+ const previousPosition = document.body.style.position;
148
+ const previousTop = document.body.style.top;
149
+ const previousLeft = document.body.style.left;
150
+ const previousRight = document.body.style.right;
151
+ const previousWidth = document.body.style.width;
152
+ const scrollY = window.scrollY;
153
+ document.body.style.position = 'fixed';
154
+ document.body.style.top = `-${scrollY}px`;
155
+ document.body.style.left = '0';
156
+ document.body.style.right = '0';
157
+ document.body.style.width = '100%';
158
+ return () => {
159
+ document.body.style.position = previousPosition;
160
+ document.body.style.top = previousTop;
161
+ document.body.style.left = previousLeft;
162
+ document.body.style.right = previousRight;
163
+ document.body.style.width = previousWidth;
164
+ window.scrollTo(0, scrollY);
165
+ };
166
+ }, [open]);
109
167
 
110
168
  return (
111
169
  <div>
@@ -137,7 +195,12 @@ export function Mermaid({ chart, title, watermarkEnabled, watermarkText, enableP
137
195
  aria-label={typeof title === 'string' ? title : 'Mermaid Preview'}
138
196
  className="fixed inset-0 z-[9999] flex items-center justify-center"
139
197
  >
140
- <div className="absolute inset-0 bg-black/60" onClick={() => { setOpen(false); resetTransform(); }} />
198
+ <div
199
+ className="absolute inset-0 bg-black/60"
200
+ onClick={() => { setOpen(false); resetTransform(); }}
201
+ onWheel={(e) => { e.preventDefault(); e.stopPropagation(); }}
202
+ onTouchMove={(e) => { e.preventDefault(); e.stopPropagation(); }}
203
+ />
141
204
  <div className="relative z-[1] max-w-[95vw] w-[95vw] h-[88vh] p-0 bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-700 rounded-md shadow-2xl overflow-hidden">
142
205
  {/* Top bar */}
143
206
  <div className="flex items-center justify-between px-3 py-2 border-b border-neutral-200 dark:border-neutral-700">
@@ -149,7 +212,7 @@ export function Mermaid({ chart, title, watermarkEnabled, watermarkText, enableP
149
212
  <button
150
213
  aria-label="Zoom out"
151
214
  className="flex h-6 w-6 items-center justify-center rounded border border-neutral-300 dark:border-neutral-600 text-[13px]"
152
- onClick={() => zoomBy(-0.2)}
215
+ onClick={() => zoomBy(-0.5)}
153
216
  >
154
217
 
155
218
  </button>
@@ -157,10 +220,40 @@ export function Mermaid({ chart, title, watermarkEnabled, watermarkText, enableP
157
220
  <button
158
221
  aria-label="Zoom in"
159
222
  className="flex h-6 w-6 items-center justify-center rounded border border-neutral-300 dark:border-neutral-600 text-[13px]"
160
- onClick={() => zoomBy(0.2)}
223
+ onClick={() => zoomBy(0.5)}
161
224
  >
162
225
 
163
226
  </button>
227
+ {/* quick zoom shortcuts */}
228
+ <div className="mx-1 h-4 w-px bg-neutral-300 dark:bg-neutral-700" />
229
+ <button
230
+ aria-label="Zoom 100%"
231
+ className="inline-flex h-6 min-w-8 items-center justify-center rounded border border-neutral-300 dark:border-neutral-600 px-1.5 text-[12px]"
232
+ onClick={() => setScale(1)}
233
+ >
234
+ X1
235
+ </button>
236
+ <button
237
+ aria-label="Zoom 200%"
238
+ className="ml-1 inline-flex h-6 min-w-8 items-center justify-center rounded border border-neutral-300 dark:border-neutral-600 px-1.5 text-[12px]"
239
+ onClick={() => setScale(2)}
240
+ >
241
+ X2
242
+ </button>
243
+ <button
244
+ aria-label="Zoom 300%"
245
+ className="ml-1 inline-flex h-6 min-w-8 items-center justify-center rounded border border-neutral-300 dark:border-neutral-600 px-1.5 text-[12px]"
246
+ onClick={() => setScale(3)}
247
+ >
248
+ X3
249
+ </button>
250
+ <button
251
+ aria-label="Zoom 1000%"
252
+ className="ml-1 inline-flex h-6 min-w-10 items-center justify-center rounded border border-neutral-300 dark:border-neutral-600 px-1.5 text-[12px]"
253
+ onClick={() => setScale(10)}
254
+ >
255
+ X10
256
+ </button>
164
257
  <button
165
258
  aria-label="Reset"
166
259
  className="ml-1 flex h-6 w-6 items-center justify-center rounded text-purple-500 hover:text-purple-600"
@@ -180,7 +273,7 @@ export function Mermaid({ chart, title, watermarkEnabled, watermarkText, enableP
180
273
 
181
274
  {/* Canvas */}
182
275
  <div
183
- className="relative h-[calc(88vh-40px)] w-full overflow-hidden bg-white dark:bg-neutral-900"
276
+ className="relative h-[calc(88vh-40px)] w-full overflow-hidden bg-white dark:bg-neutral-900 touch-none overscroll-contain"
184
277
  onWheel={onWheel}
185
278
  onPointerDown={onPointerDown}
186
279
  onPointerMove={onPointerMove}