icon-dock-nav 1.0.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.
- package/README.md +59 -0
- package/dist/DockNav.d.ts +72 -0
- package/dist/DockNav.d.ts.map +1 -0
- package/dist/DockNav.js +277 -0
- package/dist/DockNav.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# @security-scan-console/dock-nav
|
|
2
|
+
|
|
3
|
+
A macOS-style dock navigation component for React with smooth magnification effects.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @security-scan-console/dock-nav
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { DockNav, DockNavItem } from "@security-scan-console/dock-nav";
|
|
17
|
+
|
|
18
|
+
const HomeIcon = (props) => (
|
|
19
|
+
<svg {...props} viewBox="0 0 24 24" fill="currentColor">
|
|
20
|
+
<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" />
|
|
21
|
+
</svg>
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const items: DockNavItem[] = [
|
|
25
|
+
{ id: "home", label: "Home", icon: HomeIcon },
|
|
26
|
+
{ id: "settings", label: "Settings", icon: SettingsIcon },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
function App() {
|
|
30
|
+
return (
|
|
31
|
+
<DockNav
|
|
32
|
+
items={items}
|
|
33
|
+
position="top"
|
|
34
|
+
onNavigate={(id) => console.log("Navigated to:", id)}
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Props
|
|
41
|
+
|
|
42
|
+
| Prop | Type | Default | Description |
|
|
43
|
+
| --------------- | ---------------------- | -------- | --------------------------------------------------- |
|
|
44
|
+
| `items` | `DockNavItem[]` | required | Navigation items |
|
|
45
|
+
| `position` | `"top" \| "bottom"` | `"top"` | Dock position |
|
|
46
|
+
| `style` | `CSSProperties` | - | Custom styles |
|
|
47
|
+
| `onNavigate` | `(id: string) => void` | - | Navigation callback |
|
|
48
|
+
| `activeId` | `string` | - | Controlled active item |
|
|
49
|
+
| `observeScroll` | `boolean` | `false` | Auto-detect active section via IntersectionObserver |
|
|
50
|
+
| `baseSize` | `number` | `48` | Base icon size in pixels |
|
|
51
|
+
| `maxScale` | `number` | `1.4` | Max magnification multiplier |
|
|
52
|
+
|
|
53
|
+
## Features
|
|
54
|
+
|
|
55
|
+
- Smooth magnification effect on hover (like macOS dock)
|
|
56
|
+
- Keyboard navigation support
|
|
57
|
+
- Respects `prefers-reduced-motion`
|
|
58
|
+
- Optional scroll-based active detection
|
|
59
|
+
- Zero external CSS dependencies
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { CSSProperties, ReactNode } from "react";
|
|
2
|
+
export interface DockNavItem {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
icon: React.FC<React.SVGProps<SVGSVGElement>>;
|
|
6
|
+
}
|
|
7
|
+
export interface DockNavTheme {
|
|
8
|
+
/** Active button background (default: "linear-gradient(to bottom right, #3b82f6, #1d4ed8)") */
|
|
9
|
+
activeBackground?: string;
|
|
10
|
+
/** Active button text/icon color (default: "white") */
|
|
11
|
+
activeColor?: string;
|
|
12
|
+
/** Inactive button background (default: "linear-gradient(to bottom right, #374151, #1f2937)") */
|
|
13
|
+
inactiveBackground?: string;
|
|
14
|
+
/** Inactive button text/icon color (default: "#d1d5db") */
|
|
15
|
+
inactiveColor?: string;
|
|
16
|
+
/** Active indicator dot color (default: "#60a5fa") */
|
|
17
|
+
indicatorColor?: string;
|
|
18
|
+
/** Tooltip background color (default: "#111827") */
|
|
19
|
+
tooltipBackground?: string;
|
|
20
|
+
/** Tooltip text color (default: "white") */
|
|
21
|
+
tooltipColor?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface DockNavClassNames {
|
|
24
|
+
/** Class for the nav container */
|
|
25
|
+
nav?: string;
|
|
26
|
+
/** Class for the dock container */
|
|
27
|
+
dock?: string;
|
|
28
|
+
/** Class for each item wrapper */
|
|
29
|
+
item?: string;
|
|
30
|
+
/** Class for buttons */
|
|
31
|
+
button?: string;
|
|
32
|
+
/** Class for active button (added alongside button) */
|
|
33
|
+
buttonActive?: string;
|
|
34
|
+
/** Class for inactive button (added alongside button) */
|
|
35
|
+
buttonInactive?: string;
|
|
36
|
+
/** Class for tooltip */
|
|
37
|
+
tooltip?: string;
|
|
38
|
+
/** Class for active indicator dot */
|
|
39
|
+
indicator?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface DockNavProps {
|
|
42
|
+
items: DockNavItem[];
|
|
43
|
+
position?: "top" | "bottom";
|
|
44
|
+
style?: CSSProperties;
|
|
45
|
+
onNavigate?: (id: string) => void;
|
|
46
|
+
activeId?: string;
|
|
47
|
+
/** Enable scroll-based active detection via IntersectionObserver */
|
|
48
|
+
observeScroll?: boolean;
|
|
49
|
+
/** Base icon size in pixels (default: 48) */
|
|
50
|
+
baseSize?: number;
|
|
51
|
+
/** Max magnification multiplier (default: 1.4) */
|
|
52
|
+
maxScale?: number;
|
|
53
|
+
/** Gap between items in pixels (default: 12) */
|
|
54
|
+
gap?: number;
|
|
55
|
+
/** Magnification distance in pixels (default: 100) */
|
|
56
|
+
magnifyDistance?: number;
|
|
57
|
+
/** Animation duration in ms (default: 100) */
|
|
58
|
+
animationDuration?: number;
|
|
59
|
+
/** Theme customization (inline styles) */
|
|
60
|
+
theme?: DockNavTheme;
|
|
61
|
+
/** Custom tooltip renderer */
|
|
62
|
+
renderTooltip?: (item: DockNavItem, isVisible: boolean) => ReactNode;
|
|
63
|
+
/** Hide tooltips entirely */
|
|
64
|
+
hideTooltips?: boolean;
|
|
65
|
+
/** Class names for Tailwind/CSS styling (overrides theme when provided) */
|
|
66
|
+
classNames?: DockNavClassNames;
|
|
67
|
+
/** Use only classNames, skip all default inline styles */
|
|
68
|
+
unstyled?: boolean;
|
|
69
|
+
}
|
|
70
|
+
export declare const DockNav: React.FC<DockNavProps>;
|
|
71
|
+
export default DockNav;
|
|
72
|
+
//# sourceMappingURL=DockNav.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DockNav.d.ts","sourceRoot":"","sources":["../src/DockNav.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAMZ,aAAa,EACb,SAAS,EACV,MAAM,OAAO,CAAC;AAEf,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,YAAY;IAC3B,+FAA+F;IAC/F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iGAAiG;IACjG,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,kCAAkC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wBAAwB;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wBAAwB;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC5B,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,8CAA8C;IAC9C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,0CAA0C;IAC1C,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,8BAA8B;IAC9B,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,KAAK,SAAS,CAAC;IACrE,6BAA6B;IAC7B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAYD,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAgY1C,CAAC;AAEF,eAAe,OAAO,CAAC"}
|
package/dist/DockNav.js
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useCallback, useRef, useMemo, } from "react";
|
|
3
|
+
const defaultTheme = {
|
|
4
|
+
activeBackground: "linear-gradient(to bottom right, #3b82f6, #1d4ed8)",
|
|
5
|
+
activeColor: "white",
|
|
6
|
+
inactiveBackground: "linear-gradient(to bottom right, #374151, #1f2937)",
|
|
7
|
+
inactiveColor: "#d1d5db",
|
|
8
|
+
indicatorColor: "#60a5fa",
|
|
9
|
+
tooltipBackground: "#111827",
|
|
10
|
+
tooltipColor: "white",
|
|
11
|
+
};
|
|
12
|
+
export const DockNav = ({ items, position = "top", style, onNavigate, activeId, observeScroll = false, baseSize = 48, maxScale = 1.4, gap = 12, magnifyDistance = 100, animationDuration = 100, theme: userTheme, renderTooltip, hideTooltips = false, classNames = {}, unstyled = false, }) => {
|
|
13
|
+
const theme = { ...defaultTheme, ...userTheme };
|
|
14
|
+
const [mouseX, setMouseX] = useState(null);
|
|
15
|
+
const [isHoveringDock, setIsHoveringDock] = useState(false);
|
|
16
|
+
const [focusedId, setFocusedId] = useState(null);
|
|
17
|
+
const [currentActiveId, setCurrentActiveId] = useState(activeId || items[0]?.id || "");
|
|
18
|
+
const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);
|
|
19
|
+
const [buttonRects, setButtonRects] = useState(new Map());
|
|
20
|
+
const observerRef = useRef(null);
|
|
21
|
+
const dockRef = useRef(null);
|
|
22
|
+
const buttonRefs = useRef(new Map());
|
|
23
|
+
const setButtonRef = useCallback((index) => (el) => {
|
|
24
|
+
if (el) {
|
|
25
|
+
buttonRefs.current.set(index, el);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
buttonRefs.current.delete(index);
|
|
29
|
+
}
|
|
30
|
+
}, []);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
33
|
+
setPrefersReducedMotion(mediaQuery.matches);
|
|
34
|
+
const handler = (e) => setPrefersReducedMotion(e.matches);
|
|
35
|
+
mediaQuery.addEventListener("change", handler);
|
|
36
|
+
return () => mediaQuery.removeEventListener("change", handler);
|
|
37
|
+
}, []);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!observeScroll)
|
|
40
|
+
return;
|
|
41
|
+
const options = {
|
|
42
|
+
root: null,
|
|
43
|
+
rootMargin: "-20% 0px -60% 0px",
|
|
44
|
+
threshold: 0,
|
|
45
|
+
};
|
|
46
|
+
observerRef.current = new IntersectionObserver((entries) => {
|
|
47
|
+
entries.forEach((entry) => {
|
|
48
|
+
if (entry.isIntersecting) {
|
|
49
|
+
setCurrentActiveId(entry.target.id);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}, options);
|
|
53
|
+
items.forEach((item) => {
|
|
54
|
+
const element = document.getElementById(item.id);
|
|
55
|
+
if (element && observerRef.current) {
|
|
56
|
+
observerRef.current.observe(element);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
return () => {
|
|
60
|
+
observerRef.current?.disconnect();
|
|
61
|
+
};
|
|
62
|
+
}, [observeScroll, items]);
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (activeId) {
|
|
65
|
+
setCurrentActiveId(activeId);
|
|
66
|
+
}
|
|
67
|
+
}, [activeId]);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (isHoveringDock) {
|
|
70
|
+
const rects = new Map();
|
|
71
|
+
buttonRefs.current.forEach((button, index) => {
|
|
72
|
+
rects.set(index, button.getBoundingClientRect());
|
|
73
|
+
});
|
|
74
|
+
setButtonRects(rects);
|
|
75
|
+
}
|
|
76
|
+
}, [isHoveringDock, mouseX]);
|
|
77
|
+
const handleClick = useCallback((id) => {
|
|
78
|
+
if (observeScroll) {
|
|
79
|
+
const element = document.getElementById(id);
|
|
80
|
+
if (element) {
|
|
81
|
+
element.scrollIntoView({
|
|
82
|
+
behavior: prefersReducedMotion ? "auto" : "smooth",
|
|
83
|
+
block: "start",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
setCurrentActiveId(id);
|
|
88
|
+
onNavigate?.(id);
|
|
89
|
+
}, [onNavigate, prefersReducedMotion, observeScroll]);
|
|
90
|
+
const handleKeyDown = useCallback((e, id) => {
|
|
91
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
92
|
+
e.preventDefault();
|
|
93
|
+
handleClick(id);
|
|
94
|
+
}
|
|
95
|
+
}, [handleClick]);
|
|
96
|
+
const getSize = useCallback((index) => {
|
|
97
|
+
if (prefersReducedMotion)
|
|
98
|
+
return 1;
|
|
99
|
+
if (!isHoveringDock || mouseX === null)
|
|
100
|
+
return 1;
|
|
101
|
+
const rect = buttonRects.get(index);
|
|
102
|
+
if (!rect)
|
|
103
|
+
return 1;
|
|
104
|
+
const buttonCenterX = rect.left + rect.width / 2;
|
|
105
|
+
const distance = Math.abs(mouseX - buttonCenterX);
|
|
106
|
+
if (distance > magnifyDistance)
|
|
107
|
+
return 1;
|
|
108
|
+
return 1 + (maxScale - 1) * Math.pow(1 - distance / magnifyDistance, 2);
|
|
109
|
+
}, [
|
|
110
|
+
prefersReducedMotion,
|
|
111
|
+
isHoveringDock,
|
|
112
|
+
mouseX,
|
|
113
|
+
buttonRects,
|
|
114
|
+
maxScale,
|
|
115
|
+
magnifyDistance,
|
|
116
|
+
]);
|
|
117
|
+
const closestIndex = useMemo(() => {
|
|
118
|
+
if (!isHoveringDock || mouseX === null)
|
|
119
|
+
return null;
|
|
120
|
+
let closest = null;
|
|
121
|
+
let closestDistance = Infinity;
|
|
122
|
+
buttonRects.forEach((rect, index) => {
|
|
123
|
+
const buttonCenterX = rect.left + rect.width / 2;
|
|
124
|
+
const distance = Math.abs(mouseX - buttonCenterX);
|
|
125
|
+
if (distance < closestDistance && distance < 40) {
|
|
126
|
+
closestDistance = distance;
|
|
127
|
+
closest = index;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
return closest;
|
|
131
|
+
}, [isHoveringDock, mouseX, buttonRects]);
|
|
132
|
+
const showTooltip = (id, index) => {
|
|
133
|
+
if (hideTooltips)
|
|
134
|
+
return false;
|
|
135
|
+
const isClosest = closestIndex === index;
|
|
136
|
+
const isFocused = focusedId === id;
|
|
137
|
+
const isActive = currentActiveId === id;
|
|
138
|
+
return (isClosest || isFocused) && !isActive;
|
|
139
|
+
};
|
|
140
|
+
const transitionStyle = prefersReducedMotion
|
|
141
|
+
? "none"
|
|
142
|
+
: `transform ${animationDuration}ms ease-out`;
|
|
143
|
+
if (items.length === 0) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
const navStyle = unstyled
|
|
147
|
+
? style || {}
|
|
148
|
+
: {
|
|
149
|
+
display: "flex",
|
|
150
|
+
justifyContent: "center",
|
|
151
|
+
padding: "12px 0",
|
|
152
|
+
...(position === "bottom"
|
|
153
|
+
? {
|
|
154
|
+
position: "fixed",
|
|
155
|
+
bottom: 16,
|
|
156
|
+
left: "50%",
|
|
157
|
+
transform: "translateX(-50%)",
|
|
158
|
+
}
|
|
159
|
+
: { position: "sticky", top: 0, zIndex: 50 }),
|
|
160
|
+
...style,
|
|
161
|
+
};
|
|
162
|
+
const dockStyle = unstyled
|
|
163
|
+
? {}
|
|
164
|
+
: {
|
|
165
|
+
display: "flex",
|
|
166
|
+
alignItems: "flex-end",
|
|
167
|
+
gap,
|
|
168
|
+
padding: "12px 16px",
|
|
169
|
+
};
|
|
170
|
+
return (_jsx("nav", { style: navStyle, className: classNames.nav, role: "navigation", "aria-label": "Main navigation", children: _jsx("div", { ref: dockRef, style: dockStyle, className: classNames.dock, onMouseMove: (e) => setMouseX(e.clientX), onMouseEnter: () => setIsHoveringDock(true), onMouseLeave: () => {
|
|
171
|
+
setIsHoveringDock(false);
|
|
172
|
+
setMouseX(null);
|
|
173
|
+
}, children: items.map((item, index) => {
|
|
174
|
+
const Icon = item.icon;
|
|
175
|
+
const isActive = currentActiveId === item.id;
|
|
176
|
+
const sizeMultiplier = getSize(index);
|
|
177
|
+
const translateY = (sizeMultiplier - 1) * baseSize * 0.5;
|
|
178
|
+
const isTooltipVisible = showTooltip(item.id, index);
|
|
179
|
+
const buttonStyle = unstyled
|
|
180
|
+
? {
|
|
181
|
+
width: baseSize,
|
|
182
|
+
height: baseSize,
|
|
183
|
+
transform: `scale(${sizeMultiplier}) translateY(-${translateY / sizeMultiplier}px)`,
|
|
184
|
+
transition: transitionStyle,
|
|
185
|
+
}
|
|
186
|
+
: {
|
|
187
|
+
position: "relative",
|
|
188
|
+
display: "flex",
|
|
189
|
+
alignItems: "center",
|
|
190
|
+
justifyContent: "center",
|
|
191
|
+
borderRadius: 12,
|
|
192
|
+
border: "none",
|
|
193
|
+
cursor: "pointer",
|
|
194
|
+
outline: "none",
|
|
195
|
+
background: isActive
|
|
196
|
+
? theme.activeBackground
|
|
197
|
+
: theme.inactiveBackground,
|
|
198
|
+
color: isActive ? theme.activeColor : theme.inactiveColor,
|
|
199
|
+
boxShadow: isActive
|
|
200
|
+
? "0 4px 6px -1px rgba(59, 130, 246, 0.3)"
|
|
201
|
+
: "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
|
|
202
|
+
width: baseSize,
|
|
203
|
+
height: baseSize,
|
|
204
|
+
transform: `scale(${sizeMultiplier}) translateY(-${translateY / sizeMultiplier}px)`,
|
|
205
|
+
transformOrigin: "bottom center",
|
|
206
|
+
transition: transitionStyle,
|
|
207
|
+
};
|
|
208
|
+
const tooltipOffset = baseSize * sizeMultiplier + 12;
|
|
209
|
+
const tooltipStyle = unstyled
|
|
210
|
+
? {
|
|
211
|
+
opacity: isTooltipVisible ? 1 : 0,
|
|
212
|
+
pointerEvents: isTooltipVisible ? "auto" : "none",
|
|
213
|
+
}
|
|
214
|
+
: {
|
|
215
|
+
position: "absolute",
|
|
216
|
+
bottom: baseSize,
|
|
217
|
+
transform: `translateY(-${tooltipOffset - baseSize}px)`,
|
|
218
|
+
padding: "8px 16px",
|
|
219
|
+
backgroundColor: theme.tooltipBackground,
|
|
220
|
+
color: theme.tooltipColor,
|
|
221
|
+
fontSize: 15,
|
|
222
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
223
|
+
fontWeight: 500,
|
|
224
|
+
borderRadius: 10,
|
|
225
|
+
whiteSpace: "nowrap",
|
|
226
|
+
zIndex: 50,
|
|
227
|
+
opacity: isTooltipVisible ? 1 : 0,
|
|
228
|
+
pointerEvents: isTooltipVisible ? "auto" : "none",
|
|
229
|
+
transition: prefersReducedMotion
|
|
230
|
+
? "none"
|
|
231
|
+
: `opacity 150ms, transform ${animationDuration}ms ease-out`,
|
|
232
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.3)",
|
|
233
|
+
};
|
|
234
|
+
const tooltipArrowStyle = unstyled
|
|
235
|
+
? {}
|
|
236
|
+
: {
|
|
237
|
+
position: "absolute",
|
|
238
|
+
left: "50%",
|
|
239
|
+
transform: "translateX(-50%)",
|
|
240
|
+
top: "100%",
|
|
241
|
+
width: 0,
|
|
242
|
+
height: 0,
|
|
243
|
+
borderLeft: "4px solid transparent",
|
|
244
|
+
borderRight: "4px solid transparent",
|
|
245
|
+
borderTop: `4px solid ${theme.tooltipBackground}`,
|
|
246
|
+
};
|
|
247
|
+
const itemStyle = unstyled
|
|
248
|
+
? {}
|
|
249
|
+
: {
|
|
250
|
+
position: "relative",
|
|
251
|
+
display: "flex",
|
|
252
|
+
flexDirection: "column",
|
|
253
|
+
alignItems: "center",
|
|
254
|
+
};
|
|
255
|
+
const indicatorStyle = unstyled
|
|
256
|
+
? {}
|
|
257
|
+
: {
|
|
258
|
+
position: "absolute",
|
|
259
|
+
bottom: -8,
|
|
260
|
+
width: 4,
|
|
261
|
+
height: 4,
|
|
262
|
+
backgroundColor: theme.indicatorColor,
|
|
263
|
+
borderRadius: "50%",
|
|
264
|
+
};
|
|
265
|
+
const buttonClassName = [
|
|
266
|
+
classNames.button,
|
|
267
|
+
isActive ? classNames.buttonActive : classNames.buttonInactive,
|
|
268
|
+
]
|
|
269
|
+
.filter(Boolean)
|
|
270
|
+
.join(" ");
|
|
271
|
+
return (_jsxs("div", { style: itemStyle, className: classNames.item, children: [renderTooltip ? (renderTooltip(item, isTooltipVisible)) : (_jsxs("div", { role: "tooltip", id: `tooltip-${item.id}`, style: tooltipStyle, className: classNames.tooltip, children: [item.label, !unstyled && _jsx("div", { style: tooltipArrowStyle })] })), _jsx("button", { ref: setButtonRef(index), type: "button", onClick: () => handleClick(item.id), onKeyDown: (e) => handleKeyDown(e, item.id), onFocus: () => setFocusedId(item.id), onBlur: () => setFocusedId(null), "aria-label": item.label, "aria-describedby": renderTooltip ? undefined : `tooltip-${item.id}`, "aria-current": isActive ? "page" : undefined, style: buttonStyle, className: buttonClassName || undefined, children: _jsx(Icon, { style: {
|
|
272
|
+
width: baseSize,
|
|
273
|
+
height: baseSize,
|
|
274
|
+
} }) }), isActive && (_jsx("div", { style: indicatorStyle, className: classNames.indicator }))] }, item.id));
|
|
275
|
+
}) }) }));
|
|
276
|
+
};
|
|
277
|
+
export default DockNav;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DockNav.js","sourceRoot":"","sources":["../src/DockNav.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,iBAAiB,GACvB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAuC,MAAM,cAAc,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "icon-dock-nav",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "macOS-style dock navigation component for React with smooth magnification effects",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"author": "klazapp",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"react",
|
|
12
|
+
"navigation",
|
|
13
|
+
"dock",
|
|
14
|
+
"macos",
|
|
15
|
+
"component",
|
|
16
|
+
"ui",
|
|
17
|
+
"magnification",
|
|
18
|
+
"navbar"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/klazapp/icon-dock-nav.git"
|
|
23
|
+
},
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/klazapp/icon-dock-nav/issues"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://github.com/klazapp/icon-dock-nav#readme",
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc",
|
|
30
|
+
"clean": "rm -rf dist",
|
|
31
|
+
"prepublishOnly": "npm run build"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"react": "^18.0.0",
|
|
35
|
+
"react-dom": "^18.0.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/react": "^18.2.0",
|
|
39
|
+
"@types/react-dom": "^18.2.0",
|
|
40
|
+
"typescript": "^5.3.0"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"README.md"
|
|
45
|
+
]
|
|
46
|
+
}
|