@donotdev/ui 0.0.14 → 0.0.15

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.
Files changed (41) hide show
  1. package/dist/components/layout/components/index.d.ts +3 -0
  2. package/dist/components/layout/components/index.d.ts.map +1 -1
  3. package/dist/components/layout/components/index.js +3 -0
  4. package/dist/crud/components/EntityCardList.d.ts +1 -1
  5. package/dist/crud/components/EntityCardList.d.ts.map +1 -1
  6. package/dist/crud/components/EntityCardList.js +16 -13
  7. package/dist/crud/components/EntityDisplayRenderer.d.ts +1 -1
  8. package/dist/crud/components/EntityDisplayRenderer.d.ts.map +1 -1
  9. package/dist/crud/components/EntityDisplayRenderer.js +6 -2
  10. package/dist/crud/components/EntityFormRenderer.d.ts +1 -1
  11. package/dist/crud/components/EntityFormRenderer.d.ts.map +1 -1
  12. package/dist/crud/components/EntityFormRenderer.js +12 -29
  13. package/dist/crud/components/EntityRecommendations.d.ts +1 -0
  14. package/dist/crud/components/EntityRecommendations.d.ts.map +1 -1
  15. package/dist/crud/components/EntityRecommendations.js +3 -2
  16. package/dist/dndev.css +11558 -0
  17. package/dist/index.js +4 -4
  18. package/dist/internal/providers/NavigationProvider.d.ts.map +1 -1
  19. package/dist/internal/providers/NavigationProvider.js +3 -5
  20. package/dist/providers/ViteAppProviders.d.ts.map +1 -1
  21. package/dist/providers/ViteAppProviders.js +3 -5
  22. package/dist/routing/GoToInput.d.ts.map +1 -1
  23. package/dist/routing/GoToInput.js +4 -5
  24. package/dist/routing/hooks/hooks.next.js +1 -1
  25. package/dist/routing/hooks/hooks.vite.js +1 -1
  26. package/dist/routing/hooks/useNavigate.next.d.ts +1 -1
  27. package/dist/routing/hooks/useNavigate.next.d.ts.map +1 -1
  28. package/dist/routing/hooks/useNavigate.next.js +1 -7
  29. package/dist/routing/hooks/useNavigate.vite.d.ts +1 -1
  30. package/dist/routing/hooks/useNavigate.vite.d.ts.map +1 -1
  31. package/dist/routing/hooks/useNavigate.vite.js +1 -7
  32. package/dist/routing/useRouteDiscovery.d.ts +4 -15
  33. package/dist/routing/useRouteDiscovery.d.ts.map +1 -1
  34. package/dist/routing/useRouteDiscovery.js +6 -5
  35. package/dist/styles/index.css +16 -6
  36. package/dist/utils/useFormStoreSafe.d.ts +2 -15
  37. package/dist/utils/useFormStoreSafe.d.ts.map +1 -1
  38. package/dist/utils/useFormStoreSafe.js +3 -33
  39. package/dist/vite-routing/RootLayout.d.ts.map +1 -1
  40. package/dist/vite-routing/RootLayout.js +10 -15
  41. package/package.json +10 -10
@@ -1 +1 @@
1
- {"version":3,"file":"NavigationProvider.d.ts","sourceRoot":"","sources":["../../../src/internal/providers/NavigationProvider.tsx"],"names":[],"mappings":"AAgCA,OAAO,EAAkB,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAIvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC;;GAEG;AACH,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAExD;;GAEG;AACH,UAAU,uBAAuB;IAC/B,6CAA6C;IAC7C,MAAM,EAAE,SAAS,CAAC;IAClB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,MAAM,EAAE,EAAE,uBAAuB,2CAqBrE"}
1
+ {"version":3,"file":"NavigationProvider.d.ts","sourceRoot":"","sources":["../../../src/internal/providers/NavigationProvider.tsx"],"names":[],"mappings":"AAgCA,OAAO,EAAkB,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAIvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC;;GAEG;AACH,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAExD;;GAEG;AACH,UAAU,uBAAuB;IAC/B,6CAA6C;IAC7C,MAAM,EAAE,SAAS,CAAC;IAClB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,MAAM,EAAE,EAAE,uBAAuB,2CAkBrE"}
@@ -53,20 +53,18 @@ import { useAbortControllerStore, useOverlayStore } from '@donotdev/core';
53
53
  * @author AMBROISE PARK Consulting
54
54
  */
55
55
  export function NavigationProvider({ router }) {
56
- const abortAll = useAbortControllerStore((state) => state.abortAll);
57
- const closeAll = useOverlayStore((state) => state.closeAll);
58
56
  useEffect(() => {
59
57
  // Subscribe to navigation events using router.subscribe()
60
58
  // This replaces the useLocation() + useEffect pattern from the old implementation
61
59
  const unsubscribe = router.subscribe(() => {
62
60
  // When navigation occurs, abort all pending requests and close overlays
63
61
  // This prevents memory leaks and race conditions
64
- abortAll();
65
- closeAll();
62
+ useAbortControllerStore.getState().abortAll();
63
+ useOverlayStore.getState().closeAll();
66
64
  });
67
65
  // Cleanup subscription on unmount
68
66
  return unsubscribe;
69
- }, [router, abortAll, closeAll]);
67
+ }, [router]);
70
68
  // RouterProvider manages its own children from the router configuration
71
69
  // No need to render children prop (kept for backwards compatibility)
72
70
  return _jsx(RouterProvider, { router: router });
@@ -1 +1 @@
1
- {"version":3,"file":"ViteAppProviders.d.ts","sourceRoot":"","sources":["../../src/providers/ViteAppProviders.tsx"],"names":[],"mappings":"AAcA,OAAO,aAAa,CAAC;AACrB,OAAO,gBAAgB,CAAC;AACxB,OAAO,sBAAsB,CAAC;AAC9B,OAAO,gBAAgB,CAAC;AACxB,OAAO,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AA2BxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,2CAkCxD"}
1
+ {"version":3,"file":"ViteAppProviders.d.ts","sourceRoot":"","sources":["../../src/providers/ViteAppProviders.tsx"],"names":[],"mappings":"AAcA,OAAO,aAAa,CAAC;AACrB,OAAO,gBAAgB,CAAC;AACxB,OAAO,sBAAsB,CAAC;AAC9B,OAAO,gBAAgB,CAAC;AACxB,OAAO,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AA2BxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,2CAmCxD"}
@@ -8,7 +8,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
8
  * @since 0.0.1
