@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.
- package/dist/styles.css +316 -241
- package/package.json +7 -7
- package/src/panel/ReactViewPanel.tsx +80 -71
- package/src/panel/components/ElementsLayer.tsx +1 -1
- package/src/panel/components/MaterialSidebar.tsx +304 -264
- package/src/panel/components/MoveableLayer.tsx +11 -2
- package/src/panel/components/PanelCanvas.tsx +1 -0
- package/src/panel/components/PanelConfigSidebar.tsx +49 -65
- package/src/panel/components/PanelLayerDock.tsx +397 -316
- package/src/panel/components/PanelRulers.tsx +169 -42
- package/src/panel/components/SelectLayer.tsx +2 -2
- package/src/panel/components/WorkspaceProjectNav.tsx +1 -1
- package/src/panel/icons.tsx +117 -0
- package/src/tailwind.css +66 -5
|
@@ -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
|
|
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-
|
|
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
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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-
|
|
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
|
-
|
|
424
|
-
|
|
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="
|
|
427
|
-
<div className="
|
|
428
|
-
<
|
|
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
|
-
|
|
431
|
-
|
|
432
|
-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
);
|