@ahnseungchan/webviewkit 2.0.0 → 2.1.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.
package/README.md CHANGED
@@ -63,8 +63,8 @@ export default function App() {
63
63
  |------|------|------|
64
64
  | `initialActivity` | — | 루트 페이지. 마운트 시 1회만 읽음 |
65
65
  | `Activities` | `[]` | `{ path, element }[]`. path 에 `:param` 사용 가능 |
66
- | `transition` | `"slide"` | `"slide"` · `"fade"` · `"none"` |
67
- | `duration` | `300` | 전환 ms |
66
+ | `transition` | `"slide"` | `"slide"`(iOS 네이티브 스택: 패럴랙스·dim·가장자리 그림자) · `"fade"` · `"none"` |
67
+ | `duration` | `500` | 전환 ms |
68
68
  | `restoreStack` | `"with-root"` | 딥링크 복원 정책 |
69
69
  | `onBackAtRoot` / `onNotFound` | — | 콜백 |
70
70
  | `isShowHeader` | `false` | `StackHeader` 렌더 |
@@ -81,7 +81,7 @@ export default function App() {
81
81
  | `useActivity()` | 현재 페이지의 `{ key, path, pattern, params, query, data, index, isTop }` |
82
82
  | `useParams()` | `params` |
83
83
 
84
- 테마는 CSS 변수로 바꿉니다: `--wvk-page-bg`, `--wvk-header-bg`, `--wvk-header-border`, `--wvk-header-color`, `--wvk-back-color`.
84
+ 테마는 CSS 변수로 바꿉니다: `--wvk-page-bg`, `--wvk-dim-color`, `--wvk-edge-shadow`, `--wvk-header-bg`, `--wvk-header-border`, `--wvk-header-color`, `--wvk-back-color`.
85
85
 
86
86
  ## Native Bridge
87
87
 
package/dist/index.d.mts CHANGED
@@ -266,8 +266,11 @@ type WebViewBridgeProviderProps = {
266
266
  */
267
267
  declare function WebViewBridgeProvider({ children, stack, transport, enableOutboundBridge, hostOptions, }: WebViewBridgeProviderProps): _emotion_react_jsx_runtime.JSX.Element;
268
268
 
269
- /** push/back 전환 기본 시간 (ms). `StackRouterProvider` `duration` 으로 조정 */
270
- declare const STACK_TRANSITION_MS = 300;
269
+ /**
270
+ * push/back 전환 기본 시간 (ms). iOS 네비게이션 전환 체감에 맞춘 값.
271
+ * `StackRouterProvider` `duration` 으로 조정
272
+ */
273
+ declare const STACK_TRANSITION_MS = 500;
271
274
  /** `history.back()` 이 popstate 를 기다리는 최대 시간 */
272
275
  declare const BACK_TIMEOUT_MS = 1000;
273
276
  /** 브릿지 요청 응답 기본 타임아웃 */
package/dist/index.d.ts CHANGED
@@ -266,8 +266,11 @@ type WebViewBridgeProviderProps = {
266
266
  */
267
267
  declare function WebViewBridgeProvider({ children, stack, transport, enableOutboundBridge, hostOptions, }: WebViewBridgeProviderProps): _emotion_react_jsx_runtime.JSX.Element;
268
268
 
269
- /** push/back 전환 기본 시간 (ms). `StackRouterProvider` `duration` 으로 조정 */
270
- declare const STACK_TRANSITION_MS = 300;
269
+ /**
270
+ * push/back 전환 기본 시간 (ms). iOS 네비게이션 전환 체감에 맞춘 값.
271
+ * `StackRouterProvider` `duration` 으로 조정
272
+ */
273
+ declare const STACK_TRANSITION_MS = 500;
271
274
  /** `history.back()` 이 popstate 를 기다리는 최대 시간 */
272
275
  declare const BACK_TIMEOUT_MS = 1000;
273
276
  /** 브릿지 요청 응답 기본 타임아웃 */
package/dist/index.js CHANGED
@@ -84,7 +84,7 @@ __export(index_exports, {
84
84
  module.exports = __toCommonJS(index_exports);
85
85
 
86
86
  // src/constants.ts
87
- var STACK_TRANSITION_MS = 300;
87
+ var STACK_TRANSITION_MS = 500;
88
88
  var TRANSITION_END_GRACE_MS = 50;
89
89
  var BACK_TIMEOUT_MS = 1e3;
90
90
  var BRIDGE_TIMEOUT_MS = 5e3;
@@ -900,22 +900,33 @@ var import_react3 = require("@emotion/react");
900
900
  var import_styled2 = __toESM(require("@emotion/styled"));
901
901
  var import_react4 = require("react");
902
902
  var import_jsx_runtime4 = require("@emotion/react/jsx-runtime");
903
- var slideInRight = import_react3.keyframes`
903
+ var IOS_EASING = "cubic-bezier(0.32, 0.72, 0, 1)";
904
+ var FADE_EASING = "ease";
905
+ var PARALLAX_OFFSET = "-30%";
906
+ var coverIn = import_react3.keyframes`
904
907
  from { transform: translate3d(100%, 0, 0); }
905
908
  to { transform: translate3d(0, 0, 0); }
906
909
  `;
907
- var slideOutLeft = import_react3.keyframes`
908
- from { transform: translate3d(0, 0, 0); opacity: 1; }
909
- to { transform: translate3d(-30%, 0, 0); opacity: 0.6; }
910
- `;
911
- var slideInLeft = import_react3.keyframes`
912
- from { transform: translate3d(-30%, 0, 0); opacity: 0.6; }
913
- to { transform: translate3d(0, 0, 0); opacity: 1; }
914
- `;
915
- var slideOutRight = import_react3.keyframes`
910
+ var coverOut = import_react3.keyframes`
916
911
  from { transform: translate3d(0, 0, 0); }
917
912
  to { transform: translate3d(100%, 0, 0); }
918
913
  `;
914
+ var parallaxOut = import_react3.keyframes`
915
+ from { transform: translate3d(0, 0, 0); }
916
+ to { transform: translate3d(${PARALLAX_OFFSET}, 0, 0); }
917
+ `;
918
+ var parallaxIn = import_react3.keyframes`
919
+ from { transform: translate3d(${PARALLAX_OFFSET}, 0, 0); }
920
+ to { transform: translate3d(0, 0, 0); }
921
+ `;
922
+ var dimIn = import_react3.keyframes`
923
+ from { opacity: 0; }
924
+ to { opacity: 1; }
925
+ `;
926
+ var dimOut = import_react3.keyframes`
927
+ from { opacity: 1; }
928
+ to { opacity: 0; }
929
+ `;
919
930
  var fadeIn = import_react3.keyframes`
920
931
  from { opacity: 0; }
921
932
  to { opacity: 1; }
@@ -924,14 +935,27 @@ var fadeOut = import_react3.keyframes`
924
935
  from { opacity: 1; }
925
936
  to { opacity: 0; }
926
937
  `;
927
- var SLIDE_KEYFRAMES = {
928
- push: { enter: slideInRight, exit: slideOutLeft },
929
- pop: { enter: slideInLeft, exit: slideOutRight }
930
- };
931
- var FADE_KEYFRAMES = { enter: fadeIn, exit: fadeOut };
932
- function pickKeyframes(transition, direction, pageState) {
933
- if (transition === "fade") return FADE_KEYFRAMES[pageState];
934
- return SLIDE_KEYFRAMES[direction][pageState];
938
+ var layerOf = (direction, pageState) => direction === "push" === (pageState === "enter") ? "upper" : "lower";
939
+ function slideStyles(direction, pageState, duration) {
940
+ const timing = `${duration}ms ${IOS_EASING} forwards`;
941
+ if (layerOf(direction, pageState) === "upper") {
942
+ return import_react3.css`
943
+ animation: ${direction === "push" ? coverIn : coverOut} ${timing};
944
+ box-shadow: var(--wvk-edge-shadow, -6px 0 24px rgba(0, 0, 0, 0.12));
945
+ `;
946
+ }
947
+ return import_react3.css`
948
+ animation: ${direction === "push" ? parallaxOut : parallaxIn} ${timing};
949
+ &::after {
950
+ content: "";
951
+ /* transform 중인 페이지 기준 fixed = 스크롤과 무관하게 페이지 전체를 덮음 */
952
+ position: fixed;
953
+ inset: 0;
954
+ pointer-events: none;
955
+ background: var(--wvk-dim-color, rgba(0, 0, 0, 0.12));
956
+ animation: ${direction === "push" ? dimIn : dimOut} ${timing};
957
+ }
958
+ `;
935
959
  }
936
960
  var PageWrapper = import_styled2.default.div`
937
961
  position: absolute;
@@ -944,10 +968,16 @@ var PageWrapper = import_styled2.default.div`
944
968
  ${({ pageState, direction, transitionType, duration }) => {
945
969
  if (pageState !== "enter" && pageState !== "exit") return "";
946
970
  if (transitionType === "none" || duration === 0) return "";
971
+ if (transitionType === "fade") {
972
+ return import_react3.css`
973
+ will-change: opacity;
974
+ animation: ${pageState === "enter" ? fadeIn : fadeOut} ${duration}ms
975
+ ${FADE_EASING} forwards;
976
+ `;
977
+ }
947
978
  return import_react3.css`
948
- will-change: transform, opacity;
949
- animation: ${pickKeyframes(transitionType, direction, pageState)}
950
- ${duration}ms ease-in-out forwards;
979
+ will-change: transform;
980
+ ${slideStyles(direction, pageState, duration)}
951
981
  `;
952
982
  }}
953
983
  `;
@@ -970,7 +1000,9 @@ function StackPage({
970
1000
  "data-state": wrapperProps.pageState,
971
1001
  "aria-hidden": isTop ? void 0 : true,
972
1002
  onAnimationEnd: (event) => {
973
- if (event.target === event.currentTarget) onAnimationEnd == null ? void 0 : onAnimationEnd();
1003
+ if (event.target === event.currentTarget && !event.pseudoElement) {
1004
+ onAnimationEnd == null ? void 0 : onAnimationEnd();
1005
+ }
974
1006
  },
975
1007
  children
976
1008
  }
@@ -1283,12 +1315,8 @@ function useStackNavigation(options) {
1283
1315
  const delta = Math.min(Math.max(1, count), entries.length - 1);
1284
1316
  const targetIndex = entries.length - 1 - delta;
1285
1317
  busyRef.current = true;
1286
- void history.travel(-delta).then((popped) => {
1287
- if (!popped) {
1288
- busyRef.current = false;
1289
- dispatch({ type: "POP_TO", index: targetIndex, animate: false });
1290
- }
1291
- });
1318
+ dispatch({ type: "POP_TO", index: targetIndex, animate: animate() });
1319
+ void history.travel(-delta);
1292
1320
  return true;
1293
1321
  }, []);
1294
1322
  const back = (0, import_react8.useCallback)(() => pop(1), [pop]);