@papernote/ui 1.7.2 → 1.7.3
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/components/Breadcrumbs.d.ts +3 -0
- package/dist/components/Breadcrumbs.d.ts.map +1 -1
- package/dist/components/Calendar.d.ts +3 -1
- package/dist/components/Calendar.d.ts.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.esm.js +91 -52
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +89 -50
- package/dist/index.js.map +1 -1
- package/dist/styles.css +0 -4
- package/package.json +1 -1
- package/src/components/Breadcrumbs.tsx +66 -22
- package/src/components/Calendar.tsx +21 -10
package/dist/styles.css
CHANGED
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useRef } from 'react';
|
|
2
|
-
import { Link, useNavigate, useLocation } from 'react-router-dom';
|
|
2
|
+
import { Link, useNavigate, useLocation, useInRouterContext } from 'react-router-dom';
|
|
3
3
|
import { ChevronRight, Home } from 'lucide-react';
|
|
4
4
|
|
|
5
5
|
/** State passed during breadcrumb navigation */
|
|
@@ -14,6 +14,9 @@ export interface BreadcrumbNavigationState {
|
|
|
14
14
|
* Hook to detect breadcrumb navigation and trigger callbacks.
|
|
15
15
|
* Use this in host components to reset state when a breadcrumb is clicked.
|
|
16
16
|
*
|
|
17
|
+
* Note: This hook requires React Router context. If used outside a Router,
|
|
18
|
+
* it will be a no-op (the callback will never be called).
|
|
19
|
+
*
|
|
17
20
|
* @param onReset - Callback fired when breadcrumb navigation is detected
|
|
18
21
|
*
|
|
19
22
|
* @example
|
|
@@ -27,17 +30,22 @@ export interface BreadcrumbNavigationState {
|
|
|
27
30
|
* }
|
|
28
31
|
*/
|
|
29
32
|
export function useBreadcrumbReset(onReset: () => void): void {
|
|
30
|
-
const
|
|
33
|
+
const inRouter = useInRouterContext();
|
|
31
34
|
const lastResetRef = useRef<number | null>(null);
|
|
35
|
+
|
|
36
|
+
// Only use useLocation when inside Router context
|
|
37
|
+
const location = inRouter ? useLocation() : null;
|
|
32
38
|
|
|
33
39
|
useEffect(() => {
|
|
40
|
+
if (!location) return;
|
|
41
|
+
|
|
34
42
|
const state = location.state as BreadcrumbNavigationState | null;
|
|
35
43
|
|
|
36
44
|
if (state?.breadcrumbReset && state.breadcrumbReset !== lastResetRef.current) {
|
|
37
45
|
lastResetRef.current = state.breadcrumbReset;
|
|
38
46
|
onReset();
|
|
39
47
|
}
|
|
40
|
-
}, [location
|
|
48
|
+
}, [location?.state, onReset, location]);
|
|
41
49
|
}
|
|
42
50
|
|
|
43
51
|
export interface BreadcrumbItem {
|
|
@@ -94,8 +102,11 @@ export interface BreadcrumbsProps {
|
|
|
94
102
|
* }
|
|
95
103
|
*/
|
|
96
104
|
export default function Breadcrumbs({ items, showHome = true }: BreadcrumbsProps) {
|
|
97
|
-
const
|
|
98
|
-
|
|
105
|
+
const inRouter = useInRouterContext();
|
|
106
|
+
|
|
107
|
+
// Only use router hooks when inside Router context
|
|
108
|
+
const navigate = inRouter ? useNavigate() : null;
|
|
109
|
+
const location = inRouter ? useLocation() : null;
|
|
99
110
|
|
|
100
111
|
/**
|
|
101
112
|
* Handle breadcrumb click with same-route detection.
|
|
@@ -110,6 +121,9 @@ export default function Breadcrumbs({ items, showHome = true }: BreadcrumbsProps
|
|
|
110
121
|
// Always call onClick if provided (for custom actions)
|
|
111
122
|
onClick?.();
|
|
112
123
|
|
|
124
|
+
// If not in router context, let the browser handle navigation naturally
|
|
125
|
+
if (!navigate || !location) return;
|
|
126
|
+
|
|
113
127
|
// Check if we're navigating to the same base path
|
|
114
128
|
const targetPath = href.split('?')[0].split('#')[0];
|
|
115
129
|
const currentPath = location.pathname;
|
|
@@ -128,18 +142,51 @@ export default function Breadcrumbs({ items, showHome = true }: BreadcrumbsProps
|
|
|
128
142
|
// Different route - let the Link handle it normally
|
|
129
143
|
};
|
|
130
144
|
|
|
145
|
+
// Helper to render a link - uses Link when in router, <a> when not
|
|
146
|
+
const renderLink = (
|
|
147
|
+
href: string,
|
|
148
|
+
children: React.ReactNode,
|
|
149
|
+
className: string,
|
|
150
|
+
onClick?: (e: React.MouseEvent) => void,
|
|
151
|
+
ariaLabel?: string
|
|
152
|
+
) => {
|
|
153
|
+
if (inRouter) {
|
|
154
|
+
return (
|
|
155
|
+
<Link
|
|
156
|
+
to={href}
|
|
157
|
+
className={className}
|
|
158
|
+
onClick={onClick}
|
|
159
|
+
aria-label={ariaLabel}
|
|
160
|
+
>
|
|
161
|
+
{children}
|
|
162
|
+
</Link>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
return (
|
|
166
|
+
<a
|
|
167
|
+
href={href}
|
|
168
|
+
className={className}
|
|
169
|
+
onClick={(e) => {
|
|
170
|
+
onClick?.(e);
|
|
171
|
+
}}
|
|
172
|
+
aria-label={ariaLabel}
|
|
173
|
+
>
|
|
174
|
+
{children}
|
|
175
|
+
</a>
|
|
176
|
+
);
|
|
177
|
+
};
|
|
178
|
+
|
|
131
179
|
return (
|
|
132
180
|
<nav aria-label="Breadcrumb" className="flex items-center space-x-2 text-sm">
|
|
133
181
|
{showHome && (
|
|
134
182
|
<>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
className="
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
</Link>
|
|
183
|
+
{renderLink(
|
|
184
|
+
'/',
|
|
185
|
+
<Home className="h-4 w-4" />,
|
|
186
|
+
'text-ink-500 hover:text-ink-900 transition-colors',
|
|
187
|
+
(e) => handleBreadcrumbClick(e, '/'),
|
|
188
|
+
'Home'
|
|
189
|
+
)}
|
|
143
190
|
{items.length > 0 && <ChevronRight className="h-4 w-4 text-ink-400" />}
|
|
144
191
|
</>
|
|
145
192
|
)}
|
|
@@ -167,16 +214,13 @@ export default function Breadcrumbs({ items, showHome = true }: BreadcrumbsProps
|
|
|
167
214
|
);
|
|
168
215
|
}
|
|
169
216
|
|
|
170
|
-
// Has href - render as Link with same-route detection
|
|
217
|
+
// Has href - render as Link (or <a> if no router) with same-route detection
|
|
171
218
|
if (item.href) {
|
|
172
|
-
return (
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
>
|
|
178
|
-
{content}
|
|
179
|
-
</Link>
|
|
219
|
+
return renderLink(
|
|
220
|
+
item.href,
|
|
221
|
+
content,
|
|
222
|
+
'flex items-center gap-2 text-ink-500 hover:text-ink-900 hover:underline transition-colors',
|
|
223
|
+
(e) => handleBreadcrumbClick(e, item.href!, item.onClick)
|
|
180
224
|
);
|
|
181
225
|
}
|
|
182
226
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import React, { useState, useMemo } from 'react';
|
|
3
|
-
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
|
3
|
+
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react';
|
|
4
4
|
|
|
5
5
|
export interface CalendarEvent {
|
|
6
6
|
date: Date;
|
|
@@ -14,6 +14,8 @@ export interface CalendarProps {
|
|
|
14
14
|
value?: Date;
|
|
15
15
|
/** Callback when date is selected */
|
|
16
16
|
onChange?: (date: Date) => void;
|
|
17
|
+
/** Callback when displayed month changes (via navigation buttons or goToToday) */
|
|
18
|
+
onMonthChange?: (date: Date) => void;
|
|
17
19
|
/** Events to display on calendar */
|
|
18
20
|
events?: CalendarEvent[];
|
|
19
21
|
/** Callback when event marker is clicked */
|
|
@@ -41,6 +43,7 @@ export interface CalendarProps {
|
|
|
41
43
|
export default function Calendar({
|
|
42
44
|
value,
|
|
43
45
|
onChange,
|
|
46
|
+
onMonthChange,
|
|
44
47
|
events = [],
|
|
45
48
|
onEventClick,
|
|
46
49
|
rangeMode = false,
|
|
@@ -200,25 +203,35 @@ export default function Calendar({
|
|
|
200
203
|
|
|
201
204
|
// Navigate months
|
|
202
205
|
const previousMonth = () => {
|
|
203
|
-
|
|
206
|
+
const newMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1);
|
|
207
|
+
setCurrentMonth(newMonth);
|
|
208
|
+
onMonthChange?.(newMonth);
|
|
204
209
|
};
|
|
205
210
|
|
|
206
211
|
const nextMonth = () => {
|
|
207
|
-
|
|
212
|
+
const newMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1);
|
|
213
|
+
setCurrentMonth(newMonth);
|
|
214
|
+
onMonthChange?.(newMonth);
|
|
208
215
|
};
|
|
209
216
|
|
|
210
217
|
// Navigate years
|
|
211
218
|
const previousYear = () => {
|
|
212
|
-
|
|
219
|
+
const newMonth = new Date(currentMonth.getFullYear() - 1, currentMonth.getMonth(), 1);
|
|
220
|
+
setCurrentMonth(newMonth);
|
|
221
|
+
onMonthChange?.(newMonth);
|
|
213
222
|
};
|
|
214
223
|
|
|
215
224
|
const nextYear = () => {
|
|
216
|
-
|
|
225
|
+
const newMonth = new Date(currentMonth.getFullYear() + 1, currentMonth.getMonth(), 1);
|
|
226
|
+
setCurrentMonth(newMonth);
|
|
227
|
+
onMonthChange?.(newMonth);
|
|
217
228
|
};
|
|
218
229
|
|
|
219
230
|
// Go to today
|
|
220
231
|
const goToToday = () => {
|
|
221
|
-
|
|
232
|
+
const today = new Date();
|
|
233
|
+
setCurrentMonth(today);
|
|
234
|
+
onMonthChange?.(today);
|
|
222
235
|
};
|
|
223
236
|
|
|
224
237
|
// Day names
|
|
@@ -244,8 +257,7 @@ export default function Calendar({
|
|
|
244
257
|
className="p-1.5 hover:bg-paper-100 rounded transition-colors"
|
|
245
258
|
aria-label="Previous year"
|
|
246
259
|
>
|
|
247
|
-
<
|
|
248
|
-
<ChevronLeft className="h-4 w-4 text-ink-600 -ml-3" />
|
|
260
|
+
<ChevronsLeft className="h-4 w-4 text-ink-600" />
|
|
249
261
|
</button>
|
|
250
262
|
<button
|
|
251
263
|
onClick={previousMonth}
|
|
@@ -281,8 +293,7 @@ export default function Calendar({
|
|
|
281
293
|
className="p-1.5 hover:bg-paper-100 rounded transition-colors"
|
|
282
294
|
aria-label="Next year"
|
|
283
295
|
>
|
|
284
|
-
<
|
|
285
|
-
<ChevronRight className="h-4 w-4 text-ink-600 -ml-3" />
|
|
296
|
+
<ChevronsRight className="h-4 w-4 text-ink-600" />
|
|
286
297
|
</button>
|
|
287
298
|
</div>
|
|
288
299
|
</div>
|