Update MUI Joy package version and refactor z-index usage across components

This commit is contained in:
Mo Tarbin
2026-08-13 19:24:23 -04:00
parent fb4d4efe66
commit 462af85a38
12 changed files with 39 additions and 84 deletions

2
package-lock.json generated
View File

@@ -38,7 +38,7 @@
"@jcesarmobile/capacitor-ocr": "^0.3.0", "@jcesarmobile/capacitor-ocr": "^0.3.0",
"@meauxt/react-swipeable-list": "^1.0.0", "@meauxt/react-swipeable-list": "^1.0.0",
"@mui/icons-material": "^5.16.13", "@mui/icons-material": "^5.16.13",
"@mui/joy": "^5.0.0-beta.20", "@mui/joy": "5.0.0-beta.52",
"@mui/material": "^5.15.2", "@mui/material": "^5.15.2",
"@openreplay/tracker": "^14.0.4", "@openreplay/tracker": "^14.0.4",
"@revenuecat/purchases-capacitor": "^12.0.0", "@revenuecat/purchases-capacitor": "^12.0.0",

View File

@@ -70,7 +70,7 @@
"@jcesarmobile/capacitor-ocr": "^0.3.0", "@jcesarmobile/capacitor-ocr": "^0.3.0",
"@meauxt/react-swipeable-list": "^1.0.0", "@meauxt/react-swipeable-list": "^1.0.0",
"@mui/icons-material": "^5.16.13", "@mui/icons-material": "^5.16.13",
"@mui/joy": "^5.0.0-beta.20", "@mui/joy": "5.0.0-beta.52",
"@mui/material": "^5.15.2", "@mui/material": "^5.15.2",
"@openreplay/tracker": "^14.0.4", "@openreplay/tracker": "^14.0.4",
"@revenuecat/purchases-capacitor": "^12.0.0", "@revenuecat/purchases-capacitor": "^12.0.0",

View File

