@seedgrid/fe-components 0.2.10 → 2026.3.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/dist/buttons/SgFloatActionButton.d.ts.map +1 -1
- package/dist/buttons/SgFloatActionButton.js +168 -38
- package/dist/commons/SgAvatar.d.ts +66 -0
- package/dist/commons/SgAvatar.d.ts.map +1 -0
- package/dist/commons/SgAvatar.js +136 -0
- package/dist/commons/SgSkeleton.d.ts +16 -0
- package/dist/commons/SgSkeleton.d.ts.map +1 -0
- package/dist/commons/SgSkeleton.js +58 -0
- package/dist/digits/discard-digit/SgDiscardDigit.d.ts +39 -0
- package/dist/digits/discard-digit/SgDiscardDigit.d.ts.map +1 -0
- package/dist/digits/discard-digit/SgDiscardDigit.js +303 -0
- package/dist/digits/discard-digit/index.d.ts +3 -0
- package/dist/digits/discard-digit/index.d.ts.map +1 -0
- package/dist/digits/discard-digit/index.js +1 -0
- package/dist/digits/fade-digit/SgFadeDigit.d.ts +27 -0
- package/dist/digits/fade-digit/SgFadeDigit.d.ts.map +1 -0
- package/dist/digits/fade-digit/SgFadeDigit.js +85 -0
- package/dist/digits/fade-digit/index.d.ts +3 -0
- package/dist/digits/fade-digit/index.d.ts.map +1 -0
- package/dist/digits/fade-digit/index.js +1 -0
- package/dist/digits/flip-digit/SgFlipDigit.d.ts +27 -0
- package/dist/digits/flip-digit/SgFlipDigit.d.ts.map +1 -0
- package/dist/digits/flip-digit/SgFlipDigit.js +70 -0
- package/dist/digits/flip-digit/index.d.ts.map +1 -0
- package/dist/digits/matrix-digit/SgMatrixDigit.d.ts +32 -0
- package/dist/digits/matrix-digit/SgMatrixDigit.d.ts.map +1 -0
- package/dist/digits/matrix-digit/SgMatrixDigit.js +86 -0
- package/dist/digits/matrix-digit/index.d.ts +3 -0
- package/dist/digits/matrix-digit/index.d.ts.map +1 -0
- package/dist/digits/matrix-digit/index.js +1 -0
- package/dist/digits/neon-digit/SgNeonDigit.d.ts +37 -0
- package/dist/digits/neon-digit/SgNeonDigit.d.ts.map +1 -0
- package/dist/digits/neon-digit/SgNeonDigit.js +59 -0
- package/dist/digits/neon-digit/index.d.ts +3 -0
- package/dist/digits/neon-digit/index.d.ts.map +1 -0
- package/dist/digits/neon-digit/index.js +1 -0
- package/dist/digits/roller3d-digit/SgRoller3DDigit.d.ts +37 -0
- package/dist/digits/roller3d-digit/SgRoller3DDigit.d.ts.map +1 -0
- package/dist/digits/roller3d-digit/SgRoller3DDigit.js +47 -0
- package/dist/digits/roller3d-digit/index.d.ts +3 -0
- package/dist/digits/roller3d-digit/index.d.ts.map +1 -0
- package/dist/digits/roller3d-digit/index.js +1 -0
- package/dist/environment/SgEnvironmentProvider.d.ts +1 -0
- package/dist/environment/SgEnvironmentProvider.d.ts.map +1 -1
- package/dist/environment/SgEnvironmentProvider.js +51 -12
- package/dist/gadgets/clock/SgClock.d.ts +3 -1
- package/dist/gadgets/clock/SgClock.d.ts.map +1 -1
- package/dist/gadgets/clock/SgClock.js +111 -180
- package/dist/gadgets/clock/SgTimeProvider.d.ts +1 -0
- package/dist/gadgets/clock/SgTimeProvider.d.ts.map +1 -1
- package/dist/gadgets/clock/SgTimeProvider.js +11 -4
- package/dist/gadgets/gauge/SgLinearGauge.d.ts +59 -0
- package/dist/gadgets/gauge/SgLinearGauge.d.ts.map +1 -0
- package/dist/gadgets/gauge/SgLinearGauge.js +258 -0
- package/dist/gadgets/gauge/SgRadialGauge.d.ts +73 -0
- package/dist/gadgets/gauge/SgRadialGauge.d.ts.map +1 -0
- package/dist/gadgets/gauge/SgRadialGauge.js +311 -0
- package/dist/gadgets/gauge/index.d.ts +5 -0
- package/dist/gadgets/gauge/index.d.ts.map +1 -0
- package/dist/gadgets/gauge/index.js +2 -0
- package/dist/gadgets/string-animator/SgStringAnimator.d.ts +91 -0
- package/dist/gadgets/string-animator/SgStringAnimator.d.ts.map +1 -0
- package/dist/gadgets/string-animator/SgStringAnimator.js +145 -0
- package/dist/gadgets/string-animator/index.d.ts +3 -0
- package/dist/gadgets/string-animator/index.d.ts.map +1 -0
- package/dist/gadgets/string-animator/index.js +1 -0
- package/dist/i18n/en-US.json +9 -1
- package/dist/i18n/es.json +55 -47
- package/dist/i18n/index.d.ts +32 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/pt-BR.json +9 -1
- package/dist/i18n/pt-PT.json +9 -1
- package/dist/index.d.ts +46 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -1
- package/dist/inputs/SgAutocomplete.js +21 -5
- package/dist/inputs/SgCombobox.d.ts.map +1 -1
- package/dist/inputs/SgCombobox.js +8 -3
- package/dist/inputs/SgRadioGroup.d.ts +37 -0
- package/dist/inputs/SgRadioGroup.d.ts.map +1 -0
- package/dist/inputs/SgRadioGroup.js +139 -0
- package/dist/inputs/SgRating.d.ts +55 -0
- package/dist/inputs/SgRating.d.ts.map +1 -0
- package/dist/inputs/SgRating.js +135 -0
- package/dist/inputs/SgSlider.d.ts +20 -0
- package/dist/inputs/SgSlider.d.ts.map +1 -0
- package/dist/inputs/SgSlider.js +40 -0
- package/dist/inputs/SgStepperInput.d.ts +22 -0
- package/dist/inputs/SgStepperInput.d.ts.map +1 -0
- package/dist/inputs/SgStepperInput.js +51 -0
- package/dist/inputs/SgTextEditor.d.ts +1 -0
- package/dist/inputs/SgTextEditor.d.ts.map +1 -1
- package/dist/inputs/SgTextEditor.js +19 -3
- package/dist/layout/SgAccordion.d.ts +39 -0
- package/dist/layout/SgAccordion.d.ts.map +1 -0
- package/dist/layout/SgAccordion.js +116 -0
- package/dist/layout/SgBreadcrumb.d.ts +33 -0
- package/dist/layout/SgBreadcrumb.d.ts.map +1 -0
- package/dist/layout/SgBreadcrumb.js +121 -0
- package/dist/layout/SgCarousel.d.ts +43 -0
- package/dist/layout/SgCarousel.d.ts.map +1 -0
- package/dist/layout/SgCarousel.js +166 -0
- package/dist/layout/SgDockLayout.d.ts +14 -0
- package/dist/layout/SgDockLayout.d.ts.map +1 -1
- package/dist/layout/SgDockLayout.js +145 -13
- package/dist/layout/SgDockScreen.d.ts +15 -0
- package/dist/layout/SgDockScreen.d.ts.map +1 -0
- package/dist/layout/SgDockScreen.js +13 -0
- package/dist/layout/SgDockZone.d.ts.map +1 -1
- package/dist/layout/SgDockZone.js +36 -2
- package/dist/layout/SgExpandablePanel.d.ts +50 -0
- package/dist/layout/SgExpandablePanel.d.ts.map +1 -0
- package/dist/layout/SgExpandablePanel.js +302 -0
- package/dist/layout/SgMainPanel.d.ts.map +1 -1
- package/dist/layout/SgMainPanel.js +36 -14
- package/dist/layout/SgMenu.d.ts +91 -0
- package/dist/layout/SgMenu.d.ts.map +1 -0
- package/dist/layout/SgMenu.js +939 -0
- package/dist/layout/SgPageControl.d.ts +49 -0
- package/dist/layout/SgPageControl.d.ts.map +1 -0
- package/dist/layout/SgPageControl.js +152 -0
- package/dist/layout/SgPanel.d.ts.map +1 -1
- package/dist/layout/SgPanel.js +10 -1
- package/dist/layout/SgScreen.d.ts +2 -0
- package/dist/layout/SgScreen.d.ts.map +1 -1
- package/dist/layout/SgScreen.js +4 -2
- package/dist/layout/SgToolBar.d.ts +9 -3
- package/dist/layout/SgToolBar.d.ts.map +1 -1
- package/dist/layout/SgToolBar.js +461 -55
- package/dist/menus/SgDockMenu.d.ts +62 -0
- package/dist/menus/SgDockMenu.d.ts.map +1 -0
- package/dist/menus/SgDockMenu.js +480 -0
- package/dist/others/SgPlayground.js +72 -72
- package/package.json +72 -63
- package/dist/gadgets/flip-digit/SgFlipDigit.d.ts +0 -23
- package/dist/gadgets/flip-digit/SgFlipDigit.d.ts.map +0 -1
- package/dist/gadgets/flip-digit/SgFlipDigit.js +0 -118
- package/dist/gadgets/flip-digit/index.d.ts.map +0 -1
- /package/dist/{gadgets → digits}/flip-digit/index.d.ts +0 -0
- /package/dist/{gadgets → digits}/flip-digit/index.js +0 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { ChevronLeft, ChevronRight, ChevronUp, ChevronDown } from "lucide-react";
|
|
5
|
+
export function SgCarousel(props) {
|
|
6
|
+
const { id, items, numVisible = 1, numScroll = 1, orientation = "horizontal", circular = true, autoPlay = false, autoPlayInterval = 3000, showNavigators = true, showIndicators = true, className = "", itemClassName = "", width = "100%", height, gap = 16, onIndexChange, customNavigators } = props;
|
|
7
|
+
const [activeIndex, setActiveIndex] = React.useState(0);
|
|
8
|
+
const [isHovered, setIsHovered] = React.useState(false);
|
|
9
|
+
const timerRef = React.useRef(null);
|
|
10
|
+
const containerRef = React.useRef(null);
|
|
11
|
+
const lastNotifiedIndexRef = React.useRef(activeIndex);
|
|
12
|
+
const [viewportSize, setViewportSize] = React.useState({ width: 0, height: 0 });
|
|
13
|
+
const safeNumVisible = Math.max(1, numVisible);
|
|
14
|
+
const safeNumScroll = Math.max(1, numScroll);
|
|
15
|
+
const itemCount = items.length;
|
|
16
|
+
const maxStartIndex = Math.max(itemCount - safeNumVisible, 0);
|
|
17
|
+
const totalPages = itemCount === 0 ? 0 : Math.floor(maxStartIndex / safeNumScroll) + 1;
|
|
18
|
+
const isHorizontal = orientation === "horizontal";
|
|
19
|
+
// Auto play logic
|
|
20
|
+
React.useEffect(() => {
|
|
21
|
+
if (autoPlay && !isHovered) {
|
|
22
|
+
timerRef.current = setInterval(() => {
|
|
23
|
+
handleNext();
|
|
24
|
+
}, autoPlayInterval);
|
|
25
|
+
}
|
|
26
|
+
return () => {
|
|
27
|
+
if (timerRef.current) {
|
|
28
|
+
clearInterval(timerRef.current);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}, [autoPlay, autoPlayInterval, isHovered, activeIndex]);
|
|
32
|
+
// Keep active index within bounds when props/items change.
|
|
33
|
+
React.useEffect(() => {
|
|
34
|
+
setActiveIndex((prev) => Math.min(Math.max(prev, 0), maxStartIndex));
|
|
35
|
+
}, [maxStartIndex]);
|
|
36
|
+
// Notify parent after the state is committed, avoiding setState during render warnings.
|
|
37
|
+
React.useEffect(() => {
|
|
38
|
+
if (lastNotifiedIndexRef.current === activeIndex)
|
|
39
|
+
return;
|
|
40
|
+
lastNotifiedIndexRef.current = activeIndex;
|
|
41
|
+
onIndexChange?.(activeIndex);
|
|
42
|
+
}, [activeIndex, onIndexChange]);
|
|
43
|
+
React.useEffect(() => {
|
|
44
|
+
const element = containerRef.current;
|
|
45
|
+
if (!element)
|
|
46
|
+
return;
|
|
47
|
+
const updateSize = () => {
|
|
48
|
+
const rect = element.getBoundingClientRect();
|
|
49
|
+
setViewportSize({ width: rect.width, height: rect.height });
|
|
50
|
+
};
|
|
51
|
+
updateSize();
|
|
52
|
+
if (typeof ResizeObserver === "undefined") {
|
|
53
|
+
window.addEventListener("resize", updateSize);
|
|
54
|
+
return () => {
|
|
55
|
+
window.removeEventListener("resize", updateSize);
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const observer = new ResizeObserver(updateSize);
|
|
59
|
+
observer.observe(element);
|
|
60
|
+
return () => {
|
|
61
|
+
observer.disconnect();
|
|
62
|
+
};
|
|
63
|
+
}, []);
|
|
64
|
+
const handleNext = () => {
|
|
65
|
+
setActiveIndex((prev) => {
|
|
66
|
+
if (itemCount === 0)
|
|
67
|
+
return 0;
|
|
68
|
+
const next = prev + safeNumScroll;
|
|
69
|
+
if (next > maxStartIndex) {
|
|
70
|
+
return circular ? 0 : maxStartIndex;
|
|
71
|
+
}
|
|
72
|
+
return next;
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
const handlePrev = () => {
|
|
76
|
+
setActiveIndex((prev) => {
|
|
77
|
+
if (itemCount === 0)
|
|
78
|
+
return 0;
|
|
79
|
+
const next = prev - safeNumScroll;
|
|
80
|
+
if (next < 0) {
|
|
81
|
+
return circular ? maxStartIndex : 0;
|
|
82
|
+
}
|
|
83
|
+
return next;
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
const handleIndicatorClick = (index) => {
|
|
87
|
+
const next = Math.min(index * safeNumScroll, maxStartIndex);
|
|
88
|
+
setActiveIndex(next);
|
|
89
|
+
};
|
|
90
|
+
const canGoPrev = circular ? maxStartIndex > 0 : activeIndex > 0;
|
|
91
|
+
const canGoNext = circular ? maxStartIndex > 0 : activeIndex < maxStartIndex;
|
|
92
|
+
const itemPercent = 100 / safeNumVisible;
|
|
93
|
+
const gapPerItem = (gap * (safeNumVisible - 1)) / safeNumVisible;
|
|
94
|
+
const itemExtentCss = `calc(${itemPercent}% - ${gapPerItem}px)`;
|
|
95
|
+
const getTransformValue = () => {
|
|
96
|
+
const viewportExtent = isHorizontal ? viewportSize.width : viewportSize.height;
|
|
97
|
+
if (viewportExtent > 0) {
|
|
98
|
+
const itemExtent = Math.max(0, (viewportExtent - gap * (safeNumVisible - 1)) / safeNumVisible);
|
|
99
|
+
const step = itemExtent + gap;
|
|
100
|
+
const offsetPx = activeIndex * step;
|
|
101
|
+
return isHorizontal
|
|
102
|
+
? `translate3d(-${offsetPx}px, 0, 0)`
|
|
103
|
+
: `translate3d(0, -${offsetPx}px, 0)`;
|
|
104
|
+
}
|
|
105
|
+
// Fallback before first measure.
|
|
106
|
+
const itemPercent = 100 / safeNumVisible;
|
|
107
|
+
const fallbackGap = (activeIndex * gap) / safeNumVisible;
|
|
108
|
+
const fallbackOffset = activeIndex * itemPercent;
|
|
109
|
+
return isHorizontal
|
|
110
|
+
? `translateX(calc(-${fallbackOffset}% - ${fallbackGap}px))`
|
|
111
|
+
: `translateY(calc(-${fallbackOffset}% - ${fallbackGap}px))`;
|
|
112
|
+
};
|
|
113
|
+
const NavButton = ({ direction, onClick, disabled }) => {
|
|
114
|
+
const isPrev = direction === "prev";
|
|
115
|
+
const Icon = isHorizontal
|
|
116
|
+
? isPrev ? ChevronLeft : ChevronRight
|
|
117
|
+
: isPrev ? ChevronUp : ChevronDown;
|
|
118
|
+
const customButton = isPrev ? customNavigators?.prev : customNavigators?.next;
|
|
119
|
+
if (customButton) {
|
|
120
|
+
return (_jsx("button", { type: "button", onClick: onClick, disabled: disabled, className: `
|
|
121
|
+
absolute z-10 flex items-center justify-center transition-all
|
|
122
|
+
${disabled ? "opacity-30 cursor-not-allowed" : "hover:opacity-100 cursor-pointer"}
|
|
123
|
+
${isHorizontal
|
|
124
|
+
? `top-1/2 -translate-y-1/2 ${isPrev ? "left-2" : "right-2"}`
|
|
125
|
+
: `left-1/2 -translate-x-1/2 ${isPrev ? "-top-5" : "-bottom-5"}`}
|
|
126
|
+
`, children: customButton }));
|
|
127
|
+
}
|
|
128
|
+
return (_jsx("button", { type: "button", onClick: onClick, disabled: disabled, className: `
|
|
129
|
+
absolute z-10 flex h-10 w-10 items-center justify-center rounded-full
|
|
130
|
+
bg-white/90 shadow-lg opacity-70 transition-all
|
|
131
|
+
${disabled ? "opacity-30 cursor-not-allowed" : "hover:opacity-100 hover:bg-white cursor-pointer"}
|
|
132
|
+
${isHorizontal
|
|
133
|
+
? `top-1/2 -translate-y-1/2 ${isPrev ? "left-2" : "right-2"}`
|
|
134
|
+
: `left-1/2 -translate-x-1/2 ${isPrev ? "-top-5" : "-bottom-5"}`}
|
|
135
|
+
`, children: _jsx(Icon, { className: "h-6 w-6 text-foreground" }) }));
|
|
136
|
+
};
|
|
137
|
+
return (_jsxs("div", { id: id, className: `relative ${className}`, style: { width, height }, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: [showNavigators && (_jsxs(_Fragment, { children: [_jsx(NavButton, { direction: "prev", onClick: handlePrev, disabled: !canGoPrev }), _jsx(NavButton, { direction: "next", onClick: handleNext, disabled: !canGoNext })] })), _jsx("div", { ref: containerRef, className: "h-full w-full overflow-hidden rounded-lg", children: _jsx("div", { className: `
|
|
138
|
+
flex transition-transform duration-500 ease-in-out
|
|
139
|
+
${isHorizontal ? "flex-row" : "flex-col"}
|
|
140
|
+
`, style: {
|
|
141
|
+
transform: getTransformValue(),
|
|
142
|
+
gap: `${gap}px`,
|
|
143
|
+
height: isHorizontal ? undefined : "100%"
|
|
144
|
+
}, children: items.map((item, index) => (_jsx("div", { className: `
|
|
145
|
+
flex-shrink-0
|
|
146
|
+
${itemClassName}
|
|
147
|
+
`, style: {
|
|
148
|
+
width: isHorizontal ? itemExtentCss : "100%",
|
|
149
|
+
height: isHorizontal ? "100%" : itemExtentCss
|
|
150
|
+
}, children: item }, index))) }) }), showIndicators && (_jsx("div", { className: `
|
|
151
|
+
absolute flex gap-2 z-10
|
|
152
|
+
${isHorizontal
|
|
153
|
+
? "bottom-4 left-1/2 -translate-x-1/2 flex-row"
|
|
154
|
+
: "-right-6 top-1/2 -translate-y-1/2 flex-col"}
|
|
155
|
+
`, children: Array.from({ length: totalPages }).map((_, index) => {
|
|
156
|
+
const isActive = Math.floor(activeIndex / safeNumScroll) === index;
|
|
157
|
+
return (_jsx("button", { type: "button", onClick: () => handleIndicatorClick(index), className: `
|
|
158
|
+
transition-all
|
|
159
|
+
${isHorizontal ? "h-2" : "w-2"}
|
|
160
|
+
${isActive
|
|
161
|
+
? `${isHorizontal ? "w-8" : "h-8"} bg-primary`
|
|
162
|
+
: `${isHorizontal ? "w-2" : "h-2"} bg-white/60 hover:bg-white/80`}
|
|
163
|
+
rounded-full
|
|
164
|
+
`, "aria-label": `Go to slide ${index + 1}` }, index));
|
|
165
|
+
}) }))] }));
|
|
166
|
+
}
|
|
@@ -17,13 +17,27 @@ export type SgDockLayoutProps = {
|
|
|
17
17
|
children: React.ReactNode;
|
|
18
18
|
defaultState?: SgDockLayoutState;
|
|
19
19
|
};
|
|
20
|
+
export type SgDockDropIndicator = {
|
|
21
|
+
zone: SgDockZoneId;
|
|
22
|
+
index: number;
|
|
23
|
+
};
|
|
20
24
|
type DockContextValue = {
|
|
21
25
|
layoutId: string;
|
|
22
26
|
getZoneElement: (zone: SgDockZoneId) => HTMLDivElement | null;
|
|
23
27
|
registerZone: (zone: SgDockZoneId, el: HTMLDivElement | null) => void;
|
|
24
28
|
getZoneAtPoint: (x: number, y: number) => SgDockZoneId | null;
|
|
29
|
+
getDropPlacementAtPoint: (x: number, y: number, draggingToolbarId: string) => SgDockDropIndicator | null;
|
|
30
|
+
getZoneToolbarCount: (zone: SgDockZoneId) => number;
|
|
31
|
+
isDropPreviewActive: boolean;
|
|
32
|
+
setDropPreviewActive: (next: boolean) => void;
|
|
33
|
+
draggingToolbarId: string | null;
|
|
34
|
+
setDraggingToolbarId: (toolbarId: string | null) => void;
|
|
35
|
+
dropIndicator: SgDockDropIndicator | null;
|
|
36
|
+
setDropIndicator: (next: SgDockDropIndicator | null) => void;
|
|
25
37
|
getToolbarZone: (id: string) => SgDockZoneId | null;
|
|
38
|
+
getToolbarOrder: (id: string) => number | undefined;
|
|
26
39
|
moveToolbar: (id: string, zone: SgDockZoneId) => void;
|
|
40
|
+
placeToolbar: (id: string, zone: SgDockZoneId, index: number) => void;
|
|
27
41
|
ensureToolbar: (id: string, state: Partial<SgDockToolbarState>) => void;
|
|
28
42
|
getToolbarCollapsed: (id: string) => boolean | undefined;
|
|
29
43
|
setToolbarCollapsed: (id: string, next: boolean) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SgDockLayout.d.ts","sourceRoot":"","sources":["../../src/layout/SgDockLayout.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAExE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,YAAY,CAAC,EAAE,iBAAiB,CAAC;CAClC,CAAC;AAIF,KAAK,gBAAgB,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,cAAc,GAAG,IAAI,CAAC;IAC9D,YAAY,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IACtE,cAAc,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,YAAY,GAAG,IAAI,CAAC;IAC9D,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,YAAY,GAAG,IAAI,CAAC;IACpD,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACtD,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,kBAAkB,CAAC,KAAK,IAAI,CAAC;IACxE,mBAAmB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAAC;IACzD,mBAAmB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1D,CAAC;AAIF,wBAAgB,eAAe,4BAE9B;AAID,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,iBAAiB,CAAC,
|
|
1
|
+
{"version":3,"file":"SgDockLayout.d.ts","sourceRoot":"","sources":["../../src/layout/SgDockLayout.tsx"],"names":[],"mappings":"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"}
|
|
@@ -9,11 +9,26 @@ export function useSgDockLayout() {
|
|
|
9
9
|
const EMPTY_STATE = { version: 1, toolbars: {} };
|
|
10
10
|
export function SgDockLayout(props) {
|
|
11
11
|
const { id, className, children, defaultState } = props;
|
|
12
|
-
const { value: persisted, setValue } = useSgPersistentState({
|
|
12
|
+
const { value: persisted, setValue, hydrated } = useSgPersistentState({
|
|
13
13
|
baseKey: `dock-layout:${id}`,
|
|
14
14
|
defaultValue: defaultState ?? EMPTY_STATE
|
|
15
15
|
});
|
|
16
|
+
const [isDropPreviewActive, setIsDropPreviewActive] = React.useState(false);
|
|
17
|
+
const [draggingToolbarId, setDraggingToolbarIdState] = React.useState(null);
|
|
18
|
+
const [dropIndicator, setDropIndicator] = React.useState(null);
|
|
16
19
|
const zonesRef = React.useRef(new Map());
|
|
20
|
+
const getSortedZoneToolbarIds = React.useCallback((state, zone, excludingId) => {
|
|
21
|
+
return Object.values(state)
|
|
22
|
+
.filter((tb) => tb.zone === zone && tb.id !== excludingId)
|
|
23
|
+
.sort((a, b) => {
|
|
24
|
+
const orderA = a.order ?? 0;
|
|
25
|
+
const orderB = b.order ?? 0;
|
|
26
|
+
if (orderA !== orderB)
|
|
27
|
+
return orderA - orderB;
|
|
28
|
+
return a.id.localeCompare(b.id);
|
|
29
|
+
})
|
|
30
|
+
.map((tb) => tb.id);
|
|
31
|
+
}, []);
|
|
17
32
|
const registerZone = React.useCallback((zone, el) => {
|
|
18
33
|
if (!el) {
|
|
19
34
|
zonesRef.current.delete(zone);
|
|
@@ -33,43 +48,150 @@ export function SgDockLayout(props) {
|
|
|
33
48
|
}
|
|
34
49
|
return null;
|
|
35
50
|
}, []);
|
|
51
|
+
const getDropPlacementAtPoint = React.useCallback((x, y, draggingToolbarId) => {
|
|
52
|
+
const zone = getZoneAtPoint(x, y);
|
|
53
|
+
if (!zone)
|
|
54
|
+
return null;
|
|
55
|
+
const zoneEl = zonesRef.current.get(zone);
|
|
56
|
+
if (!zoneEl)
|
|
57
|
+
return null;
|
|
58
|
+
const axisHorizontal = zone === "top" || zone === "bottom";
|
|
59
|
+
const toolbarEls = Array.from(zoneEl.querySelectorAll("[data-sg-toolbar-root='true'][data-sg-toolbar-id]"))
|
|
60
|
+
.filter((el) => el.dataset.sgToolbarId !== draggingToolbarId)
|
|
61
|
+
.sort((a, b) => {
|
|
62
|
+
const rectA = a.getBoundingClientRect();
|
|
63
|
+
const rectB = b.getBoundingClientRect();
|
|
64
|
+
if (axisHorizontal) {
|
|
65
|
+
if (rectA.top !== rectB.top)
|
|
66
|
+
return rectA.top - rectB.top;
|
|
67
|
+
return rectA.left - rectB.left;
|
|
68
|
+
}
|
|
69
|
+
if (rectA.left !== rectB.left)
|
|
70
|
+
return rectA.left - rectB.left;
|
|
71
|
+
return rectA.top - rectB.top;
|
|
72
|
+
});
|
|
73
|
+
const cursorMajor = axisHorizontal ? x : y;
|
|
74
|
+
let index = toolbarEls.length;
|
|
75
|
+
for (let i = 0; i < toolbarEls.length; i += 1) {
|
|
76
|
+
const toolbarEl = toolbarEls[i];
|
|
77
|
+
if (!toolbarEl)
|
|
78
|
+
continue;
|
|
79
|
+
const rect = toolbarEl.getBoundingClientRect();
|
|
80
|
+
const majorCenter = axisHorizontal ? rect.left + rect.width / 2 : rect.top + rect.height / 2;
|
|
81
|
+
if (cursorMajor < majorCenter) {
|
|
82
|
+
index = i;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return { zone, index };
|
|
87
|
+
}, [getZoneAtPoint]);
|
|
88
|
+
const setDropPreviewActive = React.useCallback((next) => {
|
|
89
|
+
setIsDropPreviewActive((prev) => (prev === next ? prev : next));
|
|
90
|
+
if (!next) {
|
|
91
|
+
setDropIndicator(null);
|
|
92
|
+
setDraggingToolbarIdState(null);
|
|
93
|
+
}
|
|
94
|
+
}, []);
|
|
95
|
+
const setDraggingToolbarId = React.useCallback((toolbarId) => {
|
|
96
|
+
setDraggingToolbarIdState((prev) => (prev === toolbarId ? prev : toolbarId));
|
|
97
|
+
}, []);
|
|
98
|
+
React.useEffect(() => {
|
|
99
|
+
if (!isDropPreviewActive)
|
|
100
|
+
return;
|
|
101
|
+
const clearPreview = () => setDropPreviewActive(false);
|
|
102
|
+
const handleVisibilityChange = () => {
|
|
103
|
+
if (document.hidden)
|
|
104
|
+
clearPreview();
|
|
105
|
+
};
|
|
106
|
+
window.addEventListener("pointerup", clearPreview);
|
|
107
|
+
window.addEventListener("pointercancel", clearPreview);
|
|
108
|
+
window.addEventListener("blur", clearPreview);
|
|
109
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
110
|
+
return () => {
|
|
111
|
+
window.removeEventListener("pointerup", clearPreview);
|
|
112
|
+
window.removeEventListener("pointercancel", clearPreview);
|
|
113
|
+
window.removeEventListener("blur", clearPreview);
|
|
114
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
115
|
+
};
|
|
116
|
+
}, [isDropPreviewActive, setDropPreviewActive]);
|
|
36
117
|
const getToolbarZone = React.useCallback((toolbarId) => persisted.toolbars[toolbarId]?.zone ?? null, [persisted]);
|
|
118
|
+
const getToolbarOrder = React.useCallback((toolbarId) => {
|
|
119
|
+
const toolbar = persisted.toolbars[toolbarId];
|
|
120
|
+
if (!toolbar)
|
|
121
|
+
return undefined;
|
|
122
|
+
if (draggingToolbarId && toolbarId === draggingToolbarId)
|
|
123
|
+
return undefined;
|
|
124
|
+
const ids = getSortedZoneToolbarIds(persisted.toolbars, toolbar.zone, draggingToolbarId ?? undefined);
|
|
125
|
+
const index = ids.indexOf(toolbarId);
|
|
126
|
+
return index === -1 ? undefined : index;
|
|
127
|
+
}, [draggingToolbarId, getSortedZoneToolbarIds, persisted]);
|
|
128
|
+
const getZoneToolbarCount = React.useCallback((zone) => {
|
|
129
|
+
const zoneEl = zonesRef.current.get(zone);
|
|
130
|
+
if (!zoneEl)
|
|
131
|
+
return 0;
|
|
132
|
+
return Array.from(zoneEl.querySelectorAll("[data-sg-toolbar-root='true'][data-sg-toolbar-id]")).filter((el) => el.dataset.sgToolbarId !== draggingToolbarId).length;
|
|
133
|
+
}, [draggingToolbarId]);
|
|
37
134
|
const ensureToolbar = React.useCallback((toolbarId, state) => {
|
|
135
|
+
if (!hydrated)
|
|
136
|
+
return;
|
|
38
137
|
setValue((prev) => {
|
|
39
138
|
if (prev.toolbars[toolbarId])
|
|
40
139
|
return prev;
|
|
140
|
+
const targetZone = state.zone ?? "free";
|
|
141
|
+
const zoneIds = getSortedZoneToolbarIds(prev.toolbars, targetZone);
|
|
41
142
|
return {
|
|
42
143
|
...prev,
|
|
43
144
|
toolbars: {
|
|
44
145
|
...prev.toolbars,
|
|
45
146
|
[toolbarId]: {
|
|
46
147
|
id: toolbarId,
|
|
47
|
-
zone:
|
|
148
|
+
zone: targetZone,
|
|
48
149
|
collapsed: state.collapsed ?? false,
|
|
49
150
|
orientation: state.orientation,
|
|
50
|
-
order: state.order ??
|
|
151
|
+
order: state.order ?? zoneIds.length
|
|
51
152
|
}
|
|
52
153
|
}
|
|
53
154
|
};
|
|
54
155
|
});
|
|
55
|
-
}, [setValue]);
|
|
56
|
-
const
|
|
156
|
+
}, [getSortedZoneToolbarIds, hydrated, setValue]);
|
|
157
|
+
const placeToolbar = React.useCallback((toolbarId, zone, index) => {
|
|
57
158
|
setValue((prev) => {
|
|
58
159
|
const current = prev.toolbars[toolbarId];
|
|
59
160
|
if (!current)
|
|
60
161
|
return prev;
|
|
162
|
+
const sourceZone = current.zone;
|
|
163
|
+
const targetZone = zone;
|
|
164
|
+
const nextToolbars = {
|
|
165
|
+
...prev.toolbars
|
|
166
|
+
};
|
|
167
|
+
const targetIds = getSortedZoneToolbarIds(prev.toolbars, targetZone, toolbarId);
|
|
168
|
+
const safeIndex = Math.max(0, Math.min(index, targetIds.length));
|
|
169
|
+
targetIds.splice(safeIndex, 0, toolbarId);
|
|
170
|
+
targetIds.forEach((tbId, i) => {
|
|
171
|
+
const base = nextToolbars[tbId];
|
|
172
|
+
if (!base)
|
|
173
|
+
return;
|
|
174
|
+
nextToolbars[tbId] = { ...base, zone: targetZone, order: i };
|
|
175
|
+
});
|
|
176
|
+
if (sourceZone !== targetZone) {
|
|
177
|
+
const sourceIds = getSortedZoneToolbarIds(prev.toolbars, sourceZone, toolbarId);
|
|
178
|
+
sourceIds.forEach((tbId, i) => {
|
|
179
|
+
const base = nextToolbars[tbId];
|
|
180
|
+
if (!base)
|
|
181
|
+
return;
|
|
182
|
+
nextToolbars[tbId] = { ...base, order: i };
|
|
183
|
+
});
|
|
184
|
+
}
|
|
61
185
|
return {
|
|
62
186
|
...prev,
|
|
63
|
-
toolbars:
|
|
64
|
-
...prev.toolbars,
|
|
65
|
-
[toolbarId]: {
|
|
66
|
-
...current,
|
|
67
|
-
zone
|
|
68
|
-
}
|
|
69
|
-
}
|
|
187
|
+
toolbars: nextToolbars
|
|
70
188
|
};
|
|
71
189
|
});
|
|
72
|
-
}, [setValue]);
|
|
190
|
+
}, [getSortedZoneToolbarIds, setValue]);
|
|
191
|
+
const moveToolbar = React.useCallback((toolbarId, zone) => {
|
|
192
|
+
const currentZoneIds = getSortedZoneToolbarIds(persisted.toolbars, zone, toolbarId);
|
|
193
|
+
placeToolbar(toolbarId, zone, currentZoneIds.length);
|
|
194
|
+
}, [getSortedZoneToolbarIds, persisted.toolbars, placeToolbar]);
|
|
73
195
|
const getToolbarCollapsed = React.useCallback((toolbarId) => persisted.toolbars[toolbarId]?.collapsed, [persisted]);
|
|
74
196
|
const setToolbarCollapsed = React.useCallback((toolbarId, next) => {
|
|
75
197
|
setValue((prev) => {
|
|
@@ -90,8 +212,18 @@ export function SgDockLayout(props) {
|
|
|
90
212
|
getZoneElement,
|
|
91
213
|
registerZone,
|
|
92
214
|
getZoneAtPoint,
|
|
215
|
+
getDropPlacementAtPoint,
|
|
216
|
+
getZoneToolbarCount,
|
|
217
|
+
isDropPreviewActive,
|
|
218
|
+
setDropPreviewActive,
|
|
219
|
+
draggingToolbarId,
|
|
220
|
+
setDraggingToolbarId,
|
|
221
|
+
dropIndicator,
|
|
222
|
+
setDropIndicator,
|
|
93
223
|
getToolbarZone,
|
|
224
|
+
getToolbarOrder,
|
|
94
225
|
moveToolbar,
|
|
226
|
+
placeToolbar,
|
|
95
227
|
ensureToolbar,
|
|
96
228
|
getToolbarCollapsed,
|
|
97
229
|
setToolbarCollapsed
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type SgDockLayoutState } from "./SgDockLayout";
|
|
3
|
+
import { type SgScreenProps } from "./SgScreen";
|
|
4
|
+
export type SgDockScreenProps = Omit<SgScreenProps, "children" | "id"> & {
|
|
5
|
+
id: string;
|
|
6
|
+
screenId?: string;
|
|
7
|
+
defaultState?: SgDockLayoutState;
|
|
8
|
+
layoutClassName?: string;
|
|
9
|
+
children?: React.ReactNode;
|
|
10
|
+
};
|
|
11
|
+
export declare function SgDockScreen(props: Readonly<SgDockScreenProps>): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export declare namespace SgDockScreen {
|
|
13
|
+
var displayName: string;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=SgDockScreen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SgDockScreen.d.ts","sourceRoot":"","sources":["../../src/layout/SgDockScreen.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAM1D,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,aAAa,EAAE,UAAU,GAAG,IAAI,CAAC,GAAG;IACvE,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AAIF,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,iBAAiB,CAAC,2CAqB9D;yBArBe,YAAY"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { SgDockLayout } from "./SgDockLayout";
|
|
4
|
+
import { SgScreen } from "./SgScreen";
|
|
5
|
+
function cn(...parts) {
|
|
6
|
+
return parts.filter(Boolean).join(" ");
|
|
7
|
+
}
|
|
8
|
+
const AUTO_LAYOUT_ROOT_CLASS = "relative grid h-full w-full min-h-0 min-w-0 grid-cols-[12rem_1fr_12rem] grid-rows-[auto_1fr_auto]";
|
|
9
|
+
export function SgDockScreen(props) {
|
|
10
|
+
const { id, screenId, defaultState, layoutClassName, children, ...screenProps } = props;
|
|
11
|
+
return (_jsx(SgScreen, { ...screenProps, id: screenId, children: _jsx(SgDockLayout, { id: id, defaultState: defaultState, className: cn(AUTO_LAYOUT_ROOT_CLASS, layoutClassName), children: children }) }));
|
|
12
|
+
}
|
|
13
|
+
SgDockScreen.displayName = "SgDockScreen";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SgDockZone.d.ts","sourceRoot":"","sources":["../../src/layout/SgDockZone.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"SgDockZone.d.ts","sourceRoot":"","sources":["../../src/layout/SgDockZone.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGpE,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AAeF,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,CAAC,eAAe,CAAC,2CA0E1D;yBA1Ee,UAAU"}
|
|
@@ -1,20 +1,54 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { useSgDockLayout } from "./SgDockLayout";
|
|
5
|
+
import { t, useComponentsI18n } from "../i18n";
|
|
5
6
|
function cn(...parts) {
|
|
6
7
|
return parts.filter(Boolean).join(" ");
|
|
7
8
|
}
|
|
9
|
+
const POSITION_CLASS_PATTERN = /(?:^|\s)!?(?:(?:[a-z0-9-]+:)+)?(?:static|fixed|absolute|relative|sticky)(?=\s|$)/i;
|
|
10
|
+
const DEFAULT_ZONE_LAYOUT_CLASS = {
|
|
11
|
+
top: "col-span-3 row-start-1 items-start",
|
|
12
|
+
bottom: "col-span-3 row-start-3 items-end",
|
|
13
|
+
left: "col-start-1 row-start-2 items-start",
|
|
14
|
+
right: "col-start-3 row-start-2 items-start",
|
|
15
|
+
free: "col-start-2 row-start-2 items-center justify-center"
|
|
16
|
+
};
|
|
8
17
|
export function SgDockZone(props) {
|
|
9
18
|
const { zone, className, children } = props;
|
|
10
19
|
const dock = useSgDockLayout();
|
|
20
|
+
const i18n = useComponentsI18n();
|
|
11
21
|
const ref = React.useRef(null);
|
|
22
|
+
const showDropPreview = Boolean(dock?.isDropPreviewActive);
|
|
23
|
+
const dropIndicator = dock?.dropIndicator ?? null;
|
|
24
|
+
const zoneToolbarCount = dock?.getZoneToolbarCount(zone) ?? 0;
|
|
25
|
+
const showSlotPreview = showDropPreview && zone !== "free" && Boolean(dock?.draggingToolbarId);
|
|
26
|
+
const dropSlotIndexes = React.useMemo(() => {
|
|
27
|
+
if (!showSlotPreview)
|
|
28
|
+
return [];
|
|
29
|
+
const slotCount = Math.max(1, zoneToolbarCount + 1);
|
|
30
|
+
return Array.from({ length: slotCount }, (_, i) => i);
|
|
31
|
+
}, [showSlotPreview, zoneToolbarCount]);
|
|
32
|
+
const isHorizontalZone = zone === "top" || zone === "bottom";
|
|
33
|
+
const isVerticalZone = zone === "left" || zone === "right";
|
|
34
|
+
const hasExplicitPositionClass = POSITION_CLASS_PATTERN.test(className ?? "");
|
|
35
|
+
const hasCustomClass = Boolean(className?.trim());
|
|
36
|
+
const zoneDefaultLayoutClass = hasCustomClass ? null : DEFAULT_ZONE_LAYOUT_CLASS[zone];
|
|
12
37
|
React.useEffect(() => {
|
|
13
38
|
if (!dock)
|
|
14
39
|
return;
|
|
15
40
|
dock.registerZone(zone, ref.current);
|
|
16
41
|
return () => dock.registerZone(zone, null);
|
|
17
42
|
}, [dock, zone]);
|
|
18
|
-
return (
|
|
43
|
+
return (_jsxs("div", { ref: ref, "data-sg-dock-zone": zone, className: cn(hasExplicitPositionClass ? "flex min-h-0 min-w-0 gap-3 p-2" : "relative flex min-h-0 min-w-0 gap-3 p-2", isHorizontalZone
|
|
44
|
+
? "flex-row flex-wrap items-start content-start"
|
|
45
|
+
: isVerticalZone
|
|
46
|
+
? "flex-col flex-wrap items-start content-start"
|
|
47
|
+
: "flex-col items-center", showDropPreview ? "rounded-xl border-2 border-dashed border-border/70 bg-background/40 p-3 transition-colors duration-150" : null, showDropPreview && isHorizontalZone ? "min-h-16" : null, showDropPreview && isVerticalZone ? "min-w-24" : null, zoneDefaultLayoutClass, className), children: [children, dropSlotIndexes.map((slotIndex) => {
|
|
48
|
+
const isActiveSlot = dropIndicator?.zone === zone && dropIndicator.index === slotIndex;
|
|
49
|
+
return (_jsx("span", { className: cn("pointer-events-none relative z-[2] inline-flex shrink-0 items-center justify-center rounded border border-dashed text-[11px] font-semibold uppercase tracking-wide transition-colors duration-100", isHorizontalZone ? "h-10 w-16" : "h-8 w-20 self-center", isActiveSlot
|
|
50
|
+
? "border-primary/70 bg-background/95 text-primary"
|
|
51
|
+
: "border-border/60 bg-background/70 text-foreground/50"), style: { order: slotIndex * 2 - 1 }, "aria-hidden": "true", children: "><" }, `drop-slot-${zone}-${slotIndex}`));
|
|
52
|
+
}), showDropPreview && !showSlotPreview ? (_jsx("span", { className: "pointer-events-none absolute inset-0 z-[1] flex items-center justify-center text-xs font-semibold uppercase tracking-wide text-foreground/70", "aria-hidden": "true", children: t(i18n, "components.dock.dropHere") })) : null] }));
|
|
19
53
|
}
|
|
20
54
|
SgDockZone.displayName = "SgDockZone";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export type SgExpandablePanelDirection = "left" | "right" | "top" | "bottom";
|
|
3
|
+
export type SgExpandablePanelPlacement = "start" | "center" | "end";
|
|
4
|
+
export type SgExpandablePanelMode = "inline" | "overlay";
|
|
5
|
+
export type SgExpandablePanelElevation = "none" | "sm" | "md" | "lg";
|
|
6
|
+
export type SgExpandablePanelRounded = "none" | "md" | "lg" | "xl";
|
|
7
|
+
export type SgExpandablePanelRole = "dialog" | "region";
|
|
8
|
+
export type SgExpandablePanelSize = {
|
|
9
|
+
min?: number | string;
|
|
10
|
+
max?: number | string;
|
|
11
|
+
default?: number | string;
|
|
12
|
+
};
|
|
13
|
+
export type SgExpandablePanelAnimation = {
|
|
14
|
+
type?: "slide" | "fade" | "none";
|
|
15
|
+
durationMs?: number;
|
|
16
|
+
};
|
|
17
|
+
export type SgExpandablePanelProps = {
|
|
18
|
+
header?: React.ReactNode;
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
footer?: React.ReactNode;
|
|
21
|
+
handle?: React.ReactNode;
|
|
22
|
+
open?: boolean;
|
|
23
|
+
defaultOpen?: boolean;
|
|
24
|
+
onOpenChange?: (open: boolean) => void;
|
|
25
|
+
expandTo: SgExpandablePanelDirection;
|
|
26
|
+
placement?: SgExpandablePanelPlacement;
|
|
27
|
+
mode?: SgExpandablePanelMode;
|
|
28
|
+
size?: SgExpandablePanelSize;
|
|
29
|
+
resizable?: boolean;
|
|
30
|
+
onSizeChange?: (size: number | string) => void;
|
|
31
|
+
closeOnOutsideClick?: boolean;
|
|
32
|
+
closeOnEsc?: boolean;
|
|
33
|
+
trapFocus?: boolean;
|
|
34
|
+
showBackdrop?: boolean;
|
|
35
|
+
animation?: SgExpandablePanelAnimation;
|
|
36
|
+
elevation?: SgExpandablePanelElevation;
|
|
37
|
+
border?: boolean;
|
|
38
|
+
rounded?: SgExpandablePanelRounded;
|
|
39
|
+
ariaLabel?: string;
|
|
40
|
+
role?: SgExpandablePanelRole;
|
|
41
|
+
className?: string;
|
|
42
|
+
contentClassName?: string;
|
|
43
|
+
headerClassName?: string;
|
|
44
|
+
bodyClassName?: string;
|
|
45
|
+
footerClassName?: string;
|
|
46
|
+
overlayClassName?: string;
|
|
47
|
+
style?: React.CSSProperties;
|
|
48
|
+
};
|
|
49
|
+
export declare const SgExpandablePanel: React.ForwardRefExoticComponent<SgExpandablePanelProps & React.RefAttributes<HTMLDivElement>>;
|
|
50
|
+
//# sourceMappingURL=SgExpandablePanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SgExpandablePanel.d.ts","sourceRoot":"","sources":["../../src/layout/SgExpandablePanel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,MAAM,0BAA0B,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;AAC7E,MAAM,MAAM,0BAA0B,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AACpE,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,SAAS,CAAC;AACzD,MAAM,MAAM,0BAA0B,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACrE,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACnE,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAExD,MAAM,MAAM,qBAAqB,GAAG;IAClC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAEzB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAEvC,QAAQ,EAAE,0BAA0B,CAAC;IACrC,SAAS,CAAC,EAAE,0BAA0B,CAAC;IACvC,IAAI,CAAC,EAAE,qBAAqB,CAAC;IAE7B,IAAI,CAAC,EAAE,qBAAqB,CAAC;IAC7B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;IAE/C,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,SAAS,CAAC,EAAE,0BAA0B,CAAC;IAEvC,SAAS,CAAC,EAAE,0BAA0B,CAAC;IACvC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,wBAAwB,CAAC;IAEnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,qBAAqB,CAAC;IAE7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B,CAAC;AA6DF,eAAO,MAAM,iBAAiB,+FA4Y7B,CAAC"}
|