diff --git a/frontend/package.json b/frontend/package.json index ffda4b9..cdd248f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "homehub-frontend", "private": true, - "version": "0.4.7", + "version": "0.4.8", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/components/layout/BottomNav.tsx b/frontend/src/components/layout/BottomNav.tsx index 1f6fb4b..9440894 100644 --- a/frontend/src/components/layout/BottomNav.tsx +++ b/frontend/src/components/layout/BottomNav.tsx @@ -1,4 +1,5 @@ import { NavLink } from 'react-router-dom' +import { useActionButton } from '../../contexts/ActionButtonContext' const NAV_ITEMS = [ { to: '/', icon: 'house', label: 'Accueil' }, @@ -8,12 +9,16 @@ const NAV_ITEMS = [ ] export default function BottomNav() { + const { actionButton } = useActionButton() + return ( ) } diff --git a/frontend/src/components/layout/Layout.tsx b/frontend/src/components/layout/Layout.tsx index 9e808ed..12b1ed7 100644 --- a/frontend/src/components/layout/Layout.tsx +++ b/frontend/src/components/layout/Layout.tsx @@ -1,24 +1,27 @@ import { Outlet } from 'react-router-dom' import BottomNav from './BottomNav' import SideNav from './SideNav' +import { ActionButtonProvider } from '../../contexts/ActionButtonContext' export default function Layout() { return ( -
- {/* Sidebar — visible uniquement sur laptop (lg et +) */} -
- -
+ +
+ {/* Sidebar — visible uniquement sur laptop (lg et +) */} +
+ +
- {/* Contenu principal */} -
- -
+ {/* Contenu principal */} +
+ +
- {/* Navigation bas — visible uniquement sur mobile */} -
- + {/* Navigation bas — visible uniquement sur mobile */} +
+ +
-
+
) } diff --git a/frontend/src/contexts/ActionButtonContext.tsx b/frontend/src/contexts/ActionButtonContext.tsx new file mode 100644 index 0000000..dcd5394 --- /dev/null +++ b/frontend/src/contexts/ActionButtonContext.tsx @@ -0,0 +1,25 @@ +import { createContext, useContext, useState } from 'react' +import type { ReactNode } from 'react' + +interface ActionButtonContextType { + actionButton: ReactNode + setActionButton: (node: ReactNode) => void +} + +const ActionButtonContext = createContext({ + actionButton: null, + setActionButton: () => {}, +}) + +export function ActionButtonProvider({ children }: { children: ReactNode }) { + const [actionButton, setActionButton] = useState(null) + return ( + + {children} + + ) +} + +export function useActionButton() { + return useContext(ActionButtonContext) +} diff --git a/frontend/src/pages/NotesPage.tsx b/frontend/src/pages/NotesPage.tsx index 96efdcf..a800163 100644 --- a/frontend/src/pages/NotesPage.tsx +++ b/frontend/src/pages/NotesPage.tsx @@ -3,6 +3,7 @@ import type { Note, NoteFilters } from '../api/notes' import { fetchNotes, createNote, updateNote, deleteNote, addAttachment, deleteAttachment } from '../api/notes' import Modal from '../components/Modal' import NoteForm from '../components/notes/NoteForm' +import { useActionButton } from '../contexts/ActionButtonContext' const noSelect: React.CSSProperties = { userSelect: 'none' } @@ -163,6 +164,24 @@ export default function NotesPage() { const [searchInput, setSearchInput] = useState('') const searchTimer = useRef | null>(null) + const { setActionButton } = useActionButton() + useEffect(() => { + setActionButton( + + ) + return () => setActionButton(null) + }, [setActionButton]) + const load = useCallback(async () => { setLoading(true) setError(null) @@ -348,20 +367,6 @@ export default function NotesPage() {
- {/* FAB */} - ) } diff --git a/frontend/src/pages/ShoppingPage.tsx b/frontend/src/pages/ShoppingPage.tsx index 91e0b69..e375865 100644 --- a/frontend/src/pages/ShoppingPage.tsx +++ b/frontend/src/pages/ShoppingPage.tsx @@ -1,5 +1,5 @@ // frontend/src/pages/ShoppingPage.tsx -import { useState, useEffect, useCallback } from 'react' +import { useState, useEffect, useCallback, useRef } from 'react' import { matchesSearch } from '../utils/search' import type { ShoppingListDetail, ShoppingList, Store, Product, ShoppingItem } from '../api/shopping' import { @@ -13,6 +13,7 @@ import ItemRow from '../components/shopping/ItemRow' import CatalogueModal from '../components/shopping/CatalogueModal' import BoutiquesModal from '../components/shopping/BoutiquesModal' import { useWakeLock } from '../hooks/useWakeLock' +import { useActionButton } from '../contexts/ActionButtonContext' const inputStyle: React.CSSProperties = { width: '100%', @@ -76,6 +77,33 @@ export default function ShoppingPage() { useWakeLock(currentList !== null) + const { setActionButton } = useActionButton() + const openAddSheetRef = useRef(openAddSheet) + useEffect(() => { openAddSheetRef.current = openAddSheet }) + + useEffect(() => { + if (!currentList || showAddSheet) { + setActionButton(null) + return + } + setActionButton( + + ) + return () => setActionButton(null) + }, [currentList, showAddSheet, setActionButton]) + const loadData = useCallback(async () => { setLoading(true) setError(null) @@ -526,22 +554,6 @@ export default function ShoppingPage() { )} - {/* FAB — masqué quand le sheet est ouvert */} - {!showAddSheet && ( - - )} )} diff --git a/frontend/src/pages/TodosPage.tsx b/frontend/src/pages/TodosPage.tsx index 26d1c4f..5ed20fa 100644 --- a/frontend/src/pages/TodosPage.tsx +++ b/frontend/src/pages/TodosPage.tsx @@ -5,6 +5,7 @@ import { fetchTodos, createTodo, updateTodo, deleteTodo, postponeTodo } from '.. import SwipeableRow from '../components/todos/SwipeableRow' import TodoForm, { DOMAIN_COLORS } from '../components/todos/TodoForm' import Modal from '../components/Modal' +import { useActionButton } from '../contexts/ActionButtonContext' type EditingTodo = Todo | null @@ -41,6 +42,24 @@ export default function TodosPage() { const [editingTodo, setEditingTodo] = useState(null) const [filters, setFilters] = useState({ status: 'pending' }) + const { setActionButton } = useActionButton() + useEffect(() => { + setActionButton( + + ) + return () => setActionButton(null) + }, [setActionButton]) + const load = useCallback(async () => { setLoading(true) setError(null) @@ -395,27 +414,6 @@ export default function TodosPage() { )} - {/* FAB création — mobile : au-dessus de la barre de nav ; laptop : coin bas-droit */} - ) }