@seed-design/stackflow 1.1.24 → 2.0.0

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.
@@ -9,10 +9,6 @@ export declare const AppBarPropsProvider: ({ children, value }: {
9
9
  value: Partial< AppBarVariant>;
10
10
  }) => import("react/jsx-runtime").JSX.Element;
11
11
  export interface AppBarProps extends AppBarVariantProps, AppBarPrimitive.RootProps, BoxBackgroundProps {
12
- /**
13
- * @deprecated SEED React 1.3에서 제거될 예정인 옵션입니다. 옵션 제거 이후에는 AppBar 하단 구분선이 표시되지 않습니다.
14
- */
15
- divider?: AppBarVariantProps["divider"];
16
12
  }
17
13
  export declare const AppBarRoot: ForwardRefExoticComponent<AppBarProps & RefAttributes<HTMLDivElement>>;
18
14
  export interface AppBarLeftProps extends AppBarPrimitive.LeftProps {
@@ -6,6 +6,7 @@ let _seed_design_dom_utils = require("@seed-design/dom-utils");
6
6
  let _seed_design_react_primitive = require("@seed-design/react-primitive");
7
7
  let react = require("react");
8
8
  let _radix_ui_react_compose_refs = require("@radix-ui/react-compose-refs");
9
+ let _radix_ui_react_focus_scope = require("@radix-ui/react-focus-scope");
9
10
  let _stackflow_react_ui_core = require("@stackflow/react-ui-core");
10
11
  let react_jsx_runtime = require("react/jsx-runtime");
11
12
  //#region src/primitive/AppScreen/AppScreen.tsx
@@ -20,12 +21,21 @@ var AppScreenRoot = (0, react.forwardRef)((props, ref) => {
20
21
  onSwipeBackStart
21
22
  });
22
23
  (0, _stackflow_react_ui_core.usePreventTouchDuringTransition)({ ref: innerRef });
24
+ (0, react.useEffect)(() => {
25
+ if (api.activity?.transitionState === "enter-done") api.layerRef.current?.focus();
26
+ }, [api.activity?.transitionState, api.layerRef]);
23
27
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_useAppScreenContext.AppScreenProvider, {
24
28
  value: api,
25
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_seed_design_react_primitive.Primitive.div, {
26
- ref: (0, _radix_ui_react_compose_refs.composeRefs)(innerRef, ref),
27
- "data-stackflow-component-name": "AppScreen",
28
- ...(0, _seed_design_dom_utils.mergeProps)(api.activityProps, otherProps)
29
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_focus_scope.FocusScope, {
30
+ asChild: true,
31
+ trapped: true,
32
+ loop: true,
33
+ onMountAutoFocus: (e) => e.preventDefault(),
34
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_seed_design_react_primitive.Primitive.div, {
35
+ ref: (0, _radix_ui_react_compose_refs.composeRefs)(innerRef, ref),
36
+ "data-stackflow-component-name": "AppScreen",
37
+ ...(0, _seed_design_dom_utils.mergeProps)(api.activityProps, otherProps)
38
+ })
29
39
  })
30
40
  });
31
41
  });
@@ -47,9 +57,10 @@ var AppScreenEdge = (0, react.forwardRef)((props, ref) => {
47
57
  });
48
58
  AppScreenEdge.displayName = "AppScreenEdge";
49
59
  var AppScreenLayer = (0, react.forwardRef)((props, ref) => {
50
- const { layerProps } = require_useAppScreenContext.useAppScreenContext();
60
+ const { layerProps, layerRef } = require_useAppScreenContext.useAppScreenContext();
51
61
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_seed_design_react_primitive.Primitive.div, {
52
- ref,
62
+ ref: (0, _radix_ui_react_compose_refs.composeRefs)(ref, layerRef),
63
+ tabIndex: -1,
53
64
  ...(0, _seed_design_dom_utils.mergeProps)(layerProps, props)
54
65
  });
55
66
  });
@@ -3,8 +3,9 @@ import { useAppScreen } from "./useAppScreen.js";
3
3
  import { AppScreenProvider, useAppScreenContext } from "./useAppScreenContext.js";
4
4
  import { mergeProps } from "@seed-design/dom-utils";
5
5
  import { Primitive } from "@seed-design/react-primitive";
6
- import { forwardRef, useRef } from "react";
6
+ import { forwardRef, useEffect, useRef } from "react";
7
7
  import { composeRefs } from "@radix-ui/react-compose-refs";
