diff --git a/docs-vitepress/guide/rn/component.md b/docs-vitepress/guide/rn/component.md index 9f726bfe32..0c7cac447b 100644 --- a/docs-vitepress/guide/rn/component.md +++ b/docs-vitepress/guide/rn/component.md @@ -5,7 +5,7 @@ ### 目录概览 {#directory-overview} - #### 基础组件 -**容器组件**:[view](#view) · [scroll-view](#scroll-view) · [swiper](#swiper) · [swiper-item](#swiper-item) · [movable-area](#movable-area) · [movable-view](#movable-view) · [root-portal](#root-portal) · [sticky-section](#sticky-section) · [sticky-header](#sticky-header) · [cover-view](#cover-view) +**容器组件**:[view](#view) · [scroll-view](#scroll-view) · [swiper](#swiper) · [swiper-item](#swiper-item) · [movable-area](#movable-area) · [movable-view](#movable-view) · [page-container](#page-container) · [root-portal](#root-portal) · [sticky-section](#sticky-section) · [sticky-header](#sticky-header) · [cover-view](#cover-view) **媒体组件**:[image](#image) · [video](#video) · [canvas](#canvas) @@ -731,6 +731,28 @@ API > [!tip] 注意 > > - style 样式不支持中使用百分比计算、css variable + +### page-container +页面内容容器,常用于实现“假页”弹层。容器展示期间会拦截页面返回行为(包含返回按钮与侧滑返回),并通过回调通知业务侧自行关闭容器。 + +属性 + +| 属性名 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| show | boolean | `false` | 是否显示容器 | +| overlay | boolean | `true` | 是否显示遮罩层 | + +事件 + +| 事件名 | 说明 | +| --- | --- | +| bind:beforeleave | 触发返回离开前回调,业务可在回调中更新 `show=false` 关闭容器 | + +> [!tip] 注意 +> +> - 当前仅支持 `show`、`overlay` 与 `bind:beforeleave` 三项能力。 +> - 容器显示时会自动禁用页面返回手势,并在隐藏时恢复,避免 `native-stack` 场景下手势返回与 JS 状态不一致。 +> - 该组件依赖导航上下文,仅在 RN 页面路由栈内生效。 ### sticky-section 吸顶布局容器,仅支持作为 `` 的直接子节点 diff --git a/packages/webpack-plugin/lib/platform/template/wx/component-config/index.js b/packages/webpack-plugin/lib/platform/template/wx/component-config/index.js index b5a65229fb..3af422b845 100644 --- a/packages/webpack-plugin/lib/platform/template/wx/component-config/index.js +++ b/packages/webpack-plugin/lib/platform/template/wx/component-config/index.js @@ -21,6 +21,7 @@ const navigator = require('./navigator') const pickerViewColumn = require('./picker-view-column') const pickerView = require('./picker-view') const picker = require('./picker') +const pageContainer = require('./page-container') const progress = require('./progress') const radioGroup = require('./radio-group') const radio = require('./radio') @@ -121,6 +122,7 @@ module.exports = function getComponentConfigs ({ warn, error }) { form({ print }), input({ print }), picker({ print }), + pageContainer({ print }), pickerView({ print }), pickerViewColumn({ print }), slider({ print }), diff --git a/packages/webpack-plugin/lib/platform/template/wx/component-config/page-container.js b/packages/webpack-plugin/lib/platform/template/wx/component-config/page-container.js new file mode 100644 index 0000000000..78738f83c7 --- /dev/null +++ b/packages/webpack-plugin/lib/platform/template/wx/component-config/page-container.js @@ -0,0 +1,33 @@ +const TAG_NAME = 'page-container' + +module.exports = function () { + return { + test: TAG_NAME, + ios (tag, { el }) { + el.isBuiltIn = true + return 'mpx-page-container' + }, + android (tag, { el }) { + el.isBuiltIn = true + return 'mpx-page-container' + }, + harmony (tag, { el }) { + el.isBuiltIn = true + return 'mpx-page-container' + }, + event: [ + { + test: /^(beforeleave)$/, + ios (eventName) { + return eventName + }, + android (eventName) { + return eventName + }, + harmony (eventName) { + return eventName + } + } + ] + } +} diff --git a/packages/webpack-plugin/lib/platform/template/wx/component-config/unsupported.js b/packages/webpack-plugin/lib/platform/template/wx/component-config/unsupported.js index 0c880ea06a..c5dd753820 100644 --- a/packages/webpack-plugin/lib/platform/template/wx/component-config/unsupported.js +++ b/packages/webpack-plugin/lib/platform/template/wx/component-config/unsupported.js @@ -13,7 +13,7 @@ const JD_UNSUPPORTED_TAG_NAME_ARR = ['functional-page-navigator', 'live-pusher', // 快应用不支持的标签集合 const QA_UNSUPPORTED_TAG_NAME_ARR = ['movable-view', 'movable-area', 'open-data', 'official-account', 'editor', 'functional-page-navigator', 'live-player', 'live-pusher', 'ad', 'cover-image'] // RN不支持的标签集合 -const RN_UNSUPPORTED_TAG_NAME_ARR = ['open-data', 'official-account', 'editor', 'functional-page-navigator', 'live-player', 'live-pusher', 'ad', 'audio', 'match-media', 'page-container', 'editor', 'keyboard-accessory', 'map'] +const RN_UNSUPPORTED_TAG_NAME_ARR = ['open-data', 'official-account', 'editor', 'functional-page-navigator', 'live-player', 'live-pusher', 'ad', 'audio', 'match-media', 'editor', 'keyboard-accessory', 'map'] /** * @param {function(object): function} print diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-page-container.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-page-container.tsx new file mode 100644 index 0000000000..1097bc9d2a --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-page-container.tsx @@ -0,0 +1,85 @@ +/** + * ✔ show + * ✔ overlay + * ✔ bind:beforeleave + */ +import { ReactNode, useContext, useEffect, useRef } from 'react' +import { StyleSheet, View } from 'react-native' +import Portal from './mpx-portal' +import { RouteContext } from './context' +import { getCustomEvent } from './getInnerListeners' + +interface PageContainerProps { + show?: boolean + overlay?: boolean + bindbeforeleave?: (event?: any) => void + children?: ReactNode +} + +const styles = StyleSheet.create({ + container: { + position: 'absolute', + top: 0, + right: 0, + bottom: 0, + left: 0, + zIndex: 100 + }, + overlay: { + position: 'absolute', + top: 0, + right: 0, + bottom: 0, + left: 0, + backgroundColor: 'rgba(0,0,0,0.6)', + zIndex: 0 + }, + content: { + position: 'absolute', + top: 0, + right: 0, + bottom: 0, + left: 0, + zIndex: 1 + } +}) + +const PageContainer = (props: PageContainerProps) => { + const { show = false, overlay = true, bindbeforeleave, children } = props + const { navigation } = useContext(RouteContext) || {} + const showRef = useRef(show) + const propsRef = useRef(props) + + showRef.current = show + propsRef.current = props + + useEffect(() => { + if (!navigation?.addListener) return + + return navigation.addListener('beforeRemove', (e: any) => { + if (!showRef.current) return + + e.preventDefault?.() + propsRef.current.bindbeforeleave?.( + getCustomEvent('beforeleave', e, { detail: {} }, propsRef.current) + ) + }) + }, [navigation, bindbeforeleave]) + + if (!show) return null + + return ( + + + {overlay ? : null} + + {children} + + + + ) +} + +PageContainer.displayName = 'MpxPageContainer' + +export default PageContainer diff --git a/packages/webpack-plugin/lib/utils/dom-tag-config.js b/packages/webpack-plugin/lib/utils/dom-tag-config.js index a07accc10f..cece3ce2f7 100644 --- a/packages/webpack-plugin/lib/utils/dom-tag-config.js +++ b/packages/webpack-plugin/lib/utils/dom-tag-config.js @@ -87,7 +87,7 @@ const isBuildInWebTag = makeMap( const isBuildInReactTag = makeMap( 'mpx-web-view,mpx-view,mpx-video,mpx-textarea,mpx-text,mpx-switch,' + 'mpx-swiper,mpx-swiper-item,mpx-scroll-view,' + - 'mpx-root-portal,mpx-radio,mpx-radio-group,mpx-navigator,mpx-movable-view,' + + 'mpx-root-portal,mpx-page-container,mpx-radio,mpx-radio-group,mpx-navigator,mpx-movable-view,' + 'mpx-movable-area,mpx-label,mpx-input,' + 'mpx-image,mpx-form,mpx-checkbox,mpx-checkbox-group,mpx-button,' + 'mpx-rich-text,mpx-picker-view-column,mpx-picker-view,mpx-picker,' +