@arronqzy/react-view 1.0.39 → 1.0.40

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.
@@ -304,14 +304,22 @@ export function MoveableLayer({
304
304
  });
305
305
  }, [lockedSelected, pointerRoot, selectedTargets]);
306
306
 
307
+ const applyLiveMoveableZoom = useCallback(() => {
308
+ const moveable = moveableRef.current;
309
+ if (!moveable) return;
310
+ const liveScale = readLiveCanvasScale(canvasContainer, propCanvasScale);
311
+ moveable.zoom = 1 / Math.max(0.0001, liveScale);
312
+ }, [canvasContainer, propCanvasScale]);
313
+
307
314
  const scheduleViewportSync = useCallback(() => {
308
315
  if (syncRafRef.current != null) return;
309
316
  syncRafRef.current = requestAnimationFrame(() => {
310
317
  syncRafRef.current = null;
318
+ applyLiveMoveableZoom();
311
319
  moveableRef.current?.updateRect?.();
312
320
  syncLockBadge();
313
321
  });
314
- }, [syncLockBadge]);
322
+ }, [applyLiveMoveableZoom, syncLockBadge]);
315
323
 
316
324
  useEffect(() => {
317
325
  if (!viewportSyncRef) return;
@@ -322,9 +330,10 @@ export function MoveableLayer({
322
330
  }, [scheduleViewportSync, viewportSyncRef]);
323
331
 
324
332
  useLayoutEffect(() => {
333
+ applyLiveMoveableZoom();
325
334
  moveableRef.current?.updateRect?.();
326
335
  syncLockBadge();
327
- }, [canvasScale, syncLockBadge]);
336
+ }, [applyLiveMoveableZoom, canvasScale, syncLockBadge]);
328
337
 
329
338
  const updateRectNextFrame = scheduleViewportSync;
330
339
  const getGridDirectChildren = useCallback(
@@ -83,6 +83,7 @@ export const PanelCanvas = React.forwardRef<HTMLDivElement, PanelCanvasProps>(
83
83
  el.style.transform = `scale(${scale})`;
84
84
  el.style.transformOrigin = "0 0";
85
85
  el.dataset.canvasScale = String(scale);
86
+ el.style.setProperty("--rv-canvas-scale", String(scale));
86
87
  }, []);
87
88
 
