diff --git a/docs/registry/registry-ui.ts b/docs/registry/registry-ui.ts index 521b34268..6e44b6c50 100644 --- a/docs/registry/registry-ui.ts +++ b/docs/registry/registry-ui.ts @@ -7,8 +7,8 @@ import popoverPkg from "@seed-design/react-popover/package.json"; export const registryUI: RegistryUI = [ { name: "app-screen", - files: ["ui:app-screen.tsx"], - dependencies: ["@seed-design/stackflow"], + files: ["ui:app-screen.tsx", "ui:app-bar.tsx"], + dependencies: ["@seed-design/react", "@seed-design/stackflow"], }, { name: "error-state", diff --git a/docs/registry/ui/app-bar.tsx b/docs/registry/ui/app-bar.tsx new file mode 100644 index 000000000..832bca652 --- /dev/null +++ b/docs/registry/ui/app-bar.tsx @@ -0,0 +1,131 @@ +"use client"; + +import { + IconChevronLeftLine, + IconXmarkLine, +} from "@daangn/react-monochrome-icon"; +import { + AppBar as SeedAppBar, + type AppBarIconButtonProps, +} from "@seed-design/stackflow"; +import { useActions, useActivity } from "@stackflow/react"; +import * as React from "react"; +import { forwardRef } from "react"; + +export const AppBar = SeedAppBar.Root; + +export const AppBarLeft = SeedAppBar.Left; + +export const AppBarRight = SeedAppBar.Right; + +export interface AppBarTitleProps + extends Omit { + /** + * The title of the app bar. + * If children is provided as ReactElement, this prop will be ignored. + */ + title?: string; + + /** + * The subtitle of the app bar. + * If children is provided as ReactElement, this prop will be ignored. + */ + subtitle?: string; +} + +export const AppBarTitle = forwardRef( + ({ title, subtitle, children, ...otherProps }, ref) => { + if (React.isValidElement(children)) { + return ( + + {children} + + ); + } + + // TODO: shrink titleText size when subtitle is provided + return ( + + + {children ?? title} + + {subtitle ? ( + {subtitle} + ) : null} + + ); + }, +); +AppBarTitle.displayName = "AppBarTitle"; + +export const AppBarIconButton = SeedAppBar.IconButton; + +export const AppBarBackButton = forwardRef< + HTMLButtonElement, + AppBarIconButtonProps +>(({ children = , onClick, ...otherProps }, ref) => { + const activity = useActivity(); + const actions = useActions(); + + const handleOnClick = (e: React.MouseEvent) => { + onClick?.(e); + + if (!e.defaultPrevented) { + actions.pop(); + } + }; + + if (!activity) { + return null; + } + if (activity.isRoot) { + return null; + } + + return ( + + {children} + + ); +}); +AppBarBackButton.displayName = "AppBarBackButton"; + +export const AppBarCloseButton = forwardRef< + HTMLButtonElement, + AppBarIconButtonProps +>(({ children = , onClick, ...otherProps }, ref) => { + const activity = useActivity(); + + const handleOnClick = (e: React.MouseEvent) => { + onClick?.(e); + + if (!e.defaultPrevented) { + // you can do something here + } + }; + + const isRoot = !activity || activity.isRoot; + + if (!isRoot) { + return null; + } + + return ( + + {children} + + ); +}); +AppBarCloseButton.displayName = "AppBarCloseButton"; diff --git a/docs/registry/ui/app-screen.tsx b/docs/registry/ui/app-screen.tsx index f960bbaa8..49d5c7c22 100644 --- a/docs/registry/ui/app-screen.tsx +++ b/docs/registry/ui/app-screen.tsx @@ -1,115 +1,69 @@ "use client"; -import { - IconChevronLeftLine, - IconXmarkLine, -} from "@daangn/react-monochrome-icon"; -import { - AppBar as SeedAppBar, - AppScreen as SeedAppScreen, -} from "@seed-design/stackflow"; -import { useActions, useActivity } from "@stackflow/react"; -import { forwardRef, useCallback } from "react"; +import { PullToRefresh } from "@seed-design/react/primitive"; +import { AppScreen as SeedAppScreen } from "@seed-design/stackflow"; +import { useActions } from "@stackflow/react"; +import { forwardRef } from "react"; +import { ProgressCircle } from "../ui/progress-circle"; -export type AppBarProps = SeedAppBar.RootProps; +export interface AppScreenProps extends SeedAppScreen.RootProps {} -export type AppScreenProps = SeedAppScreen.RootProps; - -export const AppBar = SeedAppBar.Root; - -export const Left = SeedAppBar.Left; - -export const Right = SeedAppBar.Right; - -export const Title = SeedAppBar.Title; - -export const IconButton = SeedAppBar.IconButton; - -export const BackButton = forwardRef< - HTMLButtonElement, - SeedAppBar.IconButtonProps ->(({ children = , onClick, ...otherProps }, ref) => { - const activity = useActivity(); - const actions = useActions(); - - const handleOnClick = useCallback( - (e: React.MouseEvent) => { - onClick?.(e); - - if (!e.defaultPrevented) { - actions.pop(); - } - }, - [actions], - ); - - if (!activity) { - return null; - } - if (activity.isRoot) { - return null; - } - - return ( - - {children} - - ); -}); -BackButton.displayName = "BackButton"; +export const AppScreen = forwardRef( + ({ children, onSwipeEnd, ...otherProps }, ref) => { + const { pop } = useActions(); -export const CloseButton = forwardRef< - HTMLButtonElement, - SeedAppBar.IconButtonProps ->(({ children = , onClick, ...otherProps }, ref) => { - const activity = useActivity(); + return ( + { + if (swiped) { + pop(); + } + onSwipeEnd?.({ swiped }); + }} + {...otherProps} + > + + {children} + + + ); + }, +); +AppScreen.displayName = "AppScreen"; - const handleOnClick = useCallback( - (e: React.MouseEvent) => { - onClick?.(e); +export interface AppScreenContentProps extends SeedAppScreen.LayerProps { + ptr?: boolean; - if (!e.defaultPrevented) { - // you can do something here - } - }, - [], - ); + onPtrReady?: () => void; - const isRoot = !activity || activity.isRoot; + onPtrRefresh?: () => Promise; +} - if (!isRoot) { - return null; +export const AppScreenContent = forwardRef< + HTMLDivElement, + AppScreenContentProps +>(({ children, ptr, onPtrReady, onPtrRefresh, ...otherProps }, ref) => { + if (!ptr) { + return ( + + {children} + + ); } return ( - - {children} - + + + {(props) => } + + {children} + + ); }); -CloseButton.displayName = "CloseButton"; - -export const AppScreen = forwardRef( - ({ children, ...otherProps }, ref) => { - return ( - - - {children} - - - ); - }, -); -AppScreen.displayName = "AppScreen"; diff --git a/examples/stackflow-spa/src/activities/ActivityActionButton.tsx b/examples/stackflow-spa/src/activities/ActivityActionButton.tsx index 9fb73a261..aefc9cf61 100644 --- a/examples/stackflow-spa/src/activities/ActivityActionButton.tsx +++ b/examples/stackflow-spa/src/activities/ActivityActionButton.tsx @@ -1,5 +1,10 @@ import type { ActivityComponentType } from "@stackflow/react"; -import { AppBar, BackButton, AppBarLeft, AppBarTitle } from "../design-system/stackflow/AppBar"; +import { + AppBar, + AppBarBackButton, + AppBarLeft, + AppBarTitle, +} from "../design-system/stackflow/AppBar"; import { AppScreen, AppScreenContent } from "../design-system/stackflow/AppScreen"; import { actionButton, actionButtonVariantMap } from "@seed-design/recipe/actionButton"; @@ -19,7 +24,7 @@ const ActivityActionButton: ActivityComponentType = () => { - + Action Button diff --git a/examples/stackflow-spa/src/activities/ActivityActionChip.tsx b/examples/stackflow-spa/src/activities/ActivityActionChip.tsx index 8f129e992..5de409c69 100644 --- a/examples/stackflow-spa/src/activities/ActivityActionChip.tsx +++ b/examples/stackflow-spa/src/activities/ActivityActionChip.tsx @@ -1,5 +1,10 @@ import type { ActivityComponentType } from "@stackflow/react"; -import { AppBar, BackButton, AppBarLeft, AppBarTitle } from "../design-system/stackflow/AppBar"; +import { + AppBar, + AppBarBackButton, + AppBarLeft, + AppBarTitle, +} from "../design-system/stackflow/AppBar"; import { AppScreen, AppScreenContent } from "../design-system/stackflow/AppScreen"; import { actionChip, actionChipVariantMap } from "@seed-design/recipe/actionChip"; @@ -18,7 +23,7 @@ const ActivityActionChip: ActivityComponentType = () => { - + Action Chip diff --git a/examples/stackflow-spa/src/activities/ActivityControlChip.tsx b/examples/stackflow-spa/src/activities/ActivityControlChip.tsx index cedbfbcf4..2cc1bb4f9 100644 --- a/examples/stackflow-spa/src/activities/ActivityControlChip.tsx +++ b/examples/stackflow-spa/src/activities/ActivityControlChip.tsx @@ -1,5 +1,10 @@ import type { ActivityComponentType } from "@stackflow/react"; -import { AppBar, BackButton, AppBarLeft, AppBarTitle } from "../design-system/stackflow/AppBar"; +import { + AppBar, + AppBarBackButton, + AppBarLeft, + AppBarTitle, +} from "../design-system/stackflow/AppBar"; import { AppScreen, AppScreenContent } from "../design-system/stackflow/AppScreen"; import { controlChip, controlChipVariantMap } from "@seed-design/recipe/controlChip"; @@ -18,7 +23,7 @@ const ActivityControlChip: ActivityComponentType = () => { - + Control Chip diff --git a/examples/stackflow-spa/src/activities/ActivityErrorState.tsx b/examples/stackflow-spa/src/activities/ActivityErrorState.tsx index 5286d427a..d7b45e469 100644 --- a/examples/stackflow-spa/src/activities/ActivityErrorState.tsx +++ b/examples/stackflow-spa/src/activities/ActivityErrorState.tsx @@ -3,7 +3,12 @@ import type { ActivityComponentType } from "@stackflow/react"; import React from "react"; import { AppScreen, AppScreenContent } from "../design-system/stackflow/AppScreen"; import { ErrorState, type ErrorStateProps } from "../design-system/ui/error-state"; -import { AppBar, AppBarLeft, AppBarTitle, BackButton } from "../design-system/stackflow/AppBar"; +import { + AppBar, + AppBarLeft, + AppBarTitle, + AppBarBackButton, +} from "../design-system/stackflow/AppBar"; const ActivityErrorState: ActivityComponentType = () => { const [variant, setVariant] = React.useState("default"); @@ -12,7 +17,7 @@ const ActivityErrorState: ActivityComponentType = () => { - + Error State diff --git a/examples/stackflow-spa/src/activities/ActivityHelpBubble.tsx b/examples/stackflow-spa/src/activities/ActivityHelpBubble.tsx index b47a4a5a7..2e964401a 100644 --- a/examples/stackflow-spa/src/activities/ActivityHelpBubble.tsx +++ b/examples/stackflow-spa/src/activities/ActivityHelpBubble.tsx @@ -1,6 +1,11 @@ import type { ActivityComponentType } from "@stackflow/react"; -import { AppBar, AppBarLeft, AppBarTitle, BackButton } from "../design-system/stackflow/AppBar"; +import { + AppBar, + AppBarLeft, + AppBarTitle, + AppBarBackButton, +} from "../design-system/stackflow/AppBar"; import { AppScreen, AppScreenContent } from "../design-system/stackflow/AppScreen"; import { ActionButton } from "../design-system/ui/action-button"; import { HelpBubbleTrigger } from "../design-system/ui/help-bubble"; @@ -10,7 +15,7 @@ const ActivityHelpBubble: ActivityComponentType = () => { - + Help Bubble diff --git a/examples/stackflow-spa/src/activities/ActivityLayerBar.tsx b/examples/stackflow-spa/src/activities/ActivityLayerBar.tsx index ac99064b1..d06ca283c 100644 --- a/examples/stackflow-spa/src/activities/ActivityLayerBar.tsx +++ b/examples/stackflow-spa/src/activities/ActivityLayerBar.tsx @@ -4,8 +4,8 @@ import { AppBarLeft, AppBarRight, AppBarTitle, - BackButton, - IconButton, + AppBarBackButton, + AppBarIconButton, } from "../design-system/stackflow/AppBar"; import { AppScreen, AppScreenContent } from "../design-system/stackflow/AppScreen"; @@ -16,22 +16,22 @@ const ActivityLayerBar: ActivityComponentType = () => { - + - 야옹 + Random Long Title Hello World - + - - + + - - + + - - + + - + diff --git a/examples/stackflow-spa/src/activities/ActivityNotFound.tsx b/examples/stackflow-spa/src/activities/ActivityNotFound.tsx index cf0856a83..9d2d0ca6a 100644 --- a/examples/stackflow-spa/src/activities/ActivityNotFound.tsx +++ b/examples/stackflow-spa/src/activities/ActivityNotFound.tsx @@ -1,5 +1,10 @@ import type { ActivityComponentType } from "@stackflow/react"; -import { AppBar, BackButton, AppBarLeft, AppBarTitle } from "../design-system/stackflow/AppBar"; +import { + AppBar, + AppBarBackButton, + AppBarLeft, + AppBarTitle, +} from "../design-system/stackflow/AppBar"; import { AppScreen, AppScreenContent } from "../design-system/stackflow/AppScreen"; const ActivityNotFound: ActivityComponentType = () => { @@ -7,7 +12,7 @@ const ActivityNotFound: ActivityComponentType = () => { - + Error diff --git a/examples/stackflow-spa/src/activities/ActivityTransparentBar.tsx b/examples/stackflow-spa/src/activities/ActivityTransparentBar.tsx index 1bb4a4858..a36b08caa 100644 --- a/examples/stackflow-spa/src/activities/ActivityTransparentBar.tsx +++ b/examples/stackflow-spa/src/activities/ActivityTransparentBar.tsx @@ -4,8 +4,8 @@ import { AppBarLeft, AppBarRight, AppBarTitle, - BackButton, - IconButton, + AppBarBackButton, + AppBarIconButton, } from "../design-system/stackflow/AppBar"; import { AppScreen, AppScreenContent } from "../design-system/stackflow/AppScreen"; @@ -17,22 +17,22 @@ const ActivityTransparentBar: ActivityComponentType = () => { - + 야옹 - + - - + + - - + + - - + + - + diff --git a/examples/stackflow-spa/src/design-system/stackflow/AppBar.tsx b/examples/stackflow-spa/src/design-system/stackflow/AppBar.tsx index 6f1bda5da..fc5d57760 100644 --- a/examples/stackflow-spa/src/design-system/stackflow/AppBar.tsx +++ b/examples/stackflow-spa/src/design-system/stackflow/AppBar.tsx @@ -44,10 +44,11 @@ export const AppBarTitle = forwardRef( ); }, ); +AppBarTitle.displayName = "AppBarTitle"; -export const IconButton = SeedAppBar.IconButton; +export const AppBarIconButton = SeedAppBar.IconButton; -export const BackButton = forwardRef( +export const AppBarBackButton = forwardRef( ({ children = , onClick, ...otherProps }, ref) => { const activity = useActivity(); const actions = useActions(); @@ -80,9 +81,9 @@ export const BackButton = forwardRef( ); }, ); -BackButton.displayName = "BackButton"; +AppBarBackButton.displayName = "AppBarBackButton"; -export const CloseButton = forwardRef( +export const AppBarCloseButton = forwardRef( ({ children = , onClick, ...otherProps }, ref) => { const activity = useActivity(); @@ -101,7 +102,7 @@ export const CloseButton = forwardRef( } return ( - ( {...otherProps} > {children} - + ); }, ); -CloseButton.displayName = "CloseButton"; +AppBarCloseButton.displayName = "AppBarCloseButton"; diff --git a/examples/stackflow-spa/src/design-system/stackflow/AppScreen.tsx b/examples/stackflow-spa/src/design-system/stackflow/AppScreen.tsx index 8bee1b6ce..efff12497 100644 --- a/examples/stackflow-spa/src/design-system/stackflow/AppScreen.tsx +++ b/examples/stackflow-spa/src/design-system/stackflow/AppScreen.tsx @@ -2,7 +2,6 @@ import { PullToRefresh, usePullToRefreshContext } from "@seed-design/react/primi import { AppScreen as SeedAppScreen } from "@seed-design/stackflow"; import { useActions } from "@stackflow/react"; import { forwardRef } from "react"; -import { theme } from "../../stackflow/theme"; import { ProgressCircle } from "../ui/progress-circle"; export interface AppScreenProps extends SeedAppScreen.RootProps {} @@ -20,7 +19,6 @@ export const AppScreen = forwardRef( } onSwipeEnd?.({ swiped }); }} - theme={theme} {...otherProps} >