8
+ import { FocusScope } from "@radix-ui/react-focus-scope";
8
9
  import { usePreventTouchDuringTransition } from "@stackflow/react-ui-core";
9
10
  import { jsx } from "react/jsx-runtime";
10
11
  //#region src/primitive/AppScreen/AppScreen.tsx
@@ -19,12 +20,21 @@ var AppScreenRoot = forwardRef((props, ref) => {
19
20
  onSwipeBackStart
20
21
  });
21
22
  usePreventTouchDuringTransition({ ref: innerRef });
23
+ useEffect(() => {
24
+ if (api.activity?.transitionState === "enter-done") api.layerRef.current?.focus();
25
+ }, [api.activity?.transitionState, api.layerRef]);
22
26
  return /* @__PURE__ */ jsx(AppScreenProvider, {
23
27
  value: api,
24
- children: /* @__PURE__ */ jsx(Primitive.div, {
25
- ref: composeRefs(innerRef, ref),
26
- "data-stackflow-component-name": "AppScreen",
27
- ...mergeProps(api.activityProps, otherProps)
28
+ children: /* @__PURE__ */ jsx(FocusScope, {
29
+ asChild: true,
30
+ trapped: true,
31
+ loop: true,
32
+ onMountAutoFocus: (e) => e.preventDefault(),
33
+ children: /* @__PURE__ */ jsx(Primitive.div, {
34
+ ref: composeRefs(innerRef, ref),
35
+ "data-stackflow-component-name": "AppScreen",
36
+ ...mergeProps(api.activityProps, otherProps)
37
+ })
28
38
  })
29
39
  });
30
40
  });
@@ -46,9 +56,10 @@ var AppScreenEdge = forwardRef((props, ref) => {
46
56
  });
47
57
  AppScreenEdge.displayName = "AppScreenEdge";
48
58
  var AppScreenLayer = forwardRef((props, ref) => {
49
- const { layerProps } = useAppScreenContext();
59
+ const { layerProps, layerRef } = useAppScreenContext();
50
60
  return /* @__PURE__ */ jsx(Primitive.div, {
51
- ref,
61
+ ref: composeRefs(ref, layerRef),
62
+ tabIndex: -1,
52
63
  ...mergeProps(layerProps, props)
53
64
  });
54
65
  });
@@ -9,6 +9,7 @@ let _stackflow_react_ui_core = require("@stackflow/react-ui-core");
9
9
  //#region src/primitive/AppScreen/useAppScreen.tsx
10
10
  function useAppScreen(props) {
11
11
  const activity = (0, _stackflow_react_ui_core.useNullableActivity)();
12
+ const layerRef = (0, react.useRef)(null);
12
13
  const transitionState = activity?.transitionState ?? "enter-done";
13
14
  const { activityProps, layerProps, edgeProps } = require_useSwipeBack.useSwipeBack(props);
14
15
  const zIndexBase = require_useActivityZIndexBase.useActivityZIndexBase();
@@ -24,6 +25,7 @@ function useAppScreen(props) {
24
25
  ]);
