From f98d0ae76338bb164a0ba7ea10193adda360c335 Mon Sep 17 00:00:00 2001 From: 0xzio Date: Tue, 20 May 2025 21:48:58 +0800 Subject: [PATCH] feat: improve mobile ui --- apps/mobile/src/App.tsx | 44 +++++++-------- apps/mobile/src/components/AreaList.tsx | 32 ++++++----- apps/mobile/src/components/Footer.tsx | 53 +++---------------- apps/mobile/src/components/Menu.tsx | 19 ++++++- apps/mobile/src/pages/PageCreation.tsx | 6 ++- apps/mobile/src/pages/PageHome.tsx | 17 +++--- packages/components/src/Creation/Creation.tsx | 6 +-- packages/components/src/Creation/PropList.tsx | 2 +- .../DashboardLayout/PanelHeaderWrapper.tsx | 2 + .../src/DashboardLayout/PanelItem.tsx | 3 +- .../panel-renderer/PanelCreation.tsx | 3 +- packages/components/src/QuickInput.tsx | 3 +- .../components/src/Sidebar/app-sidebar.tsx | 6 +-- .../src/area-widgets/AreaWidgets.tsx | 3 +- .../src/area-widgets/CreationItem.tsx | 11 ++-- .../src/area-widgets/WidgetItem.tsx | 16 +++++- packages/hooks/src/useMobileMenu.ts | 15 ++++++ packages/uikit/src/ui/dropdown-menu.tsx | 2 +- 18 files changed, 132 insertions(+), 111 deletions(-) create mode 100644 packages/hooks/src/useMobileMenu.ts diff --git a/apps/mobile/src/App.tsx b/apps/mobile/src/App.tsx index c8ea98ef..9763132e 100644 --- a/apps/mobile/src/App.tsx +++ b/apps/mobile/src/App.tsx @@ -1,5 +1,3 @@ -import { DndProvider } from 'react-dnd' -import { HTML5Backend } from 'react-dnd-html5-backend' import { Redirect, Route } from 'react-router-dom' import { DarkMode } from '@aparajita/capacitor-dark-mode' import { SafeArea } from '@capacitor-community/safe-area' @@ -30,6 +28,11 @@ import '@ionic/react/css/text-alignment.css' import '@ionic/react/css/text-transformation.css' import '@ionic/react/css/flex-utils.css' import '@ionic/react/css/display.css' +// +import 'react-grid-layout/css/styles.css' +import 'react-resizable/css/styles.css' +import 'react-datepicker/dist/react-datepicker.css' +import '@glideapps/glide-data-grid/dist/index.css' /** * Ionic Dark Mode * ----------------------------------------------------- @@ -142,26 +145,25 @@ const App: React.FC = () => { return ( +
- - - - - - - - - - - - }> - - - - - - - + + + + + + + + + + + }> + + + + + + ) diff --git a/apps/mobile/src/components/AreaList.tsx b/apps/mobile/src/components/AreaList.tsx index 3208eeab..c444f62d 100644 --- a/apps/mobile/src/components/AreaList.tsx +++ b/apps/mobile/src/components/AreaList.tsx @@ -51,7 +51,21 @@ export const AreaList: React.FC = () => { return (
-
My areas
+
+
My areas
+ + { + setIsOpen(true) + // menuController.close() + menuController.close('main') + }} + > + + + +
{areas.map((item) => ( @@ -81,22 +95,6 @@ export const AreaList: React.FC = () => { ))} - - - { - setIsOpen(true) - // menuController.close() - // menuController.close('main') - }} - > - -
- -
-
-
) diff --git a/apps/mobile/src/components/Footer.tsx b/apps/mobile/src/components/Footer.tsx index 36659852..3d213df3 100644 --- a/apps/mobile/src/components/Footer.tsx +++ b/apps/mobile/src/components/Footer.tsx @@ -3,7 +3,6 @@ import { useHomeTab } from '@/hooks/useHomeTab' import { IonFooter, IonToolbar } from '@ionic/react' import { PlusIcon } from 'lucide-react' import { Button } from '@penx/uikit/button' -import { cn } from '@penx/utils' interface Props { onAdd: () => void @@ -13,63 +12,27 @@ export const Footer = ({ onAdd }: Props) => { const { setType } = useHomeTab() return ( - + -
+
- - - -
diff --git a/apps/mobile/src/components/Menu.tsx b/apps/mobile/src/components/Menu.tsx index 381b3531..29e0c8bc 100644 --- a/apps/mobile/src/components/Menu.tsx +++ b/apps/mobile/src/components/Menu.tsx @@ -1,6 +1,10 @@ +import { useEffect, useRef } from 'react' import { Capacitor } from '@capacitor/core' import { IonContent, IonMenu } from '@ionic/react' +import { AreaWidgets } from '@penx/components/area-widgets/AreaWidgets' +import { useMobileMenu } from '@penx/hooks/useMobileMenu' import { useSession } from '@penx/session' +import { store } from '@penx/store' import { cn } from '@penx/utils' import { AreaList } from './AreaList' import { MobileModeToggle } from './MobileModeToggle' @@ -8,12 +12,22 @@ import { MobileModeToggle } from './MobileModeToggle' const platform = Capacitor.getPlatform() const Menu: React.FC = () => { + const { setMenu } = useMobileMenu() const { isLoading } = useSession() + const menu = useRef(null) - if (isLoading) return null + useEffect(() => { + setMenu(menu) + }, [menu]) return ( - +
{ >
+
diff --git a/apps/mobile/src/pages/PageCreation.tsx b/apps/mobile/src/pages/PageCreation.tsx index d5b73881..ab181467 100644 --- a/apps/mobile/src/pages/PageCreation.tsx +++ b/apps/mobile/src/pages/PageCreation.tsx @@ -1,4 +1,6 @@ import React, { useEffect, useRef, useState } from 'react' +import { DndProvider } from 'react-dnd' +import { HTML5Backend } from 'react-dnd-html5-backend' import { CreationMenu } from '@/components/CreationMenu' import { MobileCreation } from '@/components/MobileCreation' import { Capacitor } from '@capacitor/core' @@ -59,7 +61,9 @@ export const PageCreation = ({ - + + + ) diff --git a/apps/mobile/src/pages/PageHome.tsx b/apps/mobile/src/pages/PageHome.tsx index 63f51a8c..31fe5019 100644 --- a/apps/mobile/src/pages/PageHome.tsx +++ b/apps/mobile/src/pages/PageHome.tsx @@ -36,6 +36,7 @@ import { } from 'motion/react' import { EditWidgetButton } from '@penx/components/area-widgets/EditWidget/EditWidgetButton' import { AreaDialog } from '@penx/components/AreaDialog' +import { PanelList } from '@penx/components/DashboardLayout/PanelList' import { QuickInput } from '@penx/components/QuickInput' import { appEmitter } from '@penx/emitter' import { useArea } from '@penx/hooks/useArea' @@ -44,6 +45,7 @@ import { ICreationNode } from '@penx/model-type' import { useSession } from '@penx/session' import { Button } from '@penx/uikit/button' import { Separator } from '@penx/uikit/separator' +import { SidebarProvider } from '@penx/uikit/ui/sidebar' import { cn } from '@penx/utils' import { PageCreation } from './PageCreation' @@ -188,7 +190,7 @@ const PageHome: React.FC = ({ nav }: any) => { - + {/* */} @@ -201,17 +203,20 @@ const PageHome: React.FC = ({ nav }: any) => { */}
- {type === 'HOME' && } - {type === 'TASK' && } - {type === 'PROFILE' && - (session ? : )} + + + + {/* {type === 'HOME' && } */} + {/* {type === 'TASK' && } */} + {/* {type === 'PROFILE' && + (session ? : )} */}
diff --git a/packages/components/src/Creation/Creation.tsx b/packages/components/src/Creation/Creation.tsx index 8cc0e489..c4709103 100644 --- a/packages/components/src/Creation/Creation.tsx +++ b/packages/components/src/Creation/Creation.tsx @@ -21,7 +21,7 @@ import { useStructs } from '@penx/hooks/useStructs' import { ICreationNode } from '@penx/model-type' import { store } from '@penx/store' import { trpc } from '@penx/trpc-client' -import { StructType, Panel } from '@penx/types' +import { Panel, StructType } from '@penx/types' import { Checkbox } from '@penx/uikit/checkbox' import { Separator } from '@penx/uikit/separator' import { cn } from '@penx/utils' @@ -111,7 +111,7 @@ export function Creation({ panel, className, ref }: Props) {
{ @@ -187,7 +187,7 @@ export function Creation({ panel, className, ref }: Props) {
)} -
+
diff --git a/packages/components/src/Creation/PropList.tsx b/packages/components/src/Creation/PropList.tsx index a1c381a0..b55d0804 100644 --- a/packages/components/src/Creation/PropList.tsx +++ b/packages/components/src/Creation/PropList.tsx @@ -11,7 +11,7 @@ export const PropList = ({ onUpdateProps }: Props) => { const { structs } = useStructs() const struct = structs.find((m) => m.id === creation.structId)! - if (!struct.columns.length) return null + if (struct.columns.length < 2) return null return (
{struct.columns.map((column, i) => { diff --git a/packages/components/src/DashboardLayout/PanelHeaderWrapper.tsx b/packages/components/src/DashboardLayout/PanelHeaderWrapper.tsx index faac3b37..05b60729 100644 --- a/packages/components/src/DashboardLayout/PanelHeaderWrapper.tsx +++ b/packages/components/src/DashboardLayout/PanelHeaderWrapper.tsx @@ -1,6 +1,7 @@ 'use client' import { ReactNode } from 'react' +import { isMobileApp } from '@penx/constants' import { SidebarTrigger } from '@penx/uikit/sidebar' export function PanelHeaderWrapper({ @@ -10,6 +11,7 @@ export function PanelHeaderWrapper({ children: ReactNode index: number }) { + if (isMobileApp!) return null if (index === 0) return (
diff --git a/packages/components/src/DashboardLayout/PanelItem.tsx b/packages/components/src/DashboardLayout/PanelItem.tsx index cb0e56e9..5fe39ffe 100644 --- a/packages/components/src/DashboardLayout/PanelItem.tsx +++ b/packages/components/src/DashboardLayout/PanelItem.tsx @@ -1,5 +1,6 @@ 'use client' +import { isMobileApp } from '@penx/constants' import { Panel, PanelType } from '@penx/types' import { ResizableHandle, ResizablePanel } from '@penx/uikit/resizable' import { cn } from '@penx/utils' @@ -60,7 +61,7 @@ export function PanelItem({ {panel.type === PanelType.WIDGET && ( <> - + {!isMobileApp && } )} diff --git a/packages/components/src/DashboardLayout/panel-renderer/PanelCreation.tsx b/packages/components/src/DashboardLayout/panel-renderer/PanelCreation.tsx index b00324f8..16dac3a8 100644 --- a/packages/components/src/DashboardLayout/panel-renderer/PanelCreation.tsx +++ b/packages/components/src/DashboardLayout/panel-renderer/PanelCreation.tsx @@ -7,7 +7,7 @@ import { usePanelCreationContext, } from '@penx/components/PanelCreationProvider' import { PublishDialog } from '@penx/components/PublishDialog' -import { BUILTIN_PAGE_SLUGS, ROOT_DOMAIN } from '@penx/constants' +import { BUILTIN_PAGE_SLUGS, isMobileApp, ROOT_DOMAIN } from '@penx/constants' import { CreationStatus } from '@penx/db/client' import { getSiteDomain } from '@penx/libs/getSiteDomain' import { Panel } from '@penx/types' @@ -45,6 +45,7 @@ export function Content({ panel, index }: Props) {
+ ) diff --git a/packages/components/src/QuickInput.tsx b/packages/components/src/QuickInput.tsx index ba39ba5e..0258e892 100644 --- a/packages/components/src/QuickInput.tsx +++ b/packages/components/src/QuickInput.tsx @@ -21,7 +21,7 @@ import { toast } from 'sonner' import { useLocalStorage, useWindowSize } from 'usehooks-ts' import { useAddCreation } from '@penx/hooks/useAddCreation' import { store } from '@penx/store' -import { StructType, PanelType } from '@penx/types' +import { PanelType, StructType } from '@penx/types' import { Button } from '@penx/uikit/button' import { Checkbox } from '@penx/uikit/checkbox' import { Textarea } from '@penx/uikit/textarea' @@ -81,6 +81,7 @@ export function QuickInput({ addCreation({ type: StructType.NOTE, content: JSON.stringify(slateValue), + isAddPanel: false, }) afterSubmit?.() setInput('') diff --git a/packages/components/src/Sidebar/app-sidebar.tsx b/packages/components/src/Sidebar/app-sidebar.tsx index 7d29ada2..edb9a432 100644 --- a/packages/components/src/Sidebar/app-sidebar.tsx +++ b/packages/components/src/Sidebar/app-sidebar.tsx @@ -31,17 +31,15 @@ import { TooltipTrigger, } from '@penx/uikit/tooltip' import { useLoginDialog } from '@penx/widgets/LoginDialog/useLoginDialog' +import { AddCreationButton } from '../AddCreationButton' import { AreaWidgets } from '../area-widgets' import { AreasPopover } from '../AreasPopover/AreasPopover' import { ProfileButton } from '../ProfileButton' import { ImportPostEntry } from './ImportPostEntry' import { QuickSearchTrigger } from './QuickSearchTrigger' import { VisitSiteButton } from './VisitSiteButton' -import { AddCreationButton } from '../AddCreationButton' export function AppSidebar({ ...props }: React.ComponentProps) { - const { session } = useSession() - const { setIsOpen } = useLoginDialog() return ( @@ -50,7 +48,7 @@ export function AppSidebar({ ...props }: React.ComponentProps) { -
+
{!isMobileApp && }
diff --git a/packages/components/src/area-widgets/AreaWidgets.tsx b/packages/components/src/area-widgets/AreaWidgets.tsx index b973e075..69328bc2 100644 --- a/packages/components/src/area-widgets/AreaWidgets.tsx +++ b/packages/components/src/area-widgets/AreaWidgets.tsx @@ -15,7 +15,8 @@ export function AreaWidgets({}: Props) {
{isMobileApp && } {!isMobileApp && } - {!isMobileApp && } + {/* {!isMobileApp && } */} +
) diff --git a/packages/components/src/area-widgets/CreationItem.tsx b/packages/components/src/area-widgets/CreationItem.tsx index 5ec1069e..f628a22f 100644 --- a/packages/components/src/area-widgets/CreationItem.tsx +++ b/packages/components/src/area-widgets/CreationItem.tsx @@ -17,10 +17,11 @@ import { useArea } from '@penx/hooks/useArea' import { updateCreationProps } from '@penx/hooks/useCreation' import { creationIdAtom, useCreationId } from '@penx/hooks/useCreationId' import { useCreationStruct } from '@penx/hooks/useCreationStruct' +import { mobileMenuAtom, useMobileMenu } from '@penx/hooks/useMobileMenu' import { usePanels } from '@penx/hooks/usePanels' import { ICreationNode } from '@penx/model-type' import { store } from '@penx/store' -import { StructType, PanelType, SiteCreation } from '@penx/types' +import { PanelType, SiteCreation, StructType } from '@penx/types' import { Checkbox } from '@penx/uikit/checkbox' import { ContextMenu, @@ -41,6 +42,7 @@ export function CreationItem({ creation, className }: CreationItemProps) { const { isCreationInPanels } = usePanels() const { isAll, setVisible } = useIsAllContext() const struct = useCreationStruct(creation) + const { close } = useMobileMenu() const getTitleFromContent = () => { try { @@ -63,10 +65,11 @@ export function CreationItem({ creation, className }: CreationItemProps) { )} onClick={() => { if (isMobileApp) { - appEmitter.emit('ROUTE_TO_CREATION', creation) - store.set(creationIdAtom, creation.id) + // appEmitter.emit('ROUTE_TO_CREATION', creation) + // store.set(creationIdAtom, creation.id) + close() setVisible?.(false) - return + // return } store.panels.updateMainPanel({ diff --git a/packages/components/src/area-widgets/WidgetItem.tsx b/packages/components/src/area-widgets/WidgetItem.tsx index 75bff81f..0fc72b0f 100644 --- a/packages/components/src/area-widgets/WidgetItem.tsx +++ b/packages/components/src/area-widgets/WidgetItem.tsx @@ -7,11 +7,13 @@ import { AnimatePresence, motion } from 'motion/react' import { Drawer } from 'vaul' import { isMobileApp, WidgetType } from '@penx/constants' import { useArea } from '@penx/hooks/useArea' +import { useMobileMenu } from '@penx/hooks/useMobileMenu' import { useStructs } from '@penx/hooks/useStructs' import { store } from '@penx/store' -import { Widget } from '@penx/types' +import { PanelType, Widget } from '@penx/types' import { ContextMenu, ContextMenuTrigger } from '@penx/uikit/context-menu' import { DialogDescription, DialogTitle } from '@penx/uikit/dialog' +import { uniqueId } from '@penx/unique-id' import { cn } from '@penx/utils' import { WidgetIcon } from '@penx/widgets/WidgetIcon' import { WidgetName } from '@penx/widgets/WidgetName' @@ -65,6 +67,7 @@ export const WidgetItem = forwardRef( const { area } = useArea() const [visible, setVisible] = useState(false) const [struct, setStruct] = useState(null as any) + const { close } = useMobileMenu() // useEffect(() => { // if (!dragOverlay) { @@ -176,7 +179,16 @@ export const WidgetItem = forwardRef( {widget.type === WidgetType.ALL_STRUCTS ? ( { - setVisible(!visible) + if (isMobileApp) { + close() + store.panels.openWidgetPanel({ + id: uniqueId(), + type: PanelType.WIDGET, + structId: struct.id, + }) + } else { + setVisible(!visible) + } setStruct(struct) }} /> diff --git a/packages/hooks/src/useMobileMenu.ts b/packages/hooks/src/useMobileMenu.ts new file mode 100644 index 00000000..aee77758 --- /dev/null +++ b/packages/hooks/src/useMobileMenu.ts @@ -0,0 +1,15 @@ +import { atom, useAtom, useAtomValue } from 'jotai' + +export const mobileMenuAtom = atom({} as any) + +export function useMobileMenu() { + const [menu, setMenu] = useAtom(mobileMenuAtom) + return { + close: () => { + return menu.current?.close() + }, + open: () => menu.current?.open(), + menu, + setMenu, + } +} diff --git a/packages/uikit/src/ui/dropdown-menu.tsx b/packages/uikit/src/ui/dropdown-menu.tsx index 6a162434..13ad712d 100644 --- a/packages/uikit/src/ui/dropdown-menu.tsx +++ b/packages/uikit/src/ui/dropdown-menu.tsx @@ -41,7 +41,7 @@ function DropdownMenuContent({ data-slot="dropdown-menu-content" sideOffset={sideOffset} className={cn( - 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 max-h-(--radix-dropdown-menu-content-available-height) origin-(--radix-dropdown-menu-content-transform-origin) border-foreground/10 z-50 min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border p-1 shadow-md', + 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 max-h-(--radix-dropdown-menu-content-available-height) origin-(--radix-dropdown-menu-content-transform-origin) shadow-popover z-50 min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md p-1', className, )} {...props}