@seed-design/stackflow 0.0.1-alpha-20250210081704

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/lib/components/AppBar/AppBar.d.ts +29 -0
  2. package/lib/components/AppBar/AppBar.namespace.d.ts +2 -0
  3. package/lib/components/AppBar/index.d.ts +3 -0
  4. package/lib/components/AppScreen/AppScreen.d.ts +18 -0
  5. package/lib/components/AppScreen/AppScreen.namespace.d.ts +2 -0
  6. package/lib/components/AppScreen/index.d.ts +3 -0
  7. package/lib/components/index.d.ts +2 -0
  8. package/lib/index.d.ts +3 -0
  9. package/lib/index.js +786 -0
  10. package/lib/index.js.map +7 -0
  11. package/lib/index.mjs +759 -0
  12. package/lib/index.mjs.map +7 -0
  13. package/lib/plugin.d.ts +5 -0
  14. package/lib/primitive/AppBar/AppBar.d.ts +14 -0
  15. package/lib/primitive/AppBar/AppBar.namespace.d.ts +1 -0
  16. package/lib/primitive/AppBar/index.d.ts +2 -0
  17. package/lib/primitive/AppBar/useAppBar.d.ts +279 -0
  18. package/lib/primitive/AppBar/useAppBarContext.d.ts +7 -0
  19. package/lib/primitive/AppScreen/AppScreen.d.ts +14 -0
  20. package/lib/primitive/AppScreen/AppScreen.namespace.d.ts +1 -0
  21. package/lib/primitive/AppScreen/index.d.ts +3 -0
  22. package/lib/primitive/AppScreen/useAppScreen.d.ts +1080 -0
  23. package/lib/primitive/AppScreen/useAppScreenContext.d.ts +7 -0
  24. package/lib/primitive/GlobalInteraction/GlobalInteraction.d.ts +4 -0
  25. package/lib/primitive/GlobalInteraction/index.d.ts +2 -0
  26. package/lib/primitive/GlobalInteraction/useGlobalInteraction.d.ts +53 -0
  27. package/lib/primitive/GlobalInteraction/useGlobalInteractionContext.d.ts +7 -0
  28. package/lib/primitive/GlobalInteraction/useSwipeBack.d.ts +8 -0
  29. package/lib/primitive/index.d.ts +3 -0
  30. package/lib/primitive/private/useElementOffset.d.ts +4 -0
  31. package/lib/primitive/private/useTopActivity.d.ts +4 -0
  32. package/lib/utils/createStyleContext.d.ts +23 -0
  33. package/lib/utils/createWithStateProps.d.ts +9 -0
  34. package/package.json +56 -0
  35. package/src/components/AppBar/AppBar.namespace.ts +19 -0
  36. package/src/components/AppBar/AppBar.tsx +109 -0
  37. package/src/components/AppBar/index.tsx +21 -0
  38. package/src/components/AppScreen/AppScreen.namespace.ts +13 -0
  39. package/src/components/AppScreen/AppScreen.tsx +65 -0
  40. package/src/components/AppScreen/index.tsx +15 -0
  41. package/src/components/index.tsx +2 -0
  42. package/src/index.ts +4 -0
  43. package/src/plugin.tsx +20 -0
  44. package/src/primitive/AppBar/AppBar.namespace.ts +10 -0
  45. package/src/primitive/AppBar/AppBar.tsx +52 -0
  46. package/src/primitive/AppBar/index.ts +12 -0
  47. package/src/primitive/AppBar/useAppBar.tsx +42 -0
  48. package/src/primitive/AppBar/useAppBarContext.ts +19 -0
  49. package/src/primitive/AppScreen/AppScreen.namespace.ts +10 -0
  50. package/src/primitive/AppScreen/AppScreen.tsx +66 -0
  51. package/src/primitive/AppScreen/index.ts +13 -0
  52. package/src/primitive/AppScreen/useAppScreen.tsx +68 -0
  53. package/src/primitive/AppScreen/useAppScreenContext.ts +19 -0
  54. package/src/primitive/GlobalInteraction/GlobalInteraction.tsx +25 -0
  55. package/src/primitive/GlobalInteraction/index.ts +6 -0
  56. package/src/primitive/GlobalInteraction/useGlobalInteraction.ts +178 -0
  57. package/src/primitive/GlobalInteraction/useGlobalInteractionContext.tsx +21 -0
  58. package/src/primitive/GlobalInteraction/useSwipeBack.ts +56 -0
  59. package/src/primitive/index.tsx +3 -0
  60. package/src/primitive/private/useElementOffset.ts +37 -0
  61. package/src/primitive/private/useTopActivity.ts +31 -0
  62. package/src/utils/createStyleContext.tsx +125 -0
  63. package/src/utils/createWithStateProps.tsx +28 -0
