@kne/system-layout 0.2.0-alpha.22 → 0.2.0-alpha.24

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.
@@ -10,7 +10,7 @@ import Icon from '@kne/react-icon';
10
10
  import ensureSlash from '@kne/ensure-slash';
11
11
  import isPlainObject from 'lodash/isPlainObject';
12
12
  import VirtualList from 'rc-virtual-list';
13
- import { useScrollElement, usePopupContainer, useResponsiveContext, useIsMobile, RESPONSIVE_BOUNDARY_CLASS, RESPONSIVE_CONTAINER_CLASS, RESPONSIVE_SCROLL_CLASS, findResponsiveScroll, getDefaultScrollElement, ResponsiveProvider, defaultResponsiveContextValue, IS_MOBILE_QUERY } from '@kne/responsive-utils';
13
+ import { useScrollElement, usePopupContainer, useResponsiveContext, useIsMobile, RESPONSIVE_BOUNDARY_CLASS, RESPONSIVE_CONTAINER_CLASS, RESPONSIVE_SCROLL_CLASS, findResponsiveScroll, findScrollParent, getDefaultScrollElement, ResponsiveProvider, IS_MOBILE_QUERY } from '@kne/responsive-utils';
14
14
  export { MOBILE_BREAKPOINT, RESPONSIVE_BOUNDARY_CLASS, RESPONSIVE_CONTAINER_CLASS, RESPONSIVE_SCROLL_CLASS, ResponsiveProvider, MOBILE_BREAKPOINT as mobileBreakpoint, useBreakpoint, useIsMobile, useMediaQuery, usePopupContainer, useResponsiveContext, useScrollElement } from '@kne/responsive-utils';
15
15
  import defaultLogo from './defaultLogo~beHinxEU.svg';
16
16
  import { ReactComponent } from './expand~bdCTnjYJ.svg';
@@ -535,16 +535,19 @@ const ErrorBoundary = props => {
535
535
  }));
536
536
  };
537
537
 
