@@ -5,8 +5,8 @@ import { ConfigProvider } from 'reka-ui';
55import SessionExpiry from ' @/components/SessionExpiry.vue' ;
66import LicensingAlert from ' @/components/LicensingAlert.vue' ;
77import PortalTargets from ' @/components/portals/PortalTargets.vue' ;
8- import Tooltips from ' @/components/Tooltips. vue' ;
9- import { provide , watch , ref } from ' vue ' ;
8+ import { provide , watch , ref , onMounted , onUnmounted , nextTick } from ' vue' ;
9+ import { router } from ' @inertiajs/vue3 ' ;
1010import useBodyClasses from ' ./body-classes.js' ;
1111import useStatamicPageProps from ' @/composables/page-props.js' ;
1212import useMaxWidthToggle from ' @/composables/use-max-width-toggle.js' ;
@@ -27,6 +27,49 @@ provide('layout', {
2727 isMaxWidthEnabled,
2828 toggleMaxWidth: toggle,
2929});
30+
31+ // Focus management: focus main element if no input has auto-focus
32+ let navigationListener = null ;
33+
34+ function focusMain () {
35+ // Wait for components to mount and autofocus to process
36+ nextTick (() => {
37+ requestAnimationFrame (() => {
38+ setTimeout (() => {
39+ // If an input is already focused, we're done
40+ if (document .activeElement ? .matches (' input, textarea, select, [contenteditable]' )) {
41+ return ;
42+ }
43+
44+ // Find any input with autofocus attribute (including nested in UI components)
45+ const autofocusInput = document .querySelector (' input[autofocus], textarea[autofocus], select[autofocus]' ) ||
46+ document .querySelector (' [data-ui-input] input[autofocus]' );
47+
48+ // If autofocus input exists but isn't focused, focus it manually
49+ if (autofocusInput && document .activeElement !== autofocusInput) {
50+ autofocusInput .focus ();
51+ return ;
52+ }
53+
54+ // Otherwise, focus the content card
55+ if (! autofocusInput) {
56+ document .querySelector (' #content-card' )? .focus ();
57+ }
58+ }, 100 );
59+ });
60+ });
61+ }
62+
63+ onMounted (() => {
64+ navigationListener = router .on (' success' , focusMain);
65+ focusMain ();
66+ });
67+
68+ onUnmounted (() => {
69+ if (navigationListener) {
70+ navigationListener ();
71+ }
72+ });
3073< / script>
3174
3275< template>
@@ -38,8 +81,8 @@ provide('layout', {
3881 < main id= " main" class = " flex bg-body-bg dark:border-t dark:border-body-border rounded-t-2xl fixed top-14 inset-x-0 bottom-0 min-h-[calc(100vh-3.5rem)]" >
3982 < Nav / >
4083 <!-- The data attribute allows CSS to target elements when max- width is disabled. -->
41- <div id =" main-content" class =" main-content sm:p-2 h-full flex-1 overflow-y-auto rounded-t-2xl" :data-max-width-enabled =" isMaxWidthEnabled" >
42- <div id =" content-card" class =" relative content-card grid min-h-full mx-auto" >
84+ < div id= " main-content" class = " main-content sm:p-2 h-full flex-1 overflow-y-auto focus:outline-none rounded-t-2xl" : data- max- width- enabled= " isMaxWidthEnabled" >
85+ < div id= " content-card" tabindex = " -1 " class = " focus:outline-none relative content-card grid min-h-full mx-auto" >
4386 <!-- Data attribute used by the CSS style tag below to override max- width when disabled.-->
4487 < div class = " w-full mx-auto max-w-page" data- max- width- wrapper>
4588 < slot / >
@@ -74,9 +117,9 @@ provide('layout', {
74117< / template>
75118
76119< style>
77- /*
120+ /*
78121 Max-width override CSS:
79- When max-width is disabled (data-max-width-enabled="false"),
122+ When max-width is disabled (data-max-width-enabled="false"),
80123 this rule removes the max-width constraint from elements tagged with data-max-width-wrapper.
81124
82125 This allows the content to expand to full width when the toggle is disabled,
0 commit comments