9
9
  * @author AMBROISE PARK Consulting
10
10
  */
11
- import { lazy, Suspense } from 'react';
11
+ import { lazy, Suspense, useMemo } from 'react';
12
12
  // Import virtual modules to populate globalThis._DNDEV_CONFIG_ before any components load
13
13
  import 'virtual:env';
14
14
  import 'virtual:themes';
@@ -76,9 +76,7 @@ export function ViteAppProviders(props) {
76
76
  // Create router instance using React Router v7 data router pattern
77
77
  // Virtual modules are loaded at module level (lines 20-26), so routes are available
78
78
  // HomePage.tsx is automatically assigned path "/" by RouteDiscovery convention
79
- const router = createAppRouter({
80
- routeGroups,
81
- layout,
82
- });
79
+ // Memoized so the router instance is stable across re-renders (Item 86)
80
+ const router = useMemo(() => createAppRouter({ routeGroups, layout }), [routeGroups, layout]);
83
81
  return (_jsxs(AppConfigProvider, { config: config, platform: "vite", children: [_jsx(SentryInitializer, {}), _jsx(ViteStoresInitializer, { customStores: customStores, children: _jsx(Suspense, { fallback: null, children: _jsxs(HelmetProvider, { children: [_jsx(FaviconHead, {}), _jsx(PerformanceHints, {}), _jsx(NavigationProvider, { router: router })] }) }) })] }));
84
82
  }