88
89
  const commitScroll = useCallback(
@@ -49,6 +49,7 @@ import {
49
49
  OPTION_INPUT_CLASS,
50
50
  OPTION_SELECT_TRIGGER_CLASS,
51
51
  } from "./config/helpers";
52
+ import { IconSearch } from "../icons";
52
53
 
53
54
  export type PanelConfigSidebarProps = {
54
55
  selectedElement: PanelElement | null;
@@ -101,9 +102,7 @@ export function PanelConfigSidebar({
101
102
  const messages = React.useMemo(() => getPanelMessages(t), [t]);
102
103
 
103
104
  const [configSearch, setConfigSearch] = useState("");
104
- const [isSearchCollapsed, setIsSearchCollapsed] = useState(false);
105
- const SEARCH_COLLAPSE_STORAGE_KEY = "panel:config-search-collapsed";
106
- const sidebarScrollRef = useRef<HTMLElement | null>(null);
105
+ const sidebarScrollRef = useRef<HTMLDivElement | null>(null);
107
106
  const matchCountRef = useRef(0);
108
107
  const themedScrollbarClass =
109
108
  "scrollbar-thin [&::-webkit-scrollbar]:h-2 [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-track]:bg-muted/40 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-border/80 [&::-webkit-scrollbar-thumb]:hover:bg-border";
@@ -208,16 +207,6 @@ export function PanelConfigSidebar({
208
207
  setExpandedSections(initial);
209
208
  }, [configScrollKey]);
210
209
 
211
- useEffect(() => {
212
- if (typeof window === "undefined") return;
213
- const saved = window.localStorage.getItem(SEARCH_COLLAPSE_STORAGE_KEY);
214
- if (saved === "1") setIsSearchCollapsed(true);
215
- }, []);
216
- useEffect(() => {
217
- if (typeof window === "undefined") return;
218
- window.localStorage.setItem(SEARCH_COLLAPSE_STORAGE_KEY, isSearchCollapsed ? "1" : "0");
219
- }, [isSearchCollapsed]);
220
-
221
210
  const isSectionExpanded = (key: string, defaultValue = true) =>
222
211
  expandedSections[key] ?? defaultValue;
223
212
 
@@ -275,14 +264,14 @@ export function PanelConfigSidebar({
275
264
  <Collapsible
276
265
  open={hasSearch ? true : isSectionExpanded(key, defaultOpen)}
277
266
  onOpenChange={(open) => setSectionExpanded(key, open)}
278
- className="rounded-xl border border-border/70 bg-card/95 shadow-sm"
267
+ className="rounded-lg border border-border/60 bg-card/90 shadow-[0_1px_1px_hsl(var(--foreground)/0.03)]"
279
268
  data-config-section={key}
280
269
  >
281
- <div className="flex items-center gap-1.5 px-3 py-2">
270
+ <div className="flex items-center gap-1 px-2 py-1.5">
282
271
  <CollapsibleTrigger asChild>
283
272
  <button
284
273
  type="button"
285
- className="flex h-6 w-6 items-center justify-center rounded-md text-xs hover:bg-accent"
274
+ className="flex h-5 w-5 items-center justify-center rounded text-xs hover:bg-accent"
286
275
  >
287
276
  {isSectionExpanded(key, defaultOpen) ? "▾" : "▸"}
288
277
  </button>
@@ -292,7 +281,7 @@ export function PanelConfigSidebar({
292
281
  {hint ? <ConfigHintIcon label={title}>{hint}</ConfigHintIcon> : null}
293
282
  </div>
294
283
  </div>
295
- <CollapsibleContent className="space-y-3 border-t border-border/60 bg-muted/[0.1] px-3 pb-3 pt-2.5">
284
+ <CollapsibleContent className="space-y-2 border-t border-border/50 bg-muted/[0.08] px-2 pb-2 pt-1.5">
296
285
  {children}
297
286
  </CollapsibleContent>
298
287
  </Collapsible>
@@ -315,7 +304,7 @@ export function PanelConfigSidebar({
315
304
  );
316
305
  if (!groupKey) {
317
306
  return (
318
- <div className="space-y-2.5 rounded-lg border border-border/55 bg-background/80 p-2.5">
307
+ <div className="space-y-1.5 rounded-md border border-border/50 bg-background/70 p-2">
319
308
  <div className="flex items-center gap-1">{header}</div>
320
309
  {children}
321
310
  </div>
@@ -326,9 +315,9 @@ export function PanelConfigSidebar({
326
315
  <Collapsible
327
316
  open={open}
328
317
  onOpenChange={(next) => setSectionExpanded(groupKey, next)}
329
- className="space-y-2.5 rounded-lg border border-border/55 bg-background/80 p-2.5"
318
+ className="space-y-1.5 rounded-md border border-border/50 bg-background/70 p-2"
330
319
  >
331
- <div className="flex items-center gap-1.5">
320
+ <div className="flex items-center gap-1">
332
321
  <CollapsibleTrigger asChild>
333
322
  <button
334
323
  type="button"
@@ -340,7 +329,7 @@ export function PanelConfigSidebar({
340
329
  </CollapsibleTrigger>
341
330
  {header}
342
331
  </div>
343
- <CollapsibleContent className="space-y-2.5">{children}</CollapsibleContent>
332
+ <CollapsibleContent className="space-y-1.5">{children}</CollapsibleContent>
344
333
  </Collapsible>
345
334
  );
346
335
  };
@@ -420,49 +409,43 @@ export function PanelConfigSidebar({
420
409
  scrollContainerRef={sidebarScrollRef}
421
410
  >
422
411
  <aside
423
- ref={sidebarScrollRef}
424
- className={`scope-config-sidebar h-full overflow-auto border-l border-border bg-muted/[0.14] px-3 py-3 text-foreground [&_.config-field--highlight]:rounded-md [&_.config-field--highlight]:ring-2 [&_.config-field--highlight]:ring-sky-400/80 [&_.scope-field--highlight]:rounded-md [&_.scope-field--highlight]:ring-2 [&_.scope-field--highlight]:ring-amber-400/80 [&_button[role=checkbox]]:border-2 [&_button[role=checkbox]]:border-foreground/80 [&_button[role=checkbox]]:bg-background [&_button[role=checkbox]]:ring-1 [&_button[role=checkbox]]:ring-foreground/40 [&_button[role=checkbox][data-state=checked]]:border-primary [&_button[role=checkbox][data-state=checked]]:ring-primary/40 ${themedScrollbarClass}`}
412
+ className="scope-config-sidebar grid h-full min-h-0 w-full overflow-hidden border-l border-border/60 bg-muted/[0.12] text-foreground [&_.config-field--highlight]:rounded-md [&_.config-field--highlight]:ring-2 [&_.config-field--highlight]:ring-sky-400/80 [&_.scope-field--highlight]:rounded-md [&_.scope-field--highlight]:ring-2 [&_.scope-field--highlight]:ring-amber-400/80 [&_button[role=checkbox]]:border-2 [&_button[role=checkbox]]:border-foreground/80 [&_button[role=checkbox]]:bg-background [&_button[role=checkbox]]:ring-1 [&_button[role=checkbox]]:ring-foreground/40 [&_button[role=checkbox][data-state=checked]]:border-primary [&_button[role=checkbox][data-state=checked]]:ring-primary/40"
413
+ style={{ gridTemplateRows: "auto 1fr" }}
425
414
  >
426
- <div className="sticky top-0 z-20 mb-3 rounded-lg border border-border/70 bg-card/95 px-2.5 py-2 shadow-sm backdrop-blur supports-[backdrop-filter]:bg-card/80">
427
- <div className="flex items-center justify-between gap-2">
428
- <div className="text-xs font-semibold tracking-wide">{t("panel.config.panelTitle")}</div>
415
+ <div className="border-b border-border/50 bg-background/70 px-2.5 py-1.5 backdrop-blur-sm">
416
+ <div className="relative">
417
+ <span
418
+ className="pointer-events-none absolute top-1/2 z-[1] -translate-y-1/2 text-muted-foreground"
419
+ style={{ left: 12 }}
420
+ >
421
+ <IconSearch />
422
+ </span>
423
+ <Input
424
+ value={configSearch}
425
+ onChange={(e) => setConfigSearch(e.target.value)}
426
+ placeholder={t("panel.config.searchPlaceholder")}
427
+ className="h-7 rounded-md border-border/60 bg-background/90 text-[12px] shadow-none"
428
+ style={{ paddingLeft: 34, paddingRight: 10 }}
429
+ aria-label={t("panel.config.searchPlaceholder")}
430
+ data-scope-autocomplete="off"
431
+ />
429
432
  </div>
430
- {showScopePanel ? <ViewElementScopePanel scope={viewElementScope} /> : null}
431
- <ScopeTemplateWarningsPanel />
432
- <div
433
- className={
434
- showScopePanel || scopeWarnings.length > 0
435
- ? "mt-2 border-t border-border/50 pt-2"
436
- : "mt-0"
437
- }
438
- >
439
- <div className="flex items-center justify-end gap-2">
440
- <button
441
- type="button"
442
- className="rounded border border-border px-1.5 py-0.5 text-[11px] text-muted-foreground hover:bg-accent"
443
- onClick={() => setIsSearchCollapsed((prev) => !prev)}
444
- >
445
- {isSearchCollapsed ? t("panel.config.expandSearch") : t("panel.config.collapseSearch")}
446
- </button>
433
+ {hasSearch ? (
434
+ <div className="mt-1 px-0.5 text-[11px] text-muted-foreground">
435
+ {t("panel.config.searching", { query: configSearch })}
447
436
  </div>
448
- {!isSearchCollapsed ? (
449
- <div className="mt-2">
450
- <Input
451
- value={configSearch}
452
- onChange={(e) => setConfigSearch(e.target.value)}
453
- placeholder={t("panel.config.searchPlaceholder")}
454
- className="h-7"
455
- data-scope-autocomplete="off"
456
- />
457
- {hasSearch ? (
458
- <div className="mt-1 text-[11px] text-muted-foreground">
459
- {t("panel.config.searching", { query: configSearch })}
460
- </div>
461
- ) : null}
462
- </div>
463
- ) : null}
464
- </div>
437
+ ) : null}
465
438
  </div>
439
+ <div
440
+ ref={sidebarScrollRef}
441
+ className={`min-h-0 overflow-auto px-2 py-1.5 ${themedScrollbarClass}`}
442
+ >
443
+ {showScopePanel || scopeWarnings.length > 0 ? (
444
+ <div className="mb-2 space-y-1.5">
445
+ {showScopePanel ? <ViewElementScopePanel scope={viewElementScope} /> : null}
446
+ <ScopeTemplateWarningsPanel />
447
+ </div>
448
+ ) : null}
466
449
  {isMultiSelectMode ? (
467
450
  <PanelConfigMultiSelect
468
451
  elements={effectiveSelectedElements}
@@ -475,7 +458,7 @@ export function PanelConfigSidebar({
475
458
  />
476
459
  ) : null}
477
460
  {!isMultiSelectMode && !selectedElement ? (
478
- <Empty className="py-7">
461
+ <Empty className="py-5">
479
462
  <EmptyIcon>
480
463
  <svg
481
464
  viewBox="0 0 24 24"
@@ -493,14 +476,14 @@ export function PanelConfigSidebar({
493
476
  <EmptyDescription>{t("panel.config.emptyNoNodeDesc")}</EmptyDescription>
494
477
  </Empty>
495
478
  ) : !isMultiSelectMode && selectedElement ? (
496
- <div className="space-y-3">
479
+ <div className="space-y-2">
497
480
  {!isNodeEditable ? (
498
481
  <div className="rounded border border-amber-500/40 bg-amber-500/10 px-2 py-1.5 text-[11px] text-amber-700 dark:text-amber-300">
499
482
  {readonlyReason}
500
483
  </div>
501
484
  ) : null}
502
485
  <fieldset disabled={!isNodeEditable} className={!isNodeEditable ? "opacity-60" : ""}>
503
- <div className="space-y-3.5 text-xs">
486
+ <div className="space-y-2 text-xs">
504
487
  <PanelConfigNodeInfo
505
488
  element={selectedElement}
506
489
  helpers={helpers}
@@ -510,7 +493,7 @@ export function PanelConfigSidebar({
510
493
  />
511
494
  </div>
512
495
  </fieldset>
513
- <div className="rounded-lg border border-border/60 bg-card/80 px-2.5 py-2 text-xs">
496
+ <div className="rounded-md border border-border/50 bg-card/80 px-2 py-1.5 text-xs">
514
497
  <label className="flex items-center gap-2">
515
498
  <Checkbox
516
499
  checked={selectedElement.locked === true}
@@ -526,7 +509,7 @@ export function PanelConfigSidebar({
526
509
  </label>
527
510
  </div>
528
511
  <fieldset disabled={!isNodeEditable} className={!isNodeEditable ? "opacity-60" : ""}>
529
- <div className="space-y-3.5 text-xs">
512
+ <div className="space-y-2 text-xs">
530
513
  {isChartElement ? (
531
514
  <PanelConfigChartSection
532
515
  element={selectedElement}
@@ -641,6 +624,7 @@ export function PanelConfigSidebar({
641
624
  </fieldset>
642
625
  </div>
643
626
  ) : null}
627
+ </div>
644
628
  </aside>
645
629
  </ScopeConfigProvider>
646
630
  );