@papernote/ui 1.7.0 → 1.7.2

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/index.js CHANGED
@@ -7279,16 +7279,113 @@ function Hide({ children, above, below, only, className = '' }) {
7279
7279
  return (jsxRuntime.jsx("div", { className: `${visibilityClasses} ${className}`, children: children }));
7280
7280
  }
7281
7281
 
7282
+ /**
7283
+ * Hook to detect breadcrumb navigation and trigger callbacks.
7284
+ * Use this in host components to reset state when a breadcrumb is clicked.
7285
+ *
7286
+ * @param onReset - Callback fired when breadcrumb navigation is detected
7287
+ *
7288
+ * @example
7289
+ * function ProductsPage() {
7290
+ * const [viewMode, setViewMode] = useState<'list' | 'detail'>('list');
7291
+ *
7292
+ * // Automatically reset to list view when breadcrumb is clicked
7293
+ * useBreadcrumbReset(() => setViewMode('list'));
7294
+ *
7295
+ * // ... rest of component
7296
+ * }
7297
+ */
7298
+ function useBreadcrumbReset(onReset) {
7299
+ const location = reactRouterDom.useLocation();
7300
+ const lastResetRef = React.useRef(null);
7301
+ React.useEffect(() => {
7302
+ const state = location.state;
7303
+ if (state?.breadcrumbReset && state.breadcrumbReset !== lastResetRef.current) {
7304
+ lastResetRef.current = state.breadcrumbReset;
7305
+ onReset();
7306
+ }
7307
+ }, [location.state, onReset]);
7308
+ }
7309
+ /**
7310
+ * Breadcrumbs navigation component.
7311
+ *
7312
+ * When a breadcrumb with href is clicked:
7313
+ * - If navigating to a different route: standard navigation occurs
7314
+ * - If navigating to the same route: navigation state is updated with a unique key,
7315
+ * which can be used by host apps to detect "reset" navigation via useLocation().state
7316
+ *
7317
+ * @example
7318
+ * // Basic usage
7319
+ * <Breadcrumbs items={[
7320
+ * { label: 'Home', href: '/' },
7321
+ * { label: 'Products', href: '/products' },
7322
+ * { label: 'Widget' } // Current page (no href)
7323
+ * ]} />
7324
+ *
7325
+ * @example
7326
+ * // Host app detecting breadcrumb navigation for state reset
7327
+ * function ProductsPage() {
7328
+ * const location = useLocation();
7329
+ * const [viewMode, setViewMode] = useState<'list' | 'detail'>('list');
7330
+ *
7331
+ * // Reset to list view when breadcrumb navigation occurs
7332
+ * useEffect(() => {
7333
+ * if (location.state?.breadcrumbReset) {
7334
+ * setViewMode('list');
7335
+ * }
7336
+ * }, [location.state?.breadcrumbReset]);
7337
+ *
7338
+ * // ... rest of component
7339
+ * }
7340
+ */
7282
7341
  function Breadcrumbs({ items, showHome = true }) {
7283
- return (jsxRuntime.jsxs("nav", { "aria-label": "Breadcrumb", className: "flex items-center space-x-2 text-sm", children: [showHome && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(reactRouterDom.Link, { to: "/", className: "text-ink-500 hover:text-ink-900 transition-colors", "aria-label": "Home", children: jsxRuntime.jsx(lucideReact.Home, { className: "h-4 w-4" }) }), items.length > 0 && jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4 text-ink-400" })] })), items.map((item, index) => {
7342
+ const navigate = reactRouterDom.useNavigate();
7343
+ const location = reactRouterDom.useLocation();
7344
+ /**
7345
+ * Handle breadcrumb click with same-route detection.
7346
+ * When clicking a breadcrumb that points to the current route,
7347
+ * we navigate with state to trigger a reset in the host component.
7348
+ */
7349
+ const handleBreadcrumbClick = (e, href, onClick) => {
7350
+ // Always call onClick if provided (for custom actions)
7351
+ onClick?.();
7352
+ // Check if we're navigating to the same base path
7353
+ const targetPath = href.split('?')[0].split('#')[0];
7354
+ const currentPath = location.pathname;
7355
+ if (targetPath === currentPath) {
7356
+ // Same route - prevent default Link behavior and use navigate with state
7357
+ e.preventDefault();
7358
+ navigate(href, {
7359
+ state: {
7360
+ breadcrumbReset: Date.now(),
7361
+ from: 'breadcrumb'
7362
+ },
7363
+ replace: true
7364
+ });
7365
+ }
7366
+ // Different route - let the Link handle it normally
7367
+ };
7368
+ return (jsxRuntime.jsxs("nav", { "aria-label": "Breadcrumb", className: "flex items-center space-x-2 text-sm", children: [showHome && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(reactRouterDom.Link, { to: "/", className: "text-ink-500 hover:text-ink-900 transition-colors", "aria-label": "Home", onClick: (e) => handleBreadcrumbClick(e, '/'), children: jsxRuntime.jsx(lucideReact.Home, { className: "h-4 w-4" }) }), items.length > 0 && jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4 text-ink-400" })] })), items.map((item, index) => {
7284
7369
  const isLast = index === items.length - 1;
7285
7370
  const isActive = isLast;
7286
- return (jsxRuntime.jsxs(React.Fragment, { children: [item.href && !isActive ? (jsxRuntime.jsxs(reactRouterDom.Link, { to: item.href, className: "flex items-center gap-2 text-ink-500 hover:text-ink-900 hover:underline transition-colors", children: [item.icon && jsxRuntime.jsx("span", { className: "flex-shrink-0", children: item.icon }), jsxRuntime.jsx("span", { children: item.label })] })) : (jsxRuntime.jsxs("span", { className: `
7287
- flex items-center gap-2 px-2 py-1 rounded-md transition-colors
7288
- ${isActive
7289
- ? 'bg-accent-50 text-accent-900 font-semibold'
7290
- : 'text-ink-700 font-medium'}
7291
- `, "aria-current": isActive ? 'page' : undefined, children: [item.icon && jsxRuntime.jsx("span", { className: "flex-shrink-0", children: item.icon }), jsxRuntime.jsx("span", { children: item.label })] })), !isLast && jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4 text-ink-400" })] }, index));
7371
+ const content = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [item.icon && jsxRuntime.jsx("span", { className: "flex-shrink-0", children: item.icon }), jsxRuntime.jsx("span", { children: item.label })] }));
7372
+ const renderBreadcrumb = () => {
7373
+ // Active item (last item) - always render as non-clickable span
7374
+ if (isActive) {
7375
+ return (jsxRuntime.jsx("span", { className: "flex items-center gap-2 px-2 py-1 rounded-md bg-accent-50 text-accent-900 font-semibold transition-colors", "aria-current": "page", children: content }));
7376
+ }
7377
+ // Has href - render as Link with same-route detection
7378
+ if (item.href) {
7379
+ return (jsxRuntime.jsx(reactRouterDom.Link, { to: item.href, onClick: (e) => handleBreadcrumbClick(e, item.href, item.onClick), className: "flex items-center gap-2 text-ink-500 hover:text-ink-900 hover:underline transition-colors", children: content }));
7380
+ }
7381
+ // Only onClick (no href) - render as button
7382
+ if (item.onClick) {
7383
+ return (jsxRuntime.jsx("button", { type: "button", onClick: item.onClick, className: "flex items-center gap-2 text-ink-500 hover:text-ink-900 hover:underline transition-colors bg-transparent border-none cursor-pointer p-0", children: content }));
7384
+ }
7385
+ // Neither href nor onClick - render as non-clickable span
7386
+ return (jsxRuntime.jsx("span", { className: "flex items-center gap-2 text-ink-700 font-medium", children: content }));
7387
+ };
7388
+ return (jsxRuntime.jsxs(React.Fragment, { children: [renderBreadcrumb(), !isLast && jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4 text-ink-400" })] }, index));
7292
7389
  })] }));
7293
7390
  }
7294
7391
 
@@ -56986,6 +57083,7 @@ exports.saveColumnOrder = saveColumnOrder;
56986
57083
  exports.saveColumnWidths = saveColumnWidths;
56987
57084
  exports.searchFormulas = searchFormulas;
56988
57085
  exports.statusManager = statusManager;
57086
+ exports.useBreadcrumbReset = useBreadcrumbReset;
56989
57087
  exports.useBreakpoint = useBreakpoint;
56990
57088
  exports.useBreakpointValue = useBreakpointValue;
56991
57089
  exports.useColumnReorder = useColumnReorder;