@@ -2,7 +2,6 @@ import { Close } from '@mui/icons-material'
import { Box, Divider, IconButton, Modal, Sheet, Typography } from '@mui/joy' import { Box, Divider, IconButton, Modal, Sheet, Typography } from '@mui/joy'
import useMediaQuery from '@mui/material/useMediaQuery' import useMediaQuery from '@mui/material/useMediaQuery'
import { forwardRef, useId } from 'react' import { forwardRef, useId } from 'react'
import { Z_INDEX } from '../../constants/zIndex'
const WIDTH_BY_SIZE = { const WIDTH_BY_SIZE = {
sm: 400, sm: 400,
@@ -27,6 +26,7 @@ const AppModal = forwardRef(
size = 'md', size = 'md',
fullWidth = true, fullWidth = true,
isMobile: isMobileProp, isMobile: isMobileProp,
keepMounted = false,
mobilePresentation = 'sheet', mobilePresentation = 'sheet',
role = 'dialog', role = 'dialog',
showCloseButton = true, showCloseButton = true,
@@ -63,9 +63,16 @@ const AppModal = forwardRef(
onClose={handleClose} onClose={handleClose}
aria-labelledby={titleId} aria-labelledby={titleId}
aria-describedby={descriptionId} aria-describedby={descriptionId}
keepMounted keepMounted={keepMounted}
sx={{ sx={{
zIndex: Z_INDEX.MODAL_BACKDROP, // Joy raises portaled listboxes above modals, but its selector misses
// Menu because Menu renders with role="menu". Keep the workaround in
// the modal primitive so individual menus never need a z-index.
...(open && {
'& ~ [role="menu"]': {
'--unstable_popup-zIndex': 'calc(var(--joy-zIndex-modal) + 1)',
},
}),
display: 'flex', display: 'flex',
alignItems: isSheet ? 'flex-end' : 'center', alignItems: isSheet ? 'flex-end' : 'center',
justifyContent: 'center', justifyContent: 'center',
@@ -85,7 +92,6 @@ const AppModal = forwardRef(
aria-describedby={descriptionId} aria-describedby={descriptionId}
variant='outlined' variant='outlined'
sx={{ sx={{
zIndex: Z_INDEX.MODAL_CONTENT,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
width: isFullscreen width: isFullscreen

View File

@@ -1,38 +0,0 @@
// Z-index constants for consistent layering
// Lower values appear behind higher values
export const Z_INDEX = {
// Base layer (0-99)
BASE: 0,
CARD_OVERLAY: 1,
DROPDOWN_ITEM: 2,
TOOLTIP: 3,
// UI Components (100-999)
SAFE_AREA: 100,
CALENDAR: 110,
SMART_INPUT: 110,
AUTOCOMPLETE: 200,
// Navigation (1000-1999)
NAVBAR: 1000,
DRAWER: 999,
// Modals and Overlays (2000-8999)
MODAL_BACKDROP: 2000,
MODAL_CONTENT: 2001,
MODAL_CLOSE_BUTTON: 2002,
// Popups that must float above open modals (portaled to document.body)
MODAL_POPOVER: 2100,
TOAST: 3000,
// Critical System UI (9000-9999)
LOADING_SCREEN: 9000,
ALERTS: 9500,
NETWORK_BANNER: 9600,
// Maximum (10000+) - Reserved for absolute emergencies
EMERGENCY: 10000,
}
export default Z_INDEX

View File

@@ -1,7 +1,6 @@
import { Alert, Box } from '@mui/joy' import { Alert, Box } from '@mui/joy'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { createContext, useCallback, useContext, useState } from 'react' import { createContext, useCallback, useContext, useState } from 'react'
import Z_INDEX from '../constants/zIndex'
const FADE_DURATION = 400 // ms const FADE_DURATION = 400 // ms
const ALERT_DURATION = 5000 // ms const ALERT_DURATION = 5000 // ms
@@ -48,7 +47,7 @@ export const AlertsProvider = ({ children }) => {
top: 0, top: 0,
left: 0, left: 0,
width: '100%', width: '100%',
zIndex: Z_INDEX.ALERTS, zIndex: 'var(--joy-zIndex-snackbar)',
overflow: 'hidden', overflow: 'hidden',
}} }}
> >

View File

@@ -38,16 +38,17 @@ import {
ButtonGroup, ButtonGroup,
Chip, Chip,
Divider, Divider,
Dropdown,
IconButton, IconButton,
Input, Input,
Menu, Menu,
MenuButton,
MenuItem, MenuItem,
Typography, Typography,
} from '@mui/joy' } from '@mui/joy'
import { useEffect, useRef, useState } from 'react' import { useEffect, useState } from 'react'
import AppModal from '../../../components/common/AppModal' import AppModal from '../../../components/common/AppModal'
import ActiveFilterChips from '../../../components/common/filter/ActiveFilterChips' import ActiveFilterChips from '../../../components/common/filter/ActiveFilterChips'
import { Z_INDEX } from '../../../constants/zIndex'
import KeyboardShortcutHint from '../../../components/common/KeyboardShortcutHint' import KeyboardShortcutHint from '../../../components/common/KeyboardShortcutHint'
import { FILTER_COLORS } from '../../../utils/Colors' import { FILTER_COLORS } from '../../../utils/Colors'
import Priorities from '../../../utils/Priorities' import Priorities from '../../../utils/Priorities'
@@ -229,9 +230,8 @@ const ChoreToolbar = ({
const [localSelections, setLocalSelections] = useState(defaultSelections()) const [localSelections, setLocalSelections] = useState(defaultSelections())
const [savingFilter, setSavingFilter] = useState(false) const [savingFilter, setSavingFilter] = useState(false)
const [saveFilterName, setSaveFilterName] = useState('') const [saveFilterName, setSaveFilterName] = useState('')
const [saveMenuAnchorEl, setSaveMenuAnchorEl] = useState(null) const [saveMenuOpen, setSaveMenuOpen] = useState(false)
const [editingSavedFilter, setEditingSavedFilter] = useState(null) const [editingSavedFilter, setEditingSavedFilter] = useState(null)
const saveMenuRef = useRef(null)
const activeConditions = selectionsToConditions(localSelections) const activeConditions = selectionsToConditions(localSelections)
// ── badge counts ───────────────────────────────────────────────────────────── // ── badge counts ─────────────────────────────────────────────────────────────
@@ -419,13 +419,13 @@ const ChoreToolbar = ({
} }
setSavingFilter(false) setSavingFilter(false)
setSaveFilterName('') setSaveFilterName('')
setSaveMenuAnchorEl(null) setSaveMenuOpen(false)
setFilterSheetOpen(true) setFilterSheetOpen(true)
} }
useEffect(() => { useEffect(() => {
if (!filterSheetOpen || savingFilter || activeConditions.length === 0) { if (!filterSheetOpen || savingFilter || activeConditions.length === 0) {
setSaveMenuAnchorEl(null) setSaveMenuOpen(false)
} }
}, [filterSheetOpen, savingFilter, activeConditions.length]) }, [filterSheetOpen, savingFilter, activeConditions.length])
@@ -504,7 +504,7 @@ const ChoreToolbar = ({
onFilterSaved?.(editingSavedFilter.name) onFilterSaved?.(editingSavedFilter.name)
}) })
setSaveMenuAnchorEl(null) setSaveMenuOpen(false)
setFilterSheetOpen(false) setFilterSheetOpen(false)
} }
@@ -681,7 +681,7 @@ const ChoreToolbar = ({
open={filterSheetOpen} open={filterSheetOpen}
isMobile isMobile
onClose={() => { onClose={() => {
setSaveMenuAnchorEl(null) setSaveMenuOpen(false)
setFilterSheetOpen(false) setFilterSheetOpen(false)
}} }}
maxHeight='92vh' maxHeight='92vh'
@@ -747,7 +747,7 @@ const ChoreToolbar = ({
disabled={!hasAnyActive && activeConditions.length === 0} disabled={!hasAnyActive && activeConditions.length === 0}
onClick={() => { onClick={() => {
setLocalSelections(defaultSelections()) setLocalSelections(defaultSelections())
setSaveMenuAnchorEl(null) setSaveMenuOpen(false)
onClearAllFilters?.() onClearAllFilters?.()
setFilterSheetOpen(false) setFilterSheetOpen(false)
}} }}
@@ -756,33 +756,29 @@ const ChoreToolbar = ({
</Button> </Button>
{activeConditions.length > 0 ? ( {activeConditions.length > 0 ? (
<> <Dropdown
open={saveMenuOpen}
onOpenChange={(_event, isOpen) => setSaveMenuOpen(isOpen)}
>
<ButtonGroup variant='solid' color='primary'> <ButtonGroup variant='solid' color='primary'>
<Button <Button
onClick={() => { onClick={() => {
setSaveMenuAnchorEl(null) setSaveMenuOpen(false)
setFilterSheetOpen(false) setFilterSheetOpen(false)
}} }}
sx={{ minWidth: 140 }} sx={{ minWidth: 140 }}
> >
{resultCount != null ? `Show ${resultCount}` : 'Done'} {resultCount != null ? `Show ${resultCount}` : 'Done'}
</Button> </Button>
<IconButton <MenuButton
ref={saveMenuRef} slots={{ root: IconButton }}
aria-label='More save options' aria-label='More save options'
onClick={e => setSaveMenuAnchorEl(e.currentTarget)}
> >
<ArrowDropDown /> <ArrowDropDown />
</IconButton> </MenuButton>
</ButtonGroup> </ButtonGroup>
<Menu <Menu placement='top-end'>
anchorEl={saveMenuAnchorEl}
open={Boolean(saveMenuAnchorEl)}
onClose={() => setSaveMenuAnchorEl(null)}
placement='top-end'
sx={{ zIndex: Z_INDEX.MODAL_CONTENT + 10 }}
>
<MenuItem <MenuItem
onClick={handleUpdateFilter} onClick={handleUpdateFilter}
disabled={!editingSavedFilter} disabled={!editingSavedFilter}
@@ -792,7 +788,7 @@ const ChoreToolbar = ({
</MenuItem> </MenuItem>
<MenuItem <MenuItem
onClick={() => { onClick={() => {
setSaveMenuAnchorEl(null) setSaveMenuOpen(false)
setSaveFilterName( setSaveFilterName(
editingSavedFilter editingSavedFilter
? `${editingSavedFilter.name} Copy` ? `${editingSavedFilter.name} Copy`
@@ -805,13 +801,13 @@ const ChoreToolbar = ({
Save as New Filter Save as New Filter
</MenuItem> </MenuItem>
</Menu> </Menu>
</> </Dropdown>
) : ( ) : (
<Button <Button
variant='solid' variant='solid'
color='primary' color='primary'
onClick={() => { onClick={() => {
setSaveMenuAnchorEl(null) setSaveMenuOpen(false)
setFilterSheetOpen(false) setFilterSheetOpen(false)
}} }}
sx={{ minWidth: 140 }} sx={{ minWidth: 140 }}

View File

@@ -3,8 +3,6 @@ import { Add } from '@mui/icons-material'
import { Divider, Menu, MenuItem } from '@mui/joy' import { Divider, Menu, MenuItem } from '@mui/joy'
import React, { useEffect } from 'react' import React, { useEffect } from 'react'
import { Z_INDEX } from '../../constants/zIndex'
const AutocompleteDropdown = ({ const AutocompleteDropdown = ({
currentValue, currentValue,
onCreateSuggestion, // Called when the "Create new" row is chosen onCreateSuggestion, // Called when the "Create new" row is chosen
@@ -62,7 +60,6 @@ const AutocompleteDropdown = ({
position: 'relative', position: 'relative',
bottom: 0, bottom: 0,
left: 0, left: 0,
zIndex: Z_INDEX.MODAL_POPOVER,
}} }}
> >
{filteredOptions.map((option, index) => ( {filteredOptions.map((option, index) => (

View File

@@ -18,7 +18,6 @@ import { flushSync } from 'react-dom'
import KeyboardShortcutHint from '../../components/common/KeyboardShortcutHint' import KeyboardShortcutHint from '../../components/common/KeyboardShortcutHint'
import ModalActions from '../../components/common/ModalActions' import ModalActions from '../../components/common/ModalActions'
import { Z_INDEX } from '../../constants/zIndex'
import { useDocumentScanner } from '../../hooks/useDocumentScanner' import { useDocumentScanner } from '../../hooks/useDocumentScanner'
import { useFileUpload } from '../../hooks/useFileUpload' import { useFileUpload } from '../../hooks/useFileUpload'
import { useResponsiveModal } from '../../hooks/useResponsiveModal' import { useResponsiveModal } from '../../hooks/useResponsiveModal'

View File

@@ -18,7 +18,6 @@ import {
import { ClickAwayListener, Popper } from '@mui/material' import { ClickAwayListener, Popper } from '@mui/material'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { Z_INDEX } from '../../constants/zIndex'
import { useDocumentScanner } from '../../hooks/useDocumentScanner' import { useDocumentScanner } from '../../hooks/useDocumentScanner'
import { useFileUpload } from '../../hooks/useFileUpload' import { useFileUpload } from '../../hooks/useFileUpload'
import { useNotification } from '../../service/NotificationProvider' import { useNotification } from '../../service/NotificationProvider'
@@ -223,7 +222,7 @@ const AttachmentPickerField = ({
options: { fallbackPlacements: ['bottom-start', 'top-start'] }, options: { fallbackPlacements: ['bottom-start', 'top-start'] },
}, },
]} ]}
sx={{ zIndex: Z_INDEX.MODAL_CLOSE_BUTTON + 1 }} sx={{ zIndex: 'calc(var(--joy-zIndex-modal) + 1)' }}
> >
<ClickAwayListener onClickAway={() => setIsOpen(false)}> <ClickAwayListener onClickAway={() => setIsOpen(false)}>
<Sheet <Sheet

View File

@@ -2,7 +2,6 @@ import { Close } from '@mui/icons-material'
import { Box, Button, IconButton, Sheet, Typography } from '@mui/joy' import { Box, Button, IconButton, Sheet, Typography } from '@mui/joy'
import { ClickAwayListener, Popper } from '@mui/material' import { ClickAwayListener, Popper } from '@mui/material'
import { useEffect, useMemo, useRef, useState } from 'react' import { useEffect, useMemo, useRef, useState } from 'react'
import { Z_INDEX } from '../../constants/zIndex'
const BaseOptionPicker = ({ const BaseOptionPicker = ({
items = [], items = [],
@@ -199,7 +198,7 @@ const BaseOptionPicker = ({
}, },
}, },
]} ]}
sx={{ zIndex: Z_INDEX.MODAL_CLOSE_BUTTON + 1 }} sx={{ zIndex: 'calc(var(--joy-zIndex-modal) + 1)' }}
> >
<ClickAwayListener onClickAway={() => setIsOpen(false)}> <ClickAwayListener onClickAway={() => setIsOpen(false)}>
<Sheet <Sheet

View File

@@ -31,7 +31,6 @@ import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'
import { version } from '../../../package.json' import { version } from '../../../package.json'
import UserProfileAvatar from '../../components/UserProfileAvatar' import UserProfileAvatar from '../../components/UserProfileAvatar'
import Z_INDEX from '../../constants/zIndex'
import { useLocalization } from '../../contexts/LocalizationContext' import { useLocalization } from '../../contexts/LocalizationContext'
import { useResource } from '../../queries/ResourceQueries' import { useResource } from '../../queries/ResourceQueries'
import { useGlobalSearch } from '../../search/GlobalSearchContext' import { useGlobalSearch } from '../../search/GlobalSearchContext'
@@ -211,7 +210,7 @@ const NavBar = () => {
? `calc(var(--safe-area-inset-top, 0px))` ? `calc(var(--safe-area-inset-top, 0px))`
: '', : '',
position: 'sticky', position: 'sticky',
zIndex: Z_INDEX.NAVBAR, zIndex: 'var(--joy-zIndex-popup)',
top: 0, top: 0,
minHeight: '35px', minHeight: '35px',
backgroundColor: 'var(--joy-palette-background-body)', backgroundColor: 'var(--joy-palette-background-body)',
@@ -239,7 +238,6 @@ const NavBar = () => {
// height: // height:
// 'calc(100vh - var(--safe-area-inset-top, 0px) - var(--safe-area-inset-bottom, 0px))', // 'calc(100vh - var(--safe-area-inset-top, 0px) - var(--safe-area-inset-bottom, 0px))',
overflow: 'auto', overflow: 'auto',
zIndex: Z_INDEX.DRAWER,
}, },
}} }}
> >

View File

@@ -1,7 +1,7 @@
import { WifiOff } from '@mui/icons-material' import { WifiOff } from '@mui/icons-material'
import { Alert, Box } from '@mui/joy' import { Alert, Box } from '@mui/joy'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import Z_INDEX from '../../constants/zIndex'
import { networkManager } from '../../hooks/NetworkManager' import { networkManager } from '../../hooks/NetworkManager'
const NetworkBanner = () => { const NetworkBanner = () => {
@@ -56,7 +56,7 @@ const NetworkBanner = () => {
paddingTop: `calc(var(--safe-area-inset-top, 0px))`, paddingTop: `calc(var(--safe-area-inset-top, 0px))`,
top: 0, top: 0,
left: 0, left: 0,
zIndex: Z_INDEX.NETWORK_BANNER, zIndex: 'var(--joy-zIndex-snackbar)',
pt: `calc( env(safe-area-inset-top, 0px))`, pt: `calc( env(safe-area-inset-top, 0px))`,
width: '100%', width: '100%',
justifyContent: 'center', justifyContent: 'center',