Skip to content

Commit

Permalink
feat: cleanup convention & registry
Browse files Browse the repository at this point in the history
  • Loading branch information
malangcat committed Jan 27, 2025
1 parent b988223 commit 811fb2a
Show file tree
Hide file tree
Showing 13 changed files with 260 additions and 146 deletions.
4 changes: 2 additions & 2 deletions docs/registry/registry-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
131 changes: 131 additions & 0 deletions docs/registry/ui/app-bar.tsx
Original file line number Diff line number Diff line change
@@ -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<SeedAppBar.TitleProps, "asChild"> {
/**
* 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<HTMLDivElement, AppBarTitleProps>(
({ title, subtitle, children, ...otherProps }, ref) => {
if (React.isValidElement(children)) {
return (
<SeedAppBar.Title {...otherProps} ref={ref}>
{children}
</SeedAppBar.Title>
);
}

// TODO: shrink titleText size when subtitle is provided
return (
<SeedAppBar.Title {...otherProps} ref={ref}>
<SeedAppBar.TitleMain>
<SeedAppBar.TitleText>{children ?? title}</SeedAppBar.TitleText>
</SeedAppBar.TitleMain>
{subtitle ? (
<SeedAppBar.SubtitleText>{subtitle}</SeedAppBar.SubtitleText>
) : null}
</SeedAppBar.Title>
);
},
);
AppBarTitle.displayName = "AppBarTitle";

export const AppBarIconButton = SeedAppBar.IconButton;

export const AppBarBackButton = forwardRef<
HTMLButtonElement,
AppBarIconButtonProps
>(({ children = <IconChevronLeftLine />, onClick, ...otherProps }, ref) => {
const activity = useActivity();
const actions = useActions();

const handleOnClick = (e: React.MouseEvent<HTMLButtonElement>) => {
onClick?.(e);

if (!e.defaultPrevented) {
actions.pop();
}
};

if (!activity) {
return null;
}
if (activity.isRoot) {
return null;
}

return (
<SeedAppBar.IconButton
ref={ref}
aria-label="Go Back"
type="button"
onClick={handleOnClick}
{...otherProps}
>
{children}
</SeedAppBar.IconButton>
);
});
AppBarBackButton.displayName = "AppBarBackButton";

export const AppBarCloseButton = forwardRef<
HTMLButtonElement,
AppBarIconButtonProps
>(({ children = <IconXmarkLine />, onClick, ...otherProps }, ref) => {
const activity = useActivity();

const handleOnClick = (e: React.MouseEvent<HTMLButtonElement>) => {
onClick?.(e);

if (!e.defaultPrevented) {
// you can do something here
}
};

const isRoot = !activity || activity.isRoot;

if (!isRoot) {
return null;
}

return (
<AppBarIconButton
ref={ref}
aria-label="Close"
type="button"
onClick={handleOnClick}
{...otherProps}
>
{children}
</AppBarIconButton>
);
});
AppBarCloseButton.displayName = "AppBarCloseButton";
154 changes: 54 additions & 100 deletions docs/registry/ui/app-screen.tsx
Original file line number Diff line number Diff line change
@@ -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 = <IconChevronLeftLine />, onClick, ...otherProps }, ref) => {
const activity = useActivity();
const actions = useActions();

const handleOnClick = useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => {
onClick?.(e);

if (!e.defaultPrevented) {
actions.pop();
}
},
[actions],
);

if (!activity) {
return null;
}
if (activity.isRoot) {
return null;
}

return (
<SeedAppBar.IconButton
ref={ref}
aria-label="Go Back"
type="button"
onClick={handleOnClick}
{...otherProps}
>
{children}
</SeedAppBar.IconButton>
);
});
BackButton.displayName = "BackButton";
export const AppScreen = forwardRef<HTMLDivElement, AppScreenProps>(
({ children, onSwipeEnd, ...otherProps }, ref) => {
const { pop } = useActions();

export const CloseButton = forwardRef<
HTMLButtonElement,
SeedAppBar.IconButtonProps
>(({ children = <IconXmarkLine />, onClick, ...otherProps }, ref) => {
const activity = useActivity();
return (
<SeedAppScreen.Root
ref={ref}
onSwipeEnd={({ swiped }) => {
if (swiped) {
pop();
}
onSwipeEnd?.({ swiped });
}}
{...otherProps}
>
<SeedAppScreen.Dim />
{children}
<SeedAppScreen.Edge />
</SeedAppScreen.Root>
);
},
);
AppScreen.displayName = "AppScreen";

const handleOnClick = useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => {
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<void>;
}

if (!isRoot) {
return null;
export const AppScreenContent = forwardRef<
HTMLDivElement,
AppScreenContentProps
>(({ children, ptr, onPtrReady, onPtrRefresh, ...otherProps }, ref) => {
if (!ptr) {
return (
<SeedAppScreen.Layer ref={ref} {...otherProps}>
{children}
</SeedAppScreen.Layer>
);
}

return (
<IconButton
ref={ref}
aria-label="Close"
type="button"
onClick={handleOnClick}
{...otherProps}
<PullToRefresh.Root
asChild
onPtrReady={onPtrReady}
onPtrRefresh={onPtrRefresh}
>
{children}
</IconButton>
<SeedAppScreen.Layer ref={ref} {...otherProps}>
<PullToRefresh.Indicator>
{(props) => <ProgressCircle tone="brand" {...props} />}
</PullToRefresh.Indicator>
<PullToRefresh.Content asChild>{children}</PullToRefresh.Content>
</SeedAppScreen.Layer>
</PullToRefresh.Root>
);
});
CloseButton.displayName = "CloseButton";

export const AppScreen = forwardRef<HTMLDivElement, AppScreenProps>(
({ children, ...otherProps }, ref) => {
return (
<SeedAppScreen.Root ref={ref} {...otherProps}>
<SeedAppScreen.Dim />
<SeedAppScreen.Layer>{children}</SeedAppScreen.Layer>
<SeedAppScreen.Edge />
</SeedAppScreen.Root>
);
},
);
AppScreen.displayName = "AppScreen";
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -19,7 +24,7 @@ const ActivityActionButton: ActivityComponentType = () => {
<AppScreen>
<AppBar>
<AppBarLeft>
<BackButton />
<AppBarBackButton />
</AppBarLeft>
<AppBarTitle>Action Button</AppBarTitle>
</AppBar>
Expand Down
9 changes: 7 additions & 2 deletions examples/stackflow-spa/src/activities/ActivityActionChip.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -18,7 +23,7 @@ const ActivityActionChip: ActivityComponentType = () => {
<AppScreen>
<AppBar>
<AppBarLeft>
<BackButton />
<AppBarBackButton />
</AppBarLeft>
<AppBarTitle>Action Chip</AppBarTitle>
</AppBar>
Expand Down
9 changes: 7 additions & 2 deletions examples/stackflow-spa/src/activities/ActivityControlChip.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -18,7 +23,7 @@ const ActivityControlChip: ActivityComponentType = () => {
<AppScreen>
<AppBar>
<AppBarLeft>
<BackButton />
<AppBarBackButton />
</AppBarLeft>
<AppBarTitle>Control Chip</AppBarTitle>
</AppBar>
Expand Down
9 changes: 7 additions & 2 deletions examples/stackflow-spa/src/activities/ActivityErrorState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<ErrorStateProps["variant"]>("default");
Expand All @@ -12,7 +17,7 @@ const ActivityErrorState: ActivityComponentType = () => {
<AppScreen>
<AppBar>
<AppBarLeft>
<BackButton />
<AppBarBackButton />
</AppBarLeft>
<AppBarTitle>Error State</AppBarTitle>
</AppBar>
Expand Down
Loading

0 comments on commit 811fb2a

Please sign in to comment.