25
26
  return (0, react.useMemo)(() => ({
26
27
  activity,
28
+ layerRef,
27
29
  stateProps,
28
30
  activityProps: (0, _seed_design_dom_utils.elementProps)({
29
31
  "data-part": require_anatomy.appScreenAnatomy.activity,
@@ -1,11 +1,12 @@
1
1
  import { UseSwipeBackProps } from '../GlobalInteraction/useSwipeBack';
2
2
  import { Activity } from '@stackflow/core';
3
- import { HTMLAttributes, CSSProperties, AriaRole, ReactNode, ClipboardEventHandler, CompositionEventHandler, FocusEventHandler, FormEventHandler, InputEventHandler, ReactEventHandler, KeyboardEventHandler, MouseEventHandler, DragEventHandler, TouchEventHandler, PointerEventHandler, UIEventHandler, WheelEventHandler, AnimationEventHandler, ToggleEventHandler, TransitionEventHandler } from 'react';
3
+ import { RefObject, HTMLAttributes, CSSProperties, AriaRole, ReactNode, ClipboardEventHandler, CompositionEventHandler, FocusEventHandler, FormEventHandler, InputEventHandler, ReactEventHandler, KeyboardEventHandler, MouseEventHandler, DragEventHandler, TouchEventHandler, PointerEventHandler, UIEventHandler, WheelEventHandler, AnimationEventHandler, ToggleEventHandler, TransitionEventHandler } from 'react';
4
4
  export interface UseAppScreenProps extends UseSwipeBackProps {
5
5
  }
6
6
  export type UseAppScreenReturn = ReturnType<typeof useAppScreen>;
7
7
  export declare function useAppScreen(props: UseAppScreenProps): {
8
8
  activity: Activity | null;
9
+ layerRef: RefObject<HTMLDivElement | null>;
9
10
  stateProps: HTMLAttributes<HTMLElement>;
10
11
  activityProps: {
11
12
  slot?: string | undefined | undefined;
@@ -3,11 +3,12 @@ import { useSwipeBack } from "../GlobalInteraction/useSwipeBack.js";
3
3
  import { useActivityZIndexBase } from "../../hooks/useActivityZIndexBase.js";
4
4
  import { appScreenAnatomy } from "./anatomy.js";
5
5
  import { dataAttr, elementProps } from "@seed-design/dom-utils";
6
- import { useMemo } from "react";
6
+ import { useMemo, useRef } from "react";
7
7
  import { useNullableActivity } from "@stackflow/react-ui-core";
8
8
  //#region src/primitive/AppScreen/useAppScreen.tsx
9
9
  function useAppScreen(props) {
10
10
  const activity = useNullableActivity();
11
+ const layerRef = useRef(null);
11
12
  const transitionState = activity?.transitionState ?? "enter-done";
12
13
  const { activityProps, layerProps, edgeProps } = useSwipeBack(props);
13
14
  const zIndexBase = useActivityZIndexBase();
@@ -23,6 +24,7 @@ function useAppScreen(props) {
23
24
  ]);
24
25
  return useMemo(() => ({
25
26
  activity,
27
+ layerRef,
26
28
  stateProps,
27
29
  activityProps: elementProps({
28
30
  "data-part": appScreenAnatomy.activity,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seed-design/stackflow",
3
- "version": "1.1.24",
3
+ "version": "2.0.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/daangn/seed-design.git",
@@ -28,15 +28,16 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "@radix-ui/react-compose-refs": "^1.1.2",
31
+ "@radix-ui/react-focus-scope": "^1.1.4",
31
32
  "@radix-ui/react-slot": "^1.2.3",
32
33
  "@radix-ui/react-use-callback-ref": "^1.1.1",
33
- "@seed-design/dom-utils": "1.0.0",
34
- "@seed-design/react-primitive": "1.0.0",
34
+ "@seed-design/dom-utils": "^2.0.0",
35
+ "@seed-design/react-primitive": "^2.0.0",
35
36
  "@stackflow/react-ui-core": "^1.3.3",
36
37
  "clsx": "^2.1.1"
37
38
  },
38
39
  "devDependencies": {
39
- "@seed-design/css": "1.2.16",
40
+ "@seed-design/css": "2.0.0",
40
41
  "@stackflow/core": "^1.2.0",
41
42
  "@stackflow/react": "^1.5.1",
42
43
  "@types/react": "^19.2.2",
@@ -49,7 +50,7 @@
49
50
  "vite-plugin-dts": "^5.0.0"
50
51
  },
51
52
  "peerDependencies": {
52
- "@seed-design/css": ">=1.1.25 <1.2.0 || >=1.2.11",
53
+ "@seed-design/css": "^2.0.0",
53
54
  "@stackflow/core": ">=1.1.0",
54
55
  "@stackflow/react": ">=1.4.1",
55
56
  "react": ">=18.0.0",
@@ -26,12 +26,7 @@ export const AppBarPropsProvider = PropsProvider;
26
26
  export interface AppBarProps
27
27
  extends AppBarVariantProps,
28
28
  AppBarPrimitive.RootProps,
29
- BoxBackgroundProps {
30
- /**
31
- * @deprecated SEED React 1.3에서 제거될 예정인 옵션입니다. 옵션 제거 이후에는 AppBar 하단 구분선이 표시되지 않습니다.
32
- */
33
- divider?: AppBarVariantProps["divider"];
34
- }
29
+ BoxBackgroundProps {}
35
30
 
36
31
  export const AppBarRoot = forwardRef<HTMLDivElement, AppBarProps>((props, ref) => {
37
32
  const { style: boxStyle, restProps: propsWithoutBoxProps } = useBoxBackgroundProps(props);
@@ -1,8 +1,9 @@
1
1
  import { composeRefs } from "@radix-ui/react-compose-refs";
2
+ import { FocusScope } from "@radix-ui/react-focus-scope";
2
3
  import { mergeProps } from "@seed-design/dom-utils";
3
4
  import { Primitive, type PrimitiveProps } from "@seed-design/react-primitive";
4
5
  import { usePreventTouchDuringTransition } from "@stackflow/react-ui-core";
5
- import { forwardRef, useRef } from "react";
6
+ import { forwardRef, useEffect, useRef } from "react";
6
7
  import { useAppScreen, type UseAppScreenProps } from "./useAppScreen";
7
8
  import { AppScreenProvider, useAppScreenContext } from "./useAppScreenContext";
8
9
 
@@ -32,13 +33,29 @@ export const AppScreenRoot = forwardRef<HTMLDivElement, AppScreenRootProps>((pro
32
33
  ref: innerRef as React.RefObject<HTMLDivElement>,
33
34
  });
34
35
 
36
+ // Focus the layer once after the enter animation completes.
37
+ // onMountAutoFocus fires during enter-active and interrupts the CSS animation, so we
38
+ // defer until transitionState reaches "enter-done". This only happens once per activity
39
+ // lifecycle — subsequent isTop changes (e.g. upper activity pop) don't change
40
+ // transitionState, so the effect won't re-fire.
41
+ useEffect(() => {
42
+ if (api.activity?.transitionState === "enter-done") {
43
+ api.layerRef.current?.focus();
44
+ }
45
+ }, [api.activity?.transitionState, api.layerRef]);
46
+
35
47
  return (
36
48
  <AppScreenProvider value={api}>
37
- <Primitive.div
38
- ref={composeRefs(innerRef, ref)}
39
- data-stackflow-component-name="AppScreen"
40
- {...mergeProps(api.activityProps, otherProps)}
41
- />
49
+ {/* onMountAutoFocus is prevented because it fires during enter-active and
50
+ interrupts the CSS animation. Focus is handled by the useEffect above instead,
51
+ which defers until transitionState reaches "enter-done". */}
52
+ <FocusScope asChild trapped loop onMountAutoFocus={(e) => e.preventDefault()}>
53
+ <Primitive.div
54
+ ref={composeRefs(innerRef, ref)}
55
+ data-stackflow-component-name="AppScreen"
56
+ {...mergeProps(api.activityProps, otherProps)}
57
+ />
58
+ </FocusScope>
42
59
  </AppScreenProvider>
43
60
  );
44
61
  });
@@ -65,8 +82,14 @@ AppScreenEdge.displayName = "AppScreenEdge";
65
82
  export interface AppScreenLayerProps extends PrimitiveProps, React.HTMLAttributes<HTMLDivElement> {}
66
83
 
67
84
  export const AppScreenLayer = forwardRef<HTMLDivElement, AppScreenLayerProps>((props, ref) => {
68
- const { layerProps } = useAppScreenContext();
85
+ const { layerProps, layerRef } = useAppScreenContext();
69
86
 
70
- return <Primitive.div ref={ref} {...mergeProps(layerProps, props)} />;
87
+ return (
88
+ <Primitive.div
89
+ ref={composeRefs(ref, layerRef)}
90
+ tabIndex={-1}
91
+ {...mergeProps(layerProps, props)}
92
+ />
93
+ );
71
94
  });
72
95
  AppScreenLayer.displayName = "AppScreenLayer";
@@ -1,6 +1,6 @@
1
1
  import { dataAttr, elementProps } from "@seed-design/dom-utils";
2
2
  import { useNullableActivity } from "@stackflow/react-ui-core";
3
- import { useMemo } from "react";
3
+ import { useMemo, useRef } from "react";
4
4
  import { type UseSwipeBackProps, useSwipeBack } from "../GlobalInteraction/useSwipeBack";
5
5
  import { useActivityZIndexBase } from "../../hooks";
6
6
  import { appScreenAnatomy } from "./anatomy";
@@ -11,6 +11,7 @@ export type UseAppScreenReturn = ReturnType<typeof useAppScreen>;
11
11
 
12
12
  export function useAppScreen(props: UseAppScreenProps) {
13
13
  const activity = useNullableActivity();
14
+ const layerRef = useRef<HTMLDivElement>(null);
14
15
 
15
16
  const transitionState = activity?.transitionState ?? "enter-done";
16
17
 
@@ -37,6 +38,7 @@ export function useAppScreen(props: UseAppScreenProps) {
37
38
  return useMemo(
38
39
  () => ({
39
40
  activity,
41
+ layerRef,
40
42
  stateProps,
41
43
  activityProps: elementProps({
42
44
  "data-part": appScreenAnatomy.activity,