@@ -0,0 +1,7 @@
1
+ import type { UseAppScreenReturn } from "./useAppScreen";
2
+ export interface UseAppScreenContext extends UseAppScreenReturn {
3
+ }
4
+ export declare const AppScreenProvider: import("react").Provider<UseAppScreenContext | null>;
5
+ export declare function useAppScreenContext<T extends boolean | undefined = true>({ strict, }?: {
6
+ strict?: T;
7
+ }): T extends false ? UseAppScreenContext | null : UseAppScreenContext;
@@ -0,0 +1,4 @@
1
+ import { type PrimitiveProps } from "@seed-design/react-primitive";
2
+ export interface GlobalInteractionProps extends PrimitiveProps, React.HTMLAttributes<HTMLDivElement> {
3
+ }
4
+ export declare const GlobalInteraction: import("react").ForwardRefExoticComponent<GlobalInteractionProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,2 @@
1
+ export { GlobalInteraction, type GlobalInteractionProps } from "./GlobalInteraction";
2
+ export { useGlobalInteractionContext, type UseGlobalInteractionContext, } from "./useGlobalInteractionContext";
@@ -0,0 +1,53 @@
1
+ export type SwipeBackState = "idle" | "swiping" | "transitioning";
2
+ export type UseGlobalInteractionReturn = ReturnType<typeof useGlobalInteraction>;
3
+ export interface SwipeBackContext {
4
+ x0: number;
5
+ t0: number;
6
+ displacement: number;
7
+ displacementRatio: number;
8
+ velocity: number;
9
+ }
10
+ export interface SwipeBackProps {
11
+ /**
12
+ * The threshold to determine whether the swipe back is intentional.
13
+ * @default 0.4
14
+ */
15
+ swipeBackDisplacementRatioThreshold?: number;
16
+ /**
17
+ * The threshold to determine whether the swipe back is intentional.
18
+ * @default 1
19
+ */
20
+ swipeBackVelocityThreshold?: number;
21
+ onSwipeBackStart?: () => void;
22
+ onSwipeBackMove?: (props: {
23
+ displacement: number;
24
+ displacementRatio: number;
25
+ }) => void;
26
+ onSwipeBackEnd?: (props: {
27
+ swiped: boolean;
28
+ }) => void;
29
+ }
30
+ export interface StartSwipeBackProps {
31
+ x0: number;
32
+ t0: number;
33
+ }
34
+ export interface MoveSwipeBackProps {
35
+ x: number;
36
+ t: number;
37
+ }
38
+ export interface EndSwipeBackProps {
39
+ }
40
+ export declare function useGlobalInteraction(): {
41
+ stackRef: import("react").RefObject<HTMLDivElement>;
42
+ swipeBackContextRef: import("react").MutableRefObject<SwipeBackContext>;
43
+ swipeBackState: SwipeBackState;
44
+ setSwipeBackState: import("react").Dispatch<import("react").SetStateAction<SwipeBackState>>;
45
+ setSwipeBackContext: (ctx: SwipeBackContext) => void;
46
+ getSwipeBackEvents: (props: SwipeBackProps) => {
47
+ startSwipeBack: ({ x0, t0 }: StartSwipeBackProps) => void;
48
+ moveSwipeBack: ({ x, t }: MoveSwipeBackProps) => void;
49
+ endSwipeBack: (_: EndSwipeBackProps) => void;
50
+ reset: () => void;
51
+ };
52
+ stackProps: import("react").HTMLAttributes<HTMLElement>;
53
+ };
@@ -0,0 +1,7 @@
1
+ import type { UseGlobalInteractionReturn } from "./useGlobalInteraction";
2
+ export interface UseGlobalInteractionContext extends UseGlobalInteractionReturn {
3
+ }
4
+ export declare const GlobalInteractionProvider: import("react").Provider<UseGlobalInteractionContext | null>;
5
+ export declare function useGlobalInteractionContext<T extends boolean | undefined = true>({ strict, }?: {
6
+ strict?: T;
7
+ }): T extends false ? UseGlobalInteractionContext | null : UseGlobalInteractionContext;
@@ -0,0 +1,8 @@
1
+ import type { SwipeBackProps } from "./useGlobalInteraction";
2
+ export interface UseSwipeBackProps extends SwipeBackProps {
3
+ }
4
+ export declare function useSwipeBack(props: UseSwipeBackProps): {
5
+ activityProps: React.HTMLAttributes<HTMLDivElement>;
6
+ layerProps: React.HTMLAttributes<HTMLDivElement>;
7
+ edgeProps: React.HTMLAttributes<HTMLElement>;
8
+ };
@@ -0,0 +1,3 @@
1
+ export * from "./AppScreen";
2
+ export * from "./AppBar";
3
+ export * from "./GlobalInteraction";
@@ -0,0 +1,4 @@
1
+ export declare function useElementOffset(element: HTMLElement | null): {
2
+ fromLeft: number;
3
+ fromRight: number;
4
+ } | undefined;
@@ -0,0 +1,4 @@
1
+ export declare function useTopActivity(): {
2
+ transitionState: "enter-active" | "enter-done" | "exit-active" | "exit-done";
3
+ activityType: string | undefined;
4
+ };
@@ -0,0 +1,23 @@
1
+ type Recipe<Props extends Record<string, string | boolean | undefined>, Classnames extends Record<string, string>> = ((props?: Props) => Classnames) & {
2
+ splitVariantProps: <T extends Props>(props: T) => [Props, Omit<T, keyof Props>];
3
+ };
4
+ export declare function createStyleContext<Props extends Record<string, string | boolean | undefined>, Classnames extends Record<string, string>>(recipe: Recipe<Props, Classnames>): {
5
+ ClassNamesProvider: ({ children, value, }: {
6
+ children: React.ReactNode;
7
+ value: Classnames;
8
+ }) => import("react/jsx-runtime").JSX.Element;
9
+ PropsProvider: ({ children, value }: {
10
+ children: React.ReactNode;
11
+ value: Props;
12
+ }) => import("react/jsx-runtime").JSX.Element;
13
+ useClassNames: () => Classnames;
14
+ useProps: () => Props | null;
15
+ withRootProvider: <P>(Component: React.ElementType<any>, options?: {
16
+ defaultProps?: Partial<P>;
17
+ }) => React.ForwardRefExoticComponent<React.PropsWithoutRef<P>>;
18
+ withProvider: <T, P>(Component: React.ElementType<any>, slot: keyof Classnames, options?: {
19
+ defaultProps?: Partial<P>;
20
+ }) => React.ForwardRefExoticComponent<React.PropsWithoutRef<P> & React.RefAttributes<T>>;
21
+ withContext: <T, P>(Component: React.ElementType<any>, slot?: keyof Classnames) => React.ForwardRefExoticComponent<React.PropsWithoutRef<P> & React.RefAttributes<T>>;
22
+ };
23
+ export {};
@@ -0,0 +1,9 @@
1
+ type AtLeastOne<T> = [T, ...T[]];
2
+ export declare function createWithStateProps(useContexts: AtLeastOne<(prop?: {
3
+ strict?: boolean;
4
+ }) => {
5
+ stateProps: React.HTMLAttributes<HTMLElement>;
6
+ } | null>, options?: {
7
+ strict?: boolean;
8
+ }): <P, R>(Component: React.ComponentType<P & React.RefAttributes<R>>) => import("react").ForwardRefExoticComponent<import("react").PropsWithoutRef<P> & import("react").RefAttributes<R>>;
9
+ export {};
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@seed-design/stackflow",
3
+ "version": "0.0.1-alpha-20250210081704",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/daangn/seed-design.git",
7
+ "directory": "packages/stackflow"
8
+ },
9
+ "sideEffects": false,
10
+ "exports": {
11
+ ".": {
12
+ "types": "./lib/index.d.ts",
13
+ "require": "./lib/index.js",
14
+ "import": "./lib/index.mjs"
15
+ },
16
+ "./package.json": "./package.json"
17
+ },
18
+ "main": "./lib/index.js",
19
+ "files": [
20
+ "lib",
21
+ "src"
22
+ ],
23
+ "scripts": {
24
+ "prepack": "bun run build",
25
+ "clean": "rm -rf lib",
26
+ "build": "bun build.mjs && tsc"
27
+ },
28
+ "dependencies": {
29
+ "@radix-ui/react-compose-refs": "^1.1.1",
30
+ "@radix-ui/react-slot": "^1.1.1",
31
+ "@radix-ui/react-use-callback-ref": "^1.1.0",
32
+ "@seed-design/dom-utils": "0.0.0-alpha-20250210081704",
33
+ "@seed-design/react-primitive": "0.0.1-alpha-20250210081704",
34
+ "clsx": "^2.1.1"
35
+ },
36
+ "devDependencies": {
37
+ "@stackflow/core": "^1.1.0",
38
+ "esbuild": "^0.25.0"
39
+ },
40
+ "peerDependencies": {
41
+ "@seed-design/recipe": "0.0.0-alpha-20250210081704",
42
+ "@stackflow/react": ">=1.4.1",
43
+ "@stackflow/react-ui-core": ">=1.2.1",
44
+ "react": ">=18.0.0",
45
+ "react-dom": ">=18.0.0"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public"
49
+ },
50
+ "ultra": {
51
+ "concurrent": [
52
+ "dev",
53
+ "build"
54
+ ]
55
+ }
56
+ }
@@ -0,0 +1,19 @@
1
+ export {
2
+ AppBarIconButton as IconButton,
3
+ AppBarLeft as Left,
4
+ AppBarMain as Main,
5
+ AppBarRight as Right,
6
+ AppBarRoot as Root,
7
+ AppBarSubtitle as Subtitle,
8
+ AppBarTitle as Title,
9
+ } from "./AppBar";
10
+
11
+ export type {
12
+ AppBarIconButtonProps as IconButtonProps,
13
+ AppBarLeftProps as LeftProps,
14
+ AppBarMainProps as MainProps,
15
+ AppBarRightProps as RightProps,
16
+ AppBarProps as RootProps,
17
+ AppBarSubtitleProps as SubtitleProps,
18
+ AppBarTitleProps as TitleProps,
19
+ } from "./AppBar";
@@ -0,0 +1,109 @@
1
+ import { Slot } from "@radix-ui/react-slot";
2
+ import { mergeProps } from "@seed-design/dom-utils";
3
+ import { Primitive, type PrimitiveProps } from "@seed-design/react-primitive";
4
+ import { type AppBarVariantProps, appBar } from "@seed-design/recipe/appBar";
5
+ import clsx from "clsx";
6
+ import { forwardRef } from "react";
7
+ import { AppBar as AppBarPrimitive } from "../../primitive";
8
+ import { useAppBarContext } from "../../primitive/AppBar/useAppBarContext";
9
+ import { createStyleContext } from "../../utils/createStyleContext";
10
+ import { appBarMain, type AppBarMainVariantProps } from "@seed-design/recipe/appBarMain";
11
+
12
+ const { PropsProvider, ClassNamesProvider, useProps, withContext, useClassNames } =
13
+ createStyleContext(appBar);
14
+ const {
15
+ PropsProvider: MainPropsProvider,
16
+ withProvider: withMainProvider,
17
+ withContext: withMainContext,
18
+ } = createStyleContext(appBarMain);
19
+
20
+ export const AppBarPropsProvider = PropsProvider;
21
+
22
+ export interface AppBarProps extends AppBarVariantProps, AppBarPrimitive.RootProps {}
23
+
24
+ export const AppBarRoot = forwardRef<HTMLDivElement, AppBarProps>((props, ref) => {
25
+ const contextProps = useProps();
26
+ const [variantProps, otherProps] = appBar.splitVariantProps({ ...contextProps, ...props });
27
+
28
+ const classNames = appBar(variantProps);
29
+
30
+ return (
31
+ <ClassNamesProvider value={classNames}>
32
+ <MainPropsProvider
33
+ value={{
34
+ theme: variantProps.theme,
35
+ transitionStyle: variantProps.transitionStyle,
36
+ tone: variantProps.tone,
37
+ }}
38
+ >
39
+ <AppBarPrimitive.Root
40
+ ref={ref}
41
+ {...mergeProps({ className: classNames.root }, otherProps)}
42
+ />
43
+ </MainPropsProvider>
44
+ </ClassNamesProvider>
45
+ );
46
+ });
47
+ AppBarRoot.displayName = "AppBarRoot";
48
+
49
+ export interface AppBarLeftProps extends AppBarPrimitive.LeftProps {}
50
+
51
+ export const AppBarLeft = withContext<HTMLDivElement, AppBarLeftProps>(
52
+ AppBarPrimitive.Left,
53
+ "left",
54
+ );
55
+
56
+ export interface AppBarRightProps extends AppBarPrimitive.RightProps {}
57
+
58
+ export const AppBarRight = withContext<HTMLDivElement, AppBarRightProps>(
59
+ AppBarPrimitive.Right,
60
+ "right",
61
+ );
62
+
63
+ export interface AppBarMainProps extends AppBarMainVariantProps, AppBarPrimitive.MainProps {}
64
+
65
+ export const AppBarMain = withMainProvider<HTMLDivElement, AppBarMainProps>(
66
+ AppBarPrimitive.Main,
67
+ "root",
68
+ );
69
+
70
+ export interface AppBarTitleProps extends PrimitiveProps, React.HTMLAttributes<HTMLSpanElement> {}
71
+
72
+ export const AppBarTitle = withMainContext<HTMLSpanElement, AppBarTitleProps>(
73
+ Primitive.span,
74
+ "title",
75
+ );
76
+
77
+ export interface AppBarSubtitleProps
78
+ extends PrimitiveProps,
79
+ React.HTMLAttributes<HTMLSpanElement> {}
80
+
81
+ export const AppBarSubtitle = withMainContext<HTMLSpanElement, AppBarSubtitleProps>(
82
+ Primitive.span,
83
+ "subtitle",
84
+ );
85
+
86
+ export interface AppBarIconButtonProps
87
+ extends PrimitiveProps,
88
+ React.ButtonHTMLAttributes<HTMLButtonElement> {}
89
+
90
+ export const AppBarIconButton = forwardRef<HTMLButtonElement, AppBarIconButtonProps>(
91
+ ({ children, className, ...otherProps }, ref) => {
92
+ const { stateProps } = useAppBarContext();
93
+ const classNames = useClassNames();
94
+
95
+ return (
96
+ <Primitive.button
97
+ ref={ref}
98
+ type="button"
99
+ className={clsx(classNames.iconButton, className)}
100
+ {...mergeProps(stateProps, otherProps)}
101
+ >
102
+ <Slot className={classNames.icon} {...stateProps}>
103
+ {children}
104
+ </Slot>
105
+ </Primitive.button>
106
+ );
107
+ },
108
+ );
109
+ AppBarIconButton.displayName = "IconButton";
@@ -0,0 +1,21 @@
1
+ export {
2
+ AppBarIconButton,
3
+ AppBarLeft,
4
+ AppBarMain,
5
+ AppBarRight,
6
+ AppBarRoot,
7
+ AppBarSubtitle,
8
+ AppBarTitle,
9
+ } from "./AppBar";
10
+
11
+ export type {
12
+ AppBarIconButtonProps,
13
+ AppBarLeftProps,
14
+ AppBarMainProps,
15
+ AppBarProps,
16
+ AppBarRightProps,
17
+ AppBarSubtitleProps,
18
+ AppBarTitleProps,
19
+ } from "./AppBar";
20
+
21
+ export * as AppBar from "./AppBar.namespace";
@@ -0,0 +1,13 @@
1
+ export {
2
+ AppScreenDim as Dim,
3
+ AppScreenEdge as Edge,
4
+ AppScreenLayer as Layer,
5
+ AppScreenRoot as Root,
6
+ } from "./AppScreen";
7
+
8
+ export type {
9
+ AppScreenDimProps as DimProps,
10
+ AppScreenEdgeProps as EdgeProps,
11
+ AppScreenLayerProps as LayerProps,
12
+ AppScreenRootProps as RootProps,
13
+ } from "./AppScreen";
@@ -0,0 +1,65 @@
1
+ import { appScreen, type AppScreenVariantProps } from "@seed-design/recipe/appScreen";
2
+ import { forwardRef, useMemo } from "react";
3
+ import { AppScreen as AppScreenPrimitive } from "../../primitive";
4
+ import { createStyleContext } from "../../utils/createStyleContext";
5
+ import { AppBarPropsProvider } from "../AppBar/AppBar";
6
+ import { mergeProps } from "@seed-design/dom-utils";
7
+
8
+ const { ClassNamesProvider, PropsProvider, withContext, useProps } = createStyleContext(appScreen);
9
+
10
+ export const AppScreenPropsProvider = PropsProvider;
11
+
12
+ export interface AppScreenRootProps extends AppScreenVariantProps, AppScreenPrimitive.RootProps {}
13
+
14
+ export const AppScreenRoot = forwardRef<HTMLDivElement, AppScreenRootProps>((props, ref) => {
15
+ const contextProps = useProps();
16
+ const [variantProps, otherProps] = appScreen.splitVariantProps({ ...contextProps, ...props });
17
+
18
+ // TODO: we have to implement conditional default in recipe; this is temporal workaround.
19
+ const transitionStyle =
20
+ variantProps.transitionStyle ??
21
+ (variantProps.theme === "cupertino" ? "slideFromRightIOS" : "fadeFromBottomAndroid");
22
+
23
+ const classNames = appScreen({
24
+ ...variantProps,
25
+ transitionStyle,
26
+ });
27
+
28
+ return (
29
+ <ClassNamesProvider value={classNames}>
30
+ <AppBarPropsProvider
31
+ value={useMemo(
32
+ () => ({ theme: variantProps.theme, transitionStyle }),
33
+ [variantProps.theme, transitionStyle],
34
+ )}
35
+ >
36
+ <AppScreenPrimitive.Root
37
+ ref={ref}
38
+ {...mergeProps({ className: classNames.root }, otherProps)}
39
+ />
40
+ </AppBarPropsProvider>
41
+ </ClassNamesProvider>
42
+ );
43
+ });
44
+ AppScreenRoot.displayName = "AppScreenRoot";
45
+
46
+ export interface AppScreenDimProps extends AppScreenPrimitive.DimProps {}
47
+
48
+ export const AppScreenDim = withContext<HTMLDivElement, AppScreenDimProps>(
49
+ AppScreenPrimitive.Dim,
50
+ "dim",
51
+ );
52
+
53
+ export interface AppScreenEdgeProps extends AppScreenPrimitive.EdgeProps {}
54
+
55
+ export const AppScreenEdge = withContext<HTMLDivElement, AppScreenEdgeProps>(
56
+ AppScreenPrimitive.Edge,
57
+ "edge",
58
+ );
59
+
60
+ export interface AppScreenLayerProps extends AppScreenPrimitive.LayerProps {}
61
+
62
+ export const AppScreenLayer = withContext<HTMLDivElement, AppScreenLayerProps>(
63
+ AppScreenPrimitive.Layer,
64
+ "layer",
65
+ );
@@ -0,0 +1,15 @@
1
+ export {
2
+ AppScreenDim,
3
+ AppScreenEdge,
4
+ AppScreenLayer,
5
+ AppScreenRoot,
6
+ } from "./AppScreen";
7
+
8
+ export type {
9
+ AppScreenDimProps,
10
+ AppScreenEdgeProps,
11
+ AppScreenLayerProps,
12
+ AppScreenRootProps,
13
+ } from "./AppScreen";
14
+
15
+ export * as AppScreen from "./AppScreen.namespace";
@@ -0,0 +1,2 @@
1
+ export * from "./AppBar";
2
+ export * from "./AppScreen";
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { seedPlugin, type SeedPluginOptions } from "./plugin";
2
+
3
+ export * from "./components";
4
+ export * as Primitive from "./primitive";
package/src/plugin.tsx ADDED
@@ -0,0 +1,20 @@
1
+ import type { StackflowReactPlugin } from "@stackflow/react";
2
+ import { AppScreenPropsProvider } from "./components/AppScreen/AppScreen";
3
+ import { GlobalInteraction } from "./primitive";
4
+
5
+ export interface SeedPluginOptions {
6
+ theme: "android" | "cupertino";
7
+ }
8
+
9
+ export const seedPlugin =
10
+ (options: SeedPluginOptions): StackflowReactPlugin =>
11
+ () => ({
12
+ key: "seed-design",
13
+ wrapStack({ stack }) {
14
+ return (
15
+ <AppScreenPropsProvider value={options}>
16
+ <GlobalInteraction>{stack.render()}</GlobalInteraction>
17
+ </AppScreenPropsProvider>
18
+ );
19
+ },
20
+ });
@@ -0,0 +1,10 @@
1
+ export {
2
+ AppBarRoot as Root,
3
+ AppBarMain as Main,
4
+ AppBarLeft as Left,
5
+ AppBarRight as Right,
6
+ type AppBarRootProps as RootProps,
7
+ type AppBarMainProps as MainProps,
8
+ type AppBarLeftProps as LeftProps,
9
+ type AppBarRightProps as RightProps,
10
+ } from "./AppBar";
@@ -0,0 +1,52 @@
1
+ import { Primitive, type PrimitiveProps } from "@seed-design/react-primitive";
2
+ import { forwardRef } from "react";
3
+ import { useAppBar, type UseAppBarProps } from "./useAppBar";
4
+ import { AppBarProvider, useAppBarContext } from "./useAppBarContext";
5
+ import { mergeProps } from "@seed-design/dom-utils";
6
+ import { composeRefs } from "@radix-ui/react-compose-refs";
7
+
8
+ export interface AppBarRootProps
9
+ extends PrimitiveProps,
10
+ UseAppBarProps,
11
+ React.HTMLAttributes<HTMLDivElement> {}
12
+
13
+ export const AppBarRoot = forwardRef<HTMLDivElement, AppBarRootProps>((props, ref) => {
14
+ const { ...otherProps } = props;
15
+ const api = useAppBar({});
16
+
17
+ return (
18
+ <AppBarProvider value={api}>
19
+ <Primitive.div
20
+ ref={composeRefs(api.refs.root, ref)}
21
+ {...mergeProps(api.rootProps, otherProps)}
22
+ />
23
+ </AppBarProvider>
24
+ );
25
+ });
26
+ AppBarRoot.displayName = "AppBarRoot";
27
+
28
+ export interface AppBarLeftProps extends PrimitiveProps, React.HTMLAttributes<HTMLDivElement> {}
29
+
30
+ export const AppBarLeft = forwardRef<HTMLDivElement, AppBarLeftProps>((props, ref) => {
31
+ const { refs, stateProps } = useAppBarContext();
32
+
33
+ return <Primitive.div ref={composeRefs(refs.left, ref)} {...mergeProps(stateProps, props)} />;
34
+ });
35
+ AppBarLeft.displayName = "AppBarLeft";
36
+
37
+ export interface AppBarRightProps extends PrimitiveProps, React.HTMLAttributes<HTMLDivElement> {}
38
+
39
+ export const AppBarRight = forwardRef<HTMLDivElement, AppBarRightProps>((props, ref) => {
40
+ const { refs, stateProps } = useAppBarContext();
41
+
42
+ return <Primitive.div ref={composeRefs(refs.right, ref)} {...mergeProps(stateProps, props)} />;
43
+ });
44
+ AppBarRight.displayName = "AppBarRight";
45
+
46
+ export interface AppBarMainProps extends PrimitiveProps, React.HTMLAttributes<HTMLDivElement> {}
47
+
48
+ export const AppBarMain = forwardRef<HTMLDivElement, AppBarMainProps>((props, ref) => {
49
+ const { stateProps } = useAppBarContext();
50
+
51
+ return <Primitive.div ref={ref} {...mergeProps(stateProps, props)} />;
52
+ });
@@ -0,0 +1,12 @@
1
+ export {
2
+ AppBarLeft,
3
+ AppBarRight,
4
+ AppBarRoot,
5
+ AppBarMain,
6
+ type AppBarLeftProps,
7
+ type AppBarRightProps,
8
+ type AppBarRootProps,
9
+ type AppBarMainProps,
10
+ } from "./AppBar";
11
+
12
+ export * as AppBar from "./AppBar.namespace";
@@ -0,0 +1,42 @@
1
+ import { elementProps } from "@seed-design/dom-utils";
2
+ import { useMemo, useState } from "react";
3
+ import { useAppScreenContext } from "../AppScreen";
4
+ import { useElementOffset } from "../private/useElementOffset";
5
+
6
+ // biome-ignore lint/suspicious/noEmptyInterface: intentionally empty for future extension
7
+ export interface UseAppBarProps {}
8
+
9
+ export type UseAppBarReturn = ReturnType<typeof useAppBar>;
10
+
11
+ export function useAppBar(_props: UseAppBarProps) {
12
+ const { stateProps } = useAppScreenContext();
13
+
14
+ const [root, rootRef] = useState<HTMLElement | null>(null);
15
+ const [left, leftRef] = useState<HTMLElement | null>(null);
16
+ const [right, rightRef] = useState<HTMLElement | null>(null);
17
+
18
+ const leftOffset = useElementOffset(left);
19
+ const rightOffset = useElementOffset(right);
20
+ const centeredTitlePaddingX = root
21
+ ? `${Math.max(leftOffset?.fromLeft ?? 0, rightOffset?.fromRight ?? 0)}px`
22
+ : "initial";
23
+
24
+ return useMemo(
25
+ () => ({
26
+ refs: {
27
+ root: rootRef,
28
+ left: leftRef,
29
+ right: rightRef,
30
+ },
31
+ stateProps,
32
+ rootProps: elementProps({
33
+ "data-part": "appBar",
34
+ ...stateProps,
35
+ style: {
36
+ "--centered-title-padding-x": centeredTitlePaddingX,
37
+ } as React.CSSProperties,
38
+ }),
39
+ }),
40
+ [stateProps, centeredTitlePaddingX],
41
+ );
42
+ }
@@ -0,0 +1,19 @@
1
+ import { createContext, useContext } from "react";
2
+ import type { UseAppBarReturn } from "./useAppBar";
3
+
4
+ export interface UseAppBarContext extends UseAppBarReturn {}
5
+
6
+ const AppBarContext = createContext<UseAppBarContext | null>(null);
7
+
8
+ export const AppBarProvider = AppBarContext.Provider;
9
+
10
+ export function useAppBarContext<T extends boolean | undefined = true>({
11
+ strict = true,
12
+ }: { strict?: T } = {}): T extends false ? UseAppBarContext | null : UseAppBarContext {
13
+ const context = useContext(AppBarContext);
14
+ if (!context && strict) {
15
+ throw new Error("useAppBarContext must be used within a AppBar");
16
+ }
17
+
18
+ return context as UseAppBarContext;
19
+ }
@@ -0,0 +1,10 @@
1
+ export {
2
+ AppScreenDim as Dim,
3
+ AppScreenEdge as Edge,
4
+ AppScreenLayer as Layer,
5
+ AppScreenRoot as Root,
6
+ type AppScreenDimProps as DimProps,
7
+ type AppScreenEdgeProps as EdgeProps,
8
+ type AppScreenLayerProps as LayerProps,
9
+ type AppScreenRootProps as RootProps,
10
+ } from "./AppScreen";