538
- const hasParentResponsiveProvider = parent => {
539
- return parent.getBoundaryElement !== defaultResponsiveContextValue.getBoundaryElement || parent.getScrollElement !== defaultResponsiveContextValue.getScrollElement;
540
- };
541
538
  const resolvePageScrollElement = (pageScrollRef, openScrollbar, deviceIsMobile) => {
542
539
  if (deviceIsMobile && !openScrollbar) {
543
540
  const _anchor = pageScrollRef.current;
544
- if (_anchor && _anchor.parentElement) {
545
- const parentScroll = findResponsiveScroll(_anchor.parentElement);
546
- if (parentScroll) {
547
- return parentScroll;
541
+ if (_anchor) {
542
+ // example 手机外框:滚动在 SimpleBar wrapper 上;class 可能晚于首帧挂上,
543
+ // findScrollParent 不依赖 kne-responsive-scroll,避免 is-scrolled 监听绑错。
544
+ const marked = findResponsiveScroll(_anchor.parentElement || _anchor);
545
+ if (marked) {
546
+ return marked;
547
+ }
548
+ const scrollParent = findScrollParent(_anchor);
549
+ if (scrollParent) {
550
+ return scrollParent;
548
551
  }
549
552
  }
550
553
  return getDefaultScrollElement();
@@ -561,6 +564,10 @@ const resolvePageScrollElement = (pageScrollRef, openScrollbar, deviceIsMobile)
561
564
  }
562
565
  return findResponsiveScroll(anchor) || getDefaultScrollElement();
563
566
  };
567
+
568
+ // 始终覆盖本页 scroller(否则嵌套在 Global 下 sticky 会绑到 body)。
569
+ // container 模式(example 手机外框)保留外层 boundary,toolbar/menu 才能 portal 到
570
+ // .example-driver-device-scroll(非滚动内容区),absolute/fixed 相对手机视口生效。
564
571
  const LayoutResponsiveScope = ({
565
572
  boundaryRef,
566
573
  scrollRef,
@@ -568,13 +575,17 @@ const LayoutResponsiveScope = ({
568
575
  children
569
576
  }) => {
570
577
  const parent = useResponsiveContext();
571
- if (hasParentResponsiveProvider(parent)) {
572
- return children;
573
- }
578
+ const inheritBoundary = parent.mode === 'container';
574
579
  return /*#__PURE__*/jsx(ResponsiveProvider, {
580
+ mode: parent.mode,
581
+ containerWidth: parent.containerWidth,
575
582
  boundaryRef: boundaryRef,
576
583
  scrollRef: scrollRef,
577
584
  getScrollElement: getScrollElement,
585
+ getBoundaryElement: inheritBoundary ? () => {
586
+ const fromParent = typeof parent.getBoundaryElement === 'function' ? parent.getBoundaryElement() : null;
587
+ return fromParent || null;
588
+ } : undefined,
578
589
  children: children
579
590
  });
580
591
  };
@@ -957,7 +968,7 @@ const Layout = ({
957
968
  });
958
969
  };
959
970
 
960
- var style = {"page":"kne-system-layout_c1kXt","page-header":"kne-system-layout_h-YXH","is-scrolled":"kne-system-layout_dEXMy","page-title-outer":"kne-system-layout_jhcGo","capsule-click":"kne-system-layout_Ps1FA","page-title-extra":"kne-system-layout_mKsLu","page-title":"kne-system-layout_IOVGr","page-top":"kne-system-layout_Y1y7o","page-inner":"kne-system-layout_wHWje","no-padding":"kne-system-layout_tBnAD","is-mobile":"kne-system-layout_yRZMx","page-loading":"kne-system-layout_vcf3J","page-bottom":"kne-system-layout_vXk1s","back-icon":"kne-system-layout_07Mna"};
971
+ var style = {"page":"kne-system-layout_c1kXt","page-header":"kne-system-layout_h-YXH","is-viewport":"kne-system-layout_XjOj5","is-boundary":"kne-system-layout_Q-5NT","page-title-outer":"kne-system-layout_jhcGo","page-title":"kne-system-layout_IOVGr","page-title-extra":"kne-system-layout_mKsLu","page-title-actions":"kne-system-layout_XTAI-","is-scrolled":"kne-system-layout_dEXMy","capsule-click":"kne-system-layout_Ps1FA","back-icon":"kne-system-layout_07Mna","has-float":"kne-system-layout_SD6-j","page-top":"kne-system-layout_Y1y7o","page-inner":"kne-system-layout_wHWje","no-padding":"kne-system-layout_tBnAD","is-mobile":"kne-system-layout_yRZMx","page-header-boundary-overlay":"kne-system-layout_4pjtx","page-loading":"kne-system-layout_vcf3J","page-bottom":"kne-system-layout_vXk1s"};
961
972
 
962
973
  const PageLoading = () => {
963
974
  return /*#__PURE__*/jsx(Flex, {
@@ -991,8 +1002,15 @@ const Page = ({
991
1002
  } = useContext();
992
1003
  const navigate = useNavigate();
993
1004
  const getScrollElement = useScrollElement();
1005
+ const getBoundaryElement = usePopupContainer();
1006
+ const {
1007
+ mode
1008
+ } = useResponsiveContext();
994
1009
  const navbarRef = useRef(null);
995
1010
  const [isScrolled, setIsScrolled] = useState(false);
1011
+ const useBoundaryMount = deviceIsMobile && mode === 'container';
1012
+ const useViewportFixed = deviceIsMobile && mode !== 'container';
1013
+ const boundaryTarget = useBoundaryMount ? getBoundaryElement() : null;
996
1014
  useEffect(() => {
997
1015
  setToolbarShow && setToolbarShow(!!_toolbar);
998
1016
  setNavbarShow && setNavbarShow(!!_navbar);
@@ -1001,35 +1019,103 @@ const Page = ({
1001
1019
  if (!deviceIsMobile || !_navbar) {
1002
1020
  return;
1003
1021
  }
1004
- const scrollEl = getScrollElement();
1005
- if (!scrollEl || typeof scrollEl.addEventListener !== 'function') {
1006
- return;
1007
- }
1008
- const handleScroll = () => {
1009
- const threshold = navbarRef.current ? navbarRef.current.offsetHeight : 48;
1010
- setIsScrolled(scrollEl.scrollTop > threshold);
1022
+ let boundEl = null;
1023
+ let remove = null;
1024
+ const bind = () => {
1025
+ const scrollEl = getScrollElement();
1026
+ if (!scrollEl || typeof scrollEl.addEventListener !== 'function' || scrollEl === boundEl) {
1027
+ return;
1028
+ }
1029
+ if (typeof remove === 'function') {
1030
+ remove();
1031
+ }
1032
+ boundEl = scrollEl;
1033
+ const handleScroll = () => {
1034
+ const threshold = navbarRef.current ? navbarRef.current.offsetHeight : 48;
1035
+ setIsScrolled(scrollEl.scrollTop > threshold);
1036
+ };
1037
+ handleScroll();
1038
+ scrollEl.addEventListener('scroll', handleScroll, {
1039
+ passive: true
1040
+ });
1041
+ remove = () => scrollEl.removeEventListener('scroll', handleScroll);
1042
+ };
1043
+ bind();
1044
+ // SimpleBar / kne-responsive-scroll 可能在下一帧才就绪,补一次绑定
1045
+ const raf = requestAnimationFrame(bind);
1046
+ return () => {
1047
+ cancelAnimationFrame(raf);
1048
+ if (typeof remove === 'function') {
1049
+ remove();
1050
+ }
1011
1051
  };
1012
- handleScroll();
1013
- scrollEl.addEventListener('scroll', handleScroll, {
1014
- passive: true
1015
- });
1016
- return () => scrollEl.removeEventListener('scroll', handleScroll);
1017
1052
  }, [deviceIsMobile, _navbar, getScrollElement, scrollReady]);
1018
- const navbarEl = _navbar ? /*#__PURE__*/jsxs(Flex, {
1053
+ const titleExtraContent = (() => {
1054
+ if (_extra) {
1055
+ if (deviceIsMobile && userAvatar) {
1056
+ return /*#__PURE__*/jsxs(Flex, {
1057
+ gap: 4,
1058
+ align: "center",
1059
+ children: [_extra, /*#__PURE__*/jsx(Image.Avatar, {
1060
+ id: userAvatar,
1061
+ src: userAvatar,
1062
+ shape: "circle",
1063
+ size: 30,
1064
+ alt: "avatar"
1065
+ })]
1066
+ });
1067
+ }
1068
+ return _extra;
1069
+ }
1070
+ if (deviceIsMobile && (buttonProps || userAvatar)) {
1071
+ const actions = buttonProps ? /*#__PURE__*/jsx("div", {
1072
+ className: classnames('page-title-actions', style['page-title-actions']),
1073
+ children: /*#__PURE__*/jsx(ButtonGroup, _extends({}, Object.assign({}, buttonProps, {
1074
+ showLength: 0,
1075
+ moreType: 'link'
1076
+ })))
1077
+ }) : null;
1078
+ const avatar = userAvatar ? /*#__PURE__*/jsx(Image.Avatar, {
1079
+ id: userAvatar,
1080
+ src: userAvatar,
1081
+ shape: "circle",
1082
+ size: 30,
1083
+ alt: "avatar"
1084
+ }) : null;
1085
+ // 仅更多按钮时不要套带 gap 的 Flex,否则 ButtonGroup 测宽空节点也会吃 gap,导致不居中
1086
+ if (actions && avatar) {
1087
+ return /*#__PURE__*/jsxs(Flex, {
1088
+ gap: 4,
1089
+ align: "center",
1090
+ children: [actions, avatar]
1091
+ });
1092
+ }
1093
+ return actions || avatar;
1094
+ }
1095
+ if (buttonProps) {
1096
+ return /*#__PURE__*/jsx(ButtonGroup, _extends({}, buttonProps));
1097
+ }
1098
+ return null;
1099
+ })();
1100
+ const navbarInner = _navbar ? /*#__PURE__*/jsxs(Flex, {
1019
1101
  ref: navbarRef,
1020
1102
  justify: "space-between",
1021
1103
  align: "center",
1022
1104
  gap: 20,
1023
1105
  className: classnames('page-header', style['page-header'], {
1106
+ ['is-mobile']: deviceIsMobile,
1107
+ [style['is-mobile']]: deviceIsMobile,
1024
1108
  ['is-scrolled']: isScrolled,
1025
- [style['is-scrolled']]: isScrolled
1109
+ [style['is-scrolled']]: isScrolled,
1110
+ [style['is-boundary']]: useBoundaryMount && !!boundaryTarget,
1111
+ [style['is-viewport']]: useViewportFixed || useBoundaryMount && !boundaryTarget
1026
1112
  }),
1027
1113
  children: [/*#__PURE__*/jsxs(Flex, {
1028
1114
  className: classnames('page-title-outer', style['page-title-outer']),
1029
1115
  align: "center",
1030
1116
  gap: 4,
1031
1117
  children: [back && /*#__PURE__*/jsx(Button, {
1032
- type: "text",
1118
+ type: "link",
1033
1119
  icon: /*#__PURE__*/jsx(Icon, {
1034
1120
  className: style['back-icon'],
1035
1121
  type: "a-Typearrow_forward_ios",
@@ -1052,24 +1138,19 @@ const Page = ({
1052
1138
  className: classnames('page-title', style['page-title']),
1053
1139
  children: title
1054
1140
  })]
1055
- }), /*#__PURE__*/jsx("div", {
1056
- className: classnames('page-title-extra', style['page-title-extra']),
1057
- children: _extra ? _extra : deviceIsMobile ? /*#__PURE__*/jsxs(Flex, {
1058
- gap: 4,
1059
- align: "center",
1060
- children: [buttonProps && /*#__PURE__*/jsx(ButtonGroup, _extends({}, Object.assign({}, buttonProps, deviceIsMobile ? {
1061
- showLength: 0,
1062
- moreType: 'link'
1063
- } : {}))), /*#__PURE__*/jsx(Image.Avatar, {
1064
- id: userAvatar,
1065
- src: userAvatar,
1066
- shape: "circle",
1067
- size: 30,
1068
- alt: "avatar"
1069
- })]
1070
- }) : buttonProps && /*#__PURE__*/jsx(ButtonGroup, _extends({}, buttonProps))
1071
- })]
1141
+ }), titleExtraContent ? /*#__PURE__*/jsx("div", {
1142
+ className: classnames('page-title-extra', style['page-title-extra'], style['has-float']),
1143
+ children: titleExtraContent
1144
+ }) : null]
1072
1145
  }) : null;
1146
+ const navbarEl = navbarInner && useBoundaryMount && boundaryTarget ? /*#__PURE__*/createPortal(/*#__PURE__*/jsx("div", {
1147
+ className: classnames('page-header-boundary-overlay', style['page-header-boundary-overlay']),
1148
+ style: {
1149
+ '--safe-area-inset-top': 'env(safe-area-inset-top)',
1150
+ '--safe-area-inset-bottom': 'env(safe-area-inset-bottom)'
1151
+ },
1152
+ children: navbarInner
1153
+ }), boundaryTarget) : navbarInner;
1073
1154
  const pageInnerClassNameOrigin = classnames('page-inner', style['page-inner']);
1074
1155
  const pageInnerClassName = classnames(pageInnerClassNameOrigin, {
1075
1156
  ['no-padding']: noPadding,
@@ -1127,7 +1208,7 @@ const isMobile = () => {
1127
1208
  const transparentBgContainer = {
1128
1209
  colorBgContainer: 'transparent'
1129
1210
  };
1130
- const transparentBgContainerComponents = ['Input', 'InputNumber', 'Card', 'Tree', 'Select', 'DatePicker'];
1211
+ const transparentBgContainerComponents = ['Input', 'InputNumber', 'Card', 'Tree', 'Select', 'DatePicker', 'Tabs'];
1131
1212
  const themeToken = {
1132
1213
  components: merge({}, transform(transparentBgContainerComponents, (result, component) => {
1133
1214
  result[component] = transparentBgContainer;