@@ -1 +1 @@
1
- {"version":3,"file":"GoToInput.d.ts","sourceRoot":"","sources":["../../src/routing/GoToInput.tsx"],"names":[],"mappings":"AA+BA,QAAA,MAAM,SAAS,+CAyEd,CAAC;AAEF,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"GoToInput.d.ts","sourceRoot":"","sources":["../../src/routing/GoToInput.tsx"],"names":[],"mappings":"AA+BA,QAAA,MAAM,SAAS,+CAwEd,CAAC;AAEF,eAAe,SAAS,CAAC"}
@@ -22,7 +22,6 @@ const GoToInput = () => {
22
22
  const { t } = useTranslation('dndev');
23
23
  const [value, setValue] = useState('');
24
24
  const inputRef = useRef(null);
25
- const openCommandDialog = useOverlayStore((state) => state.openCommandDialog);
26
25
  const isCommandDialogOpen = useOverlayStore((state) => state.isCommandDialogOpen);
27
26
  // Clear input when dialog closes
28
27
  useEffect(() => {
@@ -31,14 +30,14 @@ const GoToInput = () => {
31
30
  }
32
31
  }, [isCommandDialogOpen]);
33
32
  const handleFocus = useCallback(() => {
34
- openCommandDialog(value);
35
- }, [openCommandDialog, value]);
33
+ useOverlayStore.getState().openCommandDialog(value);
34
+ }, [value]);
36
35
  const handleKeyDown = useCallback((e) => {
37
36
  if (e.key === 'Enter') {
38
37
  e.preventDefault();
39
- openCommandDialog(value);
38
+ useOverlayStore.getState().openCommandDialog(value);
40
39
  }
41
- }, [openCommandDialog, value]);
40
+ }, [value]);
42
41
  return (_jsxs(Stack, { direction: "row", align: "center", className: "dndev-relative dndev-w-full dndev-max-w-sm", role: "search", children: [_jsx(Search, { className: "dndev-absolute dndev-size-md", style: { insetInlineStart: '0.75rem', opacity: 0.5 }, "aria-hidden": "true" }), _jsx("input", { ref: inputRef, type: "text", value: value, onChange: (e) => setValue(e.target.value), onFocus: handleFocus, onKeyDown: handleKeyDown, placeholder: t('globalGoTo.searchPlaceholder', 'Search pages...'), className: "dndev-input dndev-w-full", style: { paddingInlineStart: '2.5rem', paddingInlineEnd: '5rem' }, "aria-label": t('globalGoTo.ariaLabel', 'Search navigation') }), _jsx("div", { className: "dndev-absolute dndev-flex dndev-items-center dndev-text-sm", style: { insetInlineEnd: '0.75rem', gap: '0.25rem', opacity: 0.5 }, "aria-hidden": "true", children: _jsx("kbd", { style: {
43
42
  padding: '0.25rem 0.5rem',
44
43
  fontSize: 'var(--font-size-xs)',
@@ -1 +1 @@
1
- "use client";import{useRouter as p}from"next/navigation";import{useCallback as h}from"react";import{isClient as D,useOverlayStore as v}from"@donotdev/core";import*as F from"@donotdev/crud";var y={forms:{},startSubmit:()=>{},setUploading:()=>{},setValidating:()=>{},setSubmitting:()=>{},setSuccess:()=>{},setError:()=>{},reset:()=>{},cleanup:()=>{},setIsDirty:()=>{},hasDirtyForms:()=>!1,getDirtyFormIds:()=>[],getStatus:()=>"idle",isLoading:()=>!1,getUploadProgress:()=>0,getError:()=>null,getIsDirty:()=>!1},m=F?.useFormStore;function x(e){return m?m(e):e(y)}x.getState=()=>m&&typeof m.getState=="function"?m.getState():y;async function T(e="You have unsaved changes. Discard them?"){return x.getState().hasDirtyForms()&&typeof window<"u"&&window.confirm?window.confirm(e):!0}function g(){let e=p(),t=v(r=>r.closeAll);return h(async(r,u)=>{if(r==="back")return t(),e.back();if(await T()){if(t(),u?.preserveScroll&&D()){let o=window.scrollY;u?.replace?e.replace(r):e.push(r),requestAnimationFrame(()=>{window.scrollTo({top:o,behavior:"auto"})});return}return u?.replace?e.replace(r):e.push(r)}},[e,t])}function w(){let e=p();return h(()=>e.back(),[e])}function E(){let e=p();return h(()=>e.refresh(),[e])}function b(){let e=p();return h(t=>e.prefetch(t),[e])}import{usePathname as I,useSearchParams as N}from"next/navigation";function f(){let e=I(),t=N(),r=t.toString()?`?${t.toString()}`:"";return{pathname:e||"/",search:r,hash:"",state:null}}import{useParams as O}from"next/navigation";function S(){return O()}function G(e){let r=S()[e];if(typeof r=="string")return r;if(Array.isArray(r)&&r.length>0)return r[0]}import{useSearchParams as C}from"next/navigation";function R(){return C()}import{usePathname as L}from"next/navigation";function _(e){let t=L(),r=e.replace(/:[^/]+/g,"([^/]+)").replace(/\*/g,".*"),u=new RegExp(`^${r}$`),i=t.match(u);if(!i)return null;let o=e.match(/:[^/]+/g)?.map(n=>n.slice(1))||[],s={};return o.forEach((n,c)=>{s[n]=i[c+1]||""}),{params:s,pathname:t,pattern:e}}import{useCallback as A}from"react";function U(){let e=R(),t=g(),r=f(),u=A((s,n)=>{let c=new URLSearchParams(e.toString());c.set(s,n);let l=c.toString(),P=`${r.pathname||"/"}${l?`?${l}`:""}`;t(P)},[e,t,r.pathname]),i=A(s=>{let n=new URLSearchParams(e.toString());n.delete(s);let c=n.toString(),a=`${r.pathname||"/"}${c?`?${c}`:""}`;t(a)},[e,t,r.pathname]),o=A(()=>{let s=r.pathname||"/";t(s)},[t,r.pathname]);return{query:e,setQuery:u,removeQuery:i,clearQueries:o}}import{useMemo as Q}from"react";import{isClient as j,FEATURE_STATUS as q}from"@donotdev/core";import{useAuthConfig as Y}from"@donotdev/core";import*as $ from"@donotdev/auth";import{DEGRADED_AUTH_API as K}from"@donotdev/core";var k=$?.useAuth;function M(e){return K[e]}function d(e){return k?k(e):M(e)}function B(e={}){let{auth:t,redirectTo:r,condition:u}=e,i=f(),o=Y(),s=d("user"),n=d("can"),c=d("status");return Q(()=>{if(!j())return{shouldRedirect:!1,redirectTo:null,isChecking:!1};if(c===q.INITIALIZING)return{shouldRedirect:!1,redirectTo:null,isChecking:!0};if(u){let a=u(s,c);return{shouldRedirect:a,redirectTo:a&&r||null,isChecking:!1}}if(t!==!1&&t!==void 0){if(!n)return{shouldRedirect:!1,redirectTo:null,isChecking:!1};if(!n.navigate(t)){let a=null;return typeof t=="object"&&t.required&&!s?i.search.includes("code=")||i.search.includes("state=")||i.search.includes("error=")?a=`${o.authRoute}${i.search}`:a=o.authRoute:typeof t=="object"&&t.role?a=o.roleRoute:typeof t=="object"&&t.tier?a=o.tierRoute:a=o.roleRoute,{shouldRedirect:!0,redirectTo:r||a,isChecking:!1}}}return{shouldRedirect:!1,redirectTo:null,isChecking:!1}},[t,c,s,n,u,r,i.search,o.authRoute,o.roleRoute,o.tierRoute])}export{w as useBack,f as useLocation,_ as useMatch,g as useNavigate,S as useParams,b as usePrefetch,U as useQueryParams,B as useRedirectGuard,E as useRefresh,G as useRouteParam,R as useSearchParams};
1
+ "use client";import{useRouter as f}from"next/navigation";import{useCallback as h}from"react";import{isClient as S,useOverlayStore as y}from"@donotdev/core";function d(){let e=f(),t=y(r=>r.closeAll);return h((r,i)=>{if(r==="back")return t(),e.back();if(t(),i?.preserveScroll&&S()){let s=window.scrollY;i?.replace?e.replace(r):e.push(r),requestAnimationFrame(()=>{window.scrollTo({top:s,behavior:"auto"})});return}return i?.replace?e.replace(r):e.push(r)},[e,t])}function k(){let e=f();return h(()=>e.back(),[e])}function T(){let e=f();return h(()=>e.refresh(),[e])}function v(){let e=f();return h(t=>e.prefetch(t),[e])}import{usePathname as b,useSearchParams as N}from"next/navigation";function l(){let e=b(),t=N(),r=t.toString()?`?${t.toString()}`:"";return{pathname:e||"/",search:r,hash:"",state:null}}import{useParams as w}from"next/navigation";function g(){return w()}function C(e){let r=g()[e];if(typeof r=="string")return r;if(Array.isArray(r)&&r.length>0)return r[0]}import{useSearchParams as I}from"next/navigation";function R(){return I()}import{usePathname as G}from"next/navigation";function $(e){let t=G(),r=e.replace(/:[^/]+/g,"([^/]+)").replace(/\*/g,".*"),i=new RegExp(`^${r}$`),s=t.match(i);if(!s)return null;let o=e.match(/:[^/]+/g)?.map(a=>a.slice(1))||[],n={};return o.forEach((a,c)=>{n[a]=s[c+1]||""}),{params:n,pathname:t,pattern:e}}import{useCallback as P}from"react";function K(){let e=R(),t=d(),r=l(),i=P((n,a)=>{let c=new URLSearchParams(e.toString());c.set(n,a);let m=c.toString(),x=`${r.pathname||"/"}${m?`?${m}`:""}`;t(x)},[e,t,r.pathname]),s=P(n=>{let a=new URLSearchParams(e.toString());a.delete(n);let c=a.toString(),u=`${r.pathname||"/"}${c?`?${c}`:""}`;t(u)},[e,t,r.pathname]),o=P(()=>{let n=r.pathname||"/";t(n)},[t,r.pathname]);return{query:e,setQuery:i,removeQuery:s,clearQueries:o}}import{useMemo as E}from"react";import{isClient as Q,FEATURE_STATUS as F}from"@donotdev/core";import{useAuthConfig as M}from"@donotdev/core";import*as O from"@donotdev/auth";import{DEGRADED_AUTH_API as U}from"@donotdev/core";var A=O?.useAuth;function L(e){return U[e]}function p(e){return A?A(e):L(e)}function j(e={}){let{auth:t,redirectTo:r,condition:i}=e,s=l(),o=M(),n=p("user"),a=p("can"),c=p("status");return E(()=>{if(!Q())return{shouldRedirect:!1,redirectTo:null,isChecking:!1};if(c===F.INITIALIZING)return{shouldRedirect:!1,redirectTo:null,isChecking:!0};if(i){let u=i(n,c);return{shouldRedirect:u,redirectTo:u&&r||null,isChecking:!1}}if(t!==!1&&t!==void 0){if(!a)return{shouldRedirect:!1,redirectTo:null,isChecking:!1};if(!a.navigate(t)){let u=null;return typeof t=="object"&&t.required&&!n?s.search.includes("code=")||s.search.includes("state=")||s.search.includes("error=")?u=`${o.authRoute}${s.search}`:u=o.authRoute:typeof t=="object"&&t.role?u=o.roleRoute:typeof t=="object"&&t.tier?u=o.tierRoute:u=o.roleRoute,{shouldRedirect:!0,redirectTo:r||u,isChecking:!1}}}return{shouldRedirect:!1,redirectTo:null,isChecking:!1}},[t,c,n,a,i,r,s.search,o.authRoute,o.roleRoute,o.tierRoute])}export{k as useBack,l as useLocation,$ as useMatch,d as useNavigate,g as useParams,v as usePrefetch,K as useQueryParams,j as useRedirectGuard,T as useRefresh,C as useRouteParam,R as useSearchParams};
@@ -1 +1 @@
1
- "use client";import{useCallback as p}from"react";import{useNavigate as T}from"react-router-dom";import{isClient as k,useOverlayStore as D}from"@donotdev/core";import*as F from"@donotdev/crud";var y={forms:{},startSubmit:()=>{},setUploading:()=>{},setValidating:()=>{},setSubmitting:()=>{},setSuccess:()=>{},setError:()=>{},reset:()=>{},cleanup:()=>{},setIsDirty:()=>{},hasDirtyForms:()=>!1,getDirtyFormIds:()=>[],getStatus:()=>"idle",isLoading:()=>!1,getUploadProgress:()=>0,getError:()=>null,getIsDirty:()=>!1},f=F?.useFormStore;function P(e){return f?f(e):e(y)}P.getState=()=>f&&typeof f.getState=="function"?f.getState():y;async function x(e="You have unsaved changes. Discard them?"){return P.getState().hasDirtyForms()&&typeof window<"u"&&window.confirm?window.confirm(e):!0}function h(){let e=T(),t=D(r=>r.closeAll);return p(async(r,c)=>{if(r==="back")return t(),e(-1);if(await x())return t(),c?.replace?e(r,{replace:!0}):e(r)},[e,t])}function w(){let e=T();return p(()=>e(-1),[e])}function E(){return p(()=>{k()&&window.location.reload()},[])}function b(){return p(e=>{},[])}import{useLocation as I}from"react-router-dom";function m(){return I()}import{useParams as L}from"react-router-dom";function g(){return L()}function O(e){let r=g()[e];if(typeof r=="string")return r;if(Array.isArray(r)&&r.length>0)return r[0]}import{useSearchParams as G}from"react-router-dom";function R(){return G()[0]}import{useMatch as C}from"react-router-dom";function _(e){return C(e)}import{useCallback as S}from"react";function N(){let e=R(),t=h(),r=m(),c=S((n,i)=>{let a=new URLSearchParams(e.toString());a.set(n,i);let l=a.toString(),A=`${r.pathname||"/"}${l?`?${l}`:""}`;t(A)},[e,t,r.pathname]),u=S(n=>{let i=new URLSearchParams(e.toString());i.delete(n);let a=i.toString(),o=`${r.pathname||"/"}${a?`?${a}`:""}`;t(o)},[e,t,r.pathname]),s=S(()=>{let n=r.pathname||"/";t(n)},[t,r.pathname]);return{query:e,setQuery:c,removeQuery:u,clearQueries:s}}import{useMemo as $}from"react";import{isClient as Q,FEATURE_STATUS as j}from"@donotdev/core";import{useAuthConfig as q}from"@donotdev/core";import*as U from"@donotdev/auth";import{DEGRADED_AUTH_API as M}from"@donotdev/core";var v=U?.useAuth;function K(e){return M[e]}function d(e){return v?v(e):K(e)}function B(e={}){let{auth:t,redirectTo:r,condition:c}=e,u=m(),s=q(),n=d("user"),i=d("can"),a=d("status");return $(()=>{if(!Q())return{shouldRedirect:!1,redirectTo:null,isChecking:!1};if(a===j.INITIALIZING)return{shouldRedirect:!1,redirectTo:null,isChecking:!0};if(c){let o=c(n,a);return{shouldRedirect:o,redirectTo:o&&r||null,isChecking:!1}}if(t!==!1&&t!==void 0){if(!i)return{shouldRedirect:!1,redirectTo:null,isChecking:!1};if(!i.navigate(t)){let o=null;return typeof t=="object"&&t.required&&!n?u.search.includes("code=")||u.search.includes("state=")||u.search.includes("error=")?o=`${s.authRoute}${u.search}`:o=s.authRoute:typeof t=="object"&&t.role?o=s.roleRoute:typeof t=="object"&&t.tier?o=s.tierRoute:o=s.roleRoute,{shouldRedirect:!0,redirectTo:r||o,isChecking:!1}}}return{shouldRedirect:!1,redirectTo:null,isChecking:!1}},[t,a,n,i,c,r,u.search,s.authRoute,s.roleRoute,s.tierRoute])}export{w as useBack,m as useLocation,_ as useMatch,h as useNavigate,g as useParams,b as usePrefetch,N as useQueryParams,B as useRedirectGuard,E as useRefresh,O as useRouteParam,R as useSearchParams};
1
+ "use client";import{useCallback as m}from"react";import{useNavigate as P}from"react-router-dom";import{isClient as y,useOverlayStore as S}from"@donotdev/core";function h(){let e=P(),t=S(r=>r.closeAll);return m((r,i)=>r==="back"?(t(),e(-1)):(t(),i?.replace?e(r,{replace:!0}):e(r)),[e,t])}function v(){let e=P();return m(()=>e(-1),[e])}function k(){return m(()=>{y()&&window.location.reload()},[])}function T(){return m(e=>{},[])}import{useLocation as C}from"react-router-dom";function f(){return C()}import{useParams as I}from"react-router-dom";function d(){return I()}function b(e){let r=d()[e];if(typeof r=="string")return r;if(Array.isArray(r)&&r.length>0)return r[0]}import{useSearchParams as G}from"react-router-dom";function g(){return G()[0]}import{useMatch as w}from"react-router-dom";function L(e){return w(e)}import{useCallback as R}from"react";function N(){let e=g(),t=h(),r=f(),i=R((n,u)=>{let a=new URLSearchParams(e.toString());a.set(n,u);let l=a.toString(),A=`${r.pathname||"/"}${l?`?${l}`:""}`;t(A)},[e,t,r.pathname]),c=R(n=>{let u=new URLSearchParams(e.toString());u.delete(n);let a=u.toString(),o=`${r.pathname||"/"}${a?`?${a}`:""}`;t(o)},[e,t,r.pathname]),s=R(()=>{let n=r.pathname||"/";t(n)},[t,r.pathname]);return{query:e,setQuery:i,removeQuery:c,clearQueries:s}}import{useMemo as $}from"react";import{isClient as M,FEATURE_STATUS as Q}from"@donotdev/core";import{useAuthConfig as E}from"@donotdev/core";import*as K from"@donotdev/auth";import{DEGRADED_AUTH_API as O}from"@donotdev/core";var x=K?.useAuth;function U(e){return O[e]}function p(e){return x?x(e):U(e)}function _(e={}){let{auth:t,redirectTo:r,condition:i}=e,c=f(),s=E(),n=p("user"),u=p("can"),a=p("status");return $(()=>{if(!M())return{shouldRedirect:!1,redirectTo:null,isChecking:!1};if(a===Q.INITIALIZING)return{shouldRedirect:!1,redirectTo:null,isChecking:!0};if(i){let o=i(n,a);return{shouldRedirect:o,redirectTo:o&&r||null,isChecking:!1}}if(t!==!1&&t!==void 0){if(!u)return{shouldRedirect:!1,redirectTo:null,isChecking:!1};if(!u.navigate(t)){let o=null;return typeof t=="object"&&t.required&&!n?c.search.includes("code=")||c.search.includes("state=")||c.search.includes("error=")?o=`${s.authRoute}${c.search}`:o=s.authRoute:typeof t=="object"&&t.role?o=s.roleRoute:typeof t=="object"&&t.tier?o=s.tierRoute:o=s.roleRoute,{shouldRedirect:!0,redirectTo:r||o,isChecking:!1}}}return{shouldRedirect:!1,redirectTo:null,isChecking:!1}},[t,a,n,u,i,r,c.search,s.authRoute,s.roleRoute,s.tierRoute])}export{v as useBack,f as useLocation,L as useMatch,h as useNavigate,d as useParams,T as usePrefetch,N as useQueryParams,_ as useRedirectGuard,k as useRefresh,b as useRouteParam,g as useSearchParams};
@@ -1,5 +1,5 @@
1
1
  import type { NavigateOptions } from './types';
2
- export declare function useNavigate(): (to: string, options?: NavigateOptions) => Promise<void>;
2
+ export declare function useNavigate(): (to: string, options?: NavigateOptions) => void;
3
3
  export declare function useBack(): () => void;
4
4
  export declare function useRefresh(): () => void;
5
5
  export declare function usePrefetch(): (to: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"useNavigate.next.d.ts","sourceRoot":"","sources":["../../../src/routing/hooks/useNavigate.next.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,wBAAgB,WAAW,SAKZ,MAAM,YAAY,eAAe,mBAgC/C;AAED,wBAAgB,OAAO,eAGtB;AAED,wBAAgB,UAAU,eAGzB;AAED,wBAAgB,WAAW,SAED,MAAM,UAC/B"}
1
+ {"version":3,"file":"useNavigate.next.d.ts","sourceRoot":"","sources":["../../../src/routing/hooks/useNavigate.next.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,wBAAgB,WAAW,SAKlB,MAAM,YAAY,eAAe,UA0BzC;AAED,wBAAgB,OAAO,eAGtB;AAED,wBAAgB,UAAU,eAGzB;AAED,wBAAgB,WAAW,SAED,MAAM,UAC/B"}
@@ -11,20 +11,14 @@
11
11
  import { useRouter as useNextRouter } from 'next/navigation';
12
12
  import { useCallback } from 'react';
13
13
  import { isClient, useOverlayStore } from '@donotdev/core';
14
- import { checkFormNavigationSafe } from '../../utils/useFormStoreSafe';
15
14
  export function useNavigate() {
16
15
  const router = useNextRouter();
17
16
  const closeAll = useOverlayStore((state) => state.closeAll);
18
- return useCallback(async (to, options) => {
17
+ return useCallback((to, options) => {
19
18
  if (to === 'back') {
20
19
  closeAll();
21
20
  return router.back();
22
21
  }
23
- // Check FormStore for dirty forms before navigation (safe - works if CRUD not installed)
24
- const shouldProceed = await checkFormNavigationSafe();
25
- if (!shouldProceed) {
26
- return; // Navigation blocked by user
27
- }
28
22
  closeAll();
29
23
  if (options?.preserveScroll && isClient()) {
30
24
  const scrollY = window.scrollY;
@@ -1,5 +1,5 @@
1
1
  import type { NavigateOptions } from './types';
2
- export declare function useNavigate(): (to: string, options?: NavigateOptions) => Promise<void>;
2
+ export declare function useNavigate(): (to: string, options?: NavigateOptions) => void | Promise<void>;
3
3
  export declare function useBack(): () => void | Promise<void>;
4
4
  export declare function useRefresh(): () => void;
5
5
  export declare function usePrefetch(): (_to: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"useNavigate.vite.d.ts","sourceRoot":"","sources":["../../../src/routing/hooks/useNavigate.vite.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,wBAAgB,WAAW,SAKZ,MAAM,YAAY,eAAe,mBAmB/C;AAED,wBAAgB,OAAO,+BAGtB;AAED,wBAAgB,UAAU,eAIzB;AAED,wBAAgB,WAAW,UACA,MAAM,UAGhC"}
1
+ {"version":3,"file":"useNavigate.vite.d.ts","sourceRoot":"","sources":["../../../src/routing/hooks/useNavigate.vite.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,wBAAgB,WAAW,SAKlB,MAAM,YAAY,eAAe,0BAazC;AAED,wBAAgB,OAAO,+BAGtB;AAED,wBAAgB,UAAU,eAIzB;AAED,wBAAgB,WAAW,UACA,MAAM,UAGhC"}
@@ -11,20 +11,14 @@
11
11
  import { useCallback } from 'react';
12
12
  import { useNavigate as useRouterNavigate } from 'react-router-dom';
13
13
  import { isClient, useOverlayStore } from '@donotdev/core';
14
- import { checkFormNavigationSafe } from '../../utils/useFormStoreSafe';
15
14
  export function useNavigate() {
16
15
  const navigate = useRouterNavigate();
17
16
  const closeAll = useOverlayStore((state) => state.closeAll);
18
- return useCallback(async (to, options) => {
17
+ return useCallback((to, options) => {
19
18
  if (to === 'back') {
20
19
  closeAll();
21
20
  return navigate(-1);
22
21
  }
23
- // Check FormStore for dirty forms before navigation (safe - works if CRUD not installed)
24
- const shouldProceed = await checkFormNavigationSafe();
25
- if (!shouldProceed) {
26
- return; // Navigation blocked by user
27
- }
28
22
  closeAll();
29
23
  if (options?.replace)
30
24
  return navigate(to, { replace: true });
@@ -1,3 +1,4 @@
1
+ import type { NavigationRoute as CoreNavigationRoute } from '@donotdev/core';
1
2
  import type { JSX } from 'react';
2
3
  import type { ReactNode } from 'react';
3
4
  /**
@@ -41,22 +42,10 @@ export interface RouteGroup {
41
42
  routes: RouteInfo[];
42
43
  }
43
44
  /**
44
- * Navigation route interface for UI components
45
- *
46
- * @version 0.0.1
47
- * @since 0.0.1
48
- * @author AMBROISE PARK Consulting
45
+ * Navigation route - re-exported from @donotdev/core (Item 90)
46
+ * Core's version is the single source of truth with richer fields.
49
47
  */
50
- export interface NavigationRoute {
51
- /** Route path */
52
- path: string;
53
- /** Display label */
54
- label: string;
55
- /** Optional icon component */
56
- icon?: ReactNode;
57
- /** Additional route metadata */
58
- meta?: any;
59
- }
48
+ export type NavigationRoute = CoreNavigationRoute;
60
49
  /**
61
50
  * Hook that provides auto-discovered routes from virtual module system
62
51
  *
@@ -1 +1 @@
1
- {"version":3,"file":"useRouteDiscovery.d.ts","sourceRoot":"","sources":["../../src/routing/useRouteDiscovery.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC;;;;;;GAMG;AACH,MAAM,WAAW,SAAS;IACxB,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,gCAAgC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,IAAI,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IACnB,qCAAqC;IACrC,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,gCAAgC;IAChC,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,iBAAiB,IAAI,UAAU,EAAE,CA2BhD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,mBAAmB,IAAI,UAAU,EAAE,CAmBlD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,6BAA6B,IAAI,eAAe,EAAE,CAgBjE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,gBAK3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,gBAAgB;;;;;;SAQ/B"}
1
+ {"version":3,"file":"useRouteDiscovery.d.ts","sourceRoot":"","sources":["../../src/routing/useRouteDiscovery.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAAsB,eAAe,IAAI,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAIjG,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC;;;;;;GAMG;AACH,MAAM,WAAW,SAAS;IACxB,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,gCAAgC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,IAAI,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IACnB,qCAAqC;IACrC,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,mBAAmB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,iBAAiB,IAAI,UAAU,EAAE,CA2BhD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,mBAAmB,IAAI,UAAU,EAAE,CAoBlD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,6BAA6B,IAAI,eAAe,EAAE,CAcjE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,gBAK3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,gBAAgB;;;;;;SAQ/B"}
@@ -23,7 +23,7 @@
23
23
  * ```
24
24
  */
25
25
  import { useMemo } from 'react';
26
- import { useNavigationStore, getRoutes, getRouteManifest, buildRoutePath, } from '@donotdev/core';
26
+ import { useNavigationStore, getRoutes, getRouteManifest, } from '@donotdev/core';
27
27
  import { useAuthSafe } from '../utils/useAuthSafe';
28
28
  /**
29
29
  * Hook that provides auto-discovered routes from virtual module system
@@ -108,9 +108,11 @@ export function useAccessibleRoutes() {
108
108
  .map((group) => ({
109
109
  ...group,
110
110
  routes: group.routes.filter((route) => {
111
- // Use same filtering logic as AuthGuard for consistency
111
+ // Use same filtering logic as AuthGuard for consistency (Item 88)
112
112
  if (route.auth === false)
113
113
  return true;
114
+ if (route.auth === true)
115
+ return !!user;
114
116
  if (typeof route.auth === 'object' && route.auth.required) {
115
117
  return !!user;
116
118
  }
@@ -149,9 +151,8 @@ export function useAccessibleRoutes() {
149
151
  */
150
152
  export function useAccessibleNavigationRoutes() {
151
153
  const user = useAuthSafe('user');
152
- const { getFilteredRoutes } = useNavigationStore();
153
154
  return useMemo(() => {
154
- const filteredRoutes = getFilteredRoutes({
155
+ const filteredRoutes = useNavigationStore.getState().getFilteredRoutes({
155
156
  authenticated: !!user,
156
157
  role: user?.role || 'user',
157
158
  });
@@ -161,7 +162,7 @@ export function useAccessibleNavigationRoutes() {
161
162
  icon: route.icon,
162
163
  meta: route.meta,
163
164
  }));
164
- }, [getFilteredRoutes, user]);
165
+ }, [user]);
165
166
  }
166
167
  /**
167
168
  * Hook that returns all discovered routes as a flat list
@@ -3580,6 +3580,10 @@ em {
3580
3580
  flex-shrink: 0;
3581
3581
  }
3582
3582
 
3583
+ .dndev-combobox-create-btn:hover {
3584
+ opacity: 0.8;
3585
+ }
3586
+
3583
3587
  .dndev-combobox-open .dndev-input-with-trailing-icon .dndev-input-icon svg,
3584
3588
  .dndev-combobox-open .dndev-input-with-trailing-icon .dndev-input-icon > * {
3585
3589
  transform: rotate(180deg);
@@ -9485,9 +9489,12 @@ main[role='main'][data-routing-animation='none'] {
9485
9489
  /* Desktop: opt-in via data-footer-mode="scroll" */
9486
9490
 
9487
9491
  .dndev-layout[data-footer-mode='scroll'] {
9488
- overflow-y: auto;
9492
+ height: auto;
9493
+ min-height: 100dvh;
9494
+ max-height: none;
9495
+ overflow-y: visible;
9489
9496
  overflow-x: hidden;
9490
- grid-template-rows: var(--header-height) 1fr auto;
9497
+ grid-template-rows: var(--header-height) minmax(calc(100dvh - var(--header-height)), auto) auto;
9491
9498
  }
9492
9499
 
9493
9500
  .dndev-layout[data-footer-mode='scroll'] header[role='banner'] {
@@ -9511,6 +9518,7 @@ main[role='main'][data-routing-animation='none'] {
9511
9518
  .dndev-layout[data-footer-mode='scroll'] footer[role='contentinfo'] {
9512
9519
  grid-column: 2 / -1;
9513
9520
  height: auto;
9521
+ margin-top: var(--gap-lg);
9514
9522
  }
9515
9523
 
9516
9524
  :is(.dndev-layout[data-footer-mode='scroll'] footer[role='contentinfo']) > * {
@@ -9522,9 +9530,12 @@ main[role='main'][data-routing-animation='none'] {
9522
9530
 
9523
9531
  @media (width <=1023px) {
9524
9532
  .dndev-layout {
9525
- overflow-y: auto;
9533
+ height: auto;
9534
+ min-height: 100dvh;
9535
+ max-height: none;
9536
+ overflow-y: visible;
9526
9537
  overflow-x: hidden;
9527
- grid-template-rows: var(--header-height) 1fr auto;
9538
+ grid-template-rows: var(--header-height) minmax(calc(100dvh - var(--header-height)), auto) auto;
9528
9539
  }
9529
9540
 
9530
9541
  .dndev-layout header[role='banner'] {
@@ -9540,6 +9551,7 @@ main[role='main'][data-routing-animation='none'] {
9540
9551
 
9541
9552
  .dndev-layout footer[role='contentinfo'] {
9542
9553
  height: auto;
9554
+ margin-top: var(--gap-lg);
9543
9555
  }
9544
9556
 
9545
9557
  :is(.dndev-layout footer[role='contentinfo']) > * {
@@ -9587,7 +9599,6 @@ header[role='banner'] {
9587
9599
  padding-inline-start: var(--content-padding);
9588
9600
  padding-inline-end: var(--content-padding);
9589
9601
 
9590
- /* Theme-aware styling - 100% controlled by theme system */
9591
9602
  background: var(--background);
9592
9603
  border-bottom: var(--border-hairline) solid var(--border);
9593
9604
 
@@ -9936,7 +9947,6 @@ footer[role='contentinfo'] {
9936
9947
  min-height: var(--footer-height);
9937
9948
  z-index: var(--z-footer);
9938
9949
 
9939
- /* Theme-aware styling - 100% controlled by theme system */
9940
9950
  background: var(--background);
9941
9951
  border-top: var(--border-hairline) solid var(--border);
9942
9952
 
@@ -12,9 +12,9 @@ declare const DEGRADED_FORM_STORE_STATE: {
12
12
  readonly setError: () => void;
13
13
  readonly reset: () => void;
14
14
  readonly cleanup: () => void;
15
- readonly setIsDirty: () => void;
15
+ readonly setIsDirty: (_formId: string, _isDirty: boolean) => void;
16
16
  readonly hasDirtyForms: () => boolean;
17
- readonly getDirtyFormIds: () => never[];
17
+ readonly getDirtyFormIds: () => string[];
18
18
  readonly getStatus: () => "idle";
19
19
  readonly isLoading: () => boolean;
20
20
  readonly getUploadProgress: () => number;
@@ -44,18 +44,5 @@ export declare function useFormStoreSafe<T>(selector: (state: FormStoreLike) =>
44
44
  export declare namespace useFormStoreSafe {
45
45
  var getState: () => typeof DEGRADED_FORM_STORE_STATE;
46
46
  }
47
- /**
48
- * Check if any forms are dirty (safe version).
49
- * Returns false if CRUD package not available.
50
- */
51
- export declare function useHasDirtyFormsSafe(): boolean;
52
- /**
53
- * Check if navigation should be blocked (safe version).
54
- * Returns a function that checks FormStore and shows confirmation if needed.
55
- *
56
- * @param message - Confirmation message
57
- * @returns Promise<boolean> - true if navigation should proceed
58
- */
59
- export declare function checkFormNavigationSafe(message?: string): Promise<boolean>;
60
47
  export {};
61
48
  //# sourceMappingURL=useFormStoreSafe.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useFormStoreSafe.d.ts","sourceRoot":"","sources":["../../src/utils/useFormStoreSafe.ts"],"names":[],"mappings":"AAwBA;;;GAGG;AACH,QAAA,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;CAqBrB,CAAC;AAEX,sFAAsF;AACtF,KAAK,aAAa,GAAG,OAAO,yBAAyB,CAAC;AAQtD;;GAEG;AACH,eAAO,MAAM,oBAAoB,SAAyC,CAAC;AAE3E;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,CAAC,GAAG,CAAC,CAK5E;yBALe,gBAAgB;wBAmBA,OAAO,yBAAyB;;AAOhE;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,SAA4C,GAClD,OAAO,CAAC,OAAO,CAAC,CAUlB"}
1
+ {"version":3,"file":"useFormStoreSafe.d.ts","sourceRoot":"","sources":["../../src/utils/useFormStoreSafe.ts"],"names":[],"mappings":"AAwBA;;;GAGG;AACH,QAAA,MAAM,yBAAyB;;;;;;;;;;mCAYP,MAAM,YAAY,OAAO;;oCAElB,MAAM,EAAE;;;;;;CAO7B,CAAC;AAEX,sFAAsF;AACtF,KAAK,aAAa,GAAG,OAAO,yBAAyB,CAAC;AAQtD;;GAEG;AACH,eAAO,MAAM,oBAAoB,SAAyC,CAAC;AAE3E;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,CAAC,GAAG,CAAC,CAK5E;yBALe,gBAAgB;wBAaA,OAAO,yBAAyB"}
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * @see packages/ui/src/utils/useAuthSafe.ts for pattern reference
13
13
  * @see docs/development/GRACEFUL_DEGRADATION.md
14
- * @version 0.0.1
14
+ * @version 0.0.2
15
15
  * @since 0.0.1
16
16
  * @author AMBROISE PARK Consulting
17
17
  */
@@ -34,8 +34,8 @@ const DEGRADED_FORM_STORE_STATE = {
34
34
  reset: () => { },
35
35
  cleanup: () => { },
36
36
  // Dirty state management
37
- setIsDirty: () => { },
38
- hasDirtyForms: () => false, // Key method - returns false when degraded
37
+ setIsDirty: (_formId, _isDirty) => { },
38
+ hasDirtyForms: () => false,
39
39
  getDirtyFormIds: () => [],
40
40
  // Getters
41
41
  getStatus: () => 'idle',
@@ -75,12 +75,6 @@ export function useFormStoreSafe(selector) {
75
75
  * Use this in non-React contexts or for one-time checks.
76
76
  *
77
77
  * @returns FormStore state, or degraded state if CRUD not available
78
- *
79
- * @example
80
- * ```tsx
81
- * const state = useFormStoreSafe.getState();
82
- * const hasDirty = state.hasDirtyForms(); // Always safe to call
83
- * ```
84
78
  */
85
79
  useFormStoreSafe.getState = () => {
86
80
  if (realUseFormStore && typeof realUseFormStore.getState === 'function') {
@@ -88,27 +82,3 @@ useFormStoreSafe.getState = () => {
88
82
  }
89
83
  return DEGRADED_FORM_STORE_STATE;
90
84
  };
91
- /**
92
- * Check if any forms are dirty (safe version).
93
- * Returns false if CRUD package not available.
94
- */
95
- export function useHasDirtyFormsSafe() {
96
- return useFormStoreSafe((state) => state.hasDirtyForms());
97
- }
98
- /**
99
- * Check if navigation should be blocked (safe version).
100
- * Returns a function that checks FormStore and shows confirmation if needed.
101
- *
102
- * @param message - Confirmation message
103
- * @returns Promise<boolean> - true if navigation should proceed
104
- */
105
- export async function checkFormNavigationSafe(message = 'You have unsaved changes. Discard them?') {
106
- const state = useFormStoreSafe.getState();
107
- const hasDirtyForms = state.hasDirtyForms();
108
- if (!hasDirtyForms)
109
- return true; // No dirty forms = allow navigation
110
- if (typeof window !== 'undefined' && window.confirm) {
111
- return window.confirm(message);
112
- }
113
- return true;
114
- }
@@ -1 +1 @@
1
- {"version":3,"file":"RootLayout.d.ts","sourceRoot":"","sources":["../../src/vite-routing/RootLayout.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AA0LnD;;GAEG;AACH,UAAU,oBAAoB;IAC5B,2BAA2B;IAC3B,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,EAAE,oBAAoB,2CAgC1D"}
1
+ {"version":3,"file":"RootLayout.d.ts","sourceRoot":"","sources":["../../src/vite-routing/RootLayout.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAwLnD;;GAEG;AACH,UAAU,oBAAoB;IAC5B,2BAA2B;IAC3B,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,EAAE,oBAAoB,2CAgC1D"}
@@ -51,21 +51,17 @@ const RedirectOverlay = lazy(() => import('../components/common/RedirectOverlay'
51
51
  function CustomScrollRestoration() {
52
52
  const location = useLocation();
53
53
  const scrollPositions = useRef(new Map());
54
- const isFirstRender = useRef(true);
54
+ const prevLocationKey = useRef(null);
55
55
  useEffect(() => {
56
- // Skip on first render (initial page load)
57
- if (isFirstRender.current) {
58
- isFirstRender.current = false;
59
- return;
60
- }
61
56
  const mainElement = document.querySelector('main[role="main"]');
62
57
  if (!mainElement)
63
58
  return;
64
- // Save current scroll position before navigating
65
- const currentKey = location.key;
66
- // Check if this is a back/forward navigation (location.state can indicate this)
67
- // For now, we'll use a simple heuristic: if we have a saved position, it's likely POP
68
- const savedPosition = scrollPositions.current.get(currentKey);
59
+ // Save scroll position of the page we're leaving (Item 89)
60
+ if (prevLocationKey.current) {
61
+ scrollPositions.current.set(prevLocationKey.current, mainElement.scrollTop);
62
+ }
63
+ // Check if this is a back/forward navigation (POP) by checking saved position
64
+ const savedPosition = scrollPositions.current.get(location.key);
69
65
  if (savedPosition !== undefined) {
70
66
  // POP navigation - restore scroll position
71
67
  requestAnimationFrame(() => {
@@ -75,11 +71,10 @@ function CustomScrollRestoration() {
75
71
  else {
76
72
  // PUSH navigation - scroll to top
77
73
  mainElement.scrollTop = 0;
78
- // Save this position for potential back navigation
79
- scrollPositions.current.set(currentKey, 0);
80
74
  }
81
- // Cleanup: Keep only last 10 positions to prevent memory leaks
82
- if (scrollPositions.current.size > 10) {
75
+ prevLocationKey.current = location.key;
76
+ // Cleanup: Keep only last 20 positions to prevent memory leaks
77
+ if (scrollPositions.current.size > 20) {
83
78
  const firstKey = scrollPositions.current.keys().next().value;
84
79
  if (firstKey) {
85
80
  scrollPositions.current.delete(firstKey);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@donotdev/ui",
3
- "version": "0.0.14",
3
+ "version": "0.0.15",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -44,7 +44,7 @@
44
44
  "scripts": {
45
45
  "dev": "tsc --noEmit --watch --listFiles false --listEmittedFiles false",
46
46
  "clean": "rimraf dist tsconfig.tsbuildinfo",
47
- "type-check": "tsc --noEmit"
47
+ "type-check": "bunx tsc --noEmit"
48
48
  },
49
49
  "dependencies": {
50
50
  "@fontsource/inter": "^5.2.8",
@@ -56,14 +56,14 @@
56
56
  "react-hook-form": "^7.71.1"
57
57
  },
58
58
  "peerDependencies": {
59
- "@donotdev/adv-comps": "^0.0.11",
60
- "@donotdev/auth": "^0.0.7",
61
- "@donotdev/billing": "^0.0.6",
62
- "@donotdev/components": "^0.0.17",
63
- "@donotdev/core": "^0.0.24",
64
- "@donotdev/crud": "^0.0.15",
65
- "@donotdev/firebase": "^0.0.10",
66
- "@donotdev/oauth": "^0.0.6",
59
+ "@donotdev/adv-comps": "^0.0.13",
60
+ "@donotdev/auth": "^0.0.9",
61
+ "@donotdev/billing": "^0.0.8",
62
+ "@donotdev/components": "^0.0.19",
63
+ "@donotdev/core": "^0.0.25",
64
+ "@donotdev/crud": "^0.0.16",
65
+ "@donotdev/firebase": "^0.0.12",
66
+ "@donotdev/oauth": "^0.0.8",
67
67
  "firebase": "^12.9.0",
68
68
  "lucide-react": "^0.574.0",
69
69
  "react": "^19.2.4",